patch-2.4.0-test8 linux/drivers/usb/storage/sddr09.c

Next file: linux/drivers/usb/storage/sddr09.h
Previous file: linux/drivers/usb/storage/scsiglue.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.0-test7/linux/drivers/usb/storage/sddr09.c linux/drivers/usb/storage/sddr09.c
@@ -1,10 +1,12 @@
 /* Driver for SanDisk SDDR-09 SmartMedia reader
  *
+ * $Id: sddr09.c,v 1.10 2000/08/25 00:13:51 mdharm Exp $
+ *
  * SDDR09 driver v0.1:
  *
  * First release
  *
- * Current development and maintainance by:
+ * Current development and maintenance by:
  *   (c) 2000 Robert Baruch (autophile@dol.net)
  *
  * The SanDisk SDDR-09 SmartMedia reader uses the Shuttle EUSB-01 chip.
@@ -316,9 +318,9 @@
 
 	// Figure out the initial LBA and page
 
-	pba = (address/info->pagesize)>>4;
+	pba = address >> (info->pageshift + info->blockshift);
 	lba = info->pba_to_lba[pba];
-	page = (address/info->pagesize)&0x0F;
+	page = (address >> info->pageshift) & info->blockmask;
 
 	// This could be made much more efficient by checking for
 	// contiguous LBA's. Another exercise left to the student.
@@ -329,15 +331,16 @@
 
 		// Read as many sectors as possible in this block
 
-		pages = 0x10-page;
+		pages = info->blocksize - page;
 		if (pages > sectors)
 			pages = sectors;
 
-		US_DEBUGP("Read %01X pages, from PBA %04X"
-			" (LBA %04X) page %01X\n",
+		US_DEBUGP("Read %02X pages, from PBA %04X"
+			" (LBA %04X) page %02X\n",
 			pages, pba, lba, page);
 
-		address = ((pba<<4)+page)*info->pagesize;
+		address = ( (pba << info->blockshift) + page ) << 
+			info->pageshift;
 
 		// Unlike in the documentation, the address is in
 		// words of 2 bytes.
@@ -370,7 +373,7 @@
 
 		result = sddr09_bulk_transport(us,
 			SCSI_DATA_READ, ptr,
-			pages*info->pagesize, 0);
+			pages<<info->pageshift, 0);
 
 		if (result != USB_STOR_TRANSPORT_GOOD) {
 			if (use_sg)
@@ -381,7 +384,7 @@
 		page = 0;
 		lba++;
 		sectors -= pages;
-		ptr += pages*info->pagesize;
+		ptr += (pages << info->pageshift);
 	}
 
 	if (use_sg) {
@@ -435,7 +438,7 @@
 
 	result = sddr09_bulk_transport(us,
 		SCSI_DATA_READ, content,
-		blocks*0x40, use_sg);
+		blocks<<6, use_sg); // 0x40 bytes per block
 
 	US_DEBUGP("Result for bulk read in read_control %d\n",
 		result);
@@ -525,7 +528,7 @@
 }
 
 unsigned long sddr09_get_capacity(struct us_data *us,
-		unsigned int *pagesize) {
+		unsigned int *pagesize, unsigned int *blocksize) {
 
 	unsigned char manufacturerID;
 	unsigned char deviceID;
@@ -547,6 +550,7 @@
 	US_DEBUGP("Manuf  ID = %02X\n", manufacturerID);
 
 	*pagesize = 512;
+	*blocksize = 16;
 
 	switch (deviceID) {
 
@@ -556,8 +560,8 @@
 		*pagesize = 256;
 		return 0x00100000;
 
-	case 0x5d: // 2MB
-	case 0xea: 
+	case 0xea: // 2MB
+	case 0x5d: // 5d is a ROM card with pagesize 512.
 	case 0x64:
 		if (deviceID!=0x5D)
 			*pagesize = 256;
@@ -574,9 +578,11 @@
 		return 0x00800000;
 
 	case 0x73: // 16MB
+		*blocksize = 32;
 		return 0x01000000;
 
 	case 0x75: // 32MB
+		*blocksize = 32;
 		return 0x02000000;
 
 	default: // unknown
@@ -587,7 +593,7 @@
 
 int sddr09_read_map(struct us_data *us) {
 
-	unsigned char *control;
+	struct scatterlist *sg;
 	struct sddr09_card_info *info = (struct sddr09_card_info *)(us->extra);
 	int numblocks;
 	int i;
@@ -599,25 +605,60 @@
 		1, 0, 0, 1, 0, 1, 1, 0
 	};
 	int result;
+	int alloc_len;
+	int alloc_blocks;
 
 	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);
+	// read 64 (1<<6) bytes for every block 
+	// ( 1 << ( blockshift + pageshift ) bytes)
+	//	 of capacity:
+	// (1<<6)*capacity/(1<<(b+p)) =
+	// ((1<<6)*capacity)>>(b+p) =
+	// capacity>>(b+p-6)
+
+	alloc_len = info->capacity >> 
+		(info->blockshift + info->pageshift - 6);
+
+	// Allocate a number of scatterlist structures according to
+	// the number of 128k blocks in the alloc_len. Adding 128k-1
+	// and then dividing by 128k gives the correct number of blocks.
+	// 128k = 1<<17
+
+	alloc_blocks = (alloc_len + (1<<17) - 1) >> 17;
+	sg = kmalloc(alloc_blocks*sizeof(struct scatterlist),
+		GFP_KERNEL);
+	if (sg == NULL)
+		return 0;
 
+	for (i=0; i<alloc_blocks; i++) {
+		if (i<alloc_blocks-1) {
+			sg[i].address = kmalloc( (1<<17), GFP_KERNEL );
+			sg[i].length = (1<<17);
+		} else {
+			sg[i].address = kmalloc(alloc_len, GFP_KERNEL);
+			sg[i].length = alloc_len;
+		}
+		alloc_len -= sg[i].length;
+	}
+	for (i=0; i<alloc_blocks; i++)
+		if (sg[i].address == NULL) {
+			for (i=0; i<alloc_blocks; i++)
+				if (sg[i].address != NULL)
+					kfree(sg[i].address);
+			kfree(sg);
+			return 0;
+		}
 
-	numblocks = info->capacity>>13;
+	numblocks = info->capacity >> (info->blockshift + info->pageshift);
 
 	if ( (result = sddr09_read_control(us, 0, numblocks,
-			control, 0)) !=
+			(unsigned char *)sg, alloc_blocks)) !=
 			USB_STOR_TRANSPORT_GOOD) {
-		kfree(control);
+		for (i=0; i<alloc_blocks; i++)
+			kfree(sg[i].address);
+		kfree(sg);
 		return -1;
 	}
 
@@ -629,11 +670,28 @@
 		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);
+
+	if (info->lba_to_pba == NULL || info->pba_to_lba == NULL) {
+		if (info->lba_to_pba != NULL)
+			kfree(info->lba_to_pba);
+		if (info->pba_to_lba != NULL)
+			kfree(info->pba_to_lba);
+		info->lba_to_pba = NULL;
+		info->pba_to_lba = NULL;
+		for (i=0; i<alloc_blocks; i++)
+			kfree(sg[i].address);
+		kfree(sg);
+		return 0;
+	}
+
 	memset(info->lba_to_pba, 0, numblocks*sizeof(int));
 	memset(info->pba_to_lba, 0, numblocks*sizeof(int));
 
+	// Each block is 64 bytes of control data, so block i is located in
+	// scatterlist block i*64/128k = i*(2^6)*(2^-17) = i*(2^-11)
+
 	for (i=0; i<numblocks; i++) {
-		ptr = control+64*i;
+		ptr = sg[i>>11].address+(i<<6);
 		if (ptr[0]!=0xFF || ptr[1]!=0xFF || ptr[2]!=0xFF ||
 		    ptr[3]!=0xFF || ptr[4]!=0xFF || ptr[5]!=0xFF)
 			continue;
@@ -655,20 +713,30 @@
 
 		lba = (lba&0x07FF)>>1;
 
+			/* Every 1024 physical blocks, the LBA numbers
+			 * go back to zero, but are within a higher
+			 * block of LBA's. In other words, in blocks
+			 * 1024-2047 you will find LBA 0-1023 which are
+			 * really LBA 1024-2047.
+			 */
+
+		lba += (i&~0x3FF);
+
 		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);
+		if (lba<0x10)
+			US_DEBUGP("LBA %04X <-> PBA %04X\n", lba, i);
 
 		info->pba_to_lba[i] = lba;
 		info->lba_to_pba[lba] = i;
 	}
 
-	kfree(control);
+	for (i=0; i<alloc_blocks; i++)
+		kfree(sg[i].address);
+	kfree(sg);
 	return 0;
 }
 
@@ -687,6 +755,9 @@
 		0x03, 0x20, 0, 0, 0x0e, 0, 0, 0, 0, 0, 0, 0
 	};
 
+	// What the hey is all this for? Doesn't seem to
+	// affect the device, so we won't do device inits.
+
 	if ( (result = sddr09_send_control(us, command, data, 2)) !=
 			USB_STOR_TRANSPORT_GOOD)
 		return result;
@@ -741,11 +812,7 @@
 	int i;
 	char string[64];
 	unsigned char inquiry_response[36] = {
-		0x00, 0x80, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x00,
-		'S', 'a', 'n', 'D', 'i', 's', 'k', ' ',
-		'I', 'm', 'a', 'g', 'e', 'M', 'a', 't',
-		'e', ' ', 'S', 'D', 'D', 'R', '0', '9',
-		' ', ' ', ' ', ' '
+		0x00, 0x80, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x00
 	};
 	unsigned char mode_page_01[12] = {
 		0x01, 0x0a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
@@ -758,17 +825,11 @@
 	unsigned short pages;
 	struct sddr09_card_info *info = (struct sddr09_card_info *)(us->extra);
 
-/*
-	if (us->flags & US_FL_NEED_INIT) {
-		US_DEBUGP("SDDR-09: initializing\n");
-		init_sddr09(us);
-		us->flags &= ~US_FL_NEED_INIT;
-	}
-*/
-
 	if (!us->extra) {
 		us->extra = kmalloc(
 			sizeof(struct sddr09_card_info), GFP_KERNEL);
+		if (!us->extra)
+			return USB_STOR_TRANSPORT_ERROR;
 		memset(us->extra, 0, sizeof(struct sddr09_card_info));
 		us->extra_destructor = sddr09_card_info_destructor;
 	}
@@ -779,14 +840,29 @@
 	   respond to INQUIRY commands */
 
 	if (srb->cmnd[0] == INQUIRY) {
-		memcpy(ptr, inquiry_response, 36);
+		memset(inquiry_response+8, 0, 28);
+		fill_inquiry_response(us, inquiry_response, 36);
 		return USB_STOR_TRANSPORT_GOOD;
 	}
 
 	if (srb->cmnd[0] == READ_CAPACITY) {
 
-		capacity = sddr09_get_capacity(us, &info->pagesize);
+		capacity = sddr09_get_capacity(us, &info->pagesize,
+			&info->blocksize);
+
+		if (!capacity)
+			return USB_STOR_TRANSPORT_FAILED;
+
 		info->capacity = capacity;
+		for (info->pageshift=1; 
+			(info->pagesize>>info->pageshift);
+			info->pageshift++);
+		info->pageshift--;
+		for (info->blockshift=1; 
+			(info->blocksize>>info->blockshift);
+			info->blockshift++);
+		info->blockshift--;
+		info->blockmask = (1<<info->blockshift)-1;
 
 		// Last page in the card
 
@@ -836,12 +912,14 @@
 
 		// convert page to block and page-within-block
 
-		lba = page>>4;
-		page = page&0x0F;
+		lba = page >> info->blockshift;
+		page = page & info->blockmask;
 
 		// locate physical block corresponding to logical block
 
-		if (lba>=(info->capacity>>13)) {
+		if (lba >=
+			(info->capacity >> 
+				(info->pageshift + info->blockshift) ) ) {
 
 			// FIXME: sense buffer?
 
@@ -866,8 +944,8 @@
 			pba, lba, page, pages);
 
 		return sddr09_read_data(us,
-			((pba<<4)+page)*info->pagesize, pages,
-			ptr, srb->use_sg);
+			( (pba << info->blockshift) + page) << info->pageshift,
+			pages, ptr, srb->use_sg);
 	}
 
 	// Pass TEST_UNIT_READY and REQUEST_SENSE through

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)