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