Date: | 2011-09-25 21:11:19 (12 years 5 months ago) |
---|---|
Author: | Werner Almesberger |
Commit: | 815a0e30c5db6b829e4eb5a21070bc732bb9dd78 |
Message: | atusb driver for extended mode (untested and unfinished ) |
Files: |
drivers/ieee802154/atusb.c (1 diff) |
Change Details
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 */ | |
47 | static const struct usb_device_id atusb_device_table[] = { | |
48 | { USB_DEVICE(VENDOR_ID, PRODUCT_ID) }, | |
49 | { }, | |
50 | }; | |
51 | MODULE_DEVICE_TABLE(usb, atusb_device_table); | |
52 | ||
53 | #define ATUSB_BUILD_SIZE 256 | |
54 | struct 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 */ | |
77 | enum 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 | ||
140 | static 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 | ||
162 | static 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 | ||
174 | static void atusb_ctrl_cb(struct urb *urb) | |
175 | { | |
176 | atusb_async_errchk(urb); | |
177 | atusb_async_finish(urb); | |
178 | } | |
179 | ||
180 | static 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 | ||
188 | static 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 | ||
204 | static 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 | ||
227 | static 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); | |
263 | out_nourb: | |
264 | kfree(req); | |
265 | ||
266 | return retval; | |
267 | } | |
268 | ||
269 | ||
270 | /* ----- SPI transfers ----------------------------------------------------- */ | |
271 | ||
272 | ||
273 | static 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 | ||
282 | static 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 | ||
291 | static 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 | ||
316 | static 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 | ||
387 | bad_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 | ||
396 | static int atusb_setup(struct spi_device *spi) | |
397 | { | |
398 | return 0; | |
399 | } | |
400 | ||
401 | ||
402 | /* ----- Interrupt handling ------------------------------------------------ */ | |
403 | ||
404 | ||
405 | static void atusb_tasklet(unsigned long data) | |
406 | { | |
407 | struct atusb_local *atusb = (void *) data; | |
408 | ||
409 | generic_handle_irq(atusb->slave_irq); | |
410 | } | |
411 | ||
412 | static 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 | ||
429 | static 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 | ||
460 | static 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 | ||
468 | static 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 | ||
476 | static 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 | ||
484 | static 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 | ||
495 | static 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 | ||
515 | static 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 | ||
542 | static 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 | ||
569 | struct 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 | ||
577 | static 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 | ||
620 | err_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); | |
631 | err_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); | |
635 | err_free: | |
636 | return retval; | |
637 | } | |
638 | ||
639 | static 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 | ||
665 | void atusb_release(struct device *dev) | |
666 | { | |
667 | return; | |
668 | } | |
669 | ||
670 | static 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 | ||
677 | static struct platform_device atusb_device = { | |
678 | .name = "spi_atusb", | |
679 | .id = -1, | |
680 | .dev.release = atusb_release, | |
681 | }; | |
682 | ||
683 | static 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 | ||
694 | static void __exit atusb_exit(void) | |
695 | { | |
696 | usb_deregister(&atusb_driver); | |
697 | platform_device_unregister(&atusb_device); | |
698 | } | |
699 | ||
700 | module_init (atusb_init); | |
701 | module_exit (atusb_exit); | |
702 | ||
703 | MODULE_AUTHOR("Richard Sharpe <realrichardsharpe@gmail.com>"); | |
704 | MODULE_AUTHOR("Stefan Schmidt <stefan@datenfreihafen.org>"); | |
705 | MODULE_AUTHOR("Werner Almesberger <werner@almesberger.net>"); | |
706 | MODULE_DESCRIPTION("ATUSB ben-wpan Driver"); | |
707 | MODULE_LICENSE("GPL"); |
Branches:
ben-wpan
ben-wpan-stefan
5396a9238205f20f811ea57898980d3ca82df0b6
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9