diff options
author | Remi Denis-Courmont <remi.denis-courmont@nokia.com> | 2008-09-22 23:03:44 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-09-22 23:03:44 -0400 |
commit | f8ff60283de2b6775d7a14619056a08e3083bd40 (patch) | |
tree | aea8a14283aa5b5f7952e6e7b8e4adcc4422c592 /net/phonet | |
parent | 8ead536dec142f27d5b5f72c3994eb39f4741717 (diff) |
Phonet: network device and address handling
This provides support for adding Phonet addresses to and removing
Phonet addresses from network devices.
Signed-off-by: RĂ©mi Denis-Courmont <remi.denis-courmont@nokia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/phonet')
-rw-r--r-- | net/phonet/Makefile | 1 | ||||
-rw-r--r-- | net/phonet/af_phonet.c | 3 | ||||
-rw-r--r-- | net/phonet/pn_dev.c | 208 |
3 files changed, 212 insertions, 0 deletions
diff --git a/net/phonet/Makefile b/net/phonet/Makefile index 5dbff68a6f36..980a3866c9a3 100644 --- a/net/phonet/Makefile +++ b/net/phonet/Makefile | |||
@@ -1,4 +1,5 @@ | |||
1 | obj-$(CONFIG_PHONET) += phonet.o | 1 | obj-$(CONFIG_PHONET) += phonet.o |
2 | 2 | ||
3 | phonet-objs := \ | 3 | phonet-objs := \ |
4 | pn_dev.o \ | ||
4 | af_phonet.o | 5 | af_phonet.o |
diff --git a/net/phonet/af_phonet.c b/net/phonet/af_phonet.c index 0cfea9bc994a..a8ba6f177b29 100644 --- a/net/phonet/af_phonet.c +++ b/net/phonet/af_phonet.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <linux/if_phonet.h> | 31 | #include <linux/if_phonet.h> |
32 | #include <linux/phonet.h> | 32 | #include <linux/phonet.h> |
33 | #include <net/phonet/phonet.h> | 33 | #include <net/phonet/phonet.h> |
34 | #include <net/phonet/pn_dev.h> | ||
34 | 35 | ||
35 | static struct net_proto_family phonet_proto_family; | 36 | static struct net_proto_family phonet_proto_family; |
36 | static struct phonet_protocol *phonet_proto_get(int protocol); | 37 | static struct phonet_protocol *phonet_proto_get(int protocol); |
@@ -200,6 +201,7 @@ static int __init phonet_init(void) | |||
200 | return err; | 201 | return err; |
201 | } | 202 | } |
202 | 203 | ||
204 | phonet_device_init(); | ||
203 | dev_add_pack(&phonet_packet_type); | 205 | dev_add_pack(&phonet_packet_type); |
204 | return 0; | 206 | return 0; |
205 | } | 207 | } |
@@ -208,6 +210,7 @@ static void __exit phonet_exit(void) | |||
208 | { | 210 | { |
209 | sock_unregister(AF_PHONET); | 211 | sock_unregister(AF_PHONET); |
210 | dev_remove_pack(&phonet_packet_type); | 212 | dev_remove_pack(&phonet_packet_type); |
213 | phonet_device_exit(); | ||
211 | } | 214 | } |
212 | 215 | ||
213 | module_init(phonet_init); | 216 | module_init(phonet_init); |
diff --git a/net/phonet/pn_dev.c b/net/phonet/pn_dev.c new file mode 100644 index 000000000000..53be9fc82aaa --- /dev/null +++ b/net/phonet/pn_dev.c | |||
@@ -0,0 +1,208 @@ | |||
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 | |||
79 | if (dev_net(dev) == net && | ||
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; | ||
118 | if (bitmap_empty(pnd->addrs, 64)) | ||
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 | |||
143 | int phonet_address_lookup(u8 addr) | ||
144 | { | ||
145 | struct phonet_device *pnd; | ||
146 | |||
147 | spin_lock_bh(&pndevs.lock); | ||
148 | list_for_each_entry(pnd, &pndevs.list, list) { | ||
149 | /* Don't allow unregistering devices! */ | ||
150 | if ((pnd->netdev->reg_state != NETREG_REGISTERED) || | ||
151 | ((pnd->netdev->flags & IFF_UP)) != IFF_UP) | ||
152 | continue; | ||
153 | |||
154 | if (test_bit(addr >> 2, pnd->addrs)) { | ||
155 | spin_unlock_bh(&pndevs.lock); | ||
156 | return 0; | ||
157 | } | ||
158 | } | ||
159 | spin_unlock_bh(&pndevs.lock); | ||
160 | return -EADDRNOTAVAIL; | ||
161 | } | ||
162 | |||
163 | /* notify Phonet of device events */ | ||
164 | static int phonet_device_notify(struct notifier_block *me, unsigned long what, | ||
165 | void *arg) | ||
166 | { | ||
167 | struct net_device *dev = arg; | ||
168 | |||
169 | if (what == NETDEV_UNREGISTER) { | ||
170 | struct phonet_device *pnd; | ||
171 | |||
172 | /* Destroy phonet-specific device data */ | ||
173 | spin_lock_bh(&pndevs.lock); | ||
174 | pnd = __phonet_get(dev); | ||
175 | if (pnd) | ||
176 | __phonet_device_free(pnd); | ||
177 | spin_unlock_bh(&pndevs.lock); | ||
178 | } | ||
179 | return 0; | ||
180 | |||
181 | } | ||
182 | |||
183 | static struct notifier_block phonet_device_notifier = { | ||
184 | .notifier_call = phonet_device_notify, | ||
185 | .priority = 0, | ||
186 | }; | ||
187 | |||
188 | /* Initialize Phonet devices list */ | ||
189 | void phonet_device_init(void) | ||
190 | { | ||
191 | register_netdevice_notifier(&phonet_device_notifier); | ||
192 | } | ||
193 | |||
194 | void phonet_device_exit(void) | ||
195 | { | ||
196 | struct phonet_device *pnd, *n; | ||
197 | |||
198 | rtnl_unregister_all(PF_PHONET); | ||
199 | rtnl_lock(); | ||
200 | spin_lock_bh(&pndevs.lock); | ||
201 | |||
202 | list_for_each_entry_safe(pnd, n, &pndevs.list, list) | ||
203 | __phonet_device_free(pnd); | ||
204 | |||
205 | spin_unlock_bh(&pndevs.lock); | ||
206 | rtnl_unlock(); | ||
207 | unregister_netdevice_notifier(&phonet_device_notifier); | ||
208 | } | ||