ChangeSet 1.1504.2.16, 2003/12/08 17:45:52-08:00, stern@rowland.harvard.edu

[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.


 drivers/usb/storage/sddr09.c |  125 ++++++++++++++++++++++++-------------------
 1 files changed, 70 insertions(+), 55 deletions(-)


diff -Nru a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c
--- a/drivers/usb/storage/sddr09.c	Mon Dec 29 14:25:16 2003
+++ b/drivers/usb/storage/sddr09.c	Mon Dec 29 14:25:16 2003
@@ -28,7 +28,6 @@
  */
 
 #include "transport.h"
-#include "raw_bulk.h"
 #include "protocol.h"
 #include "usb.h"
 #include "debug.h"
@@ -664,30 +663,27 @@
 sddr09_read_data(struct us_data *us,
 		 unsigned long address,
 		 unsigned int sectors,
-		 unsigned char *content,
+		 unsigned char *buffer,
 		 int use_sg) {
 
 	struct sddr09_card_info *info = (struct sddr09_card_info *) us->extra;
 	unsigned int lba, maxlba, pba;
 	unsigned int page, pages;
-	unsigned char *buffer = NULL;
-	unsigned char *ptr;
-	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;
+	unsigned int len, index, offset;
+	int result;
 
-	buffer = (use_sg ? kmalloc(len, GFP_NOIO) : content);
-	if (buffer == NULL)
-		return USB_STOR_TRANSPORT_ERROR;
+	// Since we only read in one block at a time, we have to create
+	// a bounce buffer if the transfer uses scatter-gather.
 
-	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
 	lba = address >> info->blockshift;
@@ -698,13 +694,13 @@
 	// contiguous LBA's. Another exercise left to the student.
 
 	result = USB_STOR_TRANSPORT_GOOD;
+	index = offset = 0;
 
 	while (sectors > 0) {
 
 		/* Find number of pages we can read in this block */
-		pages = info->blocksize - page;
-		if (pages > sectors)
-			pages = sectors;
+		pages = min(sectors, info->blocksize - page);
+		len = pages << info->pageshift;
 
 		/* Not overflowing capacity? */
 		if (lba >= maxlba) {
@@ -727,7 +723,7 @@
 			   Instead of returning USB_STOR_TRANSPORT_ERROR
 			   it is better to return all zero data. */
 
-			memset(ptr, 0, pages << info->pageshift);
+			memset(buffer, 0, len);
 
 		} else {
 			US_DEBUGP("Read %d pages, from PBA %d"
@@ -738,20 +734,21 @@
 				info->pageshift;
 
 			result = sddr09_read20(us, address>>1,
-					       pages, info->pageshift, ptr, 0);
+					pages, info->pageshift, buffer, 0);
 			if (result != USB_STOR_TRANSPORT_GOOD)
 				break;
 		}
+		if (use_sg)
+			usb_stor_access_xfer_buf(buffer, len, us->srb,
+					&index, &offset, TO_XFER_BUF);
+		else
+			buffer += len;
 
 		page = 0;
 		lba++;
 		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)
 		kfree(buffer);
 
@@ -787,13 +784,13 @@
 static int
 sddr09_write_lba(struct us_data *us, unsigned int lba,
 		 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;
 	unsigned long address;
 	unsigned int pba, lbap;
-	unsigned int pagelen, blocklen;
-	unsigned char *blockbuffer, *bptr, *cptr, *xptr;
+	unsigned int pagelen;
+	unsigned char *bptr, *cptr, *xptr;
 	unsigned char ecc[3];
 	int i, result, isnew;
 
@@ -822,19 +819,13 @@
 	}
 
 	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 */
 	address = (pba << (info->pageshift + info->blockshift));
 	result = sddr09_read22(us, address>>1, info->blocksize,
 			       info->pageshift, blockbuffer, 0);
 	if (result != USB_STOR_TRANSPORT_GOOD)
-		goto err;
+		return result;
 
 	/* check old contents and fill lba */
 	for (i = 0; i < info->blocksize; i++) {
@@ -893,11 +884,6 @@
 		int result2 = sddr09_test_unit_ready(us);
 	}
 #endif
- err:
-	kfree(blockbuffer);
-
-	/* TODO: instead of doing kmalloc/kfree for each block,
-	   add a bufferpointer to the info structure */
 
 	return result;
 }
@@ -906,49 +892,77 @@
 sddr09_write_data(struct us_data *us,
 		  unsigned long address,
 		  unsigned int sectors,
-		  unsigned char *content,
+		  unsigned char *buffer,
 		  int use_sg) {
 
 	struct sddr09_card_info *info = (struct sddr09_card_info *) us->extra;
 	unsigned int lba, page, pages;
-	unsigned char *buffer = NULL;
-	unsigned char *ptr;
-	int result, len;
+	unsigned int pagelen, blocklen;
+	unsigned char *blockbuffer;
+	unsigned int len, index, offset;
+	int result;
+
+	// blockbuffer is used for reading in the old data, overwriting
+	// with the new data, and performing ECC calculations
 
-	len = sectors*info->pagesize;
+	/* TODO: instead of doing kmalloc/kfree for each write,
+	   add a bufferpointer to the info structure */
 
-	buffer = us_copy_from_sgbuf_all(content, len, use_sg);
-	if (buffer == NULL)
+	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;
+	}
 
-	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
 	lba = address >> info->blockshift;
 	page = (address & info->blockmask);
 
 	result = USB_STOR_TRANSPORT_GOOD;
+	index = offset = 0;
 
 	while (sectors > 0) {
 
 		// Write as many sectors as possible in this block
 
-		pages = info->blocksize - page;
-		if (pages > sectors)
-			pages = sectors;
+		pages = min(sectors, info->blocksize - page);
+		len = (pages << info->pageshift);
+		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)
 			break;
+		if (!use_sg)
+			buffer += len;
 
 		page = 0;
 		lba++;
 		sectors -= pages;
-		ptr += (pages << info->pageshift);
 	}
 
 	if (use_sg)
 		kfree(buffer);
+	kfree(blockbuffer);
 
 	return result;
 }
@@ -1143,6 +1157,7 @@
 	info->pba_to_lba = kmalloc(numblocks*sizeof(int), GFP_NOIO);
 
 	if (info->lba_to_pba == NULL || info->pba_to_lba == NULL) {
+		printk("sddr09_read_map: out of memory\n");
 		result = -1;
 		goto done;
 	}
