NUC472_NUC442_BSP V3.03.004
The Board Support Package for NUC472/NUC442
UmasDriver.c
Go to the documentation of this file.
1/* Driver for USB Mass Storage compliant devices
2 *
3 * $Id: usb.c,v 1.61 2001/01/13 00:10:59 mdharm Exp $
4 *
5 * Current development and maintenance by:
6 * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
7 *
8 * Developed with the assistance of:
9 * (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org)
10 *
11 * Initial work by:
12 * (c) 1999 Michael Gee (michael@linuxspecific.com)
13 *
14 * usb_device_id support by Adam J. Richter (adam@yggdrasil.com):
15 * (c) 2000 Yggdrasil Computing, Inc.
16 *
17 * This driver is based on the 'USB Mass Storage Class' document. This
18 * describes in detail the protocol used to communicate with such
19 * devices. Clearly, the designers had SCSI and ATAPI commands in
20 * mind when they created this document. The commands are all very
21 * similar to commands in the SCSI-II and ATAPI specifications.
22 *
23 * It is important to note that in a number of cases this class
24 * exhibits class-specific exemptions from the USB specification.
25 * Notably the usage of NAK, STALL and ACK differs from the norm, in
26 * that they are used to communicate wait, failed and OK on commands.
27 *
28 * Also, for certain devices, the interrupt endpoint is used to convey
29 * status of a command.
30 *
31 * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
32 * information about this driver.
33 *
34 * This program is free software; you can redistribute it and/or modify it
35 * under the terms of the GNU General Public License as published by the
36 * Free Software Foundation; either version 2, or (at your option) any
37 * later version.
38 *
39 * This program is distributed in the hope that it will be useful, but
40 * WITHOUT ANY WARRANTY; without even the implied warranty of
41 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
42 * General Public License for more details.
43 *
44 * You should have received a copy of the GNU General Public License along
45 * with this program; if not, write to the Free Software Foundation, Inc.,
46 * 675 Mass Ave, Cambridge, MA 02139, USA.
47 *
48 * MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>");
49 * MODULE_DESCRIPTION("USB Mass Storage driver for Linux");
50 */
51
52/**************************************************************************/
62#include <stdio.h>
63#include <string.h>
64#include "NUC472_442.h"
65#include "usbh_core.h"
66
67#include "Umas.h"
68#include "diskio.h"
69#include "usbh_umas.h"
70
72
73extern UMAS_DATA_T *g_umas;
74
75#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \
76 vendorName, productName,useProtocol, useTransport, \
77 initFunction, flags) \
78USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin,bcdDeviceMax)
79
80static USB_DEV_ID_T _UmasDeviceIDs[] =
81{
82//#include "unusual_devs.h"
83#undef UNUSUAL_DEV
84 /* Control/Bulk transport for all SubClass values */
85 USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, UMAS_SC_RBC, UMAS_PR_CB),
86 USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, UMAS_SC_8020, UMAS_PR_CB),
87 USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, UMAS_SC_QIC, UMAS_PR_CB),
88 USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, UMAS_SC_UFI, UMAS_PR_CB),
89 USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, UMAS_SC_8070, UMAS_PR_CB),
90 USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, UMAS_SC_SCSI, UMAS_PR_CB),
91
92 /* Control/Bulk/Interrupt transport for all SubClass values */
93 USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, UMAS_SC_RBC, UMAS_PR_CBI),
94 USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, UMAS_SC_8020, UMAS_PR_CBI),
95 USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, UMAS_SC_QIC, UMAS_PR_CBI),
96 USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, UMAS_SC_UFI, UMAS_PR_CBI),
97 USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, UMAS_SC_8070, UMAS_PR_CBI),
98 USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, UMAS_SC_SCSI, UMAS_PR_CBI),
99
100 /* Bulk-only transport for all SubClass values */
101 USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, UMAS_SC_RBC, UMAS_PR_BULK),
102 USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, UMAS_SC_8020, UMAS_PR_BULK),
103 USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, UMAS_SC_QIC, UMAS_PR_BULK),
104 USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, UMAS_SC_UFI, UMAS_PR_BULK),
105 USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, UMAS_SC_8070, UMAS_PR_BULK),
106 USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, UMAS_SC_SCSI, UMAS_PR_BULK),
107
108 /* Terminating entry */
109 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
110};
111
112
113#undef UNUSUAL_DEV
114#define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \
115 vendor_name, product_name, use_protocol, use_transport, \
116 init_function, Flags) \
117{ vendor_name, product_name, use_protocol, use_transport, init_function, Flags }
118
119static UMAS_UUDEV_T _UmasUUDevList[] =
120{
121#undef UNUSUAL_DEV
122 /* Control/Bulk transport for all SubClass values */
123 { UMAS_SC_RBC, UMAS_PR_CB },
124 { UMAS_SC_8020, UMAS_PR_CB },
125 { UMAS_SC_QIC, UMAS_PR_CB },
126 { UMAS_SC_UFI, UMAS_PR_CB },
127 { UMAS_SC_8070, UMAS_PR_CB },
128 { UMAS_SC_SCSI, UMAS_PR_CB },
129
130 /* Control/Bulk/Interrupt transport for all SubClass values */
131 { UMAS_SC_RBC, UMAS_PR_CBI },
132 { UMAS_SC_8020, UMAS_PR_CBI },
133 { UMAS_SC_QIC, UMAS_PR_CBI },
134 { UMAS_SC_UFI, UMAS_PR_CBI },
135 { UMAS_SC_8070, UMAS_PR_CBI },
136 { UMAS_SC_SCSI, UMAS_PR_CBI },
137
138 /* Bulk-only transport for all SubClass values */
139 { UMAS_SC_RBC, UMAS_PR_BULK },
140 { UMAS_SC_8020, UMAS_PR_BULK },
141 { UMAS_SC_QIC, UMAS_PR_BULK },
142 { UMAS_SC_UFI, UMAS_PR_BULK },
143 { UMAS_SC_8070, UMAS_PR_BULK },
144 { UMAS_SC_SCSI, UMAS_PR_BULK },
145
146 /* Terminating entry */
147 { 0 }
148};
149
150
151static UMAS_DATA_T _umas_pool[UMAS_MAX_DEV];
152static uint8_t _umac_alloc_mark[UMAS_MAX_DEV];
153
154#ifdef USE_NVTFAT
155extern void free_umas_drive(UMAS_DRIVE_T * umas_drive);
156#endif
157
158static UMAS_DATA_T * alloc_umas()
159{
160 int i;
161 for (i = 0; i < UMAS_MAX_DEV; i++)
162 {
163 if (_umac_alloc_mark[i] == 0)
164 {
165 _umac_alloc_mark[i] = 1;
166 return &_umas_pool[i];
167 }
168 }
169 return NULL;
170}
171
172
173void free_umas(UMAS_DATA_T * umas)
174{
175 int i;
176
177 g_umas = NULL;
178 for (i = 0; i < UMAS_MAX_DEV; i++)
179 {
180 if (umas == &_umas_pool[i])
181 _umac_alloc_mark[i] = 0;
182 }
183}
184
185
186static int storage_probe(USB_DEV_T *dev, USB_IF_DESC_T *ifd, const USB_DEV_ID_T *id);
187static void storage_disconnect(USB_DEV_T *dev);
188
189USB_DRIVER_T _UsbMassStorageDriver =
190{
191 "UMAS",
192 storage_probe,
193 storage_disconnect,
194 _UmasDeviceIDs,
195 NULL, /* suspend */
196 NULL, /* resume */
197 {NULL,NULL} /* driver_list */
198};
199
200
201/*
202 * Set up the IRQ pipe and handler
203 * Note that this function assumes that all the data in the umas_data
204 * strucuture is current. This includes the ep_int field, which gives umas
205 * the endpoint for the interrupt.
206 * Returns non-zero on failure, zero on success
207 */
208static int usb_stor_allocate_irq(UMAS_DATA_T *umas)
209{
210 uint32_t pipe;
211 int maxp;
212 int result;
213 uint8_t bEndpointAddress, bInterval;
214
215 UMAS_DEBUG("Allocating IRQ for CBI transport\n");
216
217 bEndpointAddress = umas->pusb_dev->ep_list[umas->ep_int].bEndpointAddress;
218 bInterval = umas->pusb_dev->ep_list[umas->ep_int].bInterval;
219
220 /* allocate the URB */
221 umas->irq_urb = USBH_AllocUrb();
222 if (!umas->irq_urb)
223 {
224 UMAS_DEBUG("couldn't allocate interrupt URB");
225 return USB_ERR_NOMEM;
226 }
227
228 /* calculate the pipe and max packet size */
229 pipe = usb_rcvintpipe(umas->pusb_dev, (bEndpointAddress & USB_ENDPOINT_NUMBER_MASK));
230 maxp = usb_maxpacket(umas->pusb_dev, pipe, usb_pipeout(pipe));
231 if (maxp > sizeof(umas->irqbuf))
232 maxp = sizeof(umas->irqbuf);
233
234 /* fill in the URB with our data */
235 FILL_INT_URB(umas->irq_urb, umas->pusb_dev, pipe, umas->irqbuf, maxp,
236 UMAS_CbiIrq, umas, bInterval); /* UMAS_CbiIrq() in UmasTransport.c */
237
238 umas->ip_wanted = 1;
239 /* submit the URB for processing */
240 result = USBH_SubmitUrb(umas->irq_urb);
241 if (result)
242 USBH_FreeUrb(umas->irq_urb);
243
244 return result;
245}
246
247
248
249/* Probe to see if a new device is actually a SCSI device */
250static int storage_probe(USB_DEV_T *dev, USB_IF_DESC_T *ifd, const USB_DEV_ID_T *id)
251{
252 int i, ifnum;
253 const int id_index = id - _UmasDeviceIDs;
254 UMAS_UUDEV_T *unusual_dev;
255 UMAS_DATA_T *umas = NULL;
256 int ep_in = 0;
257 int ep_out = 0;
258 int ep_int = 0;
259 uint8_t subclass = 0;
260 uint8_t protocol = 0;
261
262 ifnum = ifd->bInterfaceNumber;
263
264 //UMAS_DEBUG("id_index calculated to be: %d\n", id_index);
265
266 if (id_index < sizeof(_UmasUUDevList) / sizeof(_UmasUUDevList[0]))
267 {
268 unusual_dev = &_UmasUUDevList[id_index];
269 }
270 else
271 /* no, we can't support it */
272 return USB_ERR_NODEV;
273
274 /* At this point, we know we've got a live one */
275 UMAS_DEBUG("USB Mass Storage device detected\n");
276
277 /* Determine subclass and protocol, or copy from the interface */
278 subclass = unusual_dev->useProtocol;
279 protocol = unusual_dev->useTransport;
280
281 /*
282 * Find the endpoints we need
283 * We are expecting a minimum of 2 endpoints - in and out (bulk).
284 * An optional interrupt is OK (necessary for CBI protocol).
285 * We will ignore any others.
286 */
287 for (i = 1; i < dev->ep_list_cnt; i++)
288 {
289 if (dev->ep_list[i].ifnum != ifnum)
290 continue;
291
292 /* is it an BULK endpoint? */
293 if ((dev->ep_list[i].bmAttributes &
294 USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK)
295 {
296 /* BULK in or out? */
297 if (dev->ep_list[i].bEndpointAddress & USB_DIR_IN)
298 ep_in = i;
299 else
300 ep_out = i;
301 }
302
303 /* is it an interrupt endpoint? */
304 if ((dev->ep_list[i].bmAttributes &
305 USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)
306 {
307 ep_int = i;
308 }
309 }
310
311 //UMAS_DEBUG("Endpoints: In: %d Out: %d Int: %d\n", ep_in, ep_out, ep_int, ep_int);
312
313 /* Do some basic sanity checks, and bail if we find a problem */
314 if (!ep_in || !ep_out || ((protocol == UMAS_PR_CBI) && !ep_int))
315 {
316 UMAS_DEBUG("Endpoint sanity check failed! Rejecting dev.\n");
317 return USB_ERR_NODEV;
318 }
319
320 umas = alloc_umas();
321 if (umas == NULL)
322 {
323 UMAS_DEBUG("umas - Out of memory\n");
324 return USB_ERR_NOMEM;
325 }
326 memset((char *)umas, 0, sizeof(UMAS_DATA_T));
327
328 /* allocate the URB we're going to use */
329 umas->current_urb = USBH_AllocUrb();
330 if (!umas->current_urb)
331 {
332 free_umas(umas);
333 return USB_ERR_NOMEM;
334 }
335
336 /* copy over the subclass and protocol data */
337 umas->subclass = subclass;
338 umas->protocol = protocol;
339 umas->flags = 0;
340 umas->unusual_dev = unusual_dev;
341
342 /* copy over the endpoint data */
343 if (ep_in)
344 umas->ep_in = dev->ep_list[ep_in].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
345 if (ep_out)
346 umas->ep_out = dev->ep_list[ep_out].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
347 umas->ep_int = ep_int;
348
349 /* establish the connection to the new device */
350 umas->ifnum = ifnum;
351 umas->pusb_dev = dev;
352 umas->vendor_id = dev->descriptor.idVendor;
353 umas->product_id = dev->descriptor.idProduct;
354 umas->sector_size = 0;
355 umas->sector_number = 0;
356
357 /*
358 * Set the handler pointers based on the protocol
359 * Again, this data is persistant across reattachments
360 */
361 switch (umas->protocol)
362 {
363 case UMAS_PR_CB:
364 umas->transport_name = "Control/Bulk";
365 umas->transport = UMAS_CbTransport;
366 umas->transport_reset = UMAS_CbReset;
367 umas->max_lun = 7;
368 break;
369
370 case UMAS_PR_CBI:
371 umas->transport_name = "Control/Bulk/Interrupt";
372 umas->transport = UMAS_CbiTransport;
373 umas->transport_reset = UMAS_CbReset;
374 umas->max_lun = 7;
375 break;
376
377 case UMAS_PR_BULK:
378 umas->transport_name = "Bulk-only";
379 umas->transport = UMAS_BulkTransport;
380 umas->transport_reset = UMAS_BulkReset;
381 umas->max_lun = UMAS_BulkMaxLun(umas);
382 UMAS_DEBUG("Bulk max logical unit number: %d\n", umas->max_lun);
383 break;
384
385 default:
386 umas->transport_name = "Unknown";
387 goto err_ret;
388 } /* end of switch */
389
390 UMAS_DEBUG("Mass storage transport: %s\n", umas->transport_name);
391
392 /* fix for single-lun devices */
393 if (umas->flags & UMAS_FL_SINGLE_LUN)
394 umas->max_lun = 0;
395
396 switch (umas->subclass)
397 {
398 case UMAS_SC_RBC:
399 umas->protocol_name = "Reduced Block Commands (RBC)";
400 umas->proto_handler = UMAS_TransparentScsiCommand;
401 break;
402
403 case UMAS_SC_8020:
404 umas->protocol_name = "8020i";
405 umas->proto_handler = UMAS_AtapiCommand;
406 umas->max_lun = 0;
407 break;
408
409 case UMAS_SC_QIC:
410 umas->protocol_name = "QIC-157";
411 umas->proto_handler = UMAS_Qic157Command;
412 umas->max_lun = 0;
413 break;
414
415 case UMAS_SC_8070:
416 umas->protocol_name = "8070i";
417 umas->proto_handler = UMAS_AtapiCommand;
418 umas->max_lun = 0;
419 break;
420
421 case UMAS_SC_SCSI:
422 umas->protocol_name = "Transparent SCSI";
423 umas->proto_handler = UMAS_TransparentScsiCommand;
424 break;
425
426 case UMAS_SC_UFI:
427 umas->protocol_name = "Uniform Floppy Interface (UFI)";
428 umas->proto_handler = UMAS_UfiCommand;
429 break;
430
431 default:
432 umas->protocol_name = "Unknown";
433 goto err_ret;
434 } /* end of switch */
435
436 UMAS_DEBUG("Mass storage protocol: %s\n", umas->protocol_name);
437
438 /* allocate an IRQ callback if one is needed */
439 if ((umas->protocol == UMAS_PR_CBI) && usb_stor_allocate_irq(umas))
440 {
441 return USB_ERR_IO;
442 }
443
444 UMAS_DEBUG("WARNING: USB Mass Storage data integrity not assured\n");
445 UMAS_DEBUG("USB Mass Storage device found at %d\n", dev->devnum);
446
447 if (UMAS_InitUmasDevice(umas) < 0)
448 goto err_ret;
449
450 return 0;
451
452err_ret:
453 if (umas->current_urb)
454 {
455 USBH_UnlinkUrb(umas->current_urb);
456 USBH_FreeUrb(umas->current_urb);
457 }
458 free_umas(umas);
459 return USB_ERR_IO;
460}
461
462
463
464/* Handle a disconnect event from the USB core */
465static void storage_disconnect(USB_DEV_T *dev)
466{
467 UMAS_DATA_T *umas = NULL;
468#ifdef USE_NVTFAT
469 UMAS_DRIVE_T *umas_drive, *next_drv;
470#endif
471 int i;
472
473 UMAS_DEBUG("storage_disconnect called\n");
474
475 for (i = 0; i < UMAS_MAX_DEV; i++)
476 {
477 if ((_umac_alloc_mark[i]) && (_umas_pool[i].pusb_dev == dev))
478 umas = &_umas_pool[i];
479 }
480
481 /* this is the odd case -- we disconnected but weren't using it */
482 if (!umas)
483 {
484 UMAS_DEBUG("storage_disconnect - device not in use\n");
485 return;
486 }
487
488#ifdef USE_NVTFAT
489 /* Unmount disk */
490 umas_drive = umas->drive_list;
491 while (umas_drive != NULL)
492 {
493 next_drv = umas_drive->next;
494 fsPhysicalDiskDisconnected(umas_drive->client);
495 free_umas_drive(umas_drive);
496 umas_drive = next_drv;
497 }
498#endif
499
500 if (umas->irq_urb)
501 {
502 UMAS_DEBUG("storage_disconnect -- releasing irq URB\n");
503#ifdef DEBUG
504 UMAS_DEBUG("storage_disconnect -- usb_unlink_urb() returned %d\n", USBH_UnlinkUrb(umas->irq_urb));
505#else
506 USBH_UnlinkUrb(umas->irq_urb);
507#endif
508 USBH_FreeUrb(umas->irq_urb);
509 umas->irq_urb = NULL;
510 }
511
512 /* free up the main URB for this device */
513 UMAS_DEBUG("storage_disconnect -- releasing main URB\n");
514#ifdef DEBUG
515 UMAS_DEBUG("storage_disconnect -- usb_unlink_urb() returned %d\n", USBH_UnlinkUrb(umas->current_urb));
516#else
517 USBH_UnlinkUrb(umas->current_urb);
518#endif
519 USBH_FreeUrb(umas->current_urb);
520 umas->current_urb = NULL;
521
522 free_umas(umas);
523}
524
525
526#if 0
527void UMAS_ScanAllDevice()
528{
529 int i;
530 for (i = 0; i < UMAS_MAX_DEV; i++)
531 {
532 if (_umac_alloc_mark[i] == 0)
533 continue;
534
535 UMAS_ScanDeviceLun(&_umas_pool[i]);
536 }
537}
538#endif
539
540
542
543
564int32_t USBH_MassInit(void)
565{
566 int i = 0;
567
568 g_umas = NULL;
569
570 for (i = 0; i < UMAS_MAX_DEV; i++)
571 _umac_alloc_mark[i] = 0;
572 /* register the driver, return -1 if error */
573 if (USBH_RegisterDriver(&_UsbMassStorageDriver) < 0)
574 return -1;
575 return 0;
576}
577
578
587int32_t USBH_MassGetDiskList(mass_disk_t * dlist[], int max)
588{
589 int i, idx;
590
591 for (i = 0, idx = 0; (i < UMAS_MAX_DEV) && (idx < max); i++)
592 {
593 if (_umac_alloc_mark[i])
594 {
595 dlist[idx] = (mass_disk_t *)&_umas_pool[i];
596 idx++;
597 }
598 }
599 return idx;
600}
601 /* end of group NUC472_442_USBH_MASS_EXPORTED_FUNCTIONS */
603 /* end of group NUC472_442_USBH_MASS_Driver */
605 /* end of group NUC472_442_Device_Driver */
607
608/*** (C) COPYRIGHT 2014 Nuvoton Technology Corp. ***/
609
610
NUC472/NUC442 peripheral access layer header file. This file contains all the peripheral register's d...
#define USB_ERR_NODEV
Definition: usbh_err_code.h:23
#define USB_ERR_IO
Definition: usbh_err_code.h:18
#define USB_ERR_NOMEM
Definition: usbh_err_code.h:20
int32_t USBH_RegisterDriver(USB_DRIVER_T *new_driver)
Register a device driver to USB Host Core driver.
Definition: usbh_core.c:78
int32_t USBH_SubmitUrb(URB_T *urb)
Submit an URB to USB core for transfer data.
Definition: usbh_core.c:218
int32_t USBH_UnlinkUrb(URB_T *urb)
Cancel an URB which has been submit to USB core.
Definition: usbh_core.c:236
URB_T * USBH_AllocUrb(void)
Allocate an URB from USB Core driver internal URB pool.
Definition: usbh_support.c:90
void USBH_FreeUrb(URB_T *)
Free the URB allocated from USBH_AllocUrb()
Definition: usbh_support.c:113
USB_IF_DESC_T
Definition: usbh_core.h:335
HIDDEN_SYMBOLS struct usb_device USB_DEV_T
int32_t USBH_MassGetDiskList(mass_disk_t *dlist[], int max)
Obtain the list of currently connected USB Mass Storage disk.
Definition: UmasDriver.c:587
int32_t USBH_MassInit(void)
Initialize USB Host Mass Storage driver.
Definition: UmasDriver.c:564
#define NULL
NULL pointer.
Definition: NUC472_442.h:29018
USB Host core driver header file.
USB Host Mass Storage driver header file.