diff options
Diffstat (limited to 'drivers/usb/storage/alauda.c')
| -rw-r--r-- | drivers/usb/storage/alauda.c | 290 |
1 files changed, 217 insertions, 73 deletions
diff --git a/drivers/usb/storage/alauda.c b/drivers/usb/storage/alauda.c index 8d3711a7ff0..6636a583da1 100644 --- a/drivers/usb/storage/alauda.c +++ b/drivers/usb/storage/alauda.c @@ -31,6 +31,9 @@ * 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include <linux/module.h> +#include <linux/slab.h> + #include <scsi/scsi.h> #include <scsi/scsi_cmnd.h> #include <scsi/scsi_device.h> @@ -39,7 +42,79 @@ #include "transport.h" #include "protocol.h" #include "debug.h" -#include "alauda.h" + +MODULE_DESCRIPTION("Driver for Alauda-based card readers"); +MODULE_AUTHOR("Daniel Drake <dsd@gentoo.org>"); +MODULE_LICENSE("GPL"); + +/* + * Status bytes + */ +#define ALAUDA_STATUS_ERROR 0x01 +#define ALAUDA_STATUS_READY 0x40 + +/* + * Control opcodes (for request field) + */ +#define ALAUDA_GET_XD_MEDIA_STATUS 0x08 +#define ALAUDA_GET_SM_MEDIA_STATUS 0x98 +#define ALAUDA_ACK_XD_MEDIA_CHANGE 0x0a +#define ALAUDA_ACK_SM_MEDIA_CHANGE 0x9a +#define ALAUDA_GET_XD_MEDIA_SIG 0x86 +#define ALAUDA_GET_SM_MEDIA_SIG 0x96 + +/* + * Bulk command identity (byte 0) + */ +#define ALAUDA_BULK_CMD 0x40 + +/* + * Bulk opcodes (byte 1) + */ +#define ALAUDA_BULK_GET_REDU_DATA 0x85 +#define ALAUDA_BULK_READ_BLOCK 0x94 +#define ALAUDA_BULK_ERASE_BLOCK 0xa3 +#define ALAUDA_BULK_WRITE_BLOCK 0xb4 +#define ALAUDA_BULK_GET_STATUS2 0xb7 +#define ALAUDA_BULK_RESET_MEDIA 0xe0 + +/* + * Port to operate on (byte 8) + */ +#define ALAUDA_PORT_XD 0x00 +#define ALAUDA_PORT_SM 0x01 + +/* + * LBA and PBA are unsigned ints. Special values. + */ +#define UNDEF 0xffff +#define SPARE 0xfffe +#define UNUSABLE 0xfffd + +struct alauda_media_info { + unsigned long capacity; /* total media size in bytes */ + unsigned int pagesize; /* page size in bytes */ + unsigned int blocksize; /* number of pages per block */ + unsigned int uzonesize; /* number of usable blocks per zone */ + unsigned int zonesize; /* number of blocks per zone */ + unsigned int blockmask; /* mask to get page from address */ + + unsigned char pageshift; + unsigned char blockshift; + unsigned char zoneshift; + + u16 **lba_to_pba; /* logical to physical block map */ + u16 **pba_to_lba; /* physical to logical block map */ +}; + +struct alauda_info { + struct alauda_media_info port[2]; + int wr_ep; /* endpoint to write data out of */ + + unsigned char sense_key; + unsigned long sense_asc; /* additional sense code */ + unsigned long sense_ascq; /* additional sense code qualifier */ +}; #define short_pack(lsb,msb) ( ((u16)(lsb)) | ( ((u16)(msb))<<8 ) ) #define LSB_of(s) ((s)&0xFF) @@ -52,6 +127,48 @@ #define PBA_HI(pba) (pba >> 3) #define PBA_ZONE(pba) (pba >> 11) +static int init_alauda(struct us_data *us); + + +/* + * The table of devices + */ +#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ + vendorName, productName, useProtocol, useTransport, \ + initFunction, flags) \ +{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ + .driver_info = (flags) } + +static struct usb_device_id alauda_usb_ids[] = { +# include "unusual_alauda.h" + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, alauda_usb_ids); + +#undef UNUSUAL_DEV + +/* + * The flags table + */ +#define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \ + vendor_name, product_name, use_protocol, use_transport, \ + init_function, Flags) \ +{ \ + .vendorName = vendor_name, \ + .productName = product_name, \ + .useProtocol = use_protocol, \ + .useTransport = use_transport, \ + .initFunction = init_function, \ +} + +static struct us_unusual_dev alauda_unusual_dev_list[] = { +# include "unusual_alauda.h" + { } /* Terminating entry */ +}; + +#undef UNUSUAL_DEV + + /* * Media handling */ @@ -132,11 +249,7 @@ static void nand_init_ecc(void) { /* compute 3-byte ecc on 256 bytes */ static 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; + unsigned char par = 0, bit, bits[8] = {0}; /* collect 16 checksum bits */ for (i = 0; i < 256; i++) { @@ -209,8 +322,7 @@ static int alauda_get_media_status(struct us_data *us, unsigned char *data) rc = usb_stor_ctrl_transfer(us, us->recv_ctrl_pipe, command, 0xc0, 0, 1, data, 2); - US_DEBUGP("alauda_get_media_status: Media status %02X %02X\n", - data[0], data[1]); + usb_stor_dbg(us, "Media status %02X %02X\n", data[0], data[1]); return rc; } @@ -285,7 +397,7 @@ static int alauda_init_media(struct us_data *us) ready = 1; } - US_DEBUGP("alauda_init_media: We are ready for action!\n"); + usb_stor_dbg(us, "We are ready for action!\n"); if (alauda_ack_media(us) != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; @@ -296,26 +408,27 @@ static int alauda_init_media(struct us_data *us) return USB_STOR_TRANSPORT_ERROR; if (data[0] != 0x14) { - US_DEBUGP("alauda_init_media: Media not ready after ack\n"); + usb_stor_dbg(us, "Media not ready after ack\n"); return USB_STOR_TRANSPORT_ERROR; } if (alauda_get_media_signature(us, data) != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; - US_DEBUGP("alauda_init_media: Media signature: %02X %02X %02X %02X\n", - data[0], data[1], data[2], data[3]); + usb_stor_dbg(us, "Media signature: %02X %02X %02X %02X\n", + data[0], data[1], data[2], data[3]); media_info = alauda_card_find_id(data[1]); if (media_info == NULL) { - printk("alauda_init_media: Unrecognised media signature: " + printk(KERN_WARNING + "alauda_init_media: Unrecognised media signature: " "%02X %02X %02X %02X\n", data[0], data[1], data[2], data[3]); return USB_STOR_TRANSPORT_ERROR; } MEDIA_INFO(us).capacity = 1 << media_info->chipshift; - US_DEBUGP("Found media with capacity: %ldMB\n", - MEDIA_INFO(us).capacity >> 20); + usb_stor_dbg(us, "Found media with capacity: %ldMB\n", + MEDIA_INFO(us).capacity >> 20); MEDIA_INFO(us).pageshift = media_info->pageshift; MEDIA_INFO(us).blockshift = media_info->blockshift; @@ -354,7 +467,7 @@ static int alauda_check_media(struct us_data *us) /* Check for no media or door open */ if ((status[0] & 0x80) || ((status[0] & 0x1F) == 0x10) || ((status[1] & 0x01) == 0)) { - US_DEBUGP("alauda_check_media: No media, or door open\n"); + usb_stor_dbg(us, "No media, or door open\n"); alauda_free_maps(&MEDIA_INFO(us)); info->sense_key = 0x02; info->sense_asc = 0x3A; @@ -364,7 +477,7 @@ static int alauda_check_media(struct us_data *us) /* Check for media change */ if (status[0] & 0x08) { - US_DEBUGP("alauda_check_media: Media change detected\n"); + usb_stor_dbg(us, "Media change detected\n"); alauda_free_maps(&MEDIA_INFO(us)); alauda_init_media(us); @@ -400,7 +513,7 @@ static int alauda_check_status2(struct us_data *us) if (rc != USB_STOR_XFER_GOOD) return rc; - US_DEBUGP("alauda_check_status2: %02X %02X %02X\n", data[0], data[1], data[2]); + usb_stor_dbg(us, "%02X %02X %02X\n", data[0], data[1], data[2]); if (data[0] & ALAUDA_STATUS_ERROR) return USB_STOR_XFER_ERROR; @@ -466,7 +579,7 @@ static int alauda_read_map(struct us_data *us, unsigned int zone) goto error; } - US_DEBUGP("alauda_read_map: Mapping blocks for zone %d\n", zone); + usb_stor_dbg(us, "Mapping blocks for zone %d\n", zone); /* 1024 PBA's per zone */ for (i = 0; i < zonesize; i++) @@ -486,7 +599,7 @@ static int alauda_read_map(struct us_data *us, unsigned int zone) if (data[j] != 0) goto nonz; pba_to_lba[i] = UNUSABLE; - US_DEBUGP("alauda_read_map: PBA %d has no logical mapping\n", blocknum); + usb_stor_dbg(us, "PBA %d has no logical mapping\n", blocknum); continue; nonz: @@ -499,26 +612,26 @@ static int alauda_read_map(struct us_data *us, unsigned int zone) nonff: /* normal PBAs start with six FFs */ if (j < 6) { - US_DEBUGP("alauda_read_map: PBA %d has no logical mapping: " - "reserved area = %02X%02X%02X%02X " - "data status %02X block status %02X\n", - blocknum, data[0], data[1], data[2], data[3], - data[4], data[5]); + usb_stor_dbg(us, "PBA %d has no logical mapping: reserved area = %02X%02X%02X%02X data status %02X block status %02X\n", + blocknum, + data[0], data[1], data[2], data[3], + data[4], data[5]); pba_to_lba[i] = UNUSABLE; continue; } if ((data[6] >> 4) != 0x01) { - US_DEBUGP("alauda_read_map: PBA %d has invalid address " - "field %02X%02X/%02X%02X\n", - blocknum, data[6], data[7], data[11], data[12]); + usb_stor_dbg(us, "PBA %d has invalid address field %02X%02X/%02X%02X\n", + blocknum, data[6], data[7], + data[11], data[12]); pba_to_lba[i] = UNUSABLE; continue; } /* check even parity */ if (parity[data[6] ^ data[7]]) { - printk("alauda_read_map: Bad parity in LBA for block %d" + printk(KERN_WARNING + "alauda_read_map: Bad parity in LBA for block %d" " (%02X %02X)\n", i, data[6], data[7]); pba_to_lba[i] = UNUSABLE; continue; @@ -538,13 +651,16 @@ static int alauda_read_map(struct us_data *us, unsigned int zone) */ if (lba_offset >= uzonesize) { - printk("alauda_read_map: Bad low LBA %d for block %d\n", + printk(KERN_WARNING + "alauda_read_map: Bad low LBA %d for block %d\n", lba_real, blocknum); continue; } if (lba_to_pba[lba_offset] != UNDEF) { - printk("alauda_read_map: LBA %d seen for PBA %d and %d\n", + printk(KERN_WARNING + "alauda_read_map: " + "LBA %d seen for PBA %d and %d\n", lba_real, lba_to_pba[lba_offset], blocknum); continue; } @@ -589,7 +705,7 @@ static int alauda_erase_block(struct us_data *us, u16 pba) }; unsigned char buf[2]; - US_DEBUGP("alauda_erase_block: Erasing PBA %d\n", pba); + usb_stor_dbg(us, "Erasing PBA %d\n", pba); rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, command, 9, NULL); @@ -601,8 +717,7 @@ static int alauda_erase_block(struct us_data *us, u16 pba) if (rc != USB_STOR_XFER_GOOD) return rc; - US_DEBUGP("alauda_erase_block: Erase result: %02X %02X\n", - buf[0], buf[1]); + usb_stor_dbg(us, "Erase result: %02X %02X\n", buf[0], buf[1]); return rc; } @@ -619,8 +734,7 @@ static int alauda_read_block_raw(struct us_data *us, u16 pba, PBA_ZONE(pba), 0, PBA_LO(pba) + page, pages, 0, MEDIA_PORT(us) }; - US_DEBUGP("alauda_read_block: pba %d page %d count %d\n", - pba, page, pages); + usb_stor_dbg(us, "pba %d page %d count %d\n", pba, page, pages); rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, command, 9, NULL); @@ -671,7 +785,7 @@ static int alauda_write_block(struct us_data *us, u16 pba, unsigned char *data) PBA_ZONE(pba), 0, PBA_LO(pba), 32, 0, MEDIA_PORT(us) }; - US_DEBUGP("alauda_write_block: pba %d\n", pba); + usb_stor_dbg(us, "pba %d\n", pba); rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, command, 9, NULL); @@ -712,13 +826,15 @@ static int alauda_write_lba(struct us_data *us, u16 lba, if (pba == 1) { /* Maybe it is impossible to write to PBA 1. Fake success, but don't do anything. */ - printk("alauda_write_lba: avoid writing to pba 1\n"); + printk(KERN_WARNING + "alauda_write_lba: avoid writing to pba 1\n"); return USB_STOR_TRANSPORT_GOOD; } new_pba = alauda_find_unused_pba(&MEDIA_INFO(us), zone); if (!new_pba) { - printk("alauda_write_lba: Out of unused blocks\n"); + printk(KERN_WARNING + "alauda_write_lba: Out of unused blocks\n"); return USB_STOR_TRANSPORT_ERROR; } @@ -742,14 +858,14 @@ static int alauda_write_lba(struct us_data *us, u16 lba, cptr = bptr + pagesize; nand_compute_ecc(bptr, ecc); if (!nand_compare_ecc(cptr+13, ecc)) { - US_DEBUGP("Warning: bad ecc in page %d- of pba %d\n", - i, pba); + usb_stor_dbg(us, "Warning: bad ecc in page %d- of pba %d\n", + i, pba); nand_store_ecc(cptr+13, ecc); } nand_compute_ecc(bptr + (pagesize / 2), ecc); if (!nand_compare_ecc(cptr+8, ecc)) { - US_DEBUGP("Warning: bad ecc in page %d+ of pba %d\n", - i, pba); + usb_stor_dbg(us, "Warning: bad ecc in page %d+ of pba %d\n", + i, pba); nand_store_ecc(cptr+8, ecc); } cptr[6] = cptr[11] = MSB_of(lbap); @@ -776,8 +892,7 @@ static int alauda_write_lba(struct us_data *us, u16 lba, new_pba_offset = new_pba - (zone * zonesize); MEDIA_INFO(us).pba_to_lba[zone][new_pba_offset] = lba; MEDIA_INFO(us).lba_to_pba[zone][lba_offset] = new_pba; - US_DEBUGP("alauda_write_lba: Remapped LBA %d to PBA %d\n", - lba, new_pba); + usb_stor_dbg(us, "Remapped LBA %d to PBA %d\n", lba, new_pba); if (pba != UNDEF) { unsigned int pba_offset = pba - (zone * zonesize); @@ -818,7 +933,7 @@ static int alauda_read_data(struct us_data *us, unsigned long address, len = min(sectors, blocksize) * (pagesize + 64); buffer = kmalloc(len, GFP_NOIO); if (buffer == NULL) { - printk("alauda_read_data: Out of memory\n"); + printk(KERN_WARNING "alauda_read_data: Out of memory\n"); return USB_STOR_TRANSPORT_ERROR; } @@ -840,8 +955,8 @@ static int alauda_read_data(struct us_data *us, unsigned long address, /* Not overflowing capacity? */ if (lba >= max_lba) { - US_DEBUGP("Error: Requested lba %u exceeds " - "maximum %u\n", lba, max_lba); + usb_stor_dbg(us, "Error: Requested lba %u exceeds maximum %u\n", + lba, max_lba); result = USB_STOR_TRANSPORT_ERROR; break; } @@ -854,8 +969,8 @@ static int alauda_read_data(struct us_data *us, unsigned long address, pba = MEDIA_INFO(us).lba_to_pba[zone][lba_offset]; if (pba == UNDEF) { /* this lba was never written */ - US_DEBUGP("Read %d zero pages (LBA %d) page %d\n", - pages, lba, page); + usb_stor_dbg(us, "Read %d zero pages (LBA %d) page %d\n", + pages, lba, page); /* This is not really an error. It just means that the block has never been written. @@ -864,9 +979,8 @@ static int alauda_read_data(struct us_data *us, unsigned long address, memset(buffer, 0, len); } else { - US_DEBUGP("Read %d pages, from PBA %d" - " (LBA %d) page %d\n", - pages, pba, lba, page); + usb_stor_dbg(us, "Read %d pages, from PBA %d (LBA %d) page %d\n", + pages, pba, lba, page); result = alauda_read_block(us, pba, page, pages, buffer); if (result != USB_STOR_TRANSPORT_GOOD) @@ -911,7 +1025,7 @@ static int alauda_write_data(struct us_data *us, unsigned long address, len = min(sectors, blocksize) * pagesize; buffer = kmalloc(len, GFP_NOIO); if (buffer == NULL) { - printk("alauda_write_data: Out of memory\n"); + printk(KERN_WARNING "alauda_write_data: Out of memory\n"); return USB_STOR_TRANSPORT_ERROR; } @@ -921,7 +1035,7 @@ static int alauda_write_data(struct us_data *us, unsigned long address, */ blockbuffer = kmalloc((pagesize + 64) * blocksize, GFP_NOIO); if (blockbuffer == NULL) { - printk("alauda_write_data: Out of memory\n"); + printk(KERN_WARNING "alauda_write_data: Out of memory\n"); kfree(buffer); return USB_STOR_TRANSPORT_ERROR; } @@ -942,8 +1056,8 @@ static int alauda_write_data(struct us_data *us, unsigned long address, /* Not overflowing capacity? */ if (lba >= max_lba) { - US_DEBUGP("alauda_write_data: Requested lba %u exceeds " - "maximum %u\n", lba, max_lba); + usb_stor_dbg(us, "Requested lba %u exceeds maximum %u\n", + lba, max_lba); result = USB_STOR_TRANSPORT_ERROR; break; } @@ -991,18 +1105,16 @@ static void alauda_info_destructor(void *extra) /* * Initialize alauda_info struct and find the data-write endpoint */ -int init_alauda(struct us_data *us) +static int init_alauda(struct us_data *us) { struct alauda_info *info; struct usb_host_interface *altsetting = us->pusb_intf->cur_altsetting; nand_init_ecc(); us->extra = kzalloc(sizeof(struct alauda_info), GFP_NOIO); - if (!us->extra) { - US_DEBUGP("init_alauda: Gah! Can't allocate storage for" - "alauda info struct!\n"); + if (!us->extra) return USB_STOR_TRANSPORT_ERROR; - } + info = (struct alauda_info *) us->extra; us->extra_destructor = alauda_info_destructor; @@ -1013,7 +1125,7 @@ int init_alauda(struct us_data *us) return USB_STOR_TRANSPORT_GOOD; } -int alauda_transport(struct scsi_cmnd *srb, struct us_data *us) +static int alauda_transport(struct scsi_cmnd *srb, struct us_data *us) { int rc; struct alauda_info *info = (struct alauda_info *) us->extra; @@ -1023,15 +1135,14 @@ int alauda_transport(struct scsi_cmnd *srb, struct us_data *us) }; if (srb->cmnd[0] == INQUIRY) { - US_DEBUGP("alauda_transport: INQUIRY. " - "Returning bogus response.\n"); + usb_stor_dbg(us, "INQUIRY - Returning bogus response\n"); memcpy(ptr, inquiry_response, sizeof(inquiry_response)); fill_inquiry_response(us, ptr, 36); return USB_STOR_TRANSPORT_GOOD; } if (srb->cmnd[0] == TEST_UNIT_READY) { - US_DEBUGP("alauda_transport: TEST_UNIT_READY.\n"); + usb_stor_dbg(us, "TEST_UNIT_READY\n"); return alauda_check_media(us); } @@ -1069,8 +1180,7 @@ int alauda_transport(struct scsi_cmnd *srb, struct us_data *us) page |= short_pack(srb->cmnd[5], srb->cmnd[4]); pages = short_pack(srb->cmnd[8], srb->cmnd[7]); - US_DEBUGP("alauda_transport: READ_10: page %d pagect %d\n", - page, pages); + usb_stor_dbg(us, "READ_10: page %d pagect %d\n", page, pages); return alauda_read_data(us, page, pages); } @@ -1087,14 +1197,13 @@ int alauda_transport(struct scsi_cmnd *srb, struct us_data *us) page |= short_pack(srb->cmnd[5], srb->cmnd[4]); pages = short_pack(srb->cmnd[8], srb->cmnd[7]); - US_DEBUGP("alauda_transport: WRITE_10: page %d pagect %d\n", - page, pages); + usb_stor_dbg(us, "WRITE_10: page %d pagect %d\n", page, pages); return alauda_write_data(us, page, pages); } if (srb->cmnd[0] == REQUEST_SENSE) { - US_DEBUGP("alauda_transport: REQUEST_SENSE.\n"); + usb_stor_dbg(us, "REQUEST_SENSE\n"); memset(ptr, 0, 18); ptr[0] = 0xF0; @@ -1113,11 +1222,46 @@ int alauda_transport(struct scsi_cmnd *srb, struct us_data *us) return USB_STOR_TRANSPORT_GOOD; } - US_DEBUGP("alauda_transport: Gah! Unknown command: %d (0x%x)\n", - srb->cmnd[0], srb->cmnd[0]); + usb_stor_dbg(us, "Gah! Unknown command: %d (0x%x)\n", + srb->cmnd[0], srb->cmnd[0]); info->sense_key = 0x05; info->sense_asc = 0x20; info->sense_ascq = 0x00; return USB_STOR_TRANSPORT_FAILED; } +static int alauda_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct us_data *us; + int result; + + result = usb_stor_probe1(&us, intf, id, + (id - alauda_usb_ids) + alauda_unusual_dev_list); + if (result) + return result; + + us->transport_name = "Alauda Control/Bulk"; + us->transport = alauda_transport; + us->transport_reset = usb_stor_Bulk_reset; + us->max_lun = 1; + + result = usb_stor_probe2(us); + return result; +} + +static struct usb_driver alauda_driver = { + .name = "ums-alauda", + .probe = alauda_probe, + .disconnect = usb_stor_disconnect, + .suspend = usb_stor_suspend, + .resume = usb_stor_resume, + .reset_resume = usb_stor_reset_resume, + .pre_reset = usb_stor_pre_reset, + .post_reset = usb_stor_post_reset, + .id_table = alauda_usb_ids, + .soft_unbind = 1, + .no_dynamic_id = 1, +}; + +module_usb_driver(alauda_driver); |
