mass.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  1. /*
  2. * File : mass.c
  3. * This file is part of RT-Thread RTOS
  4. * COPYRIGHT (C) 2011, RT-Thread Development Team
  5. *
  6. * The license and distribution terms for this file may be
  7. * found in the file LICENSE in this distribution or at
  8. * http://www.rt-thread.org/license/LICENSE
  9. *
  10. * Change Logs:
  11. * Date Author Notes
  12. * 2011-12-12 Yi Qiu first version
  13. */
  14. #include <rtthread.h>
  15. #include <drivers/usb_host.h>
  16. #include "mass.h"
  17. #ifdef RT_USBH_MSTORAGE
  18. extern rt_err_t rt_udisk_run(struct uhintf* intf);
  19. extern rt_err_t rt_udisk_stop(struct uhintf* intf);
  20. static struct uclass_driver storage_driver;
  21. /**
  22. * This function will do USBREQ_GET_MAX_LUN request for the usb interface instance.
  23. *
  24. * @param intf the interface instance.
  25. * @param max_lun the buffer to save max_lun.
  26. *
  27. * @return the error code, RT_EOK on successfully.
  28. */
  29. static rt_err_t _pipe_check(struct uhintf* intf, upipe_t pipe)
  30. {
  31. struct uinstance* device;
  32. rt_err_t ret;
  33. ustor_t stor;
  34. int size = 0;
  35. struct ustorage_csw csw;
  36. if(intf == RT_NULL || pipe == RT_NULL)
  37. {
  38. rt_kprintf("the interface is not available\n");
  39. return -RT_EIO;
  40. }
  41. /* get usb device instance from the interface instance */
  42. device = intf->device;
  43. /* get storage instance from the interface instance */
  44. stor = (ustor_t)intf->user_data;
  45. /* check pipe status */
  46. if(pipe->status == UPIPE_STATUS_OK) return RT_EOK;
  47. if(pipe->status == UPIPE_STATUS_ERROR)
  48. {
  49. rt_kprintf("pipe status error\n");
  50. return -RT_EIO;
  51. }
  52. /* clear the pipe stall status */
  53. ret = rt_usbh_clear_feature(device, pipe->ep.bEndpointAddress,
  54. USB_FEATURE_ENDPOINT_HALT);
  55. if(ret != RT_EOK) return ret;
  56. rt_thread_delay(50);
  57. rt_kprintf("pipes1 0x%x, 0x%x\n", stor->pipe_in, stor->pipe_out);
  58. stor->pipe_in->status = UPIPE_STATUS_OK;
  59. RT_DEBUG_LOG(RT_DEBUG_USB, ("clean storage in pipe stall\n"));
  60. /* it should receive csw after clear the stall feature */
  61. size = rt_usb_hcd_pipe_xfer(stor->pipe_in->inst->hcd,
  62. stor->pipe_in, &csw, SIZEOF_CSW, 100);
  63. if(size != SIZEOF_CSW)
  64. {
  65. rt_kprintf("receive the csw after stall failed\n");
  66. return -RT_EIO;
  67. }
  68. return -RT_ERROR;
  69. }
  70. /**
  71. * This function will do USBREQ_GET_MAX_LUN request for the usb interface instance.
  72. *
  73. * @param intf the interface instance.
  74. * @param max_lun the buffer to save max_lun.
  75. *
  76. * @return the error code, RT_EOK on successfully.
  77. */
  78. static rt_err_t rt_usb_bulk_only_xfer(struct uhintf* intf,
  79. ustorage_cbw_t cmd, rt_uint8_t* buffer, int timeout)
  80. {
  81. rt_size_t size;
  82. rt_err_t ret;
  83. upipe_t pipe;
  84. struct ustorage_csw csw;
  85. ustor_t stor;
  86. RT_ASSERT(cmd != RT_NULL);
  87. if(intf == RT_NULL)
  88. {
  89. rt_kprintf("the interface is not available\n");
  90. return -RT_EIO;
  91. }
  92. /* get storage instance from the interface instance */
  93. stor = (ustor_t)intf->user_data;
  94. do
  95. {
  96. /* send the cbw */
  97. size = rt_usb_hcd_pipe_xfer(stor->pipe_out->inst->hcd, stor->pipe_out,
  98. cmd, SIZEOF_CBW, timeout);
  99. if(size != SIZEOF_CBW)
  100. {
  101. rt_kprintf("CBW size error\n");
  102. return -RT_EIO;
  103. }
  104. if(cmd->xfer_len != 0)
  105. {
  106. pipe = (cmd->dflags == CBWFLAGS_DIR_IN) ? stor->pipe_in :
  107. stor->pipe_out;
  108. size = rt_usb_hcd_pipe_xfer(pipe->inst->hcd, pipe, (void*)buffer,
  109. cmd->xfer_len, timeout);
  110. if(size != cmd->xfer_len)
  111. {
  112. rt_kprintf("request size %d, transfer size %d\n",
  113. cmd->xfer_len, size);
  114. break;
  115. }
  116. }
  117. /* receive the csw */
  118. size = rt_usb_hcd_pipe_xfer(stor->pipe_in->inst->hcd, stor->pipe_in,
  119. &csw, SIZEOF_CSW, timeout);
  120. if(size != SIZEOF_CSW)
  121. {
  122. rt_kprintf("csw size error\n");
  123. return -RT_EIO;
  124. }
  125. }while(0);
  126. /* check in pipes status */
  127. ret = _pipe_check(intf, stor->pipe_in);
  128. if(ret != RT_EOK)
  129. {
  130. rt_kprintf("in pipe error\n");
  131. return ret;
  132. }
  133. /* check out pipes status */
  134. ret = _pipe_check(intf, stor->pipe_out);
  135. if(ret != RT_EOK)
  136. {
  137. rt_kprintf("out pipe error\n");
  138. return ret;
  139. }
  140. /* check csw status */
  141. if(csw.signature != CSW_SIGNATURE || csw.tag != CBW_TAG_VALUE)
  142. {
  143. rt_kprintf("csw signature error\n");
  144. return -RT_EIO;
  145. }
  146. if(csw.status != 0)
  147. {
  148. rt_kprintf("csw status error:%d\n",csw.status);
  149. return -RT_ERROR;
  150. }
  151. return RT_EOK;
  152. }
  153. /**
  154. * This function will do USBREQ_GET_MAX_LUN request for the usb interface instance.
  155. *
  156. * @param intf the interface instance.
  157. * @param max_lun the buffer to save max_lun.
  158. *
  159. * @return the error code, RT_EOK on successfully.
  160. */
  161. rt_err_t rt_usbh_storage_get_max_lun(struct uhintf* intf, rt_uint8_t* max_lun)
  162. {
  163. struct uinstance* device;
  164. struct urequest setup;
  165. int timeout = 100;
  166. if(intf == RT_NULL)
  167. {
  168. rt_kprintf("the interface is not available\n");
  169. return -RT_EIO;
  170. }
  171. /* parameter check */
  172. RT_ASSERT(intf->device != RT_NULL);
  173. RT_DEBUG_LOG(RT_DEBUG_USB, ("rt_usbh_storage_get_max_lun\n"));
  174. /* get usb device instance from the interface instance */
  175. device = intf->device;
  176. /* construct the request */
  177. setup.request_type = USB_REQ_TYPE_DIR_IN | USB_REQ_TYPE_CLASS |
  178. USB_REQ_TYPE_INTERFACE;
  179. setup.bRequest = USBREQ_GET_MAX_LUN;
  180. setup.wValue = intf->intf_desc->bInterfaceNumber;
  181. setup.wIndex = 1;
  182. setup.wLength = 0;
  183. /* do control transfer request */
  184. if(rt_usb_hcd_setup_xfer(device->hcd, device->pipe_ep0_out, &setup, timeout) != 8)
  185. {
  186. if(rt_usb_hcd_pipe_xfer(device->hcd, device->pipe_ep0_in, max_lun, 1, timeout) != 1)
  187. {
  188. return -RT_EIO;
  189. }
  190. return -RT_EIO;
  191. }
  192. return RT_EOK;
  193. }
  194. /**
  195. * This function will do USBREQ_MASS_STORAGE_RESET request for the usb interface instance.
  196. *
  197. * @param intf the interface instance.
  198. *
  199. * @return the error code, RT_EOK on successfully.
  200. */
  201. rt_err_t rt_usbh_storage_reset(struct uhintf* intf)
  202. {
  203. struct urequest setup;
  204. struct uinstance* device;
  205. int timeout = 100;
  206. /* parameter check */
  207. if(intf == RT_NULL)
  208. {
  209. rt_kprintf("the interface is not available\n");
  210. return -RT_EIO;
  211. }
  212. RT_ASSERT(intf->device != RT_NULL);
  213. RT_DEBUG_LOG(RT_DEBUG_USB, ("rt_usbh_storage_reset\n"));
  214. /* get usb device instance from the interface instance */
  215. device = intf->device;
  216. /* construct the request */
  217. setup.request_type = USB_REQ_TYPE_DIR_OUT | USB_REQ_TYPE_CLASS |
  218. USB_REQ_TYPE_INTERFACE;
  219. setup.bRequest = USBREQ_MASS_STORAGE_RESET;
  220. setup.wIndex = intf->intf_desc->bInterfaceNumber;
  221. setup.wLength = 0;
  222. setup.wValue = 0;
  223. if(rt_usb_hcd_setup_xfer(device->hcd, device->pipe_ep0_out, &setup, timeout) != 8)
  224. {
  225. return -RT_EIO;
  226. }
  227. return RT_EOK;
  228. }
  229. /**
  230. * This function will execute SCSI_READ_10 command to read data from the usb device.
  231. *
  232. * @param intf the interface instance.
  233. * @param buffer the data buffer to save read data
  234. * @param sector the start sector address to read.
  235. * @param sector the sector count to read.
  236. *
  237. * @return the error code, RT_EOK on successfully.
  238. */
  239. rt_err_t rt_usbh_storage_read10(struct uhintf* intf, rt_uint8_t *buffer,
  240. rt_uint32_t sector, rt_size_t count, int timeout)
  241. {
  242. struct ustorage_cbw cmd;
  243. /* parameter check */
  244. if(intf == RT_NULL)
  245. {
  246. rt_kprintf("interface is not available\n");
  247. return -RT_EIO;
  248. }
  249. RT_ASSERT(intf->device != RT_NULL);
  250. RT_DEBUG_LOG(RT_DEBUG_USB, ("rt_usbh_storage_read10\n"));
  251. /* construct the command block wrapper */
  252. rt_memset(&cmd, 0, sizeof(struct ustorage_cbw));
  253. cmd.signature = CBW_SIGNATURE;
  254. cmd.tag = CBW_TAG_VALUE;
  255. cmd.xfer_len = SECTOR_SIZE * count;
  256. cmd.dflags = CBWFLAGS_DIR_IN;
  257. cmd.lun = 0;
  258. cmd.cb_len = 10;
  259. cmd.cb[0] = SCSI_READ_10;
  260. cmd.cb[1] = 0;
  261. cmd.cb[2] = (rt_uint8_t)(sector >> 24);
  262. cmd.cb[3] = (rt_uint8_t)(sector >> 16);
  263. cmd.cb[4] = (rt_uint8_t)(sector >> 8);
  264. cmd.cb[5] = (rt_uint8_t)sector;
  265. cmd.cb[6] = 0;
  266. cmd.cb[7] = (count & 0xff00) >> 8;
  267. cmd.cb[8] = (rt_uint8_t) count & 0xff;
  268. return rt_usb_bulk_only_xfer(intf, &cmd, buffer, timeout);
  269. }
  270. /**
  271. * This function will execute SCSI_WRITE_10 command to write data to the usb device.
  272. *
  273. * @param intf the interface instance.
  274. * @param buffer the data buffer to save write data
  275. * @param sector the start sector address to write.
  276. * @param sector the sector count to write.
  277. *
  278. * @return the error code, RT_EOK on successfully.
  279. */
  280. rt_err_t rt_usbh_storage_write10(struct uhintf* intf, rt_uint8_t *buffer,
  281. rt_uint32_t sector, rt_size_t count, int timeout)
  282. {
  283. struct ustorage_cbw cmd;
  284. /* parameter check */
  285. if(intf == RT_NULL)
  286. {
  287. rt_kprintf("the interface is not available\n");
  288. return -RT_EIO;
  289. }
  290. RT_ASSERT(intf->device != RT_NULL);
  291. RT_DEBUG_LOG(RT_DEBUG_USB, ("rt_usbh_storage_write10\n"));
  292. /* construct the command block wrapper */
  293. rt_memset(&cmd, 0, sizeof(struct ustorage_cbw));
  294. cmd.signature = CBW_SIGNATURE;
  295. cmd.tag = CBW_TAG_VALUE;
  296. cmd.xfer_len = SECTOR_SIZE * count;
  297. cmd.dflags = CBWFLAGS_DIR_OUT;
  298. cmd.lun = 0;
  299. cmd.cb_len = 10;
  300. cmd.cb[0] = SCSI_WRITE_10;
  301. cmd.cb[1] = 0;
  302. cmd.cb[2] = (rt_uint8_t)(sector >> 24);
  303. cmd.cb[3] = (rt_uint8_t)(sector >> 16);
  304. cmd.cb[4] = (rt_uint8_t)(sector >> 8);
  305. cmd.cb[5] = (rt_uint8_t)sector;
  306. cmd.cb[6] = 0;
  307. cmd.cb[7] = (count & 0xff00) >> 8;
  308. cmd.cb[8] = (rt_uint8_t) count & 0xff;
  309. return rt_usb_bulk_only_xfer(intf, &cmd, buffer, timeout);
  310. }
  311. /**
  312. * This function will execute SCSI_REQUEST_SENSE command to get sense data.
  313. *
  314. * @param intf the interface instance.
  315. * @param buffer the data buffer to save sense data
  316. *
  317. * @return the error code, RT_EOK on successfully.
  318. */
  319. rt_err_t rt_usbh_storage_request_sense(struct uhintf* intf, rt_uint8_t* buffer)
  320. {
  321. struct ustorage_cbw cmd;
  322. int timeout = 200;
  323. /* parameter check */
  324. if(intf == RT_NULL)
  325. {
  326. rt_kprintf("the interface is not available\n");
  327. return -RT_EIO;
  328. }
  329. RT_ASSERT(intf->device != RT_NULL);
  330. RT_DEBUG_LOG(RT_DEBUG_USB, ("rt_usbh_storage_request_sense\n"));
  331. /* construct the command block wrapper */
  332. rt_memset(&cmd, 0, sizeof(struct ustorage_cbw));
  333. cmd.signature = CBW_SIGNATURE;
  334. cmd.tag = CBW_TAG_VALUE;
  335. cmd.xfer_len = 18;
  336. cmd.dflags = CBWFLAGS_DIR_IN;
  337. cmd.lun = 0;
  338. cmd.cb_len = 6;
  339. cmd.cb[0] = SCSI_REQUEST_SENSE;
  340. cmd.cb[4] = 18;
  341. return rt_usb_bulk_only_xfer(intf, &cmd, buffer, timeout);
  342. }
  343. /**
  344. * This function will execute SCSI_TEST_UNIT_READY command to get unit ready status.
  345. *
  346. * @param intf the interface instance.
  347. *
  348. * @return the error code, RT_EOK on successfully.
  349. */
  350. rt_err_t rt_usbh_storage_test_unit_ready(struct uhintf* intf)
  351. {
  352. struct ustorage_cbw cmd;
  353. int timeout = 200;
  354. /* parameter check */
  355. if(intf == RT_NULL)
  356. {
  357. rt_kprintf("the interface is not available\n");
  358. return -RT_EIO;
  359. }
  360. RT_ASSERT(intf->device != RT_NULL);
  361. RT_DEBUG_LOG(RT_DEBUG_USB, ("rt_usbh_storage_test_unit_ready\n"));
  362. /* construct the command block wrapper */
  363. rt_memset(&cmd, 0, sizeof(struct ustorage_cbw));
  364. cmd.signature = CBW_SIGNATURE;
  365. cmd.tag = CBW_TAG_VALUE;
  366. cmd.xfer_len = 0;
  367. cmd.dflags = CBWFLAGS_DIR_OUT;
  368. cmd.lun = 0;
  369. cmd.cb_len = 12;
  370. cmd.cb[0] = SCSI_TEST_UNIT_READY;
  371. return rt_usb_bulk_only_xfer(intf, &cmd, RT_NULL, timeout);
  372. }
  373. /**
  374. * This function will execute SCSI_INQUIRY_CMD command to get inquiry data.
  375. *
  376. * @param intf the interface instance.
  377. * @param buffer the data buffer to save inquiry data
  378. *
  379. * @return the error code, RT_EOK on successfully.
  380. */
  381. rt_err_t rt_usbh_storage_inquiry(struct uhintf* intf, rt_uint8_t* buffer)
  382. {
  383. struct ustorage_cbw cmd;
  384. int timeout = 200;
  385. /* parameter check */
  386. if(intf == RT_NULL)
  387. {
  388. rt_kprintf("the interface is not available\n");
  389. return -RT_EIO;
  390. }
  391. RT_ASSERT(intf->device != RT_NULL);
  392. RT_DEBUG_LOG(RT_DEBUG_USB, ("rt_usbh_storage_inquiry\n"));
  393. /* construct the command block wrapper */
  394. rt_memset(&cmd, 0, sizeof(struct ustorage_cbw));
  395. cmd.signature = CBW_SIGNATURE;
  396. cmd.tag = CBW_TAG_VALUE;
  397. cmd.xfer_len = 36;
  398. cmd.dflags = CBWFLAGS_DIR_IN;
  399. cmd.lun = 0;
  400. cmd.cb_len = 12;
  401. cmd.cb[0] = SCSI_INQUIRY_CMD;
  402. cmd.cb[4] = 36;
  403. return rt_usb_bulk_only_xfer(intf, &cmd, buffer, timeout);
  404. }
  405. /**
  406. * This function will execute SCSI_READ_CAPACITY command to get capacity data.
  407. *
  408. * @param intf the interface instance.
  409. * @param buffer the data buffer to save capacity data
  410. *
  411. * @return the error code, RT_EOK on successfully.
  412. */
  413. rt_err_t rt_usbh_storage_get_capacity(struct uhintf* intf, rt_uint8_t* buffer)
  414. {
  415. struct ustorage_cbw cmd;
  416. int timeout = 200;
  417. /* parameter check */
  418. if(intf == RT_NULL)
  419. {
  420. rt_kprintf("the interface is not available\n");
  421. return -RT_EIO;
  422. }
  423. RT_ASSERT(intf->device != RT_NULL);
  424. RT_DEBUG_LOG(RT_DEBUG_USB, ("rt_usbh_storage_get_capacity\n"));
  425. /* construct the command block wrapper */
  426. rt_memset(&cmd, 0, sizeof(struct ustorage_cbw));
  427. cmd.signature = CBW_SIGNATURE;
  428. cmd.tag = CBW_TAG_VALUE;
  429. cmd.xfer_len = 8;
  430. cmd.dflags = CBWFLAGS_DIR_IN;
  431. cmd.lun = 0;
  432. cmd.cb_len = 12;
  433. cmd.cb[0] = SCSI_READ_CAPACITY;
  434. return rt_usb_bulk_only_xfer(intf, &cmd, buffer, timeout);
  435. }
  436. /**
  437. * This function will run mass storage class driver when usb device is detected
  438. * and identified as a mass storage class device, it will continue to do the enumulate
  439. * process.
  440. *
  441. * @param arg the argument.
  442. *
  443. * @return the error code, RT_EOK on successfully.
  444. */
  445. static rt_err_t rt_usbh_storage_enable(void* arg)
  446. {
  447. int i = 0;
  448. rt_err_t ret;
  449. ustor_t stor;
  450. struct uhintf* intf = (struct uhintf*)arg;
  451. /* parameter check */
  452. if(intf == RT_NULL)
  453. {
  454. rt_kprintf("the interface is not available\n");
  455. return -RT_EIO;
  456. }
  457. RT_DEBUG_LOG(RT_DEBUG_USB, ("subclass %d, protocal %d\n",
  458. intf->intf_desc->bInterfaceSubClass,
  459. intf->intf_desc->bInterfaceProtocol));
  460. RT_DEBUG_LOG(RT_DEBUG_USB, ("rt_usbh_storage_run\n"));
  461. /* only support SCSI subclass and bulk only protocal */
  462. stor = rt_malloc(sizeof(struct ustor));
  463. RT_ASSERT(stor != RT_NULL);
  464. /* initilize the data structure */
  465. rt_memset(stor, 0, sizeof(struct ustor));
  466. intf->user_data = (void*)stor;
  467. for(i=0; i<intf->intf_desc->bNumEndpoints; i++)
  468. {
  469. uep_desc_t ep_desc;
  470. /* get endpoint descriptor from interface descriptor */
  471. rt_usbh_get_endpoint_descriptor(intf->intf_desc, i, &ep_desc);
  472. if(ep_desc == RT_NULL)
  473. {
  474. rt_kprintf("rt_usb_get_endpoint_descriptor error\n");
  475. return -RT_ERROR;
  476. }
  477. /* the endpoint type of mass storage class should be BULK */
  478. if((ep_desc->bmAttributes & USB_EP_ATTR_TYPE_MASK) != USB_EP_ATTR_BULK)
  479. continue;
  480. /* allocate pipes according to the endpoint type */
  481. if(ep_desc->bEndpointAddress & USB_DIR_IN)
  482. {
  483. /* alloc an in pipe for the storage instance */
  484. stor->pipe_in = rt_usb_instance_find_pipe(intf->device,ep_desc->bEndpointAddress);
  485. }
  486. else
  487. {
  488. /* alloc an output pipe for the storage instance */
  489. stor->pipe_out = rt_usb_instance_find_pipe(intf->device,ep_desc->bEndpointAddress);
  490. }
  491. }
  492. /* check pipes infomation */
  493. if(stor->pipe_in == RT_NULL || stor->pipe_out == RT_NULL)
  494. {
  495. rt_kprintf("pipe error, unsupported device\n");
  496. return -RT_ERROR;
  497. }
  498. /* should implement as callback */
  499. ret = rt_udisk_run(intf);
  500. if(ret != RT_EOK) return ret;
  501. return RT_EOK;
  502. }
  503. /**
  504. * This function will be invoked when usb device plug out is detected and it would clean
  505. * and release all mass storage class related resources.
  506. *
  507. * @param arg the argument.
  508. *
  509. * @return the error code, RT_EOK on successfully.
  510. */
  511. static rt_err_t rt_usbh_storage_disable(void* arg)
  512. {
  513. ustor_t stor;
  514. struct uhintf* intf = (struct uhintf*)arg;
  515. /* parameter check */
  516. RT_ASSERT(intf != RT_NULL);
  517. RT_ASSERT(intf->user_data != RT_NULL);
  518. RT_ASSERT(intf->device != RT_NULL);
  519. RT_DEBUG_LOG(RT_DEBUG_USB, ("rt_usbh_storage_stop\n"));
  520. /* get storage instance from interface instance */
  521. stor = (ustor_t)intf->user_data;
  522. rt_udisk_stop(intf);
  523. /* free storage instance */
  524. if(stor != RT_NULL) rt_free(stor);
  525. /* free interface instance */
  526. if(intf != RT_NULL) rt_free(intf);
  527. return RT_EOK;
  528. }
  529. /**
  530. * This function will register mass storage class driver to the usb class driver manager.
  531. * and it should be invoked in the usb system initialization.
  532. *
  533. * @return the error code, RT_EOK on successfully.
  534. */
  535. ucd_t rt_usbh_class_driver_storage(void)
  536. {
  537. storage_driver.class_code = USB_CLASS_MASS_STORAGE;
  538. storage_driver.enable = rt_usbh_storage_enable;
  539. storage_driver.disable = rt_usbh_storage_disable;
  540. return &storage_driver;
  541. }
  542. #endif