diff options
Diffstat (limited to 'drivers/usb/storage/sddr09.c')
-rw-r--r-- | drivers/usb/storage/sddr09.c | 214 |
1 files changed, 130 insertions, 84 deletions
diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c index 0ea2f5a..fb8baca 100644 --- a/drivers/usb/storage/sddr09.c +++ b/drivers/usb/storage/sddr09.c @@ -133,13 +133,11 @@ static struct nand_flash_dev nand_flash_ids[] = { { 0,} }; -#define SIZE(a) (sizeof(a)/sizeof((a)[0])) - static struct nand_flash_dev * nand_find_id(unsigned char id) { int i; - for (i = 0; i < SIZE(nand_flash_ids); i++) + for (i = 0; i < ARRAY_SIZE(nand_flash_ids); i++) if (nand_flash_ids[i].model_id == id) return &(nand_flash_ids[i]); return NULL; @@ -214,6 +212,20 @@ static void nand_store_ecc(unsigned char *data, unsigned char *ecc) { * The actual driver starts here. */ +struct sddr09_card_info { + unsigned long capacity; /* Size of card in bytes */ + int pagesize; /* Size of page in bytes */ + int pageshift; /* log2 of pagesize */ + int blocksize; /* Size of block in pages */ + int blockshift; /* log2 of blocksize */ + int blockmask; /* 2^blockshift - 1 */ + int *lba_to_pba; /* logical to physical map */ + int *pba_to_lba; /* physical to logical map */ + int lbact; /* number of available pages */ + int flags; +#define SDDR09_WP 1 /* write protected */ +}; + /* * On my 16MB card, control blocks have size 64 (16 real control bytes, * and 48 junk bytes). In reality of course the card uses 16 control bytes, @@ -237,7 +249,7 @@ static void nand_store_ecc(unsigned char *data, unsigned char *ecc) { #define SPARE 0xfffffffe #define UNUSABLE 0xfffffffd -static int erase_bad_lba_entries = 0; +static const int erase_bad_lba_entries = 0; /* send vendor interface command (0x41) */ /* called for requests 0, 1, 8 */ @@ -260,8 +272,11 @@ sddr09_send_command(struct us_data *us, rc = usb_stor_ctrl_transfer(us, pipe, request, requesttype, 0, 0, xfer_data, xfer_len); - return (rc == USB_STOR_XFER_GOOD ? USB_STOR_TRANSPORT_GOOD : - USB_STOR_TRANSPORT_ERROR); + switch (rc) { + case USB_STOR_XFER_GOOD: return 0; + case USB_STOR_XFER_STALLED: return -EPIPE; + default: return -EIO; + } } static int @@ -308,20 +323,12 @@ sddr09_request_sense(struct us_data *us, unsigned char *sensebuf, int buflen) { command[4] = buflen; result = sddr09_send_scsi_command(us, command, 12); - if (result != USB_STOR_TRANSPORT_GOOD) { - US_DEBUGP("request sense failed\n"); + if (result) return result; - } result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, sensebuf, buflen, NULL); - if (result != USB_STOR_XFER_GOOD) { - US_DEBUGP("request sense bulk in failed\n"); - return USB_STOR_TRANSPORT_ERROR; - } else { - US_DEBUGP("request sense worked\n"); - return USB_STOR_TRANSPORT_GOOD; - } + return (result == USB_STOR_XFER_GOOD ? 0 : -EIO); } /* @@ -369,7 +376,7 @@ sddr09_readX(struct us_data *us, int x, unsigned long fromaddress, result = sddr09_send_scsi_command(us, command, 12); - if (result != USB_STOR_TRANSPORT_GOOD) { + if (result) { US_DEBUGP("Result for send_control in sddr09_read2%d %d\n", x, result); return result; @@ -381,9 +388,9 @@ sddr09_readX(struct us_data *us, int x, unsigned long fromaddress, if (result != USB_STOR_XFER_GOOD) { US_DEBUGP("Result for bulk_transfer in sddr09_read2%d %d\n", x, result); - return USB_STOR_TRANSPORT_ERROR; + return -EIO; } - return USB_STOR_TRANSPORT_GOOD; + return 0; } /* @@ -497,7 +504,7 @@ sddr09_erase(struct us_data *us, unsigned long Eaddress) { result = sddr09_send_scsi_command(us, command, 12); - if (result != USB_STOR_TRANSPORT_GOOD) + if (result) US_DEBUGP("Result for send_control in sddr09_erase %d\n", result); @@ -555,7 +562,7 @@ sddr09_writeX(struct us_data *us, result = sddr09_send_scsi_command(us, command, 12); - if (result != USB_STOR_TRANSPORT_GOOD) { + if (result) { US_DEBUGP("Result for send_control in sddr09_writeX %d\n", result); return result; @@ -567,9 +574,9 @@ sddr09_writeX(struct us_data *us, if (result != USB_STOR_XFER_GOOD) { US_DEBUGP("Result for bulk_transfer in sddr09_writeX %d\n", result); - return USB_STOR_TRANSPORT_ERROR; + return -EIO; } - return USB_STOR_TRANSPORT_GOOD; + return 0; } /* erase address, write same address */ @@ -633,7 +640,7 @@ sddr09_read_sg_test_only(struct us_data *us) { result = sddr09_send_scsi_command(us, command, 4*nsg+3); - if (result != USB_STOR_TRANSPORT_GOOD) { + if (result) { US_DEBUGP("Result for send_control in sddr09_read_sg %d\n", result); return result; @@ -641,7 +648,7 @@ sddr09_read_sg_test_only(struct us_data *us) { buf = (unsigned char *) kmalloc(bulklen, GFP_NOIO); if (!buf) - return USB_STOR_TRANSPORT_ERROR; + return -ENOMEM; result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, buf, bulklen, NULL); @@ -649,10 +656,10 @@ sddr09_read_sg_test_only(struct us_data *us) { if (result != USB_STOR_XFER_GOOD) { US_DEBUGP("Result for bulk_transfer in sddr09_read_sg %d\n", result); - return USB_STOR_TRANSPORT_ERROR; + return -EIO; } - return USB_STOR_TRANSPORT_GOOD; + return 0; } #endif @@ -681,14 +688,13 @@ sddr09_read_status(struct us_data *us, unsigned char *status) { command[1] = LUNBITS; result = sddr09_send_scsi_command(us, command, 12); - if (result != USB_STOR_TRANSPORT_GOOD) + if (result) return result; result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, data, 64, NULL); *status = data[0]; - return (result == USB_STOR_XFER_GOOD ? - USB_STOR_TRANSPORT_GOOD : USB_STOR_TRANSPORT_ERROR); + return (result == USB_STOR_XFER_GOOD ? 0 : -EIO); } static int @@ -703,6 +709,13 @@ sddr09_read_data(struct us_data *us, unsigned int len, index, offset; int result; + // Figure out the initial LBA and page + lba = address >> info->blockshift; + page = (address & info->blockmask); + maxlba = info->capacity >> (info->pageshift + info->blockshift); + if (lba >= maxlba) + return -EIO; + // Since we only read in one block at a time, we have to create // a bounce buffer and move the data a piece at a time between the // bounce buffer and the actual transfer buffer. @@ -711,18 +724,13 @@ sddr09_read_data(struct us_data *us, buffer = kmalloc(len, GFP_NOIO); if (buffer == NULL) { printk("sddr09_read_data: Out of memory\n"); - return USB_STOR_TRANSPORT_ERROR; + return -ENOMEM; } - // Figure out the initial LBA and page - lba = address >> info->blockshift; - page = (address & info->blockmask); - maxlba = info->capacity >> (info->pageshift + info->blockshift); - // This could be made much more efficient by checking for // contiguous LBA's. Another exercise left to the student. - result = USB_STOR_TRANSPORT_GOOD; + result = 0; index = offset = 0; while (sectors > 0) { @@ -735,7 +743,7 @@ sddr09_read_data(struct us_data *us, if (lba >= maxlba) { US_DEBUGP("Error: Requested lba %u exceeds " "maximum %u\n", lba, maxlba); - result = USB_STOR_TRANSPORT_ERROR; + result = -EIO; break; } @@ -749,7 +757,7 @@ sddr09_read_data(struct us_data *us, /* This is not really an error. It just means that the block has never been written. - Instead of returning USB_STOR_TRANSPORT_ERROR + Instead of returning an error it is better to return all zero data. */ memset(buffer, 0, len); @@ -764,7 +772,7 @@ sddr09_read_data(struct us_data *us, result = sddr09_read20(us, address>>1, pages, info->pageshift, buffer, 0); - if (result != USB_STOR_TRANSPORT_GOOD) + if (result) break; } @@ -830,7 +838,7 @@ sddr09_write_lba(struct us_data *us, unsigned int lba, pba = sddr09_find_unused_pba(info, lba); if (!pba) { printk("sddr09_write_lba: Out of unused blocks\n"); - return USB_STOR_TRANSPORT_ERROR; + return -ENOSPC; } info->pba_to_lba[pba] = lba; info->lba_to_pba[lba] = pba; @@ -841,7 +849,7 @@ sddr09_write_lba(struct us_data *us, unsigned int lba, /* Maybe it is impossible to write to PBA 1. Fake success, but don't do anything. */ printk("sddr09: avoid writing to pba 1\n"); - return USB_STOR_TRANSPORT_GOOD; + return 0; } pagelen = (1 << info->pageshift) + (1 << CONTROL_SHIFT); @@ -850,7 +858,7 @@ sddr09_write_lba(struct us_data *us, unsigned int lba, address = (pba << (info->pageshift + info->blockshift)); result = sddr09_read22(us, address>>1, info->blocksize, info->pageshift, blockbuffer, 0); - if (result != USB_STOR_TRANSPORT_GOOD) + if (result) return result; /* check old contents and fill lba */ @@ -897,7 +905,7 @@ sddr09_write_lba(struct us_data *us, unsigned int lba, { unsigned char status = 0; int result2 = sddr09_read_status(us, &status); - if (result2 != USB_STOR_TRANSPORT_GOOD) + if (result2) US_DEBUGP("sddr09_write_inplace: cannot read status\n"); else if (status != 0xc0) US_DEBUGP("sddr09_write_inplace: status after write: 0x%x\n", @@ -920,13 +928,20 @@ sddr09_write_data(struct us_data *us, unsigned int sectors) { struct sddr09_card_info *info = (struct sddr09_card_info *) us->extra; - unsigned int lba, page, pages; + unsigned int lba, maxlba, page, pages; unsigned int pagelen, blocklen; unsigned char *blockbuffer; unsigned char *buffer; unsigned int len, index, offset; int result; + // Figure out the initial LBA and page + lba = address >> info->blockshift; + page = (address & info->blockmask); + maxlba = info->capacity >> (info->pageshift + info->blockshift); + if (lba >= maxlba) + return -EIO; + // blockbuffer is used for reading in the old data, overwriting // with the new data, and performing ECC calculations @@ -938,7 +953,7 @@ sddr09_write_data(struct us_data *us, blockbuffer = kmalloc(blocklen, GFP_NOIO); if (!blockbuffer) { printk("sddr09_write_data: Out of memory\n"); - return USB_STOR_TRANSPORT_ERROR; + return -ENOMEM; } // Since we don't write the user data directly to the device, @@ -950,14 +965,10 @@ sddr09_write_data(struct us_data *us, if (buffer == NULL) { printk("sddr09_write_data: Out of memory\n"); kfree(blockbuffer); - return USB_STOR_TRANSPORT_ERROR; + return -ENOMEM; } - // Figure out the initial LBA and page - lba = address >> info->blockshift; - page = (address & info->blockmask); - - result = USB_STOR_TRANSPORT_GOOD; + result = 0; index = offset = 0; while (sectors > 0) { @@ -967,13 +978,21 @@ sddr09_write_data(struct us_data *us, pages = min(sectors, info->blocksize - page); len = (pages << info->pageshift); + /* Not overflowing capacity? */ + if (lba >= maxlba) { + US_DEBUGP("Error: Requested lba %u exceeds " + "maximum %u\n", lba, maxlba); + result = -EIO; + break; + } + // Get the data from the transfer buffer usb_stor_access_xfer_buf(buffer, len, us->srb, &index, &offset, FROM_XFER_BUF); result = sddr09_write_lba(us, lba, page, pages, buffer, blockbuffer); - if (result != USB_STOR_TRANSPORT_GOOD) + if (result) break; page = 0; @@ -1022,7 +1041,7 @@ sddr09_read_deviceID(struct us_data *us, unsigned char *deviceID) { command[1] = LUNBITS; result = sddr09_send_scsi_command(us, command, 12); - if (result != USB_STOR_TRANSPORT_GOOD) + if (result) return result; result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, @@ -1031,8 +1050,7 @@ sddr09_read_deviceID(struct us_data *us, unsigned char *deviceID) { for (i = 0; i < 4; i++) deviceID[i] = content[i]; - return (result == USB_STOR_XFER_GOOD ? - USB_STOR_TRANSPORT_GOOD : USB_STOR_TRANSPORT_ERROR); + return (result == USB_STOR_XFER_GOOD ? 0 : -EIO); } static int @@ -1041,7 +1059,7 @@ sddr09_get_wp(struct us_data *us, struct sddr09_card_info *info) { unsigned char status; result = sddr09_read_status(us, &status); - if (result != USB_STOR_TRANSPORT_GOOD) { + if (result) { US_DEBUGP("sddr09_get_wp: read_status fails\n"); return result; } @@ -1057,7 +1075,7 @@ sddr09_get_wp(struct us_data *us, struct sddr09_card_info *info) { if (status & 0x1) US_DEBUGP(" Error"); US_DEBUGP("\n"); - return USB_STOR_TRANSPORT_GOOD; + return 0; } #if 0 @@ -1089,7 +1107,7 @@ sddr09_get_cardinfo(struct us_data *us, unsigned char flags) { result = sddr09_read_deviceID(us, deviceID); - if (result != USB_STOR_TRANSPORT_GOOD) { + if (result) { US_DEBUGP("Result of read_deviceID is %d\n", result); printk("sddr09: could not read card info\n"); return NULL; @@ -1200,7 +1218,7 @@ sddr09_read_map(struct us_data *us) { us, address>>1, min(alloc_blocks, numblocks - i), buffer, 0); - if (result != USB_STOR_TRANSPORT_GOOD) { + if (result) { result = -1; goto done; } @@ -1342,29 +1360,53 @@ sddr09_card_info_destructor(void *extra) { kfree(info->pba_to_lba); } -static void -sddr09_init_card_info(struct us_data *us) { - if (!us->extra) { - us->extra = kmalloc(sizeof(struct sddr09_card_info), GFP_NOIO); - if (us->extra) { - memset(us->extra, 0, sizeof(struct sddr09_card_info)); - us->extra_destructor = sddr09_card_info_destructor; - } +static int +sddr09_common_init(struct us_data *us) { + int result; + + /* set the configuration -- STALL is an acceptable response here */ + if (us->pusb_dev->actconfig->desc.bConfigurationValue != 1) { + US_DEBUGP("active config #%d != 1 ??\n", us->pusb_dev + ->actconfig->desc.bConfigurationValue); + return -EINVAL; + } + + result = usb_reset_configuration(us->pusb_dev); + US_DEBUGP("Result of usb_reset_configuration is %d\n", result); + if (result == -EPIPE) { + US_DEBUGP("-- stall on control interface\n"); + } else if (result != 0) { + /* it's not a stall, but another error -- time to bail */ + US_DEBUGP("-- Unknown error. Rejecting device\n"); + return -EINVAL; } + + us->extra = kzalloc(sizeof(struct sddr09_card_info), GFP_NOIO); + if (!us->extra) + return -ENOMEM; + us->extra_destructor = sddr09_card_info_destructor; + + nand_init_ecc(); + return 0; } + /* * This is needed at a very early stage. If this is not listed in the * unusual devices list but called from here then LUN 0 of the combo reader * is not recognized. But I do not know what precisely these calls do. */ int -sddr09_init(struct us_data *us) { +usb_stor_sddr09_dpcm_init(struct us_data *us) { int result; unsigned char *data = us->iobuf; + result = sddr09_common_init(us); + if (result) + return result; + result = sddr09_send_command(us, 0x01, USB_DIR_IN, data, 2); - if (result != USB_STOR_TRANSPORT_GOOD) { + if (result) { US_DEBUGP("sddr09_init: send_command fails\n"); return result; } @@ -1373,7 +1415,7 @@ sddr09_init(struct us_data *us) { // get 07 02 result = sddr09_send_command(us, 0x08, USB_DIR_IN, data, 2); - if (result != USB_STOR_TRANSPORT_GOOD) { + if (result) { US_DEBUGP("sddr09_init: 2nd send_command fails\n"); return result; } @@ -1382,7 +1424,7 @@ sddr09_init(struct us_data *us) { // get 07 00 result = sddr09_request_sense(us, data, 18); - if (result == USB_STOR_TRANSPORT_GOOD && data[2] != 0) { + if (result == 0 && data[2] != 0) { int j; for (j=0; j<18; j++) printk(" %02X", data[j]); @@ -1398,7 +1440,7 @@ sddr09_init(struct us_data *us) { // test unit ready - return USB_STOR_TRANSPORT_GOOD; /* not result */ + return 0; /* not result */ } /* @@ -1427,13 +1469,6 @@ int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us) }; info = (struct sddr09_card_info *)us->extra; - if (!info) { - nand_init_ecc(); - sddr09_init_card_info(us); - info = (struct sddr09_card_info *)us->extra; - if (!info) - return USB_STOR_TRANSPORT_ERROR; - } if (srb->cmnd[0] == REQUEST_SENSE && havefakesense) { /* for a faked command, we have to follow with a faked sense */ @@ -1536,7 +1571,9 @@ int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us) US_DEBUGP("READ_10: read page %d pagect %d\n", page, pages); - return sddr09_read_data(us, page, pages); + result = sddr09_read_data(us, page, pages); + return (result == 0 ? USB_STOR_TRANSPORT_GOOD : + USB_STOR_TRANSPORT_ERROR); } if (srb->cmnd[0] == WRITE_10) { @@ -1549,7 +1586,9 @@ int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us) US_DEBUGP("WRITE_10: write page %d pagect %d\n", page, pages); - return sddr09_write_data(us, page, pages); + result = sddr09_write_data(us, page, pages); + return (result == 0 ? USB_STOR_TRANSPORT_GOOD : + USB_STOR_TRANSPORT_ERROR); } /* catch-all for all other commands, except @@ -1575,10 +1614,10 @@ int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us) US_DEBUGP("SDDR09: Send control for command %s\n", ptr); result = sddr09_send_scsi_command(us, srb->cmnd, 12); - if (result != USB_STOR_TRANSPORT_GOOD) { + if (result) { US_DEBUGP("sddr09_transport: sddr09_send_scsi_command " "returns %d\n", result); - return result; + return USB_STOR_TRANSPORT_ERROR; } if (srb->request_bufflen == 0) @@ -1606,3 +1645,10 @@ int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us) return USB_STOR_TRANSPORT_GOOD; } +/* + * Initialization routine for the sddr09 subdriver + */ +int +usb_stor_sddr09_init(struct us_data *us) { + return sddr09_common_init(us); +} |