mass.c 17 KB

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