]>
Commit | Line | Data |
---|---|---|
f8ff6028 RDC |
1 | /* |
2 | * File: pn_dev.c | |
3 | * | |
4 | * Phonet network device | |
5 | * | |
6 | * Copyright (C) 2008 Nokia Corporation. | |
7 | * | |
8 | * Contact: Remi Denis-Courmont <remi.denis-courmont@nokia.com> | |
9 | * Original author: Sakari Ailus <sakari.ailus@nokia.com> | |
10 | * | |
11 | * This program is free software; you can redistribute it and/or | |
12 | * modify it under the terms of the GNU General Public License | |
13 | * version 2 as published by the Free Software Foundation. | |
14 | * | |
15 | * This program is distributed in the hope that it will be useful, but | |
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
18 | * General Public License for more details. | |
19 | * | |
20 | * You should have received a copy of the GNU General Public License | |
21 | * along with this program; if not, write to the Free Software | |
22 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | |
23 | * 02110-1301 USA | |
24 | */ | |
25 | ||
26 | #include <linux/kernel.h> | |
27 | #include <linux/net.h> | |
28 | #include <linux/netdevice.h> | |
29 | #include <linux/phonet.h> | |
30 | #include <net/sock.h> | |
31 | #include <net/phonet/pn_dev.h> | |
32 | ||
33 | /* when accessing, remember to lock with spin_lock(&pndevs.lock); */ | |
34 | struct phonet_device_list pndevs = { | |
35 | .list = LIST_HEAD_INIT(pndevs.list), | |
36 | .lock = __SPIN_LOCK_UNLOCKED(pndevs.lock), | |
37 | }; | |
38 | ||
39 | /* Allocate new Phonet device. */ | |
40 | static struct phonet_device *__phonet_device_alloc(struct net_device *dev) | |
41 | { | |
42 | struct phonet_device *pnd = kmalloc(sizeof(*pnd), GFP_ATOMIC); | |
43 | if (pnd == NULL) | |
44 | return NULL; | |
45 | pnd->netdev = dev; | |
46 | bitmap_zero(pnd->addrs, 64); | |
47 | ||
48 | list_add(&pnd->list, &pndevs.list); | |
49 | return pnd; | |
50 | } | |
51 | ||
52 | static struct phonet_device *__phonet_get(struct net_device *dev) | |
53 | { | |
54 | struct phonet_device *pnd; | |
55 | ||
56 | list_for_each_entry(pnd, &pndevs.list, list) { | |
57 | if (pnd->netdev == dev) | |
58 | return pnd; | |
59 | } | |
60 | return NULL; | |
61 | } | |
62 | ||
63 | static void __phonet_device_free(struct phonet_device *pnd) | |
64 | { | |
65 | list_del(&pnd->list); | |
66 | kfree(pnd); | |
67 | } | |
68 | ||
69 | struct net_device *phonet_device_get(struct net *net) | |
70 | { | |
71 | struct phonet_device *pnd; | |
72 | struct net_device *dev; | |
73 | ||
74 | spin_lock_bh(&pndevs.lock); | |
75 | list_for_each_entry(pnd, &pndevs.list, list) { | |
76 | dev = pnd->netdev; | |
77 | BUG_ON(!dev); | |
78 | ||
52404881 | 79 | if (net_eq(dev_net(dev), net) && |
f8ff6028 RDC |
80 | (dev->reg_state == NETREG_REGISTERED) && |
81 | ((pnd->netdev->flags & IFF_UP)) == IFF_UP) | |
82 | break; | |
83 | dev = NULL; | |
84 | } | |
85 | if (dev) | |
86 | dev_hold(dev); | |
87 | spin_unlock_bh(&pndevs.lock); | |
88 | return dev; | |
89 | } | |
90 | ||
91 | int phonet_address_add(struct net_device *dev, u8 addr) | |
92 | { | |
93 | struct phonet_device *pnd; | |
94 | int err = 0; | |
95 | ||
96 | spin_lock_bh(&pndevs.lock); | |
97 | /* Find or create Phonet-specific device data */ | |
98 | pnd = __phonet_get(dev); | |
99 | if (pnd == NULL) | |
100 | pnd = __phonet_device_alloc(dev); | |
101 | if (unlikely(pnd == NULL)) | |
102 | err = -ENOMEM; | |
103 | else if (test_and_set_bit(addr >> 2, pnd->addrs)) | |
104 | err = -EEXIST; | |
105 | spin_unlock_bh(&pndevs.lock); | |
106 | return err; | |
107 | } | |
108 | ||
109 | int phonet_address_del(struct net_device *dev, u8 addr) | |
110 | { | |
111 | struct phonet_device *pnd; | |
112 | int err = 0; | |
113 | ||
114 | spin_lock_bh(&pndevs.lock); | |
115 | pnd = __phonet_get(dev); | |
116 | if (!pnd || !test_and_clear_bit(addr >> 2, pnd->addrs)) | |
117 | err = -EADDRNOTAVAIL; | |
7e5ab542 | 118 | else if (bitmap_empty(pnd->addrs, 64)) |
f8ff6028 RDC |
119 | __phonet_device_free(pnd); |
120 | spin_unlock_bh(&pndevs.lock); | |
121 | return err; | |
122 | } | |
123 | ||
124 | /* Gets a source address toward a destination, through a interface. */ | |
125 | u8 phonet_address_get(struct net_device *dev, u8 addr) | |
126 | { | |
127 | struct phonet_device *pnd; | |
128 | ||
129 | spin_lock_bh(&pndevs.lock); | |
130 | pnd = __phonet_get(dev); | |
131 | if (pnd) { | |
132 | BUG_ON(bitmap_empty(pnd->addrs, 64)); | |
133 | ||
134 | /* Use same source address as destination, if possible */ | |
135 | if (!test_bit(addr >> 2, pnd->addrs)) | |
136 | addr = find_first_bit(pnd->addrs, 64) << 2; | |
137 | } else | |
138 | addr = PN_NO_ADDR; | |
139 | spin_unlock_bh(&pndevs.lock); | |
140 | return addr; | |
141 | } | |
142 | ||
52404881 | 143 | int phonet_address_lookup(struct net *net, u8 addr) |
f8ff6028 RDC |
144 | { |
145 | struct phonet_device *pnd; | |
146 | ||
147 | spin_lock_bh(&pndevs.lock); | |
148 | list_for_each_entry(pnd, &pndevs.list, list) { | |
52404881 RDC |
149 | if (!net_eq(dev_net(pnd->netdev), net)) |
150 | continue; | |
f8ff6028 RDC |
151 | /* Don't allow unregistering devices! */ |
152 | if ((pnd->netdev->reg_state != NETREG_REGISTERED) || | |
153 | ((pnd->netdev->flags & IFF_UP)) != IFF_UP) | |
154 | continue; | |
155 | ||
156 | if (test_bit(addr >> 2, pnd->addrs)) { | |
157 | spin_unlock_bh(&pndevs.lock); | |
158 | return 0; | |
159 | } | |
160 | } | |
161 | spin_unlock_bh(&pndevs.lock); | |
162 | return -EADDRNOTAVAIL; | |
163 | } | |
164 | ||
165 | /* notify Phonet of device events */ | |
166 | static int phonet_device_notify(struct notifier_block *me, unsigned long what, | |
167 | void *arg) | |
168 | { | |
169 | struct net_device *dev = arg; | |
170 | ||
171 | if (what == NETDEV_UNREGISTER) { | |
172 | struct phonet_device *pnd; | |
173 | ||
174 | /* Destroy phonet-specific device data */ | |
175 | spin_lock_bh(&pndevs.lock); | |
176 | pnd = __phonet_get(dev); | |
177 | if (pnd) | |
178 | __phonet_device_free(pnd); | |
179 | spin_unlock_bh(&pndevs.lock); | |
180 | } | |
181 | return 0; | |
182 | ||
183 | } | |
184 | ||
185 | static struct notifier_block phonet_device_notifier = { | |
186 | .notifier_call = phonet_device_notify, | |
187 | .priority = 0, | |
188 | }; | |
189 | ||
190 | /* Initialize Phonet devices list */ | |
76e02cf6 | 191 | int __init phonet_device_init(void) |
f8ff6028 | 192 | { |
660f706d | 193 | int err; |
194 | ||
f8ff6028 | 195 | register_netdevice_notifier(&phonet_device_notifier); |
660f706d | 196 | err = phonet_netlink_register(); |
197 | if (err) | |
198 | phonet_device_exit(); | |
199 | return err; | |
f8ff6028 RDC |
200 | } |
201 | ||
202 | void phonet_device_exit(void) | |
203 | { | |
204 | struct phonet_device *pnd, *n; | |
205 | ||
206 | rtnl_unregister_all(PF_PHONET); | |
207 | rtnl_lock(); | |
208 | spin_lock_bh(&pndevs.lock); | |
209 | ||
210 | list_for_each_entry_safe(pnd, n, &pndevs.list, list) | |
211 | __phonet_device_free(pnd); | |
212 | ||
213 | spin_unlock_bh(&pndevs.lock); | |
214 | rtnl_unlock(); | |
215 | unregister_netdevice_notifier(&phonet_device_notifier); | |
216 | } |