/* * This issues a Bulk-only Reset to the device in question, including * clearing the subsequent endpoint halts that may occur. */ int UMAS_BulkReset(UMAS_DATA_T *umas) { int result; UMAS_VDEBUG("Bulk reset requested\n"); printf("UMAS_BulkReset!\n"); /* if the device was removed, then we're already reset */ if(!umas->pusb_dev) return SUCCESS; result = USBH_SendCtrlMsg(umas->pusb_dev, usb_sndctrlpipe(umas->pusb_dev, 0), UMAS_BULK_RESET_REQUEST, USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, umas->ifnum, NULL, 0, 0); if(result < 0) { UMAS_DEBUG("Bulk soft reset failed %d\n", result); return FAILED; } clear_halt(umas->pusb_dev, usb_rcvbulkpipe(umas->pusb_dev, umas->ep_in)); clear_halt(umas->pusb_dev, usb_sndbulkpipe(umas->pusb_dev, umas->ep_out)); UMAS_VDEBUG("Bulk soft reset completed\n"); return SUCCESS; }
/* * This issues a CB[I] Reset to the device in question */ int UMAS_CbReset(UMAS_DATA_T *umas) { uint8_t cmd[12]; int result; /* if the device was removed, then we're already reset */ if(!umas->pusb_dev) return SUCCESS; memset(cmd, 0xFF, sizeof(cmd)); cmd[0] = SEND_DIAGNOSTIC; cmd[1] = 4; result = USBH_SendCtrlMsg(umas->pusb_dev, usb_sndctrlpipe(umas->pusb_dev, 0), US_CBI_ADSC, USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, umas->ifnum, cmd, sizeof(cmd), 0); if(result < 0) { UMAS_DEBUG("UMAS_CbReset - CB[I] soft reset failed %d\n", result); return FAILED; } UMAS_VDEBUG("UMAS_CbReset - clearing endpoint halt\n"); clear_halt(umas->pusb_dev, usb_rcvbulkpipe(umas->pusb_dev, umas->ep_in)); clear_halt(umas->pusb_dev, usb_rcvbulkpipe(umas->pusb_dev, umas->ep_out)); UMAS_VDEBUG("UMAS_CbReset - done\n"); /* return a result code based on the result of the control message */ return SUCCESS; }
/* Determine what the maximum LUN supported is */ int UMAS_BulkMaxLun(UMAS_DATA_T *umas) { uint8_t data; int result; int pipe; uint8_t dma_buff[4]; /* issue the command */ pipe = usb_rcvctrlpipe(umas->pusb_dev, 0); result = USBH_SendCtrlMsg(umas->pusb_dev, pipe, UMAS_BULK_GET_MAX_LUN, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, umas->ifnum, dma_buff, 1, 0); data = dma_buff[0]; UMAS_DEBUG("GetMaxLUN command result is %d, data is %d\n", result, data); /* if we have a successful request, return the result */ if(result == 1) return data; /* if we get a STALL, clear the stall */ if(result == USB_ERR_PIPE) { UMAS_DEBUG("clearing endpoint halt for pipe 0x%x\n", pipe); clear_halt(umas->pusb_dev, pipe); } /* return the default -- no LUNs */ return 0; }
/* * Transfer one SCSI scatter-gather buffer via bulk transfer * * Note that this function is necessary because we want the ability to * use scatter-gather memory. Good performance is achieved by a combination * of scatter-gather and clustering (which makes each chunk bigger). * * Note that the lower layer will always retry when a NAK occurs, up to the * timeout limit. Thus we don't have to worry about it for individual * packets. */ static int usb_stor_transfer_partial(UMAS_DATA_T *umas, char *buf, int length) { int result; uint32_t partial; int pipe; /* calculate the appropriate pipe information */ if(umas->srb.sc_data_direction == SCSI_DATA_READ) pipe = usb_rcvbulkpipe(umas->pusb_dev, umas->ep_in); else pipe = usb_sndbulkpipe(umas->pusb_dev, umas->ep_out); /* transfer the data */ UMAS_VDEBUG("usb_stor_transfer_partial - xfer %d bytes\n", length); result = usb_stor_bulk_msg(umas, buf, pipe, length, &partial); UMAS_VDEBUG("usb_stor_bulk_msg() returned %d xferred %d/%d\n", result, partial, length); //UMAS_DEBUG("usb_stor_bulk_msg() returned %d xferred %d/%d\n", result, partial, length); /* if we stall, we need to clear it before we go on */ if(result == USB_ERR_PIPE) { UMAS_DEBUG("usb_stor_transfer_partial - clearing endpoint halt for pipe 0x%x\n", pipe); clear_halt(umas->pusb_dev, pipe); } /* did we send all the data? */ if(partial == length) { UMAS_VDEBUG("usb_stor_transfer_partial - transfer complete\n"); return UMAS_BULK_TRANSFER_GOOD; } /* uh oh... we have an error code, so something went wrong. */ if(result) { /* NAK - that means we've retried a few times already */ if(result == USB_ERR_TIMEOUT) { UMAS_DEBUG("usb_stor_transfer_partial - device NAKed\n"); return UMAS_BULK_TRANSFER_FAILED; } /* USB_ERR_NOENT -- we canceled this transfer */ if(result == USB_ERR_NOENT) { UMAS_DEBUG("usb_stor_transfer_partial - transfer aborted\n"); return UMAS_BULK_TRANSFER_ABORTED; } /* the catch-all case */ UMAS_DEBUG("usb_stor_transfer_partial - unknown error\n"); return UMAS_BULK_TRANSFER_FAILED; } /* no error code, so we must have transferred some data, just not all of it */ return UMAS_BULK_TRANSFER_SHORT; }
/* * Control/Bulk transport */ int UMAS_CbTransport(SCSI_CMD_T *srb, UMAS_DATA_T *umas) { int result; /* COMMAND STAGE */ /* let's send the command via the control pipe */ result = usb_stor_control_msg(umas, usb_sndctrlpipe(umas->pusb_dev, 0), US_CBI_ADSC, USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, umas->ifnum, srb->cmnd, srb->cmd_len); /* check the return code for the command */ UMAS_VDEBUG("UMAS_CbTransport - Call to usb_stor_control_msg() returned %d\n", result); if(result < 0) { /* if the command was aborted, indicate that */ if(result == USB_ERR_NOENT) return USB_STOR_TRANSPORT_ABORTED; /* a stall is a fatal condition from the device */ if(result == USB_ERR_PIPE) { UMAS_DEBUG("UMAS_CbTransport - Stall on control pipe. Clearing\n"); result = clear_halt(umas->pusb_dev, usb_sndctrlpipe(umas->pusb_dev, 0)); UMAS_DEBUG("UMAS_CbTransport - clear_halt() returns %d\n", result); return USB_STOR_TRANSPORT_FAILED; } /* Uh oh... serious problem here */ return USB_STOR_TRANSPORT_ERROR; } /* DATA STAGE */ /* transfer the data payload for this command, if one exists */ if(usb_stor_transfer_length(srb)) { us_transfer(srb, umas); UMAS_VDEBUG("UMAS_CbTransport - CB data stage result is 0x%x\n", srb->result); /* if it was aborted, we need to indicate that */ if(srb->result == USB_STOR_TRANSPORT_ABORTED) return USB_STOR_TRANSPORT_ABORTED; } /* STATUS STAGE */ /* NOTE: CB does not have a status stage. Silly, I know. So * we have to catch this at a higher level. */ return USB_STOR_TRANSPORT_GOOD; }
int UMAS_CbiTransport(SCSI_CMD_T *srb, UMAS_DATA_T *umas) { int result; /* Set up for status notification */ umas->ip_wanted = 1; /* re-initialize the mutex so that we avoid any races with * early/late IRQs from previous commands */ /* COMMAND STAGE */ /* let's send the command via the control pipe */ result = usb_stor_control_msg(umas, usb_sndctrlpipe(umas->pusb_dev, 0), US_CBI_ADSC, USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, umas->ifnum, srb->cmnd, srb->cmd_len); /* check the return code for the command */ UMAS_VDEBUG("Call to usb_stor_control_msg() returned %d\n", result); if(result < 0) { /* if the command was aborted, indicate that */ if(result == USB_ERR_NOENT) return USB_STOR_TRANSPORT_ABORTED; /* STALL must be cleared when they are detected */ if(result == USB_ERR_PIPE) { UMAS_VDEBUG("-- Stall on control pipe. Clearing\n"); result = clear_halt(umas->pusb_dev, usb_sndctrlpipe(umas->pusb_dev, 0)); UMAS_VDEBUG("-- clear_halt() returns %d\n", result); return USB_STOR_TRANSPORT_FAILED; } /* Uh oh... serious problem here */ return USB_STOR_TRANSPORT_ERROR; } /* DATA STAGE */ /* transfer the data payload for this command, if one exists*/ if(usb_stor_transfer_length(srb)) { us_transfer(srb, umas); UMAS_VDEBUG("CBI data stage result is 0x%x\n", srb->result); /* if it was aborted, we need to indicate that */ if(srb->result == USB_STOR_TRANSPORT_ABORTED) return USB_STOR_TRANSPORT_ABORTED; } /* STATUS STAGE */ //UMAS_VDEBUG("Current value of ip_waitq is: %d\n", umas->ip_waitq.count); /* if we were woken up by an abort instead of the actual interrupt */ if(umas->ip_wanted) { UMAS_DEBUG("Did not get interrupt on CBI\n"); umas->ip_wanted = 0; return USB_STOR_TRANSPORT_ABORTED; } UMAS_VDEBUG("Got interrupt data (0x%x, 0x%x)\n", umas->irqdata[0], umas->irqdata[1]); /* * UFI gives umas ASC and ASCQ, like a request sense * * REQUEST_SENSE and INQUIRY don't affect the sense data on UFI * devices, so we ignore the information for those commands. Note * that this means we could be ignoring a real error on these * commands, but that can't be helped. */ if(umas->subclass == UMAS_SC_UFI) { if((srb->cmnd[0] == REQUEST_SENSE) || (srb->cmnd[0] == INQUIRY)) return USB_STOR_TRANSPORT_GOOD; else { if(((uint8_t*)umas->irq_urb->transfer_buffer)[0]) return USB_STOR_TRANSPORT_FAILED; else return USB_STOR_TRANSPORT_GOOD; } } /* * If not UFI, we interpret the data as a result code * The first byte should always be a 0x0 * The second byte & 0x0F should be 0x0 for good, otherwise error */ if(umas->irqdata[0]) { UMAS_VDEBUG("CBI IRQ data showed reserved bType %d\n", umas->irqdata[0]); return USB_STOR_TRANSPORT_ERROR; } switch(umas->irqdata[1] & 0x0F) { case 0x00: return USB_STOR_TRANSPORT_GOOD; case 0x01: return USB_STOR_TRANSPORT_FAILED; default: return USB_STOR_TRANSPORT_ERROR; } }
int UMAS_BulkTransport(SCSI_CMD_T *srb, UMAS_DATA_T *umas) { int result, status = 0; int pipe; uint32_t partial; struct bulk_cb_wrap bcb; struct bulk_cs_wrap bcs; /* if the device was removed, then we're already reset */ if(!umas->pusb_dev) return SUCCESS; /* set up the command wrapper */ bcb.Signature = UMAS_BULK_CB_SIGN; bcb.DataTransferLength = usb_stor_transfer_length(srb); bcb.Flags = srb->sc_data_direction == SCSI_DATA_READ ? 1 << 7 : 0; bcb.Tag = srb->serial_number; bcb.Lun = srb->cmnd[1] >> 5; if(umas->flags & UMAS_FL_SCM_MULT_TARG) bcb.Lun |= srb->target << 4; bcb.Length = srb->cmd_len; /* construct the pipe handle */ pipe = usb_sndbulkpipe(umas->pusb_dev, umas->ep_out); /* copy the command payload */ memset(bcb.CDB, 0, sizeof(bcb.CDB)); memcpy(bcb.CDB, srb->cmnd, bcb.Length); /* send it to out endpoint */ UMAS_VDEBUG("Bulk command S 0x%x T 0x%x Trg %d LUN %d L %d F %d CL %d\n", bcb.Signature, bcb.Tag, (bcb.Lun >> 4), (bcb.Lun & 0x0F), bcb.DataTransferLength, bcb.Flags, bcb.Length); result = usb_stor_bulk_msg(umas, &bcb, pipe, UMAS_BULK_CB_WRAP_LEN, &partial); UMAS_VDEBUG("Bulk command transfer result=%d\n", result); /* if the command was aborted, indicate that */ if(result == USB_ERR_NOENT) return USB_STOR_TRANSPORT_ABORTED; /* if we stall, we need to clear it before we go on */ if(result == USB_ERR_PIPE) { UMAS_DEBUG("UMAS_BulkTransport - 1 clearing endpoint halt for pipe 0x%x\n", pipe); clear_halt(umas->pusb_dev, pipe); } else if(result) { /* unknown error -- we've got a problem */ status = USB_STOR_TRANSPORT_ERROR; goto bulk_error; } /* if the command transferred well, then we go to the data stage */ if(result == 0) { /* send/receive data payload, if there is any */ if(bcb.DataTransferLength) { us_transfer(srb, umas); /* if it was aborted, we need to indicate that */ if(srb->result == USB_STOR_TRANSPORT_ABORTED) { status = USB_STOR_TRANSPORT_ABORTED; goto bulk_error; } } } /* * See flow chart on pg 15 of the Bulk Only Transport spec for * an explanation of how this code works. */ /* construct the pipe handle */ pipe = usb_rcvbulkpipe(umas->pusb_dev, umas->ep_in); /* get CSW for device status */ UMAS_VDEBUG("Attempting to get CSW...\n"); result = usb_stor_bulk_msg(umas, (char *)&bcs, pipe, UMAS_BULK_CS_WRAP_LEN, &partial); /* if the command was aborted, indicate that */ if(result == USB_ERR_NOENT) { status = USB_STOR_TRANSPORT_ABORTED; goto bulk_error; } /* did the attempt to read the CSW fail? */ if(result == USB_ERR_PIPE) { UMAS_DEBUG("get CSW failed - clearing endpoint halt for pipe 0x%x\n", pipe); clear_halt(umas->pusb_dev, pipe); /* get the status again */ UMAS_DEBUG("Attempting to get CSW (2nd try)...\n"); result = usb_stor_bulk_msg(umas, &bcs, pipe, UMAS_BULK_CS_WRAP_LEN, &partial); /* if the command was aborted, indicate that */ if(result == USB_ERR_NOENT) { UMAS_DEBUG("get CSW Command was aborted!\n"); status = USB_STOR_TRANSPORT_ABORTED; goto bulk_error; } /* if it fails again, we need a reset and return an error*/ if(result == USB_ERR_PIPE) { UMAS_DEBUG("get CSW command 2nd try failed - clearing halt for pipe 0x%x\n", pipe); clear_halt(umas->pusb_dev, pipe); status = USB_STOR_TRANSPORT_ERROR; goto bulk_error; } } /* if we still have a failure at this point, we're in trouble */ UMAS_VDEBUG("Bulk status result = %d\n", result); if(result) { status = USB_STOR_TRANSPORT_ERROR; goto bulk_error; } /* check bulk status */ UMAS_VDEBUG("Bulk status Sig 0x%x T 0x%x R %d Stat 0x%x\n", USB_SWAP32(bcs.Signature), bcs.Tag, bcs.Residue, bcs.Status); if((bcs.Signature != UMAS_BULK_CS_SIGN) || (bcs.Tag != bcb.Tag) || (bcs.Status > UMAS_BULK_STAT_PHASE) || (partial != 13)) { UMAS_DEBUG("Bulk status Sig 0x%x T 0x%x R %d Stat 0x%x\n", USB_SWAP32(bcs.Signature), bcs.Tag, bcs.Residue, bcs.Status); status = USB_STOR_TRANSPORT_ERROR; goto bulk_error; } /* based on the status code, we report good or bad */ switch(bcs.Status) { case UMAS_BULK_STAT_OK: /* command good -- note that data could be short */ return USB_STOR_TRANSPORT_GOOD; case UMAS_BULK_STAT_FAIL: /* command failed */ status = USB_STOR_TRANSPORT_FAILED; goto bulk_error; case UMAS_BULK_STAT_PHASE: /* phase error -- note that a transport reset will be * invoked by the invoke_transport() function */ status = USB_STOR_TRANSPORT_ERROR; goto bulk_error; } /* we should never get here, but if we do, we're in trouble */ bulk_error: return status; }
static int usb_host_handle_data(USBHostDevice *s, USBPacket *p) { struct usbdevfs_urb *urb; AsyncURB *aurb; int ret; aurb = async_alloc(); if (!aurb) { dprintf("husb: async malloc failed\n"); return USB_RET_NAK; } aurb->hdev = s; aurb->packet = p; urb = &aurb->urb; if (p->pid == USB_TOKEN_IN) urb->endpoint = p->devep | 0x80; else urb->endpoint = p->devep; if (is_halted(s, p->devep)) { ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &urb->endpoint); if (ret < 0) { dprintf("husb: failed to clear halt. ep 0x%x errno %d\n", urb->endpoint, errno); return USB_RET_NAK; } clear_halt(s, p->devep); } urb->buffer = p->data; urb->buffer_length = p->len; if (is_isoc(s, p->devep)) { /* Setup ISOC transfer */ urb->type = USBDEVFS_URB_TYPE_ISO; urb->flags = USBDEVFS_URB_ISO_ASAP; urb->number_of_packets = 1; urb->iso_frame_desc[0].length = p->len; } else { /* Setup bulk transfer */ urb->type = USBDEVFS_URB_TYPE_BULK; } urb->usercontext = s; ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb); dprintf("husb: data submit. ep 0x%x len %u aurb %p\n", urb->endpoint, p->len, aurb); if (ret < 0) { dprintf("husb: submit failed. errno %d\n", errno); async_free(aurb); switch(errno) { case ETIMEDOUT: return USB_RET_NAK; case EPIPE: default: return USB_RET_STALL; } } usb_defer_packet(p, async_cancel, aurb); return USB_RET_ASYNC; }