162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for Alauda-based card readers 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Current development and maintenance by: 662306a36Sopenharmony_ci * (c) 2005 Daniel Drake <dsd@gentoo.org> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * The 'Alauda' is a chip manufacturered by RATOC for OEM use. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Alauda implements a vendor-specific command set to access two media reader 1162306a36Sopenharmony_ci * ports (XD, SmartMedia). This driver converts SCSI commands to the commands 1262306a36Sopenharmony_ci * which are accepted by these devices. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * The driver was developed through reverse-engineering, with the help of the 1562306a36Sopenharmony_ci * sddr09 driver which has many similarities, and with some help from the 1662306a36Sopenharmony_ci * (very old) vendor-supplied GPL sma03 driver. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * For protocol info, see http://alauda.sourceforge.net 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <linux/module.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <scsi/scsi.h> 2562306a36Sopenharmony_ci#include <scsi/scsi_cmnd.h> 2662306a36Sopenharmony_ci#include <scsi/scsi_device.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include "usb.h" 2962306a36Sopenharmony_ci#include "transport.h" 3062306a36Sopenharmony_ci#include "protocol.h" 3162306a36Sopenharmony_ci#include "debug.h" 3262306a36Sopenharmony_ci#include "scsiglue.h" 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define DRV_NAME "ums-alauda" 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ciMODULE_DESCRIPTION("Driver for Alauda-based card readers"); 3762306a36Sopenharmony_ciMODULE_AUTHOR("Daniel Drake <dsd@gentoo.org>"); 3862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3962306a36Sopenharmony_ciMODULE_IMPORT_NS(USB_STORAGE); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* 4262306a36Sopenharmony_ci * Status bytes 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ci#define ALAUDA_STATUS_ERROR 0x01 4562306a36Sopenharmony_ci#define ALAUDA_STATUS_READY 0x40 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* 4862306a36Sopenharmony_ci * Control opcodes (for request field) 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_ci#define ALAUDA_GET_XD_MEDIA_STATUS 0x08 5162306a36Sopenharmony_ci#define ALAUDA_GET_SM_MEDIA_STATUS 0x98 5262306a36Sopenharmony_ci#define ALAUDA_ACK_XD_MEDIA_CHANGE 0x0a 5362306a36Sopenharmony_ci#define ALAUDA_ACK_SM_MEDIA_CHANGE 0x9a 5462306a36Sopenharmony_ci#define ALAUDA_GET_XD_MEDIA_SIG 0x86 5562306a36Sopenharmony_ci#define ALAUDA_GET_SM_MEDIA_SIG 0x96 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* 5862306a36Sopenharmony_ci * Bulk command identity (byte 0) 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_ci#define ALAUDA_BULK_CMD 0x40 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* 6362306a36Sopenharmony_ci * Bulk opcodes (byte 1) 6462306a36Sopenharmony_ci */ 6562306a36Sopenharmony_ci#define ALAUDA_BULK_GET_REDU_DATA 0x85 6662306a36Sopenharmony_ci#define ALAUDA_BULK_READ_BLOCK 0x94 6762306a36Sopenharmony_ci#define ALAUDA_BULK_ERASE_BLOCK 0xa3 6862306a36Sopenharmony_ci#define ALAUDA_BULK_WRITE_BLOCK 0xb4 6962306a36Sopenharmony_ci#define ALAUDA_BULK_GET_STATUS2 0xb7 7062306a36Sopenharmony_ci#define ALAUDA_BULK_RESET_MEDIA 0xe0 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* 7362306a36Sopenharmony_ci * Port to operate on (byte 8) 7462306a36Sopenharmony_ci */ 7562306a36Sopenharmony_ci#define ALAUDA_PORT_XD 0x00 7662306a36Sopenharmony_ci#define ALAUDA_PORT_SM 0x01 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* 7962306a36Sopenharmony_ci * LBA and PBA are unsigned ints. Special values. 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_ci#define UNDEF 0xffff 8262306a36Sopenharmony_ci#define SPARE 0xfffe 8362306a36Sopenharmony_ci#define UNUSABLE 0xfffd 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistruct alauda_media_info { 8662306a36Sopenharmony_ci unsigned long capacity; /* total media size in bytes */ 8762306a36Sopenharmony_ci unsigned int pagesize; /* page size in bytes */ 8862306a36Sopenharmony_ci unsigned int blocksize; /* number of pages per block */ 8962306a36Sopenharmony_ci unsigned int uzonesize; /* number of usable blocks per zone */ 9062306a36Sopenharmony_ci unsigned int zonesize; /* number of blocks per zone */ 9162306a36Sopenharmony_ci unsigned int blockmask; /* mask to get page from address */ 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci unsigned char pageshift; 9462306a36Sopenharmony_ci unsigned char blockshift; 9562306a36Sopenharmony_ci unsigned char zoneshift; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci u16 **lba_to_pba; /* logical to physical block map */ 9862306a36Sopenharmony_ci u16 **pba_to_lba; /* physical to logical block map */ 9962306a36Sopenharmony_ci}; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistruct alauda_info { 10262306a36Sopenharmony_ci struct alauda_media_info port[2]; 10362306a36Sopenharmony_ci int wr_ep; /* endpoint to write data out of */ 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci unsigned char sense_key; 10662306a36Sopenharmony_ci unsigned long sense_asc; /* additional sense code */ 10762306a36Sopenharmony_ci unsigned long sense_ascq; /* additional sense code qualifier */ 10862306a36Sopenharmony_ci}; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci#define short_pack(lsb,msb) ( ((u16)(lsb)) | ( ((u16)(msb))<<8 ) ) 11162306a36Sopenharmony_ci#define LSB_of(s) ((s)&0xFF) 11262306a36Sopenharmony_ci#define MSB_of(s) ((s)>>8) 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci#define MEDIA_PORT(us) us->srb->device->lun 11562306a36Sopenharmony_ci#define MEDIA_INFO(us) ((struct alauda_info *)us->extra)->port[MEDIA_PORT(us)] 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci#define PBA_LO(pba) ((pba & 0xF) << 5) 11862306a36Sopenharmony_ci#define PBA_HI(pba) (pba >> 3) 11962306a36Sopenharmony_ci#define PBA_ZONE(pba) (pba >> 11) 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic int init_alauda(struct us_data *us); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/* 12562306a36Sopenharmony_ci * The table of devices 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_ci#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ 12862306a36Sopenharmony_ci vendorName, productName, useProtocol, useTransport, \ 12962306a36Sopenharmony_ci initFunction, flags) \ 13062306a36Sopenharmony_ci{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ 13162306a36Sopenharmony_ci .driver_info = (flags) } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic struct usb_device_id alauda_usb_ids[] = { 13462306a36Sopenharmony_ci# include "unusual_alauda.h" 13562306a36Sopenharmony_ci { } /* Terminating entry */ 13662306a36Sopenharmony_ci}; 13762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, alauda_usb_ids); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci#undef UNUSUAL_DEV 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci/* 14262306a36Sopenharmony_ci * The flags table 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_ci#define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \ 14562306a36Sopenharmony_ci vendor_name, product_name, use_protocol, use_transport, \ 14662306a36Sopenharmony_ci init_function, Flags) \ 14762306a36Sopenharmony_ci{ \ 14862306a36Sopenharmony_ci .vendorName = vendor_name, \ 14962306a36Sopenharmony_ci .productName = product_name, \ 15062306a36Sopenharmony_ci .useProtocol = use_protocol, \ 15162306a36Sopenharmony_ci .useTransport = use_transport, \ 15262306a36Sopenharmony_ci .initFunction = init_function, \ 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic struct us_unusual_dev alauda_unusual_dev_list[] = { 15662306a36Sopenharmony_ci# include "unusual_alauda.h" 15762306a36Sopenharmony_ci { } /* Terminating entry */ 15862306a36Sopenharmony_ci}; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci#undef UNUSUAL_DEV 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci/* 16462306a36Sopenharmony_ci * Media handling 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistruct alauda_card_info { 16862306a36Sopenharmony_ci unsigned char id; /* id byte */ 16962306a36Sopenharmony_ci unsigned char chipshift; /* 1<<cs bytes total capacity */ 17062306a36Sopenharmony_ci unsigned char pageshift; /* 1<<ps bytes in a page */ 17162306a36Sopenharmony_ci unsigned char blockshift; /* 1<<bs pages per block */ 17262306a36Sopenharmony_ci unsigned char zoneshift; /* 1<<zs blocks per zone */ 17362306a36Sopenharmony_ci}; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic struct alauda_card_info alauda_card_ids[] = { 17662306a36Sopenharmony_ci /* NAND flash */ 17762306a36Sopenharmony_ci { 0x6e, 20, 8, 4, 8}, /* 1 MB */ 17862306a36Sopenharmony_ci { 0xe8, 20, 8, 4, 8}, /* 1 MB */ 17962306a36Sopenharmony_ci { 0xec, 20, 8, 4, 8}, /* 1 MB */ 18062306a36Sopenharmony_ci { 0x64, 21, 8, 4, 9}, /* 2 MB */ 18162306a36Sopenharmony_ci { 0xea, 21, 8, 4, 9}, /* 2 MB */ 18262306a36Sopenharmony_ci { 0x6b, 22, 9, 4, 9}, /* 4 MB */ 18362306a36Sopenharmony_ci { 0xe3, 22, 9, 4, 9}, /* 4 MB */ 18462306a36Sopenharmony_ci { 0xe5, 22, 9, 4, 9}, /* 4 MB */ 18562306a36Sopenharmony_ci { 0xe6, 23, 9, 4, 10}, /* 8 MB */ 18662306a36Sopenharmony_ci { 0x73, 24, 9, 5, 10}, /* 16 MB */ 18762306a36Sopenharmony_ci { 0x75, 25, 9, 5, 10}, /* 32 MB */ 18862306a36Sopenharmony_ci { 0x76, 26, 9, 5, 10}, /* 64 MB */ 18962306a36Sopenharmony_ci { 0x79, 27, 9, 5, 10}, /* 128 MB */ 19062306a36Sopenharmony_ci { 0x71, 28, 9, 5, 10}, /* 256 MB */ 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci /* MASK ROM */ 19362306a36Sopenharmony_ci { 0x5d, 21, 9, 4, 8}, /* 2 MB */ 19462306a36Sopenharmony_ci { 0xd5, 22, 9, 4, 9}, /* 4 MB */ 19562306a36Sopenharmony_ci { 0xd6, 23, 9, 4, 10}, /* 8 MB */ 19662306a36Sopenharmony_ci { 0x57, 24, 9, 4, 11}, /* 16 MB */ 19762306a36Sopenharmony_ci { 0x58, 25, 9, 4, 12}, /* 32 MB */ 19862306a36Sopenharmony_ci { 0,} 19962306a36Sopenharmony_ci}; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic struct alauda_card_info *alauda_card_find_id(unsigned char id) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci int i; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci for (i = 0; alauda_card_ids[i].id != 0; i++) 20662306a36Sopenharmony_ci if (alauda_card_ids[i].id == id) 20762306a36Sopenharmony_ci return &(alauda_card_ids[i]); 20862306a36Sopenharmony_ci return NULL; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci/* 21262306a36Sopenharmony_ci * ECC computation. 21362306a36Sopenharmony_ci */ 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic unsigned char parity[256]; 21662306a36Sopenharmony_cistatic unsigned char ecc2[256]; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic void nand_init_ecc(void) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci int i, j, a; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci parity[0] = 0; 22362306a36Sopenharmony_ci for (i = 1; i < 256; i++) 22462306a36Sopenharmony_ci parity[i] = (parity[i&(i-1)] ^ 1); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci for (i = 0; i < 256; i++) { 22762306a36Sopenharmony_ci a = 0; 22862306a36Sopenharmony_ci for (j = 0; j < 8; j++) { 22962306a36Sopenharmony_ci if (i & (1<<j)) { 23062306a36Sopenharmony_ci if ((j & 1) == 0) 23162306a36Sopenharmony_ci a ^= 0x04; 23262306a36Sopenharmony_ci if ((j & 2) == 0) 23362306a36Sopenharmony_ci a ^= 0x10; 23462306a36Sopenharmony_ci if ((j & 4) == 0) 23562306a36Sopenharmony_ci a ^= 0x40; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci ecc2[i] = ~(a ^ (a<<1) ^ (parity[i] ? 0xa8 : 0)); 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci/* compute 3-byte ecc on 256 bytes */ 24362306a36Sopenharmony_cistatic void nand_compute_ecc(unsigned char *data, unsigned char *ecc) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci int i, j, a; 24662306a36Sopenharmony_ci unsigned char par = 0, bit, bits[8] = {0}; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* collect 16 checksum bits */ 24962306a36Sopenharmony_ci for (i = 0; i < 256; i++) { 25062306a36Sopenharmony_ci par ^= data[i]; 25162306a36Sopenharmony_ci bit = parity[data[i]]; 25262306a36Sopenharmony_ci for (j = 0; j < 8; j++) 25362306a36Sopenharmony_ci if ((i & (1<<j)) == 0) 25462306a36Sopenharmony_ci bits[j] ^= bit; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* put 4+4+4 = 12 bits in the ecc */ 25862306a36Sopenharmony_ci a = (bits[3] << 6) + (bits[2] << 4) + (bits[1] << 2) + bits[0]; 25962306a36Sopenharmony_ci ecc[0] = ~(a ^ (a<<1) ^ (parity[par] ? 0xaa : 0)); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci a = (bits[7] << 6) + (bits[6] << 4) + (bits[5] << 2) + bits[4]; 26262306a36Sopenharmony_ci ecc[1] = ~(a ^ (a<<1) ^ (parity[par] ? 0xaa : 0)); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci ecc[2] = ecc2[par]; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic int nand_compare_ecc(unsigned char *data, unsigned char *ecc) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci return (data[0] == ecc[0] && data[1] == ecc[1] && data[2] == ecc[2]); 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic void nand_store_ecc(unsigned char *data, unsigned char *ecc) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci memcpy(data, ecc, 3); 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci/* 27862306a36Sopenharmony_ci * Alauda driver 27962306a36Sopenharmony_ci */ 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci/* 28262306a36Sopenharmony_ci * Forget our PBA <---> LBA mappings for a particular port 28362306a36Sopenharmony_ci */ 28462306a36Sopenharmony_cistatic void alauda_free_maps (struct alauda_media_info *media_info) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci unsigned int shift = media_info->zoneshift 28762306a36Sopenharmony_ci + media_info->blockshift + media_info->pageshift; 28862306a36Sopenharmony_ci unsigned int num_zones = media_info->capacity >> shift; 28962306a36Sopenharmony_ci unsigned int i; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (media_info->lba_to_pba != NULL) 29262306a36Sopenharmony_ci for (i = 0; i < num_zones; i++) { 29362306a36Sopenharmony_ci kfree(media_info->lba_to_pba[i]); 29462306a36Sopenharmony_ci media_info->lba_to_pba[i] = NULL; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (media_info->pba_to_lba != NULL) 29862306a36Sopenharmony_ci for (i = 0; i < num_zones; i++) { 29962306a36Sopenharmony_ci kfree(media_info->pba_to_lba[i]); 30062306a36Sopenharmony_ci media_info->pba_to_lba[i] = NULL; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci/* 30562306a36Sopenharmony_ci * Returns 2 bytes of status data 30662306a36Sopenharmony_ci * The first byte describes media status, and second byte describes door status 30762306a36Sopenharmony_ci */ 30862306a36Sopenharmony_cistatic int alauda_get_media_status(struct us_data *us, unsigned char *data) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci int rc; 31162306a36Sopenharmony_ci unsigned char command; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (MEDIA_PORT(us) == ALAUDA_PORT_XD) 31462306a36Sopenharmony_ci command = ALAUDA_GET_XD_MEDIA_STATUS; 31562306a36Sopenharmony_ci else 31662306a36Sopenharmony_ci command = ALAUDA_GET_SM_MEDIA_STATUS; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci rc = usb_stor_ctrl_transfer(us, us->recv_ctrl_pipe, 31962306a36Sopenharmony_ci command, 0xc0, 0, 1, data, 2); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (rc == USB_STOR_XFER_GOOD) 32262306a36Sopenharmony_ci usb_stor_dbg(us, "Media status %02X %02X\n", data[0], data[1]); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci return rc; 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci/* 32862306a36Sopenharmony_ci * Clears the "media was changed" bit so that we know when it changes again 32962306a36Sopenharmony_ci * in the future. 33062306a36Sopenharmony_ci */ 33162306a36Sopenharmony_cistatic int alauda_ack_media(struct us_data *us) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci unsigned char command; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci if (MEDIA_PORT(us) == ALAUDA_PORT_XD) 33662306a36Sopenharmony_ci command = ALAUDA_ACK_XD_MEDIA_CHANGE; 33762306a36Sopenharmony_ci else 33862306a36Sopenharmony_ci command = ALAUDA_ACK_SM_MEDIA_CHANGE; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci return usb_stor_ctrl_transfer(us, us->send_ctrl_pipe, 34162306a36Sopenharmony_ci command, 0x40, 0, 1, NULL, 0); 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci/* 34562306a36Sopenharmony_ci * Retrieves a 4-byte media signature, which indicates manufacturer, capacity, 34662306a36Sopenharmony_ci * and some other details. 34762306a36Sopenharmony_ci */ 34862306a36Sopenharmony_cistatic int alauda_get_media_signature(struct us_data *us, unsigned char *data) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci unsigned char command; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci if (MEDIA_PORT(us) == ALAUDA_PORT_XD) 35362306a36Sopenharmony_ci command = ALAUDA_GET_XD_MEDIA_SIG; 35462306a36Sopenharmony_ci else 35562306a36Sopenharmony_ci command = ALAUDA_GET_SM_MEDIA_SIG; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci return usb_stor_ctrl_transfer(us, us->recv_ctrl_pipe, 35862306a36Sopenharmony_ci command, 0xc0, 0, 0, data, 4); 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci/* 36262306a36Sopenharmony_ci * Resets the media status (but not the whole device?) 36362306a36Sopenharmony_ci */ 36462306a36Sopenharmony_cistatic int alauda_reset_media(struct us_data *us) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci unsigned char *command = us->iobuf; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci memset(command, 0, 9); 36962306a36Sopenharmony_ci command[0] = ALAUDA_BULK_CMD; 37062306a36Sopenharmony_ci command[1] = ALAUDA_BULK_RESET_MEDIA; 37162306a36Sopenharmony_ci command[8] = MEDIA_PORT(us); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci return usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, 37462306a36Sopenharmony_ci command, 9, NULL); 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci/* 37862306a36Sopenharmony_ci * Examines the media and deduces capacity, etc. 37962306a36Sopenharmony_ci */ 38062306a36Sopenharmony_cistatic int alauda_init_media(struct us_data *us) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci unsigned char *data = us->iobuf; 38362306a36Sopenharmony_ci int ready = 0; 38462306a36Sopenharmony_ci struct alauda_card_info *media_info; 38562306a36Sopenharmony_ci unsigned int num_zones; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci while (ready == 0) { 38862306a36Sopenharmony_ci msleep(20); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (alauda_get_media_status(us, data) != USB_STOR_XFER_GOOD) 39162306a36Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (data[0] & 0x10) 39462306a36Sopenharmony_ci ready = 1; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci usb_stor_dbg(us, "We are ready for action!\n"); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (alauda_ack_media(us) != USB_STOR_XFER_GOOD) 40062306a36Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci msleep(10); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (alauda_get_media_status(us, data) != USB_STOR_XFER_GOOD) 40562306a36Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci if (data[0] != 0x14) { 40862306a36Sopenharmony_ci usb_stor_dbg(us, "Media not ready after ack\n"); 40962306a36Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (alauda_get_media_signature(us, data) != USB_STOR_XFER_GOOD) 41362306a36Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci usb_stor_dbg(us, "Media signature: %4ph\n", data); 41662306a36Sopenharmony_ci media_info = alauda_card_find_id(data[1]); 41762306a36Sopenharmony_ci if (media_info == NULL) { 41862306a36Sopenharmony_ci pr_warn("alauda_init_media: Unrecognised media signature: %4ph\n", 41962306a36Sopenharmony_ci data); 42062306a36Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci MEDIA_INFO(us).capacity = 1 << media_info->chipshift; 42462306a36Sopenharmony_ci usb_stor_dbg(us, "Found media with capacity: %ldMB\n", 42562306a36Sopenharmony_ci MEDIA_INFO(us).capacity >> 20); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci MEDIA_INFO(us).pageshift = media_info->pageshift; 42862306a36Sopenharmony_ci MEDIA_INFO(us).blockshift = media_info->blockshift; 42962306a36Sopenharmony_ci MEDIA_INFO(us).zoneshift = media_info->zoneshift; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci MEDIA_INFO(us).pagesize = 1 << media_info->pageshift; 43262306a36Sopenharmony_ci MEDIA_INFO(us).blocksize = 1 << media_info->blockshift; 43362306a36Sopenharmony_ci MEDIA_INFO(us).zonesize = 1 << media_info->zoneshift; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci MEDIA_INFO(us).uzonesize = ((1 << media_info->zoneshift) / 128) * 125; 43662306a36Sopenharmony_ci MEDIA_INFO(us).blockmask = MEDIA_INFO(us).blocksize - 1; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci num_zones = MEDIA_INFO(us).capacity >> (MEDIA_INFO(us).zoneshift 43962306a36Sopenharmony_ci + MEDIA_INFO(us).blockshift + MEDIA_INFO(us).pageshift); 44062306a36Sopenharmony_ci MEDIA_INFO(us).pba_to_lba = kcalloc(num_zones, sizeof(u16*), GFP_NOIO); 44162306a36Sopenharmony_ci MEDIA_INFO(us).lba_to_pba = kcalloc(num_zones, sizeof(u16*), GFP_NOIO); 44262306a36Sopenharmony_ci if (MEDIA_INFO(us).pba_to_lba == NULL || MEDIA_INFO(us).lba_to_pba == NULL) 44362306a36Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci if (alauda_reset_media(us) != USB_STOR_XFER_GOOD) 44662306a36Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci return USB_STOR_TRANSPORT_GOOD; 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci/* 45262306a36Sopenharmony_ci * Examines the media status and does the right thing when the media has gone, 45362306a36Sopenharmony_ci * appeared, or changed. 45462306a36Sopenharmony_ci */ 45562306a36Sopenharmony_cistatic int alauda_check_media(struct us_data *us) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci struct alauda_info *info = (struct alauda_info *) us->extra; 45862306a36Sopenharmony_ci unsigned char *status = us->iobuf; 45962306a36Sopenharmony_ci int rc; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci rc = alauda_get_media_status(us, status); 46262306a36Sopenharmony_ci if (rc != USB_STOR_XFER_GOOD) { 46362306a36Sopenharmony_ci status[0] = 0xF0; /* Pretend there's no media */ 46462306a36Sopenharmony_ci status[1] = 0; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* Check for no media or door open */ 46862306a36Sopenharmony_ci if ((status[0] & 0x80) || ((status[0] & 0x1F) == 0x10) 46962306a36Sopenharmony_ci || ((status[1] & 0x01) == 0)) { 47062306a36Sopenharmony_ci usb_stor_dbg(us, "No media, or door open\n"); 47162306a36Sopenharmony_ci alauda_free_maps(&MEDIA_INFO(us)); 47262306a36Sopenharmony_ci info->sense_key = 0x02; 47362306a36Sopenharmony_ci info->sense_asc = 0x3A; 47462306a36Sopenharmony_ci info->sense_ascq = 0x00; 47562306a36Sopenharmony_ci return USB_STOR_TRANSPORT_FAILED; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci /* Check for media change */ 47962306a36Sopenharmony_ci if (status[0] & 0x08) { 48062306a36Sopenharmony_ci usb_stor_dbg(us, "Media change detected\n"); 48162306a36Sopenharmony_ci alauda_free_maps(&MEDIA_INFO(us)); 48262306a36Sopenharmony_ci alauda_init_media(us); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci info->sense_key = UNIT_ATTENTION; 48562306a36Sopenharmony_ci info->sense_asc = 0x28; 48662306a36Sopenharmony_ci info->sense_ascq = 0x00; 48762306a36Sopenharmony_ci return USB_STOR_TRANSPORT_FAILED; 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci return USB_STOR_TRANSPORT_GOOD; 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci/* 49462306a36Sopenharmony_ci * Checks the status from the 2nd status register 49562306a36Sopenharmony_ci * Returns 3 bytes of status data, only the first is known 49662306a36Sopenharmony_ci */ 49762306a36Sopenharmony_cistatic int alauda_check_status2(struct us_data *us) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci int rc; 50062306a36Sopenharmony_ci unsigned char command[] = { 50162306a36Sopenharmony_ci ALAUDA_BULK_CMD, ALAUDA_BULK_GET_STATUS2, 50262306a36Sopenharmony_ci 0, 0, 0, 0, 3, 0, MEDIA_PORT(us) 50362306a36Sopenharmony_ci }; 50462306a36Sopenharmony_ci unsigned char data[3]; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, 50762306a36Sopenharmony_ci command, 9, NULL); 50862306a36Sopenharmony_ci if (rc != USB_STOR_XFER_GOOD) 50962306a36Sopenharmony_ci return rc; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci rc = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, 51262306a36Sopenharmony_ci data, 3, NULL); 51362306a36Sopenharmony_ci if (rc != USB_STOR_XFER_GOOD) 51462306a36Sopenharmony_ci return rc; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci usb_stor_dbg(us, "%3ph\n", data); 51762306a36Sopenharmony_ci if (data[0] & ALAUDA_STATUS_ERROR) 51862306a36Sopenharmony_ci return USB_STOR_XFER_ERROR; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci return USB_STOR_XFER_GOOD; 52162306a36Sopenharmony_ci} 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci/* 52462306a36Sopenharmony_ci * Gets the redundancy data for the first page of a PBA 52562306a36Sopenharmony_ci * Returns 16 bytes. 52662306a36Sopenharmony_ci */ 52762306a36Sopenharmony_cistatic int alauda_get_redu_data(struct us_data *us, u16 pba, unsigned char *data) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci int rc; 53062306a36Sopenharmony_ci unsigned char command[] = { 53162306a36Sopenharmony_ci ALAUDA_BULK_CMD, ALAUDA_BULK_GET_REDU_DATA, 53262306a36Sopenharmony_ci PBA_HI(pba), PBA_ZONE(pba), 0, PBA_LO(pba), 0, 0, MEDIA_PORT(us) 53362306a36Sopenharmony_ci }; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, 53662306a36Sopenharmony_ci command, 9, NULL); 53762306a36Sopenharmony_ci if (rc != USB_STOR_XFER_GOOD) 53862306a36Sopenharmony_ci return rc; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci return usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, 54162306a36Sopenharmony_ci data, 16, NULL); 54262306a36Sopenharmony_ci} 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci/* 54562306a36Sopenharmony_ci * Finds the first unused PBA in a zone 54662306a36Sopenharmony_ci * Returns the absolute PBA of an unused PBA, or 0 if none found. 54762306a36Sopenharmony_ci */ 54862306a36Sopenharmony_cistatic u16 alauda_find_unused_pba(struct alauda_media_info *info, 54962306a36Sopenharmony_ci unsigned int zone) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci u16 *pba_to_lba = info->pba_to_lba[zone]; 55262306a36Sopenharmony_ci unsigned int i; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci for (i = 0; i < info->zonesize; i++) 55562306a36Sopenharmony_ci if (pba_to_lba[i] == UNDEF) 55662306a36Sopenharmony_ci return (zone << info->zoneshift) + i; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci return 0; 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci/* 56262306a36Sopenharmony_ci * Reads the redundancy data for all PBA's in a zone 56362306a36Sopenharmony_ci * Produces lba <--> pba mappings 56462306a36Sopenharmony_ci */ 56562306a36Sopenharmony_cistatic int alauda_read_map(struct us_data *us, unsigned int zone) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci unsigned char *data = us->iobuf; 56862306a36Sopenharmony_ci int result; 56962306a36Sopenharmony_ci int i, j; 57062306a36Sopenharmony_ci unsigned int zonesize = MEDIA_INFO(us).zonesize; 57162306a36Sopenharmony_ci unsigned int uzonesize = MEDIA_INFO(us).uzonesize; 57262306a36Sopenharmony_ci unsigned int lba_offset, lba_real, blocknum; 57362306a36Sopenharmony_ci unsigned int zone_base_lba = zone * uzonesize; 57462306a36Sopenharmony_ci unsigned int zone_base_pba = zone * zonesize; 57562306a36Sopenharmony_ci u16 *lba_to_pba = kcalloc(zonesize, sizeof(u16), GFP_NOIO); 57662306a36Sopenharmony_ci u16 *pba_to_lba = kcalloc(zonesize, sizeof(u16), GFP_NOIO); 57762306a36Sopenharmony_ci if (lba_to_pba == NULL || pba_to_lba == NULL) { 57862306a36Sopenharmony_ci result = USB_STOR_TRANSPORT_ERROR; 57962306a36Sopenharmony_ci goto error; 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci usb_stor_dbg(us, "Mapping blocks for zone %d\n", zone); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci /* 1024 PBA's per zone */ 58562306a36Sopenharmony_ci for (i = 0; i < zonesize; i++) 58662306a36Sopenharmony_ci lba_to_pba[i] = pba_to_lba[i] = UNDEF; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci for (i = 0; i < zonesize; i++) { 58962306a36Sopenharmony_ci blocknum = zone_base_pba + i; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci result = alauda_get_redu_data(us, blocknum, data); 59262306a36Sopenharmony_ci if (result != USB_STOR_XFER_GOOD) { 59362306a36Sopenharmony_ci result = USB_STOR_TRANSPORT_ERROR; 59462306a36Sopenharmony_ci goto error; 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci /* special PBAs have control field 0^16 */ 59862306a36Sopenharmony_ci for (j = 0; j < 16; j++) 59962306a36Sopenharmony_ci if (data[j] != 0) 60062306a36Sopenharmony_ci goto nonz; 60162306a36Sopenharmony_ci pba_to_lba[i] = UNUSABLE; 60262306a36Sopenharmony_ci usb_stor_dbg(us, "PBA %d has no logical mapping\n", blocknum); 60362306a36Sopenharmony_ci continue; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci nonz: 60662306a36Sopenharmony_ci /* unwritten PBAs have control field FF^16 */ 60762306a36Sopenharmony_ci for (j = 0; j < 16; j++) 60862306a36Sopenharmony_ci if (data[j] != 0xff) 60962306a36Sopenharmony_ci goto nonff; 61062306a36Sopenharmony_ci continue; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci nonff: 61362306a36Sopenharmony_ci /* normal PBAs start with six FFs */ 61462306a36Sopenharmony_ci if (j < 6) { 61562306a36Sopenharmony_ci usb_stor_dbg(us, "PBA %d has no logical mapping: reserved area = %02X%02X%02X%02X data status %02X block status %02X\n", 61662306a36Sopenharmony_ci blocknum, 61762306a36Sopenharmony_ci data[0], data[1], data[2], data[3], 61862306a36Sopenharmony_ci data[4], data[5]); 61962306a36Sopenharmony_ci pba_to_lba[i] = UNUSABLE; 62062306a36Sopenharmony_ci continue; 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci if ((data[6] >> 4) != 0x01) { 62462306a36Sopenharmony_ci usb_stor_dbg(us, "PBA %d has invalid address field %02X%02X/%02X%02X\n", 62562306a36Sopenharmony_ci blocknum, data[6], data[7], 62662306a36Sopenharmony_ci data[11], data[12]); 62762306a36Sopenharmony_ci pba_to_lba[i] = UNUSABLE; 62862306a36Sopenharmony_ci continue; 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci /* check even parity */ 63262306a36Sopenharmony_ci if (parity[data[6] ^ data[7]]) { 63362306a36Sopenharmony_ci printk(KERN_WARNING 63462306a36Sopenharmony_ci "alauda_read_map: Bad parity in LBA for block %d" 63562306a36Sopenharmony_ci " (%02X %02X)\n", i, data[6], data[7]); 63662306a36Sopenharmony_ci pba_to_lba[i] = UNUSABLE; 63762306a36Sopenharmony_ci continue; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci lba_offset = short_pack(data[7], data[6]); 64162306a36Sopenharmony_ci lba_offset = (lba_offset & 0x07FF) >> 1; 64262306a36Sopenharmony_ci lba_real = lba_offset + zone_base_lba; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci /* 64562306a36Sopenharmony_ci * Every 1024 physical blocks ("zone"), the LBA numbers 64662306a36Sopenharmony_ci * go back to zero, but are within a higher block of LBA's. 64762306a36Sopenharmony_ci * Also, there is a maximum of 1000 LBA's per zone. 64862306a36Sopenharmony_ci * In other words, in PBA 1024-2047 you will find LBA 0-999 64962306a36Sopenharmony_ci * which are really LBA 1000-1999. This allows for 24 bad 65062306a36Sopenharmony_ci * or special physical blocks per zone. 65162306a36Sopenharmony_ci */ 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci if (lba_offset >= uzonesize) { 65462306a36Sopenharmony_ci printk(KERN_WARNING 65562306a36Sopenharmony_ci "alauda_read_map: Bad low LBA %d for block %d\n", 65662306a36Sopenharmony_ci lba_real, blocknum); 65762306a36Sopenharmony_ci continue; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci if (lba_to_pba[lba_offset] != UNDEF) { 66162306a36Sopenharmony_ci printk(KERN_WARNING 66262306a36Sopenharmony_ci "alauda_read_map: " 66362306a36Sopenharmony_ci "LBA %d seen for PBA %d and %d\n", 66462306a36Sopenharmony_ci lba_real, lba_to_pba[lba_offset], blocknum); 66562306a36Sopenharmony_ci continue; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci pba_to_lba[i] = lba_real; 66962306a36Sopenharmony_ci lba_to_pba[lba_offset] = blocknum; 67062306a36Sopenharmony_ci continue; 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci MEDIA_INFO(us).lba_to_pba[zone] = lba_to_pba; 67462306a36Sopenharmony_ci MEDIA_INFO(us).pba_to_lba[zone] = pba_to_lba; 67562306a36Sopenharmony_ci result = 0; 67662306a36Sopenharmony_ci goto out; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_cierror: 67962306a36Sopenharmony_ci kfree(lba_to_pba); 68062306a36Sopenharmony_ci kfree(pba_to_lba); 68162306a36Sopenharmony_ciout: 68262306a36Sopenharmony_ci return result; 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci/* 68662306a36Sopenharmony_ci * Checks to see whether we have already mapped a certain zone 68762306a36Sopenharmony_ci * If we haven't, the map is generated 68862306a36Sopenharmony_ci */ 68962306a36Sopenharmony_cistatic void alauda_ensure_map_for_zone(struct us_data *us, unsigned int zone) 69062306a36Sopenharmony_ci{ 69162306a36Sopenharmony_ci if (MEDIA_INFO(us).lba_to_pba[zone] == NULL 69262306a36Sopenharmony_ci || MEDIA_INFO(us).pba_to_lba[zone] == NULL) 69362306a36Sopenharmony_ci alauda_read_map(us, zone); 69462306a36Sopenharmony_ci} 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci/* 69762306a36Sopenharmony_ci * Erases an entire block 69862306a36Sopenharmony_ci */ 69962306a36Sopenharmony_cistatic int alauda_erase_block(struct us_data *us, u16 pba) 70062306a36Sopenharmony_ci{ 70162306a36Sopenharmony_ci int rc; 70262306a36Sopenharmony_ci unsigned char command[] = { 70362306a36Sopenharmony_ci ALAUDA_BULK_CMD, ALAUDA_BULK_ERASE_BLOCK, PBA_HI(pba), 70462306a36Sopenharmony_ci PBA_ZONE(pba), 0, PBA_LO(pba), 0x02, 0, MEDIA_PORT(us) 70562306a36Sopenharmony_ci }; 70662306a36Sopenharmony_ci unsigned char buf[2]; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci usb_stor_dbg(us, "Erasing PBA %d\n", pba); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, 71162306a36Sopenharmony_ci command, 9, NULL); 71262306a36Sopenharmony_ci if (rc != USB_STOR_XFER_GOOD) 71362306a36Sopenharmony_ci return rc; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci rc = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, 71662306a36Sopenharmony_ci buf, 2, NULL); 71762306a36Sopenharmony_ci if (rc != USB_STOR_XFER_GOOD) 71862306a36Sopenharmony_ci return rc; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci usb_stor_dbg(us, "Erase result: %02X %02X\n", buf[0], buf[1]); 72162306a36Sopenharmony_ci return rc; 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci/* 72562306a36Sopenharmony_ci * Reads data from a certain offset page inside a PBA, including interleaved 72662306a36Sopenharmony_ci * redundancy data. Returns (pagesize+64)*pages bytes in data. 72762306a36Sopenharmony_ci */ 72862306a36Sopenharmony_cistatic int alauda_read_block_raw(struct us_data *us, u16 pba, 72962306a36Sopenharmony_ci unsigned int page, unsigned int pages, unsigned char *data) 73062306a36Sopenharmony_ci{ 73162306a36Sopenharmony_ci int rc; 73262306a36Sopenharmony_ci unsigned char command[] = { 73362306a36Sopenharmony_ci ALAUDA_BULK_CMD, ALAUDA_BULK_READ_BLOCK, PBA_HI(pba), 73462306a36Sopenharmony_ci PBA_ZONE(pba), 0, PBA_LO(pba) + page, pages, 0, MEDIA_PORT(us) 73562306a36Sopenharmony_ci }; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci usb_stor_dbg(us, "pba %d page %d count %d\n", pba, page, pages); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, 74062306a36Sopenharmony_ci command, 9, NULL); 74162306a36Sopenharmony_ci if (rc != USB_STOR_XFER_GOOD) 74262306a36Sopenharmony_ci return rc; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci return usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, 74562306a36Sopenharmony_ci data, (MEDIA_INFO(us).pagesize + 64) * pages, NULL); 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci/* 74962306a36Sopenharmony_ci * Reads data from a certain offset page inside a PBA, excluding redundancy 75062306a36Sopenharmony_ci * data. Returns pagesize*pages bytes in data. Note that data must be big enough 75162306a36Sopenharmony_ci * to hold (pagesize+64)*pages bytes of data, but you can ignore those 'extra' 75262306a36Sopenharmony_ci * trailing bytes outside this function. 75362306a36Sopenharmony_ci */ 75462306a36Sopenharmony_cistatic int alauda_read_block(struct us_data *us, u16 pba, 75562306a36Sopenharmony_ci unsigned int page, unsigned int pages, unsigned char *data) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci int i, rc; 75862306a36Sopenharmony_ci unsigned int pagesize = MEDIA_INFO(us).pagesize; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci rc = alauda_read_block_raw(us, pba, page, pages, data); 76162306a36Sopenharmony_ci if (rc != USB_STOR_XFER_GOOD) 76262306a36Sopenharmony_ci return rc; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci /* Cut out the redundancy data */ 76562306a36Sopenharmony_ci for (i = 0; i < pages; i++) { 76662306a36Sopenharmony_ci int dest_offset = i * pagesize; 76762306a36Sopenharmony_ci int src_offset = i * (pagesize + 64); 76862306a36Sopenharmony_ci memmove(data + dest_offset, data + src_offset, pagesize); 76962306a36Sopenharmony_ci } 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci return rc; 77262306a36Sopenharmony_ci} 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci/* 77562306a36Sopenharmony_ci * Writes an entire block of data and checks status after write. 77662306a36Sopenharmony_ci * Redundancy data must be already included in data. Data should be 77762306a36Sopenharmony_ci * (pagesize+64)*blocksize bytes in length. 77862306a36Sopenharmony_ci */ 77962306a36Sopenharmony_cistatic int alauda_write_block(struct us_data *us, u16 pba, unsigned char *data) 78062306a36Sopenharmony_ci{ 78162306a36Sopenharmony_ci int rc; 78262306a36Sopenharmony_ci struct alauda_info *info = (struct alauda_info *) us->extra; 78362306a36Sopenharmony_ci unsigned char command[] = { 78462306a36Sopenharmony_ci ALAUDA_BULK_CMD, ALAUDA_BULK_WRITE_BLOCK, PBA_HI(pba), 78562306a36Sopenharmony_ci PBA_ZONE(pba), 0, PBA_LO(pba), 32, 0, MEDIA_PORT(us) 78662306a36Sopenharmony_ci }; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci usb_stor_dbg(us, "pba %d\n", pba); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, 79162306a36Sopenharmony_ci command, 9, NULL); 79262306a36Sopenharmony_ci if (rc != USB_STOR_XFER_GOOD) 79362306a36Sopenharmony_ci return rc; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci rc = usb_stor_bulk_transfer_buf(us, info->wr_ep, data, 79662306a36Sopenharmony_ci (MEDIA_INFO(us).pagesize + 64) * MEDIA_INFO(us).blocksize, 79762306a36Sopenharmony_ci NULL); 79862306a36Sopenharmony_ci if (rc != USB_STOR_XFER_GOOD) 79962306a36Sopenharmony_ci return rc; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci return alauda_check_status2(us); 80262306a36Sopenharmony_ci} 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci/* 80562306a36Sopenharmony_ci * Write some data to a specific LBA. 80662306a36Sopenharmony_ci */ 80762306a36Sopenharmony_cistatic int alauda_write_lba(struct us_data *us, u16 lba, 80862306a36Sopenharmony_ci unsigned int page, unsigned int pages, 80962306a36Sopenharmony_ci unsigned char *ptr, unsigned char *blockbuffer) 81062306a36Sopenharmony_ci{ 81162306a36Sopenharmony_ci u16 pba, lbap, new_pba; 81262306a36Sopenharmony_ci unsigned char *bptr, *cptr, *xptr; 81362306a36Sopenharmony_ci unsigned char ecc[3]; 81462306a36Sopenharmony_ci int i, result; 81562306a36Sopenharmony_ci unsigned int uzonesize = MEDIA_INFO(us).uzonesize; 81662306a36Sopenharmony_ci unsigned int zonesize = MEDIA_INFO(us).zonesize; 81762306a36Sopenharmony_ci unsigned int pagesize = MEDIA_INFO(us).pagesize; 81862306a36Sopenharmony_ci unsigned int blocksize = MEDIA_INFO(us).blocksize; 81962306a36Sopenharmony_ci unsigned int lba_offset = lba % uzonesize; 82062306a36Sopenharmony_ci unsigned int new_pba_offset; 82162306a36Sopenharmony_ci unsigned int zone = lba / uzonesize; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci alauda_ensure_map_for_zone(us, zone); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci pba = MEDIA_INFO(us).lba_to_pba[zone][lba_offset]; 82662306a36Sopenharmony_ci if (pba == 1) { 82762306a36Sopenharmony_ci /* 82862306a36Sopenharmony_ci * Maybe it is impossible to write to PBA 1. 82962306a36Sopenharmony_ci * Fake success, but don't do anything. 83062306a36Sopenharmony_ci */ 83162306a36Sopenharmony_ci printk(KERN_WARNING 83262306a36Sopenharmony_ci "alauda_write_lba: avoid writing to pba 1\n"); 83362306a36Sopenharmony_ci return USB_STOR_TRANSPORT_GOOD; 83462306a36Sopenharmony_ci } 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci new_pba = alauda_find_unused_pba(&MEDIA_INFO(us), zone); 83762306a36Sopenharmony_ci if (!new_pba) { 83862306a36Sopenharmony_ci printk(KERN_WARNING 83962306a36Sopenharmony_ci "alauda_write_lba: Out of unused blocks\n"); 84062306a36Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 84162306a36Sopenharmony_ci } 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci /* read old contents */ 84462306a36Sopenharmony_ci if (pba != UNDEF) { 84562306a36Sopenharmony_ci result = alauda_read_block_raw(us, pba, 0, 84662306a36Sopenharmony_ci blocksize, blockbuffer); 84762306a36Sopenharmony_ci if (result != USB_STOR_XFER_GOOD) 84862306a36Sopenharmony_ci return result; 84962306a36Sopenharmony_ci } else { 85062306a36Sopenharmony_ci memset(blockbuffer, 0, blocksize * (pagesize + 64)); 85162306a36Sopenharmony_ci } 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci lbap = (lba_offset << 1) | 0x1000; 85462306a36Sopenharmony_ci if (parity[MSB_of(lbap) ^ LSB_of(lbap)]) 85562306a36Sopenharmony_ci lbap ^= 1; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci /* check old contents and fill lba */ 85862306a36Sopenharmony_ci for (i = 0; i < blocksize; i++) { 85962306a36Sopenharmony_ci bptr = blockbuffer + (i * (pagesize + 64)); 86062306a36Sopenharmony_ci cptr = bptr + pagesize; 86162306a36Sopenharmony_ci nand_compute_ecc(bptr, ecc); 86262306a36Sopenharmony_ci if (!nand_compare_ecc(cptr+13, ecc)) { 86362306a36Sopenharmony_ci usb_stor_dbg(us, "Warning: bad ecc in page %d- of pba %d\n", 86462306a36Sopenharmony_ci i, pba); 86562306a36Sopenharmony_ci nand_store_ecc(cptr+13, ecc); 86662306a36Sopenharmony_ci } 86762306a36Sopenharmony_ci nand_compute_ecc(bptr + (pagesize / 2), ecc); 86862306a36Sopenharmony_ci if (!nand_compare_ecc(cptr+8, ecc)) { 86962306a36Sopenharmony_ci usb_stor_dbg(us, "Warning: bad ecc in page %d+ of pba %d\n", 87062306a36Sopenharmony_ci i, pba); 87162306a36Sopenharmony_ci nand_store_ecc(cptr+8, ecc); 87262306a36Sopenharmony_ci } 87362306a36Sopenharmony_ci cptr[6] = cptr[11] = MSB_of(lbap); 87462306a36Sopenharmony_ci cptr[7] = cptr[12] = LSB_of(lbap); 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci /* copy in new stuff and compute ECC */ 87862306a36Sopenharmony_ci xptr = ptr; 87962306a36Sopenharmony_ci for (i = page; i < page+pages; i++) { 88062306a36Sopenharmony_ci bptr = blockbuffer + (i * (pagesize + 64)); 88162306a36Sopenharmony_ci cptr = bptr + pagesize; 88262306a36Sopenharmony_ci memcpy(bptr, xptr, pagesize); 88362306a36Sopenharmony_ci xptr += pagesize; 88462306a36Sopenharmony_ci nand_compute_ecc(bptr, ecc); 88562306a36Sopenharmony_ci nand_store_ecc(cptr+13, ecc); 88662306a36Sopenharmony_ci nand_compute_ecc(bptr + (pagesize / 2), ecc); 88762306a36Sopenharmony_ci nand_store_ecc(cptr+8, ecc); 88862306a36Sopenharmony_ci } 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci result = alauda_write_block(us, new_pba, blockbuffer); 89162306a36Sopenharmony_ci if (result != USB_STOR_XFER_GOOD) 89262306a36Sopenharmony_ci return result; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci new_pba_offset = new_pba - (zone * zonesize); 89562306a36Sopenharmony_ci MEDIA_INFO(us).pba_to_lba[zone][new_pba_offset] = lba; 89662306a36Sopenharmony_ci MEDIA_INFO(us).lba_to_pba[zone][lba_offset] = new_pba; 89762306a36Sopenharmony_ci usb_stor_dbg(us, "Remapped LBA %d to PBA %d\n", lba, new_pba); 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci if (pba != UNDEF) { 90062306a36Sopenharmony_ci unsigned int pba_offset = pba - (zone * zonesize); 90162306a36Sopenharmony_ci result = alauda_erase_block(us, pba); 90262306a36Sopenharmony_ci if (result != USB_STOR_XFER_GOOD) 90362306a36Sopenharmony_ci return result; 90462306a36Sopenharmony_ci MEDIA_INFO(us).pba_to_lba[zone][pba_offset] = UNDEF; 90562306a36Sopenharmony_ci } 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci return USB_STOR_TRANSPORT_GOOD; 90862306a36Sopenharmony_ci} 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci/* 91162306a36Sopenharmony_ci * Read data from a specific sector address 91262306a36Sopenharmony_ci */ 91362306a36Sopenharmony_cistatic int alauda_read_data(struct us_data *us, unsigned long address, 91462306a36Sopenharmony_ci unsigned int sectors) 91562306a36Sopenharmony_ci{ 91662306a36Sopenharmony_ci unsigned char *buffer; 91762306a36Sopenharmony_ci u16 lba, max_lba; 91862306a36Sopenharmony_ci unsigned int page, len, offset; 91962306a36Sopenharmony_ci unsigned int blockshift = MEDIA_INFO(us).blockshift; 92062306a36Sopenharmony_ci unsigned int pageshift = MEDIA_INFO(us).pageshift; 92162306a36Sopenharmony_ci unsigned int blocksize = MEDIA_INFO(us).blocksize; 92262306a36Sopenharmony_ci unsigned int pagesize = MEDIA_INFO(us).pagesize; 92362306a36Sopenharmony_ci unsigned int uzonesize = MEDIA_INFO(us).uzonesize; 92462306a36Sopenharmony_ci struct scatterlist *sg; 92562306a36Sopenharmony_ci int result; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci /* 92862306a36Sopenharmony_ci * Since we only read in one block at a time, we have to create 92962306a36Sopenharmony_ci * a bounce buffer and move the data a piece at a time between the 93062306a36Sopenharmony_ci * bounce buffer and the actual transfer buffer. 93162306a36Sopenharmony_ci * We make this buffer big enough to hold temporary redundancy data, 93262306a36Sopenharmony_ci * which we use when reading the data blocks. 93362306a36Sopenharmony_ci */ 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci len = min(sectors, blocksize) * (pagesize + 64); 93662306a36Sopenharmony_ci buffer = kmalloc(len, GFP_NOIO); 93762306a36Sopenharmony_ci if (!buffer) 93862306a36Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci /* Figure out the initial LBA and page */ 94162306a36Sopenharmony_ci lba = address >> blockshift; 94262306a36Sopenharmony_ci page = (address & MEDIA_INFO(us).blockmask); 94362306a36Sopenharmony_ci max_lba = MEDIA_INFO(us).capacity >> (blockshift + pageshift); 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci result = USB_STOR_TRANSPORT_GOOD; 94662306a36Sopenharmony_ci offset = 0; 94762306a36Sopenharmony_ci sg = NULL; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci while (sectors > 0) { 95062306a36Sopenharmony_ci unsigned int zone = lba / uzonesize; /* integer division */ 95162306a36Sopenharmony_ci unsigned int lba_offset = lba - (zone * uzonesize); 95262306a36Sopenharmony_ci unsigned int pages; 95362306a36Sopenharmony_ci u16 pba; 95462306a36Sopenharmony_ci alauda_ensure_map_for_zone(us, zone); 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci /* Not overflowing capacity? */ 95762306a36Sopenharmony_ci if (lba >= max_lba) { 95862306a36Sopenharmony_ci usb_stor_dbg(us, "Error: Requested lba %u exceeds maximum %u\n", 95962306a36Sopenharmony_ci lba, max_lba); 96062306a36Sopenharmony_ci result = USB_STOR_TRANSPORT_ERROR; 96162306a36Sopenharmony_ci break; 96262306a36Sopenharmony_ci } 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci /* Find number of pages we can read in this block */ 96562306a36Sopenharmony_ci pages = min(sectors, blocksize - page); 96662306a36Sopenharmony_ci len = pages << pageshift; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci /* Find where this lba lives on disk */ 96962306a36Sopenharmony_ci pba = MEDIA_INFO(us).lba_to_pba[zone][lba_offset]; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci if (pba == UNDEF) { /* this lba was never written */ 97262306a36Sopenharmony_ci usb_stor_dbg(us, "Read %d zero pages (LBA %d) page %d\n", 97362306a36Sopenharmony_ci pages, lba, page); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci /* 97662306a36Sopenharmony_ci * This is not really an error. It just means 97762306a36Sopenharmony_ci * that the block has never been written. 97862306a36Sopenharmony_ci * Instead of returning USB_STOR_TRANSPORT_ERROR 97962306a36Sopenharmony_ci * it is better to return all zero data. 98062306a36Sopenharmony_ci */ 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci memset(buffer, 0, len); 98362306a36Sopenharmony_ci } else { 98462306a36Sopenharmony_ci usb_stor_dbg(us, "Read %d pages, from PBA %d (LBA %d) page %d\n", 98562306a36Sopenharmony_ci pages, pba, lba, page); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci result = alauda_read_block(us, pba, page, pages, buffer); 98862306a36Sopenharmony_ci if (result != USB_STOR_TRANSPORT_GOOD) 98962306a36Sopenharmony_ci break; 99062306a36Sopenharmony_ci } 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci /* Store the data in the transfer buffer */ 99362306a36Sopenharmony_ci usb_stor_access_xfer_buf(buffer, len, us->srb, 99462306a36Sopenharmony_ci &sg, &offset, TO_XFER_BUF); 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci page = 0; 99762306a36Sopenharmony_ci lba++; 99862306a36Sopenharmony_ci sectors -= pages; 99962306a36Sopenharmony_ci } 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci kfree(buffer); 100262306a36Sopenharmony_ci return result; 100362306a36Sopenharmony_ci} 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci/* 100662306a36Sopenharmony_ci * Write data to a specific sector address 100762306a36Sopenharmony_ci */ 100862306a36Sopenharmony_cistatic int alauda_write_data(struct us_data *us, unsigned long address, 100962306a36Sopenharmony_ci unsigned int sectors) 101062306a36Sopenharmony_ci{ 101162306a36Sopenharmony_ci unsigned char *buffer, *blockbuffer; 101262306a36Sopenharmony_ci unsigned int page, len, offset; 101362306a36Sopenharmony_ci unsigned int blockshift = MEDIA_INFO(us).blockshift; 101462306a36Sopenharmony_ci unsigned int pageshift = MEDIA_INFO(us).pageshift; 101562306a36Sopenharmony_ci unsigned int blocksize = MEDIA_INFO(us).blocksize; 101662306a36Sopenharmony_ci unsigned int pagesize = MEDIA_INFO(us).pagesize; 101762306a36Sopenharmony_ci struct scatterlist *sg; 101862306a36Sopenharmony_ci u16 lba, max_lba; 101962306a36Sopenharmony_ci int result; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci /* 102262306a36Sopenharmony_ci * Since we don't write the user data directly to the device, 102362306a36Sopenharmony_ci * we have to create a bounce buffer and move the data a piece 102462306a36Sopenharmony_ci * at a time between the bounce buffer and the actual transfer buffer. 102562306a36Sopenharmony_ci */ 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci len = min(sectors, blocksize) * pagesize; 102862306a36Sopenharmony_ci buffer = kmalloc(len, GFP_NOIO); 102962306a36Sopenharmony_ci if (!buffer) 103062306a36Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci /* 103362306a36Sopenharmony_ci * We also need a temporary block buffer, where we read in the old data, 103462306a36Sopenharmony_ci * overwrite parts with the new data, and manipulate the redundancy data 103562306a36Sopenharmony_ci */ 103662306a36Sopenharmony_ci blockbuffer = kmalloc_array(pagesize + 64, blocksize, GFP_NOIO); 103762306a36Sopenharmony_ci if (!blockbuffer) { 103862306a36Sopenharmony_ci kfree(buffer); 103962306a36Sopenharmony_ci return USB_STOR_TRANSPORT_ERROR; 104062306a36Sopenharmony_ci } 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci /* Figure out the initial LBA and page */ 104362306a36Sopenharmony_ci lba = address >> blockshift; 104462306a36Sopenharmony_ci page = (address & MEDIA_INFO(us).blockmask); 104562306a36Sopenharmony_ci max_lba = MEDIA_INFO(us).capacity >> (pageshift + blockshift); 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci result = USB_STOR_TRANSPORT_GOOD; 104862306a36Sopenharmony_ci offset = 0; 104962306a36Sopenharmony_ci sg = NULL; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci while (sectors > 0) { 105262306a36Sopenharmony_ci /* Write as many sectors as possible in this block */ 105362306a36Sopenharmony_ci unsigned int pages = min(sectors, blocksize - page); 105462306a36Sopenharmony_ci len = pages << pageshift; 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci /* Not overflowing capacity? */ 105762306a36Sopenharmony_ci if (lba >= max_lba) { 105862306a36Sopenharmony_ci usb_stor_dbg(us, "Requested lba %u exceeds maximum %u\n", 105962306a36Sopenharmony_ci lba, max_lba); 106062306a36Sopenharmony_ci result = USB_STOR_TRANSPORT_ERROR; 106162306a36Sopenharmony_ci break; 106262306a36Sopenharmony_ci } 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci /* Get the data from the transfer buffer */ 106562306a36Sopenharmony_ci usb_stor_access_xfer_buf(buffer, len, us->srb, 106662306a36Sopenharmony_ci &sg, &offset, FROM_XFER_BUF); 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci result = alauda_write_lba(us, lba, page, pages, buffer, 106962306a36Sopenharmony_ci blockbuffer); 107062306a36Sopenharmony_ci if (result != USB_STOR_TRANSPORT_GOOD) 107162306a36Sopenharmony_ci break; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci page = 0; 107462306a36Sopenharmony_ci lba++; 107562306a36Sopenharmony_ci sectors -= pages; 107662306a36Sopenharmony_ci } 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci kfree(buffer); 107962306a36Sopenharmony_ci kfree(blockbuffer); 108062306a36Sopenharmony_ci return result; 108162306a36Sopenharmony_ci} 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci/* 108462306a36Sopenharmony_ci * Our interface with the rest of the world 108562306a36Sopenharmony_ci */ 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_cistatic void alauda_info_destructor(void *extra) 108862306a36Sopenharmony_ci{ 108962306a36Sopenharmony_ci struct alauda_info *info = (struct alauda_info *) extra; 109062306a36Sopenharmony_ci int port; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci if (!info) 109362306a36Sopenharmony_ci return; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci for (port = 0; port < 2; port++) { 109662306a36Sopenharmony_ci struct alauda_media_info *media_info = &info->port[port]; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci alauda_free_maps(media_info); 109962306a36Sopenharmony_ci kfree(media_info->lba_to_pba); 110062306a36Sopenharmony_ci kfree(media_info->pba_to_lba); 110162306a36Sopenharmony_ci } 110262306a36Sopenharmony_ci} 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci/* 110562306a36Sopenharmony_ci * Initialize alauda_info struct and find the data-write endpoint 110662306a36Sopenharmony_ci */ 110762306a36Sopenharmony_cistatic int init_alauda(struct us_data *us) 110862306a36Sopenharmony_ci{ 110962306a36Sopenharmony_ci struct alauda_info *info; 111062306a36Sopenharmony_ci struct usb_host_interface *altsetting = us->pusb_intf->cur_altsetting; 111162306a36Sopenharmony_ci nand_init_ecc(); 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci us->extra = kzalloc(sizeof(struct alauda_info), GFP_NOIO); 111462306a36Sopenharmony_ci if (!us->extra) 111562306a36Sopenharmony_ci return -ENOMEM; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci info = (struct alauda_info *) us->extra; 111862306a36Sopenharmony_ci us->extra_destructor = alauda_info_destructor; 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci info->wr_ep = usb_sndbulkpipe(us->pusb_dev, 112162306a36Sopenharmony_ci altsetting->endpoint[0].desc.bEndpointAddress 112262306a36Sopenharmony_ci & USB_ENDPOINT_NUMBER_MASK); 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci return 0; 112562306a36Sopenharmony_ci} 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_cistatic int alauda_transport(struct scsi_cmnd *srb, struct us_data *us) 112862306a36Sopenharmony_ci{ 112962306a36Sopenharmony_ci int rc; 113062306a36Sopenharmony_ci struct alauda_info *info = (struct alauda_info *) us->extra; 113162306a36Sopenharmony_ci unsigned char *ptr = us->iobuf; 113262306a36Sopenharmony_ci static unsigned char inquiry_response[36] = { 113362306a36Sopenharmony_ci 0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00 113462306a36Sopenharmony_ci }; 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci if (srb->cmnd[0] == INQUIRY) { 113762306a36Sopenharmony_ci usb_stor_dbg(us, "INQUIRY - Returning bogus response\n"); 113862306a36Sopenharmony_ci memcpy(ptr, inquiry_response, sizeof(inquiry_response)); 113962306a36Sopenharmony_ci fill_inquiry_response(us, ptr, 36); 114062306a36Sopenharmony_ci return USB_STOR_TRANSPORT_GOOD; 114162306a36Sopenharmony_ci } 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci if (srb->cmnd[0] == TEST_UNIT_READY) { 114462306a36Sopenharmony_ci usb_stor_dbg(us, "TEST_UNIT_READY\n"); 114562306a36Sopenharmony_ci return alauda_check_media(us); 114662306a36Sopenharmony_ci } 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci if (srb->cmnd[0] == READ_CAPACITY) { 114962306a36Sopenharmony_ci unsigned int num_zones; 115062306a36Sopenharmony_ci unsigned long capacity; 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci rc = alauda_check_media(us); 115362306a36Sopenharmony_ci if (rc != USB_STOR_TRANSPORT_GOOD) 115462306a36Sopenharmony_ci return rc; 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci num_zones = MEDIA_INFO(us).capacity >> (MEDIA_INFO(us).zoneshift 115762306a36Sopenharmony_ci + MEDIA_INFO(us).blockshift + MEDIA_INFO(us).pageshift); 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci capacity = num_zones * MEDIA_INFO(us).uzonesize 116062306a36Sopenharmony_ci * MEDIA_INFO(us).blocksize; 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci /* Report capacity and page size */ 116362306a36Sopenharmony_ci ((__be32 *) ptr)[0] = cpu_to_be32(capacity - 1); 116462306a36Sopenharmony_ci ((__be32 *) ptr)[1] = cpu_to_be32(512); 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci usb_stor_set_xfer_buf(ptr, 8, srb); 116762306a36Sopenharmony_ci return USB_STOR_TRANSPORT_GOOD; 116862306a36Sopenharmony_ci } 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci if (srb->cmnd[0] == READ_10) { 117162306a36Sopenharmony_ci unsigned int page, pages; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci rc = alauda_check_media(us); 117462306a36Sopenharmony_ci if (rc != USB_STOR_TRANSPORT_GOOD) 117562306a36Sopenharmony_ci return rc; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci page = short_pack(srb->cmnd[3], srb->cmnd[2]); 117862306a36Sopenharmony_ci page <<= 16; 117962306a36Sopenharmony_ci page |= short_pack(srb->cmnd[5], srb->cmnd[4]); 118062306a36Sopenharmony_ci pages = short_pack(srb->cmnd[8], srb->cmnd[7]); 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci usb_stor_dbg(us, "READ_10: page %d pagect %d\n", page, pages); 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci return alauda_read_data(us, page, pages); 118562306a36Sopenharmony_ci } 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci if (srb->cmnd[0] == WRITE_10) { 118862306a36Sopenharmony_ci unsigned int page, pages; 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci rc = alauda_check_media(us); 119162306a36Sopenharmony_ci if (rc != USB_STOR_TRANSPORT_GOOD) 119262306a36Sopenharmony_ci return rc; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci page = short_pack(srb->cmnd[3], srb->cmnd[2]); 119562306a36Sopenharmony_ci page <<= 16; 119662306a36Sopenharmony_ci page |= short_pack(srb->cmnd[5], srb->cmnd[4]); 119762306a36Sopenharmony_ci pages = short_pack(srb->cmnd[8], srb->cmnd[7]); 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci usb_stor_dbg(us, "WRITE_10: page %d pagect %d\n", page, pages); 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci return alauda_write_data(us, page, pages); 120262306a36Sopenharmony_ci } 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci if (srb->cmnd[0] == REQUEST_SENSE) { 120562306a36Sopenharmony_ci usb_stor_dbg(us, "REQUEST_SENSE\n"); 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci memset(ptr, 0, 18); 120862306a36Sopenharmony_ci ptr[0] = 0xF0; 120962306a36Sopenharmony_ci ptr[2] = info->sense_key; 121062306a36Sopenharmony_ci ptr[7] = 11; 121162306a36Sopenharmony_ci ptr[12] = info->sense_asc; 121262306a36Sopenharmony_ci ptr[13] = info->sense_ascq; 121362306a36Sopenharmony_ci usb_stor_set_xfer_buf(ptr, 18, srb); 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci return USB_STOR_TRANSPORT_GOOD; 121662306a36Sopenharmony_ci } 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) { 121962306a36Sopenharmony_ci /* 122062306a36Sopenharmony_ci * sure. whatever. not like we can stop the user from popping 122162306a36Sopenharmony_ci * the media out of the device (no locking doors, etc) 122262306a36Sopenharmony_ci */ 122362306a36Sopenharmony_ci return USB_STOR_TRANSPORT_GOOD; 122462306a36Sopenharmony_ci } 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci usb_stor_dbg(us, "Gah! Unknown command: %d (0x%x)\n", 122762306a36Sopenharmony_ci srb->cmnd[0], srb->cmnd[0]); 122862306a36Sopenharmony_ci info->sense_key = 0x05; 122962306a36Sopenharmony_ci info->sense_asc = 0x20; 123062306a36Sopenharmony_ci info->sense_ascq = 0x00; 123162306a36Sopenharmony_ci return USB_STOR_TRANSPORT_FAILED; 123262306a36Sopenharmony_ci} 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_cistatic struct scsi_host_template alauda_host_template; 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_cistatic int alauda_probe(struct usb_interface *intf, 123762306a36Sopenharmony_ci const struct usb_device_id *id) 123862306a36Sopenharmony_ci{ 123962306a36Sopenharmony_ci struct us_data *us; 124062306a36Sopenharmony_ci int result; 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci result = usb_stor_probe1(&us, intf, id, 124362306a36Sopenharmony_ci (id - alauda_usb_ids) + alauda_unusual_dev_list, 124462306a36Sopenharmony_ci &alauda_host_template); 124562306a36Sopenharmony_ci if (result) 124662306a36Sopenharmony_ci return result; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci us->transport_name = "Alauda Control/Bulk"; 124962306a36Sopenharmony_ci us->transport = alauda_transport; 125062306a36Sopenharmony_ci us->transport_reset = usb_stor_Bulk_reset; 125162306a36Sopenharmony_ci us->max_lun = 1; 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci result = usb_stor_probe2(us); 125462306a36Sopenharmony_ci return result; 125562306a36Sopenharmony_ci} 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_cistatic struct usb_driver alauda_driver = { 125862306a36Sopenharmony_ci .name = DRV_NAME, 125962306a36Sopenharmony_ci .probe = alauda_probe, 126062306a36Sopenharmony_ci .disconnect = usb_stor_disconnect, 126162306a36Sopenharmony_ci .suspend = usb_stor_suspend, 126262306a36Sopenharmony_ci .resume = usb_stor_resume, 126362306a36Sopenharmony_ci .reset_resume = usb_stor_reset_resume, 126462306a36Sopenharmony_ci .pre_reset = usb_stor_pre_reset, 126562306a36Sopenharmony_ci .post_reset = usb_stor_post_reset, 126662306a36Sopenharmony_ci .id_table = alauda_usb_ids, 126762306a36Sopenharmony_ci .soft_unbind = 1, 126862306a36Sopenharmony_ci .no_dynamic_id = 1, 126962306a36Sopenharmony_ci}; 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_cimodule_usb_stor_driver(alauda_driver, alauda_host_template, DRV_NAME); 1272