Root/drivers/ieee802154/atusb.c

1/*
2 * atusb.c - SPI host look-alike for ATSUB
3 *
4 * Written 2011 by Werner Almesberger <werner@almesberger.net>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation, version 2
9 *
10 * Based on at86rf230.c and spi_atusb.c.
11 * at86rf230.c is
12 * Copyright (C) 2009 Siemens AG
13 * Written by: Dmitry Eremin-Solenikov <dmitry.baryshkov@siemens.com>
14 *
15 * spi_atusb.c is
16 * Copyright (c) 2011 Richard Sharpe <realrichardsharpe@gmail.com>
17 * Copyright (c) 2011 Stefan Schmidt <stefan@datenfreihafen.org>
18 * Copyright (c) 2011 Werner Almesberger <werner@almesberger.net>
19 */
20
21/*
22 * - implement more robust interrupt synchronization
23 * - check URB killing in atusb_disconnect for races
24 * - switch from bulk to interrupt endpoint
25 * - implement buffer read without extra copy
26 * - harmonize indentation style
27 * - mv atusb.c ../ieee802.15.4/spi_atusb.c, or maybe atrf_atusb.c or such
28 * - check module load/unload
29 * - review dev_* severity levels
30 */
31
32#include <linux/kernel.h>
33#include <linux/module.h>
34#include <linux/platform_device.h>
35#include <linux/jiffies.h>
36#include <linux/timer.h>
37#include <linux/interrupt.h>
38#include <linux/usb.h>
39
40#include "at86rf230.h"
41
42
43#define VENDOR_ID 0x20b7
44#define PRODUCT_ID 0x1540
45
46/* The devices we work with */
47static const struct usb_device_id atusb_device_table[] = {
48    { USB_DEVICE(VENDOR_ID, PRODUCT_ID) },
49    { },
50};
51MODULE_DEVICE_TABLE(usb, atusb_device_table);
52
53#define ATUSB_BUILD_SIZE 256
54struct atusb_local {
55    struct usb_device * udev;
56    /* The interface to the RF part info, if applicable */
57    uint8_t ep0_atusb_major;
58    uint8_t ep0_atusb_minor;
59    uint8_t atusb_hw_type;
60    struct spi_master *master;
61    int slave_irq;
62    struct urb *irq_urb;
63    uint8_t irq_buf; /* receive irq serial here*/
64    uint8_t irq_seen; /* last irq serial from bulk */
65    uint8_t irq_sync; /* last irq serial from WRITE2_SYNC */
66    struct tasklet_struct task; /* interrupt delivery tasklet */
67    struct timer_list timer; /* delay, for interrupt synch */
68    struct at86rf230_platform_data platform_data;
69    /* copy platform_data so that we can adapt .reset_data */
70    struct spi_device *spi;
71// unsigned char buffer[3];
72    unsigned char buffer[260]; /* XXL, just in case */
73    struct spi_message *msg;
74};
75
76/* Commands to our device. Make sure this is synced with the firmware */
77enum atspi_requests {
78    ATUSB_ID = 0x00, /* system status/control grp */
79    ATUSB_BUILD,
80    ATUSB_RESET,
81    ATUSB_RF_RESET = 0x10, /* debug/test group */
82    ATUSB_POLL_INT,
83    ATUSB_TEST, /* atusb-sil only */
84    ATUSB_TIMER,
85    ATUSB_GPIO,
86    ATUSB_SLP_TR,
87    ATUSB_GPIO_CLEANUP,
88    ATUSB_REG_WRITE = 0x20, /* transceiver group */
89    ATUSB_REG_READ,
90    ATUSB_BUF_WRITE,
91    ATUSB_BUF_READ,
92    ATUSB_SRAM_WRITE,
93    ATUSB_SRAM_READ,
94    ATUSB_SPI_WRITE = 0x30, /* SPI group */
95    ATUSB_SPI_READ1,
96    ATUSB_SPI_READ2,
97    ATUSB_SPI_WRITE2_SYNC,
98    ATUSB_RX_MODE = 0x40, /* HardMAC group */
99    ATUSB_TX,
100};
101
102/*
103 * Direction bRequest wValue wIndex wLength
104 *
105 * ->host ATUSB_ID - - 3
106 * ->host ATUSB_BUILD - - #bytes
107 * host-> ATUSB_RESET - - 0
108 *
109 * host-> ATUSB_RF_RESET - - 0
110 * ->host ATUSB_POLL_INT - - 1
111 * host-> ATUSB_TEST - - 0
112 * ->host ATUSB_TIMER - - #bytes (6)
113 * ->host ATUSB_GPIO dir+data mask+p# 3
114 * host-> ATUSB_SLP_TR - - 0
115 * host-> ATUSB_GPIO_CLEANUP - - 0
116 *
117 * host-> ATUSB_REG_WRITE value addr 0
118 * ->host ATUSB_REG_READ - addr 1
119 * host-> ATUSB_BUF_WRITE - - #bytes
120 * ->host ATUSB_BUF_READ - - #bytes
121 * host-> ATUSB_SRAM_WRITE - addr #bytes
122 * ->host ATUSB_SRAM_READ - addr #bytes
123 *
124 * host-> ATUSB_SPI_WRITE byte0 byte1 #bytes
125 * ->host ATUSB_SPI_READ1 byte0 - #bytes
126 * ->host ATUSB_SPI_READ2 byte0 byte1 #bytes
127 * ->host ATUSB_SPI_WRITE2_SYNC byte0 byte1 0/1
128 *
129 * host-> ATUSB_RX_MODE on - 0
130 * host-> ATUSB_TX flags 0 #bytes
131 */
132
133#define ATUSB_FROM_DEV (USB_TYPE_VENDOR | USB_DIR_IN)
134#define ATUSB_TO_DEV (USB_TYPE_VENDOR | USB_DIR_OUT)
135
136
137/* ----- Control transfers ------------------------------------------------- */
138
139
140static int atusb_async_errchk(struct urb *urb)
141{
142    struct atusb_local *atusb = urb->context;
143    struct spi_message *msg = atusb->msg;
144    struct usb_device *dev = atusb->udev;
145
146    if (!urb->status) {
147        dev_dbg(&dev->dev, "atusb_async_errchk OK len %d\n",
148            urb->actual_length);
149        return 0;
150    }
151    
152    if (urb->status != -ENOENT && urb->status != -ECONNRESET &&
153        urb->status != -ESHUTDOWN)
154        dev_info(&dev->dev, "atusb_async_errchk FAIL error %d\n",
155            urb->status);
156
157    msg->actual_length = 0;
158
159    return urb->status;
160}
161
162static void atusb_async_finish(struct urb *urb)
163{
164    struct atusb_local *atusb = urb->context;
165    struct spi_message *msg = atusb->msg;
166
167    msg->status = urb->status;
168    msg->complete(msg->context);
169
170    kfree(urb->setup_packet);
171    usb_free_urb(urb);
172}
173
174static void atusb_ctrl_cb(struct urb *urb)
175{
176    atusb_async_errchk(urb);
177    atusb_async_finish(urb);
178}
179
180static void atusb_timer(unsigned long data)
181{
182    struct urb *urb = (void *) data;
183
184    dev_warn(&urb->dev->dev, "atusb_timer\n");
185    atusb_async_finish(urb);
186}
187
188static void atusb_ctrl_cb_sync(struct urb *urb)
189{
190    struct atusb_local *atusb = urb->context;
191
192    /* @@@ needs locking/atomic */
193    if (atusb_async_errchk(urb) || atusb->irq_sync == atusb->irq_seen) {
194        atusb_async_finish(urb);
195        return;
196    }
197    
198    BUG_ON(timer_pending(&atusb->timer));
199    atusb->timer.expires = jiffies+msecs_to_jiffies(SYNC_TIMEOUT_MS);
200    atusb->timer.data = (unsigned long) urb;
201    add_timer(&atusb->timer);
202}
203
204static void atusb_read_fb_cb(struct urb *urb)
205{
206    struct atusb_local *atusb = urb->context;
207    struct spi_message *msg = atusb->msg;
208    const struct spi_transfer *xfer;
209    uint8_t *rx;
210
211    if (!atusb_async_errchk(urb)) {
212        BUG_ON(!urb->actual_length);
213
214        xfer = list_first_entry(&msg->transfers, struct spi_transfer,
215            transfer_list);
216        rx = xfer->rx_buf;
217        rx[1] = atusb->buffer[0];
218
219        xfer = list_entry(xfer->transfer_list.next,
220            struct spi_transfer, transfer_list);
221        memcpy(xfer->rx_buf, atusb->buffer+1, urb->actual_length-1);
222    }
223
224    atusb_async_finish(urb);
225}
226
227static int submit_control_msg(struct atusb_local *atusb,
228    __u8 request, __u8 requesttype, __u16 value, __u16 index,
229    void *data, __u16 size, usb_complete_t complete_fn, void *context)
230{
231    struct usb_device *dev = atusb->udev;
232    struct usb_ctrlrequest *req;
233    struct urb *urb;
234    int retval = -ENOMEM;
235
236    req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
237    if (!req)
238        return -ENOMEM;
239
240    req->bRequest = request;
241    req->bRequestType = requesttype;
242    req->wValue = cpu_to_le16(value);
243    req->wIndex = cpu_to_le16(index);
244    req->wLength = cpu_to_le16(size);
245
246    urb = usb_alloc_urb(0, GFP_KERNEL);
247    if (!urb)
248        goto out_nourb;
249
250    usb_fill_control_urb(urb, dev,
251        requesttype == ATUSB_FROM_DEV ?
252          usb_rcvctrlpipe(dev, 0) : usb_sndctrlpipe(dev, 0),
253        (unsigned char *) req, data, size, complete_fn, context);
254
255    retval = usb_submit_urb(urb, GFP_KERNEL);
256    if (!retval)
257        return 0;
258    dev_warn(&dev->dev, "failed submitting read urb, error %d",
259        retval);
260    retval = retval == -ENOMEM ? retval : -EIO;
261
262    usb_free_urb(urb);
263out_nourb:
264    kfree(req);
265
266    return retval;
267}
268
269
270/* ----- SPI transfers ----------------------------------------------------- */
271
272
273static int atusb_read1(struct atusb_local *atusb,
274    uint8_t tx, uint8_t *rx, int len)
275{
276    dev_dbg(&atusb->udev->dev, "atusb_read1: tx = 0x%x\n", tx);
277    return submit_control_msg(atusb,
278        ATUSB_SPI_READ1, ATUSB_FROM_DEV, tx, 0,
279        rx, 1, atusb_ctrl_cb, atusb);
280}
281
282static int atusb_read_fb(struct atusb_local *atusb,
283    uint8_t tx, uint8_t *rx0, uint8_t *rx, int len)
284{
285    dev_dbg(&atusb->udev->dev, "atusb_read_fb: tx = 0x%x\n", tx);
286    return submit_control_msg(atusb,
287        ATUSB_SPI_READ1, ATUSB_FROM_DEV, tx, 0,
288        atusb->buffer, len+1, atusb_read_fb_cb, atusb);
289}
290
291static int atusb_write(struct atusb_local *atusb,
292    uint8_t tx0, uint8_t tx1, const uint8_t *tx, int len)
293{
294    dev_dbg(&atusb->udev->dev,
295        "atusb_write: tx0 = 0x%x tx1 = 0x%x\n", tx0, tx1);
296
297    /*
298     * The AT86RF230 driver sometimes requires a transceiver state
299     * transition to be an interrupt barrier. This is the case after
300     * writing FORCE_TX_ON to the TRX_CMD field in the TRX_STATE register.
301     *
302     * Since there is no other means of notification, we just decode the
303     * transfer and do a bit of pattern matching.
304     */
305    if (tx0 == (CMD_REG | CMD_WRITE | RG_TRX_STATE) &&
306        (tx1 & 0x1f) == STATE_FORCE_TX_ON)
307        return submit_control_msg(atusb,
308            ATUSB_SPI_WRITE2_SYNC, ATUSB_FROM_DEV, tx0, tx1,
309             &atusb->irq_sync, 1, atusb_ctrl_cb_sync, atusb);
310    else
311        return submit_control_msg(atusb,
312            ATUSB_SPI_WRITE, ATUSB_TO_DEV, tx0, tx1,
313            (uint8_t *) tx, len, atusb_ctrl_cb, atusb);
314}
315
316static int atusb_transfer(struct spi_device *spi, struct spi_message *msg)
317{
318    struct atusb_local *atusb = spi_master_get_devdata(spi->master);
319    struct spi_transfer *xfer;
320    struct spi_transfer *x[2];
321    int n;
322    const uint8_t *tx;
323    uint8_t *rx;
324    int len;
325    int retval = 0;
326
327    if (unlikely(list_empty(&msg->transfers))) {
328        dev_err(&atusb->udev->dev, "transfer is empty\n");
329        return -EINVAL;
330    }
331
332    atusb->msg = msg;
333
334    /* Classify the request */
335    n = 0;
336    list_for_each_entry(xfer, &msg->transfers, transfer_list) {
337        if (n == ARRAY_SIZE(x)) {
338            dev_err(&atusb->udev->dev, "too many transfers\n");
339            return -EINVAL;
340        }
341        x[n] = xfer;
342        n++;
343    }
344
345    tx = x[0]->tx_buf;
346    rx = x[0]->rx_buf;
347    len = x[0]->len;
348
349    msg->actual_length = len;
350
351    if (!tx || len != 2)
352        goto bad_req;
353    if (n == 1) {
354        if (rx) {
355            dev_dbg(&atusb->udev->dev, "read 1\n");
356            retval = atusb_read1(atusb, tx[0], rx+1, len-1);
357        } else {
358            dev_dbg(&atusb->udev->dev, "write 2\n");
359            /*
360             * Don't take our clock away !! ;-)
361             */
362            if (tx[0] == (CMD_REG | CMD_WRITE | RG_TRX_CTRL_0)) {
363                msg->status = 0;
364                msg->complete(msg->context);
365            } else {
366                retval = atusb_write(atusb,
367                    tx[0], tx[1], NULL, 0);
368            }
369        }
370    } else {
371        if (x[0]->rx_buf) {
372            if (x[1]->tx_buf || !x[1]->rx_buf)
373                goto bad_req;
374            dev_dbg(&atusb->udev->dev, "read 1+\n");
375            retval = atusb_read_fb(atusb, tx[0], rx+1,
376                x[1]->rx_buf, x[1]->len);
377        } else {
378            if (!x[1]->tx_buf ||x[1]->rx_buf)
379                goto bad_req;
380            dev_dbg(&atusb->udev->dev, "write 2+n\n");
381            retval = atusb_write(atusb, tx[0], tx[1],
382                x[1]->tx_buf, x[1]->len);
383        }
384    }
385    return retval;
386
387bad_req:
388    dev_err(&atusb->udev->dev, "unrecognized request:\n");
389    list_for_each_entry(xfer, &msg->transfers, transfer_list)
390        dev_err(&atusb->udev->dev, "%stx %srx len %u\n",
391            xfer->tx_buf ? "" : "!", xfer->rx_buf ? " " : "!",
392            xfer->len);
393    return -EINVAL;
394}
395
396static int atusb_setup(struct spi_device *spi)
397{
398    return 0;
399}
400
401
402/* ----- Interrupt handling ------------------------------------------------ */
403
404
405static void atusb_tasklet(unsigned long data)
406{
407    struct atusb_local *atusb = (void *) data;
408
409    generic_handle_irq(atusb->slave_irq);
410}
411
412static void atusb_irq(struct urb *urb)
413{
414    struct atusb_local *atusb = urb->context;
415
416    dev_dbg(&urb->dev->dev, "atusb_irq (%d), seen %d sync %d\n",
417        urb->status, atusb->irq_buf, atusb->irq_sync);
418    if (!urb->status) {
419        atusb->irq_seen = atusb->irq_buf;
420        if (atusb->irq_sync == atusb->irq_seen &&
421            try_to_del_timer_sync(&atusb->timer) == 1)
422            atusb_async_finish((struct urb *) atusb->timer.data);
423    }
424    usb_free_urb(urb);
425    atusb->irq_urb = NULL;
426    tasklet_schedule(&atusb->task);
427}
428
429static int atusb_arm_interrupt(struct atusb_local *atusb)
430{
431    struct usb_device *dev = atusb->udev;
432    struct urb *urb;
433    int retval = -ENOMEM;
434
435    BUG_ON(atusb->irq_urb);
436
437    dev_vdbg(&dev->dev, "atusb_arm_interrupt\n");
438    urb = usb_alloc_urb(0, GFP_KERNEL);
439    if (!urb) {
440        dev_err(&dev->dev,
441            "atusb_arm_interrupt: usb_alloc_urb failed\n");
442        return -ENOMEM;
443    }
444
445    usb_fill_bulk_urb(urb, dev, usb_rcvbulkpipe(dev, 1),
446        &atusb->irq_buf, 1, atusb_irq, atusb);
447    atusb->irq_urb = urb;
448    retval = usb_submit_urb(urb, GFP_KERNEL);
449    if (!retval)
450        return 0;
451
452    dev_err(&dev->dev, "failed submitting bulk urb, error %d\n", retval);
453    retval = retval == -ENOMEM ? retval : -EIO;
454
455    usb_free_urb(urb);
456
457    return retval;
458}
459
460static void atusb_irq_mask(struct irq_data *data)
461{
462    struct atusb_local *atusb = irq_data_get_irq_chip_data(data);
463
464    dev_vdbg(&atusb->udev->dev, "atusb_irq_mask\n");
465    tasklet_disable_nosync(&atusb->task);
466}
467
468static void atusb_irq_unmask(struct irq_data *data)
469{
470    struct atusb_local *atusb = irq_data_get_irq_chip_data(data);
471
472    dev_vdbg(&atusb->udev->dev, "atusb_irq_unmask\n");
473    tasklet_enable(&atusb->task);
474}
475
476static void atusb_irq_ack(struct irq_data *data)
477{
478    struct atusb_local *atusb = irq_data_get_irq_chip_data(data);
479
480    dev_vdbg(&atusb->udev->dev, "atusb_irq_ack\n");
481    atusb_arm_interrupt(atusb);
482}
483
484static struct irq_chip atusb_irq_chip = {
485    .name = "atusb-slave",
486    .irq_mask = atusb_irq_mask,
487    .irq_unmask = atusb_irq_unmask,
488    .irq_ack = atusb_irq_ack,
489};
490
491
492/* ----- Transceiver reset ------------------------------------------------- */
493
494
495static void atusb_reset(void *reset_data)
496{
497    int retval;
498    struct atusb_local *atusb = reset_data;
499
500    retval = usb_control_msg(atusb->udev,
501        usb_rcvctrlpipe(atusb->udev, 0),
502        ATUSB_RF_RESET, ATUSB_TO_DEV, 0, 0,
503        NULL, 0, 1000);
504    if (retval < 0) {
505        dev_err(&atusb->udev->dev,
506            "%s: error doing reset retval = %d\n",
507            __func__, retval);
508    }
509}
510
511
512/* ----- Firmware version information -------------------------------------- */
513
514
515static int atusb_get_and_show_revision(struct atusb_local *atusb)
516{
517    struct usb_device *dev = atusb->udev;
518    int retval;
519
520    /* Get a couple of the ATMega Firmware values */
521    retval = usb_control_msg(dev,
522        usb_rcvctrlpipe(dev, 0),
523        ATUSB_ID, ATUSB_FROM_DEV, 0, 0,
524        atusb->buffer, 3, 1000);
525    if (retval < 0) {
526        dev_info(&dev->dev,
527            "failed submitting urb for ATUSB_ID, error %d\n", retval);
528        return retval == -ENOMEM ? retval : -EIO;
529    }
530
531    atusb->ep0_atusb_major = atusb->buffer[0];
532    atusb->ep0_atusb_minor = atusb->buffer[1];
533    atusb->atusb_hw_type = atusb->buffer[2];
534    dev_info(&dev->dev,
535        "Firmware: major: %u, minor: %u, hardware type: %u\n",
536        atusb->ep0_atusb_major, atusb->ep0_atusb_minor,
537        atusb->atusb_hw_type);
538
539    return 0;
540}
541
542static int atusb_get_and_show_build(struct atusb_local *atusb)
543{
544    struct usb_device *dev = atusb->udev;
545    char build[ATUSB_BUILD_SIZE+1];
546    int retval;
547
548    retval = usb_control_msg(dev,
549        usb_rcvctrlpipe(atusb->udev, 0),
550        ATUSB_BUILD, ATUSB_FROM_DEV, 0, 0,
551        build, ATUSB_BUILD_SIZE, 1000);
552    if (retval < 0) {
553        dev_err(&dev->dev,
554            "failed submitting urb for ATUSB_BUILD, error %d\n",
555            retval);
556        return retval == -ENOMEM ? retval : -EIO;
557    }
558
559    build[retval] = 0;
560    dev_info(&dev->dev, "Firmware: build %s\n", build);
561
562    return 0;
563}
564
565
566/* ----- Setup ------------------------------------------------------------- */
567
568
569struct at86rf230_platform_data at86rf230_platform_data = {
570    .rstn = -1,
571    .slp_tr = -1,
572    .dig2 = -1,
573    .reset = atusb_reset,
574    /* set .reset_data later */
575};
576
577static int atusb_probe(struct usb_interface *interface,
578            const struct usb_device_id *id)
579{
580    struct usb_device *usb_dev = interface_to_usbdev(interface);
581    struct ieee802154_dev *dev;
582    
583    struct atusb_local *atusb = NULL;
584    int retval;
585
586    /*
587     * Ignore all interfaces used for DFU, i.e., everything while in the
588     * boot loader, and interface #1 when in the application.
589     */
590    if (interface->cur_altsetting->desc.bInterfaceClass !=
591        USB_CLASS_VENDOR_SPEC) {
592        dev_dbg(&udev->dev,
593            "Ignoring interface with class 0x%02x\n",
594            interface->cur_altsetting->desc.bInterfaceClass);
595        return -ENODEV;
596    }
597
598    dev = ieee802154_alloc_device(sizeof(struct atben_prv), &atusb_ops);
599    if (!dev)
600        return -ENOMEM;
601
602    prv = dev->priv;
603    prv->usb_dev = usb_dev;
604    prv->udev = usb_get_dev(usb_dev);
605    usb_set_intfdata(interface, prv);
606
607    dev->parent = &usb_dev->dev;
608    dev->extra_tx_headroom = 0;
609    dev->phy->channels_supported[0] = 0x7FFF800;
610    dev->flags = IEEE802154_HW_OMIT_CKSUM;
611
612    if (atusb_get_and_show_revision(atusb) < 0)
613        goto err_master;
614    if (atusb_get_and_show_build(atusb) < 0)
615        goto err_master;
616
617
618    return 0;
619
620err_master:
621    /*
622     * If we come here from a partially successful driver initialization,
623     * we don't really know how much it has done. In particular, it may
624     * have triggered an interrupt and thus removed the interrupt URB and
625     * maybe scheduled the tasklet.
626     */
627    tasklet_disable(&atusb->task);
628    if (atusb->irq_urb)
629        usb_kill_urb(atusb->irq_urb);
630    spi_master_put(atusb->master);
631err_slave_irq:
632    set_irq_chained_handler(atusb->slave_irq, NULL);
633    set_irq_chip_data(atusb->slave_irq, NULL);
634    irq_free_desc(atusb->slave_irq);
635err_free:
636    return retval;
637}
638
639static void atusb_disconnect(struct usb_interface *interface)
640{
641    struct atusb_local *atusb = usb_get_intfdata(interface);
642    struct spi_master *master = atusb->master;
643
644    tasklet_disable(&atusb->task);
645    /* @@@ this needs some extra protecion - wa */
646    if (atusb->irq_urb)
647        usb_kill_urb(atusb->irq_urb);
648
649    BUG_ON(timer_pending(&atusb->timer));
650
651    usb_set_intfdata(interface, NULL);
652    usb_put_dev(atusb->udev);
653
654    spi_dev_put(atusb->spi);
655
656    spi_unregister_master(master);
657
658    set_irq_chained_handler(atusb->slave_irq, NULL);
659    set_irq_chip_data(atusb->slave_irq, NULL);
660    irq_free_desc(atusb->slave_irq);
661
662    spi_master_put(master);
663}
664
665void atusb_release(struct device *dev)
666{
667    return;
668}
669
670static struct usb_driver atusb_driver = {
671    .name = "atusb_ben-wpan",
672    .probe = atusb_probe,
673    .disconnect = atusb_disconnect,
674    .id_table = atusb_device_table,
675};
676
677static struct platform_device atusb_device = {
678    .name = "spi_atusb",
679    .id = -1,
680    .dev.release = atusb_release,
681};
682
683static int __init atusb_init(void)
684{
685    int retval;
686
687    retval = platform_device_register(&atusb_device);
688    if (retval)
689        return retval;
690
691    return usb_register(&atusb_driver);
692}
693
694static void __exit atusb_exit(void)
695{
696    usb_deregister(&atusb_driver);
697    platform_device_unregister(&atusb_device);
698}
699
700module_init (atusb_init);
701module_exit (atusb_exit);
702
703MODULE_AUTHOR("Richard Sharpe <realrichardsharpe@gmail.com>");
704MODULE_AUTHOR("Stefan Schmidt <stefan@datenfreihafen.org>");
705MODULE_AUTHOR("Werner Almesberger <werner@almesberger.net>");
706MODULE_DESCRIPTION("ATUSB ben-wpan Driver");
707MODULE_LICENSE("GPL");
708

Archive Download this file



interactive