/* * 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; }
/* * Transport for the Freecom USB/IDE adaptor. * */ static int freecom_transport(struct scsi_cmnd *srb, struct us_data *us) { struct freecom_cb_wrap *fcb; struct freecom_status *fst; unsigned int ipipe, opipe; /* We need both pipes. */ int result; unsigned int partial; int length; fcb = (struct freecom_cb_wrap *) us->iobuf; fst = (struct freecom_status *) us->iobuf; US_DEBUGP("Freecom TRANSPORT STARTED\n"); /* Get handles for both transports. */ opipe = us->send_bulk_pipe; ipipe = us->recv_bulk_pipe; /* 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_transfer_buf (us, opipe, fcb, FCM_PACKET_LENGTH, NULL); /* 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 != USB_STOR_XFER_GOOD) { US_DEBUGP ("freecom transport error\n"); 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_transfer_buf (us, ipipe, fst, FCM_STATUS_PACKET_LENGTH, &partial); US_DEBUGP("foo Status result %d %u\n", result, partial); if (result != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; 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 occurred!\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_transfer_buf (us, opipe, fcb, FCM_PACKET_LENGTH, NULL); /* 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 != USB_STOR_XFER_GOOD) { US_DEBUGP ("freecom transport error\n"); return USB_STOR_TRANSPORT_ERROR; } /* get the data */ result = usb_stor_bulk_transfer_buf (us, ipipe, fst, FCM_STATUS_PACKET_LENGTH, &partial); US_DEBUGP("bar Status result %d %u\n", result, partial); if (result != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; US_DEBUG(pdump ((void *) fst, partial)); } if (partial != 4) 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", scsi_bufflen(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 = le16_to_cpu(fst->Count); break; default: length = scsi_bufflen(srb); } /* verify that this amount is legal */ if (length > scsi_bufflen(srb)) { length = scsi_bufflen(srb); 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 DMA_FROM_DEVICE: /* catch bogus "read 0 length" case */ if (!length) break; /* 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_transfer_buf (us, ipipe, fst, FCM_PACKET_LENGTH, &partial); US_DEBUG(pdump ((void *) fst, partial)); if (partial != 4 || result > USB_STOR_XFER_SHORT) 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 DMA_TO_DEVICE: /* catch bogus "write 0 length" case */ if (!length) break; /* 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_transfer_buf (us, ipipe, fst, FCM_PACKET_LENGTH, &partial); if (partial != 4 || result > USB_STOR_XFER_SHORT) 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 DMA_NONE: /* Easy, do nothing. */ break; default: /* should never hit here -- filtered in usb.c */ 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; }
static int freecom_transport(struct scsi_cmnd *srb, struct us_data *us) { struct freecom_cb_wrap *fcb; struct freecom_status *fst; unsigned int ipipe, opipe; int result; unsigned int partial; int length; fcb = (struct freecom_cb_wrap *) us->iobuf; fst = (struct freecom_status *) us->iobuf; US_DEBUGP("Freecom TRANSPORT STARTED\n"); opipe = us->send_bulk_pipe; ipipe = us->recv_bulk_pipe; 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)); result = usb_stor_bulk_transfer_buf (us, opipe, fcb, FCM_PACKET_LENGTH, NULL); if (result != USB_STOR_XFER_GOOD) { US_DEBUGP ("freecom transport error\n"); return USB_STOR_TRANSPORT_ERROR; } result = usb_stor_bulk_transfer_buf (us, ipipe, fst, FCM_STATUS_PACKET_LENGTH, &partial); US_DEBUGP("foo Status result %d %u\n", result, partial); if (result != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; US_DEBUG(pdump ((void *) fst, partial)); while (fst->Status & FCM_STATUS_BUSY) { US_DEBUGP("20 second USB/ATAPI bridge TIMEOUT occurred!\n"); US_DEBUGP("fst->Status is %x\n", fst->Status); fcb->Type = FCM_PACKET_STATUS; fcb->Timeout = 0; memset (fcb->Atapi, 0, sizeof(fcb->Atapi)); memset (fcb->Filler, 0, sizeof (fcb->Filler)); result = usb_stor_bulk_transfer_buf (us, opipe, fcb, FCM_PACKET_LENGTH, NULL); if (result != USB_STOR_XFER_GOOD) { US_DEBUGP ("freecom transport error\n"); return USB_STOR_TRANSPORT_ERROR; } result = usb_stor_bulk_transfer_buf (us, ipipe, fst, FCM_STATUS_PACKET_LENGTH, &partial); US_DEBUGP("bar Status result %d %u\n", result, partial); if (result != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; US_DEBUG(pdump ((void *) fst, partial)); } if (partial != 4) return USB_STOR_TRANSPORT_ERROR; if ((fst->Status & 1) != 0) { US_DEBUGP("operation failed\n"); return USB_STOR_TRANSPORT_FAILED; } US_DEBUGP("Device indicates that it has %d bytes available\n", le16_to_cpu (fst->Count)); US_DEBUGP("SCSI requested %d\n", scsi_bufflen(srb)); switch (srb->cmnd[0]) { case INQUIRY: case REQUEST_SENSE: case MODE_SENSE: case MODE_SENSE_10: length = le16_to_cpu(fst->Count); break; default: length = scsi_bufflen(srb); } if (length > scsi_bufflen(srb)) { length = scsi_bufflen(srb); US_DEBUGP("Truncating request to match buffer length: %d\n", length); } switch (us->srb->sc_data_direction) { case DMA_FROM_DEVICE: if (!length) break; 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_transfer_buf (us, ipipe, fst, FCM_PACKET_LENGTH, &partial); US_DEBUG(pdump ((void *) fst, partial)); if (partial != 4 || result > USB_STOR_XFER_SHORT) 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 DMA_TO_DEVICE: if (!length) break; 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_transfer_buf (us, ipipe, fst, FCM_PACKET_LENGTH, &partial); if (partial != 4 || result > USB_STOR_XFER_SHORT) 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 DMA_NONE: break; default: US_DEBUGP ("freecom unimplemented direction: %d\n", us->srb->sc_data_direction); return USB_STOR_TRANSPORT_FAILED; break; } return USB_STOR_TRANSPORT_GOOD; }