/* * Transfer an entire SCSI command's worth of data payload over the bulk * pipe. * * Note that this uses usb_stor_transfer_partial to achieve its goals -- this * function simply determines if we're going to use scatter-gather or not, * and acts appropriately. For now, it also re-interprets the error codes. */ void usb_stor_transfer(Scsi_Cmnd *srb, struct us_data* us) { int i; int result = -1; struct scatterlist *sg; unsigned int total_transferred = 0; unsigned int transfer_amount; /* calculate how much we want to transfer */ transfer_amount = usb_stor_transfer_length(srb); /* was someone foolish enough to request more data than available * buffer space? */ if (transfer_amount > srb->request_bufflen) transfer_amount = srb->request_bufflen; /* are we scatter-gathering? */ if (srb->use_sg) { /* loop over all the scatter gather structures and * make the appropriate requests for each, until done */ sg = (struct scatterlist *) srb->request_buffer; for (i = 0; i < srb->use_sg; i++) { if (transfer_amount - total_transferred >= sg[i].length) { result = usb_stor_transfer_partial(us, sg[i].address, sg[i].length); total_transferred += sg[i].length; } else result = usb_stor_transfer_partial(us, sg[i].address, transfer_amount - total_transferred); /* if we get an error, end the loop here */ if (result) break; } } else /* no scatter-gather, just make the request */ result = usb_stor_transfer_partial(us, srb->request_buffer, transfer_amount); /* return the result in the data structure itself */ srb->result = result; }
/* * Control/Bulk transport */ int usb_stor_CB_transport(Scsi_Cmnd *srb, struct us_data *us) { int result; /* COMMAND STAGE */ /* let's send the command via the control pipe */ result = usb_stor_control_msg(us, usb_sndctrlpipe(us->pusb_dev,0), US_CBI_ADSC, USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, us->ifnum, srb->cmnd, srb->cmd_len); /* check the return code for the command */ US_DEBUGP("Call to usb_stor_control_msg() returned %d\n", result); if (result < 0) { /* if the command was aborted, indicate that */ if (result == -ECONNRESET) return USB_STOR_TRANSPORT_ABORTED; /* a stall is a fatal condition from the device */ if (result == -EPIPE) { US_DEBUGP("-- Stall on control pipe. Clearing\n"); result = usb_stor_clear_halt(us, usb_sndctrlpipe(us->pusb_dev, 0)); /* if the command was aborted, indicate that */ if (result == -ECONNRESET) return USB_STOR_TRANSPORT_ABORTED; 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)) { usb_stor_transfer(srb, us); result = srb->result; US_DEBUGP("CB data stage result is 0x%x\n", result); /* report any errors */ if (result == US_BULK_TRANSFER_ABORTED) { return USB_STOR_TRANSPORT_ABORTED; } if (result == US_BULK_TRANSFER_FAILED) { return USB_STOR_TRANSPORT_FAILED; } } /* 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; }
/* * 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; }
/* * Transfer an entire SCSI command's worth of data payload over the bulk pipe. * * Note that this uses usb_stor_transfer_partial to achieve it's goals -- this * function simply determines if we're going to use scatter-gather or not, * and acts appropriately. For now, it also re-interprets the error codes. */ static void us_transfer(SCSI_CMD_T *srb, UMAS_DATA_T* umas) { int i; int result = -1; uint32_t total_transferred = 0; uint32_t transfer_amount; SCATTER_LIST_T *sg; /* calculate how much we want to transfer */ transfer_amount = usb_stor_transfer_length(srb); /* Was someone foolish enough to request more data than available buffer space? */ if(transfer_amount > srb->request_bufflen) transfer_amount = srb->request_bufflen; /* are we scatter-gathering? */ if(srb->use_sg) { /* * loop over all the scatter gather structures and * make the appropriate requests for each, until done */ sg = (SCATTER_LIST_T *) srb->request_buff; for(i = 0; i < srb->use_sg; i++) { /* transfer the lesser of the next buffer or the remaining data */ if(transfer_amount - total_transferred >= sg[i].length) { result = usb_stor_transfer_partial(umas, sg[i].address, sg[i].length); total_transferred += sg[i].length; } else result = usb_stor_transfer_partial(umas, sg[i].address, transfer_amount - total_transferred); /* if we get an error, end the loop here */ if(result) break; } } else /* no scatter-gather, just make the request */ result = usb_stor_transfer_partial(umas, (char *)srb->request_buff, transfer_amount); /* return the result in the data structure itself */ srb->result = result; }
int usb_stor_CBI_transport(Scsi_Cmnd *srb, struct us_data *us) { int result; /* Set up for status notification */ atomic_set(us->ip_wanted, 1); /* re-initialize the mutex so that we avoid any races with * early/late IRQs from previous commands */ init_MUTEX_LOCKED(&(us->ip_waitq)); /* COMMAND STAGE */ /* let's send the command via the control pipe */ result = usb_stor_control_msg(us, usb_sndctrlpipe(us->pusb_dev,0), US_CBI_ADSC, USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, us->ifnum, srb->cmnd, srb->cmd_len); /* check the return code for the command */ US_DEBUGP("Call to usb_stor_control_msg() returned %d\n", result); if (result < 0) { /* Reset flag for status notification */ atomic_set(us->ip_wanted, 0); } /* if the command was aborted, indicate that */ if (result == -ECONNRESET) return USB_STOR_TRANSPORT_ABORTED; /* STALL must be cleared when it is detected */ if (result == -EPIPE) { US_DEBUGP("-- Stall on control pipe. Clearing\n"); result = usb_stor_clear_halt(us, usb_sndctrlpipe(us->pusb_dev, 0)); /* if the command was aborted, indicate that */ if (result == -ECONNRESET) return USB_STOR_TRANSPORT_ABORTED; return USB_STOR_TRANSPORT_FAILED; } if (result < 0) { /* 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)) { usb_stor_transfer(srb, us); result = srb->result; US_DEBUGP("CBI data stage result is 0x%x\n", result); /* report any errors */ if (result == US_BULK_TRANSFER_ABORTED) { atomic_set(us->ip_wanted, 0); return USB_STOR_TRANSPORT_ABORTED; } if (result == US_BULK_TRANSFER_FAILED) { atomic_set(us->ip_wanted, 0); return USB_STOR_TRANSPORT_FAILED; } } /* STATUS STAGE */ /* go to sleep until we get this interrupt */ US_DEBUGP("Current value of ip_waitq is: %d\n", atomic_read(&us->ip_waitq.count)); down(&(us->ip_waitq)); /* if we were woken up by an abort instead of the actual interrupt */ if (atomic_read(us->ip_wanted)) { US_DEBUGP("Did not get interrupt on CBI\n"); atomic_set(us->ip_wanted, 0); return USB_STOR_TRANSPORT_ABORTED; } US_DEBUGP("Got interrupt data (0x%x, 0x%x)\n", us->irqdata[0], us->irqdata[1]); /* UFI gives us 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 (us->subclass == US_SC_UFI) { if (srb->cmnd[0] == REQUEST_SENSE || srb->cmnd[0] == INQUIRY) return USB_STOR_TRANSPORT_GOOD; else if (((unsigned char*)us->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 (us->irqdata[0]) { US_DEBUGP("CBI IRQ data showed reserved bType %d\n", us->irqdata[0]); return USB_STOR_TRANSPORT_ERROR; } switch (us->irqdata[1] & 0x0F) { case 0x00: return USB_STOR_TRANSPORT_GOOD; case 0x01: return USB_STOR_TRANSPORT_FAILED; default: return USB_STOR_TRANSPORT_ERROR; } /* we should never get here, but if we do, we're in trouble */ return USB_STOR_TRANSPORT_ERROR; }
int usb_stor_Bulk_transport(Scsi_Cmnd *srb, struct us_data *us) { struct bulk_cb_wrap *bcb; struct bulk_cs_wrap *bcs; int result; int pipe; int partial; int ret = USB_STOR_TRANSPORT_ERROR; bcb = kmalloc(sizeof *bcb, in_interrupt() ? GFP_ATOMIC : GFP_NOIO); if (!bcb) { return USB_STOR_TRANSPORT_ERROR; } bcs = kmalloc(sizeof *bcs, in_interrupt() ? GFP_ATOMIC : GFP_NOIO); if (!bcs) { kfree(bcb); return USB_STOR_TRANSPORT_ERROR; } /* set up the command wrapper */ bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = cpu_to_le32(usb_stor_transfer_length(srb)); bcb->Flags = srb->sc_data_direction == SCSI_DATA_READ ? 1 << 7 : 0; bcb->Tag = ++(us->tag); bcb->Lun = srb->cmnd[1] >> 5; if (us->flags & US_FL_SCM_MULT_TARG) bcb->Lun |= srb->target << 4; bcb->Length = srb->cmd_len; /* construct the pipe handle */ pipe = usb_sndbulkpipe(us->pusb_dev, us->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 */ US_DEBUGP("Bulk command S 0x%x T 0x%x Trg %d LUN %d L %d F %d CL %d\n", le32_to_cpu(bcb->Signature), bcb->Tag, (bcb->Lun >> 4), (bcb->Lun & 0x0F), bcb->DataTransferLength, bcb->Flags, bcb->Length); result = usb_stor_bulk_msg(us, bcb, pipe, US_BULK_CB_WRAP_LEN, &partial); US_DEBUGP("Bulk command transfer result=%d\n", result); /* if the command was aborted, indicate that */ if (result == -ECONNRESET) { ret = USB_STOR_TRANSPORT_ABORTED; goto out; } /* if we stall, we need to clear it before we go on */ if (result == -EPIPE) { US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe); result = usb_stor_clear_halt(us, pipe); /* if the command was aborted, indicate that */ if (result == -ECONNRESET) { ret = USB_STOR_TRANSPORT_ABORTED; goto out; } result = -EPIPE; } else if (result) { /* unknown error -- we've got a problem */ ret = USB_STOR_TRANSPORT_ERROR; goto out; } /* if the command transfered well, then we go to the data stage */ if (result == 0) { /* send/receive data payload, if there is any */ if (bcb->DataTransferLength) { usb_stor_transfer(srb, us); result = srb->result; US_DEBUGP("Bulk data transfer result 0x%x\n", result); /* if it was aborted, we need to indicate that */ if (result == US_BULK_TRANSFER_ABORTED) { ret = USB_STOR_TRANSPORT_ABORTED; goto out; } } } /* 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(us->pusb_dev, us->ep_in); /* get CSW for device status */ US_DEBUGP("Attempting to get CSW...\n"); result = usb_stor_bulk_msg(us, bcs, pipe, US_BULK_CS_WRAP_LEN, &partial); /* if the command was aborted, indicate that */ if (result == -ECONNRESET) { ret = USB_STOR_TRANSPORT_ABORTED; goto out; } /* did the attempt to read the CSW fail? */ if (result == -EPIPE) { US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe); result = usb_stor_clear_halt(us, pipe); /* if the command was aborted, indicate that */ if (result == -ECONNRESET) { ret = USB_STOR_TRANSPORT_ABORTED; goto out; } /* get the status again */ US_DEBUGP("Attempting to get CSW (2nd try)...\n"); result = usb_stor_bulk_msg(us, bcs, pipe, US_BULK_CS_WRAP_LEN, &partial); /* if the command was aborted, indicate that */ if (result == -ECONNRESET) { ret = USB_STOR_TRANSPORT_ABORTED; goto out; } /* if it fails again, we need a reset and return an error*/ if (result == -EPIPE) { US_DEBUGP("clearing halt for pipe 0x%x\n", pipe); result = usb_stor_clear_halt(us, pipe); /* if the command was aborted, indicate that */ if (result == -ECONNRESET) { ret = USB_STOR_TRANSPORT_ABORTED; } else { ret = USB_STOR_TRANSPORT_ERROR; } goto out; } } /* if we still have a failure at this point, we're in trouble */ US_DEBUGP("Bulk status result = %d\n", result); if (result) { ret = USB_STOR_TRANSPORT_ERROR; goto out; } /* check bulk status */ US_DEBUGP("Bulk status Sig 0x%x T 0x%x R %d Stat 0x%x\n", le32_to_cpu(bcs->Signature), bcs->Tag, bcs->Residue, bcs->Status); if (bcs->Signature != cpu_to_le32(US_BULK_CS_SIGN) || bcs->Tag != bcb->Tag || bcs->Status > US_BULK_STAT_PHASE || partial != 13) { US_DEBUGP("Bulk logical error\n"); ret = USB_STOR_TRANSPORT_ERROR; goto out; } /* based on the status code, we report good or bad */ switch (bcs->Status) { case US_BULK_STAT_OK: /* command good -- note that data could be short */ ret = USB_STOR_TRANSPORT_GOOD; goto out; case US_BULK_STAT_FAIL: /* command failed */ ret = USB_STOR_TRANSPORT_FAILED; goto out; case US_BULK_STAT_PHASE: /* phase error -- note that a transport reset will be * invoked by the invoke_transport() function */ ret = USB_STOR_TRANSPORT_ERROR; goto out; } /* we should never get here, but if we do, we're in trouble */ out: kfree(bcb); kfree(bcs); return ret; }
/* * Transport for the Freecom USB/IDE adaptor. * */ int freecom_transport(Scsi_Cmnd *srb, struct us_data *us) { struct freecom_cb_wrap *fcb; struct freecom_status *fst; int ipipe, opipe; /* We need both pipes. */ int result; int partial; int length; freecom_udata_t extra; extra = (freecom_udata_t) us->extra; fcb = (struct freecom_cb_wrap *) extra->buffer; fst = (struct freecom_status *) extra->buffer; US_DEBUGP("Freecom TRANSPORT STARTED\n"); /* Get handles for both transports. */ opipe = usb_sndbulkpipe (us->pusb_dev, us->ep_out); ipipe = usb_rcvbulkpipe (us->pusb_dev, us->ep_in); #if 0 /* Yuck, let's see if this helps us. Artificially increase the * length on this. */ if (srb->cmnd[0] == 0x03 && srb->cmnd[4] == 0x12) srb->cmnd[4] = 0x0E; #endif /* The ATAPI Command always goes out first. */ fcb->Type = FCM_PACKET_ATAPI | 0x00; fcb->Timeout = 0; memcpy (fcb->Atapi, srb->cmnd, 12); memset (fcb->Filler, 0, sizeof (fcb->Filler)); US_DEBUG(pdump (srb->cmnd, 12)); /* Send it out. */ result = usb_stor_bulk_msg (us, fcb, opipe, FCM_PACKET_LENGTH, &partial); /* The Freecom device will only fail if there is something wrong in * USB land. It returns the status in its own registers, which * come back in the bulk pipe. */ if (result != 0) { US_DEBUGP ("freecom xport failure: r=%d, p=%d\n", result, partial); /* -ENOENT -- we canceled this transfer */ if (result == -ENOENT) { US_DEBUGP("freecom_transport(): transfer aborted\n"); return US_BULK_TRANSFER_ABORTED; } return USB_STOR_TRANSPORT_ERROR; } /* There are times we can optimize out this status read, but it * doesn't hurt us to always do it now. */ result = usb_stor_bulk_msg (us, fst, ipipe, FCM_PACKET_LENGTH, &partial); US_DEBUGP("foo Status result %d %d\n", result, partial); /* -ENOENT -- we canceled this transfer */ if (result == -ENOENT) { US_DEBUGP("freecom_transport(): transfer aborted\n"); return US_BULK_TRANSFER_ABORTED; } US_DEBUG(pdump ((void *) fst, partial)); /* while we haven't recieved the IRQ */ while (!(fst->Status & 0x2)) { /* send a command to re-fetch the status */ US_DEBUGP("Re-attempting to get status...\n"); fcb->Type = FCM_PACKET_STATUS; fcb->Timeout = 0; memset (fcb->Atapi, 0, 12); memset (fcb->Filler, 0, sizeof (fcb->Filler)); /* Send it out. */ result = usb_stor_bulk_msg (us, fcb, opipe, FCM_PACKET_LENGTH, &partial); /* The Freecom device will only fail if there is something * wrong in USB land. It returns the status in its own * registers, which come back in the bulk pipe. */ if (result != 0) { US_DEBUGP ("freecom xport failure: r=%d, p=%d\n", result, partial); /* -ENOENT -- we canceled this transfer */ if (result == -ENOENT) { US_DEBUGP("freecom_transport(): transfer aborted\n"); return US_BULK_TRANSFER_ABORTED; } return USB_STOR_TRANSPORT_ERROR; } /* actually get the status info */ result = usb_stor_bulk_msg (us, fst, ipipe, FCM_PACKET_LENGTH, &partial); US_DEBUGP("bar Status result %d %d\n", result, partial); /* -ENOENT -- we canceled this transfer */ if (result == -ENOENT) { US_DEBUGP("freecom_transport(): transfer aborted\n"); return US_BULK_TRANSFER_ABORTED; } US_DEBUG(pdump ((void *) fst, partial)); } if (partial != 4 || result != 0) { return USB_STOR_TRANSPORT_ERROR; } if ((fst->Status & 1) != 0) { US_DEBUGP("operation failed\n"); return USB_STOR_TRANSPORT_FAILED; } /* The device might not have as much data available as we * requested. If you ask for more than the device has, this reads * and such will hang. */ US_DEBUGP("Device indicates that it has %d bytes available\n", le16_to_cpu (fst->Count)); /* Find the length we desire to read. It is the lesser of the SCSI * layer's requested length, and the length the device claims to * have available. */ length = usb_stor_transfer_length (srb); US_DEBUGP("SCSI requested %d\n", length); if (length > le16_to_cpu (fst->Count)) length = le16_to_cpu (fst->Count); /* What we do now depends on what direction the data is supposed to * move in. */ switch (us->srb->sc_data_direction) { case SCSI_DATA_READ: /* Make sure that the status indicates that the device * wants data as well. */ if ((fst->Status & DRQ_STAT) == 0 || (fst->Reason & 3) != 2) { US_DEBUGP("SCSI wants data, drive doesn't have any\n"); return USB_STOR_TRANSPORT_FAILED; } result = freecom_readdata (srb, us, ipipe, opipe, length); if (result != USB_STOR_TRANSPORT_GOOD) return result; US_DEBUGP("FCM: Waiting for status\n"); result = usb_stor_bulk_msg (us, fst, ipipe, FCM_PACKET_LENGTH, &partial); US_DEBUG(pdump ((void *) fst, partial)); if (result == -ENOENT) { US_DEBUGP ("freecom_transport: transfer aborted\n"); return US_BULK_TRANSFER_ABORTED; } if (partial != 4 || result != 0) return USB_STOR_TRANSPORT_ERROR; if ((fst->Status & ERR_STAT) != 0) { US_DEBUGP("operation failed\n"); return USB_STOR_TRANSPORT_FAILED; } if ((fst->Reason & 3) != 3) { US_DEBUGP("Drive seems still hungry\n"); return USB_STOR_TRANSPORT_FAILED; } US_DEBUGP("Transfer happy\n"); break; case SCSI_DATA_WRITE: /* Make sure the status indicates that the device wants to * send us data. */ /* !!IMPLEMENT!! */ result = freecom_writedata (srb, us, ipipe, opipe, length); if (result != USB_STOR_TRANSPORT_GOOD) return result; #if 1 US_DEBUGP("FCM: Waiting for status\n"); result = usb_stor_bulk_msg (us, fst, ipipe, FCM_PACKET_LENGTH, &partial); if (result == -ENOENT) { US_DEBUGP ("freecom_transport: transfer aborted\n"); return US_BULK_TRANSFER_ABORTED; } if (partial != 4 || result != 0) return USB_STOR_TRANSPORT_ERROR; if ((fst->Status & ERR_STAT) != 0) { US_DEBUGP("operation failed\n"); return USB_STOR_TRANSPORT_FAILED; } if ((fst->Reason & 3) != 3) { US_DEBUGP("Drive seems still hungry\n"); return USB_STOR_TRANSPORT_FAILED; } #endif US_DEBUGP("Transfer happy\n"); break; case SCSI_DATA_NONE: /* Easy, do nothing. */ break; default: US_DEBUGP ("freecom unimplemented direction: %d\n", us->srb->sc_data_direction); // Return fail, SCSI seems to handle this better. return USB_STOR_TRANSPORT_FAILED; break; } return USB_STOR_TRANSPORT_GOOD; US_DEBUGP("Freecom: transfer_length = %d\n", usb_stor_transfer_length (srb)); US_DEBUGP("Freecom: direction = %d\n", srb->sc_data_direction); return USB_STOR_TRANSPORT_ERROR; }
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; }
/* * Transport for the Freecom USB/IDE adaptor. * */ int freecom_transport(Scsi_Cmnd *srb, struct us_data *us) { struct freecom_cb_wrap *fcb; struct freecom_status *fst; int ipipe, opipe; /* We need both pipes. */ int result; int partial; int length; freecom_udata_t extra; extra = (freecom_udata_t) us->extra; fcb = (struct freecom_cb_wrap *) extra->buffer; fst = (struct freecom_status *) extra->buffer; US_DEBUGP("Freecom TRANSPORT STARTED\n"); /* Get handles for both transports. */ opipe = usb_sndbulkpipe (us->pusb_dev, us->ep_out); ipipe = usb_rcvbulkpipe (us->pusb_dev, us->ep_in); /* The ATAPI Command always goes out first. */ fcb->Type = FCM_PACKET_ATAPI | 0x00; fcb->Timeout = 0; memcpy (fcb->Atapi, srb->cmnd, 12); memset (fcb->Filler, 0, sizeof (fcb->Filler)); US_DEBUG(pdump (srb->cmnd, 12)); /* Send it out. */ result = usb_stor_bulk_msg (us, fcb, opipe, FCM_PACKET_LENGTH, &partial); /* The Freecom device will only fail if there is something wrong in * USB land. It returns the status in its own registers, which * come back in the bulk pipe. */ if (result != 0) { US_DEBUGP ("freecom xport failure: r=%d, p=%d\n", result, partial); /* -ENOENT -- we canceled this transfer */ if (result == -ENOENT) { US_DEBUGP("freecom_transport(): transfer aborted\n"); return US_BULK_TRANSFER_ABORTED; } return USB_STOR_TRANSPORT_ERROR; } /* There are times we can optimize out this status read, but it * doesn't hurt us to always do it now. */ result = usb_stor_bulk_msg (us, fst, ipipe, FCM_PACKET_LENGTH, &partial); US_DEBUGP("foo Status result %d %d\n", result, partial); /* -ENOENT -- we canceled this transfer */ if (result == -ENOENT) { US_DEBUGP("freecom_transport(): transfer aborted\n"); return US_BULK_TRANSFER_ABORTED; } US_DEBUG(pdump ((void *) fst, partial)); /* The firmware will time-out commands after 20 seconds. Some commands * can legitimately take longer than this, so we use a different * command that only waits for the interrupt and then sends status, * without having to send a new ATAPI command to the device. * * NOTE: There is some indication that a data transfer after a timeout * may not work, but that is a condition that should never happen. */ while (fst->Status & FCM_STATUS_BUSY) { US_DEBUGP("20 second USB/ATAPI bridge TIMEOUT occured!\n"); US_DEBUGP("fst->Status is %x\n", fst->Status); /* Get the status again */ fcb->Type = FCM_PACKET_STATUS; fcb->Timeout = 0; memset (fcb->Atapi, 0, sizeof(fcb->Atapi)); memset (fcb->Filler, 0, sizeof (fcb->Filler)); /* Send it out. */ result = usb_stor_bulk_msg (us, fcb, opipe, FCM_PACKET_LENGTH, &partial); /* The Freecom device will only fail if there is something * wrong in USB land. It returns the status in its own * registers, which come back in the bulk pipe. */ if (result != 0) { US_DEBUGP ("freecom xport failure: r=%d, p=%d\n", result, partial); /* -ENOENT -- we canceled this transfer */ if (result == -ENOENT) { US_DEBUGP("freecom_transport(): transfer aborted\n"); return US_BULK_TRANSFER_ABORTED; } return USB_STOR_TRANSPORT_ERROR; } /* get the data */ result = usb_stor_bulk_msg (us, fst, ipipe, FCM_PACKET_LENGTH, &partial); US_DEBUGP("bar Status result %d %d\n", result, partial); /* -ENOENT -- we canceled this transfer */ if (result == -ENOENT) { US_DEBUGP("freecom_transport(): transfer aborted\n"); return US_BULK_TRANSFER_ABORTED; } US_DEBUG(pdump ((void *) fst, partial)); } if (partial != 4 || result != 0) { return USB_STOR_TRANSPORT_ERROR; } if ((fst->Status & 1) != 0) { US_DEBUGP("operation failed\n"); return USB_STOR_TRANSPORT_FAILED; } /* The device might not have as much data available as we * requested. If you ask for more than the device has, this reads * and such will hang. */ US_DEBUGP("Device indicates that it has %d bytes available\n", le16_to_cpu (fst->Count)); US_DEBUGP("SCSI requested %d\n", usb_stor_transfer_length(srb)); /* Find the length we desire to read. */ switch (srb->cmnd[0]) { case INQUIRY: case REQUEST_SENSE: /* 16 or 18 bytes? spec says 18, lots of devices only have 16 */ case MODE_SENSE: case MODE_SENSE_10: length = fst->Count; break; default: length = usb_stor_transfer_length (srb); } /* verify that this amount is legal */ if (length > srb->request_bufflen) { length = srb->request_bufflen; US_DEBUGP("Truncating request to match buffer length: %d\n", length); } /* What we do now depends on what direction the data is supposed to * move in. */ switch (us->srb->sc_data_direction) { case SCSI_DATA_READ: /* Make sure that the status indicates that the device * wants data as well. */ if ((fst->Status & DRQ_STAT) == 0 || (fst->Reason & 3) != 2) { US_DEBUGP("SCSI wants data, drive doesn't have any\n"); return USB_STOR_TRANSPORT_FAILED; } result = freecom_readdata (srb, us, ipipe, opipe, length); if (result != USB_STOR_TRANSPORT_GOOD) return result; US_DEBUGP("FCM: Waiting for status\n"); result = usb_stor_bulk_msg (us, fst, ipipe, FCM_PACKET_LENGTH, &partial); US_DEBUG(pdump ((void *) fst, partial)); if (result == -ENOENT) { US_DEBUGP ("freecom_transport: transfer aborted\n"); return US_BULK_TRANSFER_ABORTED; } if (partial != 4 || result != 0) return USB_STOR_TRANSPORT_ERROR; if ((fst->Status & ERR_STAT) != 0) { US_DEBUGP("operation failed\n"); return USB_STOR_TRANSPORT_FAILED; } if ((fst->Reason & 3) != 3) { US_DEBUGP("Drive seems still hungry\n"); return USB_STOR_TRANSPORT_FAILED; } US_DEBUGP("Transfer happy\n"); break; case SCSI_DATA_WRITE: /* Make sure the status indicates that the device wants to * send us data. */ /* !!IMPLEMENT!! */ result = freecom_writedata (srb, us, ipipe, opipe, length); if (result != USB_STOR_TRANSPORT_GOOD) return result; US_DEBUGP("FCM: Waiting for status\n"); result = usb_stor_bulk_msg (us, fst, ipipe, FCM_PACKET_LENGTH, &partial); if (result == -ENOENT) { US_DEBUGP ("freecom_transport: transfer aborted\n"); return US_BULK_TRANSFER_ABORTED; } if (partial != 4 || result != 0) return USB_STOR_TRANSPORT_ERROR; if ((fst->Status & ERR_STAT) != 0) { US_DEBUGP("operation failed\n"); return USB_STOR_TRANSPORT_FAILED; } if ((fst->Reason & 3) != 3) { US_DEBUGP("Drive seems still hungry\n"); return USB_STOR_TRANSPORT_FAILED; } US_DEBUGP("Transfer happy\n"); break; case SCSI_DATA_NONE: /* Easy, do nothing. */ break; default: US_DEBUGP ("freecom unimplemented direction: %d\n", us->srb->sc_data_direction); // Return fail, SCSI seems to handle this better. return USB_STOR_TRANSPORT_FAILED; break; } return USB_STOR_TRANSPORT_GOOD; US_DEBUGP("Freecom: transfer_length = %d\n", usb_stor_transfer_length (srb)); US_DEBUGP("Freecom: direction = %d\n", srb->sc_data_direction); return USB_STOR_TRANSPORT_ERROR; }