Root/
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 <linux/proc_fs.h> |
31 | #include <linux/if_arp.h> |
32 | #include <net/sock.h> |
33 | #include <net/netns/generic.h> |
34 | #include <net/phonet/pn_dev.h> |
35 | |
36 | struct phonet_net { |
37 | struct phonet_device_list pndevs; |
38 | }; |
39 | |
40 | int phonet_net_id; |
41 | |
42 | struct phonet_device_list *phonet_device_list(struct net *net) |
43 | { |
44 | struct phonet_net *pnn = net_generic(net, phonet_net_id); |
45 | return &pnn->pndevs; |
46 | } |
47 | |
48 | /* Allocate new Phonet device. */ |
49 | static struct phonet_device *__phonet_device_alloc(struct net_device *dev) |
50 | { |
51 | struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); |
52 | struct phonet_device *pnd = kmalloc(sizeof(*pnd), GFP_ATOMIC); |
53 | if (pnd == NULL) |
54 | return NULL; |
55 | pnd->netdev = dev; |
56 | bitmap_zero(pnd->addrs, 64); |
57 | |
58 | list_add(&pnd->list, &pndevs->list); |
59 | return pnd; |
60 | } |
61 | |
62 | static struct phonet_device *__phonet_get(struct net_device *dev) |
63 | { |
64 | struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); |
65 | struct phonet_device *pnd; |
66 | |
67 | list_for_each_entry(pnd, &pndevs->list, list) { |
68 | if (pnd->netdev == dev) |
69 | return pnd; |
70 | } |
71 | return NULL; |
72 | } |
73 | |
74 | static void phonet_device_destroy(struct net_device *dev) |
75 | { |
76 | struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); |
77 | struct phonet_device *pnd; |
78 | |
79 | ASSERT_RTNL(); |
80 | |
81 | spin_lock_bh(&pndevs->lock); |
82 | pnd = __phonet_get(dev); |
83 | if (pnd) |
84 | list_del(&pnd->list); |
85 | spin_unlock_bh(&pndevs->lock); |
86 | |
87 | if (pnd) { |
88 | u8 addr; |
89 | |
90 | for (addr = find_first_bit(pnd->addrs, 64); addr < 64; |
91 | addr = find_next_bit(pnd->addrs, 64, 1+addr)) |
92 | phonet_address_notify(RTM_DELADDR, dev, addr); |
93 | kfree(pnd); |
94 | } |
95 | } |
96 | |
97 | struct net_device *phonet_device_get(struct net *net) |
98 | { |
99 | struct phonet_device_list *pndevs = phonet_device_list(net); |
100 | struct phonet_device *pnd; |
101 | struct net_device *dev = NULL; |
102 | |
103 | spin_lock_bh(&pndevs->lock); |
104 | list_for_each_entry(pnd, &pndevs->list, list) { |
105 | dev = pnd->netdev; |
106 | BUG_ON(!dev); |
107 | |
108 | if ((dev->reg_state == NETREG_REGISTERED) && |
109 | ((pnd->netdev->flags & IFF_UP)) == IFF_UP) |
110 | break; |
111 | dev = NULL; |
112 | } |
113 | if (dev) |
114 | dev_hold(dev); |
115 | spin_unlock_bh(&pndevs->lock); |
116 | return dev; |
117 | } |
118 | |
119 | int phonet_address_add(struct net_device *dev, u8 addr) |
120 | { |
121 | struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); |
122 | struct phonet_device *pnd; |
123 | int err = 0; |
124 | |
125 | spin_lock_bh(&pndevs->lock); |
126 | /* Find or create Phonet-specific device data */ |
127 | pnd = __phonet_get(dev); |
128 | if (pnd == NULL) |
129 | pnd = __phonet_device_alloc(dev); |
130 | if (unlikely(pnd == NULL)) |
131 | err = -ENOMEM; |
132 | else if (test_and_set_bit(addr >> 2, pnd->addrs)) |
133 | err = -EEXIST; |
134 | spin_unlock_bh(&pndevs->lock); |
135 | return err; |
136 | } |
137 | |
138 | int phonet_address_del(struct net_device *dev, u8 addr) |
139 | { |
140 | struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); |
141 | struct phonet_device *pnd; |
142 | int err = 0; |
143 | |
144 | spin_lock_bh(&pndevs->lock); |
145 | pnd = __phonet_get(dev); |
146 | if (!pnd || !test_and_clear_bit(addr >> 2, pnd->addrs)) |
147 | err = -EADDRNOTAVAIL; |
148 | else if (bitmap_empty(pnd->addrs, 64)) { |
149 | list_del(&pnd->list); |
150 | kfree(pnd); |
151 | } |
152 | spin_unlock_bh(&pndevs->lock); |
153 | return err; |
154 | } |
155 | |
156 | /* Gets a source address toward a destination, through a interface. */ |
157 | u8 phonet_address_get(struct net_device *dev, u8 addr) |
158 | { |
159 | struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); |
160 | struct phonet_device *pnd; |
161 | |
162 | spin_lock_bh(&pndevs->lock); |
163 | pnd = __phonet_get(dev); |
164 | if (pnd) { |
165 | BUG_ON(bitmap_empty(pnd->addrs, 64)); |
166 | |
167 | /* Use same source address as destination, if possible */ |
168 | if (!test_bit(addr >> 2, pnd->addrs)) |
169 | addr = find_first_bit(pnd->addrs, 64) << 2; |
170 | } else |
171 | addr = PN_NO_ADDR; |
172 | spin_unlock_bh(&pndevs->lock); |
173 | return addr; |
174 | } |
175 | |
176 | int phonet_address_lookup(struct net *net, u8 addr) |
177 | { |
178 | struct phonet_device_list *pndevs = phonet_device_list(net); |
179 | struct phonet_device *pnd; |
180 | int err = -EADDRNOTAVAIL; |
181 | |
182 | spin_lock_bh(&pndevs->lock); |
183 | list_for_each_entry(pnd, &pndevs->list, list) { |
184 | /* Don't allow unregistering devices! */ |
185 | if ((pnd->netdev->reg_state != NETREG_REGISTERED) || |
186 | ((pnd->netdev->flags & IFF_UP)) != IFF_UP) |
187 | continue; |
188 | |
189 | if (test_bit(addr >> 2, pnd->addrs)) { |
190 | err = 0; |
191 | goto found; |
192 | } |
193 | } |
194 | found: |
195 | spin_unlock_bh(&pndevs->lock); |
196 | return err; |
197 | } |
198 | |
199 | /* automatically configure a Phonet device, if supported */ |
200 | static int phonet_device_autoconf(struct net_device *dev) |
201 | { |
202 | struct if_phonet_req req; |
203 | int ret; |
204 | |
205 | if (!dev->netdev_ops->ndo_do_ioctl) |
206 | return -EOPNOTSUPP; |
207 | |
208 | ret = dev->netdev_ops->ndo_do_ioctl(dev, (struct ifreq *)&req, |
209 | SIOCPNGAUTOCONF); |
210 | if (ret < 0) |
211 | return ret; |
212 | |
213 | ASSERT_RTNL(); |
214 | ret = phonet_address_add(dev, req.ifr_phonet_autoconf.device); |
215 | if (ret) |
216 | return ret; |
217 | phonet_address_notify(RTM_NEWADDR, dev, |
218 | req.ifr_phonet_autoconf.device); |
219 | return 0; |
220 | } |
221 | |
222 | /* notify Phonet of device events */ |
223 | static int phonet_device_notify(struct notifier_block *me, unsigned long what, |
224 | void *arg) |
225 | { |
226 | struct net_device *dev = arg; |
227 | |
228 | switch (what) { |
229 | case NETDEV_REGISTER: |
230 | if (dev->type == ARPHRD_PHONET) |
231 | phonet_device_autoconf(dev); |
232 | break; |
233 | case NETDEV_UNREGISTER: |
234 | phonet_device_destroy(dev); |
235 | break; |
236 | } |
237 | return 0; |
238 | |
239 | } |
240 | |
241 | static struct notifier_block phonet_device_notifier = { |
242 | .notifier_call = phonet_device_notify, |
243 | .priority = 0, |
244 | }; |
245 | |
246 | /* Per-namespace Phonet devices handling */ |
247 | static int phonet_init_net(struct net *net) |
248 | { |
249 | struct phonet_net *pnn = kmalloc(sizeof(*pnn), GFP_KERNEL); |
250 | if (!pnn) |
251 | return -ENOMEM; |
252 | |
253 | if (!proc_net_fops_create(net, "phonet", 0, &pn_sock_seq_fops)) { |
254 | kfree(pnn); |
255 | return -ENOMEM; |
256 | } |
257 | |
258 | INIT_LIST_HEAD(&pnn->pndevs.list); |
259 | spin_lock_init(&pnn->pndevs.lock); |
260 | net_assign_generic(net, phonet_net_id, pnn); |
261 | return 0; |
262 | } |
263 | |
264 | static void phonet_exit_net(struct net *net) |
265 | { |
266 | struct phonet_net *pnn = net_generic(net, phonet_net_id); |
267 | struct net_device *dev; |
268 | |
269 | rtnl_lock(); |
270 | for_each_netdev(net, dev) |
271 | phonet_device_destroy(dev); |
272 | rtnl_unlock(); |
273 | |
274 | proc_net_remove(net, "phonet"); |
275 | kfree(pnn); |
276 | } |
277 | |
278 | static struct pernet_operations phonet_net_ops = { |
279 | .init = phonet_init_net, |
280 | .exit = phonet_exit_net, |
281 | }; |
282 | |
283 | /* Initialize Phonet devices list */ |
284 | int __init phonet_device_init(void) |
285 | { |
286 | int err = register_pernet_gen_device(&phonet_net_id, &phonet_net_ops); |
287 | if (err) |
288 | return err; |
289 | |
290 | register_netdevice_notifier(&phonet_device_notifier); |
291 | err = phonet_netlink_register(); |
292 | if (err) |
293 | phonet_device_exit(); |
294 | return err; |
295 | } |
296 | |
297 | void phonet_device_exit(void) |
298 | { |
299 | rtnl_unregister_all(PF_PHONET); |
300 | unregister_netdevice_notifier(&phonet_device_notifier); |
301 | unregister_pernet_gen_device(phonet_net_id, &phonet_net_ops); |
302 | } |
303 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9