M480 BSP V3.05.006
The Board Support Package for M480 Series
msc_driver.c
Go to the documentation of this file.
1/**************************************************************************/
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13
14#include "NuMicro.h"
15#include "diskio.h" // FATFS header
16#include "usb.h"
17#include "msc.h"
18#include "ff.h"
19#include "diskio.h"
20
21
23
24
25MSC_T *g_msc_list; /* Global list of Mass Storage Class device. A multi-lun device can have
26 several instances appeared with different lun. */
27
28static volatile uint8_t g_fat_drv_used[USBDRV_CNT];
29static TCHAR _path[3] = { '3', ':', 0 };
30
31static void fatfs_drive_int()
32{
33 memset((uint8_t *)g_fat_drv_used, 0, sizeof(g_fat_drv_used));
34}
35
36static int fatfs_drive_alloc()
37{
38 int i;
39
40 for (i = 0; i < USBDRV_CNT; i++)
41 {
42 if (g_fat_drv_used[i] == 0)
43 {
44 g_fat_drv_used[i] = 1;
45 return USBDRV_0+i;
46 }
47 }
48 msc_debug_msg("Memory out! No free FATFS USB drive slots!\n");
50}
51
52static void fatfs_drive_free(int drv_no)
53{
54 _path[0] = drv_no + '0';
55 f_mount(NULL, _path, 1);
56 g_fat_drv_used[drv_no-USBDRV_0] = 0;
57}
58
59static MSC_T * find_msc_by_drive(int drv_no)
60{
61 MSC_T *msc = g_msc_list;
62
63 while (msc != NULL)
64 {
65 if (msc->drv_no == drv_no)
66 return msc;
67 msc = msc->next;
68 }
69 return NULL;
70}
71
72static void msc_list_add(MSC_T *msc)
73{
74 if (g_msc_list == NULL)
75 {
76 msc->next = NULL;
77 g_msc_list = msc;
78 }
79 else
80 {
81 msc->next = g_msc_list;
82 g_msc_list = msc;
83 }
84}
85
86static void msc_list_remove(MSC_T *msc)
87{
88 MSC_T *p;
89
90 if (g_msc_list == msc)
91 {
92 g_msc_list = msc->next;
93 }
94 else
95 {
96 p = g_msc_list;
97 while ((p->next != msc) && (p->next != NULL))
98 {
99 p = p->next;
100 }
101
102 if (p->next == msc)
103 {
104 p->next = msc->next;
105 }
106 }
107}
108
109
110static void get_max_lun(MSC_T *msc)
111{
112 UDEV_T *udev = msc->iface->udev;
113 uint32_t read_len;
114 uint8_t buff[2] = { 0, 0 };
115 int ret;
116
117 msc->max_lun = 0;
118
119 /*------------------------------------------------------------------------------------*/
120 /* Issue GET MAXLUN MSC class command to get the maximum lun number */
121 /*------------------------------------------------------------------------------------*/
122 ret = usbh_ctrl_xfer(udev, REQ_TYPE_IN | REQ_TYPE_CLASS_DEV | REQ_TYPE_TO_IFACE,
123 0xFE, 0, msc->iface->if_num, 1, buff, &read_len, 200);
124 if (ret < 0)
125 {
126 msc_debug_msg("Get Max Lun command failed! Assign 0...\n");
127 msc->max_lun = 0;
128 if (ret == USBH_ERR_STALL)
129 usbh_clear_halt(udev, 0);
130 return;
131 }
132 msc->max_lun = buff[0];
133 msc_debug_msg("Max lun is %d\n", msc->max_lun);
134}
135
136void msc_reset(MSC_T *msc)
137{
138 UDEV_T *udev = msc->iface->udev;
139 uint32_t read_len;
140 int ret;
141
142 msc_debug_msg("Reset MSC device...\n");
143
144 ret = usbh_ctrl_xfer(udev, REQ_TYPE_OUT | REQ_TYPE_CLASS_DEV | REQ_TYPE_TO_IFACE,
145 0xFF, 0, msc->iface->if_num, 0, NULL, &read_len, 100);
146 if (ret < 0)
147 {
148 msc_debug_msg("UAMSS reset request failed!\n");
149 }
150 usbh_clear_halt(udev, msc->ep_bulk_out->bEndpointAddress);
151 usbh_clear_halt(udev, msc->ep_bulk_in->bEndpointAddress);
152}
153
154static int msc_inquiry(MSC_T *msc)
155{
156 struct bulk_cb_wrap *cmd_blk = &msc->cmd_blk; /* MSC Bulk-only command block */
157 int ret;
158
159 msc_debug_msg("INQUIRY...\n");
160 memset(cmd_blk, 0, sizeof(*cmd_blk));
161
162 cmd_blk->Flags = 0x80;
163 cmd_blk->Length = 6;
164 cmd_blk->CDB[0] = INQUIRY; /* INQUIRY */
165 cmd_blk->CDB[1] = msc->lun << 5;
166 cmd_blk->CDB[4] = 36;
167
168 ret = run_scsi_command(msc, msc->scsi_buff, 36, 1, 100);
169 if (ret < 0)
170 {
171 msc_debug_msg("INQUIRY command failed. [%d]\n", ret);
172 return ret;
173 }
174 else
175 {
176 msc_debug_msg("INQUIRY command success.\n");
177 }
178 return ret;
179}
180
181static int msc_request_sense(MSC_T *msc)
182{
183 struct bulk_cb_wrap *cmd_blk = &msc->cmd_blk; /* MSC Bulk-only command block */
184 int ret;
185
186 msc_debug_msg("REQUEST_SENSE...\n");
187 memset(cmd_blk, 0, sizeof(*cmd_blk));
188
189 cmd_blk->Flags = 0x80;
190 cmd_blk->Length = 6;
191 cmd_blk->CDB[0] = REQUEST_SENSE;
192 cmd_blk->CDB[1] = msc->lun << 5;
193 cmd_blk->CDB[4] = 18;
194
195 ret = run_scsi_command(msc, msc->scsi_buff, 18, 1, 100);
196 if (ret < 0)
197 {
198 msc_debug_msg("REQUEST_SENSE command failed.\n");
199 if (ret == USBH_ERR_STALL)
200 msc_reset(msc);
201 return ret;
202 }
203 else
204 {
205 msc_debug_msg("REQUEST_SENSE command success.\n");
206 if (msc->scsi_buff[2] != 0x6)
207 {
208 msc_debug_msg("Device is still not attention. 0x%x\n", msc->scsi_buff[2]);
209 return -1;
210 }
211 }
212 return ret;
213}
214
215static int msc_test_unit_ready(MSC_T *msc)
216{
217 struct bulk_cb_wrap *cmd_blk = &msc->cmd_blk; /* MSC Bulk-only command block */
218 int ret;
219
220 msc_debug_msg("TEST_UNIT_READY...\n");
221 memset(cmd_blk, 0, sizeof(*cmd_blk));
222
223 cmd_blk->Flags = 0x80;
224 cmd_blk->Length = 6;
225 cmd_blk->CDB[0] = TEST_UNIT_READY;
226 cmd_blk->CDB[1] = msc->lun << 5;
227
228 ret = run_scsi_command(msc, msc->scsi_buff, 0, 1, 100);
229 if (ret < 0)
230 {
231 if (ret == USBH_ERR_STALL)
232 msc_reset(msc);
233 return ret;
234 }
235 else
236 {
237 msc_debug_msg("TEST_UNIT_READY command success.\n");
238 }
239 return ret;
240}
241
254int usbh_umas_read(int drv_no, uint32_t sec_no, int sec_cnt, uint8_t *buff)
255{
256 MSC_T *msc;
257 struct bulk_cb_wrap *cmd_blk; /* MSC Bulk-only command block */
258 int ret;
259
260 //msc_debug_msg("usbh_umas_read - %d, %d\n", sec_no, sec_cnt);
261
262 msc = find_msc_by_drive(drv_no);
263 if (msc == NULL)
265
266 cmd_blk = &msc->cmd_blk;
267
268 //msc_debug_msg("read sector 0x%x\n", sector_no);
269 memset(cmd_blk, 0, sizeof(*cmd_blk));
270
271 cmd_blk->Flags = 0x80;
272 cmd_blk->Length = 10;
273 cmd_blk->CDB[0] = READ_10;
274 cmd_blk->CDB[1] = msc->lun << 5;
275 cmd_blk->CDB[2] = (sec_no >> 24) & 0xFF;
276 cmd_blk->CDB[3] = (sec_no >> 16) & 0xFF;
277 cmd_blk->CDB[4] = (sec_no >> 8) & 0xFF;
278 cmd_blk->CDB[5] = sec_no & 0xFF;
279 cmd_blk->CDB[7] = (sec_cnt >> 8) & 0xFF;
280 cmd_blk->CDB[8] = sec_cnt & 0xFF;
281
282 ret = run_scsi_command(msc, buff, sec_cnt * 512, 1, 500);
283 if (ret != 0)
284 {
285 msc_debug_msg("usbh_umas_read failed! [%d]\n", ret);
286 return UMAS_ERR_IO;
287 }
288 return 0;
289}
290
303int usbh_umas_write(int drv_no, uint32_t sec_no, int sec_cnt, uint8_t *buff)
304{
305 MSC_T *msc;
306 struct bulk_cb_wrap *cmd_blk; /* MSC Bulk-only command block */
307 int ret;
308
309 //msc_debug_msg("usbh_umas_write - %d, %d\n", sec_no, sec_cnt);
310
311 msc = find_msc_by_drive(drv_no);
312 if (msc == NULL)
314
315 cmd_blk = &msc->cmd_blk;
316 memset((uint8_t *)&(msc->cmd_blk), 0, sizeof(msc->cmd_blk));
317
318 cmd_blk->Flags = 0;
319 cmd_blk->Length = 10;
320 cmd_blk->CDB[0] = WRITE_10;
321 cmd_blk->CDB[1] = msc->lun << 5;
322 cmd_blk->CDB[2] = (sec_no >> 24) & 0xFF;
323 cmd_blk->CDB[3] = (sec_no >> 16) & 0xFF;
324 cmd_blk->CDB[4] = (sec_no >> 8) & 0xFF;
325 cmd_blk->CDB[5] = sec_no & 0xFF;
326 cmd_blk->CDB[7] = (sec_cnt >> 8) & 0xFF;
327 cmd_blk->CDB[8] = sec_cnt & 0xFF;
328
329 ret = run_scsi_command(msc, buff, sec_cnt * 512, 0, 500);
330 if (ret < 0)
331 {
332 msc_debug_msg("usbh_umas_write failed!\n");
333 return UMAS_ERR_IO;
334 }
335 return 0;
336}
337
349int usbh_umas_ioctl(int drv_no, int cmd, void *buff)
350{
351 MSC_T *msc;
352
353 msc = find_msc_by_drive(drv_no);
354 if (msc == NULL)
356
357 switch (cmd)
358 {
359 case CTRL_SYNC:
360 return RES_OK;
361
362 case GET_SECTOR_COUNT:
363 *(uint32_t *)buff = msc->uTotalSectorN;
364 return RES_OK;
365
366 case GET_SECTOR_SIZE:
367 *(uint32_t *)buff = msc->nSectorSize;
368 return RES_OK;
369
370 case GET_BLOCK_SIZE:
371 *(uint32_t *)buff = msc->nSectorSize;
372 return RES_OK;
373
374 //case CTRL_ERASE_SECTOR:
375 // return RES_OK;
376 }
378}
379
386int usbh_umas_disk_status(int drv_no)
387{
388 if (find_msc_by_drive(drv_no) == NULL)
389 return STA_NODISK;
390 return 0;
391}
392
399int usbh_umas_reset_disk(int drv_no)
400{
401 MSC_T *msc;
402 UDEV_T *udev;
403
404 msc_debug_msg("usbh_umas_reset_disk ...\n");
405
406 msc = find_msc_by_drive(drv_no);
407 if (msc == NULL)
409
410 udev = msc->iface->udev;
411
412 usbh_reset_device(udev);
413
414 return 0;
415}
416
417static int umass_init_device(MSC_T *msc)
418{
419 MSC_T *try_msc = msc;
420 struct bulk_cb_wrap *cmd_blk; /* MSC Bulk-only command block */
421 int retries, lun;
422 int8_t bHasMedia = 0;
423 int ret = USBH_ERR_NOT_FOUND;
424
425 for (lun = 0; lun <= msc->max_lun; lun++)
426 {
427 msc_debug_msg("\n******* Read lun %d ******\n\n", lun);
428
429 try_msc->lun = lun;
430 cmd_blk = &try_msc->cmd_blk;
431
432 for (retries = 0; retries < 3; retries++)
433 {
434 if (msc_inquiry(try_msc) == USBH_ERR_STALL)
435 msc_reset(try_msc);
436
437 if (msc_inquiry(try_msc) == USBH_ERR_STALL)
438 msc_reset(try_msc);
439
440 if (msc_test_unit_ready(try_msc) == 0)
441 goto disk_found;
442
443 if (msc_request_sense(try_msc) == 0)
444 {
445 goto disk_found;
446 }
447
448 delay_us(100000); /* delay 100ms, retry later */
449 }
450
451 continue;
452
453disk_found:
454 /*
455 * Valid disk found in this lun. Go...
456 */
457 for (retries = 0; retries < 3; retries++)
458 {
459 msc_debug_msg("READ CAPACITY ==>\n");
460
461 memset(cmd_blk, 0, sizeof(*cmd_blk));
462
463 cmd_blk->Flags = 0x80;
464 cmd_blk->Length = 10;
465 cmd_blk->CDB[0] = READ_CAPACITY;
466 cmd_blk->CDB[1] = lun << 5;
467
468 ret = run_scsi_command(try_msc, try_msc->scsi_buff, 8, 1, 100);
469 if (ret < 0)
470 {
471 msc_debug_msg("READ_CAPACITY failed!\n");
472 if (ret == USBH_ERR_STALL)
473 msc_reset(try_msc);
474 continue;
475 }
476 else
477 break;
478 }
479
480 if (retries >= 3)
481 continue; /* try next lun */
482
483 try_msc->uTotalSectorN = (try_msc->scsi_buff[0] << 24) | (try_msc->scsi_buff[1] << 16) |
484 (try_msc->scsi_buff[2] << 8) | try_msc->scsi_buff[3];
485 try_msc->nSectorSize = (try_msc->scsi_buff[4] << 24) | (try_msc->scsi_buff[5] << 16) |
486 (try_msc->scsi_buff[6] << 8) | try_msc->scsi_buff[7];
487
488 try_msc->drv_no = fatfs_drive_alloc();
489 if (try_msc->drv_no < 0) /* should be failed, unless drive free slot is empty */
490 {
492 break;
493 }
494
495 msc_debug_msg("USB disk [%c] found: size=%d MB, uTotalSectorN=%d\n", msc->drv_no+'0', try_msc->uTotalSectorN / 2048, try_msc->uTotalSectorN);
496
497 msc_list_add(try_msc);
498
499 _path[0] = try_msc->drv_no + '0';
500 f_mount(&try_msc->fatfs_vol, _path, 1);
501 bHasMedia = 1;
502
503
504 /*
505 * duplicate another MSC for next try
506 */
507 try_msc = usbh_alloc_mem(sizeof(*try_msc));
508 if (try_msc == NULL)
509 {
511 break;
512 }
513 memcpy(try_msc, msc, sizeof(*msc));
514 }
515
516 if (bHasMedia)
517 {
518 if (try_msc)
519 usbh_free_mem(try_msc, sizeof(*try_msc));
520 return 0;
521 }
522 return ret;
523}
524
525static int msc_probe(IFACE_T *iface)
526{
527 ALT_IFACE_T *aif = iface->aif;
528 DESC_IF_T *ifd;
529 MSC_T *msc;
530 int i;
531
532 ifd = aif->ifd;
533
534 /* Is this interface mass storage class? */
535 if (ifd->bInterfaceClass != USB_CLASS_MASS_STORAGE)
537
538 /* Is supported sub-class? */
539 if ((ifd->bInterfaceSubClass != MSC_SCLASS_SCSI) && (ifd->bInterfaceSubClass != MSC_SCLASS_8070) &&
540 (ifd->bInterfaceSubClass != MSC_SCLASS_RBC))
542
543 /* Is bulk-only protocol? */
544 if (ifd->bInterfaceProtocol != MSC_SPROTO_BULK)
545 {
546 msc_debug_msg("Not bulk-only MSC device!\n");
548 }
549
550 msc = usbh_alloc_mem(sizeof(*msc));
551 if (msc == NULL)
552 return USBH_ERR_MEMORY_OUT;
553 msc->uid = get_ticks();
554
555 /* Find the bulk in and out endpoints */
556 for (i = 0; i < aif->ifd->bNumEndpoints; i++)
557 {
558 if ((aif->ep[i].bmAttributes & EP_ATTR_TT_MASK) == EP_ATTR_TT_BULK)
559 {
560 if ((aif->ep[i].bEndpointAddress & EP_ADDR_DIR_MASK) == EP_ADDR_DIR_IN)
561 msc->ep_bulk_in = &aif->ep[i];
562 else
563 msc->ep_bulk_out = &aif->ep[i];
564 }
565 }
566
567 if ((msc->ep_bulk_in == NULL) || (msc->ep_bulk_out == NULL))
568 {
569 usbh_free_mem(msc, sizeof(*msc));
571 }
572
573 msc->iface = iface;
574
575 msc_debug_msg("USB Mass Storage device found. Iface:%d, Alt Iface:%d, bep_in:0x%x, bep_out:0x%x\n", ifd->bInterfaceNumber, ifd->bAlternateSetting, msc->ep_bulk_in->bEndpointAddress, msc->ep_bulk_out->bEndpointAddress);
576
577 get_max_lun(msc);
578
579 return umass_init_device(msc);
580}
581
582static void msc_disconnect(IFACE_T *iface)
583{
584 int i;
585 MSC_T *msc_p, *msc;
586
587 /*
588 * Remove any hardware EP/QH from Host Controller hardware list.
589 * This will finally result in all transfers aborted.
590 */
591 for (i = 0; i < iface->aif->ifd->bNumEndpoints; i++)
592 {
593 iface->udev->hc_driver->quit_xfer(NULL, &(iface->aif->ep[i]));
594 }
595
596 /*
597 * unmount drive and remove it from MSC device list
598 */
599 msc = g_msc_list;
600 while (msc != NULL)
601 {
602 msc_p = msc->next;
603 if (msc->iface == iface)
604 {
605 fatfs_drive_free(msc->drv_no);
606 msc_list_remove(msc);
607 usbh_free_mem(msc, sizeof(*msc));
608 }
609 msc = msc_p;
610 }
611}
612
613UDEV_DRV_T msc_driver =
614{
615 msc_probe,
616 msc_disconnect,
617 NULL,
618 NULL
619};
620
621
623
624
632{
633 fatfs_drive_int();
634 g_msc_list = NULL;
635 return usbh_register_driver(&msc_driver);
636}
637
638
639
640
641/*** (C) COPYRIGHT 2017 Nuvoton Technology Corp. ***/
642
643
NuMicro peripheral access layer header file.
#define NULL
NULL pointer.
Definition: M480.h:605
#define UMAS_ERR_DRIVE_NOT_FOUND
Definition: usbh_lib.h:83
#define UMAS_ERR_IO
Definition: usbh_lib.h:79
#define USBH_ERR_NOT_MATCHED
Definition: usbh_lib.h:36
#define USBH_ERR_NOT_SUPPORTED
Definition: usbh_lib.h:35
#define USBH_ERR_NOT_FOUND
Definition: usbh_lib.h:39
#define USBH_ERR_STALL
Definition: usbh_lib.h:60
#define USBH_ERR_MEMORY_OUT
Definition: usbh_lib.h:32
#define UMAS_ERR_IVALID_PARM
Definition: usbh_lib.h:82
#define USBH_ERR_NOT_EXPECTED
Definition: usbh_lib.h:37
int usbh_umas_read(int drv_no, uint32_t sec_no, int sec_cnt, uint8_t *buff)
int usbh_umas_write(int drv_no, uint32_t sec_no, int sec_cnt, uint8_t *buff)
HIDDEN_SYMBOLS int usbh_umas_init(void)
Register and initialize USB Host Mass Storage driver.
Definition: msc_driver.c:631
int usbh_umas_ioctl(int drv_no, int cmd, void *buff)
int usbh_umas_disk_status(int drv_no)
uint32_t get_ticks(void)
A function return current tick count.
M480 MCU USB Host mass storage class header.
int run_scsi_command(MSC_T *msc, uint8_t *buff, uint32_t data_len, int bIsDataIn, int timeout_ticks)
Definition: msc_xfer.c:113
USB Host library header file.