2005-12-04 22:02:44 -08:00
/*
* 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 0213 9 , USA .
*/
# 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 "alauda.h"
# 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)
/*
* 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 ) {
2009-02-05 16:16:24 +01:00
printk ( KERN_WARNING
" alauda_init_media: Unrecognised media signature: "
2005-12-04 22:02:44 -08:00
" %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 2 nd 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 ] ] ) {
2009-02-05 16:16:24 +01:00
printk ( KERN_WARNING
" alauda_read_map: Bad parity in LBA for block %d "
2005-12-04 22:02:44 -08:00
" (%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 ) {
2009-02-05 16:16:24 +01:00
printk ( KERN_WARNING
" alauda_read_map: Bad low LBA %d for block %d \n " ,
2005-12-04 22:02:44 -08:00
lba_real , blocknum ) ;
continue ;
}
if ( lba_to_pba [ lba_offset ] ! = UNDEF ) {
2009-02-05 16:16:24 +01:00
printk ( KERN_WARNING
" alauda_read_map: "
" LBA %d seen for PBA %d and %d \n " ,
2005-12-04 22:02:44 -08:00
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 . */
2009-02-05 16:16:24 +01:00
printk ( KERN_WARNING
" alauda_write_lba: avoid writing to pba 1 \n " ) ;
2005-12-04 22:02:44 -08:00
return USB_STOR_TRANSPORT_GOOD ;
}
new_pba = alauda_find_unused_pba ( & MEDIA_INFO ( us ) , zone ) ;
if ( ! new_pba ) {
2009-02-05 16:16:24 +01:00
printk ( KERN_WARNING
" alauda_write_lba: Out of unused blocks \n " ) ;
2005-12-04 22:02:44 -08:00
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 ;
2007-05-11 12:33:09 +02:00
unsigned int page , len , offset ;
2005-12-04 22:02:44 -08:00
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 ;
2007-05-11 12:33:09 +02:00
struct scatterlist * sg ;
2005-12-04 22:02:44 -08:00
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 ) {
2009-02-05 16:16:24 +01:00
printk ( KERN_WARNING " alauda_read_data: Out of memory \n " ) ;
2005-12-04 22:02:44 -08:00
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 ;
2007-05-11 12:33:09 +02:00
offset = 0 ;
sg = NULL ;
2005-12-04 22:02:44 -08:00
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 ,
2007-05-11 12:33:09 +02:00
& sg , & offset , TO_XFER_BUF ) ;
2005-12-04 22:02:44 -08:00
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 ;
2007-05-11 12:33:09 +02:00
unsigned int page , len , offset ;
2005-12-04 22:02:44 -08:00
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 ;
2007-05-11 12:33:09 +02:00
struct scatterlist * sg ;
2005-12-04 22:02:44 -08:00
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 ) {
2009-02-05 16:16:24 +01:00
printk ( KERN_WARNING " alauda_write_data: Out of memory \n " ) ;
2005-12-04 22:02:44 -08:00
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 ) {
2009-02-05 16:16:24 +01:00
printk ( KERN_WARNING " alauda_write_data: Out of memory \n " ) ;
2005-12-04 22:02:44 -08:00
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 ;
2007-05-11 12:33:09 +02:00
offset = 0 ;
sg = NULL ;
2005-12-04 22:02:44 -08:00
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 ,
2007-05-11 12:33:09 +02:00
& sg , & offset , FROM_XFER_BUF ) ;
2005-12-04 22:02:44 -08:00
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
*/
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 ;
}
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 ;
}