2008-02-09 10:20:54 -08:00
/*
* Sony MemoryStick Pro storage support
*
* Copyright ( C ) 2007 Alex Dubov < oakad @ yahoo . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* Special thanks to Carlos Corbacho for providing various MemoryStick cards
* that made this driver possible .
*
*/
# include <linux/blkdev.h>
# include <linux/idr.h>
# include <linux/hdreg.h>
# include <linux/kthread.h>
2008-03-10 11:43:42 -07:00
# include <linux/delay.h>
2008-02-09 10:20:54 -08:00
# include <linux/memstick.h>
# define DRIVER_NAME "mspro_block"
static int major ;
module_param ( major , int , 0644 ) ;
# define MSPRO_BLOCK_MAX_SEGS 32
# define MSPRO_BLOCK_MAX_PAGES ((2 << 16) - 1)
# define MSPRO_BLOCK_SIGNATURE 0xa5c3
# define MSPRO_BLOCK_MAX_ATTRIBUTES 41
2008-09-13 02:33:26 -07:00
# define MSPRO_BLOCK_PART_SHIFT 3
2008-02-09 10:20:54 -08:00
enum {
MSPRO_BLOCK_ID_SYSINFO = 0x10 ,
MSPRO_BLOCK_ID_MODELNAME = 0x15 ,
MSPRO_BLOCK_ID_MBR = 0x20 ,
MSPRO_BLOCK_ID_PBR16 = 0x21 ,
MSPRO_BLOCK_ID_PBR32 = 0x22 ,
MSPRO_BLOCK_ID_SPECFILEVALUES1 = 0x25 ,
MSPRO_BLOCK_ID_SPECFILEVALUES2 = 0x26 ,
MSPRO_BLOCK_ID_DEVINFO = 0x30
} ;
struct mspro_sys_attr {
size_t size ;
void * data ;
unsigned char id ;
char name [ 32 ] ;
struct device_attribute dev_attr ;
} ;
struct mspro_attr_entry {
unsigned int address ;
unsigned int size ;
unsigned char id ;
unsigned char reserved [ 3 ] ;
} __attribute__ ( ( packed ) ) ;
struct mspro_attribute {
unsigned short signature ;
unsigned short version ;
unsigned char count ;
unsigned char reserved [ 11 ] ;
struct mspro_attr_entry entries [ ] ;
} __attribute__ ( ( packed ) ) ;
struct mspro_sys_info {
unsigned char class ;
unsigned char reserved0 ;
unsigned short block_size ;
unsigned short block_count ;
unsigned short user_block_count ;
unsigned short page_size ;
unsigned char reserved1 [ 2 ] ;
unsigned char assembly_date [ 8 ] ;
unsigned int serial_number ;
unsigned char assembly_maker_code ;
unsigned char assembly_model_code [ 3 ] ;
unsigned short memory_maker_code ;
unsigned short memory_model_code ;
unsigned char reserved2 [ 4 ] ;
unsigned char vcc ;
unsigned char vpp ;
unsigned short controller_number ;
unsigned short controller_function ;
unsigned short start_sector ;
unsigned short unit_size ;
unsigned char ms_sub_class ;
unsigned char reserved3 [ 4 ] ;
unsigned char interface_type ;
unsigned short controller_code ;
unsigned char format_type ;
unsigned char reserved4 ;
unsigned char device_type ;
unsigned char reserved5 [ 7 ] ;
unsigned char mspro_id [ 16 ] ;
unsigned char reserved6 [ 16 ] ;
} __attribute__ ( ( packed ) ) ;
struct mspro_mbr {
unsigned char boot_partition ;
unsigned char start_head ;
unsigned char start_sector ;
unsigned char start_cylinder ;
unsigned char partition_type ;
unsigned char end_head ;
unsigned char end_sector ;
unsigned char end_cylinder ;
unsigned int start_sectors ;
unsigned int sectors_per_partition ;
} __attribute__ ( ( packed ) ) ;
2008-03-10 11:43:41 -07:00
struct mspro_specfile {
char name [ 8 ] ;
char ext [ 3 ] ;
unsigned char attr ;
unsigned char reserved [ 10 ] ;
unsigned short time ;
unsigned short date ;
unsigned short cluster ;
unsigned int size ;
} __attribute__ ( ( packed ) ) ;
2008-02-09 10:20:54 -08:00
struct mspro_devinfo {
unsigned short cylinders ;
unsigned short heads ;
unsigned short bytes_per_track ;
unsigned short bytes_per_sector ;
unsigned short sectors_per_track ;
unsigned char reserved [ 6 ] ;
} __attribute__ ( ( packed ) ) ;
struct mspro_block_data {
struct memstick_dev * card ;
unsigned int usage_count ;
2008-03-19 17:01:06 -07:00
unsigned int caps ;
2008-02-09 10:20:54 -08:00
struct gendisk * disk ;
struct request_queue * queue ;
2008-07-25 19:45:02 -07:00
struct request * block_req ;
2008-02-09 10:20:54 -08:00
spinlock_t q_lock ;
unsigned short page_size ;
unsigned short cylinders ;
unsigned short heads ;
unsigned short sectors_per_track ;
unsigned char system ;
unsigned char read_only : 1 ,
2008-07-25 19:45:02 -07:00
eject : 1 ,
2008-02-09 10:20:54 -08:00
has_request : 1 ,
2008-07-25 19:45:02 -07:00
data_dir : 1 ,
active : 1 ;
2008-02-09 10:20:54 -08:00
unsigned char transfer_cmd ;
int ( * mrq_handler ) ( struct memstick_dev * card ,
struct memstick_request * * mrq ) ;
struct attribute_group attr_group ;
struct scatterlist req_sg [ MSPRO_BLOCK_MAX_SEGS ] ;
unsigned int seg_count ;
unsigned int current_seg ;
2008-07-25 19:45:02 -07:00
unsigned int current_page ;
2008-02-09 10:20:54 -08:00
} ;
static DEFINE_IDR ( mspro_block_disk_idr ) ;
static DEFINE_MUTEX ( mspro_block_disk_lock ) ;
2008-07-25 19:45:02 -07:00
static int mspro_block_complete_req ( struct memstick_dev * card , int error ) ;
2008-02-09 10:20:54 -08:00
/*** Block device ***/
2008-03-02 10:31:38 -05:00
static int mspro_block_bd_open ( struct block_device * bdev , fmode_t mode )
2008-02-09 10:20:54 -08:00
{
2008-03-02 10:31:38 -05:00
struct gendisk * disk = bdev - > bd_disk ;
2008-02-09 10:20:54 -08:00
struct mspro_block_data * msb = disk - > private_data ;
int rc = - ENXIO ;
mutex_lock ( & mspro_block_disk_lock ) ;
if ( msb & & msb - > card ) {
msb - > usage_count + + ;
2008-03-02 10:31:38 -05:00
if ( ( mode & FMODE_WRITE ) & & msb - > read_only )
2008-02-09 10:20:54 -08:00
rc = - EROFS ;
else
rc = 0 ;
}
mutex_unlock ( & mspro_block_disk_lock ) ;
return rc ;
}
static int mspro_block_disk_release ( struct gendisk * disk )
{
struct mspro_block_data * msb = disk - > private_data ;
2008-09-03 09:01:48 +02:00
int disk_id = MINOR ( disk_devt ( disk ) ) > > MSPRO_BLOCK_PART_SHIFT ;
2008-02-09 10:20:54 -08:00
mutex_lock ( & mspro_block_disk_lock ) ;
2008-07-25 19:45:02 -07:00
if ( msb ) {
if ( msb - > usage_count )
msb - > usage_count - - ;
2008-02-09 10:20:54 -08:00
if ( ! msb - > usage_count ) {
kfree ( msb ) ;
disk - > private_data = NULL ;
idr_remove ( & mspro_block_disk_idr , disk_id ) ;
put_disk ( disk ) ;
}
}
mutex_unlock ( & mspro_block_disk_lock ) ;
return 0 ;
}
2008-03-02 10:31:38 -05:00
static int mspro_block_bd_release ( struct gendisk * disk , fmode_t mode )
2008-02-09 10:20:54 -08:00
{
return mspro_block_disk_release ( disk ) ;
}
static int mspro_block_bd_getgeo ( struct block_device * bdev ,
struct hd_geometry * geo )
{
struct mspro_block_data * msb = bdev - > bd_disk - > private_data ;
geo - > heads = msb - > heads ;
geo - > sectors = msb - > sectors_per_track ;
geo - > cylinders = msb - > cylinders ;
return 0 ;
}
static struct block_device_operations ms_block_bdops = {
2008-03-02 10:31:38 -05:00
. open = mspro_block_bd_open ,
. release = mspro_block_bd_release ,
2008-02-09 10:20:54 -08:00
. getgeo = mspro_block_bd_getgeo ,
. owner = THIS_MODULE
} ;
/*** Information ***/
static struct mspro_sys_attr * mspro_from_sysfs_attr ( struct attribute * attr )
{
struct device_attribute * dev_attr
= container_of ( attr , struct device_attribute , attr ) ;
return container_of ( dev_attr , struct mspro_sys_attr , dev_attr ) ;
}
static const char * mspro_block_attr_name ( unsigned char tag )
{
switch ( tag ) {
case MSPRO_BLOCK_ID_SYSINFO :
return " attr_sysinfo " ;
case MSPRO_BLOCK_ID_MODELNAME :
return " attr_modelname " ;
case MSPRO_BLOCK_ID_MBR :
return " attr_mbr " ;
case MSPRO_BLOCK_ID_PBR16 :
return " attr_pbr16 " ;
case MSPRO_BLOCK_ID_PBR32 :
return " attr_pbr32 " ;
case MSPRO_BLOCK_ID_SPECFILEVALUES1 :
return " attr_specfilevalues1 " ;
case MSPRO_BLOCK_ID_SPECFILEVALUES2 :
return " attr_specfilevalues2 " ;
case MSPRO_BLOCK_ID_DEVINFO :
return " attr_devinfo " ;
default :
return NULL ;
} ;
}
typedef ssize_t ( * sysfs_show_t ) ( struct device * dev ,
struct device_attribute * attr ,
char * buffer ) ;
static ssize_t mspro_block_attr_show_default ( struct device * dev ,
struct device_attribute * attr ,
char * buffer )
{
struct mspro_sys_attr * s_attr = container_of ( attr ,
struct mspro_sys_attr ,
dev_attr ) ;
ssize_t cnt , rc = 0 ;
for ( cnt = 0 ; cnt < s_attr - > size ; cnt + + ) {
if ( cnt & & ! ( cnt % 16 ) ) {
if ( PAGE_SIZE - rc )
buffer [ rc + + ] = ' \n ' ;
}
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " %02x " ,
( ( unsigned char * ) s_attr - > data ) [ cnt ] ) ;
}
return rc ;
}
static ssize_t mspro_block_attr_show_sysinfo ( struct device * dev ,
struct device_attribute * attr ,
char * buffer )
{
struct mspro_sys_attr * x_attr = container_of ( attr ,
struct mspro_sys_attr ,
dev_attr ) ;
struct mspro_sys_info * x_sys = x_attr - > data ;
ssize_t rc = 0 ;
2008-03-10 11:43:42 -07:00
int date_tz = 0 , date_tz_f = 0 ;
if ( x_sys - > assembly_date [ 0 ] > 0x80U ) {
date_tz = ( ~ x_sys - > assembly_date [ 0 ] ) + 1 ;
date_tz_f = date_tz & 3 ;
date_tz > > = 2 ;
date_tz = - date_tz ;
date_tz_f * = 15 ;
} else if ( x_sys - > assembly_date [ 0 ] < 0x80U ) {
date_tz = x_sys - > assembly_date [ 0 ] ;
date_tz_f = date_tz & 3 ;
date_tz > > = 2 ;
date_tz_f * = 15 ;
}
2008-02-09 10:20:54 -08:00
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " class: %x \n " ,
x_sys - > class ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " block size: %x \n " ,
be16_to_cpu ( x_sys - > block_size ) ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " block count: %x \n " ,
be16_to_cpu ( x_sys - > block_count ) ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " user block count: %x \n " ,
be16_to_cpu ( x_sys - > user_block_count ) ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " page size: %x \n " ,
be16_to_cpu ( x_sys - > page_size ) ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " assembly date: "
2008-03-10 11:43:42 -07:00
" GMT%+d:%d %04u-%02u-%02u %02u:%02u:%02u \n " ,
date_tz , date_tz_f ,
2008-02-09 10:20:54 -08:00
be16_to_cpu ( * ( unsigned short * )
& x_sys - > assembly_date [ 1 ] ) ,
x_sys - > assembly_date [ 3 ] , x_sys - > assembly_date [ 4 ] ,
x_sys - > assembly_date [ 5 ] , x_sys - > assembly_date [ 6 ] ,
x_sys - > assembly_date [ 7 ] ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " serial number: %x \n " ,
be32_to_cpu ( x_sys - > serial_number ) ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc ,
" assembly maker code: %x \n " ,
x_sys - > assembly_maker_code ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " assembly model code: "
" %02x%02x%02x \n " , x_sys - > assembly_model_code [ 0 ] ,
x_sys - > assembly_model_code [ 1 ] ,
x_sys - > assembly_model_code [ 2 ] ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " memory maker code: %x \n " ,
be16_to_cpu ( x_sys - > memory_maker_code ) ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " memory model code: %x \n " ,
be16_to_cpu ( x_sys - > memory_model_code ) ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " vcc: %x \n " ,
x_sys - > vcc ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " vpp: %x \n " ,
x_sys - > vpp ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " controller number: %x \n " ,
be16_to_cpu ( x_sys - > controller_number ) ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc ,
" controller function: %x \n " ,
be16_to_cpu ( x_sys - > controller_function ) ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " start sector: %x \n " ,
be16_to_cpu ( x_sys - > start_sector ) ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " unit size: %x \n " ,
be16_to_cpu ( x_sys - > unit_size ) ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " sub class: %x \n " ,
x_sys - > ms_sub_class ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " interface type: %x \n " ,
x_sys - > interface_type ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " controller code: %x \n " ,
be16_to_cpu ( x_sys - > controller_code ) ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " format type: %x \n " ,
x_sys - > format_type ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " device type: %x \n " ,
x_sys - > device_type ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " mspro id: %s \n " ,
x_sys - > mspro_id ) ;
return rc ;
}
static ssize_t mspro_block_attr_show_modelname ( struct device * dev ,
struct device_attribute * attr ,
char * buffer )
{
struct mspro_sys_attr * s_attr = container_of ( attr ,
struct mspro_sys_attr ,
dev_attr ) ;
return scnprintf ( buffer , PAGE_SIZE , " %s " , ( char * ) s_attr - > data ) ;
}
static ssize_t mspro_block_attr_show_mbr ( struct device * dev ,
struct device_attribute * attr ,
char * buffer )
{
struct mspro_sys_attr * x_attr = container_of ( attr ,
struct mspro_sys_attr ,
dev_attr ) ;
struct mspro_mbr * x_mbr = x_attr - > data ;
ssize_t rc = 0 ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " boot partition: %x \n " ,
x_mbr - > boot_partition ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " start head: %x \n " ,
x_mbr - > start_head ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " start sector: %x \n " ,
x_mbr - > start_sector ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " start cylinder: %x \n " ,
x_mbr - > start_cylinder ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " partition type: %x \n " ,
x_mbr - > partition_type ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " end head: %x \n " ,
x_mbr - > end_head ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " end sector: %x \n " ,
x_mbr - > end_sector ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " end cylinder: %x \n " ,
x_mbr - > end_cylinder ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " start sectors: %x \n " ,
x_mbr - > start_sectors ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc ,
" sectors per partition: %x \n " ,
x_mbr - > sectors_per_partition ) ;
return rc ;
}
2008-03-10 11:43:41 -07:00
static ssize_t mspro_block_attr_show_specfile ( struct device * dev ,
struct device_attribute * attr ,
char * buffer )
{
struct mspro_sys_attr * x_attr = container_of ( attr ,
struct mspro_sys_attr ,
dev_attr ) ;
struct mspro_specfile * x_spfile = x_attr - > data ;
char name [ 9 ] , ext [ 4 ] ;
ssize_t rc = 0 ;
memcpy ( name , x_spfile - > name , 8 ) ;
name [ 8 ] = 0 ;
memcpy ( ext , x_spfile - > ext , 3 ) ;
ext [ 3 ] = 0 ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " name: %s \n " , name ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " ext: %s \n " , ext ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " attribute: %x \n " ,
x_spfile - > attr ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " time: %d:%d:%d \n " ,
x_spfile - > time > > 11 ,
( x_spfile - > time > > 5 ) & 0x3f ,
( x_spfile - > time & 0x1f ) * 2 ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " date: %d-%d-%d \n " ,
( x_spfile - > date > > 9 ) + 1980 ,
( x_spfile - > date > > 5 ) & 0xf ,
x_spfile - > date & 0x1f ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " start cluster: %x \n " ,
x_spfile - > cluster ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " size: %x \n " ,
x_spfile - > size ) ;
return rc ;
}
2008-02-09 10:20:54 -08:00
static ssize_t mspro_block_attr_show_devinfo ( struct device * dev ,
struct device_attribute * attr ,
char * buffer )
{
struct mspro_sys_attr * x_attr = container_of ( attr ,
struct mspro_sys_attr ,
dev_attr ) ;
struct mspro_devinfo * x_devinfo = x_attr - > data ;
ssize_t rc = 0 ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " cylinders: %x \n " ,
be16_to_cpu ( x_devinfo - > cylinders ) ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " heads: %x \n " ,
be16_to_cpu ( x_devinfo - > heads ) ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " bytes per track: %x \n " ,
be16_to_cpu ( x_devinfo - > bytes_per_track ) ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " bytes per sector: %x \n " ,
be16_to_cpu ( x_devinfo - > bytes_per_sector ) ) ;
rc + = scnprintf ( buffer + rc , PAGE_SIZE - rc , " sectors per track: %x \n " ,
be16_to_cpu ( x_devinfo - > sectors_per_track ) ) ;
return rc ;
}
static sysfs_show_t mspro_block_attr_show ( unsigned char tag )
{
switch ( tag ) {
case MSPRO_BLOCK_ID_SYSINFO :
return mspro_block_attr_show_sysinfo ;
case MSPRO_BLOCK_ID_MODELNAME :
return mspro_block_attr_show_modelname ;
case MSPRO_BLOCK_ID_MBR :
return mspro_block_attr_show_mbr ;
2008-03-10 11:43:41 -07:00
case MSPRO_BLOCK_ID_SPECFILEVALUES1 :
case MSPRO_BLOCK_ID_SPECFILEVALUES2 :
return mspro_block_attr_show_specfile ;
2008-02-09 10:20:54 -08:00
case MSPRO_BLOCK_ID_DEVINFO :
return mspro_block_attr_show_devinfo ;
default :
return mspro_block_attr_show_default ;
}
}
/*** Protocol handlers ***/
/*
* Functions prefixed with " h_ " are protocol callbacks . They can be called from
* interrupt context . Return value of 0 means that request processing is still
* ongoing , while special error value of - EAGAIN means that current request is
* finished ( and request processor should come back some time later ) .
*/
static int h_mspro_block_req_init ( struct memstick_dev * card ,
struct memstick_request * * mrq )
{
struct mspro_block_data * msb = memstick_get_drvdata ( card ) ;
* mrq = & card - > current_mrq ;
card - > next_request = msb - > mrq_handler ;
return 0 ;
}
static int h_mspro_block_default ( struct memstick_dev * card ,
struct memstick_request * * mrq )
{
2008-07-25 19:45:02 -07:00
return mspro_block_complete_req ( card , ( * mrq ) - > error ) ;
}
static int h_mspro_block_default_bad ( struct memstick_dev * card ,
struct memstick_request * * mrq )
{
return - ENXIO ;
2008-02-09 10:20:54 -08:00
}
static int h_mspro_block_get_ro ( struct memstick_dev * card ,
struct memstick_request * * mrq )
{
struct mspro_block_data * msb = memstick_get_drvdata ( card ) ;
2008-07-25 19:45:02 -07:00
if ( ! ( * mrq ) - > error ) {
if ( ( * mrq ) - > data [ offsetof ( struct ms_status_register , status0 ) ]
& MEMSTICK_STATUS0_WP )
msb - > read_only = 1 ;
else
msb - > read_only = 0 ;
2008-02-09 10:20:54 -08:00
}
2008-07-25 19:45:02 -07:00
return mspro_block_complete_req ( card , ( * mrq ) - > error ) ;
2008-02-09 10:20:54 -08:00
}
static int h_mspro_block_wait_for_ced ( struct memstick_dev * card ,
struct memstick_request * * mrq )
{
dev_dbg ( & card - > dev , " wait for ced: value %x \n " , ( * mrq ) - > data [ 0 ] ) ;
2008-07-25 19:45:02 -07:00
if ( ! ( * mrq ) - > error ) {
if ( ( * mrq ) - > data [ 0 ] & ( MEMSTICK_INT_CMDNAK | MEMSTICK_INT_ERR ) )
( * mrq ) - > error = - EFAULT ;
else if ( ! ( ( * mrq ) - > data [ 0 ] & MEMSTICK_INT_CED ) )
return 0 ;
2008-02-09 10:20:54 -08:00
}
2008-07-25 19:45:02 -07:00
return mspro_block_complete_req ( card , ( * mrq ) - > error ) ;
2008-02-09 10:20:54 -08:00
}
static int h_mspro_block_transfer_data ( struct memstick_dev * card ,
struct memstick_request * * mrq )
{
struct mspro_block_data * msb = memstick_get_drvdata ( card ) ;
unsigned char t_val = 0 ;
struct scatterlist t_sg = { 0 } ;
size_t t_offset ;
2008-07-25 19:45:02 -07:00
if ( ( * mrq ) - > error )
return mspro_block_complete_req ( card , ( * mrq ) - > error ) ;
2008-02-09 10:20:54 -08:00
switch ( ( * mrq ) - > tpc ) {
case MS_TPC_WRITE_REG :
memstick_init_req ( * mrq , MS_TPC_SET_CMD , & msb - > transfer_cmd , 1 ) ;
2008-03-19 17:01:06 -07:00
( * mrq ) - > need_card_int = 1 ;
2008-02-09 10:20:54 -08:00
return 0 ;
case MS_TPC_SET_CMD :
t_val = ( * mrq ) - > int_reg ;
memstick_init_req ( * mrq , MS_TPC_GET_INT , NULL , 1 ) ;
2008-03-19 17:01:06 -07:00
if ( msb - > caps & MEMSTICK_CAP_AUTO_GET_INT )
2008-02-09 10:20:54 -08:00
goto has_int_reg ;
return 0 ;
case MS_TPC_GET_INT :
t_val = ( * mrq ) - > data [ 0 ] ;
has_int_reg :
if ( t_val & ( MEMSTICK_INT_CMDNAK | MEMSTICK_INT_ERR ) ) {
t_val = MSPRO_CMD_STOP ;
memstick_init_req ( * mrq , MS_TPC_SET_CMD , & t_val , 1 ) ;
card - > next_request = h_mspro_block_default ;
return 0 ;
}
if ( msb - > current_page
= = ( msb - > req_sg [ msb - > current_seg ] . length
/ msb - > page_size ) ) {
msb - > current_page = 0 ;
msb - > current_seg + + ;
if ( msb - > current_seg = = msb - > seg_count ) {
if ( t_val & MEMSTICK_INT_CED ) {
2008-07-25 19:45:02 -07:00
return mspro_block_complete_req ( card ,
0 ) ;
2008-02-09 10:20:54 -08:00
} else {
card - > next_request
= h_mspro_block_wait_for_ced ;
memstick_init_req ( * mrq , MS_TPC_GET_INT ,
NULL , 1 ) ;
return 0 ;
}
}
}
if ( ! ( t_val & MEMSTICK_INT_BREQ ) ) {
memstick_init_req ( * mrq , MS_TPC_GET_INT , NULL , 1 ) ;
return 0 ;
}
t_offset = msb - > req_sg [ msb - > current_seg ] . offset ;
t_offset + = msb - > current_page * msb - > page_size ;
sg_set_page ( & t_sg ,
nth_page ( sg_page ( & ( msb - > req_sg [ msb - > current_seg ] ) ) ,
t_offset > > PAGE_SHIFT ) ,
msb - > page_size , offset_in_page ( t_offset ) ) ;
memstick_init_req_sg ( * mrq , msb - > data_dir = = READ
? MS_TPC_READ_LONG_DATA
: MS_TPC_WRITE_LONG_DATA ,
& t_sg ) ;
2008-03-19 17:01:06 -07:00
( * mrq ) - > need_card_int = 1 ;
2008-02-09 10:20:54 -08:00
return 0 ;
case MS_TPC_READ_LONG_DATA :
case MS_TPC_WRITE_LONG_DATA :
msb - > current_page + + ;
2008-03-19 17:01:06 -07:00
if ( msb - > caps & MEMSTICK_CAP_AUTO_GET_INT ) {
2008-02-09 10:20:54 -08:00
t_val = ( * mrq ) - > int_reg ;
goto has_int_reg ;
} else {
memstick_init_req ( * mrq , MS_TPC_GET_INT , NULL , 1 ) ;
return 0 ;
}
default :
BUG ( ) ;
}
}
/*** Data transfer ***/
2008-07-25 19:45:02 -07:00
static int mspro_block_issue_req ( struct memstick_dev * card , int chunk )
2008-02-09 10:20:54 -08:00
{
struct mspro_block_data * msb = memstick_get_drvdata ( card ) ;
sector_t t_sec ;
2008-07-25 19:45:02 -07:00
unsigned int count ;
struct mspro_param_register param ;
2008-02-09 10:20:54 -08:00
2008-07-25 19:45:02 -07:00
try_again :
while ( chunk ) {
msb - > current_page = 0 ;
2008-02-09 10:20:54 -08:00
msb - > current_seg = 0 ;
2008-07-25 19:45:02 -07:00
msb - > seg_count = blk_rq_map_sg ( msb - > block_req - > q ,
msb - > block_req ,
msb - > req_sg ) ;
2008-02-09 10:20:54 -08:00
2008-07-25 19:45:02 -07:00
if ( ! msb - > seg_count ) {
chunk = __blk_end_request ( msb - > block_req , - ENOMEM ,
blk_rq_cur_bytes ( msb - > block_req ) ) ;
continue ;
}
2008-02-09 10:20:54 -08:00
2008-07-25 19:45:02 -07:00
t_sec = msb - > block_req - > sector < < 9 ;
sector_div ( t_sec , msb - > page_size ) ;
2008-02-09 10:20:54 -08:00
2008-07-25 19:45:02 -07:00
count = msb - > block_req - > nr_sectors < < 9 ;
count / = msb - > page_size ;
2008-02-09 10:20:54 -08:00
2008-07-25 19:45:02 -07:00
param . system = msb - > system ;
param . data_count = cpu_to_be16 ( count ) ;
param . data_address = cpu_to_be32 ( ( uint32_t ) t_sec ) ;
param . tpc_param = 0 ;
msb - > data_dir = rq_data_dir ( msb - > block_req ) ;
msb - > transfer_cmd = msb - > data_dir = = READ
? MSPRO_CMD_READ_DATA
: MSPRO_CMD_WRITE_DATA ;
dev_dbg ( & card - > dev , " data transfer: cmd %x, "
" lba %x, count %x \n " , msb - > transfer_cmd ,
be32_to_cpu ( param . data_address ) , count ) ;
card - > next_request = h_mspro_block_req_init ;
msb - > mrq_handler = h_mspro_block_transfer_data ;
memstick_init_req ( & card - > current_mrq , MS_TPC_WRITE_REG ,
& param , sizeof ( param ) ) ;
memstick_new_req ( card - > host ) ;
return 0 ;
}
dev_dbg ( & card - > dev , " elv_next \n " ) ;
msb - > block_req = elv_next_request ( msb - > queue ) ;
if ( ! msb - > block_req ) {
dev_dbg ( & card - > dev , " issue end \n " ) ;
return - EAGAIN ;
}
dev_dbg ( & card - > dev , " trying again \n " ) ;
chunk = 1 ;
goto try_again ;
2008-02-09 10:20:54 -08:00
}
2008-07-25 19:45:02 -07:00
static int mspro_block_complete_req ( struct memstick_dev * card , int error )
2008-02-09 10:20:54 -08:00
{
2008-07-25 19:45:02 -07:00
struct mspro_block_data * msb = memstick_get_drvdata ( card ) ;
int chunk , cnt ;
unsigned int t_len = 0 ;
2008-02-09 10:20:54 -08:00
unsigned long flags ;
spin_lock_irqsave ( & msb - > q_lock , flags ) ;
2008-07-25 19:45:02 -07:00
dev_dbg ( & card - > dev , " complete %d, %d \n " , msb - > has_request ? 1 : 0 ,
error ) ;
if ( msb - > has_request ) {
/* Nothing to do - not really an error */
if ( error = = - EAGAIN )
error = 0 ;
if ( error | | ( card - > current_mrq . tpc = = MSPRO_CMD_STOP ) ) {
if ( msb - > data_dir = = READ ) {
for ( cnt = 0 ; cnt < msb - > current_seg ; cnt + + )
t_len + = msb - > req_sg [ cnt ] . length
/ msb - > page_size ;
if ( msb - > current_page )
t_len + = msb - > current_page - 1 ;
t_len * = msb - > page_size ;
}
} else
t_len = msb - > block_req - > nr_sectors < < 9 ;
dev_dbg ( & card - > dev , " transferred %x (%d) \n " , t_len , error ) ;
if ( error & & ! t_len )
t_len = blk_rq_cur_bytes ( msb - > block_req ) ;
chunk = __blk_end_request ( msb - > block_req , error , t_len ) ;
error = mspro_block_issue_req ( card , chunk ) ;
if ( ! error )
goto out ;
else
msb - > has_request = 0 ;
} else {
if ( ! error )
error = - EAGAIN ;
}
card - > next_request = h_mspro_block_default_bad ;
complete_all ( & card - > mrq_complete ) ;
out :
2008-02-09 10:20:54 -08:00
spin_unlock_irqrestore ( & msb - > q_lock , flags ) ;
2008-07-25 19:45:02 -07:00
return error ;
2008-02-09 10:20:54 -08:00
}
2008-07-25 19:45:01 -07:00
static void mspro_block_stop ( struct memstick_dev * card )
{
struct mspro_block_data * msb = memstick_get_drvdata ( card ) ;
int rc = 0 ;
unsigned long flags ;
while ( 1 ) {
spin_lock_irqsave ( & msb - > q_lock , flags ) ;
if ( ! msb - > has_request ) {
blk_stop_queue ( msb - > queue ) ;
rc = 1 ;
}
spin_unlock_irqrestore ( & msb - > q_lock , flags ) ;
if ( rc )
break ;
wait_for_completion ( & card - > mrq_complete ) ;
}
}
static void mspro_block_start ( struct memstick_dev * card )
{
struct mspro_block_data * msb = memstick_get_drvdata ( card ) ;
unsigned long flags ;
spin_lock_irqsave ( & msb - > q_lock , flags ) ;
blk_start_queue ( msb - > queue ) ;
spin_unlock_irqrestore ( & msb - > q_lock , flags ) ;
}
2008-07-25 19:45:02 -07:00
static int mspro_block_prepare_req ( struct request_queue * q , struct request * req )
2008-02-09 10:20:54 -08:00
{
2008-07-25 19:45:02 -07:00
if ( ! blk_fs_request ( req ) & & ! blk_pc_request ( req ) ) {
blk_dump_rq_flags ( req , " MSPro unsupported request " ) ;
return BLKPREP_KILL ;
}
2008-02-09 10:20:54 -08:00
2008-07-25 19:45:02 -07:00
req - > cmd_flags | = REQ_DONTPREP ;
2008-02-09 10:20:54 -08:00
2008-07-25 19:45:02 -07:00
return BLKPREP_OK ;
2008-02-09 10:20:54 -08:00
}
2008-07-25 19:45:02 -07:00
static void mspro_block_submit_req ( struct request_queue * q )
2008-02-09 10:20:54 -08:00
{
struct memstick_dev * card = q - > queuedata ;
struct mspro_block_data * msb = memstick_get_drvdata ( card ) ;
struct request * req = NULL ;
2008-07-25 19:45:02 -07:00
if ( msb - > has_request )
return ;
if ( msb - > eject ) {
2008-02-09 10:20:54 -08:00
while ( ( req = elv_next_request ( q ) ) ! = NULL )
2008-10-01 10:12:15 -04:00
__blk_end_request ( req , - ENODEV , blk_rq_bytes ( req ) ) ;
2008-07-25 19:45:02 -07:00
return ;
2008-02-09 10:20:54 -08:00
}
2008-07-25 19:45:02 -07:00
msb - > has_request = 1 ;
if ( mspro_block_issue_req ( card , 0 ) )
msb - > has_request = 0 ;
2008-02-09 10:20:54 -08:00
}
/*** Initialization ***/
static int mspro_block_wait_for_ced ( struct memstick_dev * card )
{
struct mspro_block_data * msb = memstick_get_drvdata ( card ) ;
card - > next_request = h_mspro_block_req_init ;
msb - > mrq_handler = h_mspro_block_wait_for_ced ;
memstick_init_req ( & card - > current_mrq , MS_TPC_GET_INT , NULL , 1 ) ;
memstick_new_req ( card - > host ) ;
wait_for_completion ( & card - > mrq_complete ) ;
return card - > current_mrq . error ;
}
2008-03-19 17:01:07 -07:00
static int mspro_block_set_interface ( struct memstick_dev * card ,
unsigned char sys_reg )
2008-02-09 10:20:54 -08:00
{
struct memstick_host * host = card - > host ;
struct mspro_block_data * msb = memstick_get_drvdata ( card ) ;
struct mspro_param_register param = {
2008-03-19 17:01:07 -07:00
. system = sys_reg ,
2008-02-09 10:20:54 -08:00
. data_count = 0 ,
. data_address = 0 ,
2008-03-10 11:43:37 -07:00
. tpc_param = 0
2008-02-09 10:20:54 -08:00
} ;
card - > next_request = h_mspro_block_req_init ;
msb - > mrq_handler = h_mspro_block_default ;
memstick_init_req ( & card - > current_mrq , MS_TPC_WRITE_REG , & param ,
sizeof ( param ) ) ;
memstick_new_req ( host ) ;
wait_for_completion ( & card - > mrq_complete ) ;
2008-03-19 17:01:07 -07:00
return card - > current_mrq . error ;
}
static int mspro_block_switch_interface ( struct memstick_dev * card )
{
struct memstick_host * host = card - > host ;
struct mspro_block_data * msb = memstick_get_drvdata ( card ) ;
int rc = 0 ;
2008-09-13 02:33:26 -07:00
try_again :
2008-03-19 17:01:07 -07:00
if ( msb - > caps & MEMSTICK_CAP_PAR4 )
rc = mspro_block_set_interface ( card , MEMSTICK_SYS_PAR4 ) ;
else
return 0 ;
if ( rc ) {
printk ( KERN_WARNING
" %s: could not switch to 4-bit mode, error %d \n " ,
card - > dev . bus_id , rc ) ;
return 0 ;
}
2008-02-09 10:20:54 -08:00
2008-03-10 11:43:37 -07:00
msb - > system = MEMSTICK_SYS_PAR4 ;
host - > set_param ( host , MEMSTICK_INTERFACE , MEMSTICK_PAR4 ) ;
2008-03-19 17:01:07 -07:00
printk ( KERN_INFO " %s: switching to 4-bit parallel mode \n " ,
card - > dev . bus_id ) ;
if ( msb - > caps & MEMSTICK_CAP_PAR8 ) {
rc = mspro_block_set_interface ( card , MEMSTICK_SYS_PAR8 ) ;
if ( ! rc ) {
msb - > system = MEMSTICK_SYS_PAR8 ;
host - > set_param ( host , MEMSTICK_INTERFACE ,
MEMSTICK_PAR8 ) ;
printk ( KERN_INFO
" %s: switching to 8-bit parallel mode \n " ,
card - > dev . bus_id ) ;
} else
printk ( KERN_WARNING
" %s: could not switch to 8-bit mode, error %d \n " ,
card - > dev . bus_id , rc ) ;
}
2008-02-09 10:20:54 -08:00
card - > next_request = h_mspro_block_req_init ;
msb - > mrq_handler = h_mspro_block_default ;
memstick_init_req ( & card - > current_mrq , MS_TPC_GET_INT , NULL , 1 ) ;
memstick_new_req ( card - > host ) ;
wait_for_completion ( & card - > mrq_complete ) ;
2008-03-19 17:01:07 -07:00
rc = card - > current_mrq . error ;
2008-02-09 10:20:54 -08:00
2008-03-19 17:01:07 -07:00
if ( rc ) {
printk ( KERN_WARNING
" %s: interface error, trying to fall back to serial \n " ,
card - > dev . bus_id ) ;
2008-03-10 11:43:42 -07:00
msb - > system = MEMSTICK_SYS_SERIAL ;
host - > set_param ( host , MEMSTICK_POWER , MEMSTICK_POWER_OFF ) ;
2008-03-19 17:01:07 -07:00
msleep ( 10 ) ;
2008-03-10 11:43:42 -07:00
host - > set_param ( host , MEMSTICK_POWER , MEMSTICK_POWER_ON ) ;
2008-02-09 10:20:54 -08:00
host - > set_param ( host , MEMSTICK_INTERFACE , MEMSTICK_SERIAL ) ;
2008-03-10 11:43:42 -07:00
2008-03-19 17:01:07 -07:00
rc = memstick_set_rw_addr ( card ) ;
if ( ! rc )
rc = mspro_block_set_interface ( card , msb - > system ) ;
2008-09-13 02:33:26 -07:00
if ( ! rc ) {
msleep ( 150 ) ;
rc = mspro_block_wait_for_ced ( card ) ;
if ( rc )
return rc ;
if ( msb - > caps & MEMSTICK_CAP_PAR8 ) {
msb - > caps & = ~ MEMSTICK_CAP_PAR8 ;
goto try_again ;
}
}
2008-02-09 10:20:54 -08:00
}
2008-03-19 17:01:07 -07:00
return rc ;
2008-02-09 10:20:54 -08:00
}
/* Memory allocated for attributes by this function should be freed by
* mspro_block_data_clear , no matter if the initialization process succeded
* or failed .
*/
static int mspro_block_read_attributes ( struct memstick_dev * card )
{
struct mspro_block_data * msb = memstick_get_drvdata ( card ) ;
struct mspro_param_register param = {
. system = msb - > system ,
. data_count = cpu_to_be16 ( 1 ) ,
. data_address = 0 ,
2008-03-10 11:43:37 -07:00
. tpc_param = 0
2008-02-09 10:20:54 -08:00
} ;
struct mspro_attribute * attr = NULL ;
struct mspro_sys_attr * s_attr = NULL ;
unsigned char * buffer = NULL ;
int cnt , rc , attr_count ;
unsigned int addr ;
unsigned short page_count ;
attr = kmalloc ( msb - > page_size , GFP_KERNEL ) ;
if ( ! attr )
return - ENOMEM ;
sg_init_one ( & msb - > req_sg [ 0 ] , attr , msb - > page_size ) ;
msb - > seg_count = 1 ;
msb - > current_seg = 0 ;
msb - > current_page = 0 ;
msb - > data_dir = READ ;
msb - > transfer_cmd = MSPRO_CMD_READ_ATRB ;
card - > next_request = h_mspro_block_req_init ;
msb - > mrq_handler = h_mspro_block_transfer_data ;
memstick_init_req ( & card - > current_mrq , MS_TPC_WRITE_REG , & param ,
sizeof ( param ) ) ;
memstick_new_req ( card - > host ) ;
wait_for_completion ( & card - > mrq_complete ) ;
if ( card - > current_mrq . error ) {
rc = card - > current_mrq . error ;
goto out_free_attr ;
}
if ( be16_to_cpu ( attr - > signature ) ! = MSPRO_BLOCK_SIGNATURE ) {
printk ( KERN_ERR " %s: unrecognized device signature %x \n " ,
card - > dev . bus_id , be16_to_cpu ( attr - > signature ) ) ;
rc = - ENODEV ;
goto out_free_attr ;
}
if ( attr - > count > MSPRO_BLOCK_MAX_ATTRIBUTES ) {
printk ( KERN_WARNING " %s: way too many attribute entries \n " ,
card - > dev . bus_id ) ;
attr_count = MSPRO_BLOCK_MAX_ATTRIBUTES ;
} else
attr_count = attr - > count ;
msb - > attr_group . attrs = kzalloc ( ( attr_count + 1 )
* sizeof ( struct attribute ) ,
GFP_KERNEL ) ;
if ( ! msb - > attr_group . attrs ) {
rc = - ENOMEM ;
goto out_free_attr ;
}
msb - > attr_group . name = " media_attributes " ;
buffer = kmalloc ( msb - > page_size , GFP_KERNEL ) ;
if ( ! buffer ) {
rc = - ENOMEM ;
goto out_free_attr ;
}
memcpy ( buffer , ( char * ) attr , msb - > page_size ) ;
page_count = 1 ;
for ( cnt = 0 ; cnt < attr_count ; + + cnt ) {
s_attr = kzalloc ( sizeof ( struct mspro_sys_attr ) , GFP_KERNEL ) ;
if ( ! s_attr ) {
rc = - ENOMEM ;
goto out_free_buffer ;
}
msb - > attr_group . attrs [ cnt ] = & s_attr - > dev_attr . attr ;
addr = be32_to_cpu ( attr - > entries [ cnt ] . address ) ;
rc = be32_to_cpu ( attr - > entries [ cnt ] . size ) ;
dev_dbg ( & card - > dev , " adding attribute %d: id %x, address %x, "
" size %x \n " , cnt , attr - > entries [ cnt ] . id , addr , rc ) ;
s_attr - > id = attr - > entries [ cnt ] . id ;
if ( mspro_block_attr_name ( s_attr - > id ) )
snprintf ( s_attr - > name , sizeof ( s_attr - > name ) , " %s " ,
mspro_block_attr_name ( attr - > entries [ cnt ] . id ) ) ;
else
snprintf ( s_attr - > name , sizeof ( s_attr - > name ) ,
" attr_x%02x " , attr - > entries [ cnt ] . id ) ;
s_attr - > dev_attr . attr . name = s_attr - > name ;
s_attr - > dev_attr . attr . mode = S_IRUGO ;
s_attr - > dev_attr . show = mspro_block_attr_show ( s_attr - > id ) ;
if ( ! rc )
continue ;
s_attr - > size = rc ;
s_attr - > data = kmalloc ( rc , GFP_KERNEL ) ;
if ( ! s_attr - > data ) {
rc = - ENOMEM ;
goto out_free_buffer ;
}
if ( ( ( addr / msb - > page_size )
= = be32_to_cpu ( param . data_address ) )
& & ( ( ( addr + rc - 1 ) / msb - > page_size )
= = be32_to_cpu ( param . data_address ) ) ) {
memcpy ( s_attr - > data , buffer + addr % msb - > page_size ,
rc ) ;
continue ;
}
if ( page_count < = ( rc / msb - > page_size ) ) {
kfree ( buffer ) ;
page_count = ( rc / msb - > page_size ) + 1 ;
buffer = kmalloc ( page_count * msb - > page_size ,
GFP_KERNEL ) ;
if ( ! buffer ) {
rc = - ENOMEM ;
goto out_free_attr ;
}
}
param . system = msb - > system ;
param . data_count = cpu_to_be16 ( ( rc / msb - > page_size ) + 1 ) ;
param . data_address = cpu_to_be32 ( addr / msb - > page_size ) ;
2008-03-10 11:43:37 -07:00
param . tpc_param = 0 ;
2008-02-09 10:20:54 -08:00
sg_init_one ( & msb - > req_sg [ 0 ] , buffer ,
be16_to_cpu ( param . data_count ) * msb - > page_size ) ;
msb - > seg_count = 1 ;
msb - > current_seg = 0 ;
msb - > current_page = 0 ;
msb - > data_dir = READ ;
msb - > transfer_cmd = MSPRO_CMD_READ_ATRB ;
dev_dbg ( & card - > dev , " reading attribute pages %x, %x \n " ,
be32_to_cpu ( param . data_address ) ,
be16_to_cpu ( param . data_count ) ) ;
card - > next_request = h_mspro_block_req_init ;
msb - > mrq_handler = h_mspro_block_transfer_data ;
memstick_init_req ( & card - > current_mrq , MS_TPC_WRITE_REG ,
( char * ) & param , sizeof ( param ) ) ;
memstick_new_req ( card - > host ) ;
wait_for_completion ( & card - > mrq_complete ) ;
if ( card - > current_mrq . error ) {
rc = card - > current_mrq . error ;
goto out_free_buffer ;
}
memcpy ( s_attr - > data , buffer + addr % msb - > page_size , rc ) ;
}
rc = 0 ;
out_free_buffer :
kfree ( buffer ) ;
out_free_attr :
kfree ( attr ) ;
return rc ;
}
static int mspro_block_init_card ( struct memstick_dev * card )
{
struct mspro_block_data * msb = memstick_get_drvdata ( card ) ;
struct memstick_host * host = card - > host ;
int rc = 0 ;
2008-03-10 11:43:37 -07:00
msb - > system = MEMSTICK_SYS_SERIAL ;
2008-02-09 10:20:54 -08:00
card - > reg_addr . r_offset = offsetof ( struct mspro_register , status ) ;
card - > reg_addr . r_length = sizeof ( struct ms_status_register ) ;
card - > reg_addr . w_offset = offsetof ( struct mspro_register , param ) ;
card - > reg_addr . w_length = sizeof ( struct mspro_param_register ) ;
if ( memstick_set_rw_addr ( card ) )
return - EIO ;
2008-03-19 17:01:06 -07:00
msb - > caps = host - > caps ;
2008-09-13 02:33:26 -07:00
msleep ( 150 ) ;
rc = mspro_block_wait_for_ced ( card ) ;
2008-03-19 17:01:07 -07:00
if ( rc )
return rc ;
2008-02-09 10:20:54 -08:00
2008-09-13 02:33:26 -07:00
rc = mspro_block_switch_interface ( card ) ;
2008-02-09 10:20:54 -08:00
if ( rc )
return rc ;
2008-09-13 02:33:26 -07:00
2008-02-09 10:20:54 -08:00
dev_dbg ( & card - > dev , " card activated \n " ) ;
2008-03-19 17:01:06 -07:00
if ( msb - > system ! = MEMSTICK_SYS_SERIAL )
msb - > caps | = MEMSTICK_CAP_AUTO_GET_INT ;
2008-02-09 10:20:54 -08:00
card - > next_request = h_mspro_block_req_init ;
msb - > mrq_handler = h_mspro_block_get_ro ;
memstick_init_req ( & card - > current_mrq , MS_TPC_READ_REG , NULL ,
sizeof ( struct ms_status_register ) ) ;
memstick_new_req ( card - > host ) ;
wait_for_completion ( & card - > mrq_complete ) ;
if ( card - > current_mrq . error )
return card - > current_mrq . error ;
dev_dbg ( & card - > dev , " card r/w status %d \n " , msb - > read_only ? 0 : 1 ) ;
msb - > page_size = 512 ;
rc = mspro_block_read_attributes ( card ) ;
if ( rc )
return rc ;
dev_dbg ( & card - > dev , " attributes loaded \n " ) ;
return 0 ;
}
static int mspro_block_init_disk ( struct memstick_dev * card )
{
struct mspro_block_data * msb = memstick_get_drvdata ( card ) ;
struct memstick_host * host = card - > host ;
struct mspro_devinfo * dev_info = NULL ;
struct mspro_sys_info * sys_info = NULL ;
struct mspro_sys_attr * s_attr = NULL ;
int rc , disk_id ;
u64 limit = BLK_BOUNCE_HIGH ;
unsigned long capacity ;
2008-03-04 00:13:36 +01:00
if ( host - > dev . dma_mask & & * ( host - > dev . dma_mask ) )
limit = * ( host - > dev . dma_mask ) ;
2008-02-09 10:20:54 -08:00
for ( rc = 0 ; msb - > attr_group . attrs [ rc ] ; + + rc ) {
s_attr = mspro_from_sysfs_attr ( msb - > attr_group . attrs [ rc ] ) ;
if ( s_attr - > id = = MSPRO_BLOCK_ID_DEVINFO )
dev_info = s_attr - > data ;
else if ( s_attr - > id = = MSPRO_BLOCK_ID_SYSINFO )
sys_info = s_attr - > data ;
}
if ( ! dev_info | | ! sys_info )
return - ENODEV ;
msb - > cylinders = be16_to_cpu ( dev_info - > cylinders ) ;
msb - > heads = be16_to_cpu ( dev_info - > heads ) ;
msb - > sectors_per_track = be16_to_cpu ( dev_info - > sectors_per_track ) ;
msb - > page_size = be16_to_cpu ( sys_info - > unit_size ) ;
if ( ! idr_pre_get ( & mspro_block_disk_idr , GFP_KERNEL ) )
return - ENOMEM ;
mutex_lock ( & mspro_block_disk_lock ) ;
rc = idr_get_new ( & mspro_block_disk_idr , card , & disk_id ) ;
mutex_unlock ( & mspro_block_disk_lock ) ;
if ( rc )
return rc ;
2008-09-13 02:33:26 -07:00
if ( ( disk_id < < MSPRO_BLOCK_PART_SHIFT ) > 255 ) {
2008-02-09 10:20:54 -08:00
rc = - ENOSPC ;
goto out_release_id ;
}
2008-09-13 02:33:26 -07:00
msb - > disk = alloc_disk ( 1 < < MSPRO_BLOCK_PART_SHIFT ) ;
2008-02-09 10:20:54 -08:00
if ( ! msb - > disk ) {
rc = - ENOMEM ;
goto out_release_id ;
}
2008-07-25 19:45:02 -07:00
msb - > queue = blk_init_queue ( mspro_block_submit_req , & msb - > q_lock ) ;
2008-02-09 10:20:54 -08:00
if ( ! msb - > queue ) {
rc = - ENOMEM ;
goto out_put_disk ;
}
msb - > queue - > queuedata = card ;
2008-07-25 19:45:02 -07:00
blk_queue_prep_rq ( msb - > queue , mspro_block_prepare_req ) ;
2008-02-09 10:20:54 -08:00
blk_queue_bounce_limit ( msb - > queue , limit ) ;
blk_queue_max_sectors ( msb - > queue , MSPRO_BLOCK_MAX_PAGES ) ;
blk_queue_max_phys_segments ( msb - > queue , MSPRO_BLOCK_MAX_SEGS ) ;
blk_queue_max_hw_segments ( msb - > queue , MSPRO_BLOCK_MAX_SEGS ) ;
blk_queue_max_segment_size ( msb - > queue ,
MSPRO_BLOCK_MAX_PAGES * msb - > page_size ) ;
msb - > disk - > major = major ;
2008-09-13 02:33:26 -07:00
msb - > disk - > first_minor = disk_id < < MSPRO_BLOCK_PART_SHIFT ;
2008-02-09 10:20:54 -08:00
msb - > disk - > fops = & ms_block_bdops ;
msb - > usage_count = 1 ;
msb - > disk - > private_data = msb ;
msb - > disk - > queue = msb - > queue ;
msb - > disk - > driverfs_dev = & card - > dev ;
sprintf ( msb - > disk - > disk_name , " mspblk%d " , disk_id ) ;
blk_queue_hardsect_size ( msb - > queue , msb - > page_size ) ;
capacity = be16_to_cpu ( sys_info - > user_block_count ) ;
capacity * = be16_to_cpu ( sys_info - > block_size ) ;
capacity * = msb - > page_size > > 9 ;
set_capacity ( msb - > disk , capacity ) ;
dev_dbg ( & card - > dev , " capacity set %ld \n " , capacity ) ;
add_disk ( msb - > disk ) ;
msb - > active = 1 ;
return 0 ;
out_put_disk :
put_disk ( msb - > disk ) ;
out_release_id :
mutex_lock ( & mspro_block_disk_lock ) ;
idr_remove ( & mspro_block_disk_idr , disk_id ) ;
mutex_unlock ( & mspro_block_disk_lock ) ;
return rc ;
}
static void mspro_block_data_clear ( struct mspro_block_data * msb )
{
int cnt ;
struct mspro_sys_attr * s_attr ;
if ( msb - > attr_group . attrs ) {
for ( cnt = 0 ; msb - > attr_group . attrs [ cnt ] ; + + cnt ) {
s_attr = mspro_from_sysfs_attr ( msb - > attr_group
. attrs [ cnt ] ) ;
kfree ( s_attr - > data ) ;
kfree ( s_attr ) ;
}
kfree ( msb - > attr_group . attrs ) ;
}
msb - > card = NULL ;
}
static int mspro_block_check_card ( struct memstick_dev * card )
{
struct mspro_block_data * msb = memstick_get_drvdata ( card ) ;
return ( msb - > active = = 1 ) ;
}
static int mspro_block_probe ( struct memstick_dev * card )
{
struct mspro_block_data * msb ;
int rc = 0 ;
msb = kzalloc ( sizeof ( struct mspro_block_data ) , GFP_KERNEL ) ;
if ( ! msb )
return - ENOMEM ;
memstick_set_drvdata ( card , msb ) ;
msb - > card = card ;
2008-07-25 19:45:02 -07:00
spin_lock_init ( & msb - > q_lock ) ;
2008-02-09 10:20:54 -08:00
rc = mspro_block_init_card ( card ) ;
if ( rc )
goto out_free ;
rc = sysfs_create_group ( & card - > dev . kobj , & msb - > attr_group ) ;
if ( rc )
goto out_free ;
rc = mspro_block_init_disk ( card ) ;
if ( ! rc ) {
card - > check = mspro_block_check_card ;
2008-07-25 19:45:01 -07:00
card - > stop = mspro_block_stop ;
card - > start = mspro_block_start ;
2008-02-09 10:20:54 -08:00
return 0 ;
}
sysfs_remove_group ( & card - > dev . kobj , & msb - > attr_group ) ;
out_free :
memstick_set_drvdata ( card , NULL ) ;
mspro_block_data_clear ( msb ) ;
kfree ( msb ) ;
return rc ;
}
static void mspro_block_remove ( struct memstick_dev * card )
{
struct mspro_block_data * msb = memstick_get_drvdata ( card ) ;
unsigned long flags ;
del_gendisk ( msb - > disk ) ;
dev_dbg ( & card - > dev , " mspro block remove \n " ) ;
spin_lock_irqsave ( & msb - > q_lock , flags ) ;
2008-07-25 19:45:02 -07:00
msb - > eject = 1 ;
blk_start_queue ( msb - > queue ) ;
2008-02-09 10:20:54 -08:00
spin_unlock_irqrestore ( & msb - > q_lock , flags ) ;
blk_cleanup_queue ( msb - > queue ) ;
2008-07-25 19:45:02 -07:00
msb - > queue = NULL ;
2008-02-09 10:20:54 -08:00
sysfs_remove_group ( & card - > dev . kobj , & msb - > attr_group ) ;
mutex_lock ( & mspro_block_disk_lock ) ;
mspro_block_data_clear ( msb ) ;
mutex_unlock ( & mspro_block_disk_lock ) ;
mspro_block_disk_release ( msb - > disk ) ;
memstick_set_drvdata ( card , NULL ) ;
}
# ifdef CONFIG_PM
static int mspro_block_suspend ( struct memstick_dev * card , pm_message_t state )
{
struct mspro_block_data * msb = memstick_get_drvdata ( card ) ;
unsigned long flags ;
spin_lock_irqsave ( & msb - > q_lock , flags ) ;
blk_stop_queue ( msb - > queue ) ;
2008-07-25 19:45:02 -07:00
msb - > active = 0 ;
2008-02-09 10:20:54 -08:00
spin_unlock_irqrestore ( & msb - > q_lock , flags ) ;
return 0 ;
}
static int mspro_block_resume ( struct memstick_dev * card )
{
struct mspro_block_data * msb = memstick_get_drvdata ( card ) ;
unsigned long flags ;
int rc = 0 ;
# ifdef CONFIG_MEMSTICK_UNSAFE_RESUME
struct mspro_block_data * new_msb ;
struct memstick_host * host = card - > host ;
struct mspro_sys_attr * s_attr , * r_attr ;
unsigned char cnt ;
mutex_lock ( & host - > lock ) ;
new_msb = kzalloc ( sizeof ( struct mspro_block_data ) , GFP_KERNEL ) ;
if ( ! new_msb ) {
rc = - ENOMEM ;
goto out_unlock ;
}
new_msb - > card = card ;
memstick_set_drvdata ( card , new_msb ) ;
if ( mspro_block_init_card ( card ) )
goto out_free ;
for ( cnt = 0 ; new_msb - > attr_group . attrs [ cnt ]
& & msb - > attr_group . attrs [ cnt ] ; + + cnt ) {
s_attr = mspro_from_sysfs_attr ( new_msb - > attr_group . attrs [ cnt ] ) ;
r_attr = mspro_from_sysfs_attr ( msb - > attr_group . attrs [ cnt ] ) ;
if ( s_attr - > id = = MSPRO_BLOCK_ID_SYSINFO
& & r_attr - > id = = s_attr - > id ) {
if ( memcmp ( s_attr - > data , r_attr - > data , s_attr - > size ) )
break ;
2008-07-25 19:45:02 -07:00
msb - > active = 1 ;
2008-02-09 10:20:54 -08:00
break ;
}
}
out_free :
memstick_set_drvdata ( card , msb ) ;
mspro_block_data_clear ( new_msb ) ;
kfree ( new_msb ) ;
out_unlock :
mutex_unlock ( & host - > lock ) ;
# endif /* CONFIG_MEMSTICK_UNSAFE_RESUME */
spin_lock_irqsave ( & msb - > q_lock , flags ) ;
blk_start_queue ( msb - > queue ) ;
spin_unlock_irqrestore ( & msb - > q_lock , flags ) ;
return rc ;
}
# else
# define mspro_block_suspend NULL
# define mspro_block_resume NULL
# endif /* CONFIG_PM */
static struct memstick_device_id mspro_block_id_tbl [ ] = {
{ MEMSTICK_MATCH_ALL , MEMSTICK_TYPE_PRO , MEMSTICK_CATEGORY_STORAGE_DUO ,
2008-09-13 02:33:26 -07:00
MEMSTICK_CLASS_DUO } ,
2008-02-09 10:20:54 -08:00
{ }
} ;
static struct memstick_driver mspro_block_driver = {
. driver = {
. name = DRIVER_NAME ,
. owner = THIS_MODULE
} ,
. id_table = mspro_block_id_tbl ,
. probe = mspro_block_probe ,
. remove = mspro_block_remove ,
. suspend = mspro_block_suspend ,
. resume = mspro_block_resume
} ;
static int __init mspro_block_init ( void )
{
int rc = - ENOMEM ;
rc = register_blkdev ( major , DRIVER_NAME ) ;
if ( rc < 0 ) {
printk ( KERN_ERR DRIVER_NAME " : failed to register "
" major %d, error %d \n " , major , rc ) ;
return rc ;
}
if ( ! major )
major = rc ;
rc = memstick_register_driver ( & mspro_block_driver ) ;
if ( rc )
unregister_blkdev ( major , DRIVER_NAME ) ;
return rc ;
}
static void __exit mspro_block_exit ( void )
{
memstick_unregister_driver ( & mspro_block_driver ) ;
unregister_blkdev ( major , DRIVER_NAME ) ;
idr_destroy ( & mspro_block_disk_idr ) ;
}
module_init ( mspro_block_init ) ;
module_exit ( mspro_block_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Alex Dubov " ) ;
MODULE_DESCRIPTION ( " Sony MemoryStickPro block device driver " ) ;
MODULE_DEVICE_TABLE ( memstick , mspro_block_id_tbl ) ;