Commit b3fa3588 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

[PATCH] USB storage: Change sddr09 to use the new s-g access routine

This patch updates the sddr09 driver to use the new scatter-gather access
routine.  After installing it, the user who experienced memory access
violations says everything is now working properly.
parent f3badfb7
...@@ -28,7 +28,6 @@ ...@@ -28,7 +28,6 @@
*/ */
#include "transport.h" #include "transport.h"
#include "raw_bulk.h"
#include "protocol.h" #include "protocol.h"
#include "usb.h" #include "usb.h"
#include "debug.h" #include "debug.h"
...@@ -664,30 +663,27 @@ static int ...@@ -664,30 +663,27 @@ static int
sddr09_read_data(struct us_data *us, sddr09_read_data(struct us_data *us,
unsigned long address, unsigned long address,
unsigned int sectors, unsigned int sectors,
unsigned char *content, unsigned char *buffer,
int use_sg) { int use_sg) {
struct sddr09_card_info *info = (struct sddr09_card_info *) us->extra; struct sddr09_card_info *info = (struct sddr09_card_info *) us->extra;
unsigned int lba, maxlba, pba; unsigned int lba, maxlba, pba;
unsigned int page, pages; unsigned int page, pages;
unsigned char *buffer = NULL; unsigned int len, index, offset;
unsigned char *ptr; int result;
int result, len;
// 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;
buffer = (use_sg ? kmalloc(len, GFP_NOIO) : content); // Since we only read in one block at a time, we have to create
if (buffer == NULL) // a bounce buffer if the transfer uses scatter-gather.
return USB_STOR_TRANSPORT_ERROR;
ptr = buffer; if (use_sg) {
len = min(sectors, (unsigned int) info->blocksize) *
info->pagesize;
buffer = kmalloc(len, GFP_NOIO);
if (buffer == NULL) {
printk("sddr09_read_data: Out of memory\n");
return USB_STOR_TRANSPORT_ERROR;
}
}
// Figure out the initial LBA and page // Figure out the initial LBA and page
lba = address >> info->blockshift; lba = address >> info->blockshift;
...@@ -698,13 +694,13 @@ sddr09_read_data(struct us_data *us, ...@@ -698,13 +694,13 @@ sddr09_read_data(struct us_data *us,
// contiguous LBA's. Another exercise left to the student. // contiguous LBA's. Another exercise left to the student.
result = USB_STOR_TRANSPORT_GOOD; result = USB_STOR_TRANSPORT_GOOD;
index = offset = 0;
while (sectors > 0) { while (sectors > 0) {
/* Find number of pages we can read in this block */ /* Find number of pages we can read in this block */
pages = info->blocksize - page; pages = min(sectors, info->blocksize - page);
if (pages > sectors) len = pages << info->pageshift;
pages = sectors;
/* Not overflowing capacity? */ /* Not overflowing capacity? */
if (lba >= maxlba) { if (lba >= maxlba) {
...@@ -727,7 +723,7 @@ sddr09_read_data(struct us_data *us, ...@@ -727,7 +723,7 @@ sddr09_read_data(struct us_data *us,
Instead of returning USB_STOR_TRANSPORT_ERROR Instead of returning USB_STOR_TRANSPORT_ERROR
it is better to return all zero data. */ it is better to return all zero data. */
memset(ptr, 0, pages << info->pageshift); memset(buffer, 0, len);
} else { } else {
US_DEBUGP("Read %d pages, from PBA %d" US_DEBUGP("Read %d pages, from PBA %d"
...@@ -738,20 +734,21 @@ sddr09_read_data(struct us_data *us, ...@@ -738,20 +734,21 @@ sddr09_read_data(struct us_data *us,
info->pageshift; info->pageshift;
result = sddr09_read20(us, address>>1, result = sddr09_read20(us, address>>1,
pages, info->pageshift, ptr, 0); pages, info->pageshift, buffer, 0);
if (result != USB_STOR_TRANSPORT_GOOD) if (result != USB_STOR_TRANSPORT_GOOD)
break; break;
} }
if (use_sg)
usb_stor_access_xfer_buf(buffer, len, us->srb,
&index, &offset, TO_XFER_BUF);
else
buffer += len;
page = 0; page = 0;
lba++; lba++;
sectors -= pages; sectors -= pages;
ptr += (pages << info->pageshift);
} }
if (use_sg && result == USB_STOR_TRANSPORT_GOOD)
us_copy_to_sgbuf_all(buffer, len, content, use_sg);
if (use_sg) if (use_sg)
kfree(buffer); kfree(buffer);
...@@ -787,13 +784,13 @@ sddr09_find_unused_pba(struct sddr09_card_info *info, unsigned int lba) { ...@@ -787,13 +784,13 @@ sddr09_find_unused_pba(struct sddr09_card_info *info, unsigned int lba) {
static int static int
sddr09_write_lba(struct us_data *us, unsigned int lba, sddr09_write_lba(struct us_data *us, unsigned int lba,
unsigned int page, unsigned int pages, unsigned int page, unsigned int pages,
unsigned char *ptr) { unsigned char *ptr, unsigned char *blockbuffer) {
struct sddr09_card_info *info = (struct sddr09_card_info *) us->extra; struct sddr09_card_info *info = (struct sddr09_card_info *) us->extra;
unsigned long address; unsigned long address;
unsigned int pba, lbap; unsigned int pba, lbap;
unsigned int pagelen, blocklen; unsigned int pagelen;
unsigned char *blockbuffer, *bptr, *cptr, *xptr; unsigned char *bptr, *cptr, *xptr;
unsigned char ecc[3]; unsigned char ecc[3];
int i, result, isnew; int i, result, isnew;
...@@ -822,19 +819,13 @@ sddr09_write_lba(struct us_data *us, unsigned int lba, ...@@ -822,19 +819,13 @@ sddr09_write_lba(struct us_data *us, unsigned int lba,
} }
pagelen = (1 << info->pageshift) + (1 << CONTROL_SHIFT); pagelen = (1 << info->pageshift) + (1 << CONTROL_SHIFT);
blocklen = (pagelen << info->blockshift);
blockbuffer = kmalloc(blocklen, GFP_NOIO);
if (!blockbuffer) {
printk("sddr09_write_lba: Out of memory\n");
return USB_STOR_TRANSPORT_ERROR;
}
/* read old contents */ /* read old contents */
address = (pba << (info->pageshift + info->blockshift)); address = (pba << (info->pageshift + info->blockshift));
result = sddr09_read22(us, address>>1, info->blocksize, result = sddr09_read22(us, address>>1, info->blocksize,
info->pageshift, blockbuffer, 0); info->pageshift, blockbuffer, 0);
if (result != USB_STOR_TRANSPORT_GOOD) if (result != USB_STOR_TRANSPORT_GOOD)
goto err; return result;
/* check old contents and fill lba */ /* check old contents and fill lba */
for (i = 0; i < info->blocksize; i++) { for (i = 0; i < info->blocksize; i++) {
...@@ -893,11 +884,6 @@ sddr09_write_lba(struct us_data *us, unsigned int lba, ...@@ -893,11 +884,6 @@ sddr09_write_lba(struct us_data *us, unsigned int lba,
int result2 = sddr09_test_unit_ready(us); int result2 = sddr09_test_unit_ready(us);
} }
#endif #endif
err:
kfree(blockbuffer);
/* TODO: instead of doing kmalloc/kfree for each block,
add a bufferpointer to the info structure */
return result; return result;
} }
...@@ -906,49 +892,77 @@ static int ...@@ -906,49 +892,77 @@ static int
sddr09_write_data(struct us_data *us, sddr09_write_data(struct us_data *us,
unsigned long address, unsigned long address,
unsigned int sectors, unsigned int sectors,
unsigned char *content, unsigned char *buffer,
int use_sg) { int use_sg) {
struct sddr09_card_info *info = (struct sddr09_card_info *) us->extra; struct sddr09_card_info *info = (struct sddr09_card_info *) us->extra;
unsigned int lba, page, pages; unsigned int lba, page, pages;
unsigned char *buffer = NULL; unsigned int pagelen, blocklen;
unsigned char *ptr; unsigned char *blockbuffer;
int result, len; unsigned int len, index, offset;
int result;
len = sectors*info->pagesize; // blockbuffer is used for reading in the old data, overwriting
// with the new data, and performing ECC calculations
buffer = us_copy_from_sgbuf_all(content, len, use_sg); /* TODO: instead of doing kmalloc/kfree for each write,
if (buffer == NULL) add a bufferpointer to the info structure */
pagelen = (1 << info->pageshift) + (1 << CONTROL_SHIFT);
blocklen = (pagelen << info->blockshift);
blockbuffer = kmalloc(blocklen, GFP_NOIO);
if (!blockbuffer) {
printk("sddr09_write_data: Out of memory\n");
return USB_STOR_TRANSPORT_ERROR; return USB_STOR_TRANSPORT_ERROR;
}
ptr = buffer; // Since we don't write the user data directly to the device,
// we have to create a bounce buffer if the transfer uses
// scatter-gather.
if (use_sg) {
len = min(sectors, (unsigned int) info->blocksize) *
info->pagesize;
buffer = kmalloc(len, GFP_NOIO);
if (buffer == NULL) {
printk("sddr09_write_data: Out of memory\n");
kfree(blockbuffer);
return USB_STOR_TRANSPORT_ERROR;
}
}
// Figure out the initial LBA and page // Figure out the initial LBA and page
lba = address >> info->blockshift; lba = address >> info->blockshift;
page = (address & info->blockmask); page = (address & info->blockmask);
result = USB_STOR_TRANSPORT_GOOD; result = USB_STOR_TRANSPORT_GOOD;
index = offset = 0;
while (sectors > 0) { while (sectors > 0) {
// Write as many sectors as possible in this block // Write as many sectors as possible in this block
pages = info->blocksize - page; pages = min(sectors, info->blocksize - page);
if (pages > sectors) len = (pages << info->pageshift);
pages = sectors; if (use_sg)
usb_stor_access_xfer_buf(buffer, len, us->srb,
&index, &offset, FROM_XFER_BUF);
result = sddr09_write_lba(us, lba, page, pages, ptr); result = sddr09_write_lba(us, lba, page, pages,
buffer, blockbuffer);
if (result != USB_STOR_TRANSPORT_GOOD) if (result != USB_STOR_TRANSPORT_GOOD)
break; break;
if (!use_sg)
buffer += len;
page = 0; page = 0;
lba++; lba++;
sectors -= pages; sectors -= pages;
ptr += (pages << info->pageshift);
} }
if (use_sg) if (use_sg)
kfree(buffer); kfree(buffer);
kfree(blockbuffer);
return result; return result;
} }
...@@ -1143,6 +1157,7 @@ sddr09_read_map(struct us_data *us) { ...@@ -1143,6 +1157,7 @@ sddr09_read_map(struct us_data *us) {
info->pba_to_lba = kmalloc(numblocks*sizeof(int), GFP_NOIO); info->pba_to_lba = kmalloc(numblocks*sizeof(int), GFP_NOIO);
if (info->lba_to_pba == NULL || info->pba_to_lba == NULL) { if (info->lba_to_pba == NULL || info->pba_to_lba == NULL) {
printk("sddr09_read_map: out of memory\n");
result = -1; result = -1;
goto done; goto done;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment