summaryrefslogtreecommitdiffstats
path: root/drivers/usb/storage/sddr09.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/storage/sddr09.c')
-rw-r--r--drivers/usb/storage/sddr09.c534
1 files changed, 412 insertions, 122 deletions
diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c
index 275712961..6bf9f1477 100644
--- a/drivers/usb/storage/sddr09.c
+++ b/drivers/usb/storage/sddr09.c
@@ -44,7 +44,7 @@ extern int usb_stor_control_msg(struct us_data *us, unsigned int pipe,
extern int usb_stor_bulk_msg(struct us_data *us, void *data, int pipe,
unsigned int len, unsigned int *act_len);
-#define short_pack(b1,b2) ( ((u16)(b1)) | ( ((u16)(b2))<<8 ) )
+#define short_pack(lsb,msb) ( ((u16)(lsb)) | ( ((u16)(msb))<<8 ) )
#define LSB_of(s) ((s)&0xFF)
#define MSB_of(s) ((s)>>8)
@@ -132,7 +132,7 @@ static int sddr09_send_control(struct us_data *us,
static int sddr09_raw_bulk(struct us_data *us,
int direction,
unsigned char *data,
- unsigned short len) {
+ unsigned int len) {
int result;
int act_len;
@@ -196,64 +196,38 @@ static int sddr09_raw_bulk(struct us_data *us,
*/
static int sddr09_bulk_transport(struct us_data *us,
- unsigned char *command,
- unsigned short command_len,
int direction,
unsigned char *data,
- unsigned short len,
+ unsigned int len,
int use_sg) {
int result = USB_STOR_TRANSPORT_GOOD;
int transferred = 0;
- unsigned char execute[8] = {
- 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
- };
int i;
struct scatterlist *sg;
char string[64];
-/*
- if (command_len != 0) {
-
- // Fix up the command's data length
- command[6] = len&0xFF;
- command[7] = (len>>8)&0xFF;
-
- result = sddr09_send_control(us,
- execute,
- command,
- command_len);
-
- if (result != USB_STOR_TRANSPORT_GOOD)
- return result;
- }
-*/
if (len==0)
return USB_STOR_TRANSPORT_GOOD;
- /* transfer the data payload for the command, if there is any */
-
-
- if (command_len != 0)
- direction = (command[0]&0x80) ? SCSI_DATA_READ :
- SCSI_DATA_WRITE;
+ /* transfer the data */
if (direction == SCSI_DATA_WRITE) {
/* Debug-print the first 48 bytes of the write transfer */
if (!use_sg) {
- string[0] = 0;
+ strcpy(string, "wr: ");
for (i=0; i<len && i<48; i++) {
sprintf(string+strlen(string), "%02X ",
data[i]);
if ((i%16)==15) {
US_DEBUGP("%s\n", string);
- string[0] = 0;
+ strcpy(string, "wr: ");
}
}
- if (string[0]!=0)
+ if ((i%16)!=0)
US_DEBUGP("%s\n", string);
}
}
@@ -278,6 +252,25 @@ static int sddr09_bulk_transport(struct us_data *us,
}
}
+ if (direction == SCSI_DATA_READ) {
+
+ /* Debug-print the first 48 bytes of the read transfer */
+
+ if (!use_sg) {
+ strcpy(string, "rd: ");
+ for (i=0; i<len && i<48; i++) {
+ sprintf(string+strlen(string), "%02X ",
+ data[i]);
+ if ((i%16)==15) {
+ US_DEBUGP("%s\n", string);
+ strcpy(string, "rd: ");
+ }
+ }
+ if ((i%16)!=0)
+ US_DEBUGP("%s\n", string);
+ }
+ }
+
return result;
}
@@ -289,45 +282,142 @@ int sddr09_read_data(struct us_data *us,
int result;
unsigned char command[12] = {
- 0xe8, 0x20, MSB_of(address>>16),
- LSB_of(address>>16), MSB_of(address&0xFFFF),
- LSB_of(address&0xFFFF), 0, 0, 0, 0,
- MSB_of(sectors), LSB_of(sectors)
+ 0xe8, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
+ struct sddr09_card_info *info = (struct sddr09_card_info *)us->extra;
+ unsigned int lba;
+ unsigned int pba;
+ unsigned short page;
+ unsigned short pages;
+ unsigned char *buffer = NULL;
+ unsigned char *ptr;
+ struct scatterlist *sg = NULL;
+ int i;
+ int len;
+ int transferred;
- result = sddr09_send_control(us,
- usb_sndctrlpipe(us->pusb_dev,0),
- 0,
- 0x41,
- 0,
- 0,
- command,
- 12);
+ // If we're using scatter-gather, we have to create a new
+ // buffer to read all of the data in first, since a
+ // scatter-gather buffer could in theory start in the middle
+ // of a page, which would be bad. A developer who wants a
+ // challenge might want to write a limited-buffer
+ // version of this code.
+
+ len = sectors*info->pagesize;
+
+ if (use_sg) {
+ sg = (struct scatterlist *)content;
+ buffer = kmalloc(len, GFP_KERNEL);
+ if (buffer == NULL)
+ return USB_STOR_TRANSPORT_ERROR;
+ ptr = buffer;
+ } else
+ ptr = content;
+
+ // Figure out the initial LBA and page
+
+ pba = (address/info->pagesize)>>4;
+ lba = info->pba_to_lba[pba];
+ page = (address/info->pagesize)&0x0F;
+
+ // This could be made much more efficient by checking for
+ // contiguous LBA's. Another exercise left to the student.
+
+ while (sectors>0) {
+
+ pba = info->lba_to_pba[lba];
+
+ // Read as many sectors as possible in this block
+
+ pages = 0x10-page;
+ if (pages > sectors)
+ pages = sectors;
+
+ US_DEBUGP("Read %01X pages, from PBA %04X"
+ " (LBA %04X) page %01X\n",
+ pages, pba, lba, page);
+
+ address = ((pba<<4)+page)*info->pagesize;
+
+ // Unlike in the documentation, the address is in
+ // words of 2 bytes.
+
+ command[2] = MSB_of(address>>17);
+ command[3] = LSB_of(address>>17);
+ command[4] = MSB_of((address>>1)&0xFFFF);
+ command[5] = LSB_of((address>>1)&0xFFFF);
+
+ command[10] = MSB_of(pages);
+ command[11] = LSB_of(pages);
+
+ result = sddr09_send_control(us,
+ usb_sndctrlpipe(us->pusb_dev,0),
+ 0,
+ 0x41,
+ 0,
+ 0,
+ command,
+ 12);
- if (result != USB_STOR_TRANSPORT_GOOD)
- return result;
+ US_DEBUGP("Result for send_control in read_data %d\n",
+ result);
+
+ if (result != USB_STOR_TRANSPORT_GOOD) {
+ if (use_sg)
+ kfree(buffer);
+ return result;
+ }
- result = sddr09_bulk_transport(us,
- NULL, 0, SCSI_DATA_READ, content,
- sectors*512, use_sg);
+ result = sddr09_bulk_transport(us,
+ SCSI_DATA_READ, ptr,
+ pages*info->pagesize, 0);
- return result;
+ if (result != USB_STOR_TRANSPORT_GOOD) {
+ if (use_sg)
+ kfree(buffer);
+ return result;
+ }
+
+ page = 0;
+ lba++;
+ sectors -= pages;
+ ptr += pages*info->pagesize;
+ }
+
+ if (use_sg) {
+ transferred = 0;
+ for (i=0; i<use_sg && transferred<len; i++) {
+ memcpy(sg[i].address, buffer+transferred,
+ len-transferred > sg[i].length ?
+ sg[i].length : len-transferred);
+ transferred += sg[i].length;
+ }
+ kfree(buffer);
+ }
+
+ return USB_STOR_TRANSPORT_GOOD;
}
int sddr09_read_control(struct us_data *us,
unsigned long address,
- unsigned short sectors,
+ unsigned short blocks,
unsigned char *content,
int use_sg) {
+ // Unlike in the documentation, the last two bytes are the
+ // number of blocks, not sectors.
+
int result;
unsigned char command[12] = {
0xe8, 0x21, MSB_of(address>>16),
LSB_of(address>>16), MSB_of(address&0xFFFF),
LSB_of(address&0xFFFF), 0, 0, 0, 0,
- MSB_of(sectors), LSB_of(sectors)
+ MSB_of(blocks), LSB_of(blocks)
};
+ US_DEBUGP("Read control address %08X blocks %04X\n",
+ address, blocks);
+
result = sddr09_send_control(us,
usb_sndctrlpipe(us->pusb_dev,0),
0,
@@ -336,13 +426,19 @@ int sddr09_read_control(struct us_data *us,
0,
command,
12);
+
+ US_DEBUGP("Result for send_control in read_control %d\n",
+ result);
if (result != USB_STOR_TRANSPORT_GOOD)
return result;
result = sddr09_bulk_transport(us,
- NULL, 0, SCSI_DATA_READ, content,
- sectors*64, use_sg);
+ SCSI_DATA_READ, content,
+ blocks*0x40, use_sg);
+
+ US_DEBUGP("Result for bulk read in read_control %d\n",
+ result);
return result;
}
@@ -373,7 +469,7 @@ int sddr09_read_deviceID(struct us_data *us,
return result;
result = sddr09_bulk_transport(us,
- NULL, 0, SCSI_DATA_READ, content,
+ SCSI_DATA_READ, content,
64, 0);
*manufacturerID = content[0];
@@ -389,7 +485,6 @@ int sddr09_read_status(struct us_data *us,
unsigned char command[12] = {
0xec, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
- unsigned char content[2];
result = sddr09_send_control(us,
usb_sndctrlpipe(us->pusb_dev,0),
@@ -404,7 +499,7 @@ int sddr09_read_status(struct us_data *us,
return result;
result = sddr09_bulk_transport(us,
- NULL, 0, SCSI_DATA_READ, status,
+ SCSI_DATA_READ, status,
1, 0);
return result;
@@ -429,6 +524,154 @@ int sddr09_reset(struct us_data *us) {
return result;
}
+unsigned long sddr09_get_capacity(struct us_data *us,
+ unsigned int *pagesize) {
+
+ unsigned char manufacturerID;
+ unsigned char deviceID;
+ int result;
+
+ US_DEBUGP("Reading capacity...\n");
+
+ result = sddr09_read_deviceID(us,
+ &manufacturerID,
+ &deviceID);
+
+ US_DEBUGP("Result of read_deviceID is %d\n",
+ result);
+
+ if (result != USB_STOR_TRANSPORT_GOOD)
+ return 0;
+
+ US_DEBUGP("Device ID = %02X\n", deviceID);
+ US_DEBUGP("Manuf ID = %02X\n", manufacturerID);
+
+ *pagesize = 512;
+
+ switch (deviceID) {
+
+ case 0x6e: // 1MB
+ case 0xe8:
+ case 0xec:
+ *pagesize = 256;
+ return 0x00100000;
+
+ case 0x5d: // 2MB
+ case 0xea:
+ case 0x64:
+ if (deviceID!=0x5D)
+ *pagesize = 256;
+ return 0x00200000;
+
+ case 0xe3: // 4MB
+ case 0xe5:
+ case 0x6b:
+ case 0xd5:
+ return 0x00400000;
+
+ case 0xe6: // 8MB
+ case 0xd6:
+ return 0x00800000;
+
+ case 0x73: // 16MB
+ return 0x01000000;
+
+ case 0x75: // 32MB
+ return 0x02000000;
+
+ default: // unknown
+ return 0;
+
+ }
+}
+
+int sddr09_read_map(struct us_data *us) {
+
+ unsigned char *control;
+ struct sddr09_card_info *info = (struct sddr09_card_info *)(us->extra);
+ int numblocks;
+ int i;
+ unsigned char *ptr;
+ unsigned short lba;
+ unsigned char parity;
+ unsigned char fast_parity[16] = {
+ 0, 1, 1, 0, 1, 0, 0, 1,
+ 1, 0, 0, 1, 0, 1, 1, 0
+ };
+ int result;
+
+ if (!info->capacity)
+ return -1;
+
+ /* read 64 (2^6) bytes for every block (8192 (2^13) bytes)
+ of capacity:
+ 64*(capacity/8192) = capacity*(2^6)*(2^-13) =
+ capacity*2^(6-13) = capacity*(2^-7)
+ */
+
+ control = kmalloc(info->capacity>>7, GFP_KERNEL);
+
+
+ numblocks = info->capacity>>13;
+
+ if ( (result = sddr09_read_control(us, 0, numblocks,
+ control, 0)) !=
+ USB_STOR_TRANSPORT_GOOD) {
+ kfree(control);
+ return -1;
+ }
+
+
+
+ if (info->lba_to_pba)
+ kfree(info->lba_to_pba);
+ if (info->pba_to_lba)
+ kfree(info->pba_to_lba);
+ info->lba_to_pba = kmalloc(numblocks*sizeof(int), GFP_KERNEL);
+ info->pba_to_lba = kmalloc(numblocks*sizeof(int), GFP_KERNEL);
+ memset(info->lba_to_pba, 0, numblocks*sizeof(int));
+ memset(info->pba_to_lba, 0, numblocks*sizeof(int));
+
+ for (i=0; i<numblocks; i++) {
+ ptr = control+64*i;
+ if (ptr[0]!=0xFF || ptr[1]!=0xFF || ptr[2]!=0xFF ||
+ ptr[3]!=0xFF || ptr[4]!=0xFF || ptr[5]!=0xFF)
+ continue;
+ if ((ptr[6]>>4)!=0x01)
+ continue;
+
+ /* ensure even parity */
+
+ lba = short_pack(ptr[7], ptr[6]);
+ parity = 1; // the parity of 0x1000
+ parity ^= fast_parity[lba & 0x000F];
+ parity ^= fast_parity[(lba>>4) & 0x000F];
+ parity ^= fast_parity[(lba>>8) & 0x000F];
+
+ if (parity) { /* bad parity bit */
+ US_DEBUGP("Bad parity in LBA for block %04X\n", i);
+ continue;
+ }
+
+ lba = (lba&0x07FF)>>1;
+
+ if (lba>=numblocks) {
+ US_DEBUGP("Bad LBA %04X for block %04X\n", lba, i);
+ continue;
+ }
+
+ if (i<0x10)
+ US_DEBUGP("LBA %04X <-> PBA %04X\n",
+ lba, i);
+
+ info->pba_to_lba[i] = lba;
+ info->lba_to_pba[lba] = i;
+ }
+
+ kfree(control);
+ return 0;
+}
+
/*
static int init_sddr09(struct us_data *us) {
@@ -477,15 +720,24 @@ static int init_sddr09(struct us_data *us) {
}
*/
+void sddr09_card_info_destructor(void *extra) {
+ struct sddr09_card_info *info = (struct sddr09_card_info *)extra;
+
+ if (!extra)
+ return;
+
+ if (info->lba_to_pba)
+ kfree(info->lba_to_pba);
+ if (info->pba_to_lba)
+ kfree(info->pba_to_lba);
+}
+
/*
* Transport for the Sandisk SDDR-09
*/
int sddr09_transport(Scsi_Cmnd *srb, struct us_data *us)
{
int result;
- unsigned char send_scsi_command[8] = {
- 0x41, 0, 0, 0, 0, 0, 0, 0
- };
int i;
char string[64];
unsigned char inquiry_response[36] = {
@@ -495,9 +747,16 @@ int sddr09_transport(Scsi_Cmnd *srb, struct us_data *us)
'e', ' ', 'S', 'D', 'D', 'R', '0', '9',
' ', ' ', ' ', ' '
};
- unsigned char deviceID;
- unsigned char manufacturerID;
+ unsigned char mode_page_01[12] = {
+ 0x01, 0x0a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
unsigned char *ptr;
+ unsigned long capacity;
+ unsigned int lba;
+ unsigned int pba;
+ unsigned int page;
+ unsigned short pages;
+ struct sddr09_card_info *info = (struct sddr09_card_info *)(us->extra);
/*
if (us->flags & US_FL_NEED_INIT) {
@@ -507,85 +766,116 @@ int sddr09_transport(Scsi_Cmnd *srb, struct us_data *us)
}
*/
+ if (!us->extra) {
+ us->extra = kmalloc(
+ sizeof(struct sddr09_card_info), GFP_KERNEL);
+ memset(us->extra, 0, sizeof(struct sddr09_card_info));
+ us->extra_destructor = sddr09_card_info_destructor;
+ }
+
ptr = (unsigned char *)srb->request_buffer;
/* Dummy up a response for INQUIRY since SDDR09 doesn't
respond to INQUIRY commands */
if (srb->cmnd[0] == INQUIRY) {
- memcpy(srb->request_buffer, inquiry_response, 36);
+ memcpy(ptr, inquiry_response, 36);
return USB_STOR_TRANSPORT_GOOD;
}
if (srb->cmnd[0] == READ_CAPACITY) {
- US_DEBUGP("Reading capacity...\n");
+ capacity = sddr09_get_capacity(us, &info->pagesize);
+ info->capacity = capacity;
- result = sddr09_read_deviceID(us,
- &manufacturerID,
- &deviceID);
+ // Last page in the card
- US_DEBUGP("Result of read_deviceID is %d\n",
- result);
+ capacity /= info->pagesize;
+ capacity--;
- if (result != USB_STOR_TRANSPORT_GOOD)
- return result;
+ ptr[0] = MSB_of(capacity>>16);
+ ptr[1] = LSB_of(capacity>>16);
+ ptr[2] = MSB_of(capacity&0xFFFF);
+ ptr[3] = LSB_of(capacity&0xFFFF);
- US_DEBUGP("Device ID = %02X\n", deviceID);
- US_DEBUGP("Manuf ID = %02X\n", manufacturerID);
-
- ptr[0] = 0;
- ptr[1] = 0;
- ptr[2] = 0;
- ptr[3] = 0;
-
- switch (deviceID) {
-
- case 0x6e: // 1MB
- case 0xe8:
- case 0xec:
- ptr[4] = 0;
- ptr[5] = 0x10;
- break;
-
- case 0xea: // 2MB
- case 0x64:
- case 0x5d:
- ptr[4] = 0;
- ptr[5] = 0x20;
- break;
-
- case 0xe3: // 4MB
- case 0xe5:
- case 0x6b:
- case 0xd5:
- ptr[4] = 0;
- ptr[5] = 0x40;
- break;
-
- case 0xe6: // 8MB
- case 0xd6:
- ptr[4] = 0;
- ptr[5] = 0x80;
- break;
-
- case 0x75: // 32MB
- ptr[4] = 0x02;
- ptr[5] = 0;
- break;
-
- default: // unknown
- ptr[4] = 0;
- ptr[5] = 0;
+ // The page size
- }
+ ptr[4] = MSB_of(info->pagesize>>16);
+ ptr[5] = LSB_of(info->pagesize>>16);
+ ptr[6] = MSB_of(info->pagesize&0xFFFF);
+ ptr[7] = LSB_of(info->pagesize&0xFFFF);
- ptr[6] = 0;
- ptr[7] = 0;
+ sddr09_read_map(us);
return USB_STOR_TRANSPORT_GOOD;
}
+ if (srb->cmnd[0] == MODE_SENSE) {
+
+ // Read-write error recovery page: there needs to
+ // be a check for write-protect here
+
+ if ( (srb->cmnd[2] & 0x3F) == 0x01 ) {
+ if (ptr==NULL || srb->request_bufflen<12)
+ return USB_STOR_TRANSPORT_ERROR;
+ memcpy(ptr, mode_page_01, 12);
+ return USB_STOR_TRANSPORT_GOOD;
+ }
+
+ // FIXME: sense buffer?
+
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ if (srb->cmnd[0] == READ_10) {
+
+ page = short_pack(srb->cmnd[3], srb->cmnd[2]);
+ page <<= 16;
+ page |= short_pack(srb->cmnd[5], srb->cmnd[4]);
+ pages = short_pack(srb->cmnd[8], srb->cmnd[7]);
+
+ // convert page to block and page-within-block
+
+ lba = page>>4;
+ page = page&0x0F;
+
+ // locate physical block corresponding to logical block
+
+ if (lba>=(info->capacity>>13)) {
+
+ // FIXME: sense buffer?
+
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ pba = info->lba_to_pba[lba];
+
+ // if pba is 0, either it's really 0, in which case
+ // the pba-to-lba map for pba 0 will be the lba,
+ // or that lba doesn't exist.
+
+ if (pba==0 && info->pba_to_lba[0] != lba) {
+
+ // FIXME: sense buffer?
+
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ US_DEBUGP("READ_10: read block %04X (LBA %04X) page %01X"
+ " pages %d\n",
+ pba, lba, page, pages);
+
+ return sddr09_read_data(us,
+ ((pba<<4)+page)*info->pagesize, pages,
+ ptr, srb->use_sg);
+ }
+
+ // Pass TEST_UNIT_READY and REQUEST_SENSE through
+
+ if (srb->cmnd[0] != TEST_UNIT_READY &&
+ srb->cmnd[0] != REQUEST_SENSE)
+ return USB_STOR_TRANSPORT_ERROR; // FIXME: sense buffer?
+
for (; srb->cmd_len<12; srb->cmd_len++)
srb->cmnd[srb->cmd_len] = 0;
@@ -622,7 +912,7 @@ int sddr09_transport(Scsi_Cmnd *srb, struct us_data *us)
srb->request_bufflen);
result = sddr09_bulk_transport(us,
- NULL, 0, srb->sc_data_direction,
+ srb->sc_data_direction,
srb->request_buffer,
srb->request_bufflen, srb->use_sg);