Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / drivers / usb / storage / sddr09.c
index 0ea2f5a..fb8baca 100644 (file)
@@ -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);
+}