+/* clear a stall on an endpoint - special for BBB devices */
+int usb_stor_BBB_clear_endpt_stall(struct us_data *us, __u8 endpt)
+{
+ int result;
+
+ /* ENDPOINT_HALT = 0, so set value to 0 */
+ result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
+ USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT,
+ 0, endpt, 0, 0, USB_CNTL_TIMEOUT*5);
+ return result;
+}
+
+int usb_stor_BBB_transport(ccb *srb, struct us_data *us)
+{
+ int result, retry;
+ int dir_in;
+ int actlen, data_actlen;
+ unsigned int pipe, pipein, pipeout;
+ umass_bbb_csw_t csw;
+#ifdef BBB_XPORT_TRACE
+ unsigned char *ptr;
+ int index;
+#endif
+
+ dir_in = US_DIRECTION(srb->cmd[0]);
+
+ /* COMMAND phase */
+ USB_STOR_PRINTF("COMMAND phase\n");
+ result = usb_stor_BBB_comdat(srb, us);
+ if (result < 0) {
+ USB_STOR_PRINTF("failed to send CBW status %ld\n",
+ us->pusb_dev->status);
+ usb_stor_BBB_reset(us);
+ return USB_STOR_TRANSPORT_FAILED;
+ }
+ wait_ms(5);
+ pipein = usb_rcvbulkpipe(us->pusb_dev, us->ep_in);
+ pipeout = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
+ /* DATA phase + error handling */
+ data_actlen = 0;
+ /* no data, go immediately to the STATUS phase */
+ if (srb->datalen == 0)
+ goto st;
+ USB_STOR_PRINTF("DATA phase\n");
+ if (dir_in)
+ pipe = pipein;
+ else
+ pipe = pipeout;
+ result = usb_bulk_msg(us->pusb_dev, pipe, srb->pdata, srb->datalen, &data_actlen, USB_CNTL_TIMEOUT*5);
+ /* special handling of STALL in DATA phase */
+ if((result < 0) && (us->pusb_dev->status & USB_ST_STALLED)) {
+ USB_STOR_PRINTF("DATA:stall\n");
+ /* clear the STALL on the endpoint */
+ result = usb_stor_BBB_clear_endpt_stall(us, dir_in? us->ep_in : us->ep_out);
+ if (result >= 0)
+ /* continue on to STATUS phase */
+ goto st;
+ }
+ if (result < 0) {
+ USB_STOR_PRINTF("usb_bulk_msg error status %ld\n",
+ us->pusb_dev->status);
+ usb_stor_BBB_reset(us);
+ return USB_STOR_TRANSPORT_FAILED;
+ }
+#ifdef BBB_XPORT_TRACE
+ for (index = 0; index < data_actlen; index++)
+ printf("pdata[%d] %#x ", index, srb->pdata[index]);
+ printf("\n");
+#endif
+ /* STATUS phase + error handling */
+ st:
+ retry = 0;
+ again:
+ USB_STOR_PRINTF("STATUS phase\n");
+ result = usb_bulk_msg(us->pusb_dev, pipein, &csw, UMASS_BBB_CSW_SIZE,
+ &actlen, USB_CNTL_TIMEOUT*5);
+
+ /* special handling of STALL in STATUS phase */
+ if((result < 0) && (retry < 1) && (us->pusb_dev->status & USB_ST_STALLED)) {
+ USB_STOR_PRINTF("STATUS:stall\n");
+ /* clear the STALL on the endpoint */
+ result = usb_stor_BBB_clear_endpt_stall(us, us->ep_in);
+ if (result >= 0 && (retry++ < 1))
+ /* do a retry */
+ goto again;
+ }
+ if (result < 0) {
+ USB_STOR_PRINTF("usb_bulk_msg error status %ld\n",
+ us->pusb_dev->status);
+ usb_stor_BBB_reset(us);
+ return USB_STOR_TRANSPORT_FAILED;
+ }
+#ifdef BBB_XPORT_TRACE
+ ptr = (unsigned char *)&csw;
+ for (index = 0; index < UMASS_BBB_CSW_SIZE; index++)
+ printf("ptr[%d] %#x ", index, ptr[index]);
+ printf("\n");
+#endif
+ /* misuse pipe to get the residue */
+ pipe = swap_32(csw.dCSWDataResidue);
+ if (pipe == 0 && srb->datalen != 0 && srb->datalen - data_actlen != 0)
+ pipe = srb->datalen - data_actlen;
+ if (CSWSIGNATURE != swap_32(csw.dCSWSignature)) {
+ USB_STOR_PRINTF("!CSWSIGNATURE\n");
+ usb_stor_BBB_reset(us);
+ return USB_STOR_TRANSPORT_FAILED;
+ } else if ((CBWTag - 1) != swap_32(csw.dCSWTag)) {
+ USB_STOR_PRINTF("!Tag\n");
+ usb_stor_BBB_reset(us);
+ return USB_STOR_TRANSPORT_FAILED;
+ } else if (csw.bCSWStatus > CSWSTATUS_PHASE) {
+ USB_STOR_PRINTF(">PHASE\n");
+ usb_stor_BBB_reset(us);
+ return USB_STOR_TRANSPORT_FAILED;
+ } else if (csw.bCSWStatus == CSWSTATUS_PHASE) {
+ USB_STOR_PRINTF("=PHASE\n");
+ usb_stor_BBB_reset(us);
+ return USB_STOR_TRANSPORT_FAILED;
+ } else if (data_actlen > srb->datalen) {
+ USB_STOR_PRINTF("transferred %dB instead of %dB\n",
+ data_actlen, srb->datalen);
+ return USB_STOR_TRANSPORT_FAILED;
+ } else if (csw.bCSWStatus == CSWSTATUS_FAILED) {
+ USB_STOR_PRINTF("FAILED\n");
+ return USB_STOR_TRANSPORT_FAILED;
+ }
+
+ return result;
+}
+