1 /*
2  * Copyright (c) 2024, sakumisu
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include "usbh_core.h"
7 #include "usbh_msc.h"
8 
9 #include "rtthread.h"
10 #include <dfs_fs.h>
11 
12 #define DEV_FORMAT "/dev/sd%c"
13 
14 #ifndef RT_USING_DFS_ELMFAT
15 #error "RT_USING_DFS_ELMFAT must be enabled to use USB mass storage device"
16 #endif
17 
18 #ifndef CONFIG_USB_DFS_MOUNT_POINT
19 #define CONFIG_USB_DFS_MOUNT_POINT "/"
20 #endif
21 
rt_udisk_init(rt_device_t dev)22 static rt_err_t rt_udisk_init(rt_device_t dev)
23 {
24     struct usbh_msc *msc_class = (struct usbh_msc *)dev->user_data;
25 
26     if (usbh_msc_scsi_init(msc_class) < 0) {
27         return -RT_ERROR;
28     }
29 
30     return RT_EOK;
31 }
32 
rt_udisk_read(rt_device_t dev,rt_off_t pos,void * buffer,rt_size_t size)33 static rt_ssize_t rt_udisk_read(rt_device_t dev, rt_off_t pos, void *buffer,
34                                 rt_size_t size)
35 {
36     struct usbh_msc *msc_class = (struct usbh_msc *)dev->user_data;
37     int ret;
38     rt_uint8_t *align_buf;
39 
40     align_buf = (rt_uint8_t *)buffer;
41 #ifdef CONFIG_USB_DCACHE_ENABLE
42     if ((uint32_t)buffer & (CONFIG_USB_ALIGN_SIZE - 1)) {
43         align_buf = rt_malloc_align(size * msc_class->blocksize, CONFIG_USB_ALIGN_SIZE);
44         if (!align_buf) {
45             rt_kprintf("msc get align buf failed\n");
46             return 0;
47         }
48     } else {
49     }
50 #endif
51     ret = usbh_msc_scsi_read10(msc_class, pos, (uint8_t *)align_buf, size);
52     if (ret < 0) {
53         rt_kprintf("usb mass_storage read failed\n");
54         return 0;
55     }
56 #ifdef CONFIG_USB_DCACHE_ENABLE
57     if ((uint32_t)buffer & (CONFIG_USB_ALIGN_SIZE - 1)) {
58         usb_memcpy(buffer, align_buf, size * msc_class->blocksize);
59         rt_free_align(align_buf);
60     }
61 #endif
62     return size;
63 }
64 
rt_udisk_write(rt_device_t dev,rt_off_t pos,const void * buffer,rt_size_t size)65 static rt_ssize_t rt_udisk_write(rt_device_t dev, rt_off_t pos, const void *buffer,
66                                  rt_size_t size)
67 {
68     struct usbh_msc *msc_class = (struct usbh_msc *)dev->user_data;
69     int ret;
70     rt_uint8_t *align_buf;
71 
72     align_buf = (rt_uint8_t *)buffer;
73 #ifdef CONFIG_USB_DCACHE_ENABLE
74     if ((uint32_t)buffer & (CONFIG_USB_ALIGN_SIZE - 1)) {
75         align_buf = rt_malloc_align(size * msc_class->blocksize, CONFIG_USB_ALIGN_SIZE);
76         if (!align_buf) {
77             rt_kprintf("msc get align buf failed\n");
78             return 0;
79         }
80 
81         usb_memcpy(align_buf, buffer, size * msc_class->blocksize);
82     }
83 #endif
84     ret = usbh_msc_scsi_write10(msc_class, pos, (uint8_t *)align_buf, size);
85     if (ret < 0) {
86         rt_kprintf("usb mass_storage write failed\n");
87         return 0;
88     }
89 #ifdef CONFIG_USB_DCACHE_ENABLE
90     if ((uint32_t)buffer & (CONFIG_USB_ALIGN_SIZE - 1)) {
91         rt_free_align(align_buf);
92     }
93 #endif
94 
95     return size;
96 }
97 
rt_udisk_control(rt_device_t dev,int cmd,void * args)98 static rt_err_t rt_udisk_control(rt_device_t dev, int cmd, void *args)
99 {
100     /* check parameter */
101     RT_ASSERT(dev != RT_NULL);
102     struct usbh_msc *msc_class = (struct usbh_msc *)dev->user_data;
103 
104     if (cmd == RT_DEVICE_CTRL_BLK_GETGEOME) {
105         struct rt_device_blk_geometry *geometry;
106 
107         geometry = (struct rt_device_blk_geometry *)args;
108         if (geometry == RT_NULL)
109             return -RT_ERROR;
110 
111         geometry->bytes_per_sector = msc_class->blocksize;
112         geometry->block_size = msc_class->blocksize;
113         geometry->sector_count = msc_class->blocknum;
114     }
115 
116     return RT_EOK;
117 }
118 
119 #ifdef RT_USING_DEVICE_OPS
120 const static struct rt_device_ops udisk_device_ops = {
121     rt_udisk_init,
122     RT_NULL,
123     RT_NULL,
124     rt_udisk_read,
125     rt_udisk_write,
126     rt_udisk_control
127 };
128 #endif
129 
usbh_msc_thread(CONFIG_USB_OSAL_THREAD_SET_ARGV)130 static void usbh_msc_thread(CONFIG_USB_OSAL_THREAD_SET_ARGV)
131 {
132     struct usbh_msc *msc_class = (struct usbh_msc *)CONFIG_USB_OSAL_THREAD_GET_ARGV;
133     char name[CONFIG_USBHOST_DEV_NAMELEN];
134     char mount_point[CONFIG_USBHOST_DEV_NAMELEN];
135     int ret;
136 
137     snprintf(name, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, msc_class->sdchar);
138     snprintf(mount_point, CONFIG_USBHOST_DEV_NAMELEN, CONFIG_USB_DFS_MOUNT_POINT, msc_class->sdchar);
139 
140     ret = dfs_mount(name, mount_point, "elm", 0, 0);
141     if (ret == 0) {
142         rt_kprintf("udisk: %s mount successfully\n", name);
143     } else {
144         rt_kprintf("udisk: %s mount failed, ret = %d\n", name, ret);
145     }
146 
147     usb_osal_thread_delete(NULL);
148 }
149 
usbh_msc_run(struct usbh_msc * msc_class)150 void usbh_msc_run(struct usbh_msc *msc_class)
151 {
152     struct rt_device *dev;
153     char name[CONFIG_USBHOST_DEV_NAMELEN];
154 
155     dev = rt_malloc(sizeof(struct rt_device));
156     memset(dev, 0, sizeof(struct rt_device));
157 
158     snprintf(name, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, msc_class->sdchar);
159 
160     dev->type = RT_Device_Class_Block;
161 #ifdef RT_USING_DEVICE_OPS
162     dev->ops = &udisk_device_ops;
163 #else
164     dev->init = rt_udisk_init;
165     dev->read = rt_udisk_read;
166     dev->write = rt_udisk_write;
167     dev->control = rt_udisk_control;
168 #endif
169     dev->user_data = msc_class;
170 
171     rt_device_register(dev, name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_REMOVABLE | RT_DEVICE_FLAG_STANDALONE);
172 
173     usb_osal_thread_create("usbh_msc", 2048, CONFIG_USBHOST_PSC_PRIO + 1, usbh_msc_thread, msc_class);
174 }
175 
usbh_msc_stop(struct usbh_msc * msc_class)176 void usbh_msc_stop(struct usbh_msc *msc_class)
177 {
178     struct rt_device *dev;
179 
180     char name[CONFIG_USBHOST_DEV_NAMELEN];
181     char mount_point[CONFIG_USBHOST_DEV_NAMELEN];
182 
183     snprintf(name, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, msc_class->sdchar);
184     snprintf(mount_point, CONFIG_USBHOST_DEV_NAMELEN, CONFIG_USB_DFS_MOUNT_POINT, msc_class->sdchar);
185 
186     dfs_unmount(mount_point);
187     dev = rt_device_find(name);
188     if (dev) {
189         rt_device_unregister(dev);
190         rt_free(dev);
191     }
192 }
193