EFM32 Happy Gecko Software Documentation  efm32hg-doc-5.1.2
msdh.c
Go to the documentation of this file.
1 /**************************************************************************/
17 #include <inttypes.h>
18 #include "em_device.h"
19 #include "em_usb.h"
20 #include "msdscsi.h"
21 
24 #define DEV_ADDR 1 /* USB bus address */
25 #define BULK_OUT &ep[ epOutIndex ] /* Endpoint "handles" */
26 #define BULK_IN &ep[ epInIndex ]
27 #define SCSI_INQUIRY_RETRIES 10 // Retries during MSDH_Init()
28 #define SCSI_TESTUNITREADY_RETRIES 10 // Retries during MSDH_Init()
29 
30 static void PrintDeviceStrings(uint8_t *buf);
31 static bool QualifyDevice(uint8_t *buf);
32 
33 static USBH_Device_TypeDef device;
34 static USBH_Ep_TypeDef ep[ 2 ];
35 static int epOutIndex;
36 static int epInIndex;
37 
40 /***************************************************************************/
61 bool MSDH_Init(uint8_t *usbDeviceInfo, int usbDeviceInfoSize)
62 {
63  SL_ALIGN(4)
65 
66  SL_ALIGN(4)
68 
69  SL_ALIGN(4)
71 
72  bool ready;
73  int result, i;
74 
75  /* Read all device descriptors. */
76  if (USBH_QueryDeviceB(usbDeviceInfo, usbDeviceInfoSize, USBH_GetPortSpeed())
77  != USB_STATUS_OK)
78  {
79  USB_PRINTF("\nUSB descriptor retrieval failed.");
80  return false;
81  }
82 
83  /* Check if a valid MSD device (will activate device if OK). */
84  if (!QualifyDevice(usbDeviceInfo))
85  {
86  return false;
87  }
88 
89  /* Initialize MSD SCSI module. */
90  if (!MSDSCSI_Init(BULK_OUT, BULK_IN))
91  {
92  USB_PRINTF("\nMSD BOT initialization failed.");
93  return false;
94  }
95 
96  // Do a SCSI Inquiry to get some info from the device.
97  // Some devices are slow to get started, do a couple of retries before
98  // giving up.
99  i = 0;
100  do
101  {
102  ready = MSDSCSI_Inquiry(&inquiryData);
103  if (!ready)
104  {
105  USBTIMER_DelayMs(500);
106  }
107  i++;
108  } while (!ready && i < SCSI_INQUIRY_RETRIES);
109 
110  if (!ready)
111  {
112  USB_PRINTF("\nMSD SCSI Inquiry failed.");
113  return false;
114  }
115 
116  memcpy(usbDeviceInfo, &inquiryData.T10VendorId, sizeof(inquiryData.T10VendorId));
117  usbDeviceInfo[ sizeof(inquiryData.T10VendorId) ] = '\0';
118  USB_PRINTF("\nSCSI Inquiry Vendor ID string : \"%s\"", usbDeviceInfo);
119 
120  memcpy(usbDeviceInfo, &inquiryData.ProductId, sizeof(inquiryData.ProductId));
121  usbDeviceInfo[ sizeof(inquiryData.ProductId) ] = '\0';
122  USB_PRINTF("\nSCSI Inquiry Product ID string : \"%s\"", usbDeviceInfo);
123 
124  memcpy(usbDeviceInfo, &inquiryData.ProductRevisionLevel, sizeof(inquiryData.ProductRevisionLevel));
125  usbDeviceInfo[ sizeof(inquiryData.ProductRevisionLevel) ] = '\0';
126  USB_PRINTF("\nSCSI Inquiry Product Revision string : \"%s\"", usbDeviceInfo);
127 
128  /* Is it a block device ? */
129  if ((inquiryData.PeripheralQualifier != 0) ||
130  (inquiryData.PeripheralDeviceType != 0))
131  {
132  return false;
133  }
134 
135  /* Wait for upto 5 seconds for device to become ready. */
136  i = 0;
137  do
138  {
139  result = MSDSCSI_RequestSense(&reqSenseData);
140  ready = MSDSCSI_TestUnitReady();
141  if (!ready)
142  {
143  USBTIMER_DelayMs(500);
144  }
145  i++;
146  } while (!ready && i < SCSI_TESTUNITREADY_RETRIES && result);
147 
148  if (!result)
149  {
150  USB_PRINTF("\n\nSCSI Request Sense execution error");
151  return false;
152  }
153 
154  if (!ready)
155  {
156  USB_PRINTF("\n\nMSD device not ready");
157  return false;
158  }
159 
160  /* Get device capacity. */
161  if (!MSDSCSI_ReadCapacity(&capacityData))
162  {
163  USB_PRINTF("\n\nSCSI Read Capacity execution error");
164  return false;
165  }
166 
167  USB_PRINTF("\n\nSCSI Read Capacity LBA count : %ld = %ld MiB",
168  capacityData.LogicalBlockAddress,
169  (uint32_t)
170  (((uint64_t)capacityData.LogicalBlockAddress
171  * capacityData.LogicalBlockLength) / (1024 * 1024)));
172  USB_PRINTF("\nSCSI Read Capacity LBA size : %ld\n\n",
173  capacityData.LogicalBlockLength);
174 
175  return true;
176 }
177 
178 /***************************************************************************/
188 bool MSDH_GetSectorCount(uint32_t *sectorCount)
189 {
190  SL_ALIGN(4)
192 
193  if (!MSDSCSI_ReadCapacity(&capacityData))
194  return false;
195 
196  *sectorCount = capacityData.LogicalBlockAddress;
197 
198  return true;
199 }
200 
201 /***************************************************************************/
211 bool MSDH_GetSectorSize(uint16_t *sectorSize)
212 {
213  SL_ALIGN(4)
215 
216  if (!MSDSCSI_ReadCapacity(&capacityData))
217  return false;
218 
219  *sectorSize = (uint16_t) capacityData.LogicalBlockLength;
220 
221  return true;
222 }
223 
224 /***************************************************************************/
237 bool MSDH_GetBlockSize(uint32_t *blockSize)
238 {
239  SL_ALIGN(4)
241 
242  if (!MSDSCSI_ReadCapacity(&capacityData))
243  return false;
244 
245  *blockSize = capacityData.LogicalBlockLength;
246 
247  return true;
248 }
249 
250 /***************************************************************************/
266 bool MSDH_ReadSectors(uint32_t lba, uint16_t sectors, void *data)
267 {
268  return MSDSCSI_Read10(lba, sectors, data);
269 }
270 
271 /***************************************************************************/
287 bool MSDH_WriteSectors(uint32_t lba, uint16_t sectors, const void *data)
288 {
289  return MSDSCSI_Write10(lba, sectors, data);
290 }
291 
294 /***************************************************************************/
305 static bool QualifyDevice(uint8_t *buf)
306 {
307  int i;
308  bool retVal = false;
309  bool epIn = false, epOut = false;
310 
311  if ((USBH_QGetDeviceDescriptor(buf)->bDeviceClass == 0) &&
312  (USBH_QGetDeviceDescriptor(buf)->bDeviceSubClass == 0) &&
313  (USBH_QGetDeviceDescriptor(buf)->bDeviceProtocol == 0) &&
314  (USBH_QGetInterfaceDescriptor(buf, 0, 0)->bInterfaceClass == USB_CLASS_MSD) &&
315  (USBH_QGetInterfaceDescriptor(buf, 0, 0)->bInterfaceSubClass == USB_CLASS_MSD_SCSI_CMDSET) &&
316  (USBH_QGetInterfaceDescriptor(buf, 0, 0)->bInterfaceProtocol == USB_CLASS_MSD_BOT_TRANSPORT) &&
317  (USBH_QGetInterfaceDescriptor(buf, 0, 0)->bNumEndpoints >= 2))
318  {
319  /*
320  * OK so far, scan through endpoints and look for one BULK IN and
321  * one BULK OUT endpoint.
322  */
323  for (i = 0; i < USBH_QGetInterfaceDescriptor(buf, 0, 0)->bNumEndpoints; i++)
324  {
325  if (USBH_QGetEndpointDescriptor(buf, 0, 0, i)->bmAttributes == USB_EPTYPE_BULK)
326  {
327  if (USBH_QGetEndpointDescriptor(buf, 0, 0, i)->bEndpointAddress & USB_EP_DIR_IN)
328  {
329  if (!epIn)
330  {
331  epIn = true;
332  epInIndex = i;
333  }
334  }
335  else
336  {
337  if (!epOut)
338  {
339  epOut = true;
340  epOutIndex = i;
341  }
342  }
343  }
344 
345  /* Success ? */
346  if (epIn && epOut)
347  break;
348  }
349 
350  if ((epIn && epOut) && (epInIndex < 2) && (epOutIndex < 2))
351  {
352  /*
353  * Some MSD devices has more than the two required bulk endpoints.
354  * We will only accept devices where the two bulk endpoints are
355  * the two first endpoints within the interface.
356  */
357  USB_PRINTF("\nThis is a valid MSD device.");
358  retVal = true;
359  }
360  }
361 
362  if (retVal == false)
363  {
364  USBH_PrintDeviceDescriptor(USBH_QGetDeviceDescriptor(buf));
365  USBH_PrintConfigurationDescriptor(USBH_QGetConfigurationDescriptor(buf, 0), USB_CONFIG_DESCSIZE);
366  USBH_PrintInterfaceDescriptor(USBH_QGetInterfaceDescriptor(buf, 0, 0));
367 
368  for (i = 0; i < USBH_QGetInterfaceDescriptor(buf, 0, 0)->bNumEndpoints; i++)
369  {
370  USBH_PrintEndpointDescriptor(USBH_QGetEndpointDescriptor(buf, 0, 0, i));
371  }
372  USB_PRINTF("\nThis is not a valid MSD device, review device descriptors.");
373  }
374  else
375  {
376  /* All set, activate the device. */
377 
378  USBH_InitDeviceData(&device, buf, ep, 2, USBH_GetPortSpeed());
379  PrintDeviceStrings(buf);
380  USBH_SetAddressB(&device, DEV_ADDR);
381  USBH_SetConfigurationB(&device, device.confDesc.bConfigurationValue);
382 
383  /* Assign Host Channels to the endpoints */
384  USBH_AssignHostChannel(BULK_OUT, 2);
385  USBH_AssignHostChannel(BULK_IN, 3);
386 
387  USB_PRINTF("\n\nDevice VID/PID is 0x%04X/0x%04X, device bus speed is %s",
388  device.devDesc.idVendor, device.devDesc.idProduct,
389  USBH_GetPortSpeed() == PORT_FULL_SPEED ? "FULL" : "LOW");
390  }
391 
392  return retVal;
393 }
394 
395 /***************************************************************************/
402 static void PrintDeviceStrings(uint8_t *buf)
403 {
404  /* Get and print device string descriptors. */
405 
406  if (device.devDesc.iManufacturer)
407  {
408  USBH_GetStringB(&device, buf, 255, device.devDesc.iManufacturer,
410  USBH_PrintString("\n\niManufacturer = \"",
411  (USB_StringDescriptor_TypeDef*) buf, "\"");
412  }
413  else
414  {
415  USB_PRINTF("\n\niManufacturer = <NONE>");
416  }
417 
418  if (device.devDesc.iProduct)
419  {
420  USBH_GetStringB(&device, buf, 255, device.devDesc.iProduct,
422  USBH_PrintString("\niProduct = \"",
423  (USB_StringDescriptor_TypeDef*) buf, "\"");
424  }
425  else
426  {
427  USB_PRINTF("\niProduct = <NONE>");
428  }
429 
430  if (device.devDesc.iSerialNumber)
431  {
432  USBH_GetStringB(&device, buf, 255, device.devDesc.iSerialNumber,
434  USBH_PrintString("\niSerialNumber = \"",
435  (USB_StringDescriptor_TypeDef*) buf, "\"\n");
436  }
437  else
438  {
439  USB_PRINTF("\niSerialNumber = <NONE>\n");
440  }
441 }
442 
bool MSDSCSI_TestUnitReady(void)
Issue a SCSI Test Unit Ready command.
Definition: msdscsi.c:283
SCSI Inquiry response data typedef.
Definition: msdscsi.h:76
bool MSDH_WriteSectors(uint32_t lba, uint16_t sectors, const void *data)
Write sectors to device.
Definition: msdh.c:287
SCSI Request Sense response data typedef.
Definition: msdscsi.h:155
int USB_PRINTF(const char *format,...)
Transmit "printf" formated data on the debug serial port.
#define PORT_FULL_SPEED
Definition: em_usb.h:239
CMSIS Cortex-M Peripheral Access Layer for Silicon Laboratories microcontroller devices.
#define SL_ALIGN(X)
Macro for aligning a variable. Use this macro before the variable definition. X denotes the stora...
Definition: em_common.h:168
#define USB_EP_DIR_IN
Definition: em_usb.h:185
bool MSDH_ReadSectors(uint32_t lba, uint16_t sectors, void *data)
Read sectors from device.
Definition: msdh.c:266
SCSI Read Capacity response data typedef.
Definition: msdscsi.h:214
bool MSDH_GetBlockSize(uint32_t *blockSize)
Get blocksize from the device.
Definition: msdh.c:237
#define USB_CLASS_MSD_BOT_TRANSPORT
Definition: em_usb.h:223
bool MSDSCSI_Write10(uint32_t lba, uint16_t sectors, const void *data)
Perform a SCSI Write(10) command.
Definition: msdscsi.c:307
void USBTIMER_DelayMs(uint32_t msec)
Active wait millisecond delay function. Can also be used inside interrupt handlers.
Definition: em_usbtimer.c:138
bool MSDH_GetSectorSize(uint16_t *sectorSize)
Get sectorsize from the device.
Definition: msdh.c:211
SCSI interface for Mass Storage Devices (MSD).
#define USB_CONFIG_DESCSIZE
Definition: em_usb.h:153
#define SL_ATTRIBUTE_ALIGN(X)
GCC style macro for aligning a variable.
Definition: em_common.h:160
bool MSDH_Init(uint8_t *usbDeviceInfo, int usbDeviceInfoSize)
Initialize an USB connected Mass Storage Device. Checks if the device is a valid MSD device...
Definition: msdh.c:61
USB protocol stack library API for EFM32/EZR32.
#define USB_CLASS_MSD
Definition: em_usb.h:222
#define USB_CLASS_MSD_SCSI_CMDSET
Definition: em_usb.h:224
#define USB_LANGID_ENUS
Definition: em_usb.h:188
bool MSDSCSI_RequestSense(MSDSCSI_RequestSenseData_TypeDef *data)
Issue a SCSI Request Sense command.
Definition: msdscsi.c:267
bool MSDH_GetSectorCount(uint32_t *sectorCount)
Get sectorcount from the device.
Definition: msdh.c:188
bool MSDSCSI_Init(USBH_Ep_TypeDef *out, USBH_Ep_TypeDef *in)
MSDSCSI module initialization.
Definition: msdscsi.c:157
bool MSDSCSI_Read10(uint32_t lba, uint16_t sectors, void *data)
Issue a SCSI Read(10) command.
Definition: msdscsi.c:213
USB String Descriptor.
Definition: em_usb.h:514
bool MSDSCSI_ReadCapacity(MSDSCSI_ReadCapacityData_TypeDef *data)
Issue a SCSI Read Capacity command.
Definition: msdscsi.c:240
#define USB_EPTYPE_BULK
Definition: em_usb.h:179
bool MSDSCSI_Inquiry(MSDSCSI_InquiryData_TypeDef *data)
Issue a SCSI Inquiry command.
Definition: msdscsi.c:189