18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for Alauda-based card readers 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Current development and maintenance by: 68c2ecf20Sopenharmony_ci * (c) 2005 Daniel Drake <dsd@gentoo.org> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * The 'Alauda' is a chip manufacturered by RATOC for OEM use. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Alauda implements a vendor-specific command set to access two media reader 118c2ecf20Sopenharmony_ci * ports (XD, SmartMedia). This driver converts SCSI commands to the commands 128c2ecf20Sopenharmony_ci * which are accepted by these devices. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * The driver was developed through reverse-engineering, with the help of the 158c2ecf20Sopenharmony_ci * sddr09 driver which has many similarities, and with some help from the 168c2ecf20Sopenharmony_ci * (very old) vendor-supplied GPL sma03 driver. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * For protocol info, see http://alauda.sourceforge.net 198c2ecf20Sopenharmony_ci */ 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <linux/module.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <scsi/scsi.h> 258c2ecf20Sopenharmony_ci#include <scsi/scsi_cmnd.h> 268c2ecf20Sopenharmony_ci#include <scsi/scsi_device.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include "usb.h" 298c2ecf20Sopenharmony_ci#include "transport.h" 308c2ecf20Sopenharmony_ci#include "protocol.h" 318c2ecf20Sopenharmony_ci#include "debug.h" 328c2ecf20Sopenharmony_ci#include "scsiglue.h" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define DRV_NAME "ums-alauda" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for Alauda-based card readers"); 378c2ecf20Sopenharmony_ciMODULE_AUTHOR("Daniel Drake <dsd@gentoo.org>"); 388c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 398c2ecf20Sopenharmony_ciMODULE_IMPORT_NS(USB_STORAGE); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* 428c2ecf20Sopenharmony_ci * Status bytes 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_ci#define ALAUDA_STATUS_ERROR 0x01 458c2ecf20Sopenharmony_ci#define ALAUDA_STATUS_READY 0x40 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* 488c2ecf20Sopenharmony_ci * Control opcodes (for request field) 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_ci#define ALAUDA_GET_XD_MEDIA_STATUS 0x08 518c2ecf20Sopenharmony_ci#define ALAUDA_GET_SM_MEDIA_STATUS 0x98 528c2ecf20Sopenharmony_ci#define ALAUDA_ACK_XD_MEDIA_CHANGE 0x0a 538c2ecf20Sopenharmony_ci#define ALAUDA_ACK_SM_MEDIA_CHANGE 0x9a 548c2ecf20Sopenharmony_ci#define ALAUDA_GET_XD_MEDIA_SIG 0x86 558c2ecf20Sopenharmony_ci#define ALAUDA_GET_SM_MEDIA_SIG 0x96 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* 588c2ecf20Sopenharmony_ci * Bulk command identity (byte 0) 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_ci#define ALAUDA_BULK_CMD 0x40 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* 638c2ecf20Sopenharmony_ci * Bulk opcodes (byte 1) 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_ci#define ALAUDA_BULK_GET_REDU_DATA 0x85 668c2ecf20Sopenharmony_ci#define ALAUDA_BULK_READ_BLOCK 0x94 678c2ecf20Sopenharmony_ci#define ALAUDA_BULK_ERASE_BLOCK 0xa3 688c2ecf20Sopenharmony_ci#define ALAUDA_BULK_WRITE_BLOCK 0xb4 698c2ecf20Sopenharmony_ci#define ALAUDA_BULK_GET_STATUS2 0xb7 708c2ecf20Sopenharmony_ci#define ALAUDA_BULK_RESET_MEDIA 0xe0 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* 738c2ecf20Sopenharmony_ci * Port to operate on (byte 8) 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_ci#define ALAUDA_PORT_XD 0x00 768c2ecf20Sopenharmony_ci#define ALAUDA_PORT_SM 0x01 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* 798c2ecf20Sopenharmony_ci * LBA and PBA are unsigned ints. Special values. 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ci#define UNDEF 0xffff 828c2ecf20Sopenharmony_ci#define SPARE 0xfffe 838c2ecf20Sopenharmony_ci#define UNUSABLE 0xfffd 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistruct alauda_media_info { 868c2ecf20Sopenharmony_ci unsigned long capacity; /* total media size in bytes */ 878c2ecf20Sopenharmony_ci unsigned int pagesize; /* page size in bytes */ 888c2ecf20Sopenharmony_ci unsigned int blocksize; /* number of pages per block */ 898c2ecf20Sopenharmony_ci unsigned int uzonesize; /* number of usable blocks per zone */ 908c2ecf20Sopenharmony_ci unsigned int zonesize; /* number of blocks per zone */ 918c2ecf20Sopenharmony_ci unsigned int blockmask; /* mask to get page from address */ 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci unsigned char pageshift; 948c2ecf20Sopenharmony_ci unsigned char blockshift; 958c2ecf20Sopenharmony_ci unsigned char zoneshift; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci u16 **lba_to_pba; /* logical to physical block map */ 988c2ecf20Sopenharmony_ci u16 **pba_to_lba; /* physical to logical block map */ 998c2ecf20Sopenharmony_ci}; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistruct alauda_info { 1028c2ecf20Sopenharmony_ci struct alauda_media_info port[2]; 1038c2ecf20Sopenharmony_ci int wr_ep; /* endpoint to write data out of */ 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci unsigned char sense_key; 1068c2ecf20Sopenharmony_ci unsigned long sense_asc; /* additional sense code */ 1078c2ecf20Sopenharmony_ci unsigned long sense_ascq; /* additional sense code qualifier */ 1088c2ecf20Sopenharmony_ci}; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci#define short_pack(lsb,msb) ( ((u16)(lsb)) | ( ((u16)(msb))<<8 ) ) 1118c2ecf20Sopenharmony_ci#define LSB_of(s) ((s)&0xFF) 1128c2ecf20Sopenharmony_ci#define MSB_of(s) ((s)>>8) 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci#define MEDIA_PORT(us) us->srb->device->lun 1158c2ecf20Sopenharmony_ci#define MEDIA_INFO(us) ((struct alauda_info *)us->extra)->port[MEDIA_PORT(us)] 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci#define PBA_LO(pba) ((pba & 0xF) << 5) 1188c2ecf20Sopenharmony_ci#define PBA_HI(pba) (pba >> 3) 1198c2ecf20Sopenharmony_ci#define PBA_ZONE(pba) (pba >> 11) 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic int init_alauda(struct us_data *us); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci/* 1258c2ecf20Sopenharmony_ci * The table of devices 1268c2ecf20Sopenharmony_ci */ 1278c2ecf20Sopenharmony_ci#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ 1288c2ecf20Sopenharmony_ci vendorName, productName, useProtocol, useTransport, \ 1298c2ecf20Sopenharmony_ci initFunction, flags) \ 1308c2ecf20Sopenharmony_ci{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ 1318c2ecf20Sopenharmony_ci .driver_info = (flags) } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic struct usb_device_id alauda_usb_ids[] = { 1348c2ecf20Sopenharmony_ci# include "unusual_alauda.h" 1358c2ecf20Sopenharmony_ci { } /* Terminating entry */ 1368c2ecf20Sopenharmony_ci}; 1378c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, alauda_usb_ids); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci#undef UNUSUAL_DEV 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci/* 1428c2ecf20Sopenharmony_ci * The flags table 1438c2ecf20Sopenharmony_ci */ 1448c2ecf20Sopenharmony_ci#define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \ 1458c2ecf20Sopenharmony_ci vendor_name, product_name, use_protocol, use_transport, \ 1468c2ecf20Sopenharmony_ci init_function, Flags) \ 1478c2ecf20Sopenharmony_ci{ \ 1488c2ecf20Sopenharmony_ci .vendorName = vendor_name, \ 1498c2ecf20Sopenharmony_ci .productName = product_name, \ 1508c2ecf20Sopenharmony_ci .useProtocol = use_protocol, \ 1518c2ecf20Sopenharmony_ci .useTransport = use_transport, \ 1528c2ecf20Sopenharmony_ci .initFunction = init_function, \ 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic struct us_unusual_dev alauda_unusual_dev_list[] = { 1568c2ecf20Sopenharmony_ci# include "unusual_alauda.h" 1578c2ecf20Sopenharmony_ci { } /* Terminating entry */ 1588c2ecf20Sopenharmony_ci}; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci#undef UNUSUAL_DEV 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci/* 1648c2ecf20Sopenharmony_ci * Media handling 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistruct alauda_card_info { 1688c2ecf20Sopenharmony_ci unsigned char id; /* id byte */ 1698c2ecf20Sopenharmony_ci unsigned char chipshift; /* 1<<cs bytes total capacity */ 1708c2ecf20Sopenharmony_ci unsigned char pageshift; /* 1<<ps bytes in a page */ 1718c2ecf20Sopenharmony_ci unsigned char blockshift; /* 1<<bs pages per block */ 1728c2ecf20Sopenharmony_ci unsigned char zoneshift; /* 1<<zs blocks per zone */ 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic struct alauda_card_info alauda_card_ids[] = { 1768c2ecf20Sopenharmony_ci /* NAND flash */ 1778c2ecf20Sopenharmony_ci { 0x6e, 20, 8, 4, 8}, /* 1 MB */ 1788c2ecf20Sopenharmony_ci { 0xe8, 20, 8, 4, 8}, /* 1 MB */ 1798c2ecf20Sopenharmony_ci { 0xec, 20, 8, 4, 8}, /* 1 MB */ 1808c2ecf20Sopenharmony_ci { 0x64, 21, 8, 4, 9}, /* 2 MB */ 1818c2ecf20Sopenharmony_ci { 0xea, 21, 8, 4, 9}, /* 2 MB */ 1828c2ecf20Sopenharmony_ci { 0x6b, 22, 9, 4, 9}, /* 4 MB */ 1838c2ecf20Sopenharmony_ci { 0xe3, 22, 9, 4, 9}, /* 4 MB */ 1848c2ecf20Sopenharmony_ci { 0xe5, 22, 9, 4, 9}, /* 4 MB */ 1858c2ecf20Sopenharmony_ci { 0xe6, 23, 9, 4, 10}, /* 8 MB */ 1868c2ecf20Sopenharmony_ci { 0x73, 24, 9, 5, 10}, /* 16 MB */ 1878c2ecf20Sopenharmony_ci { 0x75, 25, 9, 5, 10}, /* 32 MB */ 1888c2ecf20Sopenharmony_ci { 0x76, 26, 9, 5, 10}, /* 64 MB */ 1898c2ecf20Sopenharmony_ci { 0x79, 27, 9, 5, 10}, /* 128 MB */ 1908c2ecf20Sopenharmony_ci { 0x71, 28, 9, 5, 10}, /* 256 MB */ 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* MASK ROM */ 1938c2ecf20Sopenharmony_ci { 0x5d, 21, 9, 4, 8}, /* 2 MB */ 1948c2ecf20Sopenharmony_ci { 0xd5, 22, 9, 4, 9}, /* 4 MB */ 1958c2ecf20Sopenharmony_ci { 0xd6, 23, 9, 4, 10}, /* 8 MB */ 1968c2ecf20Sopenharmony_ci { 0x57, 24, 9, 4, 11}, /* 16 MB */ 1978c2ecf20Sopenharmony_ci { 0x58, 25, 9, 4, 12}, /* 32 MB */ 1988c2ecf20Sopenharmony_ci { 0,} 1998c2ecf20Sopenharmony_ci}; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic struct alauda_card_info *alauda_card_find_id(unsigned char id) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci int i; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci for (i = 0; alauda_card_ids[i].id != 0; i++) 2068c2ecf20Sopenharmony_ci if (alauda_card_ids[i].id == id) 2078c2ecf20Sopenharmony_ci return &(alauda_card_ids[i]); 2088c2ecf20Sopenharmony_ci return NULL; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci/* 2128c2ecf20Sopenharmony_ci * ECC computation. 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic unsigned char parity[256]; 2168c2ecf20Sopenharmony_cistatic unsigned char ecc2[256]; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic void nand_init_ecc(void) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci int i, j, a; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci parity[0] = 0; 2238c2ecf20Sopenharmony_ci for (i = 1; i < 256; i++) 2248c2ecf20Sopenharmony_ci parity[i] = (parity[i&(i-1)] ^ 1); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci for (i = 0; i < 256; i++) { 2278c2ecf20Sopenharmony_ci a = 0; 2288c2ecf20Sopenharmony_ci for (j = 0; j < 8; j++) { 2298c2ecf20Sopenharmony_ci if (i & (1<<j)) { 2308c2ecf20Sopenharmony_ci if ((j & 1) == 0) 2318c2ecf20Sopenharmony_ci a ^= 0x04; 2328c2ecf20Sopenharmony_ci if ((j & 2) == 0) 2338c2ecf20Sopenharmony_ci a ^= 0x10; 2348c2ecf20Sopenharmony_ci if ((j & 4) == 0) 2358c2ecf20Sopenharmony_ci a ^= 0x40; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci ecc2[i] = ~(a ^ (a<<1) ^ (parity[i] ? 0xa8 : 0)); 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci/* compute 3-byte ecc on 256 bytes */ 2438c2ecf20Sopenharmony_cistatic void nand_compute_ecc(unsigned char *data, unsigned char *ecc) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci int i, j, a; 2468c2ecf20Sopenharmony_ci unsigned char par = 0, bit, bits[8] = {0}; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* collect 16 checksum bits */ 2498c2ecf20Sopenharmony_ci for (i = 0; i < 256; i++) { 2508c2ecf20Sopenharmony_ci par ^= data[i]; 2518c2ecf20Sopenharmony_ci bit = parity[data[i]]; 2528c2ecf20Sopenharmony_ci for (j = 0; j < 8; j++) 2538c2ecf20Sopenharmony_ci if ((i & (1<<j)) == 0) 2548c2ecf20Sopenharmony_ci bits[j] ^= bit; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci /* put 4+4+4 = 12 bits in the ecc */ 2588c2ecf20Sopenharmony_ci a = (bits[3] << 6) + (bits[2] << 4) + (bits[1] << 2) + bits[0]; 2598c2ecf20Sopenharmony_ci ecc[0] = ~(a ^ (a<<1) ^ (parity[par] ? 0xaa : 0)); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci a = (bits[7] << 6) + (bits[6] << 4) + (bits[5] << 2) + bits[4]; 2628c2ecf20Sopenharmony_ci ecc[1] = ~(a ^ (a<<1) ^ (parity[par] ? 0xaa : 0)); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci ecc[2] = ecc2[par]; 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic int nand_compare_ecc(unsigned char *data, unsigned char *ecc) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci return (data[0] == ecc[0] && data[1] == ecc[1] && data[2] == ecc[2]); 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic void nand_store_ecc(unsigned char *data, unsigned char *ecc) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci memcpy(data, ecc, 3); 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci/* 2788c2ecf20Sopenharmony_ci * Alauda driver 2798c2ecf20Sopenharmony_ci */ 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci/* 2828c2ecf20Sopenharmony_ci * Forget our PBA <---> LBA mappings for a particular port 2838c2ecf20Sopenharmony_ci */ 2848c2ecf20Sopenharmony_cistatic void alauda_free_maps (struct alauda_media_info *media_info) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci unsigned int shift = media_info->zoneshift 2878c2ecf20Sopenharmony_ci + media_info->blockshift + media_info->pageshift; 2888c2ecf20Sopenharmony_ci unsigned int num_zones = media_info->capacity >> shift; 2898c2ecf20Sopenharmony_ci unsigned int i; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (media_info->lba_to_pba != NULL) 2928c2ecf20Sopenharmony_ci for (i = 0; i < num_zones; i++) { 2938c2ecf20Sopenharmony_ci kfree(media_info->lba_to_pba[i]); 2948c2ecf20Sopenharmony_ci media_info->lba_to_pba[i] = NULL; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (media_info->pba_to_lba != NULL) 2988c2ecf20Sopenharmony_ci for (i = 0; i < num_zones; i++) { 2998c2ecf20Sopenharmony_ci kfree(media_info->pba_to_lba[i]); 3008c2ecf20Sopenharmony_ci media_info->pba_to_lba[i] = NULL; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci/* 3058c2ecf20Sopenharmony_ci * Returns 2 bytes of status data 3068c2ecf20Sopenharmony_ci * The first byte describes media status, and second byte describes door status 3078c2ecf20Sopenharmony_ci */ 3088c2ecf20Sopenharmony_cistatic int alauda_get_media_status(struct us_data *us, unsigned char *data) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci int rc; 3118c2ecf20Sopenharmony_ci unsigned char command; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (MEDIA_PORT(us) == ALAUDA_PORT_XD) 3148c2ecf20Sopenharmony_ci command = ALAUDA_GET_XD_MEDIA_STATUS; 3158c2ecf20Sopenharmony_ci else 3168c2ecf20Sopenharmony_ci command = ALAUDA_GET_SM_MEDIA_STATUS; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci rc = usb_stor_ctrl_transfer(us, us->recv_ctrl_pipe, 3198c2ecf20Sopenharmony_ci command, 0xc0, 0, 1, data, 2); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci if (rc == USB_STOR_XFER_GOOD) 3228c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Media status %02X %02X\n", data[0], data[1]); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci return rc; 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci/* 3288c2ecf20Sopenharmony_ci * Clears the "media was changed" bit so that we know when it changes again 3298c2ecf20Sopenharmony_ci * in the future. 3308c2ecf20Sopenharmony_ci */ 3318c2ecf20Sopenharmony_cistatic int alauda_ack_media(struct us_data *us) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci unsigned char command; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (MEDIA_PORT(us) == ALAUDA_PORT_XD) 3368c2ecf20Sopenharmony_ci command = ALAUDA_ACK_XD_MEDIA_CHANGE; 3378c2ecf20Sopenharmony_ci else 3388c2ecf20Sopenharmony_ci command = ALAUDA_ACK_SM_MEDIA_CHANGE; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci return usb_stor_ctrl_transfer(us, us->send_ctrl_pipe, 3418c2ecf20Sopenharmony_ci command, 0x40, 0, 1, NULL, 0); 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci/* 3458c2ecf20Sopenharmony_ci * Retrieves a 4-byte media signature, which indicates manufacturer, capacity, 3468c2ecf20Sopenharmony_ci * and some other details. 3478c2ecf20Sopenharmony_ci */ 3488c2ecf20Sopenharmony_cistatic int alauda_get_media_signature(struct us_data *us, unsigned char *data) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci unsigned char command; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci if (MEDIA_PORT(us) == ALAUDA_PORT_XD) 3538c2ecf20Sopenharmony_ci command = ALAUDA_GET_XD_MEDIA_SIG; 3548c2ecf20Sopenharmony_ci else 3558c2ecf20Sopenharmony_ci command = ALAUDA_GET_SM_MEDIA_SIG; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci return usb_stor_ctrl_transfer(us, us->recv_ctrl_pipe, 3588c2ecf20Sopenharmony_ci command, 0xc0, 0, 0, data, 4); 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci/* 3628c2ecf20Sopenharmony_ci * Resets the media status (but not the whole device?) 3638c2ecf20Sopenharmony_ci */ 3648c2ecf20Sopenharmony_cistatic int alauda_reset_media(struct us_data *us) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci unsigned char *command = us->iobuf; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci memset(command, 0, 9); 3698c2ecf20Sopenharmony_ci command[0] = ALAUDA_BULK_CMD; 3708c2ecf20Sopenharmony_ci command[1] = ALAUDA_BULK_RESET_MEDIA; 3718c2ecf20Sopenharmony_ci command[8] = MEDIA_PORT(us); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci return usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, 3748c2ecf20Sopenharmony_ci command, 9, NULL); 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci/* 3788c2ecf20Sopenharmony_ci * Examines the media and deduces capacity, etc. 3798c2ecf20Sopenharmony_ci */ 3808c2ecf20Sopenharmony_cistatic int alauda_init_media(struct us_data *us) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci unsigned char *data = us->iobuf; 3838c2ecf20Sopenharmony_ci int ready = 0; 3848c2ecf20Sopenharmony_ci struct alauda_card_info *media_info; 3858c2ecf20Sopenharmony_ci unsigned int num_zones; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci while (ready == 0) { 3888c2ecf20Sopenharmony_ci msleep(20); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (alauda_get_media_status(us, data) != USB_STOR_XFER_GOOD) 3918c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci if (data[0] & 0x10) 3948c2ecf20Sopenharmony_ci ready = 1; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci usb_stor_dbg(us, "We are ready for action!\n"); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci if (alauda_ack_media(us) != USB_STOR_XFER_GOOD) 4008c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci msleep(10); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci if (alauda_get_media_status(us, data) != USB_STOR_XFER_GOOD) 4058c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci if (data[0] != 0x14) { 4088c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Media not ready after ack\n"); 4098c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci if (alauda_get_media_signature(us, data) != USB_STOR_XFER_GOOD) 4138c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Media signature: %4ph\n", data); 4168c2ecf20Sopenharmony_ci media_info = alauda_card_find_id(data[1]); 4178c2ecf20Sopenharmony_ci if (media_info == NULL) { 4188c2ecf20Sopenharmony_ci pr_warn("alauda_init_media: Unrecognised media signature: %4ph\n", 4198c2ecf20Sopenharmony_ci data); 4208c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci MEDIA_INFO(us).capacity = 1 << media_info->chipshift; 4248c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Found media with capacity: %ldMB\n", 4258c2ecf20Sopenharmony_ci MEDIA_INFO(us).capacity >> 20); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci MEDIA_INFO(us).pageshift = media_info->pageshift; 4288c2ecf20Sopenharmony_ci MEDIA_INFO(us).blockshift = media_info->blockshift; 4298c2ecf20Sopenharmony_ci MEDIA_INFO(us).zoneshift = media_info->zoneshift; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci MEDIA_INFO(us).pagesize = 1 << media_info->pageshift; 4328c2ecf20Sopenharmony_ci MEDIA_INFO(us).blocksize = 1 << media_info->blockshift; 4338c2ecf20Sopenharmony_ci MEDIA_INFO(us).zonesize = 1 << media_info->zoneshift; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci MEDIA_INFO(us).uzonesize = ((1 << media_info->zoneshift) / 128) * 125; 4368c2ecf20Sopenharmony_ci MEDIA_INFO(us).blockmask = MEDIA_INFO(us).blocksize - 1; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci num_zones = MEDIA_INFO(us).capacity >> (MEDIA_INFO(us).zoneshift 4398c2ecf20Sopenharmony_ci + MEDIA_INFO(us).blockshift + MEDIA_INFO(us).pageshift); 4408c2ecf20Sopenharmony_ci MEDIA_INFO(us).pba_to_lba = kcalloc(num_zones, sizeof(u16*), GFP_NOIO); 4418c2ecf20Sopenharmony_ci MEDIA_INFO(us).lba_to_pba = kcalloc(num_zones, sizeof(u16*), GFP_NOIO); 4428c2ecf20Sopenharmony_ci if (MEDIA_INFO(us).pba_to_lba == NULL || MEDIA_INFO(us).lba_to_pba == NULL) 4438c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci if (alauda_reset_media(us) != USB_STOR_XFER_GOOD) 4468c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_GOOD; 4498c2ecf20Sopenharmony_ci} 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci/* 4528c2ecf20Sopenharmony_ci * Examines the media status and does the right thing when the media has gone, 4538c2ecf20Sopenharmony_ci * appeared, or changed. 4548c2ecf20Sopenharmony_ci */ 4558c2ecf20Sopenharmony_cistatic int alauda_check_media(struct us_data *us) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci struct alauda_info *info = (struct alauda_info *) us->extra; 4588c2ecf20Sopenharmony_ci unsigned char *status = us->iobuf; 4598c2ecf20Sopenharmony_ci int rc; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci rc = alauda_get_media_status(us, status); 4628c2ecf20Sopenharmony_ci if (rc != USB_STOR_XFER_GOOD) { 4638c2ecf20Sopenharmony_ci status[0] = 0xF0; /* Pretend there's no media */ 4648c2ecf20Sopenharmony_ci status[1] = 0; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci /* Check for no media or door open */ 4688c2ecf20Sopenharmony_ci if ((status[0] & 0x80) || ((status[0] & 0x1F) == 0x10) 4698c2ecf20Sopenharmony_ci || ((status[1] & 0x01) == 0)) { 4708c2ecf20Sopenharmony_ci usb_stor_dbg(us, "No media, or door open\n"); 4718c2ecf20Sopenharmony_ci alauda_free_maps(&MEDIA_INFO(us)); 4728c2ecf20Sopenharmony_ci info->sense_key = 0x02; 4738c2ecf20Sopenharmony_ci info->sense_asc = 0x3A; 4748c2ecf20Sopenharmony_ci info->sense_ascq = 0x00; 4758c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_FAILED; 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci /* Check for media change */ 4798c2ecf20Sopenharmony_ci if (status[0] & 0x08) { 4808c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Media change detected\n"); 4818c2ecf20Sopenharmony_ci alauda_free_maps(&MEDIA_INFO(us)); 4828c2ecf20Sopenharmony_ci alauda_init_media(us); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci info->sense_key = UNIT_ATTENTION; 4858c2ecf20Sopenharmony_ci info->sense_asc = 0x28; 4868c2ecf20Sopenharmony_ci info->sense_ascq = 0x00; 4878c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_FAILED; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_GOOD; 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci/* 4948c2ecf20Sopenharmony_ci * Checks the status from the 2nd status register 4958c2ecf20Sopenharmony_ci * Returns 3 bytes of status data, only the first is known 4968c2ecf20Sopenharmony_ci */ 4978c2ecf20Sopenharmony_cistatic int alauda_check_status2(struct us_data *us) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci int rc; 5008c2ecf20Sopenharmony_ci unsigned char command[] = { 5018c2ecf20Sopenharmony_ci ALAUDA_BULK_CMD, ALAUDA_BULK_GET_STATUS2, 5028c2ecf20Sopenharmony_ci 0, 0, 0, 0, 3, 0, MEDIA_PORT(us) 5038c2ecf20Sopenharmony_ci }; 5048c2ecf20Sopenharmony_ci unsigned char data[3]; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, 5078c2ecf20Sopenharmony_ci command, 9, NULL); 5088c2ecf20Sopenharmony_ci if (rc != USB_STOR_XFER_GOOD) 5098c2ecf20Sopenharmony_ci return rc; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci rc = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, 5128c2ecf20Sopenharmony_ci data, 3, NULL); 5138c2ecf20Sopenharmony_ci if (rc != USB_STOR_XFER_GOOD) 5148c2ecf20Sopenharmony_ci return rc; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci usb_stor_dbg(us, "%3ph\n", data); 5178c2ecf20Sopenharmony_ci if (data[0] & ALAUDA_STATUS_ERROR) 5188c2ecf20Sopenharmony_ci return USB_STOR_XFER_ERROR; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci return USB_STOR_XFER_GOOD; 5218c2ecf20Sopenharmony_ci} 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci/* 5248c2ecf20Sopenharmony_ci * Gets the redundancy data for the first page of a PBA 5258c2ecf20Sopenharmony_ci * Returns 16 bytes. 5268c2ecf20Sopenharmony_ci */ 5278c2ecf20Sopenharmony_cistatic int alauda_get_redu_data(struct us_data *us, u16 pba, unsigned char *data) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci int rc; 5308c2ecf20Sopenharmony_ci unsigned char command[] = { 5318c2ecf20Sopenharmony_ci ALAUDA_BULK_CMD, ALAUDA_BULK_GET_REDU_DATA, 5328c2ecf20Sopenharmony_ci PBA_HI(pba), PBA_ZONE(pba), 0, PBA_LO(pba), 0, 0, MEDIA_PORT(us) 5338c2ecf20Sopenharmony_ci }; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, 5368c2ecf20Sopenharmony_ci command, 9, NULL); 5378c2ecf20Sopenharmony_ci if (rc != USB_STOR_XFER_GOOD) 5388c2ecf20Sopenharmony_ci return rc; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci return usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, 5418c2ecf20Sopenharmony_ci data, 16, NULL); 5428c2ecf20Sopenharmony_ci} 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci/* 5458c2ecf20Sopenharmony_ci * Finds the first unused PBA in a zone 5468c2ecf20Sopenharmony_ci * Returns the absolute PBA of an unused PBA, or 0 if none found. 5478c2ecf20Sopenharmony_ci */ 5488c2ecf20Sopenharmony_cistatic u16 alauda_find_unused_pba(struct alauda_media_info *info, 5498c2ecf20Sopenharmony_ci unsigned int zone) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci u16 *pba_to_lba = info->pba_to_lba[zone]; 5528c2ecf20Sopenharmony_ci unsigned int i; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci for (i = 0; i < info->zonesize; i++) 5558c2ecf20Sopenharmony_ci if (pba_to_lba[i] == UNDEF) 5568c2ecf20Sopenharmony_ci return (zone << info->zoneshift) + i; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci return 0; 5598c2ecf20Sopenharmony_ci} 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci/* 5628c2ecf20Sopenharmony_ci * Reads the redundancy data for all PBA's in a zone 5638c2ecf20Sopenharmony_ci * Produces lba <--> pba mappings 5648c2ecf20Sopenharmony_ci */ 5658c2ecf20Sopenharmony_cistatic int alauda_read_map(struct us_data *us, unsigned int zone) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci unsigned char *data = us->iobuf; 5688c2ecf20Sopenharmony_ci int result; 5698c2ecf20Sopenharmony_ci int i, j; 5708c2ecf20Sopenharmony_ci unsigned int zonesize = MEDIA_INFO(us).zonesize; 5718c2ecf20Sopenharmony_ci unsigned int uzonesize = MEDIA_INFO(us).uzonesize; 5728c2ecf20Sopenharmony_ci unsigned int lba_offset, lba_real, blocknum; 5738c2ecf20Sopenharmony_ci unsigned int zone_base_lba = zone * uzonesize; 5748c2ecf20Sopenharmony_ci unsigned int zone_base_pba = zone * zonesize; 5758c2ecf20Sopenharmony_ci u16 *lba_to_pba = kcalloc(zonesize, sizeof(u16), GFP_NOIO); 5768c2ecf20Sopenharmony_ci u16 *pba_to_lba = kcalloc(zonesize, sizeof(u16), GFP_NOIO); 5778c2ecf20Sopenharmony_ci if (lba_to_pba == NULL || pba_to_lba == NULL) { 5788c2ecf20Sopenharmony_ci result = USB_STOR_TRANSPORT_ERROR; 5798c2ecf20Sopenharmony_ci goto error; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Mapping blocks for zone %d\n", zone); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci /* 1024 PBA's per zone */ 5858c2ecf20Sopenharmony_ci for (i = 0; i < zonesize; i++) 5868c2ecf20Sopenharmony_ci lba_to_pba[i] = pba_to_lba[i] = UNDEF; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci for (i = 0; i < zonesize; i++) { 5898c2ecf20Sopenharmony_ci blocknum = zone_base_pba + i; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci result = alauda_get_redu_data(us, blocknum, data); 5928c2ecf20Sopenharmony_ci if (result != USB_STOR_XFER_GOOD) { 5938c2ecf20Sopenharmony_ci result = USB_STOR_TRANSPORT_ERROR; 5948c2ecf20Sopenharmony_ci goto error; 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci /* special PBAs have control field 0^16 */ 5988c2ecf20Sopenharmony_ci for (j = 0; j < 16; j++) 5998c2ecf20Sopenharmony_ci if (data[j] != 0) 6008c2ecf20Sopenharmony_ci goto nonz; 6018c2ecf20Sopenharmony_ci pba_to_lba[i] = UNUSABLE; 6028c2ecf20Sopenharmony_ci usb_stor_dbg(us, "PBA %d has no logical mapping\n", blocknum); 6038c2ecf20Sopenharmony_ci continue; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci nonz: 6068c2ecf20Sopenharmony_ci /* unwritten PBAs have control field FF^16 */ 6078c2ecf20Sopenharmony_ci for (j = 0; j < 16; j++) 6088c2ecf20Sopenharmony_ci if (data[j] != 0xff) 6098c2ecf20Sopenharmony_ci goto nonff; 6108c2ecf20Sopenharmony_ci continue; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci nonff: 6138c2ecf20Sopenharmony_ci /* normal PBAs start with six FFs */ 6148c2ecf20Sopenharmony_ci if (j < 6) { 6158c2ecf20Sopenharmony_ci usb_stor_dbg(us, "PBA %d has no logical mapping: reserved area = %02X%02X%02X%02X data status %02X block status %02X\n", 6168c2ecf20Sopenharmony_ci blocknum, 6178c2ecf20Sopenharmony_ci data[0], data[1], data[2], data[3], 6188c2ecf20Sopenharmony_ci data[4], data[5]); 6198c2ecf20Sopenharmony_ci pba_to_lba[i] = UNUSABLE; 6208c2ecf20Sopenharmony_ci continue; 6218c2ecf20Sopenharmony_ci } 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci if ((data[6] >> 4) != 0x01) { 6248c2ecf20Sopenharmony_ci usb_stor_dbg(us, "PBA %d has invalid address field %02X%02X/%02X%02X\n", 6258c2ecf20Sopenharmony_ci blocknum, data[6], data[7], 6268c2ecf20Sopenharmony_ci data[11], data[12]); 6278c2ecf20Sopenharmony_ci pba_to_lba[i] = UNUSABLE; 6288c2ecf20Sopenharmony_ci continue; 6298c2ecf20Sopenharmony_ci } 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci /* check even parity */ 6328c2ecf20Sopenharmony_ci if (parity[data[6] ^ data[7]]) { 6338c2ecf20Sopenharmony_ci printk(KERN_WARNING 6348c2ecf20Sopenharmony_ci "alauda_read_map: Bad parity in LBA for block %d" 6358c2ecf20Sopenharmony_ci " (%02X %02X)\n", i, data[6], data[7]); 6368c2ecf20Sopenharmony_ci pba_to_lba[i] = UNUSABLE; 6378c2ecf20Sopenharmony_ci continue; 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci lba_offset = short_pack(data[7], data[6]); 6418c2ecf20Sopenharmony_ci lba_offset = (lba_offset & 0x07FF) >> 1; 6428c2ecf20Sopenharmony_ci lba_real = lba_offset + zone_base_lba; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci /* 6458c2ecf20Sopenharmony_ci * Every 1024 physical blocks ("zone"), the LBA numbers 6468c2ecf20Sopenharmony_ci * go back to zero, but are within a higher block of LBA's. 6478c2ecf20Sopenharmony_ci * Also, there is a maximum of 1000 LBA's per zone. 6488c2ecf20Sopenharmony_ci * In other words, in PBA 1024-2047 you will find LBA 0-999 6498c2ecf20Sopenharmony_ci * which are really LBA 1000-1999. This allows for 24 bad 6508c2ecf20Sopenharmony_ci * or special physical blocks per zone. 6518c2ecf20Sopenharmony_ci */ 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci if (lba_offset >= uzonesize) { 6548c2ecf20Sopenharmony_ci printk(KERN_WARNING 6558c2ecf20Sopenharmony_ci "alauda_read_map: Bad low LBA %d for block %d\n", 6568c2ecf20Sopenharmony_ci lba_real, blocknum); 6578c2ecf20Sopenharmony_ci continue; 6588c2ecf20Sopenharmony_ci } 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci if (lba_to_pba[lba_offset] != UNDEF) { 6618c2ecf20Sopenharmony_ci printk(KERN_WARNING 6628c2ecf20Sopenharmony_ci "alauda_read_map: " 6638c2ecf20Sopenharmony_ci "LBA %d seen for PBA %d and %d\n", 6648c2ecf20Sopenharmony_ci lba_real, lba_to_pba[lba_offset], blocknum); 6658c2ecf20Sopenharmony_ci continue; 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci pba_to_lba[i] = lba_real; 6698c2ecf20Sopenharmony_ci lba_to_pba[lba_offset] = blocknum; 6708c2ecf20Sopenharmony_ci continue; 6718c2ecf20Sopenharmony_ci } 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci MEDIA_INFO(us).lba_to_pba[zone] = lba_to_pba; 6748c2ecf20Sopenharmony_ci MEDIA_INFO(us).pba_to_lba[zone] = pba_to_lba; 6758c2ecf20Sopenharmony_ci result = 0; 6768c2ecf20Sopenharmony_ci goto out; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_cierror: 6798c2ecf20Sopenharmony_ci kfree(lba_to_pba); 6808c2ecf20Sopenharmony_ci kfree(pba_to_lba); 6818c2ecf20Sopenharmony_ciout: 6828c2ecf20Sopenharmony_ci return result; 6838c2ecf20Sopenharmony_ci} 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci/* 6868c2ecf20Sopenharmony_ci * Checks to see whether we have already mapped a certain zone 6878c2ecf20Sopenharmony_ci * If we haven't, the map is generated 6888c2ecf20Sopenharmony_ci */ 6898c2ecf20Sopenharmony_cistatic void alauda_ensure_map_for_zone(struct us_data *us, unsigned int zone) 6908c2ecf20Sopenharmony_ci{ 6918c2ecf20Sopenharmony_ci if (MEDIA_INFO(us).lba_to_pba[zone] == NULL 6928c2ecf20Sopenharmony_ci || MEDIA_INFO(us).pba_to_lba[zone] == NULL) 6938c2ecf20Sopenharmony_ci alauda_read_map(us, zone); 6948c2ecf20Sopenharmony_ci} 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci/* 6978c2ecf20Sopenharmony_ci * Erases an entire block 6988c2ecf20Sopenharmony_ci */ 6998c2ecf20Sopenharmony_cistatic int alauda_erase_block(struct us_data *us, u16 pba) 7008c2ecf20Sopenharmony_ci{ 7018c2ecf20Sopenharmony_ci int rc; 7028c2ecf20Sopenharmony_ci unsigned char command[] = { 7038c2ecf20Sopenharmony_ci ALAUDA_BULK_CMD, ALAUDA_BULK_ERASE_BLOCK, PBA_HI(pba), 7048c2ecf20Sopenharmony_ci PBA_ZONE(pba), 0, PBA_LO(pba), 0x02, 0, MEDIA_PORT(us) 7058c2ecf20Sopenharmony_ci }; 7068c2ecf20Sopenharmony_ci unsigned char buf[2]; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Erasing PBA %d\n", pba); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, 7118c2ecf20Sopenharmony_ci command, 9, NULL); 7128c2ecf20Sopenharmony_ci if (rc != USB_STOR_XFER_GOOD) 7138c2ecf20Sopenharmony_ci return rc; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci rc = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, 7168c2ecf20Sopenharmony_ci buf, 2, NULL); 7178c2ecf20Sopenharmony_ci if (rc != USB_STOR_XFER_GOOD) 7188c2ecf20Sopenharmony_ci return rc; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Erase result: %02X %02X\n", buf[0], buf[1]); 7218c2ecf20Sopenharmony_ci return rc; 7228c2ecf20Sopenharmony_ci} 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci/* 7258c2ecf20Sopenharmony_ci * Reads data from a certain offset page inside a PBA, including interleaved 7268c2ecf20Sopenharmony_ci * redundancy data. Returns (pagesize+64)*pages bytes in data. 7278c2ecf20Sopenharmony_ci */ 7288c2ecf20Sopenharmony_cistatic int alauda_read_block_raw(struct us_data *us, u16 pba, 7298c2ecf20Sopenharmony_ci unsigned int page, unsigned int pages, unsigned char *data) 7308c2ecf20Sopenharmony_ci{ 7318c2ecf20Sopenharmony_ci int rc; 7328c2ecf20Sopenharmony_ci unsigned char command[] = { 7338c2ecf20Sopenharmony_ci ALAUDA_BULK_CMD, ALAUDA_BULK_READ_BLOCK, PBA_HI(pba), 7348c2ecf20Sopenharmony_ci PBA_ZONE(pba), 0, PBA_LO(pba) + page, pages, 0, MEDIA_PORT(us) 7358c2ecf20Sopenharmony_ci }; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci usb_stor_dbg(us, "pba %d page %d count %d\n", pba, page, pages); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, 7408c2ecf20Sopenharmony_ci command, 9, NULL); 7418c2ecf20Sopenharmony_ci if (rc != USB_STOR_XFER_GOOD) 7428c2ecf20Sopenharmony_ci return rc; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci return usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, 7458c2ecf20Sopenharmony_ci data, (MEDIA_INFO(us).pagesize + 64) * pages, NULL); 7468c2ecf20Sopenharmony_ci} 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci/* 7498c2ecf20Sopenharmony_ci * Reads data from a certain offset page inside a PBA, excluding redundancy 7508c2ecf20Sopenharmony_ci * data. Returns pagesize*pages bytes in data. Note that data must be big enough 7518c2ecf20Sopenharmony_ci * to hold (pagesize+64)*pages bytes of data, but you can ignore those 'extra' 7528c2ecf20Sopenharmony_ci * trailing bytes outside this function. 7538c2ecf20Sopenharmony_ci */ 7548c2ecf20Sopenharmony_cistatic int alauda_read_block(struct us_data *us, u16 pba, 7558c2ecf20Sopenharmony_ci unsigned int page, unsigned int pages, unsigned char *data) 7568c2ecf20Sopenharmony_ci{ 7578c2ecf20Sopenharmony_ci int i, rc; 7588c2ecf20Sopenharmony_ci unsigned int pagesize = MEDIA_INFO(us).pagesize; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci rc = alauda_read_block_raw(us, pba, page, pages, data); 7618c2ecf20Sopenharmony_ci if (rc != USB_STOR_XFER_GOOD) 7628c2ecf20Sopenharmony_ci return rc; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci /* Cut out the redundancy data */ 7658c2ecf20Sopenharmony_ci for (i = 0; i < pages; i++) { 7668c2ecf20Sopenharmony_ci int dest_offset = i * pagesize; 7678c2ecf20Sopenharmony_ci int src_offset = i * (pagesize + 64); 7688c2ecf20Sopenharmony_ci memmove(data + dest_offset, data + src_offset, pagesize); 7698c2ecf20Sopenharmony_ci } 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci return rc; 7728c2ecf20Sopenharmony_ci} 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci/* 7758c2ecf20Sopenharmony_ci * Writes an entire block of data and checks status after write. 7768c2ecf20Sopenharmony_ci * Redundancy data must be already included in data. Data should be 7778c2ecf20Sopenharmony_ci * (pagesize+64)*blocksize bytes in length. 7788c2ecf20Sopenharmony_ci */ 7798c2ecf20Sopenharmony_cistatic int alauda_write_block(struct us_data *us, u16 pba, unsigned char *data) 7808c2ecf20Sopenharmony_ci{ 7818c2ecf20Sopenharmony_ci int rc; 7828c2ecf20Sopenharmony_ci struct alauda_info *info = (struct alauda_info *) us->extra; 7838c2ecf20Sopenharmony_ci unsigned char command[] = { 7848c2ecf20Sopenharmony_ci ALAUDA_BULK_CMD, ALAUDA_BULK_WRITE_BLOCK, PBA_HI(pba), 7858c2ecf20Sopenharmony_ci PBA_ZONE(pba), 0, PBA_LO(pba), 32, 0, MEDIA_PORT(us) 7868c2ecf20Sopenharmony_ci }; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci usb_stor_dbg(us, "pba %d\n", pba); 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, 7918c2ecf20Sopenharmony_ci command, 9, NULL); 7928c2ecf20Sopenharmony_ci if (rc != USB_STOR_XFER_GOOD) 7938c2ecf20Sopenharmony_ci return rc; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci rc = usb_stor_bulk_transfer_buf(us, info->wr_ep, data, 7968c2ecf20Sopenharmony_ci (MEDIA_INFO(us).pagesize + 64) * MEDIA_INFO(us).blocksize, 7978c2ecf20Sopenharmony_ci NULL); 7988c2ecf20Sopenharmony_ci if (rc != USB_STOR_XFER_GOOD) 7998c2ecf20Sopenharmony_ci return rc; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci return alauda_check_status2(us); 8028c2ecf20Sopenharmony_ci} 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci/* 8058c2ecf20Sopenharmony_ci * Write some data to a specific LBA. 8068c2ecf20Sopenharmony_ci */ 8078c2ecf20Sopenharmony_cistatic int alauda_write_lba(struct us_data *us, u16 lba, 8088c2ecf20Sopenharmony_ci unsigned int page, unsigned int pages, 8098c2ecf20Sopenharmony_ci unsigned char *ptr, unsigned char *blockbuffer) 8108c2ecf20Sopenharmony_ci{ 8118c2ecf20Sopenharmony_ci u16 pba, lbap, new_pba; 8128c2ecf20Sopenharmony_ci unsigned char *bptr, *cptr, *xptr; 8138c2ecf20Sopenharmony_ci unsigned char ecc[3]; 8148c2ecf20Sopenharmony_ci int i, result; 8158c2ecf20Sopenharmony_ci unsigned int uzonesize = MEDIA_INFO(us).uzonesize; 8168c2ecf20Sopenharmony_ci unsigned int zonesize = MEDIA_INFO(us).zonesize; 8178c2ecf20Sopenharmony_ci unsigned int pagesize = MEDIA_INFO(us).pagesize; 8188c2ecf20Sopenharmony_ci unsigned int blocksize = MEDIA_INFO(us).blocksize; 8198c2ecf20Sopenharmony_ci unsigned int lba_offset = lba % uzonesize; 8208c2ecf20Sopenharmony_ci unsigned int new_pba_offset; 8218c2ecf20Sopenharmony_ci unsigned int zone = lba / uzonesize; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci alauda_ensure_map_for_zone(us, zone); 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci pba = MEDIA_INFO(us).lba_to_pba[zone][lba_offset]; 8268c2ecf20Sopenharmony_ci if (pba == 1) { 8278c2ecf20Sopenharmony_ci /* 8288c2ecf20Sopenharmony_ci * Maybe it is impossible to write to PBA 1. 8298c2ecf20Sopenharmony_ci * Fake success, but don't do anything. 8308c2ecf20Sopenharmony_ci */ 8318c2ecf20Sopenharmony_ci printk(KERN_WARNING 8328c2ecf20Sopenharmony_ci "alauda_write_lba: avoid writing to pba 1\n"); 8338c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_GOOD; 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci new_pba = alauda_find_unused_pba(&MEDIA_INFO(us), zone); 8378c2ecf20Sopenharmony_ci if (!new_pba) { 8388c2ecf20Sopenharmony_ci printk(KERN_WARNING 8398c2ecf20Sopenharmony_ci "alauda_write_lba: Out of unused blocks\n"); 8408c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 8418c2ecf20Sopenharmony_ci } 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci /* read old contents */ 8448c2ecf20Sopenharmony_ci if (pba != UNDEF) { 8458c2ecf20Sopenharmony_ci result = alauda_read_block_raw(us, pba, 0, 8468c2ecf20Sopenharmony_ci blocksize, blockbuffer); 8478c2ecf20Sopenharmony_ci if (result != USB_STOR_XFER_GOOD) 8488c2ecf20Sopenharmony_ci return result; 8498c2ecf20Sopenharmony_ci } else { 8508c2ecf20Sopenharmony_ci memset(blockbuffer, 0, blocksize * (pagesize + 64)); 8518c2ecf20Sopenharmony_ci } 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci lbap = (lba_offset << 1) | 0x1000; 8548c2ecf20Sopenharmony_ci if (parity[MSB_of(lbap) ^ LSB_of(lbap)]) 8558c2ecf20Sopenharmony_ci lbap ^= 1; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci /* check old contents and fill lba */ 8588c2ecf20Sopenharmony_ci for (i = 0; i < blocksize; i++) { 8598c2ecf20Sopenharmony_ci bptr = blockbuffer + (i * (pagesize + 64)); 8608c2ecf20Sopenharmony_ci cptr = bptr + pagesize; 8618c2ecf20Sopenharmony_ci nand_compute_ecc(bptr, ecc); 8628c2ecf20Sopenharmony_ci if (!nand_compare_ecc(cptr+13, ecc)) { 8638c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Warning: bad ecc in page %d- of pba %d\n", 8648c2ecf20Sopenharmony_ci i, pba); 8658c2ecf20Sopenharmony_ci nand_store_ecc(cptr+13, ecc); 8668c2ecf20Sopenharmony_ci } 8678c2ecf20Sopenharmony_ci nand_compute_ecc(bptr + (pagesize / 2), ecc); 8688c2ecf20Sopenharmony_ci if (!nand_compare_ecc(cptr+8, ecc)) { 8698c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Warning: bad ecc in page %d+ of pba %d\n", 8708c2ecf20Sopenharmony_ci i, pba); 8718c2ecf20Sopenharmony_ci nand_store_ecc(cptr+8, ecc); 8728c2ecf20Sopenharmony_ci } 8738c2ecf20Sopenharmony_ci cptr[6] = cptr[11] = MSB_of(lbap); 8748c2ecf20Sopenharmony_ci cptr[7] = cptr[12] = LSB_of(lbap); 8758c2ecf20Sopenharmony_ci } 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci /* copy in new stuff and compute ECC */ 8788c2ecf20Sopenharmony_ci xptr = ptr; 8798c2ecf20Sopenharmony_ci for (i = page; i < page+pages; i++) { 8808c2ecf20Sopenharmony_ci bptr = blockbuffer + (i * (pagesize + 64)); 8818c2ecf20Sopenharmony_ci cptr = bptr + pagesize; 8828c2ecf20Sopenharmony_ci memcpy(bptr, xptr, pagesize); 8838c2ecf20Sopenharmony_ci xptr += pagesize; 8848c2ecf20Sopenharmony_ci nand_compute_ecc(bptr, ecc); 8858c2ecf20Sopenharmony_ci nand_store_ecc(cptr+13, ecc); 8868c2ecf20Sopenharmony_ci nand_compute_ecc(bptr + (pagesize / 2), ecc); 8878c2ecf20Sopenharmony_ci nand_store_ecc(cptr+8, ecc); 8888c2ecf20Sopenharmony_ci } 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci result = alauda_write_block(us, new_pba, blockbuffer); 8918c2ecf20Sopenharmony_ci if (result != USB_STOR_XFER_GOOD) 8928c2ecf20Sopenharmony_ci return result; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci new_pba_offset = new_pba - (zone * zonesize); 8958c2ecf20Sopenharmony_ci MEDIA_INFO(us).pba_to_lba[zone][new_pba_offset] = lba; 8968c2ecf20Sopenharmony_ci MEDIA_INFO(us).lba_to_pba[zone][lba_offset] = new_pba; 8978c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Remapped LBA %d to PBA %d\n", lba, new_pba); 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci if (pba != UNDEF) { 9008c2ecf20Sopenharmony_ci unsigned int pba_offset = pba - (zone * zonesize); 9018c2ecf20Sopenharmony_ci result = alauda_erase_block(us, pba); 9028c2ecf20Sopenharmony_ci if (result != USB_STOR_XFER_GOOD) 9038c2ecf20Sopenharmony_ci return result; 9048c2ecf20Sopenharmony_ci MEDIA_INFO(us).pba_to_lba[zone][pba_offset] = UNDEF; 9058c2ecf20Sopenharmony_ci } 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_GOOD; 9088c2ecf20Sopenharmony_ci} 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci/* 9118c2ecf20Sopenharmony_ci * Read data from a specific sector address 9128c2ecf20Sopenharmony_ci */ 9138c2ecf20Sopenharmony_cistatic int alauda_read_data(struct us_data *us, unsigned long address, 9148c2ecf20Sopenharmony_ci unsigned int sectors) 9158c2ecf20Sopenharmony_ci{ 9168c2ecf20Sopenharmony_ci unsigned char *buffer; 9178c2ecf20Sopenharmony_ci u16 lba, max_lba; 9188c2ecf20Sopenharmony_ci unsigned int page, len, offset; 9198c2ecf20Sopenharmony_ci unsigned int blockshift = MEDIA_INFO(us).blockshift; 9208c2ecf20Sopenharmony_ci unsigned int pageshift = MEDIA_INFO(us).pageshift; 9218c2ecf20Sopenharmony_ci unsigned int blocksize = MEDIA_INFO(us).blocksize; 9228c2ecf20Sopenharmony_ci unsigned int pagesize = MEDIA_INFO(us).pagesize; 9238c2ecf20Sopenharmony_ci unsigned int uzonesize = MEDIA_INFO(us).uzonesize; 9248c2ecf20Sopenharmony_ci struct scatterlist *sg; 9258c2ecf20Sopenharmony_ci int result; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci /* 9288c2ecf20Sopenharmony_ci * Since we only read in one block at a time, we have to create 9298c2ecf20Sopenharmony_ci * a bounce buffer and move the data a piece at a time between the 9308c2ecf20Sopenharmony_ci * bounce buffer and the actual transfer buffer. 9318c2ecf20Sopenharmony_ci * We make this buffer big enough to hold temporary redundancy data, 9328c2ecf20Sopenharmony_ci * which we use when reading the data blocks. 9338c2ecf20Sopenharmony_ci */ 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci len = min(sectors, blocksize) * (pagesize + 64); 9368c2ecf20Sopenharmony_ci buffer = kmalloc(len, GFP_NOIO); 9378c2ecf20Sopenharmony_ci if (!buffer) 9388c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci /* Figure out the initial LBA and page */ 9418c2ecf20Sopenharmony_ci lba = address >> blockshift; 9428c2ecf20Sopenharmony_ci page = (address & MEDIA_INFO(us).blockmask); 9438c2ecf20Sopenharmony_ci max_lba = MEDIA_INFO(us).capacity >> (blockshift + pageshift); 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci result = USB_STOR_TRANSPORT_GOOD; 9468c2ecf20Sopenharmony_ci offset = 0; 9478c2ecf20Sopenharmony_ci sg = NULL; 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci while (sectors > 0) { 9508c2ecf20Sopenharmony_ci unsigned int zone = lba / uzonesize; /* integer division */ 9518c2ecf20Sopenharmony_ci unsigned int lba_offset = lba - (zone * uzonesize); 9528c2ecf20Sopenharmony_ci unsigned int pages; 9538c2ecf20Sopenharmony_ci u16 pba; 9548c2ecf20Sopenharmony_ci alauda_ensure_map_for_zone(us, zone); 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci /* Not overflowing capacity? */ 9578c2ecf20Sopenharmony_ci if (lba >= max_lba) { 9588c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Error: Requested lba %u exceeds maximum %u\n", 9598c2ecf20Sopenharmony_ci lba, max_lba); 9608c2ecf20Sopenharmony_ci result = USB_STOR_TRANSPORT_ERROR; 9618c2ecf20Sopenharmony_ci break; 9628c2ecf20Sopenharmony_ci } 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci /* Find number of pages we can read in this block */ 9658c2ecf20Sopenharmony_ci pages = min(sectors, blocksize - page); 9668c2ecf20Sopenharmony_ci len = pages << pageshift; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci /* Find where this lba lives on disk */ 9698c2ecf20Sopenharmony_ci pba = MEDIA_INFO(us).lba_to_pba[zone][lba_offset]; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci if (pba == UNDEF) { /* this lba was never written */ 9728c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Read %d zero pages (LBA %d) page %d\n", 9738c2ecf20Sopenharmony_ci pages, lba, page); 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci /* 9768c2ecf20Sopenharmony_ci * This is not really an error. It just means 9778c2ecf20Sopenharmony_ci * that the block has never been written. 9788c2ecf20Sopenharmony_ci * Instead of returning USB_STOR_TRANSPORT_ERROR 9798c2ecf20Sopenharmony_ci * it is better to return all zero data. 9808c2ecf20Sopenharmony_ci */ 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci memset(buffer, 0, len); 9838c2ecf20Sopenharmony_ci } else { 9848c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Read %d pages, from PBA %d (LBA %d) page %d\n", 9858c2ecf20Sopenharmony_ci pages, pba, lba, page); 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci result = alauda_read_block(us, pba, page, pages, buffer); 9888c2ecf20Sopenharmony_ci if (result != USB_STOR_TRANSPORT_GOOD) 9898c2ecf20Sopenharmony_ci break; 9908c2ecf20Sopenharmony_ci } 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci /* Store the data in the transfer buffer */ 9938c2ecf20Sopenharmony_ci usb_stor_access_xfer_buf(buffer, len, us->srb, 9948c2ecf20Sopenharmony_ci &sg, &offset, TO_XFER_BUF); 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci page = 0; 9978c2ecf20Sopenharmony_ci lba++; 9988c2ecf20Sopenharmony_ci sectors -= pages; 9998c2ecf20Sopenharmony_ci } 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci kfree(buffer); 10028c2ecf20Sopenharmony_ci return result; 10038c2ecf20Sopenharmony_ci} 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci/* 10068c2ecf20Sopenharmony_ci * Write data to a specific sector address 10078c2ecf20Sopenharmony_ci */ 10088c2ecf20Sopenharmony_cistatic int alauda_write_data(struct us_data *us, unsigned long address, 10098c2ecf20Sopenharmony_ci unsigned int sectors) 10108c2ecf20Sopenharmony_ci{ 10118c2ecf20Sopenharmony_ci unsigned char *buffer, *blockbuffer; 10128c2ecf20Sopenharmony_ci unsigned int page, len, offset; 10138c2ecf20Sopenharmony_ci unsigned int blockshift = MEDIA_INFO(us).blockshift; 10148c2ecf20Sopenharmony_ci unsigned int pageshift = MEDIA_INFO(us).pageshift; 10158c2ecf20Sopenharmony_ci unsigned int blocksize = MEDIA_INFO(us).blocksize; 10168c2ecf20Sopenharmony_ci unsigned int pagesize = MEDIA_INFO(us).pagesize; 10178c2ecf20Sopenharmony_ci struct scatterlist *sg; 10188c2ecf20Sopenharmony_ci u16 lba, max_lba; 10198c2ecf20Sopenharmony_ci int result; 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci /* 10228c2ecf20Sopenharmony_ci * Since we don't write the user data directly to the device, 10238c2ecf20Sopenharmony_ci * we have to create a bounce buffer and move the data a piece 10248c2ecf20Sopenharmony_ci * at a time between the bounce buffer and the actual transfer buffer. 10258c2ecf20Sopenharmony_ci */ 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci len = min(sectors, blocksize) * pagesize; 10288c2ecf20Sopenharmony_ci buffer = kmalloc(len, GFP_NOIO); 10298c2ecf20Sopenharmony_ci if (!buffer) 10308c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci /* 10338c2ecf20Sopenharmony_ci * We also need a temporary block buffer, where we read in the old data, 10348c2ecf20Sopenharmony_ci * overwrite parts with the new data, and manipulate the redundancy data 10358c2ecf20Sopenharmony_ci */ 10368c2ecf20Sopenharmony_ci blockbuffer = kmalloc_array(pagesize + 64, blocksize, GFP_NOIO); 10378c2ecf20Sopenharmony_ci if (!blockbuffer) { 10388c2ecf20Sopenharmony_ci kfree(buffer); 10398c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 10408c2ecf20Sopenharmony_ci } 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci /* Figure out the initial LBA and page */ 10438c2ecf20Sopenharmony_ci lba = address >> blockshift; 10448c2ecf20Sopenharmony_ci page = (address & MEDIA_INFO(us).blockmask); 10458c2ecf20Sopenharmony_ci max_lba = MEDIA_INFO(us).capacity >> (pageshift + blockshift); 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci result = USB_STOR_TRANSPORT_GOOD; 10488c2ecf20Sopenharmony_ci offset = 0; 10498c2ecf20Sopenharmony_ci sg = NULL; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci while (sectors > 0) { 10528c2ecf20Sopenharmony_ci /* Write as many sectors as possible in this block */ 10538c2ecf20Sopenharmony_ci unsigned int pages = min(sectors, blocksize - page); 10548c2ecf20Sopenharmony_ci len = pages << pageshift; 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci /* Not overflowing capacity? */ 10578c2ecf20Sopenharmony_ci if (lba >= max_lba) { 10588c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Requested lba %u exceeds maximum %u\n", 10598c2ecf20Sopenharmony_ci lba, max_lba); 10608c2ecf20Sopenharmony_ci result = USB_STOR_TRANSPORT_ERROR; 10618c2ecf20Sopenharmony_ci break; 10628c2ecf20Sopenharmony_ci } 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci /* Get the data from the transfer buffer */ 10658c2ecf20Sopenharmony_ci usb_stor_access_xfer_buf(buffer, len, us->srb, 10668c2ecf20Sopenharmony_ci &sg, &offset, FROM_XFER_BUF); 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci result = alauda_write_lba(us, lba, page, pages, buffer, 10698c2ecf20Sopenharmony_ci blockbuffer); 10708c2ecf20Sopenharmony_ci if (result != USB_STOR_TRANSPORT_GOOD) 10718c2ecf20Sopenharmony_ci break; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci page = 0; 10748c2ecf20Sopenharmony_ci lba++; 10758c2ecf20Sopenharmony_ci sectors -= pages; 10768c2ecf20Sopenharmony_ci } 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci kfree(buffer); 10798c2ecf20Sopenharmony_ci kfree(blockbuffer); 10808c2ecf20Sopenharmony_ci return result; 10818c2ecf20Sopenharmony_ci} 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci/* 10848c2ecf20Sopenharmony_ci * Our interface with the rest of the world 10858c2ecf20Sopenharmony_ci */ 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_cistatic void alauda_info_destructor(void *extra) 10888c2ecf20Sopenharmony_ci{ 10898c2ecf20Sopenharmony_ci struct alauda_info *info = (struct alauda_info *) extra; 10908c2ecf20Sopenharmony_ci int port; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci if (!info) 10938c2ecf20Sopenharmony_ci return; 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci for (port = 0; port < 2; port++) { 10968c2ecf20Sopenharmony_ci struct alauda_media_info *media_info = &info->port[port]; 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci alauda_free_maps(media_info); 10998c2ecf20Sopenharmony_ci kfree(media_info->lba_to_pba); 11008c2ecf20Sopenharmony_ci kfree(media_info->pba_to_lba); 11018c2ecf20Sopenharmony_ci } 11028c2ecf20Sopenharmony_ci} 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci/* 11058c2ecf20Sopenharmony_ci * Initialize alauda_info struct and find the data-write endpoint 11068c2ecf20Sopenharmony_ci */ 11078c2ecf20Sopenharmony_cistatic int init_alauda(struct us_data *us) 11088c2ecf20Sopenharmony_ci{ 11098c2ecf20Sopenharmony_ci struct alauda_info *info; 11108c2ecf20Sopenharmony_ci struct usb_host_interface *altsetting = us->pusb_intf->cur_altsetting; 11118c2ecf20Sopenharmony_ci nand_init_ecc(); 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci us->extra = kzalloc(sizeof(struct alauda_info), GFP_NOIO); 11148c2ecf20Sopenharmony_ci if (!us->extra) 11158c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci info = (struct alauda_info *) us->extra; 11188c2ecf20Sopenharmony_ci us->extra_destructor = alauda_info_destructor; 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci info->wr_ep = usb_sndbulkpipe(us->pusb_dev, 11218c2ecf20Sopenharmony_ci altsetting->endpoint[0].desc.bEndpointAddress 11228c2ecf20Sopenharmony_ci & USB_ENDPOINT_NUMBER_MASK); 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_GOOD; 11258c2ecf20Sopenharmony_ci} 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_cistatic int alauda_transport(struct scsi_cmnd *srb, struct us_data *us) 11288c2ecf20Sopenharmony_ci{ 11298c2ecf20Sopenharmony_ci int rc; 11308c2ecf20Sopenharmony_ci struct alauda_info *info = (struct alauda_info *) us->extra; 11318c2ecf20Sopenharmony_ci unsigned char *ptr = us->iobuf; 11328c2ecf20Sopenharmony_ci static unsigned char inquiry_response[36] = { 11338c2ecf20Sopenharmony_ci 0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00 11348c2ecf20Sopenharmony_ci }; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci if (srb->cmnd[0] == INQUIRY) { 11378c2ecf20Sopenharmony_ci usb_stor_dbg(us, "INQUIRY - Returning bogus response\n"); 11388c2ecf20Sopenharmony_ci memcpy(ptr, inquiry_response, sizeof(inquiry_response)); 11398c2ecf20Sopenharmony_ci fill_inquiry_response(us, ptr, 36); 11408c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_GOOD; 11418c2ecf20Sopenharmony_ci } 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci if (srb->cmnd[0] == TEST_UNIT_READY) { 11448c2ecf20Sopenharmony_ci usb_stor_dbg(us, "TEST_UNIT_READY\n"); 11458c2ecf20Sopenharmony_ci return alauda_check_media(us); 11468c2ecf20Sopenharmony_ci } 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci if (srb->cmnd[0] == READ_CAPACITY) { 11498c2ecf20Sopenharmony_ci unsigned int num_zones; 11508c2ecf20Sopenharmony_ci unsigned long capacity; 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci rc = alauda_check_media(us); 11538c2ecf20Sopenharmony_ci if (rc != USB_STOR_TRANSPORT_GOOD) 11548c2ecf20Sopenharmony_ci return rc; 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci num_zones = MEDIA_INFO(us).capacity >> (MEDIA_INFO(us).zoneshift 11578c2ecf20Sopenharmony_ci + MEDIA_INFO(us).blockshift + MEDIA_INFO(us).pageshift); 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci capacity = num_zones * MEDIA_INFO(us).uzonesize 11608c2ecf20Sopenharmony_ci * MEDIA_INFO(us).blocksize; 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci /* Report capacity and page size */ 11638c2ecf20Sopenharmony_ci ((__be32 *) ptr)[0] = cpu_to_be32(capacity - 1); 11648c2ecf20Sopenharmony_ci ((__be32 *) ptr)[1] = cpu_to_be32(512); 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci usb_stor_set_xfer_buf(ptr, 8, srb); 11678c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_GOOD; 11688c2ecf20Sopenharmony_ci } 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci if (srb->cmnd[0] == READ_10) { 11718c2ecf20Sopenharmony_ci unsigned int page, pages; 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci rc = alauda_check_media(us); 11748c2ecf20Sopenharmony_ci if (rc != USB_STOR_TRANSPORT_GOOD) 11758c2ecf20Sopenharmony_ci return rc; 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci page = short_pack(srb->cmnd[3], srb->cmnd[2]); 11788c2ecf20Sopenharmony_ci page <<= 16; 11798c2ecf20Sopenharmony_ci page |= short_pack(srb->cmnd[5], srb->cmnd[4]); 11808c2ecf20Sopenharmony_ci pages = short_pack(srb->cmnd[8], srb->cmnd[7]); 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci usb_stor_dbg(us, "READ_10: page %d pagect %d\n", page, pages); 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci return alauda_read_data(us, page, pages); 11858c2ecf20Sopenharmony_ci } 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci if (srb->cmnd[0] == WRITE_10) { 11888c2ecf20Sopenharmony_ci unsigned int page, pages; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci rc = alauda_check_media(us); 11918c2ecf20Sopenharmony_ci if (rc != USB_STOR_TRANSPORT_GOOD) 11928c2ecf20Sopenharmony_ci return rc; 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci page = short_pack(srb->cmnd[3], srb->cmnd[2]); 11958c2ecf20Sopenharmony_ci page <<= 16; 11968c2ecf20Sopenharmony_ci page |= short_pack(srb->cmnd[5], srb->cmnd[4]); 11978c2ecf20Sopenharmony_ci pages = short_pack(srb->cmnd[8], srb->cmnd[7]); 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci usb_stor_dbg(us, "WRITE_10: page %d pagect %d\n", page, pages); 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci return alauda_write_data(us, page, pages); 12028c2ecf20Sopenharmony_ci } 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci if (srb->cmnd[0] == REQUEST_SENSE) { 12058c2ecf20Sopenharmony_ci usb_stor_dbg(us, "REQUEST_SENSE\n"); 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci memset(ptr, 0, 18); 12088c2ecf20Sopenharmony_ci ptr[0] = 0xF0; 12098c2ecf20Sopenharmony_ci ptr[2] = info->sense_key; 12108c2ecf20Sopenharmony_ci ptr[7] = 11; 12118c2ecf20Sopenharmony_ci ptr[12] = info->sense_asc; 12128c2ecf20Sopenharmony_ci ptr[13] = info->sense_ascq; 12138c2ecf20Sopenharmony_ci usb_stor_set_xfer_buf(ptr, 18, srb); 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_GOOD; 12168c2ecf20Sopenharmony_ci } 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) { 12198c2ecf20Sopenharmony_ci /* 12208c2ecf20Sopenharmony_ci * sure. whatever. not like we can stop the user from popping 12218c2ecf20Sopenharmony_ci * the media out of the device (no locking doors, etc) 12228c2ecf20Sopenharmony_ci */ 12238c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_GOOD; 12248c2ecf20Sopenharmony_ci } 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci usb_stor_dbg(us, "Gah! Unknown command: %d (0x%x)\n", 12278c2ecf20Sopenharmony_ci srb->cmnd[0], srb->cmnd[0]); 12288c2ecf20Sopenharmony_ci info->sense_key = 0x05; 12298c2ecf20Sopenharmony_ci info->sense_asc = 0x20; 12308c2ecf20Sopenharmony_ci info->sense_ascq = 0x00; 12318c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_FAILED; 12328c2ecf20Sopenharmony_ci} 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_cistatic struct scsi_host_template alauda_host_template; 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_cistatic int alauda_probe(struct usb_interface *intf, 12378c2ecf20Sopenharmony_ci const struct usb_device_id *id) 12388c2ecf20Sopenharmony_ci{ 12398c2ecf20Sopenharmony_ci struct us_data *us; 12408c2ecf20Sopenharmony_ci int result; 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci result = usb_stor_probe1(&us, intf, id, 12438c2ecf20Sopenharmony_ci (id - alauda_usb_ids) + alauda_unusual_dev_list, 12448c2ecf20Sopenharmony_ci &alauda_host_template); 12458c2ecf20Sopenharmony_ci if (result) 12468c2ecf20Sopenharmony_ci return result; 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci us->transport_name = "Alauda Control/Bulk"; 12498c2ecf20Sopenharmony_ci us->transport = alauda_transport; 12508c2ecf20Sopenharmony_ci us->transport_reset = usb_stor_Bulk_reset; 12518c2ecf20Sopenharmony_ci us->max_lun = 1; 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci result = usb_stor_probe2(us); 12548c2ecf20Sopenharmony_ci return result; 12558c2ecf20Sopenharmony_ci} 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_cistatic struct usb_driver alauda_driver = { 12588c2ecf20Sopenharmony_ci .name = DRV_NAME, 12598c2ecf20Sopenharmony_ci .probe = alauda_probe, 12608c2ecf20Sopenharmony_ci .disconnect = usb_stor_disconnect, 12618c2ecf20Sopenharmony_ci .suspend = usb_stor_suspend, 12628c2ecf20Sopenharmony_ci .resume = usb_stor_resume, 12638c2ecf20Sopenharmony_ci .reset_resume = usb_stor_reset_resume, 12648c2ecf20Sopenharmony_ci .pre_reset = usb_stor_pre_reset, 12658c2ecf20Sopenharmony_ci .post_reset = usb_stor_post_reset, 12668c2ecf20Sopenharmony_ci .id_table = alauda_usb_ids, 12678c2ecf20Sopenharmony_ci .soft_unbind = 1, 12688c2ecf20Sopenharmony_ci .no_dynamic_id = 1, 12698c2ecf20Sopenharmony_ci}; 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_cimodule_usb_stor_driver(alauda_driver, alauda_host_template, DRV_NAME); 1272