PS2SDK
PS2 Homebrew Libraries
usb_mass.c
1 /*
2  * usb_driver.c - USB Mass Storage Driver
3  * See usbmass-ufi10.pdf and usbmassbulk_10.pdf
4  */
5 
6 #include <errno.h>
7 #include <stdio.h>
8 #include <sysclib.h>
9 #include <thbase.h>
10 #include <thsemap.h>
11 #include <usbd.h>
12 #include <usbd_macro.h>
13 
14 #include "scsi.h"
15 #include <usbhdfsd-common.h>
16 
17 // #define ASYNC
18 // #define DEBUG //comment out this line when not debugging
19 #include "module_debug.h"
20 
21 #define USB_SUBCLASS_MASS_RBC 0x01
22 #define USB_SUBCLASS_MASS_ATAPI 0x02
23 #define USB_SUBCLASS_MASS_QIC 0x03
24 #define USB_SUBCLASS_MASS_UFI 0x04
25 #define USB_SUBCLASS_MASS_SFF_8070I 0x05
26 #define USB_SUBCLASS_MASS_SCSI 0x06
27 
28 #define USB_PROTOCOL_MASS_CBI 0x00
29 #define USB_PROTOCOL_MASS_CBI_NO_CCI 0x01
30 #define USB_PROTOCOL_MASS_BULK_ONLY 0x50
31 
32 #define USB_BLK_EP_IN 0
33 #define USB_BLK_EP_OUT 1
34 
35 #define USB_XFER_MAX_RETRIES 8
36 
37 #define CBW_TAG 0x43425355
38 #define CSW_TAG 0x53425355
39 
40 typedef struct _mass_dev
41 {
42  int controlEp; // config endpoint id
43  int bulkEpI; // in endpoint id
44  int bulkEpO; // out endpoint id
45  int devId; // device id
46  unsigned char configId; // configuration id
47  unsigned char status;
48  unsigned char interfaceNumber; // interface number
49  unsigned char interfaceAlt; // interface alternate setting
50  int ioSema;
51  struct scsi_interface scsi;
52 } mass_dev;
53 
54 typedef struct _cbw_packet
55 {
56  unsigned int signature;
57  unsigned int tag;
58  unsigned int dataTransferLength;
59  unsigned char flags; // 80->data in, 00->out
60  unsigned char lun;
61  unsigned char comLength; // command data length
62  unsigned char comData[16]; // command data
63 } cbw_packet;
64 
65 typedef struct _csw_packet
66 {
67  unsigned int signature;
68  unsigned int tag;
69  unsigned int dataResidue;
70  unsigned char status;
71 } csw_packet;
72 
73 static sceUsbdLddOps driver;
74 
75 typedef struct _usb_callback_data
76 {
77  int sema;
78  int returnCode;
79  int returnSize;
81 
82 #define USB_BLOCK_SIZE 4096 // Maximum single USB 1.1 transfer length.
83 
84 typedef struct _usb_transfer_callback_data
85 {
86  int sema;
87  int pipe;
88  u8 *buffer;
89  int returnCode;
90  unsigned int remaining;
92 
93 #define NUM_DEVICES 2
94 static mass_dev g_mass_device[NUM_DEVICES];
95 static int usb_mass_update_sema;
96 
97 static void usb_callback(int resultCode, int bytes, void *arg);
98 #ifndef ASYNC
99 static int perform_bulk_transfer(usb_transfer_callback_data *data);
100 static void usb_transfer_callback(int resultCode, int bytes, void *arg);
101 #endif
102 static void usb_mass_release(mass_dev *dev);
103 
104 static void usb_callback(int resultCode, int bytes, void *arg)
105 {
106  usb_callback_data *data = (usb_callback_data *)arg;
107  data->returnCode = resultCode;
108  data->returnSize = bytes;
109  M_DEBUG("callback: res %d, bytes %d, arg %p \n", resultCode, bytes, arg);
110  SignalSema(data->sema);
111 }
112 
113 #ifndef ASYNC
114 static int perform_bulk_transfer(usb_transfer_callback_data *data)
115 {
116  int ret, len;
117 
118  len = data->remaining > USB_BLOCK_SIZE ? USB_BLOCK_SIZE : data->remaining;
119  ret = sceUsbdBulkTransfer(
120  data->pipe, // bulk pipe epI (Read) or epO (Write)
121  data->buffer, // data ptr
122  len, // data length
123  &usb_transfer_callback,
124  (void *)data);
125  return ret;
126 }
127 
128 static void usb_transfer_callback(int resultCode, int bytes, void *arg)
129 {
131 
132  data->returnCode = resultCode;
133  if (resultCode == USB_RC_OK) { // Update transfer progress if successful.
134  data->remaining -= bytes;
135  data->buffer += bytes;
136  }
137 
138  if ((resultCode == USB_RC_OK) && (data->remaining > 0)) { // OK to continue.
139  int ret;
140 
141  ret = perform_bulk_transfer(data);
142  if (ret != USB_RC_OK) {
143  data->returnCode = ret;
144  SignalSema(data->sema);
145  }
146  } else {
147  SignalSema(data->sema);
148  }
149 }
150 #endif
151 
152 static int usb_set_configuration(mass_dev *dev, int configNumber)
153 {
154  int ret;
155  usb_callback_data cb_data;
156 
157  cb_data.sema = dev->ioSema;
158 
159  M_DEBUG("setting configuration controlEp=%i, confNum=%i \n", dev->controlEp, configNumber);
160  ret = sceUsbdSetConfiguration(dev->controlEp, configNumber, usb_callback, (void *)&cb_data);
161 
162  if (ret == USB_RC_OK) {
163  WaitSema(cb_data.sema);
164  ret = cb_data.returnCode;
165  }
166 
167  return ret;
168 }
169 
170 static int usb_set_interface(mass_dev *dev, int interface, int altSetting)
171 {
172  int ret;
173  usb_callback_data cb_data;
174 
175  cb_data.sema = dev->ioSema;
176 
177  M_DEBUG("setting interface controlEp=%i, interface=%i altSetting=%i\n", dev->controlEp, interface, altSetting);
178  ret = sceUsbdSetInterface(dev->controlEp, interface, altSetting, usb_callback, (void *)&cb_data);
179 
180  if (ret == USB_RC_OK) {
181  WaitSema(cb_data.sema);
182  ret = cb_data.returnCode;
183  }
184 
185  return ret;
186 }
187 
188 static int usb_bulk_clear_halt(mass_dev *dev, int endpoint)
189 {
190  int ret;
191  usb_callback_data cb_data;
192 
193  cb_data.sema = dev->ioSema;
194 
195  ret = sceUsbdClearEndpointFeature(
196  dev->controlEp, // Config pipe
197  0, // HALT feature
198  (endpoint == USB_BLK_EP_IN) ? dev->bulkEpI : dev->bulkEpO,
199  usb_callback,
200  (void *)&cb_data);
201 
202  if (ret == USB_RC_OK) {
203  WaitSema(cb_data.sema);
204  ret = cb_data.returnCode;
205  }
206  if (ret != USB_RC_OK) {
207  M_DEBUG("ERROR: sending clear halt %d\n", ret);
208  }
209 
210  return ret;
211 }
212 
213 #ifndef ASYNC
214 static void usb_bulk_reset(mass_dev *dev, int mode)
215 {
216  int ret;
217  usb_callback_data cb_data;
218 
219  cb_data.sema = dev->ioSema;
220 
221  // Call Bulk only mass storage reset
222  ret = sceUsbdControlTransfer(
223  dev->controlEp, // default pipe
224  0x21, // bulk reset
225  0xFF,
226  0,
227  dev->interfaceNumber, // interface number
228  0, // length
229  NULL, // data
230  usb_callback,
231  (void *)&cb_data);
232 
233  if (ret == USB_RC_OK) {
234  WaitSema(cb_data.sema);
235  ret = cb_data.returnCode;
236  }
237  if (ret == USB_RC_OK) {
238  // clear bulk-in endpoint
239  if (mode & 0x01)
240  ret = usb_bulk_clear_halt(dev, USB_BLK_EP_IN);
241  }
242  if (ret == USB_RC_OK) {
243  // clear bulk-out endpoint
244  if (mode & 0x02)
245  ret = usb_bulk_clear_halt(dev, USB_BLK_EP_OUT);
246  }
247  if (ret != USB_RC_OK) {
248  M_DEBUG("ERROR: sending reset %d to device %d.\n", ret, dev->devId);
249  dev->status |= USBMASS_DEV_STAT_ERR;
250  }
251 }
252 
253 static int usb_bulk_status(mass_dev *dev, csw_packet *csw, unsigned int tag)
254 {
255  int ret;
256  usb_callback_data cb_data;
257 
258  cb_data.sema = dev->ioSema;
259 
260  csw->signature = CSW_TAG;
261  csw->tag = tag;
262  csw->dataResidue = 0;
263  csw->status = 0;
264 
265  ret = sceUsbdBulkTransfer(
266  dev->bulkEpI, // bulk input pipe
267  csw, // data ptr
268  13, // data length
269  usb_callback,
270  (void *)&cb_data);
271 
272  if (ret == USB_RC_OK) {
273  WaitSema(cb_data.sema);
274  ret = cb_data.returnCode;
275 
276 #ifdef DEBUG
277  if (cb_data.returnSize != 13)
278  M_DEBUG("bulk csw.status returnSize: %i != 13\n", cb_data.returnSize);
279  if (csw->dataResidue != 0)
280  M_DEBUG("bulk csw.status residue: %i\n", csw->dataResidue);
281  M_DEBUG("bulk csw result: %d, csw.status: %i\n", ret, csw->status);
282 #endif
283  }
284 
285  return ret;
286 }
287 
288 /* see flow chart in the usbmassbulk_10.pdf doc (page 15)
289 
290  Returned values:
291  <0 Low-level USBD error.
292  0 = Command completed successfully.
293  1 = Command failed.
294  2 = Phase error.
295 */
296 static int usb_bulk_manage_status(mass_dev *dev, unsigned int tag)
297 {
298  int ret;
299  csw_packet csw;
300 
301  // XPRINTF("USBHDFSD: usb_bulk_manage_status 1 ...\n");
302  ret = usb_bulk_status(dev, &csw, tag); /* Attempt to read CSW from bulk in endpoint */
303  if (ret != USB_RC_OK) { /* STALL bulk in -OR- Bulk error */
304  usb_bulk_clear_halt(dev, USB_BLK_EP_IN); /* clear the stall condition for bulk in */
305 
306  M_DEBUG("ERROR: usb_bulk_manage_status error %d ...\n", ret);
307  ret = usb_bulk_status(dev, &csw, tag); /* Attempt to read CSW from bulk in endpoint */
308  }
309 
310  /* CSW not valid or stalled or phase error */
311  if (ret != USB_RC_OK || csw.signature != CSW_TAG || csw.tag != tag || csw.status == 2) {
312  M_DEBUG("ERROR: usb_bulk_manage_status call reset recovery ...\n");
313  usb_bulk_reset(dev, 3); /* Perform reset recovery */
314  }
315 
316  return ((ret == USB_RC_OK && csw.signature == CSW_TAG && csw.tag == tag) ? csw.status : -1);
317 }
318 #endif
319 
320 static int usb_bulk_get_max_lun(struct scsi_interface *scsi)
321 {
322  mass_dev *dev = (mass_dev *)scsi->priv;
323  int ret;
324  usb_callback_data cb_data;
325  char max_lun;
326 
327  cb_data.sema = dev->ioSema;
328 
329  // Call Bulk only mass storage reset
330  ret = sceUsbdControlTransfer(
331  dev->controlEp, // default pipe
332  0xA1,
333  0xFE,
334  0,
335  dev->interfaceNumber, // interface number
336  1, // length
337  &max_lun, // data
338  usb_callback,
339  (void *)&cb_data);
340 
341  if (ret == USB_RC_OK) {
342  WaitSema(cb_data.sema);
343  ret = cb_data.returnCode;
344  }
345  if (ret == USB_RC_OK) {
346  ret = max_lun;
347  } else {
348  // Devices that do not support multiple LUNs may STALL this command.
349  usb_bulk_clear_halt(dev, USB_BLK_EP_IN);
350  usb_bulk_clear_halt(dev, USB_BLK_EP_OUT);
351 
352  ret = -ret;
353  }
354 
355  return ret;
356 }
357 
359 {
360  mass_dev *dev;
361 
362  cbw_packet cbw;
363  csw_packet csw;
364  int cmd_count;
365 
366  int returnCode;
367 };
368 
369 #ifndef ASYNC
370 static int usb_bulk_command(mass_dev *dev, cbw_packet *packet)
371 {
372  int ret;
373  usb_callback_data cb_data;
374 
375  if (dev->status & USBMASS_DEV_STAT_ERR) {
376  M_DEBUG("Rejecting I/O to offline device %d.\n", dev->devId);
377  return -1;
378  }
379 
380  cb_data.sema = dev->ioSema;
381 
382  ret = sceUsbdBulkTransfer(
383  dev->bulkEpO, // bulk output pipe
384  packet, // data ptr
385  31, // data length
386  usb_callback,
387  (void *)&cb_data);
388 
389  if (ret == USB_RC_OK) {
390  WaitSema(cb_data.sema);
391  ret = cb_data.returnCode;
392  }
393  if (ret != USB_RC_OK) {
394  M_DEBUG("ERROR: sending bulk command %d. Calling reset recovery.\n", ret);
395  usb_bulk_reset(dev, 3);
396  }
397 
398  return ret;
399 }
400 
401 static int usb_bulk_transfer(mass_dev *dev, int direction, void *buffer, unsigned int transferSize)
402 {
403  int ret;
405 
406  cb_data.sema = dev->ioSema;
407  cb_data.pipe = (direction == USB_BLK_EP_IN) ? dev->bulkEpI : dev->bulkEpO;
408  cb_data.buffer = buffer;
409  cb_data.returnCode = 0;
410  cb_data.remaining = transferSize;
411 
412  ret = perform_bulk_transfer(&cb_data);
413  if (ret == USB_RC_OK) {
414  WaitSema(cb_data.sema);
415  ret = cb_data.returnCode;
416  }
417 
418  if (ret != USB_RC_OK) {
419  M_DEBUG("ERROR: bulk data transfer %d. Clearing HALT state.\n", ret);
420  usb_bulk_clear_halt(dev, direction);
421  }
422 
423  return ret;
424 }
425 
426 #else
427 
428 static void scsi_cmd_callback(int resultCode, int bytes, void *arg)
429 {
430  struct usbmass_cmd *ucmd = (struct usbmass_cmd *)arg;
431  mass_dev *dev = (mass_dev *)ucmd->dev;
432 
433  M_DEBUG("%s, result=%d\n", __func__, resultCode);
434 
435  ucmd->cmd_count--;
436  if ((resultCode != USB_RC_OK) || (ucmd->cmd_count == 0)) {
437  // TODO: handle error in an async way (cannot block in usb callback)
438  ucmd->returnCode = resultCode;
439  SignalSema(dev->ioSema);
440  }
441 }
442 #endif
443 
444 int usb_queue_cmd(struct scsi_interface *scsi, const unsigned char *cmd, unsigned int cmd_len, unsigned char *data, unsigned int data_len, unsigned int data_wr)
445 {
446  mass_dev *dev = (mass_dev *)scsi->priv;
447  static struct usbmass_cmd ucmd;
448  int result;
449 #ifndef ASYNC
450  int rcode;
451 #endif
452  static unsigned int tag = 0;
453 
454  M_DEBUG("%s\n", __func__);
455 
456  tag++;
457 
458  // Create USB command
459  ucmd.dev = dev;
460  ucmd.cmd_count = 0;
461 
462  // Create CBW
463  ucmd.cbw.signature = CBW_TAG;
464  ucmd.cbw.tag = tag;
465  ucmd.cbw.dataTransferLength = data_len;
466  ucmd.cbw.flags = data_wr ? 0 : 0x80;
467  ucmd.cbw.lun = 0;
468  ucmd.cbw.comLength = cmd_len;
469  memcpy(ucmd.cbw.comData, cmd, cmd_len);
470 
471  // Create CSW
472  ucmd.csw.signature = CSW_TAG;
473  ucmd.csw.tag = tag;
474  ucmd.csw.dataResidue = 0;
475  ucmd.csw.status = 0;
476 
477 #ifndef ASYNC
478  result = -EIO;
479  if (usb_bulk_command(dev, &ucmd.cbw) == USB_RC_OK) {
480  if (data_len > 0)
481  rcode = usb_bulk_transfer(dev, data_wr ? USB_BLK_EP_OUT : USB_BLK_EP_IN, data, data_len);
482  else
483  rcode = USB_RC_OK;
484 
485  result = usb_bulk_manage_status(dev, tag);
486 
487  if (rcode != USB_RC_OK)
488  result = -EIO;
489  }
490 
491  return result;
492 #else
493  // Send the CBW (command)
494  ucmd.cmd_count++;
495  result = sceUsbdBulkTransfer(dev->bulkEpO, &ucmd.cbw, 31, scsi_cmd_callback, (void *)&ucmd);
496  if (result != USB_RC_OK)
497  return -EIO;
498 
499  // Send/Receive data
500  while (data_len > 0) {
501  unsigned int tr_len = (data_len < USB_BLOCK_SIZE) ? data_len : USB_BLOCK_SIZE;
502  ucmd.cmd_count++;
503  result = sceUsbdBulkTransfer(data_wr ? dev->bulkEpO : dev->bulkEpI, data, tr_len, scsi_cmd_callback, (void *)&ucmd);
504  if (result != USB_RC_OK)
505  return -EIO;
506  data_len -= tr_len;
507  data += tr_len;
508  }
509 
510  // Receive CSW (status)
511  ucmd.cmd_count++;
512  result = sceUsbdBulkTransfer(dev->bulkEpI, &ucmd.csw, 13, scsi_cmd_callback, (void *)&ucmd);
513  if (result != USB_RC_OK)
514  return -EIO;
515 
516  // Wait for SCSI command to finish
517  WaitSema(dev->ioSema);
518  if (ucmd.returnCode != USB_RC_OK)
519  return -EIO;
520 
521  return 0;
522 #endif
523 }
524 
525 static mass_dev *usb_mass_findDevice(int devId, int create)
526 {
527  mass_dev *dev = NULL;
528  int i;
529  M_DEBUG("usb_mass_findDevice devId %i\n", devId);
530  for (i = 0; i < NUM_DEVICES; ++i) {
531  if (g_mass_device[i].devId == devId) {
532  M_DEBUG("usb_mass_findDevice exists %i\n", i);
533  dev = &g_mass_device[i];
534  break;
535  } else if (create && dev == NULL && g_mass_device[i].devId == -1) {
536  dev = &g_mass_device[i];
537  break;
538  }
539  }
540  return dev;
541 }
542 
543 /* test that endpoint is bulk endpoint and if so, update device info */
544 static void usb_bulk_probeEndpoint(int devId, mass_dev *dev, UsbEndpointDescriptor *endpoint)
545 {
546  if (endpoint->bmAttributes == USB_ENDPOINT_XFER_BULK) {
547  if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT && dev->bulkEpO < 0) {
548  /* When sceUsbdOpenPipe() is used to work around the hardware errata that occurs when an unaligned memory address is specified,
549  some USB devices become incompatible. Hence it is preferable to do alignment correction in software instead. */
550  dev->bulkEpO = sceUsbdOpenPipeAligned(devId, endpoint);
551  M_DEBUG("register Output endpoint id =%i addr=%02X packetSize=%i\n", dev->bulkEpO, endpoint->bEndpointAddress, (unsigned short int)endpoint->wMaxPacketSizeHB << 8 | endpoint->wMaxPacketSizeLB);
552  } else
553  /* Open this pipe with sceUsbdOpenPipe, to allow unaligned addresses to be used.
554  According to the Sony documentation and the USBD code,
555  there is always an alignment check if the pipe is opened with the sceUsbdOpenPipeAligned(),
556  even when there is never any correction for the bulk out pipe. */
557  if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN && dev->bulkEpI < 0) {
558  dev->bulkEpI = sceUsbdOpenPipe(devId, endpoint);
559  M_DEBUG("register Input endpoint id =%i addr=%02X packetSize=%i\n", dev->bulkEpI, endpoint->bEndpointAddress, (unsigned short int)endpoint->wMaxPacketSizeHB << 8 | endpoint->wMaxPacketSizeLB);
560  }
561  }
562 }
563 
564 static int usb_mass_probe(int devId)
565 {
566  UsbDeviceDescriptor *device = NULL;
567  UsbConfigDescriptor *config = NULL;
568  UsbInterfaceDescriptor *intf = NULL;
569 
570  M_DEBUG("probe: devId=%i\n", devId);
571 
572  mass_dev *mass_device = usb_mass_findDevice(devId, 0);
573 
574  /* only one device supported */
575  if ((mass_device != NULL) && (mass_device->status & USBMASS_DEV_STAT_CONN)) {
576  M_PRINTF("ERROR: Only one mass storage device allowed!\n");
577  return 0;
578  }
579 
580  /* get device descriptor */
581  device = (UsbDeviceDescriptor *)sceUsbdScanStaticDescriptor(devId, NULL, USB_DT_DEVICE);
582  if (device == NULL) {
583  M_DEBUG("ERROR: Couldn't get device descriptor\n");
584  return 0;
585  }
586 
587  /* Check if the device has at least one configuration */
588  if (device->bNumConfigurations < 1) {
589  return 0;
590  }
591 
592  /* read configuration */
593  config = (UsbConfigDescriptor *)sceUsbdScanStaticDescriptor(devId, device, USB_DT_CONFIG);
594  if (config == NULL) {
595  M_DEBUG("ERROR: Couldn't get configuration descriptor\n");
596  return 0;
597  }
598  /* check that at least one interface exists */
599  M_DEBUG("bNumInterfaces %d\n", config->bNumInterfaces);
600  if ((config->bNumInterfaces < 1) || (config->wTotalLength < (sizeof(UsbConfigDescriptor) + sizeof(UsbInterfaceDescriptor)))) {
601  M_DEBUG("ERROR: No interfaces available\n");
602  return 0;
603  }
604  /* get interface */
605  intf = (UsbInterfaceDescriptor *)((char *)config + config->bLength); /* Get first interface */
606  M_DEBUG("bInterfaceClass %X bInterfaceSubClass %X bInterfaceProtocol %X\n",
607  intf->bInterfaceClass, intf->bInterfaceSubClass, intf->bInterfaceProtocol);
608 
609  if ((intf->bInterfaceClass != USB_CLASS_MASS_STORAGE) || (intf->bInterfaceSubClass != USB_SUBCLASS_MASS_SCSI && intf->bInterfaceSubClass != USB_SUBCLASS_MASS_SFF_8070I) || (intf->bInterfaceProtocol != USB_PROTOCOL_MASS_BULK_ONLY) || (intf->bNumEndpoints < 2)) { // one bulk endpoint is not enough because
610  return 0; // we send the CBW to te bulk out endpoint
611  }
612 
613  return 1;
614 }
615 
616 static int usb_mass_connect(int devId)
617 {
618  int i;
619  int epCount;
620  UsbDeviceDescriptor *device;
621  UsbConfigDescriptor *config;
622  UsbInterfaceDescriptor *interface;
623  UsbEndpointDescriptor *endpoint;
624  iop_sema_t SemaData;
625  mass_dev *dev;
626 
627  M_PRINTF("connect: devId=%i\n", devId);
628  dev = usb_mass_findDevice(devId, 1);
629 
630  if (dev == NULL) {
631  M_PRINTF("ERROR: Unable to allocate space!\n");
632  return 1;
633  }
634 
635  /* only one mass device allowed */
636  if (dev->devId != -1) {
637  M_PRINTF("ERROR: Only one mass storage device allowed!\n");
638  return 1;
639  }
640 
641  dev->status = 0;
642  dev->bulkEpI = -1;
643  dev->bulkEpO = -1;
644 
645  /* open the config endpoint */
646  dev->controlEp = sceUsbdOpenPipe(devId, NULL);
647 
648  device = (UsbDeviceDescriptor *)sceUsbdScanStaticDescriptor(devId, NULL, USB_DT_DEVICE);
649 
650  config = (UsbConfigDescriptor *)sceUsbdScanStaticDescriptor(devId, device, USB_DT_CONFIG);
651 
652  interface = (UsbInterfaceDescriptor *)((char *)config + config->bLength); /* Get first interface */
653 
654  // store interface numbers
655  dev->interfaceNumber = interface->bInterfaceNumber;
656  dev->interfaceAlt = interface->bAlternateSetting;
657 
658  epCount = interface->bNumEndpoints;
659  endpoint = (UsbEndpointDescriptor *)sceUsbdScanStaticDescriptor(devId, NULL, USB_DT_ENDPOINT);
660  usb_bulk_probeEndpoint(devId, dev, endpoint);
661 
662  for (i = 1; i < epCount; i++) {
663  endpoint = (UsbEndpointDescriptor *)((char *)endpoint + endpoint->bLength);
664  usb_bulk_probeEndpoint(devId, dev, endpoint);
665  }
666 
667  // Bail out if we do NOT have enough bulk endpoints.
668  if (dev->bulkEpI < 0 || dev->bulkEpO < 0) {
669  usb_mass_release(dev);
670  M_PRINTF("ERROR: connect failed: not enough bulk endpoints!\n");
671  return -1;
672  }
673 
674  SemaData.initial = 0;
675  SemaData.max = 1;
676  SemaData.option = 0;
677  SemaData.attr = 0;
678  if ((dev->ioSema = CreateSema(&SemaData)) < 0) {
679  M_PRINTF("ERROR: Failed to allocate I/O semaphore\n");
680  return -1;
681  }
682 
683  /*store current configuration id - can't call set_configuration here */
684  dev->configId = config->bConfigurationValue;
685  dev->status = USBMASS_DEV_STAT_CONN;
686  // Set this last, with a memory barrier, in order to avoid a race condition in usb_mass_update with partially updated data
687  __asm__ __volatile__("" : : : "memory");
688  dev->devId = devId;
689  M_DEBUG("connect ok: epI=%i, epO=%i\n", dev->bulkEpI, dev->bulkEpO);
690 
691  SignalSema(usb_mass_update_sema);
692 
693  return 0;
694 }
695 
696 static void usb_mass_release(mass_dev *dev)
697 {
698  if (dev->bulkEpI >= 0)
699  sceUsbdClosePipe(dev->bulkEpI);
700 
701  if (dev->bulkEpO >= 0)
702  sceUsbdClosePipe(dev->bulkEpO);
703 
704  dev->bulkEpI = -1;
705  dev->bulkEpO = -1;
706  dev->controlEp = -1;
707  dev->status = 0;
708 }
709 
710 static int usb_mass_disconnect(int devId)
711 {
712  mass_dev *dev;
713  dev = usb_mass_findDevice(devId, 0);
714 
715  M_PRINTF("disconnect: devId=%i\n", devId);
716 
717  if (dev == NULL) {
718  M_PRINTF("ERROR: disconnect: no device storage!\n");
719  return 0;
720  }
721 
722  if (dev->status & USBMASS_DEV_STAT_CONN) {
723  usb_mass_release(dev);
724  dev->devId = -1;
725 
726  DeleteSema(dev->ioSema);
727 
728  // Should this move to the thread
729  // just like the scsi_connect?
730  scsi_disconnect(&dev->scsi);
731  }
732 
733  return 0;
734 }
735 
736 static void usb_mass_update(void *arg)
737 {
738  int i;
739 
740  (void)arg;
741 
742  M_DEBUG("update thread running\n");
743 
744  while (1) {
745  mass_dev *new_devs[NUM_DEVICES];
746  int new_devs_count;
747 
748  // Wait for event from USBD thread
749  WaitSema(usb_mass_update_sema);
750 
751  // Determine which devices are new and need to be connected.
752  {
753  new_devs_count = 0;
754  for (i = 0; i < NUM_DEVICES; i += 1) {
755  mass_dev *dev = &g_mass_device[i];
756  if (dev->devId != -1 && (dev->status & USBMASS_DEV_STAT_CONN) && !(dev->status & USBMASS_DEV_STAT_CONF)) {
757  new_devs[new_devs_count] = dev;
758  new_devs_count += 1;
759  }
760  }
761  }
762 
763  // Connect new devices
764  for (i = 0; i < new_devs_count; i += 1) {
765  mass_dev *dev = new_devs[i];
766  {
767  u8 path[16] = {0};
768  int ret;
769 
770  if ((ret = usb_set_configuration(dev, dev->configId)) != USB_RC_OK) {
771  M_PRINTF("ERROR: sending set_configuration %d\n", ret);
772  usb_mass_release(dev);
773  continue;
774  }
775 
776  if ((ret = usb_set_interface(dev, dev->interfaceNumber, dev->interfaceAlt)) != USB_RC_OK) {
777  M_PRINTF("ERROR: sending set_interface %d\n", ret);
778  if (ret == USB_RC_STALL) {
779  /* USB Specification 1.1, section 9.4.10: Devices that only support a default setting for the specified interface may return a STALL.
780  As with Linux, we shall clear the halt state of the interface's pipes and continue. */
781  usb_bulk_clear_halt(dev, USB_BLK_EP_IN);
782  usb_bulk_clear_halt(dev, USB_BLK_EP_OUT);
783  } else {
784  usb_mass_release(dev);
785  continue;
786  }
787  }
788 
789  sceUsbdGetDeviceLocation(dev->devId, path);
790  if (path[0] == 2)
791  dev->scsi.devNr = 0; // first USB port
792  else if (path[0] == 1)
793  dev->scsi.devNr = 1; // second USB port
794  else
795  dev->scsi.devNr = 2; // hub?
796 
797  dev->status |= USBMASS_DEV_STAT_CONF;
798  scsi_connect(&dev->scsi);
799 
800  // This is the same wait amount as done in fat_getData in usbhdfsd.
801  // This is a workaround to avoid incorrect initialization when attaching multiple drives at the same time.
802  DelayThread(5000);
803  }
804  }
805  }
806 }
807 
808 int usb_mass_init(void)
809 {
811  iop_sema_t sema;
812  int ret;
813  int i;
814 
815  M_DEBUG("%s\n", __func__);
816 
817  for (i = 0; i < NUM_DEVICES; ++i) {
818  g_mass_device[i].status = 0;
819  g_mass_device[i].devId = -1;
820 
821  g_mass_device[i].scsi.priv = &g_mass_device[i];
822  g_mass_device[i].scsi.name = "usb";
823  // The maximum number of sectors should be 0xffff but
824  // some usb drives seem to freeze above 128 sectors (64Kib)
825  g_mass_device[i].scsi.max_sectors = 128; // 0xffff
826  g_mass_device[i].scsi.get_max_lun = usb_bulk_get_max_lun;
827  g_mass_device[i].scsi.queue_cmd = usb_queue_cmd;
828  }
829 
830  sema.attr = 0;
831  sema.option = 0;
832  sema.initial = 0;
833  sema.max = 1;
834  usb_mass_update_sema = CreateSema(&sema);
835 
836  driver.next = NULL;
837  driver.prev = NULL;
838  driver.name = "mass-stor";
839  driver.probe = usb_mass_probe;
840  driver.connect = usb_mass_connect;
841  driver.disconnect = usb_mass_disconnect;
842 
843  ret = sceUsbdRegisterLdd(&driver);
844  M_DEBUG("sceUsbdRegisterLdd=%i\n", ret);
845  if (ret < 0) {
846  M_PRINTF("ERROR: register driver failed! ret=%d\n", ret);
847  return -1;
848  }
849 
850  thread.attr = TH_C;
851  thread.option = 0;
852  thread.thread = usb_mass_update;
853  thread.stacksize = 2 * 1024;
854  thread.priority = 0x10;
855 
856  ret = CreateThread(&thread);
857  if (ret < 0) {
858  M_PRINTF("ERROR: CreateThread failed! ret=%d\n", ret);
859  return -1;
860  }
861 
862  ret = StartThread(ret, 0);
863  if (ret < 0) {
864  M_PRINTF("ERROR: StartThread failed! ret=%d\n", ret);
865  DeleteThread(ret);
866  return -1;
867  }
868 
869  return 0;
870 }
UsbDeviceDescriptor
Definition: usbd.h:63
USB_RC_OK
#define USB_RC_OK
Definition: usbd.h:238
USBMASS_DEV_STAT_CONN
#define USBMASS_DEV_STAT_CONN
Definition: usbhdfsd-common.h:49
thbase.h
iop_sema_t
Definition: thsemap.h:38
sysclib.h
_mass_dev
Definition: usbhdfsd.h:64
_UsbDriver
Definition: usbd.h:47
thread
Definition: thcommon.h:143
_usb_callback_data
Definition: usb_driver.c:105
usbhdfsd-common.h
EIO
#define EIO
Definition: errno.h:29
USBMASS_DEV_STAT_ERR
#define USBMASS_DEV_STAT_ERR
Definition: usbhdfsd-common.h:53
UsbEndpointDescriptor
Definition: usbd.h:108
usbd.h
USBMASS_DEV_STAT_CONF
#define USBMASS_DEV_STAT_CONF
Definition: usbhdfsd-common.h:51
_cbw_packet
Definition: usb_driver.c:52
USB_RC_STALL
#define USB_RC_STALL
Definition: usbd.h:246
thsemap.h
usbd_macro.h
_csw_packet
Definition: usb_driver.c:63
stdio.h
_UsbDriver::name
char * name
Definition: usbd.h:51
_iop_thread
Definition: thbase.h:39
UsbConfigDescriptor
Definition: usbd.h:81
_usb_transfer_callback_data
Definition: usb_driver.c:114
usbmass_cmd
Definition: usb_mass.c:358
UsbInterfaceDescriptor
Definition: usbd.h:95
scsi_interface
Definition: scsi.h:4
errno.h