forked from Minki/linux
5a0e3ad6af
percpu.h is included by sched.h and module.h and thus ends up being included when building most .c files. percpu.h includes slab.h which in turn includes gfp.h making everything defined by the two files universally available and complicating inclusion dependencies. percpu.h -> slab.h dependency is about to be removed. Prepare for this change by updating users of gfp and slab facilities include those headers directly instead of assuming availability. As this conversion needs to touch large number of source files, the following script is used as the basis of conversion. http://userweb.kernel.org/~tj/misc/slabh-sweep.py The script does the followings. * Scan files for gfp and slab usages and update includes such that only the necessary includes are there. ie. if only gfp is used, gfp.h, if slab is used, slab.h. * When the script inserts a new include, it looks at the include blocks and try to put the new include such that its order conforms to its surrounding. It's put in the include block which contains core kernel includes, in the same order that the rest are ordered - alphabetical, Christmas tree, rev-Xmas-tree or at the end if there doesn't seem to be any matching order. * If the script can't find a place to put a new include (mostly because the file doesn't have fitting include block), it prints out an error message indicating which .h file needs to be added to the file. The conversion was done in the following steps. 1. The initial automatic conversion of all .c files updated slightly over 4000 files, deleting around 700 includes and adding ~480 gfp.h and ~3000 slab.h inclusions. The script emitted errors for ~400 files. 2. Each error was manually checked. Some didn't need the inclusion, some needed manual addition while adding it to implementation .h or embedding .c file was more appropriate for others. This step added inclusions to around 150 files. 3. The script was run again and the output was compared to the edits from #2 to make sure no file was left behind. 4. Several build tests were done and a couple of problems were fixed. e.g. lib/decompress_*.c used malloc/free() wrappers around slab APIs requiring slab.h to be added manually. 5. The script was run on all .h files but without automatically editing them as sprinkling gfp.h and slab.h inclusions around .h files could easily lead to inclusion dependency hell. Most gfp.h inclusion directives were ignored as stuff from gfp.h was usually wildly available and often used in preprocessor macros. Each slab.h inclusion directive was examined and added manually as necessary. 6. percpu.h was updated not to include slab.h. 7. Build test were done on the following configurations and failures were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my distributed build env didn't work with gcov compiles) and a few more options had to be turned off depending on archs to make things build (like ipr on powerpc/64 which failed due to missing writeq). * x86 and x86_64 UP and SMP allmodconfig and a custom test config. * powerpc and powerpc64 SMP allmodconfig * sparc and sparc64 SMP allmodconfig * ia64 SMP allmodconfig * s390 SMP allmodconfig * alpha SMP allmodconfig * um on x86_64 SMP allmodconfig 8. percpu.h modifications were reverted so that it could be applied as a separate patch and serve as bisection point. Given the fact that I had only a couple of failures from tests on step 6, I'm fairly confident about the coverage of this conversion patch. If there is a breakage, it's likely to be something in one of the arch headers which should be easily discoverable easily on most builds of the specific arch. Signed-off-by: Tejun Heo <tj@kernel.org> Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
1293 lines
34 KiB
C
1293 lines
34 KiB
C
/*
|
|
* Driver for Alauda-based card readers
|
|
*
|
|
* Current development and maintenance by:
|
|
* (c) 2005 Daniel Drake <dsd@gentoo.org>
|
|
*
|
|
* The 'Alauda' is a chip manufacturered by RATOC for OEM use.
|
|
*
|
|
* Alauda implements a vendor-specific command set to access two media reader
|
|
* ports (XD, SmartMedia). This driver converts SCSI commands to the commands
|
|
* which are accepted by these devices.
|
|
*
|
|
* The driver was developed through reverse-engineering, with the help of the
|
|
* sddr09 driver which has many similarities, and with some help from the
|
|
* (very old) vendor-supplied GPL sma03 driver.
|
|
*
|
|
* For protocol info, see http://alauda.sourceforge.net
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2, or (at your option) any
|
|
* later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
|
|
#include <scsi/scsi.h>
|
|
#include <scsi/scsi_cmnd.h>
|
|
#include <scsi/scsi_device.h>
|
|
|
|
#include "usb.h"
|
|
#include "transport.h"
|
|
#include "protocol.h"
|
|
#include "debug.h"
|
|
|
|
MODULE_DESCRIPTION("Driver for Alauda-based card readers");
|
|
MODULE_AUTHOR("Daniel Drake <dsd@gentoo.org>");
|
|
MODULE_LICENSE("GPL");
|
|
|
|
/*
|
|
* Status bytes
|
|
*/
|
|
#define ALAUDA_STATUS_ERROR 0x01
|
|
#define ALAUDA_STATUS_READY 0x40
|
|
|
|
/*
|
|
* Control opcodes (for request field)
|
|
*/
|
|
#define ALAUDA_GET_XD_MEDIA_STATUS 0x08
|
|
#define ALAUDA_GET_SM_MEDIA_STATUS 0x98
|
|
#define ALAUDA_ACK_XD_MEDIA_CHANGE 0x0a
|
|
#define ALAUDA_ACK_SM_MEDIA_CHANGE 0x9a
|
|
#define ALAUDA_GET_XD_MEDIA_SIG 0x86
|
|
#define ALAUDA_GET_SM_MEDIA_SIG 0x96
|
|
|
|
/*
|
|
* Bulk command identity (byte 0)
|
|
*/
|
|
#define ALAUDA_BULK_CMD 0x40
|
|
|
|
/*
|
|
* Bulk opcodes (byte 1)
|
|
*/
|
|
#define ALAUDA_BULK_GET_REDU_DATA 0x85
|
|
#define ALAUDA_BULK_READ_BLOCK 0x94
|
|
#define ALAUDA_BULK_ERASE_BLOCK 0xa3
|
|
#define ALAUDA_BULK_WRITE_BLOCK 0xb4
|
|
#define ALAUDA_BULK_GET_STATUS2 0xb7
|
|
#define ALAUDA_BULK_RESET_MEDIA 0xe0
|
|
|
|
/*
|
|
* Port to operate on (byte 8)
|
|
*/
|
|
#define ALAUDA_PORT_XD 0x00
|
|
#define ALAUDA_PORT_SM 0x01
|
|
|
|
/*
|
|
* LBA and PBA are unsigned ints. Special values.
|
|
*/
|
|
#define UNDEF 0xffff
|
|
#define SPARE 0xfffe
|
|
#define UNUSABLE 0xfffd
|
|
|
|
struct alauda_media_info {
|
|
unsigned long capacity; /* total media size in bytes */
|
|
unsigned int pagesize; /* page size in bytes */
|
|
unsigned int blocksize; /* number of pages per block */
|
|
unsigned int uzonesize; /* number of usable blocks per zone */
|
|
unsigned int zonesize; /* number of blocks per zone */
|
|
unsigned int blockmask; /* mask to get page from address */
|
|
|
|
unsigned char pageshift;
|
|
unsigned char blockshift;
|
|
unsigned char zoneshift;
|
|
|
|
u16 **lba_to_pba; /* logical to physical block map */
|
|
u16 **pba_to_lba; /* physical to logical block map */
|
|
};
|
|
|
|
struct alauda_info {
|
|
struct alauda_media_info port[2];
|
|
int wr_ep; /* endpoint to write data out of */
|
|
|
|
unsigned char sense_key;
|
|
unsigned long sense_asc; /* additional sense code */
|
|
unsigned long sense_ascq; /* additional sense code qualifier */
|
|
};
|
|
|
|
#define short_pack(lsb,msb) ( ((u16)(lsb)) | ( ((u16)(msb))<<8 ) )
|
|
#define LSB_of(s) ((s)&0xFF)
|
|
#define MSB_of(s) ((s)>>8)
|
|
|
|
#define MEDIA_PORT(us) us->srb->device->lun
|
|
#define MEDIA_INFO(us) ((struct alauda_info *)us->extra)->port[MEDIA_PORT(us)]
|
|
|
|
#define PBA_LO(pba) ((pba & 0xF) << 5)
|
|
#define PBA_HI(pba) (pba >> 3)
|
|
#define PBA_ZONE(pba) (pba >> 11)
|
|
|
|
static int init_alauda(struct us_data *us);
|
|
|
|
|
|
/*
|
|
* The table of devices
|
|
*/
|
|
#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \
|
|
vendorName, productName, useProtocol, useTransport, \
|
|
initFunction, flags) \
|
|
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
|
|
.driver_info = (flags)|(USB_US_TYPE_STOR<<24) }
|
|
|
|
struct usb_device_id alauda_usb_ids[] = {
|
|
# include "unusual_alauda.h"
|
|
{ } /* Terminating entry */
|
|
};
|
|
MODULE_DEVICE_TABLE(usb, alauda_usb_ids);
|
|
|
|
#undef UNUSUAL_DEV
|
|
|
|
/*
|
|
* The flags table
|
|
*/
|
|
#define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \
|
|
vendor_name, product_name, use_protocol, use_transport, \
|
|
init_function, Flags) \
|
|
{ \
|
|
.vendorName = vendor_name, \
|
|
.productName = product_name, \
|
|
.useProtocol = use_protocol, \
|
|
.useTransport = use_transport, \
|
|
.initFunction = init_function, \
|
|
}
|
|
|
|
static struct us_unusual_dev alauda_unusual_dev_list[] = {
|
|
# include "unusual_alauda.h"
|
|
{ } /* Terminating entry */
|
|
};
|
|
|
|
#undef UNUSUAL_DEV
|
|
|
|
|
|
/*
|
|
* Media handling
|
|
*/
|
|
|
|
struct alauda_card_info {
|
|
unsigned char id; /* id byte */
|
|
unsigned char chipshift; /* 1<<cs bytes total capacity */
|
|
unsigned char pageshift; /* 1<<ps bytes in a page */
|
|
unsigned char blockshift; /* 1<<bs pages per block */
|
|
unsigned char zoneshift; /* 1<<zs blocks per zone */
|
|
};
|
|
|
|
static struct alauda_card_info alauda_card_ids[] = {
|
|
/* NAND flash */
|
|
{ 0x6e, 20, 8, 4, 8}, /* 1 MB */
|
|
{ 0xe8, 20, 8, 4, 8}, /* 1 MB */
|
|
{ 0xec, 20, 8, 4, 8}, /* 1 MB */
|
|
{ 0x64, 21, 8, 4, 9}, /* 2 MB */
|
|
{ 0xea, 21, 8, 4, 9}, /* 2 MB */
|
|
{ 0x6b, 22, 9, 4, 9}, /* 4 MB */
|
|
{ 0xe3, 22, 9, 4, 9}, /* 4 MB */
|
|
{ 0xe5, 22, 9, 4, 9}, /* 4 MB */
|
|
{ 0xe6, 23, 9, 4, 10}, /* 8 MB */
|
|
{ 0x73, 24, 9, 5, 10}, /* 16 MB */
|
|
{ 0x75, 25, 9, 5, 10}, /* 32 MB */
|
|
{ 0x76, 26, 9, 5, 10}, /* 64 MB */
|
|
{ 0x79, 27, 9, 5, 10}, /* 128 MB */
|
|
{ 0x71, 28, 9, 5, 10}, /* 256 MB */
|
|
|
|
/* MASK ROM */
|
|
{ 0x5d, 21, 9, 4, 8}, /* 2 MB */
|
|
{ 0xd5, 22, 9, 4, 9}, /* 4 MB */
|
|
{ 0xd6, 23, 9, 4, 10}, /* 8 MB */
|
|
{ 0x57, 24, 9, 4, 11}, /* 16 MB */
|
|
{ 0x58, 25, 9, 4, 12}, /* 32 MB */
|
|
{ 0,}
|
|
};
|
|
|
|
static struct alauda_card_info *alauda_card_find_id(unsigned char id) {
|
|
int i;
|
|
|
|
for (i = 0; alauda_card_ids[i].id != 0; i++)
|
|
if (alauda_card_ids[i].id == id)
|
|
return &(alauda_card_ids[i]);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* ECC computation.
|
|
*/
|
|
|
|
static unsigned char parity[256];
|
|
static unsigned char ecc2[256];
|
|
|
|
static void nand_init_ecc(void) {
|
|
int i, j, a;
|
|
|
|
parity[0] = 0;
|
|
for (i = 1; i < 256; i++)
|
|
parity[i] = (parity[i&(i-1)] ^ 1);
|
|
|
|
for (i = 0; i < 256; i++) {
|
|
a = 0;
|
|
for (j = 0; j < 8; j++) {
|
|
if (i & (1<<j)) {
|
|
if ((j & 1) == 0)
|
|
a ^= 0x04;
|
|
if ((j & 2) == 0)
|
|
a ^= 0x10;
|
|
if ((j & 4) == 0)
|
|
a ^= 0x40;
|
|
}
|
|
}
|
|
ecc2[i] = ~(a ^ (a<<1) ^ (parity[i] ? 0xa8 : 0));
|
|
}
|
|
}
|
|
|
|
/* compute 3-byte ecc on 256 bytes */
|
|
static void nand_compute_ecc(unsigned char *data, unsigned char *ecc) {
|
|
int i, j, a;
|
|
unsigned char par, bit, bits[8];
|
|
|
|
par = 0;
|
|
for (j = 0; j < 8; j++)
|
|
bits[j] = 0;
|
|
|
|
/* collect 16 checksum bits */
|
|
for (i = 0; i < 256; i++) {
|
|
par ^= data[i];
|
|
bit = parity[data[i]];
|
|
for (j = 0; j < 8; j++)
|
|
if ((i & (1<<j)) == 0)
|
|
bits[j] ^= bit;
|
|
}
|
|
|
|
/* put 4+4+4 = 12 bits in the ecc */
|
|
a = (bits[3] << 6) + (bits[2] << 4) + (bits[1] << 2) + bits[0];
|
|
ecc[0] = ~(a ^ (a<<1) ^ (parity[par] ? 0xaa : 0));
|
|
|
|
a = (bits[7] << 6) + (bits[6] << 4) + (bits[5] << 2) + bits[4];
|
|
ecc[1] = ~(a ^ (a<<1) ^ (parity[par] ? 0xaa : 0));
|
|
|
|
ecc[2] = ecc2[par];
|
|
}
|
|
|
|
static int nand_compare_ecc(unsigned char *data, unsigned char *ecc) {
|
|
return (data[0] == ecc[0] && data[1] == ecc[1] && data[2] == ecc[2]);
|
|
}
|
|
|
|
static void nand_store_ecc(unsigned char *data, unsigned char *ecc) {
|
|
memcpy(data, ecc, 3);
|
|
}
|
|
|
|
/*
|
|
* Alauda driver
|
|
*/
|
|
|
|
/*
|
|
* Forget our PBA <---> LBA mappings for a particular port
|
|
*/
|
|
static void alauda_free_maps (struct alauda_media_info *media_info)
|
|
{
|
|
unsigned int shift = media_info->zoneshift
|
|
+ media_info->blockshift + media_info->pageshift;
|
|
unsigned int num_zones = media_info->capacity >> shift;
|
|
unsigned int i;
|
|
|
|
if (media_info->lba_to_pba != NULL)
|
|
for (i = 0; i < num_zones; i++) {
|
|
kfree(media_info->lba_to_pba[i]);
|
|
media_info->lba_to_pba[i] = NULL;
|
|
}
|
|
|
|
if (media_info->pba_to_lba != NULL)
|
|
for (i = 0; i < num_zones; i++) {
|
|
kfree(media_info->pba_to_lba[i]);
|
|
media_info->pba_to_lba[i] = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Returns 2 bytes of status data
|
|
* The first byte describes media status, and second byte describes door status
|
|
*/
|
|
static int alauda_get_media_status(struct us_data *us, unsigned char *data)
|
|
{
|
|
int rc;
|
|
unsigned char command;
|
|
|
|
if (MEDIA_PORT(us) == ALAUDA_PORT_XD)
|
|
command = ALAUDA_GET_XD_MEDIA_STATUS;
|
|
else
|
|
command = ALAUDA_GET_SM_MEDIA_STATUS;
|
|
|
|
rc = usb_stor_ctrl_transfer(us, us->recv_ctrl_pipe,
|
|
command, 0xc0, 0, 1, data, 2);
|
|
|
|
US_DEBUGP("alauda_get_media_status: Media status %02X %02X\n",
|
|
data[0], data[1]);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* Clears the "media was changed" bit so that we know when it changes again
|
|
* in the future.
|
|
*/
|
|
static int alauda_ack_media(struct us_data *us)
|
|
{
|
|
unsigned char command;
|
|
|
|
if (MEDIA_PORT(us) == ALAUDA_PORT_XD)
|
|
command = ALAUDA_ACK_XD_MEDIA_CHANGE;
|
|
else
|
|
command = ALAUDA_ACK_SM_MEDIA_CHANGE;
|
|
|
|
return usb_stor_ctrl_transfer(us, us->send_ctrl_pipe,
|
|
command, 0x40, 0, 1, NULL, 0);
|
|
}
|
|
|
|
/*
|
|
* Retrieves a 4-byte media signature, which indicates manufacturer, capacity,
|
|
* and some other details.
|
|
*/
|
|
static int alauda_get_media_signature(struct us_data *us, unsigned char *data)
|
|
{
|
|
unsigned char command;
|
|
|
|
if (MEDIA_PORT(us) == ALAUDA_PORT_XD)
|
|
command = ALAUDA_GET_XD_MEDIA_SIG;
|
|
else
|
|
command = ALAUDA_GET_SM_MEDIA_SIG;
|
|
|
|
return usb_stor_ctrl_transfer(us, us->recv_ctrl_pipe,
|
|
command, 0xc0, 0, 0, data, 4);
|
|
}
|
|
|
|
/*
|
|
* Resets the media status (but not the whole device?)
|
|
*/
|
|
static int alauda_reset_media(struct us_data *us)
|
|
{
|
|
unsigned char *command = us->iobuf;
|
|
|
|
memset(command, 0, 9);
|
|
command[0] = ALAUDA_BULK_CMD;
|
|
command[1] = ALAUDA_BULK_RESET_MEDIA;
|
|
command[8] = MEDIA_PORT(us);
|
|
|
|
return usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
|
|
command, 9, NULL);
|
|
}
|
|
|
|
/*
|
|
* Examines the media and deduces capacity, etc.
|
|
*/
|
|
static int alauda_init_media(struct us_data *us)
|
|
{
|
|
unsigned char *data = us->iobuf;
|
|
int ready = 0;
|
|
struct alauda_card_info *media_info;
|
|
unsigned int num_zones;
|
|
|
|
while (ready == 0) {
|
|
msleep(20);
|
|
|
|
if (alauda_get_media_status(us, data) != USB_STOR_XFER_GOOD)
|
|
return USB_STOR_TRANSPORT_ERROR;
|
|
|
|
if (data[0] & 0x10)
|
|
ready = 1;
|
|
}
|
|
|
|
US_DEBUGP("alauda_init_media: We are ready for action!\n");
|
|
|
|
if (alauda_ack_media(us) != USB_STOR_XFER_GOOD)
|
|
return USB_STOR_TRANSPORT_ERROR;
|
|
|
|
msleep(10);
|
|
|
|
if (alauda_get_media_status(us, data) != USB_STOR_XFER_GOOD)
|
|
return USB_STOR_TRANSPORT_ERROR;
|
|
|
|
if (data[0] != 0x14) {
|
|
US_DEBUGP("alauda_init_media: Media not ready after ack\n");
|
|
return USB_STOR_TRANSPORT_ERROR;
|
|
}
|
|
|
|
if (alauda_get_media_signature(us, data) != USB_STOR_XFER_GOOD)
|
|
return USB_STOR_TRANSPORT_ERROR;
|
|
|
|
US_DEBUGP("alauda_init_media: Media signature: %02X %02X %02X %02X\n",
|
|
data[0], data[1], data[2], data[3]);
|
|
media_info = alauda_card_find_id(data[1]);
|
|
if (media_info == NULL) {
|
|
printk(KERN_WARNING
|
|
"alauda_init_media: Unrecognised media signature: "
|
|
"%02X %02X %02X %02X\n",
|
|
data[0], data[1], data[2], data[3]);
|
|
return USB_STOR_TRANSPORT_ERROR;
|
|
}
|
|
|
|
MEDIA_INFO(us).capacity = 1 << media_info->chipshift;
|
|
US_DEBUGP("Found media with capacity: %ldMB\n",
|
|
MEDIA_INFO(us).capacity >> 20);
|
|
|
|
MEDIA_INFO(us).pageshift = media_info->pageshift;
|
|
MEDIA_INFO(us).blockshift = media_info->blockshift;
|
|
MEDIA_INFO(us).zoneshift = media_info->zoneshift;
|
|
|
|
MEDIA_INFO(us).pagesize = 1 << media_info->pageshift;
|
|
MEDIA_INFO(us).blocksize = 1 << media_info->blockshift;
|
|
MEDIA_INFO(us).zonesize = 1 << media_info->zoneshift;
|
|
|
|
MEDIA_INFO(us).uzonesize = ((1 << media_info->zoneshift) / 128) * 125;
|
|
MEDIA_INFO(us).blockmask = MEDIA_INFO(us).blocksize - 1;
|
|
|
|
num_zones = MEDIA_INFO(us).capacity >> (MEDIA_INFO(us).zoneshift
|
|
+ MEDIA_INFO(us).blockshift + MEDIA_INFO(us).pageshift);
|
|
MEDIA_INFO(us).pba_to_lba = kcalloc(num_zones, sizeof(u16*), GFP_NOIO);
|
|
MEDIA_INFO(us).lba_to_pba = kcalloc(num_zones, sizeof(u16*), GFP_NOIO);
|
|
|
|
if (alauda_reset_media(us) != USB_STOR_XFER_GOOD)
|
|
return USB_STOR_TRANSPORT_ERROR;
|
|
|
|
return USB_STOR_TRANSPORT_GOOD;
|
|
}
|
|
|
|
/*
|
|
* Examines the media status and does the right thing when the media has gone,
|
|
* appeared, or changed.
|
|
*/
|
|
static int alauda_check_media(struct us_data *us)
|
|
{
|
|
struct alauda_info *info = (struct alauda_info *) us->extra;
|
|
unsigned char status[2];
|
|
int rc;
|
|
|
|
rc = alauda_get_media_status(us, status);
|
|
|
|
/* Check for no media or door open */
|
|
if ((status[0] & 0x80) || ((status[0] & 0x1F) == 0x10)
|
|
|| ((status[1] & 0x01) == 0)) {
|
|
US_DEBUGP("alauda_check_media: No media, or door open\n");
|
|
alauda_free_maps(&MEDIA_INFO(us));
|
|
info->sense_key = 0x02;
|
|
info->sense_asc = 0x3A;
|
|
info->sense_ascq = 0x00;
|
|
return USB_STOR_TRANSPORT_FAILED;
|
|
}
|
|
|
|
/* Check for media change */
|
|
if (status[0] & 0x08) {
|
|
US_DEBUGP("alauda_check_media: Media change detected\n");
|
|
alauda_free_maps(&MEDIA_INFO(us));
|
|
alauda_init_media(us);
|
|
|
|
info->sense_key = UNIT_ATTENTION;
|
|
info->sense_asc = 0x28;
|
|
info->sense_ascq = 0x00;
|
|
return USB_STOR_TRANSPORT_FAILED;
|
|
}
|
|
|
|
return USB_STOR_TRANSPORT_GOOD;
|
|
}
|
|
|
|
/*
|
|
* Checks the status from the 2nd status register
|
|
* Returns 3 bytes of status data, only the first is known
|
|
*/
|
|
static int alauda_check_status2(struct us_data *us)
|
|
{
|
|
int rc;
|
|
unsigned char command[] = {
|
|
ALAUDA_BULK_CMD, ALAUDA_BULK_GET_STATUS2,
|
|
0, 0, 0, 0, 3, 0, MEDIA_PORT(us)
|
|
};
|
|
unsigned char data[3];
|
|
|
|
rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
|
|
command, 9, NULL);
|
|
if (rc != USB_STOR_XFER_GOOD)
|
|
return rc;
|
|
|
|
rc = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
|
|
data, 3, NULL);
|
|
if (rc != USB_STOR_XFER_GOOD)
|
|
return rc;
|
|
|
|
US_DEBUGP("alauda_check_status2: %02X %02X %02X\n", data[0], data[1], data[2]);
|
|
if (data[0] & ALAUDA_STATUS_ERROR)
|
|
return USB_STOR_XFER_ERROR;
|
|
|
|
return USB_STOR_XFER_GOOD;
|
|
}
|
|
|
|
/*
|
|
* Gets the redundancy data for the first page of a PBA
|
|
* Returns 16 bytes.
|
|
*/
|
|
static int alauda_get_redu_data(struct us_data *us, u16 pba, unsigned char *data)
|
|
{
|
|
int rc;
|
|
unsigned char command[] = {
|
|
ALAUDA_BULK_CMD, ALAUDA_BULK_GET_REDU_DATA,
|
|
PBA_HI(pba), PBA_ZONE(pba), 0, PBA_LO(pba), 0, 0, MEDIA_PORT(us)
|
|
};
|
|
|
|
rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
|
|
command, 9, NULL);
|
|
if (rc != USB_STOR_XFER_GOOD)
|
|
return rc;
|
|
|
|
return usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
|
|
data, 16, NULL);
|
|
}
|
|
|
|
/*
|
|
* Finds the first unused PBA in a zone
|
|
* Returns the absolute PBA of an unused PBA, or 0 if none found.
|
|
*/
|
|
static u16 alauda_find_unused_pba(struct alauda_media_info *info,
|
|
unsigned int zone)
|
|
{
|
|
u16 *pba_to_lba = info->pba_to_lba[zone];
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < info->zonesize; i++)
|
|
if (pba_to_lba[i] == UNDEF)
|
|
return (zone << info->zoneshift) + i;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Reads the redundancy data for all PBA's in a zone
|
|
* Produces lba <--> pba mappings
|
|
*/
|
|
static int alauda_read_map(struct us_data *us, unsigned int zone)
|
|
{
|
|
unsigned char *data = us->iobuf;
|
|
int result;
|
|
int i, j;
|
|
unsigned int zonesize = MEDIA_INFO(us).zonesize;
|
|
unsigned int uzonesize = MEDIA_INFO(us).uzonesize;
|
|
unsigned int lba_offset, lba_real, blocknum;
|
|
unsigned int zone_base_lba = zone * uzonesize;
|
|
unsigned int zone_base_pba = zone * zonesize;
|
|
u16 *lba_to_pba = kcalloc(zonesize, sizeof(u16), GFP_NOIO);
|
|
u16 *pba_to_lba = kcalloc(zonesize, sizeof(u16), GFP_NOIO);
|
|
if (lba_to_pba == NULL || pba_to_lba == NULL) {
|
|
result = USB_STOR_TRANSPORT_ERROR;
|
|
goto error;
|
|
}
|
|
|
|
US_DEBUGP("alauda_read_map: Mapping blocks for zone %d\n", zone);
|
|
|
|
/* 1024 PBA's per zone */
|
|
for (i = 0; i < zonesize; i++)
|
|
lba_to_pba[i] = pba_to_lba[i] = UNDEF;
|
|
|
|
for (i = 0; i < zonesize; i++) {
|
|
blocknum = zone_base_pba + i;
|
|
|
|
result = alauda_get_redu_data(us, blocknum, data);
|
|
if (result != USB_STOR_XFER_GOOD) {
|
|
result = USB_STOR_TRANSPORT_ERROR;
|
|
goto error;
|
|
}
|
|
|
|
/* special PBAs have control field 0^16 */
|
|
for (j = 0; j < 16; j++)
|
|
if (data[j] != 0)
|
|
goto nonz;
|
|
pba_to_lba[i] = UNUSABLE;
|
|
US_DEBUGP("alauda_read_map: PBA %d has no logical mapping\n", blocknum);
|
|
continue;
|
|
|
|
nonz:
|
|
/* unwritten PBAs have control field FF^16 */
|
|
for (j = 0; j < 16; j++)
|
|
if (data[j] != 0xff)
|
|
goto nonff;
|
|
continue;
|
|
|
|
nonff:
|
|
/* normal PBAs start with six FFs */
|
|
if (j < 6) {
|
|
US_DEBUGP("alauda_read_map: PBA %d has no logical mapping: "
|
|
"reserved area = %02X%02X%02X%02X "
|
|
"data status %02X block status %02X\n",
|
|
blocknum, data[0], data[1], data[2], data[3],
|
|
data[4], data[5]);
|
|
pba_to_lba[i] = UNUSABLE;
|
|
continue;
|
|
}
|
|
|
|
if ((data[6] >> 4) != 0x01) {
|
|
US_DEBUGP("alauda_read_map: PBA %d has invalid address "
|
|
"field %02X%02X/%02X%02X\n",
|
|
blocknum, data[6], data[7], data[11], data[12]);
|
|
pba_to_lba[i] = UNUSABLE;
|
|
continue;
|
|
}
|
|
|
|
/* check even parity */
|
|
if (parity[data[6] ^ data[7]]) {
|
|
printk(KERN_WARNING
|
|
"alauda_read_map: Bad parity in LBA for block %d"
|
|
" (%02X %02X)\n", i, data[6], data[7]);
|
|
pba_to_lba[i] = UNUSABLE;
|
|
continue;
|
|
}
|
|
|
|
lba_offset = short_pack(data[7], data[6]);
|
|
lba_offset = (lba_offset & 0x07FF) >> 1;
|
|
lba_real = lba_offset + zone_base_lba;
|
|
|
|
/*
|
|
* Every 1024 physical blocks ("zone"), the LBA numbers
|
|
* go back to zero, but are within a higher block of LBA's.
|
|
* Also, there is a maximum of 1000 LBA's per zone.
|
|
* In other words, in PBA 1024-2047 you will find LBA 0-999
|
|
* which are really LBA 1000-1999. This allows for 24 bad
|
|
* or special physical blocks per zone.
|
|
*/
|
|
|
|
if (lba_offset >= uzonesize) {
|
|
printk(KERN_WARNING
|
|
"alauda_read_map: Bad low LBA %d for block %d\n",
|
|
lba_real, blocknum);
|
|
continue;
|
|
}
|
|
|
|
if (lba_to_pba[lba_offset] != UNDEF) {
|
|
printk(KERN_WARNING
|
|
"alauda_read_map: "
|
|
"LBA %d seen for PBA %d and %d\n",
|
|
lba_real, lba_to_pba[lba_offset], blocknum);
|
|
continue;
|
|
}
|
|
|
|
pba_to_lba[i] = lba_real;
|
|
lba_to_pba[lba_offset] = blocknum;
|
|
continue;
|
|
}
|
|
|
|
MEDIA_INFO(us).lba_to_pba[zone] = lba_to_pba;
|
|
MEDIA_INFO(us).pba_to_lba[zone] = pba_to_lba;
|
|
result = 0;
|
|
goto out;
|
|
|
|
error:
|
|
kfree(lba_to_pba);
|
|
kfree(pba_to_lba);
|
|
out:
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Checks to see whether we have already mapped a certain zone
|
|
* If we haven't, the map is generated
|
|
*/
|
|
static void alauda_ensure_map_for_zone(struct us_data *us, unsigned int zone)
|
|
{
|
|
if (MEDIA_INFO(us).lba_to_pba[zone] == NULL
|
|
|| MEDIA_INFO(us).pba_to_lba[zone] == NULL)
|
|
alauda_read_map(us, zone);
|
|
}
|
|
|
|
/*
|
|
* Erases an entire block
|
|
*/
|
|
static int alauda_erase_block(struct us_data *us, u16 pba)
|
|
{
|
|
int rc;
|
|
unsigned char command[] = {
|
|
ALAUDA_BULK_CMD, ALAUDA_BULK_ERASE_BLOCK, PBA_HI(pba),
|
|
PBA_ZONE(pba), 0, PBA_LO(pba), 0x02, 0, MEDIA_PORT(us)
|
|
};
|
|
unsigned char buf[2];
|
|
|
|
US_DEBUGP("alauda_erase_block: Erasing PBA %d\n", pba);
|
|
|
|
rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
|
|
command, 9, NULL);
|
|
if (rc != USB_STOR_XFER_GOOD)
|
|
return rc;
|
|
|
|
rc = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
|
|
buf, 2, NULL);
|
|
if (rc != USB_STOR_XFER_GOOD)
|
|
return rc;
|
|
|
|
US_DEBUGP("alauda_erase_block: Erase result: %02X %02X\n",
|
|
buf[0], buf[1]);
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* Reads data from a certain offset page inside a PBA, including interleaved
|
|
* redundancy data. Returns (pagesize+64)*pages bytes in data.
|
|
*/
|
|
static int alauda_read_block_raw(struct us_data *us, u16 pba,
|
|
unsigned int page, unsigned int pages, unsigned char *data)
|
|
{
|
|
int rc;
|
|
unsigned char command[] = {
|
|
ALAUDA_BULK_CMD, ALAUDA_BULK_READ_BLOCK, PBA_HI(pba),
|
|
PBA_ZONE(pba), 0, PBA_LO(pba) + page, pages, 0, MEDIA_PORT(us)
|
|
};
|
|
|
|
US_DEBUGP("alauda_read_block: pba %d page %d count %d\n",
|
|
pba, page, pages);
|
|
|
|
rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
|
|
command, 9, NULL);
|
|
if (rc != USB_STOR_XFER_GOOD)
|
|
return rc;
|
|
|
|
return usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
|
|
data, (MEDIA_INFO(us).pagesize + 64) * pages, NULL);
|
|
}
|
|
|
|
/*
|
|
* Reads data from a certain offset page inside a PBA, excluding redundancy
|
|
* data. Returns pagesize*pages bytes in data. Note that data must be big enough
|
|
* to hold (pagesize+64)*pages bytes of data, but you can ignore those 'extra'
|
|
* trailing bytes outside this function.
|
|
*/
|
|
static int alauda_read_block(struct us_data *us, u16 pba,
|
|
unsigned int page, unsigned int pages, unsigned char *data)
|
|
{
|
|
int i, rc;
|
|
unsigned int pagesize = MEDIA_INFO(us).pagesize;
|
|
|
|
rc = alauda_read_block_raw(us, pba, page, pages, data);
|
|
if (rc != USB_STOR_XFER_GOOD)
|
|
return rc;
|
|
|
|
/* Cut out the redundancy data */
|
|
for (i = 0; i < pages; i++) {
|
|
int dest_offset = i * pagesize;
|
|
int src_offset = i * (pagesize + 64);
|
|
memmove(data + dest_offset, data + src_offset, pagesize);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* Writes an entire block of data and checks status after write.
|
|
* Redundancy data must be already included in data. Data should be
|
|
* (pagesize+64)*blocksize bytes in length.
|
|
*/
|
|
static int alauda_write_block(struct us_data *us, u16 pba, unsigned char *data)
|
|
{
|
|
int rc;
|
|
struct alauda_info *info = (struct alauda_info *) us->extra;
|
|
unsigned char command[] = {
|
|
ALAUDA_BULK_CMD, ALAUDA_BULK_WRITE_BLOCK, PBA_HI(pba),
|
|
PBA_ZONE(pba), 0, PBA_LO(pba), 32, 0, MEDIA_PORT(us)
|
|
};
|
|
|
|
US_DEBUGP("alauda_write_block: pba %d\n", pba);
|
|
|
|
rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
|
|
command, 9, NULL);
|
|
if (rc != USB_STOR_XFER_GOOD)
|
|
return rc;
|
|
|
|
rc = usb_stor_bulk_transfer_buf(us, info->wr_ep, data,
|
|
(MEDIA_INFO(us).pagesize + 64) * MEDIA_INFO(us).blocksize,
|
|
NULL);
|
|
if (rc != USB_STOR_XFER_GOOD)
|
|
return rc;
|
|
|
|
return alauda_check_status2(us);
|
|
}
|
|
|
|
/*
|
|
* Write some data to a specific LBA.
|
|
*/
|
|
static int alauda_write_lba(struct us_data *us, u16 lba,
|
|
unsigned int page, unsigned int pages,
|
|
unsigned char *ptr, unsigned char *blockbuffer)
|
|
{
|
|
u16 pba, lbap, new_pba;
|
|
unsigned char *bptr, *cptr, *xptr;
|
|
unsigned char ecc[3];
|
|
int i, result;
|
|
unsigned int uzonesize = MEDIA_INFO(us).uzonesize;
|
|
unsigned int zonesize = MEDIA_INFO(us).zonesize;
|
|
unsigned int pagesize = MEDIA_INFO(us).pagesize;
|
|
unsigned int blocksize = MEDIA_INFO(us).blocksize;
|
|
unsigned int lba_offset = lba % uzonesize;
|
|
unsigned int new_pba_offset;
|
|
unsigned int zone = lba / uzonesize;
|
|
|
|
alauda_ensure_map_for_zone(us, zone);
|
|
|
|
pba = MEDIA_INFO(us).lba_to_pba[zone][lba_offset];
|
|
if (pba == 1) {
|
|
/* Maybe it is impossible to write to PBA 1.
|
|
Fake success, but don't do anything. */
|
|
printk(KERN_WARNING
|
|
"alauda_write_lba: avoid writing to pba 1\n");
|
|
return USB_STOR_TRANSPORT_GOOD;
|
|
}
|
|
|
|
new_pba = alauda_find_unused_pba(&MEDIA_INFO(us), zone);
|
|
if (!new_pba) {
|
|
printk(KERN_WARNING
|
|
"alauda_write_lba: Out of unused blocks\n");
|
|
return USB_STOR_TRANSPORT_ERROR;
|
|
}
|
|
|
|
/* read old contents */
|
|
if (pba != UNDEF) {
|
|
result = alauda_read_block_raw(us, pba, 0,
|
|
blocksize, blockbuffer);
|
|
if (result != USB_STOR_XFER_GOOD)
|
|
return result;
|
|
} else {
|
|
memset(blockbuffer, 0, blocksize * (pagesize + 64));
|
|
}
|
|
|
|
lbap = (lba_offset << 1) | 0x1000;
|
|
if (parity[MSB_of(lbap) ^ LSB_of(lbap)])
|
|
lbap ^= 1;
|
|
|
|
/* check old contents and fill lba */
|
|
for (i = 0; i < blocksize; i++) {
|
|
bptr = blockbuffer + (i * (pagesize + 64));
|
|
cptr = bptr + pagesize;
|
|
nand_compute_ecc(bptr, ecc);
|
|
if (!nand_compare_ecc(cptr+13, ecc)) {
|
|
US_DEBUGP("Warning: bad ecc in page %d- of pba %d\n",
|
|
i, pba);
|
|
nand_store_ecc(cptr+13, ecc);
|
|
}
|
|
nand_compute_ecc(bptr + (pagesize / 2), ecc);
|
|
if (!nand_compare_ecc(cptr+8, ecc)) {
|
|
US_DEBUGP("Warning: bad ecc in page %d+ of pba %d\n",
|
|
i, pba);
|
|
nand_store_ecc(cptr+8, ecc);
|
|
}
|
|
cptr[6] = cptr[11] = MSB_of(lbap);
|
|
cptr[7] = cptr[12] = LSB_of(lbap);
|
|
}
|
|
|
|
/* copy in new stuff and compute ECC */
|
|
xptr = ptr;
|
|
for (i = page; i < page+pages; i++) {
|
|
bptr = blockbuffer + (i * (pagesize + 64));
|
|
cptr = bptr + pagesize;
|
|
memcpy(bptr, xptr, pagesize);
|
|
xptr += pagesize;
|
|
nand_compute_ecc(bptr, ecc);
|
|
nand_store_ecc(cptr+13, ecc);
|
|
nand_compute_ecc(bptr + (pagesize / 2), ecc);
|
|
nand_store_ecc(cptr+8, ecc);
|
|
}
|
|
|
|
result = alauda_write_block(us, new_pba, blockbuffer);
|
|
if (result != USB_STOR_XFER_GOOD)
|
|
return result;
|
|
|
|
new_pba_offset = new_pba - (zone * zonesize);
|
|
MEDIA_INFO(us).pba_to_lba[zone][new_pba_offset] = lba;
|
|
MEDIA_INFO(us).lba_to_pba[zone][lba_offset] = new_pba;
|
|
US_DEBUGP("alauda_write_lba: Remapped LBA %d to PBA %d\n",
|
|
lba, new_pba);
|
|
|
|
if (pba != UNDEF) {
|
|
unsigned int pba_offset = pba - (zone * zonesize);
|
|
result = alauda_erase_block(us, pba);
|
|
if (result != USB_STOR_XFER_GOOD)
|
|
return result;
|
|
MEDIA_INFO(us).pba_to_lba[zone][pba_offset] = UNDEF;
|
|
}
|
|
|
|
return USB_STOR_TRANSPORT_GOOD;
|
|
}
|
|
|
|
/*
|
|
* Read data from a specific sector address
|
|
*/
|
|
static int alauda_read_data(struct us_data *us, unsigned long address,
|
|
unsigned int sectors)
|
|
{
|
|
unsigned char *buffer;
|
|
u16 lba, max_lba;
|
|
unsigned int page, len, offset;
|
|
unsigned int blockshift = MEDIA_INFO(us).blockshift;
|
|
unsigned int pageshift = MEDIA_INFO(us).pageshift;
|
|
unsigned int blocksize = MEDIA_INFO(us).blocksize;
|
|
unsigned int pagesize = MEDIA_INFO(us).pagesize;
|
|
unsigned int uzonesize = MEDIA_INFO(us).uzonesize;
|
|
struct scatterlist *sg;
|
|
int result;
|
|
|
|
/*
|
|
* Since we only read in one block at a time, we have to create
|
|
* a bounce buffer and move the data a piece at a time between the
|
|
* bounce buffer and the actual transfer buffer.
|
|
* We make this buffer big enough to hold temporary redundancy data,
|
|
* which we use when reading the data blocks.
|
|
*/
|
|
|
|
len = min(sectors, blocksize) * (pagesize + 64);
|
|
buffer = kmalloc(len, GFP_NOIO);
|
|
if (buffer == NULL) {
|
|
printk(KERN_WARNING "alauda_read_data: Out of memory\n");
|
|
return USB_STOR_TRANSPORT_ERROR;
|
|
}
|
|
|
|
/* Figure out the initial LBA and page */
|
|
lba = address >> blockshift;
|
|
page = (address & MEDIA_INFO(us).blockmask);
|
|
max_lba = MEDIA_INFO(us).capacity >> (blockshift + pageshift);
|
|
|
|
result = USB_STOR_TRANSPORT_GOOD;
|
|
offset = 0;
|
|
sg = NULL;
|
|
|
|
while (sectors > 0) {
|
|
unsigned int zone = lba / uzonesize; /* integer division */
|
|
unsigned int lba_offset = lba - (zone * uzonesize);
|
|
unsigned int pages;
|
|
u16 pba;
|
|
alauda_ensure_map_for_zone(us, zone);
|
|
|
|
/* Not overflowing capacity? */
|
|
if (lba >= max_lba) {
|
|
US_DEBUGP("Error: Requested lba %u exceeds "
|
|
"maximum %u\n", lba, max_lba);
|
|
result = USB_STOR_TRANSPORT_ERROR;
|
|
break;
|
|
}
|
|
|
|
/* Find number of pages we can read in this block */
|
|
pages = min(sectors, blocksize - page);
|
|
len = pages << pageshift;
|
|
|
|
/* Find where this lba lives on disk */
|
|
pba = MEDIA_INFO(us).lba_to_pba[zone][lba_offset];
|
|
|
|
if (pba == UNDEF) { /* this lba was never written */
|
|
US_DEBUGP("Read %d zero pages (LBA %d) page %d\n",
|
|
pages, lba, page);
|
|
|
|
/* This is not really an error. It just means
|
|
that the block has never been written.
|
|
Instead of returning USB_STOR_TRANSPORT_ERROR
|
|
it is better to return all zero data. */
|
|
|
|
memset(buffer, 0, len);
|
|
} else {
|
|
US_DEBUGP("Read %d pages, from PBA %d"
|
|
" (LBA %d) page %d\n",
|
|
pages, pba, lba, page);
|
|
|
|
result = alauda_read_block(us, pba, page, pages, buffer);
|
|
if (result != USB_STOR_TRANSPORT_GOOD)
|
|
break;
|
|
}
|
|
|
|
/* Store the data in the transfer buffer */
|
|
usb_stor_access_xfer_buf(buffer, len, us->srb,
|
|
&sg, &offset, TO_XFER_BUF);
|
|
|
|
page = 0;
|
|
lba++;
|
|
sectors -= pages;
|
|
}
|
|
|
|
kfree(buffer);
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Write data to a specific sector address
|
|
*/
|
|
static int alauda_write_data(struct us_data *us, unsigned long address,
|
|
unsigned int sectors)
|
|
{
|
|
unsigned char *buffer, *blockbuffer;
|
|
unsigned int page, len, offset;
|
|
unsigned int blockshift = MEDIA_INFO(us).blockshift;
|
|
unsigned int pageshift = MEDIA_INFO(us).pageshift;
|
|
unsigned int blocksize = MEDIA_INFO(us).blocksize;
|
|
unsigned int pagesize = MEDIA_INFO(us).pagesize;
|
|
struct scatterlist *sg;
|
|
u16 lba, max_lba;
|
|
int result;
|
|
|
|
/*
|
|
* Since we don't write the user data directly to the device,
|
|
* we have to create a bounce buffer and move the data a piece
|
|
* at a time between the bounce buffer and the actual transfer buffer.
|
|
*/
|
|
|
|
len = min(sectors, blocksize) * pagesize;
|
|
buffer = kmalloc(len, GFP_NOIO);
|
|
if (buffer == NULL) {
|
|
printk(KERN_WARNING "alauda_write_data: Out of memory\n");
|
|
return USB_STOR_TRANSPORT_ERROR;
|
|
}
|
|
|
|
/*
|
|
* We also need a temporary block buffer, where we read in the old data,
|
|
* overwrite parts with the new data, and manipulate the redundancy data
|
|
*/
|
|
blockbuffer = kmalloc((pagesize + 64) * blocksize, GFP_NOIO);
|
|
if (blockbuffer == NULL) {
|
|
printk(KERN_WARNING "alauda_write_data: Out of memory\n");
|
|
kfree(buffer);
|
|
return USB_STOR_TRANSPORT_ERROR;
|
|
}
|
|
|
|
/* Figure out the initial LBA and page */
|
|
lba = address >> blockshift;
|
|
page = (address & MEDIA_INFO(us).blockmask);
|
|
max_lba = MEDIA_INFO(us).capacity >> (pageshift + blockshift);
|
|
|
|
result = USB_STOR_TRANSPORT_GOOD;
|
|
offset = 0;
|
|
sg = NULL;
|
|
|
|
while (sectors > 0) {
|
|
/* Write as many sectors as possible in this block */
|
|
unsigned int pages = min(sectors, blocksize - page);
|
|
len = pages << pageshift;
|
|
|
|
/* Not overflowing capacity? */
|
|
if (lba >= max_lba) {
|
|
US_DEBUGP("alauda_write_data: Requested lba %u exceeds "
|
|
"maximum %u\n", lba, max_lba);
|
|
result = USB_STOR_TRANSPORT_ERROR;
|
|
break;
|
|
}
|
|
|
|
/* Get the data from the transfer buffer */
|
|
usb_stor_access_xfer_buf(buffer, len, us->srb,
|
|
&sg, &offset, FROM_XFER_BUF);
|
|
|
|
result = alauda_write_lba(us, lba, page, pages, buffer,
|
|
blockbuffer);
|
|
if (result != USB_STOR_TRANSPORT_GOOD)
|
|
break;
|
|
|
|
page = 0;
|
|
lba++;
|
|
sectors -= pages;
|
|
}
|
|
|
|
kfree(buffer);
|
|
kfree(blockbuffer);
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Our interface with the rest of the world
|
|
*/
|
|
|
|
static void alauda_info_destructor(void *extra)
|
|
{
|
|
struct alauda_info *info = (struct alauda_info *) extra;
|
|
int port;
|
|
|
|
if (!info)
|
|
return;
|
|
|
|
for (port = 0; port < 2; port++) {
|
|
struct alauda_media_info *media_info = &info->port[port];
|
|
|
|
alauda_free_maps(media_info);
|
|
kfree(media_info->lba_to_pba);
|
|
kfree(media_info->pba_to_lba);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Initialize alauda_info struct and find the data-write endpoint
|
|
*/
|
|
static int init_alauda(struct us_data *us)
|
|
{
|
|
struct alauda_info *info;
|
|
struct usb_host_interface *altsetting = us->pusb_intf->cur_altsetting;
|
|
nand_init_ecc();
|
|
|
|
us->extra = kzalloc(sizeof(struct alauda_info), GFP_NOIO);
|
|
if (!us->extra) {
|
|
US_DEBUGP("init_alauda: Gah! Can't allocate storage for"
|
|
"alauda info struct!\n");
|
|
return USB_STOR_TRANSPORT_ERROR;
|
|
}
|
|
info = (struct alauda_info *) us->extra;
|
|
us->extra_destructor = alauda_info_destructor;
|
|
|
|
info->wr_ep = usb_sndbulkpipe(us->pusb_dev,
|
|
altsetting->endpoint[0].desc.bEndpointAddress
|
|
& USB_ENDPOINT_NUMBER_MASK);
|
|
|
|
return USB_STOR_TRANSPORT_GOOD;
|
|
}
|
|
|
|
static int alauda_transport(struct scsi_cmnd *srb, struct us_data *us)
|
|
{
|
|
int rc;
|
|
struct alauda_info *info = (struct alauda_info *) us->extra;
|
|
unsigned char *ptr = us->iobuf;
|
|
static unsigned char inquiry_response[36] = {
|
|
0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00
|
|
};
|
|
|
|
if (srb->cmnd[0] == INQUIRY) {
|
|
US_DEBUGP("alauda_transport: INQUIRY. "
|
|
"Returning bogus response.\n");
|
|
memcpy(ptr, inquiry_response, sizeof(inquiry_response));
|
|
fill_inquiry_response(us, ptr, 36);
|
|
return USB_STOR_TRANSPORT_GOOD;
|
|
}
|
|
|
|
if (srb->cmnd[0] == TEST_UNIT_READY) {
|
|
US_DEBUGP("alauda_transport: TEST_UNIT_READY.\n");
|
|
return alauda_check_media(us);
|
|
}
|
|
|
|
if (srb->cmnd[0] == READ_CAPACITY) {
|
|
unsigned int num_zones;
|
|
unsigned long capacity;
|
|
|
|
rc = alauda_check_media(us);
|
|
if (rc != USB_STOR_TRANSPORT_GOOD)
|
|
return rc;
|
|
|
|
num_zones = MEDIA_INFO(us).capacity >> (MEDIA_INFO(us).zoneshift
|
|
+ MEDIA_INFO(us).blockshift + MEDIA_INFO(us).pageshift);
|
|
|
|
capacity = num_zones * MEDIA_INFO(us).uzonesize
|
|
* MEDIA_INFO(us).blocksize;
|
|
|
|
/* Report capacity and page size */
|
|
((__be32 *) ptr)[0] = cpu_to_be32(capacity - 1);
|
|
((__be32 *) ptr)[1] = cpu_to_be32(512);
|
|
|
|
usb_stor_set_xfer_buf(ptr, 8, srb);
|
|
return USB_STOR_TRANSPORT_GOOD;
|
|
}
|
|
|
|
if (srb->cmnd[0] == READ_10) {
|
|
unsigned int page, pages;
|
|
|
|
rc = alauda_check_media(us);
|
|
if (rc != USB_STOR_TRANSPORT_GOOD)
|
|
return rc;
|
|
|
|
page = short_pack(srb->cmnd[3], srb->cmnd[2]);
|
|
page <<= 16;
|
|
page |= short_pack(srb->cmnd[5], srb->cmnd[4]);
|
|
pages = short_pack(srb->cmnd[8], srb->cmnd[7]);
|
|
|
|
US_DEBUGP("alauda_transport: READ_10: page %d pagect %d\n",
|
|
page, pages);
|
|
|
|
return alauda_read_data(us, page, pages);
|
|
}
|
|
|
|
if (srb->cmnd[0] == WRITE_10) {
|
|
unsigned int page, pages;
|
|
|
|
rc = alauda_check_media(us);
|
|
if (rc != USB_STOR_TRANSPORT_GOOD)
|
|
return rc;
|
|
|
|
page = short_pack(srb->cmnd[3], srb->cmnd[2]);
|
|
page <<= 16;
|
|
page |= short_pack(srb->cmnd[5], srb->cmnd[4]);
|
|
pages = short_pack(srb->cmnd[8], srb->cmnd[7]);
|
|
|
|
US_DEBUGP("alauda_transport: WRITE_10: page %d pagect %d\n",
|
|
page, pages);
|
|
|
|
return alauda_write_data(us, page, pages);
|
|
}
|
|
|
|
if (srb->cmnd[0] == REQUEST_SENSE) {
|
|
US_DEBUGP("alauda_transport: REQUEST_SENSE.\n");
|
|
|
|
memset(ptr, 0, 18);
|
|
ptr[0] = 0xF0;
|
|
ptr[2] = info->sense_key;
|
|
ptr[7] = 11;
|
|
ptr[12] = info->sense_asc;
|
|
ptr[13] = info->sense_ascq;
|
|
usb_stor_set_xfer_buf(ptr, 18, srb);
|
|
|
|
return USB_STOR_TRANSPORT_GOOD;
|
|
}
|
|
|
|
if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) {
|
|
/* sure. whatever. not like we can stop the user from popping
|
|
the media out of the device (no locking doors, etc) */
|
|
return USB_STOR_TRANSPORT_GOOD;
|
|
}
|
|
|
|
US_DEBUGP("alauda_transport: Gah! Unknown command: %d (0x%x)\n",
|
|
srb->cmnd[0], srb->cmnd[0]);
|
|
info->sense_key = 0x05;
|
|
info->sense_asc = 0x20;
|
|
info->sense_ascq = 0x00;
|
|
return USB_STOR_TRANSPORT_FAILED;
|
|
}
|
|
|
|
static int alauda_probe(struct usb_interface *intf,
|
|
const struct usb_device_id *id)
|
|
{
|
|
struct us_data *us;
|
|
int result;
|
|
|
|
result = usb_stor_probe1(&us, intf, id,
|
|
(id - alauda_usb_ids) + alauda_unusual_dev_list);
|
|
if (result)
|
|
return result;
|
|
|
|
us->transport_name = "Alauda Control/Bulk";
|
|
us->transport = alauda_transport;
|
|
us->transport_reset = usb_stor_Bulk_reset;
|
|
us->max_lun = 1;
|
|
|
|
result = usb_stor_probe2(us);
|
|
return result;
|
|
}
|
|
|
|
static struct usb_driver alauda_driver = {
|
|
.name = "ums-alauda",
|
|
.probe = alauda_probe,
|
|
.disconnect = usb_stor_disconnect,
|
|
.suspend = usb_stor_suspend,
|
|
.resume = usb_stor_resume,
|
|
.reset_resume = usb_stor_reset_resume,
|
|
.pre_reset = usb_stor_pre_reset,
|
|
.post_reset = usb_stor_post_reset,
|
|
.id_table = alauda_usb_ids,
|
|
.soft_unbind = 1,
|
|
};
|
|
|
|
static int __init alauda_init(void)
|
|
{
|
|
return usb_register(&alauda_driver);
|
|
}
|
|
|
|
static void __exit alauda_exit(void)
|
|
{
|
|
usb_deregister(&alauda_driver);
|
|
}
|
|
|
|
module_init(alauda_init);
|
|
module_exit(alauda_exit);
|