diff options
Diffstat (limited to 'drivers/net/irda/kingsun-sir.c')
-rw-r--r-- | drivers/net/irda/kingsun-sir.c | 657 |
1 files changed, 657 insertions, 0 deletions
diff --git a/drivers/net/irda/kingsun-sir.c b/drivers/net/irda/kingsun-sir.c new file mode 100644 index 00000000000..217429122e7 --- /dev/null +++ b/drivers/net/irda/kingsun-sir.c | |||
@@ -0,0 +1,657 @@ | |||
1 | /***************************************************************************** | ||
2 | * | ||
3 | * Filename: kingsun-sir.c | ||
4 | * Version: 0.1.1 | ||
5 | * Description: Irda KingSun/DonShine USB Dongle | ||
6 | * Status: Experimental | ||
7 | * Author: Alex Villac�s Lasso <a_villacis@palosanto.com> | ||
8 | * | ||
9 | * Based on stir4200 and mcs7780 drivers, with (strange?) differences | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
23 | * | ||
24 | *****************************************************************************/ | ||
25 | |||
26 | /* | ||
27 | * This is my current (2007-04-25) understanding of how this dongle is supposed | ||
28 | * to work. This is based on reverse-engineering and examination of the packet | ||
29 | * data sent and received by the WinXP driver using USBSnoopy. Feel free to | ||
30 | * update here as more of this dongle is known: | ||
31 | * | ||
32 | * General: Unlike the other USB IrDA dongles, this particular dongle exposes, | ||
33 | * not two bulk (in and out) endpoints, but two *interrupt* ones. This dongle, | ||
34 | * like the bulk based ones (stir4200.c and mcs7780.c), requires polling in | ||
35 | * order to receive data. | ||
36 | * Transmission: Just like stir4200, this dongle uses a raw stream of data, | ||
37 | * which needs to be wrapped and escaped in a similar way as in stir4200.c. | ||
38 | * Reception: Poll-based, as in stir4200. Each read returns the contents of a | ||
39 | * 8-byte buffer, of which the first byte (LSB) indicates the number of bytes | ||
40 | * (1-7) of valid data contained within the remaining 7 bytes. For example, if | ||
41 | * the buffer had the following contents: | ||
42 | * 06 ff ff ff c0 01 04 aa | ||
43 | * This means that (06) there are 6 bytes of valid data. The byte 0xaa at the | ||
44 | * end is garbage (left over from a previous reception) and is discarded. | ||
45 | * If a read returns an "impossible" value as the length of valid data (such as | ||
46 | * 0x36) in the first byte, then the buffer is uninitialized (as is the case of | ||
47 | * first plug-in) and its contents should be discarded. There is currently no | ||
48 | * evidence that the top 5 bits of the 1st byte of the buffer can have values | ||
49 | * other than 0 once reception begins. | ||
50 | * Once valid bytes are collected, the assembled stream is a sequence of | ||
51 | * wrapped IrDA frames that is unwrapped and unescaped as in stir4200.c. | ||
52 | * BIG FAT WARNING: the dongle does *not* reset the RX buffer in any way after | ||
53 | * a successful read from the host, which means that in absence of further | ||
54 | * reception, repeated reads from the dongle will return the exact same | ||
55 | * contents repeatedly. Attempts to be smart and cache a previous read seem | ||
56 | * to result in corrupted packets, so this driver depends on the unwrap logic | ||
57 | * to sort out any repeated reads. | ||
58 | * Speed change: no commands observed so far to change speed, assumed fixed | ||
59 | * 9600bps (SIR). | ||
60 | */ | ||
61 | |||
62 | #include <linux/module.h> | ||
63 | #include <linux/moduleparam.h> | ||
64 | #include <linux/kernel.h> | ||
65 | #include <linux/types.h> | ||
66 | #include <linux/errno.h> | ||
67 | #include <linux/init.h> | ||
68 | #include <linux/slab.h> | ||
69 | #include <linux/module.h> | ||
70 | #include <linux/kref.h> | ||
71 | #include <linux/usb.h> | ||
72 | #include <linux/device.h> | ||
73 | #include <linux/crc32.h> | ||
74 | |||
75 | #include <asm/unaligned.h> | ||
76 | #include <asm/byteorder.h> | ||
77 | #include <asm/uaccess.h> | ||
78 | |||
79 | #include <net/irda/irda.h> | ||
80 | #include <net/irda/wrapper.h> | ||
81 | #include <net/irda/crc.h> | ||
82 | |||
83 | /* | ||
84 | * According to lsusb, 0x07c0 is assigned to | ||
85 | * "Code Mercenaries Hard- und Software GmbH" | ||
86 | */ | ||
87 | #define KING_VENDOR_ID 0x07c0 | ||
88 | #define KING_PRODUCT_ID 0x4200 | ||
89 | |||
90 | /* These are the currently known USB ids */ | ||
91 | static struct usb_device_id dongles[] = { | ||
92 | /* KingSun Co,Ltd IrDA/USB Bridge */ | ||
93 | { USB_DEVICE(KING_VENDOR_ID, KING_PRODUCT_ID) }, | ||
94 | { } | ||
95 | }; | ||
96 | |||
97 | MODULE_DEVICE_TABLE(usb, dongles); | ||
98 | |||
99 | #define KINGSUN_MTT 0x07 | ||
100 | |||
101 | #define KINGSUN_FIFO_SIZE 4096 | ||
102 | #define KINGSUN_EP_IN 0 | ||
103 | #define KINGSUN_EP_OUT 1 | ||
104 | |||
105 | struct kingsun_cb { | ||
106 | struct usb_device *usbdev; /* init: probe_irda */ | ||
107 | struct net_device *netdev; /* network layer */ | ||
108 | struct irlap_cb *irlap; /* The link layer we are binded to */ | ||
109 | struct net_device_stats stats; /* network statistics */ | ||
110 | struct qos_info qos; | ||
111 | |||
112 | __u8 *in_buf; /* receive buffer */ | ||
113 | __u8 *out_buf; /* transmit buffer */ | ||
114 | __u8 max_rx; /* max. atomic read from dongle | ||
115 | (usually 8), also size of in_buf */ | ||
116 | __u8 max_tx; /* max. atomic write to dongle | ||
117 | (usually 8) */ | ||
118 | |||
119 | iobuff_t rx_buff; /* receive unwrap state machine */ | ||
120 | struct timeval rx_time; | ||
121 | spinlock_t lock; | ||
122 | int receiving; | ||
123 | |||
124 | __u8 ep_in; | ||
125 | __u8 ep_out; | ||
126 | |||
127 | struct urb *tx_urb; | ||
128 | struct urb *rx_urb; | ||
129 | }; | ||
130 | |||
131 | /* Callback transmission routine */ | ||
132 | static void kingsun_send_irq(struct urb *urb) | ||
133 | { | ||
134 | struct kingsun_cb *kingsun = urb->context; | ||
135 | struct net_device *netdev = kingsun->netdev; | ||
136 | |||
137 | /* in process of stopping, just drop data */ | ||
138 | if (!netif_running(kingsun->netdev)) { | ||
139 | err("kingsun_send_irq: Network not running!"); | ||
140 | return; | ||
141 | } | ||
142 | |||
143 | /* unlink, shutdown, unplug, other nasties */ | ||
144 | if (urb->status != 0) { | ||
145 | err("kingsun_send_irq: urb asynchronously failed - %d", | ||
146 | urb->status); | ||
147 | } | ||
148 | netif_wake_queue(netdev); | ||
149 | } | ||
150 | |||
151 | /* | ||
152 | * Called from net/core when new frame is available. | ||
153 | */ | ||
154 | static int kingsun_hard_xmit(struct sk_buff *skb, struct net_device *netdev) | ||
155 | { | ||
156 | struct kingsun_cb *kingsun; | ||
157 | int wraplen; | ||
158 | int ret = 0; | ||
159 | |||
160 | if (skb == NULL || netdev == NULL) | ||
161 | return -EINVAL; | ||
162 | |||
163 | netif_stop_queue(netdev); | ||
164 | |||
165 | /* the IRDA wrapping routines don't deal with non linear skb */ | ||
166 | SKB_LINEAR_ASSERT(skb); | ||
167 | |||
168 | kingsun = netdev_priv(netdev); | ||
169 | |||
170 | spin_lock(&kingsun->lock); | ||
171 | |||
172 | /* Append data to the end of whatever data remains to be transmitted */ | ||
173 | wraplen = async_wrap_skb(skb, | ||
174 | kingsun->out_buf, | ||
175 | KINGSUN_FIFO_SIZE); | ||
176 | |||
177 | /* Calculate how much data can be transmitted in this urb */ | ||
178 | usb_fill_int_urb(kingsun->tx_urb, kingsun->usbdev, | ||
179 | usb_sndintpipe(kingsun->usbdev, kingsun->ep_out), | ||
180 | kingsun->out_buf, wraplen, kingsun_send_irq, | ||
181 | kingsun, 1); | ||
182 | |||
183 | if ((ret = usb_submit_urb(kingsun->tx_urb, GFP_ATOMIC))) { | ||
184 | err("kingsun_hard_xmit: failed tx_urb submit: %d", ret); | ||
185 | switch (ret) { | ||
186 | case -ENODEV: | ||
187 | case -EPIPE: | ||
188 | break; | ||
189 | default: | ||
190 | kingsun->stats.tx_errors++; | ||
191 | netif_start_queue(netdev); | ||
192 | } | ||
193 | } else { | ||
194 | kingsun->stats.tx_packets++; | ||
195 | kingsun->stats.tx_bytes += skb->len; | ||
196 | } | ||
197 | |||
198 | dev_kfree_skb(skb); | ||
199 | spin_unlock(&kingsun->lock); | ||
200 | |||
201 | return ret; | ||
202 | } | ||
203 | |||
204 | /* Receive callback function */ | ||
205 | static void kingsun_rcv_irq(struct urb *urb) | ||
206 | { | ||
207 | struct kingsun_cb *kingsun = urb->context; | ||
208 | int ret; | ||
209 | |||
210 | /* in process of stopping, just drop data */ | ||
211 | if (!netif_running(kingsun->netdev)) { | ||
212 | kingsun->receiving = 0; | ||
213 | return; | ||
214 | } | ||
215 | |||
216 | /* unlink, shutdown, unplug, other nasties */ | ||
217 | if (urb->status != 0) { | ||
218 | err("kingsun_rcv_irq: urb asynchronously failed - %d", | ||
219 | urb->status); | ||
220 | kingsun->receiving = 0; | ||
221 | return; | ||
222 | } | ||
223 | |||
224 | if (urb->actual_length == kingsun->max_rx) { | ||
225 | __u8 *bytes = urb->transfer_buffer; | ||
226 | int i; | ||
227 | |||
228 | /* The very first byte in the buffer indicates the length of | ||
229 | valid data in the read. This byte must be in the range | ||
230 | 1..kingsun->max_rx -1 . Values outside this range indicate | ||
231 | an uninitialized Rx buffer when the dongle has just been | ||
232 | plugged in. */ | ||
233 | if (bytes[0] >= 1 && bytes[0] < kingsun->max_rx) { | ||
234 | for (i = 1; i <= bytes[0]; i++) { | ||
235 | async_unwrap_char(kingsun->netdev, | ||
236 | &kingsun->stats, | ||
237 | &kingsun->rx_buff, bytes[i]); | ||
238 | } | ||
239 | kingsun->netdev->last_rx = jiffies; | ||
240 | do_gettimeofday(&kingsun->rx_time); | ||
241 | kingsun->receiving = | ||
242 | (kingsun->rx_buff.state != OUTSIDE_FRAME) | ||
243 | ? 1 : 0; | ||
244 | } | ||
245 | } else if (urb->actual_length > 0) { | ||
246 | err("%s(): Unexpected response length, expected %d got %d", | ||
247 | __FUNCTION__, kingsun->max_rx, urb->actual_length); | ||
248 | } | ||
249 | /* This urb has already been filled in kingsun_net_open */ | ||
250 | ret = usb_submit_urb(urb, GFP_ATOMIC); | ||
251 | } | ||
252 | |||
253 | /* | ||
254 | * Function kingsun_net_open (dev) | ||
255 | * | ||
256 | * Network device is taken up. Usually this is done by "ifconfig irda0 up" | ||
257 | */ | ||
258 | static int kingsun_net_open(struct net_device *netdev) | ||
259 | { | ||
260 | struct kingsun_cb *kingsun = netdev_priv(netdev); | ||
261 | int err = -ENOMEM; | ||
262 | char hwname[16]; | ||
263 | |||
264 | /* At this point, urbs are NULL, and skb is NULL (see kingsun_probe) */ | ||
265 | kingsun->receiving = 0; | ||
266 | |||
267 | /* Initialize for SIR to copy data directly into skb. */ | ||
268 | kingsun->rx_buff.in_frame = FALSE; | ||
269 | kingsun->rx_buff.state = OUTSIDE_FRAME; | ||
270 | kingsun->rx_buff.truesize = IRDA_SKB_MAX_MTU; | ||
271 | kingsun->rx_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU); | ||
272 | if (!kingsun->rx_buff.skb) | ||
273 | goto free_mem; | ||
274 | |||
275 | skb_reserve(kingsun->rx_buff.skb, 1); | ||
276 | kingsun->rx_buff.head = kingsun->rx_buff.skb->data; | ||
277 | do_gettimeofday(&kingsun->rx_time); | ||
278 | |||
279 | kingsun->rx_urb = usb_alloc_urb(0, GFP_KERNEL); | ||
280 | if (!kingsun->rx_urb) | ||
281 | goto free_mem; | ||
282 | |||
283 | kingsun->tx_urb = usb_alloc_urb(0, GFP_KERNEL); | ||
284 | if (!kingsun->tx_urb) | ||
285 | goto free_mem; | ||
286 | |||
287 | /* | ||
288 | * Now that everything should be initialized properly, | ||
289 | * Open new IrLAP layer instance to take care of us... | ||
290 | */ | ||
291 | sprintf(hwname, "usb#%d", kingsun->usbdev->devnum); | ||
292 | kingsun->irlap = irlap_open(netdev, &kingsun->qos, hwname); | ||
293 | if (!kingsun->irlap) { | ||
294 | err("kingsun-sir: irlap_open failed"); | ||
295 | goto free_mem; | ||
296 | } | ||
297 | |||
298 | /* Start first reception */ | ||
299 | usb_fill_int_urb(kingsun->rx_urb, kingsun->usbdev, | ||
300 | usb_rcvintpipe(kingsun->usbdev, kingsun->ep_in), | ||
301 | kingsun->in_buf, kingsun->max_rx, | ||
302 | kingsun_rcv_irq, kingsun, 1); | ||
303 | kingsun->rx_urb->status = 0; | ||
304 | err = usb_submit_urb(kingsun->rx_urb, GFP_KERNEL); | ||
305 | if (err) { | ||
306 | err("kingsun-sir: first urb-submit failed: %d", err); | ||
307 | goto close_irlap; | ||
308 | } | ||
309 | |||
310 | netif_start_queue(netdev); | ||
311 | |||
312 | /* Situation at this point: | ||
313 | - all work buffers allocated | ||
314 | - urbs allocated and ready to fill | ||
315 | - max rx packet known (in max_rx) | ||
316 | - unwrap state machine initialized, in state outside of any frame | ||
317 | - receive request in progress | ||
318 | - IrLAP layer started, about to hand over packets to send | ||
319 | */ | ||
320 | |||
321 | return 0; | ||
322 | |||
323 | close_irlap: | ||
324 | irlap_close(kingsun->irlap); | ||
325 | free_mem: | ||
326 | if (kingsun->tx_urb) { | ||
327 | usb_free_urb(kingsun->tx_urb); | ||
328 | kingsun->tx_urb = NULL; | ||
329 | } | ||
330 | if (kingsun->rx_urb) { | ||
331 | usb_free_urb(kingsun->rx_urb); | ||
332 | kingsun->rx_urb = NULL; | ||
333 | } | ||
334 | if (kingsun->rx_buff.skb) { | ||
335 | kfree_skb(kingsun->rx_buff.skb); | ||
336 | kingsun->rx_buff.skb = NULL; | ||
337 | kingsun->rx_buff.head = NULL; | ||
338 | } | ||
339 | return err; | ||
340 | } | ||
341 | |||
342 | /* | ||
343 | * Function kingsun_net_close (kingsun) | ||
344 | * | ||
345 | * Network device is taken down. Usually this is done by | ||
346 | * "ifconfig irda0 down" | ||
347 | */ | ||
348 | static int kingsun_net_close(struct net_device *netdev) | ||
349 | { | ||
350 | struct kingsun_cb *kingsun = netdev_priv(netdev); | ||
351 | |||
352 | /* Stop transmit processing */ | ||
353 | netif_stop_queue(netdev); | ||
354 | |||
355 | /* Mop up receive && transmit urb's */ | ||
356 | usb_kill_urb(kingsun->tx_urb); | ||
357 | usb_kill_urb(kingsun->rx_urb); | ||
358 | |||
359 | usb_free_urb(kingsun->tx_urb); | ||
360 | usb_free_urb(kingsun->rx_urb); | ||
361 | |||
362 | kingsun->tx_urb = NULL; | ||
363 | kingsun->rx_urb = NULL; | ||
364 | |||
365 | kfree_skb(kingsun->rx_buff.skb); | ||
366 | kingsun->rx_buff.skb = NULL; | ||
367 | kingsun->rx_buff.head = NULL; | ||
368 | kingsun->rx_buff.in_frame = FALSE; | ||
369 | kingsun->rx_buff.state = OUTSIDE_FRAME; | ||
370 | kingsun->receiving = 0; | ||
371 | |||
372 | /* Stop and remove instance of IrLAP */ | ||
373 | if (kingsun->irlap) | ||
374 | irlap_close(kingsun->irlap); | ||
375 | |||
376 | kingsun->irlap = NULL; | ||
377 | |||
378 | return 0; | ||
379 | } | ||
380 | |||
381 | /* | ||
382 | * IOCTLs : Extra out-of-band network commands... | ||
383 | */ | ||
384 | static int kingsun_net_ioctl(struct net_device *netdev, struct ifreq *rq, | ||
385 | int cmd) | ||
386 | { | ||
387 | struct if_irda_req *irq = (struct if_irda_req *) rq; | ||
388 | struct kingsun_cb *kingsun = netdev_priv(netdev); | ||
389 | int ret = 0; | ||
390 | |||
391 | switch (cmd) { | ||
392 | case SIOCSBANDWIDTH: /* Set bandwidth */ | ||
393 | if (!capable(CAP_NET_ADMIN)) | ||
394 | return -EPERM; | ||
395 | |||
396 | /* Check if the device is still there */ | ||
397 | if (netif_device_present(kingsun->netdev)) | ||
398 | /* No observed commands for speed change */ | ||
399 | ret = -EOPNOTSUPP; | ||
400 | break; | ||
401 | |||
402 | case SIOCSMEDIABUSY: /* Set media busy */ | ||
403 | if (!capable(CAP_NET_ADMIN)) | ||
404 | return -EPERM; | ||
405 | |||
406 | /* Check if the IrDA stack is still there */ | ||
407 | if (netif_running(kingsun->netdev)) | ||
408 | irda_device_set_media_busy(kingsun->netdev, TRUE); | ||
409 | break; | ||
410 | |||
411 | case SIOCGRECEIVING: | ||
412 | /* Only approximately true */ | ||
413 | irq->ifr_receiving = kingsun->receiving; | ||
414 | break; | ||
415 | |||
416 | default: | ||
417 | ret = -EOPNOTSUPP; | ||
418 | } | ||
419 | |||
420 | return ret; | ||
421 | } | ||
422 | |||
423 | /* | ||
424 | * Get device stats (for /proc/net/dev and ifconfig) | ||
425 | */ | ||
426 | static struct net_device_stats * | ||
427 | kingsun_net_get_stats(struct net_device *netdev) | ||
428 | { | ||
429 | struct kingsun_cb *kingsun = netdev_priv(netdev); | ||
430 | return &kingsun->stats; | ||
431 | } | ||
432 | |||
433 | /* | ||
434 | * This routine is called by the USB subsystem for each new device | ||
435 | * in the system. We need to check if the device is ours, and in | ||
436 | * this case start handling it. | ||
437 | */ | ||
438 | static int kingsun_probe(struct usb_interface *intf, | ||
439 | const struct usb_device_id *id) | ||
440 | { | ||
441 | struct usb_host_interface *interface; | ||
442 | struct usb_endpoint_descriptor *endpoint; | ||
443 | |||
444 | struct usb_device *dev = interface_to_usbdev(intf); | ||
445 | struct kingsun_cb *kingsun = NULL; | ||
446 | struct net_device *net = NULL; | ||
447 | int ret = -ENOMEM; | ||
448 | int pipe, maxp_in, maxp_out; | ||
449 | __u8 ep_in; | ||
450 | __u8 ep_out; | ||
451 | |||
452 | /* Check that there really are two interrupt endpoints. | ||
453 | Check based on the one in drivers/usb/input/usbmouse.c | ||
454 | */ | ||
455 | interface = intf->cur_altsetting; | ||
456 | if (interface->desc.bNumEndpoints != 2) { | ||
457 | err("kingsun-sir: expected 2 endpoints, found %d", | ||
458 | interface->desc.bNumEndpoints); | ||
459 | return -ENODEV; | ||
460 | } | ||
461 | endpoint = &interface->endpoint[KINGSUN_EP_IN].desc; | ||
462 | if (!usb_endpoint_is_int_in(endpoint)) { | ||
463 | err("kingsun-sir: endpoint 0 is not interrupt IN"); | ||
464 | return -ENODEV; | ||
465 | } | ||
466 | |||
467 | ep_in = endpoint->bEndpointAddress; | ||
468 | pipe = usb_rcvintpipe(dev, ep_in); | ||
469 | maxp_in = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); | ||
470 | if (maxp_in > 255 || maxp_in <= 1) { | ||
471 | err("%s: endpoint 0 has max packet size %d not in range", | ||
472 | __FILE__, maxp_in); | ||
473 | return -ENODEV; | ||
474 | } | ||
475 | |||
476 | endpoint = &interface->endpoint[KINGSUN_EP_OUT].desc; | ||
477 | if (!usb_endpoint_is_int_out(endpoint)) { | ||
478 | err("kingsun-sir: endpoint 1 is not interrupt OUT"); | ||
479 | return -ENODEV; | ||
480 | } | ||
481 | |||
482 | ep_out = endpoint->bEndpointAddress; | ||
483 | pipe = usb_sndintpipe(dev, ep_out); | ||
484 | maxp_out = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); | ||
485 | |||
486 | /* Allocate network device container. */ | ||
487 | net = alloc_irdadev(sizeof(*kingsun)); | ||
488 | if(!net) | ||
489 | goto err_out1; | ||
490 | |||
491 | SET_MODULE_OWNER(net); | ||
492 | SET_NETDEV_DEV(net, &intf->dev); | ||
493 | kingsun = netdev_priv(net); | ||
494 | kingsun->irlap = NULL; | ||
495 | kingsun->tx_urb = NULL; | ||
496 | kingsun->rx_urb = NULL; | ||
497 | kingsun->ep_in = ep_in; | ||
498 | kingsun->ep_out = ep_out; | ||
499 | kingsun->in_buf = NULL; | ||
500 | kingsun->out_buf = NULL; | ||
501 | kingsun->max_rx = (__u8)maxp_in; | ||
502 | kingsun->max_tx = (__u8)maxp_out; | ||
503 | kingsun->netdev = net; | ||
504 | kingsun->usbdev = dev; | ||
505 | kingsun->rx_buff.in_frame = FALSE; | ||
506 | kingsun->rx_buff.state = OUTSIDE_FRAME; | ||
507 | kingsun->rx_buff.skb = NULL; | ||
508 | kingsun->receiving = 0; | ||
509 | spin_lock_init(&kingsun->lock); | ||
510 | |||
511 | /* Allocate input buffer */ | ||
512 | kingsun->in_buf = (__u8 *)kmalloc(kingsun->max_rx, GFP_KERNEL); | ||
513 | if (!kingsun->in_buf) | ||
514 | goto free_mem; | ||
515 | |||
516 | /* Allocate output buffer */ | ||
517 | kingsun->out_buf = (__u8 *)kmalloc(KINGSUN_FIFO_SIZE, GFP_KERNEL); | ||
518 | if (!kingsun->out_buf) | ||
519 | goto free_mem; | ||
520 | |||
521 | printk(KERN_INFO "KingSun/DonShine IRDA/USB found at address %d, " | ||
522 | "Vendor: %x, Product: %x\n", | ||
523 | dev->devnum, le16_to_cpu(dev->descriptor.idVendor), | ||
524 | le16_to_cpu(dev->descriptor.idProduct)); | ||
525 | |||
526 | /* Initialize QoS for this device */ | ||
527 | irda_init_max_qos_capabilies(&kingsun->qos); | ||
528 | |||
529 | /* That's the Rx capability. */ | ||
530 | kingsun->qos.baud_rate.bits &= IR_9600; | ||
531 | kingsun->qos.min_turn_time.bits &= KINGSUN_MTT; | ||
532 | irda_qos_bits_to_value(&kingsun->qos); | ||
533 | |||
534 | /* Override the network functions we need to use */ | ||
535 | net->hard_start_xmit = kingsun_hard_xmit; | ||
536 | net->open = kingsun_net_open; | ||
537 | net->stop = kingsun_net_close; | ||
538 | net->get_stats = kingsun_net_get_stats; | ||
539 | net->do_ioctl = kingsun_net_ioctl; | ||
540 | |||
541 | ret = register_netdev(net); | ||
542 | if (ret != 0) | ||
543 | goto free_mem; | ||
544 | |||
545 | info("IrDA: Registered KingSun/DonShine device %s", net->name); | ||
546 | |||
547 | usb_set_intfdata(intf, kingsun); | ||
548 | |||
549 | /* Situation at this point: | ||
550 | - all work buffers allocated | ||
551 | - urbs not allocated, set to NULL | ||
552 | - max rx packet known (in max_rx) | ||
553 | - unwrap state machine (partially) initialized, but skb == NULL | ||
554 | */ | ||
555 | |||
556 | return 0; | ||
557 | |||
558 | free_mem: | ||
559 | if (kingsun->out_buf) kfree(kingsun->out_buf); | ||
560 | if (kingsun->in_buf) kfree(kingsun->in_buf); | ||
561 | free_netdev(net); | ||
562 | err_out1: | ||
563 | return ret; | ||
564 | } | ||
565 | |||
566 | /* | ||
567 | * The current device is removed, the USB layer tell us to shut it down... | ||
568 | */ | ||
569 | static void kingsun_disconnect(struct usb_interface *intf) | ||
570 | { | ||
571 | struct kingsun_cb *kingsun = usb_get_intfdata(intf); | ||
572 | |||
573 | if (!kingsun) | ||
574 | return; | ||
575 | |||
576 | unregister_netdev(kingsun->netdev); | ||
577 | |||
578 | /* Mop up receive && transmit urb's */ | ||
579 | if (kingsun->tx_urb != NULL) { | ||
580 | usb_kill_urb(kingsun->tx_urb); | ||
581 | usb_free_urb(kingsun->tx_urb); | ||
582 | kingsun->tx_urb = NULL; | ||
583 | } | ||
584 | if (kingsun->rx_urb != NULL) { | ||
585 | usb_kill_urb(kingsun->rx_urb); | ||
586 | usb_free_urb(kingsun->rx_urb); | ||
587 | kingsun->rx_urb = NULL; | ||
588 | } | ||
589 | |||
590 | kfree(kingsun->out_buf); | ||
591 | kfree(kingsun->in_buf); | ||
592 | free_netdev(kingsun->netdev); | ||
593 | |||
594 | usb_set_intfdata(intf, NULL); | ||
595 | } | ||
596 | |||
597 | #ifdef CONFIG_PM | ||
598 | /* USB suspend, so power off the transmitter/receiver */ | ||
599 | static int kingsun_suspend(struct usb_interface *intf, pm_message_t message) | ||
600 | { | ||
601 | struct kingsun_cb *kingsun = usb_get_intfdata(intf); | ||
602 | |||
603 | netif_device_detach(kingsun->netdev); | ||
604 | if (kingsun->tx_urb != NULL) usb_kill_urb(kingsun->tx_urb); | ||
605 | if (kingsun->rx_urb != NULL) usb_kill_urb(kingsun->rx_urb); | ||
606 | return 0; | ||
607 | } | ||
608 | |||
609 | /* Coming out of suspend, so reset hardware */ | ||
610 | static int kingsun_resume(struct usb_interface *intf) | ||
611 | { | ||
612 | struct kingsun_cb *kingsun = usb_get_intfdata(intf); | ||
613 | |||
614 | if (kingsun->rx_urb != NULL) | ||
615 | usb_submit_urb(kingsun->rx_urb, GFP_KERNEL); | ||
616 | netif_device_attach(kingsun->netdev); | ||
617 | |||
618 | return 0; | ||
619 | } | ||
620 | #endif | ||
621 | |||
622 | /* | ||
623 | * USB device callbacks | ||
624 | */ | ||
625 | static struct usb_driver irda_driver = { | ||
626 | .name = "kingsun-sir", | ||
627 | .probe = kingsun_probe, | ||
628 | .disconnect = kingsun_disconnect, | ||
629 | .id_table = dongles, | ||
630 | #ifdef CONFIG_PM | ||
631 | .suspend = kingsun_suspend, | ||
632 | .resume = kingsun_resume, | ||
633 | #endif | ||
634 | }; | ||
635 | |||
636 | /* | ||
637 | * Module insertion | ||
638 | */ | ||
639 | static int __init kingsun_init(void) | ||
640 | { | ||
641 | return usb_register(&irda_driver); | ||
642 | } | ||
643 | module_init(kingsun_init); | ||
644 | |||
645 | /* | ||
646 | * Module removal | ||
647 | */ | ||
648 | static void __exit kingsun_cleanup(void) | ||
649 | { | ||
650 | /* Deregister the driver and remove all pending instances */ | ||
651 | usb_deregister(&irda_driver); | ||
652 | } | ||
653 | module_exit(kingsun_cleanup); | ||
654 | |||
655 | MODULE_AUTHOR("Alex Villac�s Lasso <a_villacis@palosanto.com>"); | ||
656 | MODULE_DESCRIPTION("IrDA-USB Dongle Driver for KingSun/DonShine"); | ||
657 | MODULE_LICENSE("GPL"); | ||