1*9d71dd0cSThe j1939 authors // SPDX-License-Identifier: GPL-2.0
2*9d71dd0cSThe j1939 authors // Copyright (c) 2010-2011 EIA Electronics,
3*9d71dd0cSThe j1939 authors // Kurt Van Dijck <kurt.van.dijck@eia.be>
4*9d71dd0cSThe j1939 authors // Copyright (c) 2017-2019 Pengutronix,
5*9d71dd0cSThe j1939 authors // Marc Kleine-Budde <kernel@pengutronix.de>
6*9d71dd0cSThe j1939 authors // Copyright (c) 2017-2019 Pengutronix,
7*9d71dd0cSThe j1939 authors // Oleksij Rempel <kernel@pengutronix.de>
8*9d71dd0cSThe j1939 authors
9*9d71dd0cSThe j1939 authors /* bus for j1939 remote devices
10*9d71dd0cSThe j1939 authors * Since rtnetlink, no real bus is used.
11*9d71dd0cSThe j1939 authors */
12*9d71dd0cSThe j1939 authors
13*9d71dd0cSThe j1939 authors #include <net/sock.h>
14*9d71dd0cSThe j1939 authors
15*9d71dd0cSThe j1939 authors #include "j1939-priv.h"
16*9d71dd0cSThe j1939 authors
__j1939_ecu_release(struct kref * kref)17*9d71dd0cSThe j1939 authors static void __j1939_ecu_release(struct kref *kref)
18*9d71dd0cSThe j1939 authors {
19*9d71dd0cSThe j1939 authors struct j1939_ecu *ecu = container_of(kref, struct j1939_ecu, kref);
20*9d71dd0cSThe j1939 authors struct j1939_priv *priv = ecu->priv;
21*9d71dd0cSThe j1939 authors
22*9d71dd0cSThe j1939 authors list_del(&ecu->list);
23*9d71dd0cSThe j1939 authors kfree(ecu);
24*9d71dd0cSThe j1939 authors j1939_priv_put(priv);
25*9d71dd0cSThe j1939 authors }
26*9d71dd0cSThe j1939 authors
j1939_ecu_put(struct j1939_ecu * ecu)27*9d71dd0cSThe j1939 authors void j1939_ecu_put(struct j1939_ecu *ecu)
28*9d71dd0cSThe j1939 authors {
29*9d71dd0cSThe j1939 authors kref_put(&ecu->kref, __j1939_ecu_release);
30*9d71dd0cSThe j1939 authors }
31*9d71dd0cSThe j1939 authors
j1939_ecu_get(struct j1939_ecu * ecu)32*9d71dd0cSThe j1939 authors static void j1939_ecu_get(struct j1939_ecu *ecu)
33*9d71dd0cSThe j1939 authors {
34*9d71dd0cSThe j1939 authors kref_get(&ecu->kref);
35*9d71dd0cSThe j1939 authors }
36*9d71dd0cSThe j1939 authors
j1939_ecu_is_mapped_locked(struct j1939_ecu * ecu)37*9d71dd0cSThe j1939 authors static bool j1939_ecu_is_mapped_locked(struct j1939_ecu *ecu)
38*9d71dd0cSThe j1939 authors {
39*9d71dd0cSThe j1939 authors struct j1939_priv *priv = ecu->priv;
40*9d71dd0cSThe j1939 authors
41*9d71dd0cSThe j1939 authors lockdep_assert_held(&priv->lock);
42*9d71dd0cSThe j1939 authors
43*9d71dd0cSThe j1939 authors return j1939_ecu_find_by_addr_locked(priv, ecu->addr) == ecu;
44*9d71dd0cSThe j1939 authors }
45*9d71dd0cSThe j1939 authors
46*9d71dd0cSThe j1939 authors /* ECU device interface */
47*9d71dd0cSThe j1939 authors /* map ECU to a bus address space */
j1939_ecu_map_locked(struct j1939_ecu * ecu)48*9d71dd0cSThe j1939 authors static void j1939_ecu_map_locked(struct j1939_ecu *ecu)
49*9d71dd0cSThe j1939 authors {
50*9d71dd0cSThe j1939 authors struct j1939_priv *priv = ecu->priv;
51*9d71dd0cSThe j1939 authors struct j1939_addr_ent *ent;
52*9d71dd0cSThe j1939 authors
53*9d71dd0cSThe j1939 authors lockdep_assert_held(&priv->lock);
54*9d71dd0cSThe j1939 authors
55*9d71dd0cSThe j1939 authors if (!j1939_address_is_unicast(ecu->addr))
56*9d71dd0cSThe j1939 authors return;
57*9d71dd0cSThe j1939 authors
58*9d71dd0cSThe j1939 authors ent = &priv->ents[ecu->addr];
59*9d71dd0cSThe j1939 authors
60*9d71dd0cSThe j1939 authors if (ent->ecu) {
61*9d71dd0cSThe j1939 authors netdev_warn(priv->ndev, "Trying to map already mapped ECU, addr: 0x%02x, name: 0x%016llx. Skip it.\n",
62*9d71dd0cSThe j1939 authors ecu->addr, ecu->name);
63*9d71dd0cSThe j1939 authors return;
64*9d71dd0cSThe j1939 authors }
65*9d71dd0cSThe j1939 authors
66*9d71dd0cSThe j1939 authors j1939_ecu_get(ecu);
67*9d71dd0cSThe j1939 authors ent->ecu = ecu;
68*9d71dd0cSThe j1939 authors ent->nusers += ecu->nusers;
69*9d71dd0cSThe j1939 authors }
70*9d71dd0cSThe j1939 authors
71*9d71dd0cSThe j1939 authors /* unmap ECU from a bus address space */
j1939_ecu_unmap_locked(struct j1939_ecu * ecu)72*9d71dd0cSThe j1939 authors void j1939_ecu_unmap_locked(struct j1939_ecu *ecu)
73*9d71dd0cSThe j1939 authors {
74*9d71dd0cSThe j1939 authors struct j1939_priv *priv = ecu->priv;
75*9d71dd0cSThe j1939 authors struct j1939_addr_ent *ent;
76*9d71dd0cSThe j1939 authors
77*9d71dd0cSThe j1939 authors lockdep_assert_held(&priv->lock);
78*9d71dd0cSThe j1939 authors
79*9d71dd0cSThe j1939 authors if (!j1939_address_is_unicast(ecu->addr))
80*9d71dd0cSThe j1939 authors return;
81*9d71dd0cSThe j1939 authors
82*9d71dd0cSThe j1939 authors if (!j1939_ecu_is_mapped_locked(ecu))
83*9d71dd0cSThe j1939 authors return;
84*9d71dd0cSThe j1939 authors
85*9d71dd0cSThe j1939 authors ent = &priv->ents[ecu->addr];
86*9d71dd0cSThe j1939 authors ent->ecu = NULL;
87*9d71dd0cSThe j1939 authors ent->nusers -= ecu->nusers;
88*9d71dd0cSThe j1939 authors j1939_ecu_put(ecu);
89*9d71dd0cSThe j1939 authors }
90*9d71dd0cSThe j1939 authors
j1939_ecu_unmap(struct j1939_ecu * ecu)91*9d71dd0cSThe j1939 authors void j1939_ecu_unmap(struct j1939_ecu *ecu)
92*9d71dd0cSThe j1939 authors {
93*9d71dd0cSThe j1939 authors write_lock_bh(&ecu->priv->lock);
94*9d71dd0cSThe j1939 authors j1939_ecu_unmap_locked(ecu);
95*9d71dd0cSThe j1939 authors write_unlock_bh(&ecu->priv->lock);
96*9d71dd0cSThe j1939 authors }
97*9d71dd0cSThe j1939 authors
j1939_ecu_unmap_all(struct j1939_priv * priv)98*9d71dd0cSThe j1939 authors void j1939_ecu_unmap_all(struct j1939_priv *priv)
99*9d71dd0cSThe j1939 authors {
100*9d71dd0cSThe j1939 authors int i;
101*9d71dd0cSThe j1939 authors
102*9d71dd0cSThe j1939 authors write_lock_bh(&priv->lock);
103*9d71dd0cSThe j1939 authors for (i = 0; i < ARRAY_SIZE(priv->ents); i++)
104*9d71dd0cSThe j1939 authors if (priv->ents[i].ecu)
105*9d71dd0cSThe j1939 authors j1939_ecu_unmap_locked(priv->ents[i].ecu);
106*9d71dd0cSThe j1939 authors write_unlock_bh(&priv->lock);
107*9d71dd0cSThe j1939 authors }
108*9d71dd0cSThe j1939 authors
j1939_ecu_timer_start(struct j1939_ecu * ecu)109*9d71dd0cSThe j1939 authors void j1939_ecu_timer_start(struct j1939_ecu *ecu)
110*9d71dd0cSThe j1939 authors {
111*9d71dd0cSThe j1939 authors /* The ECU is held here and released in the
112*9d71dd0cSThe j1939 authors * j1939_ecu_timer_handler() or j1939_ecu_timer_cancel().
113*9d71dd0cSThe j1939 authors */
114*9d71dd0cSThe j1939 authors j1939_ecu_get(ecu);
115*9d71dd0cSThe j1939 authors
116*9d71dd0cSThe j1939 authors /* Schedule timer in 250 msec to commit address change. */
117*9d71dd0cSThe j1939 authors hrtimer_start(&ecu->ac_timer, ms_to_ktime(250),
118*9d71dd0cSThe j1939 authors HRTIMER_MODE_REL_SOFT);
119*9d71dd0cSThe j1939 authors }
120*9d71dd0cSThe j1939 authors
j1939_ecu_timer_cancel(struct j1939_ecu * ecu)121*9d71dd0cSThe j1939 authors void j1939_ecu_timer_cancel(struct j1939_ecu *ecu)
122*9d71dd0cSThe j1939 authors {
123*9d71dd0cSThe j1939 authors if (hrtimer_cancel(&ecu->ac_timer))
124*9d71dd0cSThe j1939 authors j1939_ecu_put(ecu);
125*9d71dd0cSThe j1939 authors }
126*9d71dd0cSThe j1939 authors
j1939_ecu_timer_handler(struct hrtimer * hrtimer)127*9d71dd0cSThe j1939 authors static enum hrtimer_restart j1939_ecu_timer_handler(struct hrtimer *hrtimer)
128*9d71dd0cSThe j1939 authors {
129*9d71dd0cSThe j1939 authors struct j1939_ecu *ecu =
130*9d71dd0cSThe j1939 authors container_of(hrtimer, struct j1939_ecu, ac_timer);
131*9d71dd0cSThe j1939 authors struct j1939_priv *priv = ecu->priv;
132*9d71dd0cSThe j1939 authors
133*9d71dd0cSThe j1939 authors write_lock_bh(&priv->lock);
134*9d71dd0cSThe j1939 authors /* TODO: can we test if ecu->addr is unicast before starting
135*9d71dd0cSThe j1939 authors * the timer?
136*9d71dd0cSThe j1939 authors */
137*9d71dd0cSThe j1939 authors j1939_ecu_map_locked(ecu);
138*9d71dd0cSThe j1939 authors
139*9d71dd0cSThe j1939 authors /* The corresponding j1939_ecu_get() is in
140*9d71dd0cSThe j1939 authors * j1939_ecu_timer_start().
141*9d71dd0cSThe j1939 authors */
142*9d71dd0cSThe j1939 authors j1939_ecu_put(ecu);
143*9d71dd0cSThe j1939 authors write_unlock_bh(&priv->lock);
144*9d71dd0cSThe j1939 authors
145*9d71dd0cSThe j1939 authors return HRTIMER_NORESTART;
146*9d71dd0cSThe j1939 authors }
147*9d71dd0cSThe j1939 authors
j1939_ecu_create_locked(struct j1939_priv * priv,name_t name)148*9d71dd0cSThe j1939 authors struct j1939_ecu *j1939_ecu_create_locked(struct j1939_priv *priv, name_t name)
149*9d71dd0cSThe j1939 authors {
150*9d71dd0cSThe j1939 authors struct j1939_ecu *ecu;
151*9d71dd0cSThe j1939 authors
152*9d71dd0cSThe j1939 authors lockdep_assert_held(&priv->lock);
153*9d71dd0cSThe j1939 authors
154*9d71dd0cSThe j1939 authors ecu = kzalloc(sizeof(*ecu), gfp_any());
155*9d71dd0cSThe j1939 authors if (!ecu)
156*9d71dd0cSThe j1939 authors return ERR_PTR(-ENOMEM);
157*9d71dd0cSThe j1939 authors kref_init(&ecu->kref);
158*9d71dd0cSThe j1939 authors ecu->addr = J1939_IDLE_ADDR;
159*9d71dd0cSThe j1939 authors ecu->name = name;
160*9d71dd0cSThe j1939 authors
161*9d71dd0cSThe j1939 authors hrtimer_init(&ecu->ac_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
162*9d71dd0cSThe j1939 authors ecu->ac_timer.function = j1939_ecu_timer_handler;
163*9d71dd0cSThe j1939 authors INIT_LIST_HEAD(&ecu->list);
164*9d71dd0cSThe j1939 authors
165*9d71dd0cSThe j1939 authors j1939_priv_get(priv);
166*9d71dd0cSThe j1939 authors ecu->priv = priv;
167*9d71dd0cSThe j1939 authors list_add_tail(&ecu->list, &priv->ecus);
168*9d71dd0cSThe j1939 authors
169*9d71dd0cSThe j1939 authors return ecu;
170*9d71dd0cSThe j1939 authors }
171*9d71dd0cSThe j1939 authors
j1939_ecu_find_by_addr_locked(struct j1939_priv * priv,u8 addr)172*9d71dd0cSThe j1939 authors struct j1939_ecu *j1939_ecu_find_by_addr_locked(struct j1939_priv *priv,
173*9d71dd0cSThe j1939 authors u8 addr)
174*9d71dd0cSThe j1939 authors {
175*9d71dd0cSThe j1939 authors lockdep_assert_held(&priv->lock);
176*9d71dd0cSThe j1939 authors
177*9d71dd0cSThe j1939 authors return priv->ents[addr].ecu;
178*9d71dd0cSThe j1939 authors }
179*9d71dd0cSThe j1939 authors
j1939_ecu_get_by_addr_locked(struct j1939_priv * priv,u8 addr)180*9d71dd0cSThe j1939 authors struct j1939_ecu *j1939_ecu_get_by_addr_locked(struct j1939_priv *priv, u8 addr)
181*9d71dd0cSThe j1939 authors {
182*9d71dd0cSThe j1939 authors struct j1939_ecu *ecu;
183*9d71dd0cSThe j1939 authors
184*9d71dd0cSThe j1939 authors lockdep_assert_held(&priv->lock);
185*9d71dd0cSThe j1939 authors
186*9d71dd0cSThe j1939 authors if (!j1939_address_is_unicast(addr))
187*9d71dd0cSThe j1939 authors return NULL;
188*9d71dd0cSThe j1939 authors
189*9d71dd0cSThe j1939 authors ecu = j1939_ecu_find_by_addr_locked(priv, addr);
190*9d71dd0cSThe j1939 authors if (ecu)
191*9d71dd0cSThe j1939 authors j1939_ecu_get(ecu);
192*9d71dd0cSThe j1939 authors
193*9d71dd0cSThe j1939 authors return ecu;
194*9d71dd0cSThe j1939 authors }
195*9d71dd0cSThe j1939 authors
j1939_ecu_get_by_addr(struct j1939_priv * priv,u8 addr)196*9d71dd0cSThe j1939 authors struct j1939_ecu *j1939_ecu_get_by_addr(struct j1939_priv *priv, u8 addr)
197*9d71dd0cSThe j1939 authors {
198*9d71dd0cSThe j1939 authors struct j1939_ecu *ecu;
199*9d71dd0cSThe j1939 authors
200*9d71dd0cSThe j1939 authors read_lock_bh(&priv->lock);
201*9d71dd0cSThe j1939 authors ecu = j1939_ecu_get_by_addr_locked(priv, addr);
202*9d71dd0cSThe j1939 authors read_unlock_bh(&priv->lock);
203*9d71dd0cSThe j1939 authors
204*9d71dd0cSThe j1939 authors return ecu;
205*9d71dd0cSThe j1939 authors }
206*9d71dd0cSThe j1939 authors
207*9d71dd0cSThe j1939 authors /* get pointer to ecu without increasing ref counter */
j1939_ecu_find_by_name_locked(struct j1939_priv * priv,name_t name)208*9d71dd0cSThe j1939 authors static struct j1939_ecu *j1939_ecu_find_by_name_locked(struct j1939_priv *priv,
209*9d71dd0cSThe j1939 authors name_t name)
210*9d71dd0cSThe j1939 authors {
211*9d71dd0cSThe j1939 authors struct j1939_ecu *ecu;
212*9d71dd0cSThe j1939 authors
213*9d71dd0cSThe j1939 authors lockdep_assert_held(&priv->lock);
214*9d71dd0cSThe j1939 authors
215*9d71dd0cSThe j1939 authors list_for_each_entry(ecu, &priv->ecus, list) {
216*9d71dd0cSThe j1939 authors if (ecu->name == name)
217*9d71dd0cSThe j1939 authors return ecu;
218*9d71dd0cSThe j1939 authors }
219*9d71dd0cSThe j1939 authors
220*9d71dd0cSThe j1939 authors return NULL;
221*9d71dd0cSThe j1939 authors }
222*9d71dd0cSThe j1939 authors
j1939_ecu_get_by_name_locked(struct j1939_priv * priv,name_t name)223*9d71dd0cSThe j1939 authors struct j1939_ecu *j1939_ecu_get_by_name_locked(struct j1939_priv *priv,
224*9d71dd0cSThe j1939 authors name_t name)
225*9d71dd0cSThe j1939 authors {
226*9d71dd0cSThe j1939 authors struct j1939_ecu *ecu;
227*9d71dd0cSThe j1939 authors
228*9d71dd0cSThe j1939 authors lockdep_assert_held(&priv->lock);
229*9d71dd0cSThe j1939 authors
230*9d71dd0cSThe j1939 authors if (!name)
231*9d71dd0cSThe j1939 authors return NULL;
232*9d71dd0cSThe j1939 authors
233*9d71dd0cSThe j1939 authors ecu = j1939_ecu_find_by_name_locked(priv, name);
234*9d71dd0cSThe j1939 authors if (ecu)
235*9d71dd0cSThe j1939 authors j1939_ecu_get(ecu);
236*9d71dd0cSThe j1939 authors
237*9d71dd0cSThe j1939 authors return ecu;
238*9d71dd0cSThe j1939 authors }
239*9d71dd0cSThe j1939 authors
j1939_ecu_get_by_name(struct j1939_priv * priv,name_t name)240*9d71dd0cSThe j1939 authors struct j1939_ecu *j1939_ecu_get_by_name(struct j1939_priv *priv, name_t name)
241*9d71dd0cSThe j1939 authors {
242*9d71dd0cSThe j1939 authors struct j1939_ecu *ecu;
243*9d71dd0cSThe j1939 authors
244*9d71dd0cSThe j1939 authors read_lock_bh(&priv->lock);
245*9d71dd0cSThe j1939 authors ecu = j1939_ecu_get_by_name_locked(priv, name);
246*9d71dd0cSThe j1939 authors read_unlock_bh(&priv->lock);
247*9d71dd0cSThe j1939 authors
248*9d71dd0cSThe j1939 authors return ecu;
249*9d71dd0cSThe j1939 authors }
250*9d71dd0cSThe j1939 authors
j1939_name_to_addr(struct j1939_priv * priv,name_t name)251*9d71dd0cSThe j1939 authors u8 j1939_name_to_addr(struct j1939_priv *priv, name_t name)
252*9d71dd0cSThe j1939 authors {
253*9d71dd0cSThe j1939 authors struct j1939_ecu *ecu;
254*9d71dd0cSThe j1939 authors int addr = J1939_IDLE_ADDR;
255*9d71dd0cSThe j1939 authors
256*9d71dd0cSThe j1939 authors if (!name)
257*9d71dd0cSThe j1939 authors return J1939_NO_ADDR;
258*9d71dd0cSThe j1939 authors
259*9d71dd0cSThe j1939 authors read_lock_bh(&priv->lock);
260*9d71dd0cSThe j1939 authors ecu = j1939_ecu_find_by_name_locked(priv, name);
261*9d71dd0cSThe j1939 authors if (ecu && j1939_ecu_is_mapped_locked(ecu))
262*9d71dd0cSThe j1939 authors /* ecu's SA is registered */
263*9d71dd0cSThe j1939 authors addr = ecu->addr;
264*9d71dd0cSThe j1939 authors
265*9d71dd0cSThe j1939 authors read_unlock_bh(&priv->lock);
266*9d71dd0cSThe j1939 authors
267*9d71dd0cSThe j1939 authors return addr;
268*9d71dd0cSThe j1939 authors }
269*9d71dd0cSThe j1939 authors
270*9d71dd0cSThe j1939 authors /* TX addr/name accounting
271*9d71dd0cSThe j1939 authors * Transport protocol needs to know if a SA is local or not
272*9d71dd0cSThe j1939 authors * These functions originate from userspace manipulating sockets,
273*9d71dd0cSThe j1939 authors * so locking is straigforward
274*9d71dd0cSThe j1939 authors */
275*9d71dd0cSThe j1939 authors
j1939_local_ecu_get(struct j1939_priv * priv,name_t name,u8 sa)276*9d71dd0cSThe j1939 authors int j1939_local_ecu_get(struct j1939_priv *priv, name_t name, u8 sa)
277*9d71dd0cSThe j1939 authors {
278*9d71dd0cSThe j1939 authors struct j1939_ecu *ecu;
279*9d71dd0cSThe j1939 authors int err = 0;
280*9d71dd0cSThe j1939 authors
281*9d71dd0cSThe j1939 authors write_lock_bh(&priv->lock);
282*9d71dd0cSThe j1939 authors
283*9d71dd0cSThe j1939 authors if (j1939_address_is_unicast(sa))
284*9d71dd0cSThe j1939 authors priv->ents[sa].nusers++;
285*9d71dd0cSThe j1939 authors
286*9d71dd0cSThe j1939 authors if (!name)
287*9d71dd0cSThe j1939 authors goto done;
288*9d71dd0cSThe j1939 authors
289*9d71dd0cSThe j1939 authors ecu = j1939_ecu_get_by_name_locked(priv, name);
290*9d71dd0cSThe j1939 authors if (!ecu)
291*9d71dd0cSThe j1939 authors ecu = j1939_ecu_create_locked(priv, name);
292*9d71dd0cSThe j1939 authors err = PTR_ERR_OR_ZERO(ecu);
293*9d71dd0cSThe j1939 authors if (err)
294*9d71dd0cSThe j1939 authors goto done;
295*9d71dd0cSThe j1939 authors
296*9d71dd0cSThe j1939 authors ecu->nusers++;
297*9d71dd0cSThe j1939 authors /* TODO: do we care if ecu->addr != sa? */
298*9d71dd0cSThe j1939 authors if (j1939_ecu_is_mapped_locked(ecu))
299*9d71dd0cSThe j1939 authors /* ecu's sa is active already */
300*9d71dd0cSThe j1939 authors priv->ents[ecu->addr].nusers++;
301*9d71dd0cSThe j1939 authors
302*9d71dd0cSThe j1939 authors done:
303*9d71dd0cSThe j1939 authors write_unlock_bh(&priv->lock);
304*9d71dd0cSThe j1939 authors
305*9d71dd0cSThe j1939 authors return err;
306*9d71dd0cSThe j1939 authors }
307*9d71dd0cSThe j1939 authors
j1939_local_ecu_put(struct j1939_priv * priv,name_t name,u8 sa)308*9d71dd0cSThe j1939 authors void j1939_local_ecu_put(struct j1939_priv *priv, name_t name, u8 sa)
309*9d71dd0cSThe j1939 authors {
310*9d71dd0cSThe j1939 authors struct j1939_ecu *ecu;
311*9d71dd0cSThe j1939 authors
312*9d71dd0cSThe j1939 authors write_lock_bh(&priv->lock);
313*9d71dd0cSThe j1939 authors
314*9d71dd0cSThe j1939 authors if (j1939_address_is_unicast(sa))
315*9d71dd0cSThe j1939 authors priv->ents[sa].nusers--;
316*9d71dd0cSThe j1939 authors
317*9d71dd0cSThe j1939 authors if (!name)
318*9d71dd0cSThe j1939 authors goto done;
319*9d71dd0cSThe j1939 authors
320*9d71dd0cSThe j1939 authors ecu = j1939_ecu_find_by_name_locked(priv, name);
321*9d71dd0cSThe j1939 authors if (WARN_ON_ONCE(!ecu))
322*9d71dd0cSThe j1939 authors goto done;
323*9d71dd0cSThe j1939 authors
324*9d71dd0cSThe j1939 authors ecu->nusers--;
325*9d71dd0cSThe j1939 authors /* TODO: do we care if ecu->addr != sa? */
326*9d71dd0cSThe j1939 authors if (j1939_ecu_is_mapped_locked(ecu))
327*9d71dd0cSThe j1939 authors /* ecu's sa is active already */
328*9d71dd0cSThe j1939 authors priv->ents[ecu->addr].nusers--;
329*9d71dd0cSThe j1939 authors j1939_ecu_put(ecu);
330*9d71dd0cSThe j1939 authors
331*9d71dd0cSThe j1939 authors done:
332*9d71dd0cSThe j1939 authors write_unlock_bh(&priv->lock);
333*9d71dd0cSThe j1939 authors }
334