1 /***************************************************************************
2 * Copyright (c) 2024 Microsoft Corporation
3 *
4 * This program and the accompanying materials are made available under the
5 * terms of the MIT License which is available at
6 * https://opensource.org/licenses/MIT.
7 *
8 * SPDX-License-Identifier: MIT
9 **************************************************************************/
10
11
12 /**************************************************************************/
13 /**************************************************************************/
14 /** */
15 /** USBX Component */
16 /** */
17 /** Storage Class */
18 /** */
19 /**************************************************************************/
20 /**************************************************************************/
21
22
23 /* Include necessary system files. */
24
25 #define UX_SOURCE_CODE
26
27 #include "ux_api.h"
28 #include "ux_host_class_storage.h"
29 #include "ux_host_stack.h"
30
31
32 #if !defined(UX_HOST_STANDALONE)
33 /**************************************************************************/
34 /* */
35 /* FUNCTION RELEASE */
36 /* */
37 /* _ux_host_class_storage_thread_entry PORTABLE C */
38 /* 6.1.11 */
39 /* AUTHOR */
40 /* */
41 /* Chaoqiong Xiao, Microsoft Corporation */
42 /* */
43 /* DESCRIPTION */
44 /* */
45 /* This function is awaken every 2 seconds to check if there was a */
46 /* device insertion on a specific media. This is the only way we can */
47 /* remount a media after the storage instance has opened the media to */
48 /* UX_MEDIA (default FileX) and the media is either not present */
49 /* or was removed and is being re-inserted. */
50 /* */
51 /* It's for RTOS mode. */
52 /* */
53 /* INPUT */
54 /* */
55 /* class_address Class address */
56 /* */
57 /* OUTPUT */
58 /* */
59 /* None */
60 /* */
61 /* CALLS */
62 /* */
63 /* ux_media_close Close media */
64 /* _ux_host_class_storage_device_reset Reset device */
65 /* _ux_host_class_storage_media_mount Mount the media */
66 /* _ux_host_class_storage_unit_ready_test */
67 /* Test for unit ready */
68 /* _ux_host_class_storage_media_characteristics_get */
69 /* Get media characteristics */
70 /* _ux_host_class_storage_media_format_capacity_get */
71 /* Get media format capacity */
72 /* _ux_utility_memory_free Free memory block */
73 /* _ux_host_semaphore_get Get a semaphore */
74 /* _ux_host_semaphore_put Put a semaphore */
75 /* _ux_utility_delay_ms Thread sleep */
76 /* */
77 /* CALLED BY */
78 /* */
79 /* ThreadX */
80 /* */
81 /* RELEASE HISTORY */
82 /* */
83 /* DATE NAME DESCRIPTION */
84 /* */
85 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
86 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
87 /* added option to disable FX */
88 /* media integration, used UX_ */
89 /* things instead of FX_ */
90 /* things directly, used host */
91 /* class extension pointer for */
92 /* class specific structured */
93 /* data, */
94 /* resulting in version 6.1 */
95 /* 01-31-2022 Chaoqiong Xiao Modified comment(s), */
96 /* refined macros names, */
97 /* resulting in version 6.1.10 */
98 /* 04-25-2022 Chaoqiong Xiao Modified comment(s), */
99 /* internal clean up, */
100 /* resulting in version 6.1.11 */
101 /* */
102 /**************************************************************************/
_ux_host_class_storage_thread_entry(ULONG class_address)103 VOID _ux_host_class_storage_thread_entry(ULONG class_address)
104 {
105
106 UX_HOST_CLASS *class_inst;
107 UX_HOST_CLASS_STORAGE *storage;
108 UINT status;
109 ULONG lun_index;
110 UX_HOST_CLASS_STORAGE_MEDIA *storage_media;
111 UINT media_index;
112 #if !defined(UX_HOST_CLASS_STORAGE_NO_FILEX)
113 UX_MEDIA *media;
114 UCHAR *memory;
115 #endif
116
117
118 /* Setup pointer to class. */
119 UX_THREAD_EXTENSION_PTR_GET(class_inst, UX_HOST_CLASS, class_address)
120
121 /* This thread goes on forever once started. */
122 while(1)
123 {
124
125 /* We need to wake every 2 seconds or so. */
126 _ux_utility_delay_ms(UX_HOST_CLASS_STORAGE_THREAD_SLEEP_TIME);
127
128 /* We need to parse all the storage instances and check for a removable
129 media flag. */
130 storage = (UX_HOST_CLASS_STORAGE *) class_inst -> ux_host_class_first_instance;
131
132 while (storage != UX_NULL)
133 {
134
135 /* Check if the instance is live and the device is removable. */
136 if ((storage -> ux_host_class_storage_state == UX_HOST_CLASS_INSTANCE_LIVE))
137 {
138
139 /* We need to ensure nobody is accessing this storage instance. We use
140 the storage class instance semaphore to protect. */
141 status = _ux_host_semaphore_get(&storage -> ux_host_class_storage_semaphore, UX_WAIT_FOREVER);
142 if (status != UX_SUCCESS)
143 break;
144
145 /* Each LUN must be parsed and mounted. */
146 for (lun_index = 0; lun_index <= storage -> ux_host_class_storage_max_lun; lun_index++)
147 {
148
149 if (storage -> ux_host_class_storage_lun_removable_media_flags[lun_index] != UX_HOST_CLASS_STORAGE_MEDIA_REMOVABLE)
150 continue;
151
152 /* Check the type of LUN, we only deal with the ones we know how to mount. */
153 if ((storage -> ux_host_class_storage_lun_types[lun_index] == UX_HOST_CLASS_STORAGE_MEDIA_FAT_DISK) ||
154 (storage -> ux_host_class_storage_lun_types[lun_index] == UX_HOST_CLASS_STORAGE_MEDIA_OPTICAL_DISK) ||
155 (storage -> ux_host_class_storage_lun_types[lun_index] == UX_HOST_CLASS_STORAGE_MEDIA_IOMEGA_CLICK))
156 {
157
158 /* Set the LUN into the storage instance. */
159 storage -> ux_host_class_storage_lun = lun_index;
160
161 /* Check if the device is now ready. */
162 status = _ux_host_class_storage_unit_ready_test(storage);
163
164 /* If we have an transport failure here, we are in trouble! The storage device
165 is in an unstable state and should be reset completely. */
166 if (status != UX_SUCCESS)
167 {
168
169 /* Reset device. */
170 _ux_host_class_storage_device_reset(storage);
171 break;
172 }
173
174 /* Process relative to device status. */
175 switch(storage -> ux_host_class_storage_sense_code >> 16)
176 {
177
178 case UX_HOST_CLASS_STORAGE_SENSE_KEY_NOT_READY:
179
180 /* We may need to unmount this partition if it was mounted before.
181 To do so, we need to parse the existing media instance and find out
182 if this partition was already mounted. */
183
184 storage_media = (UX_HOST_CLASS_STORAGE_MEDIA *) class_inst -> ux_host_class_media;
185
186 /* Scan all instances of media. */
187 for (media_index = 0; media_index < UX_HOST_CLASS_STORAGE_MAX_MEDIA;
188 storage_media++, media_index++)
189 {
190 #if !defined(UX_HOST_CLASS_STORAGE_NO_FILEX)
191 /* Get the UX_MEDIA (default FileX) Media attached to this media. */
192 media = &storage_media -> ux_host_class_storage_media;
193
194 /* Check for the storage instance and lun number. */
195 if ((ux_media_id_get(media) != 0) && (ux_media_driver_info_get(media) == (VOID *) storage) &&
196 (storage_media -> ux_host_class_storage_media_lun == storage -> ux_host_class_storage_lun))
197 {
198
199 /* We preserve the memory used by this media. */
200 memory = storage_media -> ux_host_class_storage_media_memory;
201
202 /* Let UX_MEDIA (default FileX) use this instance. */
203 _ux_host_semaphore_put(&storage -> ux_host_class_storage_semaphore);
204
205 /* Ask UX_MEDIA (default FileX) to unmount the partition. */
206 ux_media_close(media);
207
208 /* This device is now unmounted. */
209 storage_media -> ux_host_class_storage_media_status = UX_HOST_CLASS_STORAGE_MEDIA_UNMOUNTED;
210
211 /* Reset the media ID. */
212 ux_media_id_set(media, 0);
213
214 /* Now, we protect the storage instance. */
215 status = _ux_host_semaphore_get(&storage -> ux_host_class_storage_semaphore, UX_WAIT_FOREVER);
216 if (status != UX_SUCCESS)
217 break;
218
219 /* Free the memory block used for data transfer on behalf of UX_MEDIA (default FileX). */
220 _ux_utility_memory_free(memory);
221 }
222 #else
223
224 /* Check storage instance and lun number. */
225 if (storage_media -> ux_host_class_storage_media_status != UX_USED)
226 continue;
227 if (storage_media -> ux_host_class_storage_media_storage != storage)
228 continue;
229 if (storage_media -> ux_host_class_storage_media_lun != storage -> ux_host_class_storage_lun)
230 continue;
231
232 /* Release the instance. */
233 status = _ux_host_semaphore_put_rc(&storage -> ux_host_class_storage_semaphore);
234
235 /* Free the storage media. */
236 storage_media -> ux_host_class_storage_media_status = UX_UNUSED;
237
238 /* Invoke callback for media removal. */
239 if (_ux_system_host -> ux_system_host_change_function != UX_NULL)
240 {
241
242 /* Call system change function. */
243 _ux_system_host -> ux_system_host_change_function(UX_STORAGE_MEDIA_REMOVAL,
244 storage -> ux_host_class_storage_class, (VOID *) storage_media);
245 }
246
247 /* Now, we protect the storage instance. */
248 status = _ux_host_semaphore_get(&storage -> ux_host_class_storage_semaphore, UX_WAIT_FOREVER);
249 if (status != UX_SUCCESS)
250 break;
251 #endif
252 }
253 break;
254
255 case UX_HOST_CLASS_STORAGE_SENSE_KEY_UNIT_ATTENTION:
256
257 /* We may need to unmount this partition if it was mounted before.
258 To do so, we need to parse the existing media instance and find out
259 if this partition was already mounted. */
260 storage_media = (UX_HOST_CLASS_STORAGE_MEDIA *) class_inst -> ux_host_class_media;
261
262 /* Scan all instances of media. */
263 for (media_index = 0; media_index < UX_HOST_CLASS_STORAGE_MAX_MEDIA;
264 storage_media++, media_index++)
265 {
266
267 #if !defined(UX_HOST_CLASS_STORAGE_NO_FILEX)
268 /* Get the UX_MEDIA (default FileX) Media attached to this media. */
269 media = &storage_media -> ux_host_class_storage_media;
270
271 /* Check for the storage instance and lun number. */
272 if ((ux_media_id_get(media) != 0) && (ux_media_driver_info_get(media) == (VOID *) storage) &&
273 (storage_media -> ux_host_class_storage_media_lun == storage -> ux_host_class_storage_lun))
274 {
275
276 /* We preserve the memory used by this media. */
277 memory = storage_media -> ux_host_class_storage_media_memory;
278
279 /* Let UX_MEDIA (default FileX) use this instance. */
280 _ux_host_semaphore_put(&storage -> ux_host_class_storage_semaphore);
281
282 /* Ask UX_MEDIA (default FileX) to unmount the partition. */
283 ux_media_close(media);
284
285 /* This device is now unmounted. */
286 storage_media -> ux_host_class_storage_media_status = UX_HOST_CLASS_STORAGE_MEDIA_UNMOUNTED;
287
288 /* Reset the media ID. */
289 ux_media_id_set(media, 0);
290
291 /* Now, we protect the storage instance. */
292 status = _ux_host_semaphore_get(&storage -> ux_host_class_storage_semaphore, UX_WAIT_FOREVER);
293 if (status != UX_SUCCESS)
294 break;
295
296 /* Free the memory block used for data transfer on behalf of FileX. */
297 _ux_utility_memory_free(memory);
298 }
299 #else
300
301 /* Check storage instance and lun number. */
302 if (storage_media -> ux_host_class_storage_media_status != UX_USED)
303 continue;
304 if (storage_media -> ux_host_class_storage_media_storage != storage)
305 continue;
306 if (storage_media -> ux_host_class_storage_media_lun != storage -> ux_host_class_storage_lun)
307 continue;
308
309 /* Release the instance. */
310 status = _ux_host_semaphore_put_rc(&storage -> ux_host_class_storage_semaphore);
311
312 /* Free the storage media. */
313 storage_media -> ux_host_class_storage_media_status = UX_UNUSED;
314
315 /* Invoke callback for media removal. */
316 if (_ux_system_host -> ux_system_host_change_function != UX_NULL)
317 {
318
319 /* Call system change function. */
320 _ux_system_host -> ux_system_host_change_function(UX_STORAGE_MEDIA_REMOVAL,
321 storage -> ux_host_class_storage_class, (VOID *) storage_media);
322 }
323
324 /* Now, we protect the storage instance. */
325 status = _ux_host_semaphore_get(&storage -> ux_host_class_storage_semaphore, UX_WAIT_FOREVER);
326 if (status != UX_SUCCESS)
327 break;
328 #endif
329 }
330
331 /* Now, we need to retest the media to see if it is there. */
332 status = _ux_host_class_storage_unit_ready_test(storage);
333
334 /* If we have an transport failure here, we are in trouble! The storage device
335 is in an unstable state and should be reset completely. */
336 if (status != UX_SUCCESS)
337 {
338
339 /* Reset device. */
340 _ux_host_class_storage_device_reset(storage);
341 break;
342 }
343
344 if (storage -> ux_host_class_storage_sense_code == 0)
345 {
346
347 /* Get the media type supported by this storage device. */
348 status = _ux_host_class_storage_media_characteristics_get(storage);
349 if (status != UX_SUCCESS)
350 break;
351
352 /* Get the format capacity of this storage device. */
353 status = _ux_host_class_storage_media_format_capacity_get(storage);
354 if (status != UX_SUCCESS)
355 break;
356
357 /* Let UX_MEDIA (default FileX) use this instance. */
358 _ux_host_semaphore_put(&storage -> ux_host_class_storage_semaphore);
359
360 #if !defined(UX_HOST_CLASS_STORAGE_NO_FILEX)
361
362 /* The device seems to have been inserted, try to mount it. */
363 _ux_host_class_storage_media_mount(storage, 0);
364 #else
365
366 /* Find a free media slot for inserted media. */
367 storage_media = (UX_HOST_CLASS_STORAGE_MEDIA *) class_inst -> ux_host_class_media;
368 for (media_index = 0; media_index < UX_HOST_CLASS_STORAGE_MAX_MEDIA;
369 storage_media ++, media_index ++)
370 {
371
372 /* Skip used storage media slots. */
373 if (storage_media -> ux_host_class_storage_media_status == UX_USED)
374 continue;
375
376 /* Use this free storage media slot. */
377 storage_media -> ux_host_class_storage_media_status = UX_USED;
378 storage_media -> ux_host_class_storage_media_storage = storage;
379
380 /* Save media information. */
381 storage_media -> ux_host_class_storage_media_lun = (UCHAR)storage -> ux_host_class_storage_lun;
382 storage_media -> ux_host_class_storage_media_sector_size = (USHORT)storage -> ux_host_class_storage_sector_size;
383 storage_media -> ux_host_class_storage_media_number_sectors = storage -> ux_host_class_storage_last_sector_number + 1;
384
385 /* Invoke callback for media insertion. */
386 if (_ux_system_host -> ux_system_host_change_function != UX_NULL)
387 {
388
389 /* Call system change function. */
390 _ux_system_host -> ux_system_host_change_function(UX_STORAGE_MEDIA_INSERTION,
391 storage -> ux_host_class_storage_class, (VOID *) storage_media);
392 }
393 }
394 #endif
395
396 /* Now, we protect the storage instance. */
397 _ux_host_semaphore_get(&storage -> ux_host_class_storage_semaphore, UX_WAIT_FOREVER);
398 }
399 break;
400
401 default:
402 break;
403 }
404 }
405 }
406
407 /* Other threads are now allowed to access this storage instance. */
408 _ux_host_semaphore_put(&storage -> ux_host_class_storage_semaphore);
409 }
410
411 /* Move to the next entry in the storage instances link. */
412 storage = storage -> ux_host_class_storage_next_instance;
413 }
414 }
415 }
416 #endif
417