xref: /openbmc/linux/net/can/j1939/bus.c (revision 9d71dd0c)
19d71dd0cSThe j1939 authors // SPDX-License-Identifier: GPL-2.0
29d71dd0cSThe j1939 authors // Copyright (c) 2010-2011 EIA Electronics,
39d71dd0cSThe j1939 authors //                         Kurt Van Dijck <kurt.van.dijck@eia.be>
49d71dd0cSThe j1939 authors // Copyright (c) 2017-2019 Pengutronix,
59d71dd0cSThe j1939 authors //                         Marc Kleine-Budde <kernel@pengutronix.de>
69d71dd0cSThe j1939 authors // Copyright (c) 2017-2019 Pengutronix,
79d71dd0cSThe j1939 authors //                         Oleksij Rempel <kernel@pengutronix.de>
89d71dd0cSThe j1939 authors 
99d71dd0cSThe j1939 authors /* bus for j1939 remote devices
109d71dd0cSThe j1939 authors  * Since rtnetlink, no real bus is used.
119d71dd0cSThe j1939 authors  */
129d71dd0cSThe j1939 authors 
139d71dd0cSThe j1939 authors #include <net/sock.h>
149d71dd0cSThe j1939 authors 
159d71dd0cSThe j1939 authors #include "j1939-priv.h"
169d71dd0cSThe j1939 authors 
__j1939_ecu_release(struct kref * kref)179d71dd0cSThe j1939 authors static void __j1939_ecu_release(struct kref *kref)
189d71dd0cSThe j1939 authors {
199d71dd0cSThe j1939 authors 	struct j1939_ecu *ecu = container_of(kref, struct j1939_ecu, kref);
209d71dd0cSThe j1939 authors 	struct j1939_priv *priv = ecu->priv;
219d71dd0cSThe j1939 authors 
229d71dd0cSThe j1939 authors 	list_del(&ecu->list);
239d71dd0cSThe j1939 authors 	kfree(ecu);
249d71dd0cSThe j1939 authors 	j1939_priv_put(priv);
259d71dd0cSThe j1939 authors }
269d71dd0cSThe j1939 authors 
j1939_ecu_put(struct j1939_ecu * ecu)279d71dd0cSThe j1939 authors void j1939_ecu_put(struct j1939_ecu *ecu)
289d71dd0cSThe j1939 authors {
299d71dd0cSThe j1939 authors 	kref_put(&ecu->kref, __j1939_ecu_release);
309d71dd0cSThe j1939 authors }
319d71dd0cSThe j1939 authors 
j1939_ecu_get(struct j1939_ecu * ecu)329d71dd0cSThe j1939 authors static void j1939_ecu_get(struct j1939_ecu *ecu)
339d71dd0cSThe j1939 authors {
349d71dd0cSThe j1939 authors 	kref_get(&ecu->kref);
359d71dd0cSThe j1939 authors }
369d71dd0cSThe j1939 authors 
j1939_ecu_is_mapped_locked(struct j1939_ecu * ecu)379d71dd0cSThe j1939 authors static bool j1939_ecu_is_mapped_locked(struct j1939_ecu *ecu)
389d71dd0cSThe j1939 authors {
399d71dd0cSThe j1939 authors 	struct j1939_priv *priv = ecu->priv;
409d71dd0cSThe j1939 authors 
419d71dd0cSThe j1939 authors 	lockdep_assert_held(&priv->lock);
429d71dd0cSThe j1939 authors 
439d71dd0cSThe j1939 authors 	return j1939_ecu_find_by_addr_locked(priv, ecu->addr) == ecu;
449d71dd0cSThe j1939 authors }
459d71dd0cSThe j1939 authors 
469d71dd0cSThe j1939 authors /* ECU device interface */
479d71dd0cSThe j1939 authors /* map ECU to a bus address space */
j1939_ecu_map_locked(struct j1939_ecu * ecu)489d71dd0cSThe j1939 authors static void j1939_ecu_map_locked(struct j1939_ecu *ecu)
499d71dd0cSThe j1939 authors {
509d71dd0cSThe j1939 authors 	struct j1939_priv *priv = ecu->priv;
519d71dd0cSThe j1939 authors 	struct j1939_addr_ent *ent;
529d71dd0cSThe j1939 authors 
539d71dd0cSThe j1939 authors 	lockdep_assert_held(&priv->lock);
549d71dd0cSThe j1939 authors 
559d71dd0cSThe j1939 authors 	if (!j1939_address_is_unicast(ecu->addr))
569d71dd0cSThe j1939 authors 		return;
579d71dd0cSThe j1939 authors 
589d71dd0cSThe j1939 authors 	ent = &priv->ents[ecu->addr];
599d71dd0cSThe j1939 authors 
609d71dd0cSThe j1939 authors 	if (ent->ecu) {
619d71dd0cSThe j1939 authors 		netdev_warn(priv->ndev, "Trying to map already mapped ECU, addr: 0x%02x, name: 0x%016llx. Skip it.\n",
629d71dd0cSThe j1939 authors 			    ecu->addr, ecu->name);
639d71dd0cSThe j1939 authors 		return;
649d71dd0cSThe j1939 authors 	}
659d71dd0cSThe j1939 authors 
669d71dd0cSThe j1939 authors 	j1939_ecu_get(ecu);
679d71dd0cSThe j1939 authors 	ent->ecu = ecu;
689d71dd0cSThe j1939 authors 	ent->nusers += ecu->nusers;
699d71dd0cSThe j1939 authors }
709d71dd0cSThe j1939 authors 
719d71dd0cSThe j1939 authors /* unmap ECU from a bus address space */
j1939_ecu_unmap_locked(struct j1939_ecu * ecu)729d71dd0cSThe j1939 authors void j1939_ecu_unmap_locked(struct j1939_ecu *ecu)
739d71dd0cSThe j1939 authors {
749d71dd0cSThe j1939 authors 	struct j1939_priv *priv = ecu->priv;
759d71dd0cSThe j1939 authors 	struct j1939_addr_ent *ent;
769d71dd0cSThe j1939 authors 
779d71dd0cSThe j1939 authors 	lockdep_assert_held(&priv->lock);
789d71dd0cSThe j1939 authors 
799d71dd0cSThe j1939 authors 	if (!j1939_address_is_unicast(ecu->addr))
809d71dd0cSThe j1939 authors 		return;
819d71dd0cSThe j1939 authors 
829d71dd0cSThe j1939 authors 	if (!j1939_ecu_is_mapped_locked(ecu))
839d71dd0cSThe j1939 authors 		return;
849d71dd0cSThe j1939 authors 
859d71dd0cSThe j1939 authors 	ent = &priv->ents[ecu->addr];
869d71dd0cSThe j1939 authors 	ent->ecu = NULL;
879d71dd0cSThe j1939 authors 	ent->nusers -= ecu->nusers;
889d71dd0cSThe j1939 authors 	j1939_ecu_put(ecu);
899d71dd0cSThe j1939 authors }
909d71dd0cSThe j1939 authors 
j1939_ecu_unmap(struct j1939_ecu * ecu)919d71dd0cSThe j1939 authors void j1939_ecu_unmap(struct j1939_ecu *ecu)
929d71dd0cSThe j1939 authors {
939d71dd0cSThe j1939 authors 	write_lock_bh(&ecu->priv->lock);
949d71dd0cSThe j1939 authors 	j1939_ecu_unmap_locked(ecu);
959d71dd0cSThe j1939 authors 	write_unlock_bh(&ecu->priv->lock);
969d71dd0cSThe j1939 authors }
979d71dd0cSThe j1939 authors 
j1939_ecu_unmap_all(struct j1939_priv * priv)989d71dd0cSThe j1939 authors void j1939_ecu_unmap_all(struct j1939_priv *priv)
999d71dd0cSThe j1939 authors {
1009d71dd0cSThe j1939 authors 	int i;
1019d71dd0cSThe j1939 authors 
1029d71dd0cSThe j1939 authors 	write_lock_bh(&priv->lock);
1039d71dd0cSThe j1939 authors 	for (i = 0; i < ARRAY_SIZE(priv->ents); i++)
1049d71dd0cSThe j1939 authors 		if (priv->ents[i].ecu)
1059d71dd0cSThe j1939 authors 			j1939_ecu_unmap_locked(priv->ents[i].ecu);
1069d71dd0cSThe j1939 authors 	write_unlock_bh(&priv->lock);
1079d71dd0cSThe j1939 authors }
1089d71dd0cSThe j1939 authors 
j1939_ecu_timer_start(struct j1939_ecu * ecu)1099d71dd0cSThe j1939 authors void j1939_ecu_timer_start(struct j1939_ecu *ecu)
1109d71dd0cSThe j1939 authors {
1119d71dd0cSThe j1939 authors 	/* The ECU is held here and released in the
1129d71dd0cSThe j1939 authors 	 * j1939_ecu_timer_handler() or j1939_ecu_timer_cancel().
1139d71dd0cSThe j1939 authors 	 */
1149d71dd0cSThe j1939 authors 	j1939_ecu_get(ecu);
1159d71dd0cSThe j1939 authors 
1169d71dd0cSThe j1939 authors 	/* Schedule timer in 250 msec to commit address change. */
1179d71dd0cSThe j1939 authors 	hrtimer_start(&ecu->ac_timer, ms_to_ktime(250),
1189d71dd0cSThe j1939 authors 		      HRTIMER_MODE_REL_SOFT);
1199d71dd0cSThe j1939 authors }
1209d71dd0cSThe j1939 authors 
j1939_ecu_timer_cancel(struct j1939_ecu * ecu)1219d71dd0cSThe j1939 authors void j1939_ecu_timer_cancel(struct j1939_ecu *ecu)
1229d71dd0cSThe j1939 authors {
1239d71dd0cSThe j1939 authors 	if (hrtimer_cancel(&ecu->ac_timer))
1249d71dd0cSThe j1939 authors 		j1939_ecu_put(ecu);
1259d71dd0cSThe j1939 authors }
1269d71dd0cSThe j1939 authors 
j1939_ecu_timer_handler(struct hrtimer * hrtimer)1279d71dd0cSThe j1939 authors static enum hrtimer_restart j1939_ecu_timer_handler(struct hrtimer *hrtimer)
1289d71dd0cSThe j1939 authors {
1299d71dd0cSThe j1939 authors 	struct j1939_ecu *ecu =
1309d71dd0cSThe j1939 authors 		container_of(hrtimer, struct j1939_ecu, ac_timer);
1319d71dd0cSThe j1939 authors 	struct j1939_priv *priv = ecu->priv;
1329d71dd0cSThe j1939 authors 
1339d71dd0cSThe j1939 authors 	write_lock_bh(&priv->lock);
1349d71dd0cSThe j1939 authors 	/* TODO: can we test if ecu->addr is unicast before starting
1359d71dd0cSThe j1939 authors 	 * the timer?
1369d71dd0cSThe j1939 authors 	 */
1379d71dd0cSThe j1939 authors 	j1939_ecu_map_locked(ecu);
1389d71dd0cSThe j1939 authors 
1399d71dd0cSThe j1939 authors 	/* The corresponding j1939_ecu_get() is in
1409d71dd0cSThe j1939 authors 	 * j1939_ecu_timer_start().
1419d71dd0cSThe j1939 authors 	 */
1429d71dd0cSThe j1939 authors 	j1939_ecu_put(ecu);
1439d71dd0cSThe j1939 authors 	write_unlock_bh(&priv->lock);
1449d71dd0cSThe j1939 authors 
1459d71dd0cSThe j1939 authors 	return HRTIMER_NORESTART;
1469d71dd0cSThe j1939 authors }
1479d71dd0cSThe j1939 authors 
j1939_ecu_create_locked(struct j1939_priv * priv,name_t name)1489d71dd0cSThe j1939 authors struct j1939_ecu *j1939_ecu_create_locked(struct j1939_priv *priv, name_t name)
1499d71dd0cSThe j1939 authors {
1509d71dd0cSThe j1939 authors 	struct j1939_ecu *ecu;
1519d71dd0cSThe j1939 authors 
1529d71dd0cSThe j1939 authors 	lockdep_assert_held(&priv->lock);
1539d71dd0cSThe j1939 authors 
1549d71dd0cSThe j1939 authors 	ecu = kzalloc(sizeof(*ecu), gfp_any());
1559d71dd0cSThe j1939 authors 	if (!ecu)
1569d71dd0cSThe j1939 authors 		return ERR_PTR(-ENOMEM);
1579d71dd0cSThe j1939 authors 	kref_init(&ecu->kref);
1589d71dd0cSThe j1939 authors 	ecu->addr = J1939_IDLE_ADDR;
1599d71dd0cSThe j1939 authors 	ecu->name = name;
1609d71dd0cSThe j1939 authors 
1619d71dd0cSThe j1939 authors 	hrtimer_init(&ecu->ac_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
1629d71dd0cSThe j1939 authors 	ecu->ac_timer.function = j1939_ecu_timer_handler;
1639d71dd0cSThe j1939 authors 	INIT_LIST_HEAD(&ecu->list);
1649d71dd0cSThe j1939 authors 
1659d71dd0cSThe j1939 authors 	j1939_priv_get(priv);
1669d71dd0cSThe j1939 authors 	ecu->priv = priv;
1679d71dd0cSThe j1939 authors 	list_add_tail(&ecu->list, &priv->ecus);
1689d71dd0cSThe j1939 authors 
1699d71dd0cSThe j1939 authors 	return ecu;
1709d71dd0cSThe j1939 authors }
1719d71dd0cSThe j1939 authors 
j1939_ecu_find_by_addr_locked(struct j1939_priv * priv,u8 addr)1729d71dd0cSThe j1939 authors struct j1939_ecu *j1939_ecu_find_by_addr_locked(struct j1939_priv *priv,
1739d71dd0cSThe j1939 authors 						u8 addr)
1749d71dd0cSThe j1939 authors {
1759d71dd0cSThe j1939 authors 	lockdep_assert_held(&priv->lock);
1769d71dd0cSThe j1939 authors 
1779d71dd0cSThe j1939 authors 	return priv->ents[addr].ecu;
1789d71dd0cSThe j1939 authors }
1799d71dd0cSThe j1939 authors 
j1939_ecu_get_by_addr_locked(struct j1939_priv * priv,u8 addr)1809d71dd0cSThe j1939 authors struct j1939_ecu *j1939_ecu_get_by_addr_locked(struct j1939_priv *priv, u8 addr)
1819d71dd0cSThe j1939 authors {
1829d71dd0cSThe j1939 authors 	struct j1939_ecu *ecu;
1839d71dd0cSThe j1939 authors 
1849d71dd0cSThe j1939 authors 	lockdep_assert_held(&priv->lock);
1859d71dd0cSThe j1939 authors 
1869d71dd0cSThe j1939 authors 	if (!j1939_address_is_unicast(addr))
1879d71dd0cSThe j1939 authors 		return NULL;
1889d71dd0cSThe j1939 authors 
1899d71dd0cSThe j1939 authors 	ecu = j1939_ecu_find_by_addr_locked(priv, addr);
1909d71dd0cSThe j1939 authors 	if (ecu)
1919d71dd0cSThe j1939 authors 		j1939_ecu_get(ecu);
1929d71dd0cSThe j1939 authors 
1939d71dd0cSThe j1939 authors 	return ecu;
1949d71dd0cSThe j1939 authors }
1959d71dd0cSThe j1939 authors 
j1939_ecu_get_by_addr(struct j1939_priv * priv,u8 addr)1969d71dd0cSThe j1939 authors struct j1939_ecu *j1939_ecu_get_by_addr(struct j1939_priv *priv, u8 addr)
1979d71dd0cSThe j1939 authors {
1989d71dd0cSThe j1939 authors 	struct j1939_ecu *ecu;
1999d71dd0cSThe j1939 authors 
2009d71dd0cSThe j1939 authors 	read_lock_bh(&priv->lock);
2019d71dd0cSThe j1939 authors 	ecu = j1939_ecu_get_by_addr_locked(priv, addr);
2029d71dd0cSThe j1939 authors 	read_unlock_bh(&priv->lock);
2039d71dd0cSThe j1939 authors 
2049d71dd0cSThe j1939 authors 	return ecu;
2059d71dd0cSThe j1939 authors }
2069d71dd0cSThe j1939 authors 
2079d71dd0cSThe j1939 authors /* get pointer to ecu without increasing ref counter */
j1939_ecu_find_by_name_locked(struct j1939_priv * priv,name_t name)2089d71dd0cSThe j1939 authors static struct j1939_ecu *j1939_ecu_find_by_name_locked(struct j1939_priv *priv,
2099d71dd0cSThe j1939 authors 						       name_t name)
2109d71dd0cSThe j1939 authors {
2119d71dd0cSThe j1939 authors 	struct j1939_ecu *ecu;
2129d71dd0cSThe j1939 authors 
2139d71dd0cSThe j1939 authors 	lockdep_assert_held(&priv->lock);
2149d71dd0cSThe j1939 authors 
2159d71dd0cSThe j1939 authors 	list_for_each_entry(ecu, &priv->ecus, list) {
2169d71dd0cSThe j1939 authors 		if (ecu->name == name)
2179d71dd0cSThe j1939 authors 			return ecu;
2189d71dd0cSThe j1939 authors 	}
2199d71dd0cSThe j1939 authors 
2209d71dd0cSThe j1939 authors 	return NULL;
2219d71dd0cSThe j1939 authors }
2229d71dd0cSThe j1939 authors 
j1939_ecu_get_by_name_locked(struct j1939_priv * priv,name_t name)2239d71dd0cSThe j1939 authors struct j1939_ecu *j1939_ecu_get_by_name_locked(struct j1939_priv *priv,
2249d71dd0cSThe j1939 authors 					       name_t name)
2259d71dd0cSThe j1939 authors {
2269d71dd0cSThe j1939 authors 	struct j1939_ecu *ecu;
2279d71dd0cSThe j1939 authors 
2289d71dd0cSThe j1939 authors 	lockdep_assert_held(&priv->lock);
2299d71dd0cSThe j1939 authors 
2309d71dd0cSThe j1939 authors 	if (!name)
2319d71dd0cSThe j1939 authors 		return NULL;
2329d71dd0cSThe j1939 authors 
2339d71dd0cSThe j1939 authors 	ecu = j1939_ecu_find_by_name_locked(priv, name);
2349d71dd0cSThe j1939 authors 	if (ecu)
2359d71dd0cSThe j1939 authors 		j1939_ecu_get(ecu);
2369d71dd0cSThe j1939 authors 
2379d71dd0cSThe j1939 authors 	return ecu;
2389d71dd0cSThe j1939 authors }
2399d71dd0cSThe j1939 authors 
j1939_ecu_get_by_name(struct j1939_priv * priv,name_t name)2409d71dd0cSThe j1939 authors struct j1939_ecu *j1939_ecu_get_by_name(struct j1939_priv *priv, name_t name)
2419d71dd0cSThe j1939 authors {
2429d71dd0cSThe j1939 authors 	struct j1939_ecu *ecu;
2439d71dd0cSThe j1939 authors 
2449d71dd0cSThe j1939 authors 	read_lock_bh(&priv->lock);
2459d71dd0cSThe j1939 authors 	ecu = j1939_ecu_get_by_name_locked(priv, name);
2469d71dd0cSThe j1939 authors 	read_unlock_bh(&priv->lock);
2479d71dd0cSThe j1939 authors 
2489d71dd0cSThe j1939 authors 	return ecu;
2499d71dd0cSThe j1939 authors }
2509d71dd0cSThe j1939 authors 
j1939_name_to_addr(struct j1939_priv * priv,name_t name)2519d71dd0cSThe j1939 authors u8 j1939_name_to_addr(struct j1939_priv *priv, name_t name)
2529d71dd0cSThe j1939 authors {
2539d71dd0cSThe j1939 authors 	struct j1939_ecu *ecu;
2549d71dd0cSThe j1939 authors 	int addr = J1939_IDLE_ADDR;
2559d71dd0cSThe j1939 authors 
2569d71dd0cSThe j1939 authors 	if (!name)
2579d71dd0cSThe j1939 authors 		return J1939_NO_ADDR;
2589d71dd0cSThe j1939 authors 
2599d71dd0cSThe j1939 authors 	read_lock_bh(&priv->lock);
2609d71dd0cSThe j1939 authors 	ecu = j1939_ecu_find_by_name_locked(priv, name);
2619d71dd0cSThe j1939 authors 	if (ecu && j1939_ecu_is_mapped_locked(ecu))
2629d71dd0cSThe j1939 authors 		/* ecu's SA is registered */
2639d71dd0cSThe j1939 authors 		addr = ecu->addr;
2649d71dd0cSThe j1939 authors 
2659d71dd0cSThe j1939 authors 	read_unlock_bh(&priv->lock);
2669d71dd0cSThe j1939 authors 
2679d71dd0cSThe j1939 authors 	return addr;
2689d71dd0cSThe j1939 authors }
2699d71dd0cSThe j1939 authors 
2709d71dd0cSThe j1939 authors /* TX addr/name accounting
2719d71dd0cSThe j1939 authors  * Transport protocol needs to know if a SA is local or not
2729d71dd0cSThe j1939 authors  * These functions originate from userspace manipulating sockets,
2739d71dd0cSThe j1939 authors  * so locking is straigforward
2749d71dd0cSThe j1939 authors  */
2759d71dd0cSThe j1939 authors 
j1939_local_ecu_get(struct j1939_priv * priv,name_t name,u8 sa)2769d71dd0cSThe j1939 authors int j1939_local_ecu_get(struct j1939_priv *priv, name_t name, u8 sa)
2779d71dd0cSThe j1939 authors {
2789d71dd0cSThe j1939 authors 	struct j1939_ecu *ecu;
2799d71dd0cSThe j1939 authors 	int err = 0;
2809d71dd0cSThe j1939 authors 
2819d71dd0cSThe j1939 authors 	write_lock_bh(&priv->lock);
2829d71dd0cSThe j1939 authors 
2839d71dd0cSThe j1939 authors 	if (j1939_address_is_unicast(sa))
2849d71dd0cSThe j1939 authors 		priv->ents[sa].nusers++;
2859d71dd0cSThe j1939 authors 
2869d71dd0cSThe j1939 authors 	if (!name)
2879d71dd0cSThe j1939 authors 		goto done;
2889d71dd0cSThe j1939 authors 
2899d71dd0cSThe j1939 authors 	ecu = j1939_ecu_get_by_name_locked(priv, name);
2909d71dd0cSThe j1939 authors 	if (!ecu)
2919d71dd0cSThe j1939 authors 		ecu = j1939_ecu_create_locked(priv, name);
2929d71dd0cSThe j1939 authors 	err = PTR_ERR_OR_ZERO(ecu);
2939d71dd0cSThe j1939 authors 	if (err)
2949d71dd0cSThe j1939 authors 		goto done;
2959d71dd0cSThe j1939 authors 
2969d71dd0cSThe j1939 authors 	ecu->nusers++;
2979d71dd0cSThe j1939 authors 	/* TODO: do we care if ecu->addr != sa? */
2989d71dd0cSThe j1939 authors 	if (j1939_ecu_is_mapped_locked(ecu))
2999d71dd0cSThe j1939 authors 		/* ecu's sa is active already */
3009d71dd0cSThe j1939 authors 		priv->ents[ecu->addr].nusers++;
3019d71dd0cSThe j1939 authors 
3029d71dd0cSThe j1939 authors  done:
3039d71dd0cSThe j1939 authors 	write_unlock_bh(&priv->lock);
3049d71dd0cSThe j1939 authors 
3059d71dd0cSThe j1939 authors 	return err;
3069d71dd0cSThe j1939 authors }
3079d71dd0cSThe j1939 authors 
j1939_local_ecu_put(struct j1939_priv * priv,name_t name,u8 sa)3089d71dd0cSThe j1939 authors void j1939_local_ecu_put(struct j1939_priv *priv, name_t name, u8 sa)
3099d71dd0cSThe j1939 authors {
3109d71dd0cSThe j1939 authors 	struct j1939_ecu *ecu;
3119d71dd0cSThe j1939 authors 
3129d71dd0cSThe j1939 authors 	write_lock_bh(&priv->lock);
3139d71dd0cSThe j1939 authors 
3149d71dd0cSThe j1939 authors 	if (j1939_address_is_unicast(sa))
3159d71dd0cSThe j1939 authors 		priv->ents[sa].nusers--;
3169d71dd0cSThe j1939 authors 
3179d71dd0cSThe j1939 authors 	if (!name)
3189d71dd0cSThe j1939 authors 		goto done;
3199d71dd0cSThe j1939 authors 
3209d71dd0cSThe j1939 authors 	ecu = j1939_ecu_find_by_name_locked(priv, name);
3219d71dd0cSThe j1939 authors 	if (WARN_ON_ONCE(!ecu))
3229d71dd0cSThe j1939 authors 		goto done;
3239d71dd0cSThe j1939 authors 
3249d71dd0cSThe j1939 authors 	ecu->nusers--;
3259d71dd0cSThe j1939 authors 	/* TODO: do we care if ecu->addr != sa? */
3269d71dd0cSThe j1939 authors 	if (j1939_ecu_is_mapped_locked(ecu))
3279d71dd0cSThe j1939 authors 		/* ecu's sa is active already */
3289d71dd0cSThe j1939 authors 		priv->ents[ecu->addr].nusers--;
3299d71dd0cSThe j1939 authors 	j1939_ecu_put(ecu);
3309d71dd0cSThe j1939 authors 
3319d71dd0cSThe j1939 authors  done:
3329d71dd0cSThe j1939 authors 	write_unlock_bh(&priv->lock);
3339d71dd0cSThe j1939 authors }
334