1 /*******************************************************************************
2  * Copyright 2019-2020 Microchip FPGA Embedded Systems Solutions.
3  *
4  * SPDX-License-Identifier: MIT
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to
8  * deal in the Software without restriction, including without limitation the
9  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10  * sell copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22  * IN THE SOFTWARE.
23  *
24  * PolarFire SoC MSS USB Host logical driver layer Implementation.
25  *
26  * PolarFire SoC MSS USB Driver Stack
27  *      USB Logical Layer (USB-LL)
28  *          USBD-MSC class driver.
29  *
30  *  USBD-MSC class driver public API.
31  *
32  */
33 
34 /*=========================================================================*//**
35   @mainpage PolarFire SoC MSS USB driver (USBD-MSC)
36 
37   ==============================================================================
38   Introduction
39   ==============================================================================
40   The Mass Storage Class device driver implements the USB MSC device as specified
41   by the USB-IF. This driver enables easy implementation of mass storage drive
42   functionality on the Smarfusion2 devices.
43 
44   This driver implements the MSC class using Bulk Only transport (BOT).
45   One BULK IN and one BULK OUT endpoints are used to implement BOT. This driver
46   is independent of the storage medium used for storing the data and the number
47   of Logical Units (LUN) it supports.
48 
49   This driver uses the USBD-Class driver template to implement the USB MSC
50   device.
51 
52   This driver passes the USB-IF USB2.0 compliance test for high speed MSC class
53   device.
54 
55   ==============================================================================
56   Theory of Operation
57   ==============================================================================
58   The following steps are involved in the operation of the USBD-MSC driver:
59     - Configuration
60     - Initialization
61     - Enumeration
62     - Class Specific requests
63     - Data transfer
64 
65   --------------------------------
66   Configuration
67   --------------------------------
68   To use this driver, the MSS USB driver must first be configured in the USB
69   device mode using the MSS_USB_PERIPHERAL_MODE. No other configuration is
70   necessary.
71 
72   --------------------------------
73   Initialization
74   --------------------------------
75   The MSC class driver must be initialized using the MSS_USBD_MSC_init() function.
76   Once initialized, this driver gets configured by the USBD driver during the
77   enumeration process. The Call-back function usbd_msc_init_cb() is
78   implemented by this driver which will be called by the USBD driver when the
79   host configures the this device. The usbd_msc_get_descriptor_cb() function
80   is implemented to provide the class specific descriptor table to the USBD driver.
81 
82   --------------------------------
83   Class Specific requests
84   --------------------------------
85   This driver defines descriptor table which contains a configuration descriptor,
86   an interface descriptor, two endpoint descriptors for successful enumeration
87   as a MSC class device.
88 
89   Note: For successful enumeration, the device specific descriptors must also be
90   provided by the application using the MSS_USBD_set_desc_cb_handler()
91   function to the USBD Driver. Since the device descriptor, string descriptors etc.
92   are not class specific, they are not part of the MSC class driver.
93 
94   Class Specific requests
95   The usbd_msc_process_request_cb() call-back function is implemented by
96   this driver which processes the MSC class specific requests. This driver may
97   not have all the information related to the storage medium e.g. storage
98   capacity etc. To process such requests; they are passed on to the application
99   by calling appropriate call-back functions. The application must implement
100   these call-back functions for the driver to successfully respond to the host
101   requests. Refer data structures section for more information.
102 
103   --------------------------------
104   Data transfer
105   --------------------------------
106   The MSC class driver performs the data transfers using one BULK IN endpoint
107   and one BULK OUT endpoint. The data transfers follow the BoT protocol. This
108   driver implements the usbd_msc_tx_complete_cb() and the usbd_msc_tx_complete_cb()
109   function to get the information on data transfer events on the USB bus which
110   are called by the USBD Driver.
111 
112   The BoT read/write operations happen on logical units of fixed block sizes.
113   This driver translates these read write operation into memory read/write
114   operation and calls the call-back function where application can perform the
115   memory read/write operations. This method makes the MSC class driver
116   independent of the storage medium being used. The call-back function elements
117   media_acquire_write_buf, media_write_ready and media_read (as part of structure
118   of type mss_usbd_msc_media_t) must be implemented by the application.
119 
120 *//*==========================================================================*/
121 
122 #ifndef __MSS_USB_DEVICE_MSD_H_
123 #define __MSS_USB_DEVICE_MSD_H_
124 
125 #include <stdint.h>
126 #include "mss_usb_device.h"
127 
128 #ifdef __cplusplus
129 extern "C" {
130 #endif
131 
132 #ifdef MSS_USB_DEVICE_ENABLED
133 /*******************************************************************************
134  * User Descriptor lengths
135  */
136 
137 /* Full configuration descriptor length*/
138 #define FULL_CONFIG_DESCR_LENGTH                    (USB_STD_CONFIG_DESCR_LEN + \
139                                                      USB_STD_INTERFACE_DESCR_LEN + \
140                                                      USB_STD_ENDPOINT_DESCR_LEN + \
141                                                      USB_STD_ENDPOINT_DESCR_LEN )
142 
143 /***************************************************************************//**
144  Exported Types from USBD-MSC class driver
145  */
146 /***************************************************************************//**
147   mss_usbd_msc_state_t
148   The mss_usbd_msc_state_t provides a type to identify the current state of the
149   MSC class driver.
150     USBD_MSC_NOT_CONFIGURED – The USB MSC class driver is not configured and it
151                               cannot perform the data transfers.
152     USBD_MSC_CONFIGURED    -  The USB MSC class driver is configured by the host
153                               and it can perform the data transfers.
154 
155   Note:    The application must not access the flash media (on board SPI Flash
156   in this case) while the MSC class driver is in USBD_MSC_CONFIGURED state
157   because the USB host might be performing read/write operation on the flash
158   media. If the application also attempts to access the flash media at the same
159   time, the data might get corrupted and the host will get misleading information
160   from the flash media.
161 */
162 typedef enum mss_usbd_msc_state {
163     USBD_MSC_NOT_CONFIGURED,
164     USBD_MSC_CONFIGURED,
165 }mss_usbd_msc_state_t;
166 
167 /***************************************************************************//**
168  mss_usbd_msc_media_t
169   The mss_usbd_msc_media_t provides the prototype for all the call-back
170   functions which must be implemented by the user application. The user
171   application must define and initialize a structure of this type and provide
172   the address of that structure as parameter to the MSS_USBD_MSC_init() function.
173 
174   media_init
175   The function pointed by the media_init function pointer will be called to
176   indicate that the MSC driver is configured by the USB host. The application
177   can use this function to prepare itself for data exchange with the host.
178   This driver cannot perform data transfers unless it is configured by the USB
179   host to do so.
180 
181   media_get_capacity
182   The function pointed to by the media_get_capacity function pointer is called
183   to find out the capacity of the LUN on the storage medium. The parameter lun
184   indicates the logical unit on which the current operation is being performed.
185   The application must return address of the last logical block and the size of
186   the logical block in the parameters last_block_lba and block_size respectively.
187 
188   media_is_ready
189   The function pointed by the media_is_ready function pointer is called to find
190   out if the LUN on the media is ready for the data transfers. The parameter lun
191   indicates the logical unit on which the current operation is being performed.
192 
193   media_is_write_protected
194   The function pointed by the media_is_write_protected function pointer is called
195   to find out if the LUN on the media is write-protected, before a write operation
196   can be performed on the LUN. The parameter lun indicates the logical unit on
197   which the current operation is being performed.
198 
199   media_read
200   The function pointed by the media_read function pointer is called when the
201   host wants to read the data from the storage medium. The application must
202   provide a buffer and its length which can be sent to the host. The parameter
203   lun indicates the logical unit on which the current operation is being performed.
204   The blk_addr parameter provides the logical block address and the len
205   parameter provides the number of bytes to be read. The application must provide
206   a buffer in return parameter buf and the length of this buffer must be provided
207   as the return value of this function.
208 
209   media_acquire_write_buffer
210   The function pointed by the msd_media_acquire_write_buffer function pointer is
211   called when the host wants to write data on to the storage medium. The
212   application must provide a buffer where the data sent by the host can be stored.
213   The parameter lun indicates the logical unit on which the current operation is
214   being performed. The blk_addr parameter provides the logical block address
215   where the data is to be written. The application must provide a buffer as a
216   return value of this function and the length of the buffer must be provided
217   in the return parameter length.
218 
219   media_write_ready
220   The function pointed by the media_write_ready function pointer is called when
221   the data to be written is received from the host and is ready to be written on
222   the storage medium. The data is stored in the previously provided write buffer
223   using media_acquire_write_buffer. The parameter lun indicates the logical unit
224   on which the current operation is being performed. The blk_addr parameter
225   provides the logical block address where data is to be written. The parameter
226   length provides the number of bytes to be written.
227 
228   media_get_max_lun
229   The function pointed by the media_get_max_lun function pointer is called to
230   find out the maximum number of logical units supported by the application or
231   the storage medium.
232 
233   media_inquiry
234   The function pointed by the media_inquiry function pointer is called to get
235   the response for the INQUIRY request from the MSC class host. The parameter
236   lun indicates the logical unit on which the current operation is being performed.
237   The application must provide a buffer as a return value of this function and
238   must provide the length of this buffer in the parameter length.
239 
240   media_release
241   The function pointed by the media_inquiry function pointer is called to
242   indicate that the either the MSC class device has been un-configured or is
243   disconnected from the host. The MSC class device is un-configured when it
244   receives SET_CONFIGURATION request from USB host with a cfgidx = 0. This value
245   is passed as a parameter. In case when the disconnect event is detected by the
246   USBD driver a value of cfgidx = 0xFF is passed. The application can use this
247   call-back function and its parameter to take appropriate action as required.
248  */
249 
250 typedef struct mss_usbd_msc_media {
251     uint8_t (*media_init)(uint8_t lun);
252     uint8_t (*media_get_capacity)(uint8_t lun,
253                                   uint32_t *last_block_lba,
254                                   uint32_t *block_size);
255 
256     uint8_t (*media_is_ready)(uint8_t lun);
257     uint8_t (*media_is_write_protected)(uint8_t lun);
258     uint32_t(*media_read)(uint8_t lun,
259                           uint8_t **buf,
260                           uint32_t blk_addr,
261                           uint32_t len);
262 
263     uint8_t*(*media_acquire_write_buf)(uint8_t lun,
264                                        uint32_t blk_addr,
265                                        uint32_t *len);
266 
267     uint32_t(*media_write_ready)(uint8_t lun,
268                                  uint32_t blk_addr,
269                                  uint32_t len);
270 
271     uint8_t (*media_get_max_lun)(void);
272     uint8_t*(*media_inquiry)(uint8_t lun, uint32_t *len);
273     uint8_t (*media_release)(uint8_t cfgidx);
274 
275 } mss_usbd_msc_media_t;
276 
277 /***************************************************************************//**
278  Types Internal to the USBD-MSC class driver
279  */
280 typedef enum mss_usbd_msc_bot_events {
281     BOT_EVENT_IDLE,
282     BOT_EVENT_TX,
283     BOT_EVENT_RX,
284     BOT_EVENT_TX_ERROR,
285     BOT_EVENT_RX_ERROR
286 } mss_usbd_msc_bot_events_t;
287 
288 typedef enum mss_usbd_msc_csw_status {
289     SCSI_COMMAND_PASS,
290     SCSI_COMMAND_FAIL,
291     SCSI_COMMAND_PHASE_ERR,
292     SCSI_COMMAND_LESSDATAPASS,
293 } mss_usbd_msc_csw_status_t;
294 
295 typedef enum mss_usbd_msc_bot_state {
296     BOT_IDLE,
297     BOT_DATA_RX,
298     BOT_DATA_TX,
299     BOT_SEND_STATUS,
300     BOT_ABORTED
301 } mss_usbd_msc_bot_state_t;
302 
303 /*  Command Block Wrapper (CBW) */
304 typedef struct mss_usb_msc_cbw {
305     uint32_t signature;
306     uint32_t tag;
307     uint32_t xfr_length;
308     uint8_t flags;
309     uint8_t lun;
310     uint8_t cmd_length;
311     uint8_t cmd_block[16];
312 } mss_usb_msc_cbw_t;
313 
314 /* Command Status Wrapper (CSW) */
315 typedef struct mss_usbd_msc_csw {
316     uint32_t signature;
317     uint32_t tag;
318     uint32_t data_residue;
319     mss_usbd_msc_csw_status_t status;
320 } mss_usbd_msc_csw_t;
321 
322 /* SCSI inquiry response */
323 typedef struct mss_usbd_msc_scsi_inq_resp {
324     uint8_t peripheral;
325     uint8_t removable;
326     uint8_t version;
327     uint8_t resp_data_format;
328     uint8_t additional_length;
329     uint8_t sccstp;
330     uint8_t bqueetc;
331     uint8_t cmd_que;
332     uint8_t vendor_id[8];
333     uint8_t product_id[16];
334     uint8_t product_rev[4];
335 } mss_usbd_msc_scsi_inq_resp_t;
336 
337 typedef struct mss_usbd_msc_scsi_sense_resp {
338     const uint8_t response_code;    /*0x70 - spc3-table12*/
339     const uint8_t obsolete;
340     uint8_t sense_key;              /*Table 27 - spc3-4.5.6*/
341     const uint8_t info[4];
342     const uint8_t additional_length;
343     const uint8_t command_info[4];
344     uint8_t asc;                    /*Table28 - spc3-4.5.6*/
345     const uint8_t unused[5];
346 } mss_usbd_msc_scsi_sense_resp_t;
347 
348 typedef struct mss_usbd_msc_lun_capacity {
349     uint8_t last_lba_msb;
350     uint8_t last_lba_2;
351     uint8_t last_lba_1;
352     uint8_t last_lba_lsb;
353     uint8_t block_size_msb;
354     uint8_t block_size_2;
355     uint8_t block_size_1;
356     uint8_t block_size_lsb;
357     uint32_t blk_sz_len;
358 } mss_usbd_msc_lun_capacity_t;
359 
360 /***************************************************************************//**
361  Exported functions from USBD-MSC class driver
362  */
363 
364 /***************************************************************************//**
365   @brief MSS_USBD_MSC_init()
366   The MSS_USBD_MSC_init() function must be used by the application to initialize
367   the MSC class driver. A pointer to the structure of type mss_usbd_msc_media_t
368   must be passed as a parameter to this function.
369 
370   @param media_ops
371   The media_ops parameter is a pointer to the structure of type
372   mss_usbd_msc_media_t. This is used by the MSC class driver to call the
373   call-back functions implemented by the application.
374 
375   @param speed
376   The speed parameter indicates the USB speed at which this class driver must
377   operate.
378 
379   @return
380     This function does not return a value.
381 
382   Example:
383   @code
384         //Assign call-back function handler structure needed by MSD class driver
385         MSS_USBD_MSC_init(&usb_flash_media, MSS_USB_DEVICE_HS);
386 
387         //Assign call-back function handler structure needed by USB Device Core driver
388         MSS_USBD_set_desc_cb_handler(&flash_drive_descr_cb);
389 
390         //Initialize USB driver HS device mode
391         MSS_USBD_init(MSS_USB_DEVICE_HS);
392 
393   @endcode
394 */
395 void
396 MSS_USBD_MSC_init
397 (
398     mss_usbd_msc_media_t* media_ops,
399     mss_usb_device_speed_t speed
400 );
401 
402 /***************************************************************************//**
403   @brief USBD_get_state()
404   The MSS_USBD_MSC_get_state() function can be used by the application to find
405   out the current state of the MSC class driver.
406 
407   @param media_ops
408     This function does not take a parameter.
409 
410   @return
411     This function returns a value of type mss_usbd_msc_state_t indicating the
412     current state of the MSC class driver.
413 
414   Example:
415   @code
416         uint8_t
417         usb_flash_media_get_capacity
418         (
419             uint8_t lun,
420             uint32_t *no_of_blocks,
421             uint32_t *block_size
422         )
423         {
424            if(USBD_MSC_CONFIGURED == MSS_USBD_MSC_get_state())
425            {
426               *no_of_blocks = lun_data[lun].number_of_blocks;
427               *block_size = lun_data[lun].lba_block_size;
428               return 1;
429            }
430            Else
431            {
432               Return 0;
433            }
434         }
435 
436   @endcode
437 */
438 mss_usbd_msc_state_t
439 MSS_USBD_MSC_get_state
440 (
441     void
442 );
443 
444 #endif  //MSS_USB_DEVICE_ENABLED
445 
446 #ifdef __cplusplus
447 }
448 #endif
449 
450 #endif  /* __MSS_USB_DEVICE_MSD_H_ */
451