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