/*
* 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"
#include "scsiglue.h"
#define DRV_NAME "ums-alauda"
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) }
static struct usb_device_id alauda_usb_ids[] = {
# include "unusual_alauda.h"
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, alauda_usb_ids);
#undef UNUSUAL_DEV
/*
* The flags table
*/
#define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \
vendor_name, product_name, use_protocol, use_transport, \
init_function, Flags) \
{ \
.vendorName = vendor_name, \
.productName = product_name, \
.useProtocol = use_protocol, \
.useTransport = use_transport, \
.initFunction = init_function, \
}