Recently I have written three drivers for USB SmartMedia readers.
One reached 2.5.13. One is visible on ftp.XX.kernel.org.
The third was this evening.
Such drivers share a lot of code, and instead of having N copies
of the code (as is already today visible in usb/storage) I separated
out all that was common. Let me show you here some fragments of code.
Note, Matt, Greg: this is not a code submission, it is just a request
for comments. This may not even compile. Sooner or later I'll submit
some version of this.
The first fragment is smartmedia.c.
It knows about the structure of a smartmedia card, but not about the
commands the various drivers use to read and write.
----- smartmedia.h -----
#ifndef _SMARTMEDIA_H_
#define _SMARTMEDIA_H_
#include "usb.h"
extern unsigned char nand_parity[256];
extern void nand_compute_ecc(unsigned char *data, unsigned char *ecc);
extern void nand_store_ecc (unsigned char *data, unsigned char *ecc);
extern int nand_compare_ecc(unsigned char *data, unsigned char *ecc);
struct sm_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 controlshift; /* log2 of extra bytes/page */
int lbact; /* number of available pages */
int flags;
#define SMARTMEDIA_WP 1 /* write protected */
#define SMARTMEDIA_RO 2 /* set to read-only */
};
struct sm_ops {
int (*sm_init)(struct us_data *us);
int (*sm_read_deviceID)(struct us_data *us, unsigned char *id4);
int (*sm_read_map)(struct us_data *us);
int (*sm_read_control)(struct us_data *us,
unsigned long address, unsigned int sectorct,
unsigned char *buf, int use_sg);
int (*sm_read_data)(struct us_data *us,
unsigned long address, unsigned int sectorct,
unsigned char *buf, int use_sg);
int (*sm_write_lba)(struct us_data *us, unsigned int lba,
unsigned int page, unsigned int pagect,
unsigned char *buf);
int (*sm_test_unit_ready)(struct us_data *us,
Scsi_Cmnd *srb, int cmdlen);
int (*sm_request_sense)(struct us_data *us,
Scsi_Cmnd *srb, int cmdlen);
};
extern int smartmedia_raw;
extern int
smartmedia_transport(Scsi_Cmnd *srb, struct us_data *us, struct sm_ops *sm);
extern int
smartmedia_allocate_map(struct sm_card_info *info);
extern unsigned int
smartmedia_find_unused_pba(struct sm_card_info *info, unsigned int lba);
/*
* LBA and PBA are unsigned ints. Special values.
*/
#define UNDEF 0xffffffff
#define SPARE 0xfffffffe
#define UNUSABLE 0xfffffffd
#endif
----- smartmedia.c -----
/*
* SmartMedia driver, aeb, koninginnedag 2002
*
* (c) 2000, 2001 Robert Baruch ([EMAIL PROTECTED])
* (c) 2002 Andries Brouwer ([EMAIL PROTECTED])
*/
#include <linux/config.h>
#include <linux/module.h>
#include "transport.h"
#include "smartmedia.h"
#include "debug.h"
/* #define US_DEBUGP printk */
#define short_pack(lsb,msb) (((u16)(lsb)) | (((u16)(msb))<<8))
#define LSB_of(s) ((s)&0xFF)
#define MSB_of(s) ((s)>>8)
/*
* The below two parameters are meant for debugging use only.
* They work globally, not per device.
*/
/* direct access to card */
int smartmedia_raw = 0;
MODULE_PARM(smartmedia_raw, "i");
MODULE_PARM_DESC(smartmedia_raw, "make PBA=LBA");
/* each 512-byte sector has 64 bytes control */
/* the control is given in PBA order */
static int smartmedia_control = 0;
MODULE_PARM(smartmedia_control, "i");
MODULE_PARM_DESC(smartmedia_control, "read control");
/*
* First some stuff that does not belong here:
* data on SmartMedia and other cards, unrelated to USB.
* Similar stuff occurs in <linux/mtd/nand_ids.h>.
*/
struct nand_flash_dev {
int model_id;
int chipshift; /* 1<<cs bytes total capacity */
char pageshift; /* 1<<ps bytes in a page */
char blockshift; /* 1<<bs pages in an erase block */
char zoneshift; /* 1<<zs blocks in a zone */
/* # of logical blocks is 125/128 of this */
char pageadrlen; /* length of an address in bytes - 1 */
};
/*
* NAND Flash Manufacturer ID Codes
*/
#define NAND_MFR_AMD 0x01
#define NAND_MFR_TOSHIBA 0x98
#define NAND_MFR_SAMSUNG 0xec
static inline char *
nand_flash_manufacturer(int manuf_id) {
switch(manuf_id) {
case NAND_MFR_AMD:
return "AMD";
case NAND_MFR_TOSHIBA:
return "Toshiba";
case NAND_MFR_SAMSUNG:
return "Samsung";
default:
return "unknown";
}
}
/*
* It looks like it is unnecessary to attach manufacturer to the
* remaining data: SSFDC prescribes manufacturer-independent id codes.
*/
static struct nand_flash_dev nand_flash_ids[] = {
/* NAND flash - these I verified */
{ 0x6e, 20, 8, 4, 8, 2}, /* 1 MB, 5V */
{ 0xe8, 20, 8, 4, 8, 2}, /* 1 MB */
{ 0xec, 20, 8, 4, 8, 2}, /* 1 MB */
{ 0x64, 21, 8, 4, 9, 2}, /* 2 MB, 5V */
{ 0xea, 21, 8, 4, 9, 2}, /* 2 MB */
{ 0x6b, 22, 9, 4, 9, 2}, /* 4 MB, 5V */
{ 0xe3, 22, 9, 4, 9, 2}, /* 4 MB */
{ 0xe5, 22, 9, 4, 9, 2}, /* 4 MB, 3.3V or 5V */
{ 0xe6, 23, 9, 4, 10, 2}, /* 8 MB */
{ 0x73, 24, 9, 5, 10, 2}, /* 16 MB */
{ 0x75, 25, 9, 5, 10, 2}, /* 32 MB */
{ 0x76, 26, 9, 5, 10, 3}, /* 64 MB */
{ 0x79, 27, 9, 5, 10, 3}, /* 128 MB */
/* Maybe also 48 MB, 96 MB and 256 MB cards do exist */
/* MASK ROM - from unknown source */
{ 0x5d, 21, 9, 4, 8, 2}, /* 2 MB */
{ 0xd5, 22, 9, 4, 9, 2}, /* 4 MB */
{ 0xd6, 23, 9, 4, 10, 2}, /* 8 MB */
{ 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++)
if (nand_flash_ids[i].model_id == id)
return &(nand_flash_ids[i]);
return NULL;
}
/*
* ECC computation.
*/
unsigned char nand_parity[256];
static unsigned char nand_ecc2[256];
static void
nand_init_ecc(void) {
static int nand_inited = 0;
int i, j, a;
if (nand_inited)
return;
nand_inited = 1;
nand_parity[0] = 0;
for (i = 1; i < 256; i++)
nand_parity[i] = (nand_parity[i&(i-1)] ^ 1);
for (i = 0; i < 256; i++) {
a = 0;
for (j = 0; j < 8; j++) {
if (i & (1<<j)) {
if ((j & 1) == 0)
a ^= 0x04;
if ((j & 2) == 0)
a ^= 0x10;
if ((j & 4) == 0)
a ^= 0x40;
}
}
nand_ecc2[i] = ~(a ^ (a<<1) ^ (nand_parity[i] ? 0xa8 : 0));
}
}
/* compute 3-byte ecc on 256 bytes */
void
nand_compute_ecc(unsigned char *data, unsigned char *ecc) {
int i, j, a;
unsigned char par, bit, bits[8];
par = 0;
for (j = 0; j < 8; j++)
bits[j] = 0;
/* collect 16 checksum bits */
for (i = 0; i < 256; i++) {
par ^= data[i];
bit = nand_parity[data[i]];
for (j = 0; j < 8; j++)
if ((i & (1<<j)) == 0)
bits[j] ^= bit;
}
/* put 4+4+4 = 12 bits in the ecc */
a = (bits[3] << 6) + (bits[2] << 4) + (bits[1] << 2) + bits[0];
ecc[0] = ~(a ^ (a<<1) ^ (nand_parity[par] ? 0xaa : 0));
a = (bits[7] << 6) + (bits[6] << 4) + (bits[5] << 2) + bits[4];
ecc[1] = ~(a ^ (a<<1) ^ (nand_parity[par] ? 0xaa : 0));
ecc[2] = nand_ecc2[par];
}
int
nand_compare_ecc(unsigned char *data, unsigned char *ecc) {
return (data[0] == ecc[0] && data[1] == ecc[1] && data[2] == ecc[2]);
}
void
nand_store_ecc(unsigned char *data, unsigned char *ecc) {
memcpy(data, ecc, 3);
}
/*
* The actual driver starts here.
*/
static inline void *
kcmalloc(size_t len, int prio) {
void *ptr;
ptr = kmalloc(len, prio);
if (ptr)
memset(ptr, 0, len);
return ptr;
}
#if 0
static void
sm_card_info_destructor(void *extra) {
struct sm_card_info *info;
info = (struct sm_card_info *) extra;
if (!info)
return;
kfree(info->lba_to_pba);
kfree(info->pba_to_lba);
}
#endif
static void
sm_init_card_info(struct sm_card_info **infop) {
if (!*infop)
*infop = kcmalloc(sizeof(struct sm_card_info), GFP_NOIO);
/* set destructor */
}
static struct nand_flash_dev *
sm_get_cardinfo(struct us_data *us, unsigned char flags, struct sm_ops *sm) {
struct nand_flash_dev *cardinfo;
unsigned char deviceID[4];
char blurbtxt[256];
int result;
US_DEBUGP("Reading deviceID...\n");
result = sm->sm_read_deviceID(us, deviceID);
if (result != USB_STOR_TRANSPORT_GOOD) {
US_DEBUGP("Result of read_deviceID is %d\n", result);
return NULL;
}
sprintf(blurbtxt, "Found SmartMedia card, ID = %02X %02X %02X %02X",
deviceID[0], deviceID[1], deviceID[2], deviceID[3]);
/* Byte 0 is the manufacturer */
sprintf(blurbtxt + strlen(blurbtxt),
": Manuf. %s",
nand_flash_manufacturer(deviceID[0]));
/* Byte 1 is the device type */
cardinfo = nand_find_id(deviceID[1]);
if (cardinfo) {
/* MB or MiB? It is neither. A 16 MB card has
17301504 raw bytes, of which 16384000 are
usable for user data. */
sprintf(blurbtxt + strlen(blurbtxt),
", %d MB", 1<<(cardinfo->chipshift - 20));
} else {
sprintf(blurbtxt + strlen(blurbtxt),
", type unrecognized");
}
/* Byte 2 is code to signal availability of 128-bit ID */
if (deviceID[2] == 0xa5) {
sprintf(blurbtxt + strlen(blurbtxt),
", 128-bit ID");
}
/* Byte 3 announces the availability of another read ID command */
if (deviceID[3] == 0xc0) {
sprintf(blurbtxt + strlen(blurbtxt),
", extra cmd");
}
if (flags & SMARTMEDIA_WP)
sprintf(blurbtxt + strlen(blurbtxt),
", WP");
printk("%s\n", blurbtxt);
return cardinfo;
}
/*
* Read from disk address the indicated number of sectors.
* Here a sector has size info->pagesize (and address is
* given in this unit).
*/
static int
sm_read_data(struct us_data *us, struct sm_ops *sm,
unsigned long address, unsigned int count,
unsigned char *content, int use_sg) {
struct sm_card_info *info = (struct sm_card_info *) us->extra;
unsigned int lba, maxlba, pba;
unsigned int page, pages;
unsigned char *buffer = NULL;
unsigned char *ptr;
struct scatterlist *sg = NULL;
int result, i, 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 = count * info->pagesize;
if (use_sg) {
sg = (struct scatterlist *) content;
buffer = kmalloc(len, GFP_NOIO);
if (buffer == NULL)
return USB_STOR_TRANSPORT_ERROR;
ptr = buffer;
} else
ptr = content;
// 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;
while (count > 0) {
/* Find number of pages we can read in this block */
pages = info->blocksize - page;
if (pages > count)
pages = count;
/* Not overflowing capacity? */
if (lba >= maxlba) {
US_DEBUGP("Error: Requested lba %u exceeds "
"maximum %u\n", lba, maxlba);
result = USB_STOR_TRANSPORT_ERROR;
break;
}
/* Find where this lba lives on disk */
pba = info->lba_to_pba[lba];
if (pba == UNDEF) { /* this lba was never written */
#if 0
US_DEBUGP("Read %d null pages (LBA %d) page %d\n",
pages, lba, page);
#endif
/*
* This is not an error. It just means
* that the block has never been written.
* Unwritten blocks read as 0xff.
*/
memset(ptr, 0xff, pages << info->pageshift);
} else {
US_DEBUGP("Read %d pages, from PBA %d"
" (LBA %d) page %d\n",
pages, pba, lba, page);
address = (pba << info->blockshift) + page;
result = sm->sm_read_data(us, address, pages, ptr, 0);
if (result != USB_STOR_TRANSPORT_GOOD)
break;
}
page = 0;
lba++;
count -= pages;
ptr += (pages << info->pageshift);
}
if (use_sg && result == USB_STOR_TRANSPORT_GOOD) {
int transferred = 0;
for (i=0; i<use_sg && transferred<len; i++) {
unsigned char *buf;
unsigned int length;
buf = page_address(sg[i].page) + sg[i].offset;
length = len-transferred;
if (length > sg[i].length)
length = sg[i].length;
memcpy(buf, buffer+transferred, length);
transferred += sg[i].length;
}
}
if (use_sg)
kfree(buffer);
return result;
}
unsigned int
smartmedia_find_unused_pba(struct sm_card_info *info, unsigned int lba) {
static unsigned int lastpba = 1;
int numblocks = info->capacity >> (info->blockshift + info->pageshift);
int minpba, maxpba, i;
/* find pba in the same block as lba */
minpba = (lba / 1000) * 1024;
if (minpba == 0)
minpba = 2;
maxpba = minpba + 1024;
if (maxpba > numblocks)
maxpba = numblocks;
/* avoid giving out recent blocks again */
if (lastpba/1024 != pbamin/1024)
lastpba = pbamin-1;
for (i = lastpba+1; i < maxpba; i++) {
if (info->pba_to_lba[i] == UNDEF) {
lastpba = i;
return i;
}
}
for (i = minpba; i <= lastpba; i++) {
if (info->pba_to_lba[i] == UNDEF) {
lastpba = i;
return i;
}
}
/* medium error; no defect spare location available */
set_sense_info(0x03, 0x32, 0);
return 0;
}
/*
* Write to disk address the indicated number of sectors.
* Here a sector has size info->pagesize (and address is
* given in this unit).
*/
static int
sm_write_data(struct us_data *us, struct sm_ops *sm,
unsigned long address, unsigned int sectors,
unsigned char *content, int use_sg) {
struct sm_card_info *info = us->extra;
unsigned int lba, page, pages;
unsigned char *buffer = NULL;
unsigned char *ptr;
struct scatterlist *sg = NULL;
int result, i, len;
// If we're using scatter-gather, we have to create a new
// buffer to write 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) {
int transferred = 0;
sg = (struct scatterlist *)content;
buffer = kmalloc(len, GFP_NOIO);
if (buffer == NULL)
return USB_STOR_TRANSPORT_ERROR;
for (i=0; i<use_sg && transferred<len; i++) {
unsigned char *buf;
unsigned int length;
buf = page_address(sg[i].page) + sg[i].offset;
length = len-transferred;
if (length > sg[i].length)
length = sg[i].length;
memcpy(buffer+transferred, buf, length);
transferred += sg[i].length;
}
ptr = buffer;
} else
ptr = content;
// Figure out the initial LBA and page
lba = address >> info->blockshift;
page = (address & info->blockmask);
// This could be made much more efficient by checking for
// contiguous LBA's. Another exercise left to the student.
result = USB_STOR_TRANSPORT_GOOD;
while (sectors > 0) {
// Write as many sectors as possible in this block
pages = info->blocksize - page;
if (pages > sectors)
pages = sectors;
result = sm->sm_write_lba(us, lba, page, pages, ptr);
if (result != USB_STOR_TRANSPORT_GOOD)
break;
page = 0;
lba++;
sectors -= pages;
ptr += (pages << info->pageshift);
}
if (use_sg)
kfree(buffer);
return result;
}
/* return 0 on failure */
int
smartmedia_allocate_map(struct sm_card_info *info) {
int numblocks = info->capacity >> (info->blockshift + info->pageshift);
int i;
kfree(info->lba_to_pba);
kfree(info->pba_to_lba);
info->lba_to_pba = 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) {
kfree(info->lba_to_pba);
kfree(info->pba_to_lba);
info->lba_to_pba = NULL;
info->pba_to_lba = NULL;
return 0;
}
for (i = 0; i < numblocks; i++)
info->lba_to_pba[i] = info->pba_to_lba[i] = UNDEF;
return 1;
}
static void
sm_set_trivial_map(struct sm_card_info *info) {
int numblocks = info->capacity >> (info->blockshift + info->pageshift);
int i;
/* No translation: PBA = LBA */
for (i=0; i<numblocks; i++)
info->pba_to_lba[i] = info->lba_to_pba[i] = i;
printk("Note: no smartmedia translation today\n");
}
int
smartmedia_transport(Scsi_Cmnd *srb, struct us_data *us, struct sm_ops *sm) {
static unsigned char sensekey = 0;
static unsigned char sensecode = 0;
static unsigned char havefakesense = 0;
unsigned char *ptr;
unsigned long capacity;
unsigned int page, pages;
int result;
unsigned char inquiry_response[36] = {
0x00, 0x80, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x00
};
unsigned char mode_page_01[16] = {
0x0F, 0x00, 0, 0x00,
0x01, 0x0A,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
struct sm_card_info *info = us->extra;
if (!info) {
nand_init_ecc();
sm_init_card_info((struct sm_card_info **) & us->extra);
info = us->extra;
if (!info)
return USB_STOR_TRANSPORT_ERROR;
}
ptr = (unsigned char *)srb->request_buffer;
if (srb->cmnd[0] == REQUEST_SENSE) {
if (havefakesense || !sm->sm_request_sense) {
memset(ptr, 0, srb->request_bufflen);
if (srb->request_bufflen > 7) {
ptr[0] = 0xF0;
ptr[2] = sensekey;
ptr[7] = srb->request_bufflen - 7;
}
if (srb->request_bufflen > 12)
ptr[12] = sensecode;
sensekey = sensecode = havefakesense = 0;
return USB_STOR_TRANSPORT_GOOD;
} else {
return sm->sm_request_sense(us, srb, srb->cmd_len);
}
}
havefakesense = 1;
/* Dummy up a response for INQUIRY - it seems SmartMedia readers
(sddr09, datafab) do not respond to INQUIRY commands */
if (srb->cmnd[0] == INQUIRY) {
memset(inquiry_response+8, 0, 28);
fill_inquiry_response(us, inquiry_response, 36);
return USB_STOR_TRANSPORT_GOOD;
}
if (srb->cmnd[0] == READ_CAPACITY) {
struct nand_flash_dev *cardinfo;
sm->sm_init(us); /* get controlshift, read WP bit */
cardinfo = sm_get_cardinfo(us, info->flags, sm);
if (!cardinfo) {
/* probably no media */
sensekey = 0x02; /* not ready */
sensecode = 0x3a; /* medium not present */
return USB_STOR_TRANSPORT_FAILED;
}
info->capacity = (1 << cardinfo->chipshift);
info->pageshift = cardinfo->pageshift;
info->pagesize = (1 << info->pageshift);
info->blockshift = cardinfo->blockshift;
info->blocksize = (1 << info->blockshift);
info->blockmask = info->blocksize - 1;
// map initialization, must follow get_cardinfo()
// Report capacity in 512-byte sectors
if (smartmedia_control) {
/* no lba-pba available */
capacity = (1 << (cardinfo->chipshift -
cardinfo->pageshift +
info->controlshift - 9));
} else if (smartmedia_raw) {
if (!smartmedia_allocate_map(info))
return USB_STOR_TRANSPORT_FAILED;
sm_set_trivial_map(info);
capacity = (info->capacity >> 9);
} else {
sm->sm_read_map(us);
capacity = (info->capacity >> 16) * 125;
}
printk("sm: capacity = %ld sectors (%ld MB)\n",
capacity, ((capacity+1024) >> 11));
// In fact, the number of the last sector
capacity--;
ptr[0] = MSB_of(capacity>>16);
ptr[1] = LSB_of(capacity>>16);
ptr[2] = MSB_of(capacity&0xFFFF);
ptr[3] = LSB_of(capacity&0xFFFF);
// Report 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);
return USB_STOR_TRANSPORT_GOOD;
}
if (srb->cmnd[0] == MODE_SENSE ||
srb->cmnd[0] == MODE_SENSE_10) {
// Read-write error recovery page: there needs to
// be a check for write-protect here
if ( (srb->cmnd[2] & 0x3F) == 0x01 ) {
US_DEBUGP(
"Dummy up request for mode page 1\n");
if (ptr == NULL ||
srb->request_bufflen<sizeof(mode_page_01))
return USB_STOR_TRANSPORT_ERROR;
mode_page_01[0] = sizeof(mode_page_01) - 1;
mode_page_01[2] = (info->flags & SMARTMEDIA_WP) ? 0x80 : 0;
memcpy(ptr, mode_page_01, sizeof(mode_page_01));
return USB_STOR_TRANSPORT_GOOD;
} else if ( (srb->cmnd[2] & 0x3F) == 0x3F ) {
US_DEBUGP("Dummy up request for all mode pages\n");
if (ptr == NULL ||
srb->request_bufflen<sizeof(mode_page_01))
return USB_STOR_TRANSPORT_ERROR;
memcpy(ptr, mode_page_01, sizeof(mode_page_01));
return USB_STOR_TRANSPORT_GOOD;
}
US_DEBUGP("Unexpected MODE_SENSE request\n");
return USB_STOR_TRANSPORT_ERROR;
}
if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) {
US_DEBUGP("%s medium removal.\n",
(srb->cmnd[4] & 0x03) ? "Prevent" : "Allow");
return USB_STOR_TRANSPORT_GOOD;
}
sensekey = sensecode = havefakesense = 0;
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]);
US_DEBUGP("READ_10: read page %d pagect %d\n",
page, pages);
if (smartmedia_control) {
if (!sm->sm_read_control)
return USB_STOR_TRANSPORT_ERROR;
return sm->sm_read_control(us, page, pages,
ptr, srb->use_sg);
}
return sm_read_data(us, sm, page, pages, ptr, srb->use_sg);
}
if (srb->cmnd[0] == WRITE_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]);
US_DEBUGP("WRITE_10: write page %d pagect %d\n",
page, pages);
if (info->flags & (SMARTMEDIA_WP | SMARTMEDIA_RO)) {
sensekey = 0x07; /* data protect */
sensecode = 0x27; /* write protected */
havefakesense = 1;
return USB_STOR_TRANSPORT_FAILED;
}
return sm_write_data(us, sm, page, pages, ptr, srb->use_sg);
}
if (srb->cmnd[0] == TEST_UNIT_READY)
return sm->sm_test_unit_ready(us, srb, srb->cmd_len);
US_DEBUGP("Unknown SCSI request 0x%02X\n", srb->cmnd[0]);
havefakesense = 1;
return USB_STOR_TRANSPORT_ERROR;
}
_______________________________________________________________
Have big pipes? SourceForge.net is looking for download mirrors. We supply
the hardware. You get the recognition. Email Us: [EMAIL PROTECTED]
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel