diff options
author | Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | 2008-09-17 11:34:08 -0400 |
---|---|---|
committer | David Vrabel <dv02@dv02pc01.europe.root.pri> | 2008-09-17 11:54:24 -0400 |
commit | 22d203ecef9b0cc1fa8d8f64c935b451ca7d1022 (patch) | |
tree | 5f310e8cff93a533f877f01f99b2fe8a8fc5f919 /drivers/uwb/address.c | |
parent | 0612edfd95ffe92201a2267e9e1b0fc68becf76d (diff) |
uwb: add the UWB stack (MLME)
Most of the MAC Layer Management Entity (MLME) support: address, beacon, IE
and scan management.
Signed-off-by: David Vrabel <david.vrabel@csr.com>
Diffstat (limited to 'drivers/uwb/address.c')
-rw-r--r-- | drivers/uwb/address.c | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/drivers/uwb/address.c b/drivers/uwb/address.c new file mode 100644 index 00000000000..1664ae5f170 --- /dev/null +++ b/drivers/uwb/address.c | |||
@@ -0,0 +1,374 @@ | |||
1 | /* | ||
2 | * Ultra Wide Band | ||
3 | * Address management | ||
4 | * | ||
5 | * Copyright (C) 2005-2006 Intel Corporation | ||
6 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License version | ||
10 | * 2 as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
20 | * 02110-1301, USA. | ||
21 | * | ||
22 | * | ||
23 | * FIXME: docs | ||
24 | */ | ||
25 | |||
26 | #include <linux/errno.h> | ||
27 | #include <linux/module.h> | ||
28 | #include <linux/device.h> | ||
29 | #include <linux/random.h> | ||
30 | #include <linux/etherdevice.h> | ||
31 | #include <linux/uwb/debug.h> | ||
32 | #include "uwb-internal.h" | ||
33 | |||
34 | |||
35 | /** Device Address Management command */ | ||
36 | struct uwb_rc_cmd_dev_addr_mgmt { | ||
37 | struct uwb_rccb rccb; | ||
38 | u8 bmOperationType; | ||
39 | u8 baAddr[6]; | ||
40 | } __attribute__((packed)); | ||
41 | |||
42 | |||
43 | /** | ||
44 | * Low level command for setting/getting UWB radio's addresses | ||
45 | * | ||
46 | * @hwarc: HWA Radio Control interface instance | ||
47 | * @bmOperationType: | ||
48 | * Set/get, MAC/DEV (see WUSB1.0[8.6.2.2]) | ||
49 | * @baAddr: address buffer--assumed to have enough data to hold | ||
50 | * the address type requested. | ||
51 | * @reply: Pointer to reply buffer (can be stack allocated) | ||
52 | * @returns: 0 if ok, < 0 errno code on error. | ||
53 | * | ||
54 | * @cmd has to be allocated because USB cannot grok USB or vmalloc | ||
55 | * buffers depending on your combination of host architecture. | ||
56 | */ | ||
57 | static | ||
58 | int uwb_rc_dev_addr_mgmt(struct uwb_rc *rc, | ||
59 | u8 bmOperationType, const u8 *baAddr, | ||
60 | struct uwb_rc_evt_dev_addr_mgmt *reply) | ||
61 | { | ||
62 | int result; | ||
63 | struct uwb_rc_cmd_dev_addr_mgmt *cmd; | ||
64 | |||
65 | result = -ENOMEM; | ||
66 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | ||
67 | if (cmd == NULL) | ||
68 | goto error_kzalloc; | ||
69 | cmd->rccb.bCommandType = UWB_RC_CET_GENERAL; | ||
70 | cmd->rccb.wCommand = cpu_to_le16(UWB_RC_CMD_DEV_ADDR_MGMT); | ||
71 | cmd->bmOperationType = bmOperationType; | ||
72 | if (baAddr) { | ||
73 | size_t size = 0; | ||
74 | switch (bmOperationType >> 1) { | ||
75 | case 0: size = 2; break; | ||
76 | case 1: size = 6; break; | ||
77 | default: BUG(); | ||
78 | } | ||
79 | memcpy(cmd->baAddr, baAddr, size); | ||
80 | } | ||
81 | reply->rceb.bEventType = UWB_RC_CET_GENERAL; | ||
82 | reply->rceb.wEvent = UWB_RC_CMD_DEV_ADDR_MGMT; | ||
83 | result = uwb_rc_cmd(rc, "DEV-ADDR-MGMT", | ||
84 | &cmd->rccb, sizeof(*cmd), | ||
85 | &reply->rceb, sizeof(*reply)); | ||
86 | if (result < 0) | ||
87 | goto error_cmd; | ||
88 | if (result < sizeof(*reply)) { | ||
89 | dev_err(&rc->uwb_dev.dev, | ||
90 | "DEV-ADDR-MGMT: not enough data replied: " | ||
91 | "%d vs %zu bytes needed\n", result, sizeof(*reply)); | ||
92 | result = -ENOMSG; | ||
93 | } else if (reply->bResultCode != UWB_RC_RES_SUCCESS) { | ||
94 | dev_err(&rc->uwb_dev.dev, | ||
95 | "DEV-ADDR-MGMT: command execution failed: %s (%d)\n", | ||
96 | uwb_rc_strerror(reply->bResultCode), | ||
97 | reply->bResultCode); | ||
98 | result = -EIO; | ||
99 | } else | ||
100 | result = 0; | ||
101 | error_cmd: | ||
102 | kfree(cmd); | ||
103 | error_kzalloc: | ||
104 | return result; | ||
105 | } | ||
106 | |||
107 | |||
108 | /** | ||
109 | * Set the UWB RC MAC or device address. | ||
110 | * | ||
111 | * @rc: UWB Radio Controller | ||
112 | * @_addr: Pointer to address to write [assumed to be either a | ||
113 | * 'struct uwb_mac_addr *' or a 'struct uwb_dev_addr *']. | ||
114 | * @type: Type of address to set (UWB_ADDR_DEV or UWB_ADDR_MAC). | ||
115 | * @returns: 0 if ok, < 0 errno code on error. | ||
116 | * | ||
117 | * Some anal retentivity here: even if both 'struct | ||
118 | * uwb_{dev,mac}_addr' have the actual byte array in the same offset | ||
119 | * and I could just pass _addr to hwarc_cmd_dev_addr_mgmt(), I prefer | ||
120 | * to use some syntatic sugar in case someday we decide to change the | ||
121 | * format of the structs. The compiler will optimize it out anyway. | ||
122 | */ | ||
123 | static int uwb_rc_addr_set(struct uwb_rc *rc, | ||
124 | const void *_addr, enum uwb_addr_type type) | ||
125 | { | ||
126 | int result; | ||
127 | u8 bmOperationType = 0x1; /* Set address */ | ||
128 | const struct uwb_dev_addr *dev_addr = _addr; | ||
129 | const struct uwb_mac_addr *mac_addr = _addr; | ||
130 | struct uwb_rc_evt_dev_addr_mgmt reply; | ||
131 | const u8 *baAddr; | ||
132 | |||
133 | result = -EINVAL; | ||
134 | switch (type) { | ||
135 | case UWB_ADDR_DEV: | ||
136 | baAddr = dev_addr->data; | ||
137 | break; | ||
138 | case UWB_ADDR_MAC: | ||
139 | baAddr = mac_addr->data; | ||
140 | bmOperationType |= 0x2; | ||
141 | break; | ||
142 | default: | ||
143 | return result; | ||
144 | } | ||
145 | return uwb_rc_dev_addr_mgmt(rc, bmOperationType, baAddr, &reply); | ||
146 | } | ||
147 | |||
148 | |||
149 | /** | ||
150 | * Get the UWB radio's MAC or device address. | ||
151 | * | ||
152 | * @rc: UWB Radio Controller | ||
153 | * @_addr: Where to write the address data [assumed to be either a | ||
154 | * 'struct uwb_mac_addr *' or a 'struct uwb_dev_addr *']. | ||
155 | * @type: Type of address to get (UWB_ADDR_DEV or UWB_ADDR_MAC). | ||
156 | * @returns: 0 if ok (and *_addr set), < 0 errno code on error. | ||
157 | * | ||
158 | * See comment in uwb_rc_addr_set() about anal retentivity in the | ||
159 | * type handling of the address variables. | ||
160 | */ | ||
161 | static int uwb_rc_addr_get(struct uwb_rc *rc, | ||
162 | void *_addr, enum uwb_addr_type type) | ||
163 | { | ||
164 | int result; | ||
165 | u8 bmOperationType = 0x0; /* Get address */ | ||
166 | struct uwb_rc_evt_dev_addr_mgmt evt; | ||
167 | struct uwb_dev_addr *dev_addr = _addr; | ||
168 | struct uwb_mac_addr *mac_addr = _addr; | ||
169 | u8 *baAddr; | ||
170 | |||
171 | result = -EINVAL; | ||
172 | switch (type) { | ||
173 | case UWB_ADDR_DEV: | ||
174 | baAddr = dev_addr->data; | ||
175 | break; | ||
176 | case UWB_ADDR_MAC: | ||
177 | bmOperationType |= 0x2; | ||
178 | baAddr = mac_addr->data; | ||
179 | break; | ||
180 | default: | ||
181 | return result; | ||
182 | } | ||
183 | result = uwb_rc_dev_addr_mgmt(rc, bmOperationType, baAddr, &evt); | ||
184 | if (result == 0) | ||
185 | switch (type) { | ||
186 | case UWB_ADDR_DEV: | ||
187 | memcpy(&dev_addr->data, evt.baAddr, | ||
188 | sizeof(dev_addr->data)); | ||
189 | break; | ||
190 | case UWB_ADDR_MAC: | ||
191 | memcpy(&mac_addr->data, evt.baAddr, | ||
192 | sizeof(mac_addr->data)); | ||
193 | break; | ||
194 | default: /* shut gcc up */ | ||
195 | BUG(); | ||
196 | } | ||
197 | return result; | ||
198 | } | ||
199 | |||
200 | |||
201 | /** Get @rc's MAC address to @addr */ | ||
202 | int uwb_rc_mac_addr_get(struct uwb_rc *rc, | ||
203 | struct uwb_mac_addr *addr) { | ||
204 | return uwb_rc_addr_get(rc, addr, UWB_ADDR_MAC); | ||
205 | } | ||
206 | EXPORT_SYMBOL_GPL(uwb_rc_mac_addr_get); | ||
207 | |||
208 | |||
209 | /** Get @rc's device address to @addr */ | ||
210 | int uwb_rc_dev_addr_get(struct uwb_rc *rc, | ||
211 | struct uwb_dev_addr *addr) { | ||
212 | return uwb_rc_addr_get(rc, addr, UWB_ADDR_DEV); | ||
213 | } | ||
214 | EXPORT_SYMBOL_GPL(uwb_rc_dev_addr_get); | ||
215 | |||
216 | |||
217 | /** Set @rc's address to @addr */ | ||
218 | int uwb_rc_mac_addr_set(struct uwb_rc *rc, | ||
219 | const struct uwb_mac_addr *addr) | ||
220 | { | ||
221 | int result = -EINVAL; | ||
222 | mutex_lock(&rc->uwb_dev.mutex); | ||
223 | result = uwb_rc_addr_set(rc, addr, UWB_ADDR_MAC); | ||
224 | mutex_unlock(&rc->uwb_dev.mutex); | ||
225 | return result; | ||
226 | } | ||
227 | |||
228 | |||
229 | /** Set @rc's address to @addr */ | ||
230 | int uwb_rc_dev_addr_set(struct uwb_rc *rc, | ||
231 | const struct uwb_dev_addr *addr) | ||
232 | { | ||
233 | int result = -EINVAL; | ||
234 | mutex_lock(&rc->uwb_dev.mutex); | ||
235 | result = uwb_rc_addr_set(rc, addr, UWB_ADDR_DEV); | ||
236 | rc->uwb_dev.dev_addr = *addr; | ||
237 | mutex_unlock(&rc->uwb_dev.mutex); | ||
238 | return result; | ||
239 | } | ||
240 | |||
241 | /* Returns !0 if given address is already assigned to device. */ | ||
242 | int __uwb_mac_addr_assigned_check(struct device *dev, void *_addr) | ||
243 | { | ||
244 | struct uwb_dev *uwb_dev = to_uwb_dev(dev); | ||
245 | struct uwb_mac_addr *addr = _addr; | ||
246 | |||
247 | if (!uwb_mac_addr_cmp(addr, &uwb_dev->mac_addr)) | ||
248 | return !0; | ||
249 | return 0; | ||
250 | } | ||
251 | |||
252 | /* Returns !0 if given address is already assigned to device. */ | ||
253 | int __uwb_dev_addr_assigned_check(struct device *dev, void *_addr) | ||
254 | { | ||
255 | struct uwb_dev *uwb_dev = to_uwb_dev(dev); | ||
256 | struct uwb_dev_addr *addr = _addr; | ||
257 | if (!uwb_dev_addr_cmp(addr, &uwb_dev->dev_addr)) | ||
258 | return !0; | ||
259 | return 0; | ||
260 | } | ||
261 | |||
262 | /** | ||
263 | * uwb_dev_addr_assign - assigned a generated DevAddr to a radio controller | ||
264 | * @rc: the (local) radio controller device requiring a new DevAddr | ||
265 | * | ||
266 | * A new DevAddr is required when: | ||
267 | * - first setting up a radio controller | ||
268 | * - if the hardware reports a DevAddr conflict | ||
269 | * | ||
270 | * The DevAddr is randomly generated in the generated DevAddr range | ||
271 | * [0x100, 0xfeff]. The number of devices in a beacon group is limited | ||
272 | * by mMaxBPLength (96) so this address space will never be exhausted. | ||
273 | * | ||
274 | * [ECMA-368] 17.1.1, 17.16. | ||
275 | */ | ||
276 | int uwb_rc_dev_addr_assign(struct uwb_rc *rc) | ||
277 | { | ||
278 | struct uwb_dev_addr new_addr; | ||
279 | |||
280 | do { | ||
281 | get_random_bytes(new_addr.data, sizeof(new_addr.data)); | ||
282 | } while (new_addr.data[0] == 0x00 || new_addr.data[0] == 0xff | ||
283 | || __uwb_dev_addr_assigned(rc, &new_addr)); | ||
284 | |||
285 | return uwb_rc_dev_addr_set(rc, &new_addr); | ||
286 | } | ||
287 | |||
288 | /** | ||
289 | * uwbd_evt_handle_rc_dev_addr_conflict - handle a DEV_ADDR_CONFLICT event | ||
290 | * @evt: the DEV_ADDR_CONFLICT notification from the radio controller | ||
291 | * | ||
292 | * A new (non-conflicting) DevAddr is assigned to the radio controller. | ||
293 | * | ||
294 | * [ECMA-368] 17.1.1.1. | ||
295 | */ | ||
296 | int uwbd_evt_handle_rc_dev_addr_conflict(struct uwb_event *evt) | ||
297 | { | ||
298 | struct uwb_rc *rc = evt->rc; | ||
299 | |||
300 | return uwb_rc_dev_addr_assign(rc); | ||
301 | } | ||
302 | |||
303 | /* | ||
304 | * Print the 48-bit EUI MAC address of the radio controller when | ||
305 | * reading /sys/class/uwb_rc/XX/mac_address | ||
306 | */ | ||
307 | static ssize_t uwb_rc_mac_addr_show(struct device *dev, | ||
308 | struct device_attribute *attr, char *buf) | ||
309 | { | ||
310 | struct uwb_dev *uwb_dev = to_uwb_dev(dev); | ||
311 | struct uwb_rc *rc = uwb_dev->rc; | ||
312 | struct uwb_mac_addr addr; | ||
313 | ssize_t result; | ||
314 | |||
315 | mutex_lock(&rc->uwb_dev.mutex); | ||
316 | result = uwb_rc_addr_get(rc, &addr, UWB_ADDR_MAC); | ||
317 | mutex_unlock(&rc->uwb_dev.mutex); | ||
318 | if (result >= 0) { | ||
319 | result = uwb_mac_addr_print(buf, UWB_ADDR_STRSIZE, &addr); | ||
320 | buf[result++] = '\n'; | ||
321 | } | ||
322 | return result; | ||
323 | } | ||
324 | |||
325 | /* | ||
326 | * Parse a 48 bit address written to /sys/class/uwb_rc/XX/mac_address | ||
327 | * and if correct, set it. | ||
328 | */ | ||
329 | static ssize_t uwb_rc_mac_addr_store(struct device *dev, | ||
330 | struct device_attribute *attr, | ||
331 | const char *buf, size_t size) | ||
332 | { | ||
333 | struct uwb_dev *uwb_dev = to_uwb_dev(dev); | ||
334 | struct uwb_rc *rc = uwb_dev->rc; | ||
335 | struct uwb_mac_addr addr; | ||
336 | ssize_t result; | ||
337 | |||
338 | result = sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx\n", | ||
339 | &addr.data[0], &addr.data[1], &addr.data[2], | ||
340 | &addr.data[3], &addr.data[4], &addr.data[5]); | ||
341 | if (result != 6) { | ||
342 | result = -EINVAL; | ||
343 | goto out; | ||
344 | } | ||
345 | if (is_multicast_ether_addr(addr.data)) { | ||
346 | dev_err(&rc->uwb_dev.dev, "refusing to set multicast " | ||
347 | "MAC address %s\n", buf); | ||
348 | result = -EINVAL; | ||
349 | goto out; | ||
350 | } | ||
351 | result = uwb_rc_mac_addr_set(rc, &addr); | ||
352 | if (result == 0) | ||
353 | rc->uwb_dev.mac_addr = addr; | ||
354 | out: | ||
355 | return result < 0 ? result : size; | ||
356 | } | ||
357 | DEVICE_ATTR(mac_address, S_IRUGO | S_IWUSR, uwb_rc_mac_addr_show, uwb_rc_mac_addr_store); | ||
358 | |||
359 | /** Print @addr to @buf, @return bytes written */ | ||
360 | size_t __uwb_addr_print(char *buf, size_t buf_size, const unsigned char *addr, | ||
361 | int type) | ||
362 | { | ||
363 | size_t result; | ||
364 | if (type) | ||
365 | result = scnprintf(buf, buf_size, | ||
366 | "%02x:%02x:%02x:%02x:%02x:%02x", | ||
367 | addr[0], addr[1], addr[2], | ||
368 | addr[3], addr[4], addr[5]); | ||
369 | else | ||
370 | result = scnprintf(buf, buf_size, "%02x:%02x", | ||
371 | addr[1], addr[0]); | ||
372 | return result; | ||
373 | } | ||
374 | EXPORT_SYMBOL_GPL(__uwb_addr_print); | ||