18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for SanDisk SDDR-55 SmartMedia reader 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * SDDR55 driver v0.1: 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * First release 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Current development and maintenance by: 108c2ecf20Sopenharmony_ci * (c) 2002 Simon Munton 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 148c2ecf20Sopenharmony_ci#include <linux/errno.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <scsi/scsi.h> 198c2ecf20Sopenharmony_ci#include <scsi/scsi_cmnd.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "usb.h" 228c2ecf20Sopenharmony_ci#include "transport.h" 238c2ecf20Sopenharmony_ci#include "protocol.h" 248c2ecf20Sopenharmony_ci#include "debug.h" 258c2ecf20Sopenharmony_ci#include "scsiglue.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define DRV_NAME "ums-sddr55" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for SanDisk SDDR-55 SmartMedia reader"); 308c2ecf20Sopenharmony_ciMODULE_AUTHOR("Simon Munton"); 318c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 328c2ecf20Sopenharmony_ciMODULE_IMPORT_NS(USB_STORAGE); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* 358c2ecf20Sopenharmony_ci * The table of devices 368c2ecf20Sopenharmony_ci */ 378c2ecf20Sopenharmony_ci#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ 388c2ecf20Sopenharmony_ci vendorName, productName, useProtocol, useTransport, \ 398c2ecf20Sopenharmony_ci initFunction, flags) \ 408c2ecf20Sopenharmony_ci{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ 418c2ecf20Sopenharmony_ci .driver_info = (flags) } 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic struct usb_device_id sddr55_usb_ids[] = { 448c2ecf20Sopenharmony_ci# include "unusual_sddr55.h" 458c2ecf20Sopenharmony_ci { } /* Terminating entry */ 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, sddr55_usb_ids); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#undef UNUSUAL_DEV 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* 528c2ecf20Sopenharmony_ci * The flags table 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_ci#define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \ 558c2ecf20Sopenharmony_ci vendor_name, product_name, use_protocol, use_transport, \ 568c2ecf20Sopenharmony_ci init_function, Flags) \ 578c2ecf20Sopenharmony_ci{ \ 588c2ecf20Sopenharmony_ci .vendorName = vendor_name, \ 598c2ecf20Sopenharmony_ci .productName = product_name, \ 608c2ecf20Sopenharmony_ci .useProtocol = use_protocol, \ 618c2ecf20Sopenharmony_ci .useTransport = use_transport, \ 628c2ecf20Sopenharmony_ci .initFunction = init_function, \ 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic struct us_unusual_dev sddr55_unusual_dev_list[] = { 668c2ecf20Sopenharmony_ci# include "unusual_sddr55.h" 678c2ecf20Sopenharmony_ci { } /* Terminating entry */ 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci#undef UNUSUAL_DEV 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci#define short_pack(lsb,msb) ( ((u16)(lsb)) | ( ((u16)(msb))<<8 ) ) 748c2ecf20Sopenharmony_ci#define LSB_of(s) ((s)&0xFF) 758c2ecf20Sopenharmony_ci#define MSB_of(s) ((s)>>8) 768c2ecf20Sopenharmony_ci#define PAGESIZE 512 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci#define set_sense_info(sk, asc, ascq) \ 798c2ecf20Sopenharmony_ci do { \ 808c2ecf20Sopenharmony_ci info->sense_data[2] = sk; \ 818c2ecf20Sopenharmony_ci info->sense_data[12] = asc; \ 828c2ecf20Sopenharmony_ci info->sense_data[13] = ascq; \ 838c2ecf20Sopenharmony_ci } while (0) 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistruct sddr55_card_info { 878c2ecf20Sopenharmony_ci unsigned long capacity; /* Size of card in bytes */ 888c2ecf20Sopenharmony_ci int max_log_blks; /* maximum number of logical blocks */ 898c2ecf20Sopenharmony_ci int pageshift; /* log2 of pagesize */ 908c2ecf20Sopenharmony_ci int smallpageshift; /* 1 if pagesize == 256 */ 918c2ecf20Sopenharmony_ci int blocksize; /* Size of block in pages */ 928c2ecf20Sopenharmony_ci int blockshift; /* log2 of blocksize */ 938c2ecf20Sopenharmony_ci int blockmask; /* 2^blockshift - 1 */ 948c2ecf20Sopenharmony_ci int read_only; /* non zero if card is write protected */ 958c2ecf20Sopenharmony_ci int force_read_only; /* non zero if we find a map error*/ 968c2ecf20Sopenharmony_ci int *lba_to_pba; /* logical to physical map */ 978c2ecf20Sopenharmony_ci int *pba_to_lba; /* physical to logical map */ 988c2ecf20Sopenharmony_ci int fatal_error; /* set if we detect something nasty */ 998c2ecf20Sopenharmony_ci unsigned long last_access; /* number of jiffies since we last talked to device */ 1008c2ecf20Sopenharmony_ci unsigned char sense_data[18]; 1018c2ecf20Sopenharmony_ci}; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci#define NOT_ALLOCATED 0xffffffff 1058c2ecf20Sopenharmony_ci#define BAD_BLOCK 0xffff 1068c2ecf20Sopenharmony_ci#define CIS_BLOCK 0x400 1078c2ecf20Sopenharmony_ci#define UNUSED_BLOCK 0x3ff 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic int 1108c2ecf20Sopenharmony_cisddr55_bulk_transport(struct us_data *us, int direction, 1118c2ecf20Sopenharmony_ci unsigned char *data, unsigned int len) { 1128c2ecf20Sopenharmony_ci struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra; 1138c2ecf20Sopenharmony_ci unsigned int pipe = (direction == DMA_FROM_DEVICE) ? 1148c2ecf20Sopenharmony_ci us->recv_bulk_pipe : us->send_bulk_pipe; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (!len) 1178c2ecf20Sopenharmony_ci return USB_STOR_XFER_GOOD; 1188c2ecf20Sopenharmony_ci info->last_access = jiffies; 1198c2ecf20Sopenharmony_ci return usb_stor_bulk_transfer_buf(us, pipe, data, len, NULL); 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci/* 1238c2ecf20Sopenharmony_ci * check if card inserted, if there is, update read_only status 1248c2ecf20Sopenharmony_ci * return non zero if no card 1258c2ecf20Sopenharmony_ci */ 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic int sddr55_status(struct us_data *us) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci int result; 1308c2ecf20Sopenharmony_ci unsigned char *command = us->iobuf; 1318c2ecf20Sopenharmony_ci unsigned char *status = us->iobuf; 1328c2ecf20Sopenharmony_ci struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* send command */ 1358c2ecf20Sopenharmony_ci memset(command, 0, 8); 1368c2ecf20Sopenharmony_ci command[5] = 0xB0; 1378c2ecf20Sopenharmony_ci command[7] = 0x80; 1388c2ecf20Sopenharmony_ci result = sddr55_bulk_transport(us, 1398c2ecf20Sopenharmony_ci DMA_TO_DEVICE, command, 8); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Result for send_command in status %d\n", result); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (result != USB_STOR_XFER_GOOD) { 1448c2ecf20Sopenharmony_ci set_sense_info (4, 0, 0); /* hardware error */ 1458c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci result = sddr55_bulk_transport(us, 1498c2ecf20Sopenharmony_ci DMA_FROM_DEVICE, status, 4); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* expect to get short transfer if no card fitted */ 1528c2ecf20Sopenharmony_ci if (result == USB_STOR_XFER_SHORT || result == USB_STOR_XFER_STALLED) { 1538c2ecf20Sopenharmony_ci /* had a short transfer, no card inserted, free map memory */ 1548c2ecf20Sopenharmony_ci kfree(info->lba_to_pba); 1558c2ecf20Sopenharmony_ci kfree(info->pba_to_lba); 1568c2ecf20Sopenharmony_ci info->lba_to_pba = NULL; 1578c2ecf20Sopenharmony_ci info->pba_to_lba = NULL; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci info->fatal_error = 0; 1608c2ecf20Sopenharmony_ci info->force_read_only = 0; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci set_sense_info (2, 0x3a, 0); /* not ready, medium not present */ 1638c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_FAILED; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (result != USB_STOR_XFER_GOOD) { 1678c2ecf20Sopenharmony_ci set_sense_info (4, 0, 0); /* hardware error */ 1688c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_FAILED; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* check write protect status */ 1728c2ecf20Sopenharmony_ci info->read_only = (status[0] & 0x20); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* now read status */ 1758c2ecf20Sopenharmony_ci result = sddr55_bulk_transport(us, 1768c2ecf20Sopenharmony_ci DMA_FROM_DEVICE, status, 2); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (result != USB_STOR_XFER_GOOD) { 1798c2ecf20Sopenharmony_ci set_sense_info (4, 0, 0); /* hardware error */ 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci return (result == USB_STOR_XFER_GOOD ? 1838c2ecf20Sopenharmony_ci USB_STOR_TRANSPORT_GOOD : USB_STOR_TRANSPORT_FAILED); 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic int sddr55_read_data(struct us_data *us, 1888c2ecf20Sopenharmony_ci unsigned int lba, 1898c2ecf20Sopenharmony_ci unsigned int page, 1908c2ecf20Sopenharmony_ci unsigned short sectors) { 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci int result = USB_STOR_TRANSPORT_GOOD; 1938c2ecf20Sopenharmony_ci unsigned char *command = us->iobuf; 1948c2ecf20Sopenharmony_ci unsigned char *status = us->iobuf; 1958c2ecf20Sopenharmony_ci struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra; 1968c2ecf20Sopenharmony_ci unsigned char *buffer; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci unsigned int pba; 1998c2ecf20Sopenharmony_ci unsigned long address; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci unsigned short pages; 2028c2ecf20Sopenharmony_ci unsigned int len, offset; 2038c2ecf20Sopenharmony_ci struct scatterlist *sg; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci // Since we only read in one block at a time, we have to create 2068c2ecf20Sopenharmony_ci // a bounce buffer and move the data a piece at a time between the 2078c2ecf20Sopenharmony_ci // bounce buffer and the actual transfer buffer. 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci len = min((unsigned int) sectors, (unsigned int) info->blocksize >> 2108c2ecf20Sopenharmony_ci info->smallpageshift) * PAGESIZE; 2118c2ecf20Sopenharmony_ci buffer = kmalloc(len, GFP_NOIO); 2128c2ecf20Sopenharmony_ci if (buffer == NULL) 2138c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; /* out of memory */ 2148c2ecf20Sopenharmony_ci offset = 0; 2158c2ecf20Sopenharmony_ci sg = NULL; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci while (sectors>0) { 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci /* have we got to end? */ 2208c2ecf20Sopenharmony_ci if (lba >= info->max_log_blks) 2218c2ecf20Sopenharmony_ci break; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci pba = info->lba_to_pba[lba]; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci // Read as many sectors as possible in this block 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci pages = min((unsigned int) sectors << info->smallpageshift, 2288c2ecf20Sopenharmony_ci info->blocksize - page); 2298c2ecf20Sopenharmony_ci len = pages << info->pageshift; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Read %02X pages, from PBA %04X (LBA %04X) page %02X\n", 2328c2ecf20Sopenharmony_ci pages, pba, lba, page); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (pba == NOT_ALLOCATED) { 2358c2ecf20Sopenharmony_ci /* no pba for this lba, fill with zeroes */ 2368c2ecf20Sopenharmony_ci memset (buffer, 0, len); 2378c2ecf20Sopenharmony_ci } else { 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci address = (pba << info->blockshift) + page; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci command[0] = 0; 2428c2ecf20Sopenharmony_ci command[1] = LSB_of(address>>16); 2438c2ecf20Sopenharmony_ci command[2] = LSB_of(address>>8); 2448c2ecf20Sopenharmony_ci command[3] = LSB_of(address); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci command[4] = 0; 2478c2ecf20Sopenharmony_ci command[5] = 0xB0; 2488c2ecf20Sopenharmony_ci command[6] = LSB_of(pages << (1 - info->smallpageshift)); 2498c2ecf20Sopenharmony_ci command[7] = 0x85; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci /* send command */ 2528c2ecf20Sopenharmony_ci result = sddr55_bulk_transport(us, 2538c2ecf20Sopenharmony_ci DMA_TO_DEVICE, command, 8); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Result for send_command in read_data %d\n", 2568c2ecf20Sopenharmony_ci result); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (result != USB_STOR_XFER_GOOD) { 2598c2ecf20Sopenharmony_ci result = USB_STOR_TRANSPORT_ERROR; 2608c2ecf20Sopenharmony_ci goto leave; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* read data */ 2648c2ecf20Sopenharmony_ci result = sddr55_bulk_transport(us, 2658c2ecf20Sopenharmony_ci DMA_FROM_DEVICE, buffer, len); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci if (result != USB_STOR_XFER_GOOD) { 2688c2ecf20Sopenharmony_ci result = USB_STOR_TRANSPORT_ERROR; 2698c2ecf20Sopenharmony_ci goto leave; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci /* now read status */ 2738c2ecf20Sopenharmony_ci result = sddr55_bulk_transport(us, 2748c2ecf20Sopenharmony_ci DMA_FROM_DEVICE, status, 2); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (result != USB_STOR_XFER_GOOD) { 2778c2ecf20Sopenharmony_ci result = USB_STOR_TRANSPORT_ERROR; 2788c2ecf20Sopenharmony_ci goto leave; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci /* check status for error */ 2828c2ecf20Sopenharmony_ci if (status[0] == 0xff && status[1] == 0x4) { 2838c2ecf20Sopenharmony_ci set_sense_info (3, 0x11, 0); 2848c2ecf20Sopenharmony_ci result = USB_STOR_TRANSPORT_FAILED; 2858c2ecf20Sopenharmony_ci goto leave; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci // Store the data in the transfer buffer 2908c2ecf20Sopenharmony_ci usb_stor_access_xfer_buf(buffer, len, us->srb, 2918c2ecf20Sopenharmony_ci &sg, &offset, TO_XFER_BUF); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci page = 0; 2948c2ecf20Sopenharmony_ci lba++; 2958c2ecf20Sopenharmony_ci sectors -= pages >> info->smallpageshift; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci result = USB_STOR_TRANSPORT_GOOD; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cileave: 3018c2ecf20Sopenharmony_ci kfree(buffer); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci return result; 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic int sddr55_write_data(struct us_data *us, 3078c2ecf20Sopenharmony_ci unsigned int lba, 3088c2ecf20Sopenharmony_ci unsigned int page, 3098c2ecf20Sopenharmony_ci unsigned short sectors) { 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci int result = USB_STOR_TRANSPORT_GOOD; 3128c2ecf20Sopenharmony_ci unsigned char *command = us->iobuf; 3138c2ecf20Sopenharmony_ci unsigned char *status = us->iobuf; 3148c2ecf20Sopenharmony_ci struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra; 3158c2ecf20Sopenharmony_ci unsigned char *buffer; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci unsigned int pba; 3188c2ecf20Sopenharmony_ci unsigned int new_pba; 3198c2ecf20Sopenharmony_ci unsigned long address; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci unsigned short pages; 3228c2ecf20Sopenharmony_ci int i; 3238c2ecf20Sopenharmony_ci unsigned int len, offset; 3248c2ecf20Sopenharmony_ci struct scatterlist *sg; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci /* check if we are allowed to write */ 3278c2ecf20Sopenharmony_ci if (info->read_only || info->force_read_only) { 3288c2ecf20Sopenharmony_ci set_sense_info (7, 0x27, 0); /* read only */ 3298c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_FAILED; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci // Since we only write one block at a time, we have to create 3338c2ecf20Sopenharmony_ci // a bounce buffer and move the data a piece at a time between the 3348c2ecf20Sopenharmony_ci // bounce buffer and the actual transfer buffer. 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci len = min((unsigned int) sectors, (unsigned int) info->blocksize >> 3378c2ecf20Sopenharmony_ci info->smallpageshift) * PAGESIZE; 3388c2ecf20Sopenharmony_ci buffer = kmalloc(len, GFP_NOIO); 3398c2ecf20Sopenharmony_ci if (buffer == NULL) 3408c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 3418c2ecf20Sopenharmony_ci offset = 0; 3428c2ecf20Sopenharmony_ci sg = NULL; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci while (sectors > 0) { 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* have we got to end? */ 3478c2ecf20Sopenharmony_ci if (lba >= info->max_log_blks) 3488c2ecf20Sopenharmony_ci break; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci pba = info->lba_to_pba[lba]; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci // Write as many sectors as possible in this block 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci pages = min((unsigned int) sectors << info->smallpageshift, 3558c2ecf20Sopenharmony_ci info->blocksize - page); 3568c2ecf20Sopenharmony_ci len = pages << info->pageshift; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci // Get the data from the transfer buffer 3598c2ecf20Sopenharmony_ci usb_stor_access_xfer_buf(buffer, len, us->srb, 3608c2ecf20Sopenharmony_ci &sg, &offset, FROM_XFER_BUF); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Write %02X pages, to PBA %04X (LBA %04X) page %02X\n", 3638c2ecf20Sopenharmony_ci pages, pba, lba, page); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci command[4] = 0; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci if (pba == NOT_ALLOCATED) { 3688c2ecf20Sopenharmony_ci /* no pba allocated for this lba, find a free pba to use */ 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci int max_pba = (info->max_log_blks / 250 ) * 256; 3718c2ecf20Sopenharmony_ci int found_count = 0; 3728c2ecf20Sopenharmony_ci int found_pba = -1; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci /* set pba to first block in zone lba is in */ 3758c2ecf20Sopenharmony_ci pba = (lba / 1000) * 1024; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci usb_stor_dbg(us, "No PBA for LBA %04X\n", lba); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (max_pba > 1024) 3808c2ecf20Sopenharmony_ci max_pba = 1024; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci /* 3838c2ecf20Sopenharmony_ci * Scan through the map looking for an unused block 3848c2ecf20Sopenharmony_ci * leave 16 unused blocks at start (or as many as 3858c2ecf20Sopenharmony_ci * possible) since the sddr55 seems to reuse a used 3868c2ecf20Sopenharmony_ci * block when it shouldn't if we don't leave space. 3878c2ecf20Sopenharmony_ci */ 3888c2ecf20Sopenharmony_ci for (i = 0; i < max_pba; i++, pba++) { 3898c2ecf20Sopenharmony_ci if (info->pba_to_lba[pba] == UNUSED_BLOCK) { 3908c2ecf20Sopenharmony_ci found_pba = pba; 3918c2ecf20Sopenharmony_ci if (found_count++ > 16) 3928c2ecf20Sopenharmony_ci break; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci pba = found_pba; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci if (pba == -1) { 3998c2ecf20Sopenharmony_ci /* oh dear */ 4008c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Couldn't find unallocated block\n"); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci set_sense_info (3, 0x31, 0); /* medium error */ 4038c2ecf20Sopenharmony_ci result = USB_STOR_TRANSPORT_FAILED; 4048c2ecf20Sopenharmony_ci goto leave; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Allocating PBA %04X for LBA %04X\n", 4088c2ecf20Sopenharmony_ci pba, lba); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci /* set writing to unallocated block flag */ 4118c2ecf20Sopenharmony_ci command[4] = 0x40; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci address = (pba << info->blockshift) + page; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci command[1] = LSB_of(address>>16); 4178c2ecf20Sopenharmony_ci command[2] = LSB_of(address>>8); 4188c2ecf20Sopenharmony_ci command[3] = LSB_of(address); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci /* set the lba into the command, modulo 1000 */ 4218c2ecf20Sopenharmony_ci command[0] = LSB_of(lba % 1000); 4228c2ecf20Sopenharmony_ci command[6] = MSB_of(lba % 1000); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci command[4] |= LSB_of(pages >> info->smallpageshift); 4258c2ecf20Sopenharmony_ci command[5] = 0xB0; 4268c2ecf20Sopenharmony_ci command[7] = 0x86; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci /* send command */ 4298c2ecf20Sopenharmony_ci result = sddr55_bulk_transport(us, 4308c2ecf20Sopenharmony_ci DMA_TO_DEVICE, command, 8); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci if (result != USB_STOR_XFER_GOOD) { 4338c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Result for send_command in write_data %d\n", 4348c2ecf20Sopenharmony_ci result); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci /* set_sense_info is superfluous here? */ 4378c2ecf20Sopenharmony_ci set_sense_info (3, 0x3, 0);/* peripheral write error */ 4388c2ecf20Sopenharmony_ci result = USB_STOR_TRANSPORT_FAILED; 4398c2ecf20Sopenharmony_ci goto leave; 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* send the data */ 4438c2ecf20Sopenharmony_ci result = sddr55_bulk_transport(us, 4448c2ecf20Sopenharmony_ci DMA_TO_DEVICE, buffer, len); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci if (result != USB_STOR_XFER_GOOD) { 4478c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Result for send_data in write_data %d\n", 4488c2ecf20Sopenharmony_ci result); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci /* set_sense_info is superfluous here? */ 4518c2ecf20Sopenharmony_ci set_sense_info (3, 0x3, 0);/* peripheral write error */ 4528c2ecf20Sopenharmony_ci result = USB_STOR_TRANSPORT_FAILED; 4538c2ecf20Sopenharmony_ci goto leave; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* now read status */ 4578c2ecf20Sopenharmony_ci result = sddr55_bulk_transport(us, DMA_FROM_DEVICE, status, 6); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci if (result != USB_STOR_XFER_GOOD) { 4608c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Result for get_status in write_data %d\n", 4618c2ecf20Sopenharmony_ci result); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci /* set_sense_info is superfluous here? */ 4648c2ecf20Sopenharmony_ci set_sense_info (3, 0x3, 0);/* peripheral write error */ 4658c2ecf20Sopenharmony_ci result = USB_STOR_TRANSPORT_FAILED; 4668c2ecf20Sopenharmony_ci goto leave; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci new_pba = (status[3] + (status[4] << 8) + (status[5] << 16)) 4708c2ecf20Sopenharmony_ci >> info->blockshift; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci /* check status for error */ 4738c2ecf20Sopenharmony_ci if (status[0] == 0xff && status[1] == 0x4) { 4748c2ecf20Sopenharmony_ci info->pba_to_lba[new_pba] = BAD_BLOCK; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci set_sense_info (3, 0x0c, 0); 4778c2ecf20Sopenharmony_ci result = USB_STOR_TRANSPORT_FAILED; 4788c2ecf20Sopenharmony_ci goto leave; 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Updating maps for LBA %04X: old PBA %04X, new PBA %04X\n", 4828c2ecf20Sopenharmony_ci lba, pba, new_pba); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci /* update the lba<->pba maps, note new_pba might be the same as pba */ 4858c2ecf20Sopenharmony_ci info->lba_to_pba[lba] = new_pba; 4868c2ecf20Sopenharmony_ci info->pba_to_lba[pba] = UNUSED_BLOCK; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci /* check that new_pba wasn't already being used */ 4898c2ecf20Sopenharmony_ci if (info->pba_to_lba[new_pba] != UNUSED_BLOCK) { 4908c2ecf20Sopenharmony_ci printk(KERN_ERR "sddr55 error: new PBA %04X already in use for LBA %04X\n", 4918c2ecf20Sopenharmony_ci new_pba, info->pba_to_lba[new_pba]); 4928c2ecf20Sopenharmony_ci info->fatal_error = 1; 4938c2ecf20Sopenharmony_ci set_sense_info (3, 0x31, 0); 4948c2ecf20Sopenharmony_ci result = USB_STOR_TRANSPORT_FAILED; 4958c2ecf20Sopenharmony_ci goto leave; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci /* update the pba<->lba maps for new_pba */ 4998c2ecf20Sopenharmony_ci info->pba_to_lba[new_pba] = lba % 1000; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci page = 0; 5028c2ecf20Sopenharmony_ci lba++; 5038c2ecf20Sopenharmony_ci sectors -= pages >> info->smallpageshift; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci result = USB_STOR_TRANSPORT_GOOD; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci leave: 5088c2ecf20Sopenharmony_ci kfree(buffer); 5098c2ecf20Sopenharmony_ci return result; 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_cistatic int sddr55_read_deviceID(struct us_data *us, 5138c2ecf20Sopenharmony_ci unsigned char *manufacturerID, 5148c2ecf20Sopenharmony_ci unsigned char *deviceID) { 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci int result; 5178c2ecf20Sopenharmony_ci unsigned char *command = us->iobuf; 5188c2ecf20Sopenharmony_ci unsigned char *content = us->iobuf; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci memset(command, 0, 8); 5218c2ecf20Sopenharmony_ci command[5] = 0xB0; 5228c2ecf20Sopenharmony_ci command[7] = 0x84; 5238c2ecf20Sopenharmony_ci result = sddr55_bulk_transport(us, DMA_TO_DEVICE, command, 8); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Result of send_control for device ID is %d\n", 5268c2ecf20Sopenharmony_ci result); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci if (result != USB_STOR_XFER_GOOD) 5298c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci result = sddr55_bulk_transport(us, 5328c2ecf20Sopenharmony_ci DMA_FROM_DEVICE, content, 4); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci if (result != USB_STOR_XFER_GOOD) 5358c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci *manufacturerID = content[0]; 5388c2ecf20Sopenharmony_ci *deviceID = content[1]; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci if (content[0] != 0xff) { 5418c2ecf20Sopenharmony_ci result = sddr55_bulk_transport(us, 5428c2ecf20Sopenharmony_ci DMA_FROM_DEVICE, content, 2); 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_GOOD; 5468c2ecf20Sopenharmony_ci} 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_cistatic int sddr55_reset(struct us_data *us) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci return 0; 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cistatic unsigned long sddr55_get_capacity(struct us_data *us) { 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci unsigned char manufacturerID; 5588c2ecf20Sopenharmony_ci unsigned char deviceID; 5598c2ecf20Sopenharmony_ci int result; 5608c2ecf20Sopenharmony_ci struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Reading capacity...\n"); 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci result = sddr55_read_deviceID(us, 5658c2ecf20Sopenharmony_ci &manufacturerID, 5668c2ecf20Sopenharmony_ci &deviceID); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Result of read_deviceID is %d\n", result); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci if (result != USB_STOR_XFER_GOOD) 5718c2ecf20Sopenharmony_ci return 0; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Device ID = %02X\n", deviceID); 5748c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Manuf ID = %02X\n", manufacturerID); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci info->pageshift = 9; 5778c2ecf20Sopenharmony_ci info->smallpageshift = 0; 5788c2ecf20Sopenharmony_ci info->blocksize = 16; 5798c2ecf20Sopenharmony_ci info->blockshift = 4; 5808c2ecf20Sopenharmony_ci info->blockmask = 15; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci switch (deviceID) { 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci case 0x6e: // 1MB 5858c2ecf20Sopenharmony_ci case 0xe8: 5868c2ecf20Sopenharmony_ci case 0xec: 5878c2ecf20Sopenharmony_ci info->pageshift = 8; 5888c2ecf20Sopenharmony_ci info->smallpageshift = 1; 5898c2ecf20Sopenharmony_ci return 0x00100000; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci case 0xea: // 2MB 5928c2ecf20Sopenharmony_ci case 0x64: 5938c2ecf20Sopenharmony_ci info->pageshift = 8; 5948c2ecf20Sopenharmony_ci info->smallpageshift = 1; 5958c2ecf20Sopenharmony_ci fallthrough; 5968c2ecf20Sopenharmony_ci case 0x5d: // 5d is a ROM card with pagesize 512. 5978c2ecf20Sopenharmony_ci return 0x00200000; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci case 0xe3: // 4MB 6008c2ecf20Sopenharmony_ci case 0xe5: 6018c2ecf20Sopenharmony_ci case 0x6b: 6028c2ecf20Sopenharmony_ci case 0xd5: 6038c2ecf20Sopenharmony_ci return 0x00400000; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci case 0xe6: // 8MB 6068c2ecf20Sopenharmony_ci case 0xd6: 6078c2ecf20Sopenharmony_ci return 0x00800000; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci case 0x73: // 16MB 6108c2ecf20Sopenharmony_ci info->blocksize = 32; 6118c2ecf20Sopenharmony_ci info->blockshift = 5; 6128c2ecf20Sopenharmony_ci info->blockmask = 31; 6138c2ecf20Sopenharmony_ci return 0x01000000; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci case 0x75: // 32MB 6168c2ecf20Sopenharmony_ci info->blocksize = 32; 6178c2ecf20Sopenharmony_ci info->blockshift = 5; 6188c2ecf20Sopenharmony_ci info->blockmask = 31; 6198c2ecf20Sopenharmony_ci return 0x02000000; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci case 0x76: // 64MB 6228c2ecf20Sopenharmony_ci info->blocksize = 32; 6238c2ecf20Sopenharmony_ci info->blockshift = 5; 6248c2ecf20Sopenharmony_ci info->blockmask = 31; 6258c2ecf20Sopenharmony_ci return 0x04000000; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci case 0x79: // 128MB 6288c2ecf20Sopenharmony_ci info->blocksize = 32; 6298c2ecf20Sopenharmony_ci info->blockshift = 5; 6308c2ecf20Sopenharmony_ci info->blockmask = 31; 6318c2ecf20Sopenharmony_ci return 0x08000000; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci default: // unknown 6348c2ecf20Sopenharmony_ci return 0; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci} 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_cistatic int sddr55_read_map(struct us_data *us) { 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci struct sddr55_card_info *info = (struct sddr55_card_info *)(us->extra); 6428c2ecf20Sopenharmony_ci int numblocks; 6438c2ecf20Sopenharmony_ci unsigned char *buffer; 6448c2ecf20Sopenharmony_ci unsigned char *command = us->iobuf; 6458c2ecf20Sopenharmony_ci int i; 6468c2ecf20Sopenharmony_ci unsigned short lba; 6478c2ecf20Sopenharmony_ci unsigned short max_lba; 6488c2ecf20Sopenharmony_ci int result; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci if (!info->capacity) 6518c2ecf20Sopenharmony_ci return -1; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci numblocks = info->capacity >> (info->blockshift + info->pageshift); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci buffer = kmalloc_array(numblocks, 2, GFP_NOIO ); 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci if (!buffer) 6588c2ecf20Sopenharmony_ci return -1; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci memset(command, 0, 8); 6618c2ecf20Sopenharmony_ci command[5] = 0xB0; 6628c2ecf20Sopenharmony_ci command[6] = numblocks * 2 / 256; 6638c2ecf20Sopenharmony_ci command[7] = 0x8A; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci result = sddr55_bulk_transport(us, DMA_TO_DEVICE, command, 8); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci if ( result != USB_STOR_XFER_GOOD) { 6688c2ecf20Sopenharmony_ci kfree (buffer); 6698c2ecf20Sopenharmony_ci return -1; 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci result = sddr55_bulk_transport(us, DMA_FROM_DEVICE, buffer, numblocks * 2); 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci if ( result != USB_STOR_XFER_GOOD) { 6758c2ecf20Sopenharmony_ci kfree (buffer); 6768c2ecf20Sopenharmony_ci return -1; 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci result = sddr55_bulk_transport(us, DMA_FROM_DEVICE, command, 2); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci if ( result != USB_STOR_XFER_GOOD) { 6828c2ecf20Sopenharmony_ci kfree (buffer); 6838c2ecf20Sopenharmony_ci return -1; 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci kfree(info->lba_to_pba); 6878c2ecf20Sopenharmony_ci kfree(info->pba_to_lba); 6888c2ecf20Sopenharmony_ci info->lba_to_pba = kmalloc_array(numblocks, sizeof(int), GFP_NOIO); 6898c2ecf20Sopenharmony_ci info->pba_to_lba = kmalloc_array(numblocks, sizeof(int), GFP_NOIO); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci if (info->lba_to_pba == NULL || info->pba_to_lba == NULL) { 6928c2ecf20Sopenharmony_ci kfree(info->lba_to_pba); 6938c2ecf20Sopenharmony_ci kfree(info->pba_to_lba); 6948c2ecf20Sopenharmony_ci info->lba_to_pba = NULL; 6958c2ecf20Sopenharmony_ci info->pba_to_lba = NULL; 6968c2ecf20Sopenharmony_ci kfree(buffer); 6978c2ecf20Sopenharmony_ci return -1; 6988c2ecf20Sopenharmony_ci } 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci memset(info->lba_to_pba, 0xff, numblocks*sizeof(int)); 7018c2ecf20Sopenharmony_ci memset(info->pba_to_lba, 0xff, numblocks*sizeof(int)); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci /* set maximum lba */ 7048c2ecf20Sopenharmony_ci max_lba = info->max_log_blks; 7058c2ecf20Sopenharmony_ci if (max_lba > 1000) 7068c2ecf20Sopenharmony_ci max_lba = 1000; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci /* 7098c2ecf20Sopenharmony_ci * Each block is 64 bytes of control data, so block i is located in 7108c2ecf20Sopenharmony_ci * scatterlist block i*64/128k = i*(2^6)*(2^-17) = i*(2^-11) 7118c2ecf20Sopenharmony_ci */ 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci for (i=0; i<numblocks; i++) { 7148c2ecf20Sopenharmony_ci int zone = i / 1024; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci lba = short_pack(buffer[i * 2], buffer[i * 2 + 1]); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci /* 7198c2ecf20Sopenharmony_ci * Every 1024 physical blocks ("zone"), the LBA numbers 7208c2ecf20Sopenharmony_ci * go back to zero, but are within a higher 7218c2ecf20Sopenharmony_ci * block of LBA's. Also, there is a maximum of 7228c2ecf20Sopenharmony_ci * 1000 LBA's per zone. In other words, in PBA 7238c2ecf20Sopenharmony_ci * 1024-2047 you will find LBA 0-999 which are 7248c2ecf20Sopenharmony_ci * really LBA 1000-1999. Yes, this wastes 24 7258c2ecf20Sopenharmony_ci * physical blocks per zone. Go figure. 7268c2ecf20Sopenharmony_ci * These devices can have blocks go bad, so there 7278c2ecf20Sopenharmony_ci * are 24 spare blocks to use when blocks do go bad. 7288c2ecf20Sopenharmony_ci */ 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci /* 7318c2ecf20Sopenharmony_ci * SDDR55 returns 0xffff for a bad block, and 0x400 for the 7328c2ecf20Sopenharmony_ci * CIS block. (Is this true for cards 8MB or less??) 7338c2ecf20Sopenharmony_ci * Record these in the physical to logical map 7348c2ecf20Sopenharmony_ci */ 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci info->pba_to_lba[i] = lba; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci if (lba >= max_lba) { 7398c2ecf20Sopenharmony_ci continue; 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci if (info->lba_to_pba[lba + zone * 1000] != NOT_ALLOCATED && 7438c2ecf20Sopenharmony_ci !info->force_read_only) { 7448c2ecf20Sopenharmony_ci printk(KERN_WARNING 7458c2ecf20Sopenharmony_ci "sddr55: map inconsistency at LBA %04X\n", 7468c2ecf20Sopenharmony_ci lba + zone * 1000); 7478c2ecf20Sopenharmony_ci info->force_read_only = 1; 7488c2ecf20Sopenharmony_ci } 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci if (lba<0x10 || (lba>=0x3E0 && lba<0x3EF)) 7518c2ecf20Sopenharmony_ci usb_stor_dbg(us, "LBA %04X <-> PBA %04X\n", lba, i); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci info->lba_to_pba[lba + zone * 1000] = i; 7548c2ecf20Sopenharmony_ci } 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci kfree(buffer); 7578c2ecf20Sopenharmony_ci return 0; 7588c2ecf20Sopenharmony_ci} 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_cistatic void sddr55_card_info_destructor(void *extra) { 7628c2ecf20Sopenharmony_ci struct sddr55_card_info *info = (struct sddr55_card_info *)extra; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci if (!extra) 7658c2ecf20Sopenharmony_ci return; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci kfree(info->lba_to_pba); 7688c2ecf20Sopenharmony_ci kfree(info->pba_to_lba); 7698c2ecf20Sopenharmony_ci} 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci/* 7738c2ecf20Sopenharmony_ci * Transport for the Sandisk SDDR-55 7748c2ecf20Sopenharmony_ci */ 7758c2ecf20Sopenharmony_cistatic int sddr55_transport(struct scsi_cmnd *srb, struct us_data *us) 7768c2ecf20Sopenharmony_ci{ 7778c2ecf20Sopenharmony_ci int result; 7788c2ecf20Sopenharmony_ci static unsigned char inquiry_response[8] = { 7798c2ecf20Sopenharmony_ci 0x00, 0x80, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x00 7808c2ecf20Sopenharmony_ci }; 7818c2ecf20Sopenharmony_ci // write-protected for now, no block descriptor support 7828c2ecf20Sopenharmony_ci static unsigned char mode_page_01[20] = { 7838c2ecf20Sopenharmony_ci 0x0, 0x12, 0x00, 0x80, 0x0, 0x0, 0x0, 0x0, 7848c2ecf20Sopenharmony_ci 0x01, 0x0A, 7858c2ecf20Sopenharmony_ci 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 7868c2ecf20Sopenharmony_ci }; 7878c2ecf20Sopenharmony_ci unsigned char *ptr = us->iobuf; 7888c2ecf20Sopenharmony_ci unsigned long capacity; 7898c2ecf20Sopenharmony_ci unsigned int lba; 7908c2ecf20Sopenharmony_ci unsigned int pba; 7918c2ecf20Sopenharmony_ci unsigned int page; 7928c2ecf20Sopenharmony_ci unsigned short pages; 7938c2ecf20Sopenharmony_ci struct sddr55_card_info *info; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci if (!us->extra) { 7968c2ecf20Sopenharmony_ci us->extra = kzalloc( 7978c2ecf20Sopenharmony_ci sizeof(struct sddr55_card_info), GFP_NOIO); 7988c2ecf20Sopenharmony_ci if (!us->extra) 7998c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 8008c2ecf20Sopenharmony_ci us->extra_destructor = sddr55_card_info_destructor; 8018c2ecf20Sopenharmony_ci } 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci info = (struct sddr55_card_info *)(us->extra); 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci if (srb->cmnd[0] == REQUEST_SENSE) { 8068c2ecf20Sopenharmony_ci usb_stor_dbg(us, "request sense %02x/%02x/%02x\n", 8078c2ecf20Sopenharmony_ci info->sense_data[2], 8088c2ecf20Sopenharmony_ci info->sense_data[12], 8098c2ecf20Sopenharmony_ci info->sense_data[13]); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci memcpy (ptr, info->sense_data, sizeof info->sense_data); 8128c2ecf20Sopenharmony_ci ptr[0] = 0x70; 8138c2ecf20Sopenharmony_ci ptr[7] = 11; 8148c2ecf20Sopenharmony_ci usb_stor_set_xfer_buf (ptr, sizeof info->sense_data, srb); 8158c2ecf20Sopenharmony_ci memset (info->sense_data, 0, sizeof info->sense_data); 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_GOOD; 8188c2ecf20Sopenharmony_ci } 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci memset (info->sense_data, 0, sizeof info->sense_data); 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci /* 8238c2ecf20Sopenharmony_ci * Dummy up a response for INQUIRY since SDDR55 doesn't 8248c2ecf20Sopenharmony_ci * respond to INQUIRY commands 8258c2ecf20Sopenharmony_ci */ 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci if (srb->cmnd[0] == INQUIRY) { 8288c2ecf20Sopenharmony_ci memcpy(ptr, inquiry_response, 8); 8298c2ecf20Sopenharmony_ci fill_inquiry_response(us, ptr, 36); 8308c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_GOOD; 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci /* 8348c2ecf20Sopenharmony_ci * only check card status if the map isn't allocated, ie no card seen yet 8358c2ecf20Sopenharmony_ci * or if it's been over half a second since we last accessed it 8368c2ecf20Sopenharmony_ci */ 8378c2ecf20Sopenharmony_ci if (info->lba_to_pba == NULL || time_after(jiffies, info->last_access + HZ/2)) { 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci /* check to see if a card is fitted */ 8408c2ecf20Sopenharmony_ci result = sddr55_status (us); 8418c2ecf20Sopenharmony_ci if (result) { 8428c2ecf20Sopenharmony_ci result = sddr55_status (us); 8438c2ecf20Sopenharmony_ci if (!result) { 8448c2ecf20Sopenharmony_ci set_sense_info (6, 0x28, 0); /* new media, set unit attention, not ready to ready */ 8458c2ecf20Sopenharmony_ci } 8468c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_FAILED; 8478c2ecf20Sopenharmony_ci } 8488c2ecf20Sopenharmony_ci } 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci /* 8518c2ecf20Sopenharmony_ci * if we detected a problem with the map when writing, 8528c2ecf20Sopenharmony_ci * don't allow any more access 8538c2ecf20Sopenharmony_ci */ 8548c2ecf20Sopenharmony_ci if (info->fatal_error) { 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci set_sense_info (3, 0x31, 0); 8578c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_FAILED; 8588c2ecf20Sopenharmony_ci } 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci if (srb->cmnd[0] == READ_CAPACITY) { 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci capacity = sddr55_get_capacity(us); 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci if (!capacity) { 8658c2ecf20Sopenharmony_ci set_sense_info (3, 0x30, 0); /* incompatible medium */ 8668c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_FAILED; 8678c2ecf20Sopenharmony_ci } 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci info->capacity = capacity; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci /* 8728c2ecf20Sopenharmony_ci * figure out the maximum logical block number, allowing for 8738c2ecf20Sopenharmony_ci * the fact that only 250 out of every 256 are used 8748c2ecf20Sopenharmony_ci */ 8758c2ecf20Sopenharmony_ci info->max_log_blks = ((info->capacity >> (info->pageshift + info->blockshift)) / 256) * 250; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci /* 8788c2ecf20Sopenharmony_ci * Last page in the card, adjust as we only use 250 out of 8798c2ecf20Sopenharmony_ci * every 256 pages 8808c2ecf20Sopenharmony_ci */ 8818c2ecf20Sopenharmony_ci capacity = (capacity / 256) * 250; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci capacity /= PAGESIZE; 8848c2ecf20Sopenharmony_ci capacity--; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci ((__be32 *) ptr)[0] = cpu_to_be32(capacity); 8878c2ecf20Sopenharmony_ci ((__be32 *) ptr)[1] = cpu_to_be32(PAGESIZE); 8888c2ecf20Sopenharmony_ci usb_stor_set_xfer_buf(ptr, 8, srb); 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci sddr55_read_map(us); 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_GOOD; 8938c2ecf20Sopenharmony_ci } 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci if (srb->cmnd[0] == MODE_SENSE_10) { 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci memcpy(ptr, mode_page_01, sizeof mode_page_01); 8988c2ecf20Sopenharmony_ci ptr[3] = (info->read_only || info->force_read_only) ? 0x80 : 0; 8998c2ecf20Sopenharmony_ci usb_stor_set_xfer_buf(ptr, sizeof(mode_page_01), srb); 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci if ( (srb->cmnd[2] & 0x3F) == 0x01 ) { 9028c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Dummy up request for mode page 1\n"); 9038c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_GOOD; 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci } else if ( (srb->cmnd[2] & 0x3F) == 0x3F ) { 9068c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Dummy up request for all mode pages\n"); 9078c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_GOOD; 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci set_sense_info (5, 0x24, 0); /* invalid field in command */ 9118c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_FAILED; 9128c2ecf20Sopenharmony_ci } 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) { 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci usb_stor_dbg(us, "%s medium removal. Not that I can do anything about it...\n", 9178c2ecf20Sopenharmony_ci (srb->cmnd[4]&0x03) ? "Prevent" : "Allow"); 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_GOOD; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci } 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci if (srb->cmnd[0] == READ_10 || srb->cmnd[0] == WRITE_10) { 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci page = short_pack(srb->cmnd[3], srb->cmnd[2]); 9268c2ecf20Sopenharmony_ci page <<= 16; 9278c2ecf20Sopenharmony_ci page |= short_pack(srb->cmnd[5], srb->cmnd[4]); 9288c2ecf20Sopenharmony_ci pages = short_pack(srb->cmnd[8], srb->cmnd[7]); 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci page <<= info->smallpageshift; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci // convert page to block and page-within-block 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci lba = page >> info->blockshift; 9358c2ecf20Sopenharmony_ci page = page & info->blockmask; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci // locate physical block corresponding to logical block 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci if (lba >= info->max_log_blks) { 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Error: Requested LBA %04X exceeds maximum block %04X\n", 9428c2ecf20Sopenharmony_ci lba, info->max_log_blks - 1); 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci set_sense_info (5, 0x24, 0); /* invalid field in command */ 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_FAILED; 9478c2ecf20Sopenharmony_ci } 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci pba = info->lba_to_pba[lba]; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci if (srb->cmnd[0] == WRITE_10) { 9528c2ecf20Sopenharmony_ci usb_stor_dbg(us, "WRITE_10: write block %04X (LBA %04X) page %01X pages %d\n", 9538c2ecf20Sopenharmony_ci pba, lba, page, pages); 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci return sddr55_write_data(us, lba, page, pages); 9568c2ecf20Sopenharmony_ci } else { 9578c2ecf20Sopenharmony_ci usb_stor_dbg(us, "READ_10: read block %04X (LBA %04X) page %01X pages %d\n", 9588c2ecf20Sopenharmony_ci pba, lba, page, pages); 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci return sddr55_read_data(us, lba, page, pages); 9618c2ecf20Sopenharmony_ci } 9628c2ecf20Sopenharmony_ci } 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci if (srb->cmnd[0] == TEST_UNIT_READY) { 9668c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_GOOD; 9678c2ecf20Sopenharmony_ci } 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci if (srb->cmnd[0] == START_STOP) { 9708c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_GOOD; 9718c2ecf20Sopenharmony_ci } 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci set_sense_info (5, 0x20, 0); /* illegal command */ 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_FAILED; // FIXME: sense buffer? 9768c2ecf20Sopenharmony_ci} 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_cistatic struct scsi_host_template sddr55_host_template; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_cistatic int sddr55_probe(struct usb_interface *intf, 9818c2ecf20Sopenharmony_ci const struct usb_device_id *id) 9828c2ecf20Sopenharmony_ci{ 9838c2ecf20Sopenharmony_ci struct us_data *us; 9848c2ecf20Sopenharmony_ci int result; 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci result = usb_stor_probe1(&us, intf, id, 9878c2ecf20Sopenharmony_ci (id - sddr55_usb_ids) + sddr55_unusual_dev_list, 9888c2ecf20Sopenharmony_ci &sddr55_host_template); 9898c2ecf20Sopenharmony_ci if (result) 9908c2ecf20Sopenharmony_ci return result; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci us->transport_name = "SDDR55"; 9938c2ecf20Sopenharmony_ci us->transport = sddr55_transport; 9948c2ecf20Sopenharmony_ci us->transport_reset = sddr55_reset; 9958c2ecf20Sopenharmony_ci us->max_lun = 0; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci result = usb_stor_probe2(us); 9988c2ecf20Sopenharmony_ci return result; 9998c2ecf20Sopenharmony_ci} 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_cistatic struct usb_driver sddr55_driver = { 10028c2ecf20Sopenharmony_ci .name = DRV_NAME, 10038c2ecf20Sopenharmony_ci .probe = sddr55_probe, 10048c2ecf20Sopenharmony_ci .disconnect = usb_stor_disconnect, 10058c2ecf20Sopenharmony_ci .suspend = usb_stor_suspend, 10068c2ecf20Sopenharmony_ci .resume = usb_stor_resume, 10078c2ecf20Sopenharmony_ci .reset_resume = usb_stor_reset_resume, 10088c2ecf20Sopenharmony_ci .pre_reset = usb_stor_pre_reset, 10098c2ecf20Sopenharmony_ci .post_reset = usb_stor_post_reset, 10108c2ecf20Sopenharmony_ci .id_table = sddr55_usb_ids, 10118c2ecf20Sopenharmony_ci .soft_unbind = 1, 10128c2ecf20Sopenharmony_ci .no_dynamic_id = 1, 10138c2ecf20Sopenharmony_ci}; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_cimodule_usb_stor_driver(sddr55_driver, sddr55_host_template, DRV_NAME); 1016