aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/irda/kingsun-sir.c
diff options
context:
space:
mode:
authorAlex Villac�s Lasso <a_villacis@palosanto.com>2007-05-09 19:18:21 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2007-05-11 02:46:13 -0400
commita2af421f1819946556c6f467b1efdd0dc84af4d5 (patch)
treeeaee8d9b7066e28651398e18e404a4c97c8702fb /drivers/net/irda/kingsun-sir.c
parent9a6bf6fe716ea09160ee11660ee66a930167692b (diff)
[IrDA]: KingSun/DonShine USB IrDA dongle support.
This dongle does not follow the usb-irda specification, so it needs its own special driver. In addition, it uses interrupt endpoints instead of bulk ones as the rest of USB IrDA dongles supported by Linux (just to be different?) and data reads need to be parsed to extract the valid bytes before being unwrapped (details in the comment at the start of the source). No speed commands have been discovered for this dongle, and I suspect it does not have any at all. On plugin, this dongle reports vendor and device IDs: 0x07c0:0x4200 . The Windows driver that is used normally to control this dongle has a filename of DSIR620.SYS . Signed-off-by: Alex Villac�s Lasso <a_villacis@palosanto.com> Signed-off-by: Samuel Ortiz <samuel@sortiz.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/irda/kingsun-sir.c')
-rw-r--r--drivers/net/irda/kingsun-sir.c657
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 000000000000..217429122e79
--- /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 */
91static struct usb_device_id dongles[] = {
92 /* KingSun Co,Ltd IrDA/USB Bridge */
93 { USB_DEVICE(KING_VENDOR_ID, KING_PRODUCT_ID) },
94 { }
95};
96
97MODULE_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
105struct 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 */
132static 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 */
154static 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 */
205static 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 */
258static 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 */
348static 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 */
384static 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 */
426static struct net_device_stats *
427kingsun_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 */
438static 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
558free_mem:
559 if (kingsun->out_buf) kfree(kingsun->out_buf);
560 if (kingsun->in_buf) kfree(kingsun->in_buf);
561 free_netdev(net);
562err_out1:
563 return ret;
564}
565
566/*
567 * The current device is removed, the USB layer tell us to shut it down...
568 */
569static 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 */
599static 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 */
610static 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 */
625static 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 */
639static int __init kingsun_init(void)
640{
641 return usb_register(&irda_driver);
642}
643module_init(kingsun_init);
644
645/*
646 * Module removal
647 */
648static void __exit kingsun_cleanup(void)
649{
650 /* Deregister the driver and remove all pending instances */
651 usb_deregister(&irda_driver);
652}
653module_exit(kingsun_cleanup);
654
655MODULE_AUTHOR("Alex Villac�s Lasso <a_villacis@palosanto.com>");
656MODULE_DESCRIPTION("IrDA-USB Dongle Driver for KingSun/DonShine");
657MODULE_LICENSE("GPL");