]>
Commit | Line | Data |
---|---|---|
a21b963a IPG |
1 | /* |
2 | * WUSB Wire Adapter: WLP interface | |
3 | * Driver for the Linux Network stack. | |
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 | * This implements a very simple network driver for the WLP USB | |
26 | * device that is associated to a UWB (Ultra Wide Band) host. | |
27 | * | |
28 | * This is seen as an interface of a composite device. Once the UWB | |
29 | * host has an association to another WLP capable device, the | |
30 | * networking interface (aka WLP) can start to send packets back and | |
31 | * forth. | |
32 | * | |
33 | * Limitations: | |
34 | * | |
35 | * - Hand cranked; can't ifup the interface until there is an association | |
36 | * | |
37 | * - BW allocation very simplistic [see i1480u_mas_set() and callees]. | |
38 | * | |
39 | * | |
40 | * ROADMAP: | |
41 | * | |
42 | * ENTRY POINTS (driver model): | |
43 | * | |
44 | * i1480u_driver_{exit,init}(): initialization of the driver. | |
45 | * | |
46 | * i1480u_probe(): called by the driver code when a device | |
47 | * matching 'i1480u_id_table' is connected. | |
48 | * | |
49 | * This allocs a netdev instance, inits with | |
50 | * i1480u_add(), then registers_netdev(). | |
51 | * i1480u_init() | |
52 | * i1480u_add() | |
53 | * | |
54 | * i1480u_disconnect(): device has been disconnected/module | |
55 | * is being removed. | |
56 | * i1480u_rm() | |
57 | */ | |
a21b963a IPG |
58 | #include <linux/if_arp.h> |
59 | #include <linux/etherdevice.h> | |
a01777ec | 60 | |
a21b963a IPG |
61 | #include "i1480u-wlp.h" |
62 | ||
63 | ||
64 | ||
65 | static inline | |
66 | void i1480u_init(struct i1480u *i1480u) | |
67 | { | |
68 | /* nothing so far... doesn't it suck? */ | |
69 | spin_lock_init(&i1480u->lock); | |
70 | INIT_LIST_HEAD(&i1480u->tx_list); | |
71 | spin_lock_init(&i1480u->tx_list_lock); | |
72 | wlp_options_init(&i1480u->options); | |
73 | edc_init(&i1480u->tx_errors); | |
74 | edc_init(&i1480u->rx_errors); | |
75 | #ifdef i1480u_FLOW_CONTROL | |
76 | edc_init(&i1480u->notif_edc); | |
77 | #endif | |
78 | stats_init(&i1480u->lqe_stats); | |
79 | stats_init(&i1480u->rssi_stats); | |
80 | wlp_init(&i1480u->wlp); | |
81 | } | |
82 | ||
83 | /** | |
84 | * Fill WLP device information structure | |
85 | * | |
86 | * The structure will contain a few character arrays, each ending with a | |
87 | * null terminated string. Each string has to fit (excluding terminating | |
88 | * character) into a specified range obtained from the WLP substack. | |
89 | * | |
90 | * It is still not clear exactly how this device information should be | |
91 | * obtained. Until we find out we use the USB device descriptor as backup, some | |
92 | * information elements have intuitive mappings, other not. | |
93 | */ | |
94 | static | |
95 | void i1480u_fill_device_info(struct wlp *wlp, struct wlp_device_info *dev_info) | |
96 | { | |
97 | struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp); | |
98 | struct usb_device *usb_dev = i1480u->usb_dev; | |
99 | /* Treat device name and model name the same */ | |
100 | if (usb_dev->descriptor.iProduct) { | |
101 | usb_string(usb_dev, usb_dev->descriptor.iProduct, | |
102 | dev_info->name, sizeof(dev_info->name)); | |
103 | usb_string(usb_dev, usb_dev->descriptor.iProduct, | |
104 | dev_info->model_name, sizeof(dev_info->model_name)); | |
105 | } | |
106 | if (usb_dev->descriptor.iManufacturer) | |
107 | usb_string(usb_dev, usb_dev->descriptor.iManufacturer, | |
108 | dev_info->manufacturer, | |
109 | sizeof(dev_info->manufacturer)); | |
110 | scnprintf(dev_info->model_nr, sizeof(dev_info->model_nr), "%04x", | |
111 | __le16_to_cpu(usb_dev->descriptor.bcdDevice)); | |
112 | if (usb_dev->descriptor.iSerialNumber) | |
113 | usb_string(usb_dev, usb_dev->descriptor.iSerialNumber, | |
114 | dev_info->serial, sizeof(dev_info->serial)); | |
115 | /* FIXME: where should we obtain category? */ | |
116 | dev_info->prim_dev_type.category = cpu_to_le16(WLP_DEV_CAT_OTHER); | |
117 | /* FIXME: Complete OUI and OUIsubdiv attributes */ | |
118 | } | |
119 | ||
120 | #ifdef i1480u_FLOW_CONTROL | |
121 | /** | |
122 | * Callback for the notification endpoint | |
123 | * | |
124 | * This mostly controls the xon/xoff protocol. In case of hard error, | |
125 | * we stop the queue. If not, we always retry. | |
126 | */ | |
127 | static | |
128 | void i1480u_notif_cb(struct urb *urb, struct pt_regs *regs) | |
129 | { | |
130 | struct i1480u *i1480u = urb->context; | |
131 | struct usb_interface *usb_iface = i1480u->usb_iface; | |
132 | struct device *dev = &usb_iface->dev; | |
133 | int result; | |
134 | ||
135 | switch (urb->status) { | |
136 | case 0: /* Got valid data, do xon/xoff */ | |
137 | switch (i1480u->notif_buffer[0]) { | |
138 | case 'N': | |
139 | dev_err(dev, "XOFF STOPPING queue at %lu\n", jiffies); | |
140 | netif_stop_queue(i1480u->net_dev); | |
141 | break; | |
142 | case 'A': | |
143 | dev_err(dev, "XON STARTING queue at %lu\n", jiffies); | |
144 | netif_start_queue(i1480u->net_dev); | |
145 | break; | |
146 | default: | |
147 | dev_err(dev, "NEP: unknown data 0x%02hhx\n", | |
148 | i1480u->notif_buffer[0]); | |
149 | } | |
150 | break; | |
151 | case -ECONNRESET: /* Controlled situation ... */ | |
152 | case -ENOENT: /* we killed the URB... */ | |
153 | dev_err(dev, "NEP: URB reset/noent %d\n", urb->status); | |
154 | goto error; | |
155 | case -ESHUTDOWN: /* going away! */ | |
156 | dev_err(dev, "NEP: URB down %d\n", urb->status); | |
157 | goto error; | |
158 | default: /* Retry unless it gets ugly */ | |
159 | if (edc_inc(&i1480u->notif_edc, EDC_MAX_ERRORS, | |
160 | EDC_ERROR_TIMEFRAME)) { | |
161 | dev_err(dev, "NEP: URB max acceptable errors " | |
162 | "exceeded; resetting device\n"); | |
163 | goto error_reset; | |
164 | } | |
165 | dev_err(dev, "NEP: URB error %d\n", urb->status); | |
166 | break; | |
167 | } | |
168 | result = usb_submit_urb(urb, GFP_ATOMIC); | |
169 | if (result < 0) { | |
170 | dev_err(dev, "NEP: Can't resubmit URB: %d; resetting device\n", | |
171 | result); | |
172 | goto error_reset; | |
173 | } | |
174 | return; | |
175 | ||
176 | error_reset: | |
177 | wlp_reset_all(&i1480-wlp); | |
178 | error: | |
179 | netif_stop_queue(i1480u->net_dev); | |
180 | return; | |
181 | } | |
182 | #endif | |
183 | ||
184 | static | |
185 | int i1480u_add(struct i1480u *i1480u, struct usb_interface *iface) | |
186 | { | |
187 | int result = -ENODEV; | |
188 | struct wlp *wlp = &i1480u->wlp; | |
189 | struct usb_device *usb_dev = interface_to_usbdev(iface); | |
190 | struct net_device *net_dev = i1480u->net_dev; | |
191 | struct uwb_rc *rc; | |
192 | struct uwb_dev *uwb_dev; | |
193 | #ifdef i1480u_FLOW_CONTROL | |
194 | struct usb_endpoint_descriptor *epd; | |
195 | #endif | |
196 | ||
197 | i1480u->usb_dev = usb_get_dev(usb_dev); | |
198 | i1480u->usb_iface = iface; | |
199 | rc = uwb_rc_get_by_grandpa(&i1480u->usb_dev->dev); | |
200 | if (rc == NULL) { | |
201 | dev_err(&iface->dev, "Cannot get associated UWB Radio " | |
202 | "Controller\n"); | |
203 | goto out; | |
204 | } | |
205 | wlp->xmit_frame = i1480u_xmit_frame; | |
206 | wlp->fill_device_info = i1480u_fill_device_info; | |
207 | wlp->stop_queue = i1480u_stop_queue; | |
208 | wlp->start_queue = i1480u_start_queue; | |
e8e1594c | 209 | result = wlp_setup(wlp, rc, net_dev); |
a21b963a IPG |
210 | if (result < 0) { |
211 | dev_err(&iface->dev, "Cannot setup WLP\n"); | |
212 | goto error_wlp_setup; | |
213 | } | |
214 | result = 0; | |
215 | ether_setup(net_dev); /* make it an etherdevice */ | |
216 | uwb_dev = &rc->uwb_dev; | |
217 | /* FIXME: hookup address change notifications? */ | |
218 | ||
219 | memcpy(net_dev->dev_addr, uwb_dev->mac_addr.data, | |
220 | sizeof(net_dev->dev_addr)); | |
221 | ||
222 | net_dev->hard_header_len = sizeof(struct untd_hdr_cmp) | |
223 | + sizeof(struct wlp_tx_hdr) | |
224 | + WLP_DATA_HLEN | |
225 | + ETH_HLEN; | |
226 | net_dev->mtu = 3500; | |
227 | net_dev->tx_queue_len = 20; /* FIXME: maybe use 1000? */ | |
228 | ||
229 | /* net_dev->flags &= ~IFF_BROADCAST; FIXME: BUG in firmware */ | |
230 | /* FIXME: multicast disabled */ | |
231 | net_dev->flags &= ~IFF_MULTICAST; | |
232 | net_dev->features &= ~NETIF_F_SG; | |
233 | net_dev->features &= ~NETIF_F_FRAGLIST; | |
234 | /* All NETIF_F_*_CSUM disabled */ | |
235 | net_dev->features |= NETIF_F_HIGHDMA; | |
236 | net_dev->watchdog_timeo = 5*HZ; /* FIXME: a better default? */ | |
237 | ||
238 | net_dev->open = i1480u_open; | |
239 | net_dev->stop = i1480u_stop; | |
240 | net_dev->hard_start_xmit = i1480u_hard_start_xmit; | |
241 | net_dev->tx_timeout = i1480u_tx_timeout; | |
242 | net_dev->get_stats = i1480u_get_stats; | |
243 | net_dev->set_config = i1480u_set_config; | |
244 | net_dev->change_mtu = i1480u_change_mtu; | |
245 | ||
246 | #ifdef i1480u_FLOW_CONTROL | |
247 | /* Notification endpoint setup (submitted when we open the device) */ | |
248 | i1480u->notif_urb = usb_alloc_urb(0, GFP_KERNEL); | |
249 | if (i1480u->notif_urb == NULL) { | |
250 | dev_err(&iface->dev, "Unable to allocate notification URB\n"); | |
251 | result = -ENOMEM; | |
252 | goto error_urb_alloc; | |
253 | } | |
254 | epd = &iface->cur_altsetting->endpoint[0].desc; | |
255 | usb_fill_int_urb(i1480u->notif_urb, usb_dev, | |
256 | usb_rcvintpipe(usb_dev, epd->bEndpointAddress), | |
257 | i1480u->notif_buffer, sizeof(i1480u->notif_buffer), | |
258 | i1480u_notif_cb, i1480u, epd->bInterval); | |
259 | ||
260 | #endif | |
261 | ||
262 | i1480u->tx_inflight.max = i1480u_TX_INFLIGHT_MAX; | |
263 | i1480u->tx_inflight.threshold = i1480u_TX_INFLIGHT_THRESHOLD; | |
264 | i1480u->tx_inflight.restart_ts = jiffies; | |
265 | usb_set_intfdata(iface, i1480u); | |
266 | return result; | |
267 | ||
268 | #ifdef i1480u_FLOW_CONTROL | |
269 | error_urb_alloc: | |
270 | #endif | |
271 | wlp_remove(wlp); | |
272 | error_wlp_setup: | |
273 | uwb_rc_put(rc); | |
274 | out: | |
275 | usb_put_dev(i1480u->usb_dev); | |
276 | return result; | |
277 | } | |
278 | ||
279 | static void i1480u_rm(struct i1480u *i1480u) | |
280 | { | |
281 | struct uwb_rc *rc = i1480u->wlp.rc; | |
282 | usb_set_intfdata(i1480u->usb_iface, NULL); | |
283 | #ifdef i1480u_FLOW_CONTROL | |
284 | usb_kill_urb(i1480u->notif_urb); | |
285 | usb_free_urb(i1480u->notif_urb); | |
286 | #endif | |
287 | wlp_remove(&i1480u->wlp); | |
288 | uwb_rc_put(rc); | |
289 | usb_put_dev(i1480u->usb_dev); | |
290 | } | |
291 | ||
292 | /** Just setup @net_dev's i1480u private data */ | |
293 | static void i1480u_netdev_setup(struct net_device *net_dev) | |
294 | { | |
295 | struct i1480u *i1480u = netdev_priv(net_dev); | |
296 | /* Initialize @i1480u */ | |
297 | memset(i1480u, 0, sizeof(*i1480u)); | |
298 | i1480u_init(i1480u); | |
299 | } | |
300 | ||
301 | /** | |
302 | * Probe a i1480u interface and register it | |
303 | * | |
304 | * @iface: USB interface to link to | |
305 | * @id: USB class/subclass/protocol id | |
306 | * @returns: 0 if ok, < 0 errno code on error. | |
307 | * | |
308 | * Does basic housekeeping stuff and then allocs a netdev with space | |
309 | * for the i1480u data. Initializes, registers in i1480u, registers in | |
310 | * netdev, ready to go. | |
311 | */ | |
312 | static int i1480u_probe(struct usb_interface *iface, | |
313 | const struct usb_device_id *id) | |
314 | { | |
315 | int result; | |
316 | struct net_device *net_dev; | |
317 | struct device *dev = &iface->dev; | |
318 | struct i1480u *i1480u; | |
319 | ||
320 | /* Allocate instance [calls i1480u_netdev_setup() on it] */ | |
321 | result = -ENOMEM; | |
322 | net_dev = alloc_netdev(sizeof(*i1480u), "wlp%d", i1480u_netdev_setup); | |
323 | if (net_dev == NULL) { | |
324 | dev_err(dev, "no memory for network device instance\n"); | |
325 | goto error_alloc_netdev; | |
326 | } | |
327 | SET_NETDEV_DEV(net_dev, dev); | |
328 | i1480u = netdev_priv(net_dev); | |
329 | i1480u->net_dev = net_dev; | |
330 | result = i1480u_add(i1480u, iface); /* Now setup all the wlp stuff */ | |
331 | if (result < 0) { | |
332 | dev_err(dev, "cannot add i1480u device: %d\n", result); | |
333 | goto error_i1480u_add; | |
334 | } | |
335 | result = register_netdev(net_dev); /* Okey dokey, bring it up */ | |
336 | if (result < 0) { | |
337 | dev_err(dev, "cannot register network device: %d\n", result); | |
338 | goto error_register_netdev; | |
339 | } | |
340 | i1480u_sysfs_setup(i1480u); | |
341 | if (result < 0) | |
342 | goto error_sysfs_init; | |
343 | return 0; | |
344 | ||
345 | error_sysfs_init: | |
346 | unregister_netdev(net_dev); | |
347 | error_register_netdev: | |
348 | i1480u_rm(i1480u); | |
349 | error_i1480u_add: | |
350 | free_netdev(net_dev); | |
351 | error_alloc_netdev: | |
352 | return result; | |
353 | } | |
354 | ||
355 | ||
356 | /** | |
357 | * Disconect a i1480u from the system. | |
358 | * | |
359 | * i1480u_stop() has been called before, so al the rx and tx contexts | |
360 | * have been taken down already. Make sure the queue is stopped, | |
361 | * unregister netdev and i1480u, free and kill. | |
362 | */ | |
363 | static void i1480u_disconnect(struct usb_interface *iface) | |
364 | { | |
365 | struct i1480u *i1480u; | |
366 | struct net_device *net_dev; | |
367 | ||
368 | i1480u = usb_get_intfdata(iface); | |
369 | net_dev = i1480u->net_dev; | |
370 | netif_stop_queue(net_dev); | |
371 | #ifdef i1480u_FLOW_CONTROL | |
372 | usb_kill_urb(i1480u->notif_urb); | |
373 | #endif | |
374 | i1480u_sysfs_release(i1480u); | |
375 | unregister_netdev(net_dev); | |
376 | i1480u_rm(i1480u); | |
377 | free_netdev(net_dev); | |
378 | } | |
379 | ||
380 | static struct usb_device_id i1480u_id_table[] = { | |
381 | { | |
382 | .match_flags = USB_DEVICE_ID_MATCH_DEVICE \ | |
383 | | USB_DEVICE_ID_MATCH_DEV_INFO \ | |
384 | | USB_DEVICE_ID_MATCH_INT_INFO, | |
385 | .idVendor = 0x8086, | |
386 | .idProduct = 0x0c3b, | |
387 | .bDeviceClass = 0xef, | |
388 | .bDeviceSubClass = 0x02, | |
389 | .bDeviceProtocol = 0x02, | |
390 | .bInterfaceClass = 0xff, | |
391 | .bInterfaceSubClass = 0xff, | |
392 | .bInterfaceProtocol = 0xff, | |
393 | }, | |
394 | {}, | |
395 | }; | |
396 | MODULE_DEVICE_TABLE(usb, i1480u_id_table); | |
397 | ||
398 | static struct usb_driver i1480u_driver = { | |
399 | .name = KBUILD_MODNAME, | |
400 | .probe = i1480u_probe, | |
401 | .disconnect = i1480u_disconnect, | |
402 | .id_table = i1480u_id_table, | |
403 | }; | |
404 | ||
405 | static int __init i1480u_driver_init(void) | |
406 | { | |
407 | return usb_register(&i1480u_driver); | |
408 | } | |
409 | module_init(i1480u_driver_init); | |
410 | ||
411 | ||
412 | static void __exit i1480u_driver_exit(void) | |
413 | { | |
414 | usb_deregister(&i1480u_driver); | |
415 | } | |
416 | module_exit(i1480u_driver_exit); | |
417 | ||
418 | MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>"); | |
419 | MODULE_DESCRIPTION("i1480 Wireless UWB Link WLP networking for USB"); | |
420 | MODULE_LICENSE("GPL"); |