2016-04-18 13:09:11 +03:00
/*
* Driver for Realtek RTS51xx USB card reader
2011-01-14 11:53:34 +03:00
*
* Copyright ( c ) 2009 Realtek Semiconductor Corp . All rights reserved .
*
* 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 , see < http : //www.gnu.org/licenses/>.
*
* Author :
* wwang ( wei_wang @ realsil . com . cn )
* No . 450 , Shenhu Road , Suzhou Industry Park , Suzhou , China
*/
# include <linux/module.h>
# include <linux/blkdev.h>
# include <linux/kthread.h>
# include <linux/sched.h>
# include <linux/kernel.h>
# include <scsi/scsi.h>
# include <scsi/scsi_cmnd.h>
# include <scsi/scsi_device.h>
# include <linux/cdrom.h>
# include <linux/usb.h>
# include <linux/slab.h>
# include <linux/usb_usual.h>
# include "usb.h"
# include "transport.h"
# include "protocol.h"
# include "debug.h"
2015-05-06 12:24:21 +03:00
# include "scsiglue.h"
# define DRV_NAME "ums-realtek"
2011-01-14 11:53:34 +03:00
MODULE_DESCRIPTION ( " Driver for Realtek USB Card Reader " ) ;
MODULE_AUTHOR ( " wwang <wei_wang@realsil.com.cn> " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_VERSION ( " 1.03 " ) ;
static int auto_delink_en = 1 ;
module_param ( auto_delink_en , int , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( auto_delink_en , " enable auto delink " ) ;
2011-06-17 15:35:11 +04:00
# ifdef CONFIG_REALTEK_AUTOPM
static int ss_en = 1 ;
module_param ( ss_en , int , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( ss_en , " enable selective suspend " ) ;
static int ss_delay = 50 ;
module_param ( ss_delay , int , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( ss_delay ,
" seconds to delay before entering selective suspend " ) ;
enum RTS51X_STAT {
RTS51X_STAT_INIT ,
RTS51X_STAT_IDLE ,
RTS51X_STAT_RUN ,
RTS51X_STAT_SS
} ;
# define POLLING_INTERVAL 50
# define rts51x_set_stat(chip, stat) \
( ( chip ) - > state = ( enum RTS51X_STAT ) ( stat ) )
# define rts51x_get_stat(chip) ((chip)->state)
# define SET_LUN_READY(chip, lun) ((chip)->lun_ready |= ((u8)1 << (lun)))
# define CLR_LUN_READY(chip, lun) ((chip)->lun_ready &= ~((u8)1 << (lun)))
# define TST_LUN_READY(chip, lun) ((chip)->lun_ready & ((u8)1 << (lun)))
# endif
2011-01-14 11:53:34 +03:00
struct rts51x_status {
u16 vid ;
u16 pid ;
u8 cur_lun ;
u8 card_type ;
u8 total_lun ;
u16 fw_ver ;
u8 phy_exist ;
u8 multi_flag ;
u8 multi_card ;
u8 log_exist ;
union {
u8 detailed_type1 ;
u8 detailed_type2 ;
} detailed_type ;
u8 function [ 2 ] ;
} ;
struct rts51x_chip {
2011-06-17 15:35:09 +04:00
u16 vendor_id ;
u16 product_id ;
char max_lun ;
2011-01-14 11:53:34 +03:00
2011-06-17 15:35:09 +04:00
struct rts51x_status * status ;
int status_len ;
2011-01-14 11:53:34 +03:00
2011-06-17 15:35:11 +04:00
u32 flag ;
struct us_data * us ;
2013-04-27 06:49:13 +04:00
# ifdef CONFIG_REALTEK_AUTOPM
2011-06-17 15:35:11 +04:00
struct timer_list rts51x_suspend_timer ;
unsigned long timer_expires ;
int pwr_state ;
u8 lun_ready ;
enum RTS51X_STAT state ;
int support_auto_delink ;
# endif
2014-09-18 13:25:04 +04:00
/* used to back up the protocol chosen in probe1 phase */
2011-06-17 15:35:11 +04:00
proto_cmnd proto_handler_backup ;
2011-01-14 11:53:34 +03:00
} ;
/* flag definition */
# define FLIDX_AUTO_DELINK 0x01
# define SCSI_LUN(srb) ((srb)->device->lun)
/* Bit Operation */
# define SET_BIT(data, idx) ((data) |= 1 << (idx))
# define CLR_BIT(data, idx) ((data) &= ~(1 << (idx)))
# define CHK_BIT(data, idx) ((data) & (1 << (idx)))
# define SET_AUTO_DELINK(chip) ((chip)->flag |= FLIDX_AUTO_DELINK)
# define CLR_AUTO_DELINK(chip) ((chip)->flag &= ~FLIDX_AUTO_DELINK)
# define CHK_AUTO_DELINK(chip) ((chip)->flag & FLIDX_AUTO_DELINK)
# define RTS51X_GET_VID(chip) ((chip)->vendor_id)
# define RTS51X_GET_PID(chip) ((chip)->product_id)
2011-06-17 15:35:11 +04:00
# define VENDOR_ID(chip) ((chip)->status[0].vid)
# define PRODUCT_ID(chip) ((chip)->status[0].pid)
2011-01-14 11:53:34 +03:00
# define FW_VERSION(chip) ((chip)->status[0].fw_ver)
# define STATUS_LEN(chip) ((chip)->status_len)
2011-06-17 15:35:11 +04:00
# define STATUS_SUCCESS 0
# define STATUS_FAIL 1
2011-01-14 11:53:34 +03:00
/* Check card reader function */
# define SUPPORT_DETAILED_TYPE1(chip) \
CHK_BIT ( ( chip ) - > status [ 0 ] . function [ 0 ] , 1 )
# define SUPPORT_OT(chip) \
CHK_BIT ( ( chip ) - > status [ 0 ] . function [ 0 ] , 2 )
# define SUPPORT_OC(chip) \
CHK_BIT ( ( chip ) - > status [ 0 ] . function [ 0 ] , 3 )
# define SUPPORT_AUTO_DELINK(chip) \
CHK_BIT ( ( chip ) - > status [ 0 ] . function [ 0 ] , 4 )
# define SUPPORT_SDIO(chip) \
CHK_BIT ( ( chip ) - > status [ 0 ] . function [ 1 ] , 0 )
# define SUPPORT_DETAILED_TYPE2(chip) \
CHK_BIT ( ( chip ) - > status [ 0 ] . function [ 1 ] , 1 )
# define CHECK_PID(chip, pid) (RTS51X_GET_PID(chip) == (pid))
# define CHECK_FW_VER(chip, fw_ver) (FW_VERSION(chip) == (fw_ver))
# define CHECK_ID(chip, pid, fw_ver) \
( CHECK_PID ( ( chip ) , ( pid ) ) & & CHECK_FW_VER ( ( chip ) , ( fw_ver ) ) )
static int init_realtek_cr ( 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 ) , \
2012-08-29 00:37:13 +04:00
. driver_info = ( flags ) \
2011-01-14 11:53:34 +03:00
}
2011-02-15 12:02:47 +03:00
static const struct usb_device_id realtek_cr_ids [ ] = {
2011-01-14 11:53:34 +03:00
# include "unusual_realtek.h"
2011-06-17 15:35:09 +04:00
{ } /* Terminating entry */
2011-01-14 11:53:34 +03:00
} ;
2011-06-17 15:35:09 +04:00
2011-01-14 11:53:34 +03:00
MODULE_DEVICE_TABLE ( usb , realtek_cr_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 realtek_cr_unusual_dev_list [ ] = {
# include "unusual_realtek.h"
2011-06-17 15:35:09 +04:00
{ } /* Terminating entry */
2011-01-14 11:53:34 +03:00
} ;
# undef UNUSUAL_DEV
static int rts51x_bulk_transport ( struct us_data * us , u8 lun ,
u8 * cmd , int cmd_len , u8 * buf , int buf_len ,
enum dma_data_direction dir , int * act_len )
{
2011-06-17 15:35:09 +04:00
struct bulk_cb_wrap * bcb = ( struct bulk_cb_wrap * ) us - > iobuf ;
struct bulk_cs_wrap * bcs = ( struct bulk_cs_wrap * ) us - > iobuf ;
2011-01-14 11:53:34 +03:00
int result ;
unsigned int residue ;
unsigned int cswlen ;
unsigned int cbwlen = US_BULK_CB_WRAP_LEN ;
/* set up the command wrapper */
bcb - > Signature = cpu_to_le32 ( US_BULK_CB_SIGN ) ;
bcb - > DataTransferLength = cpu_to_le32 ( buf_len ) ;
2012-02-25 21:28:10 +04:00
bcb - > Flags = ( dir = = DMA_FROM_DEVICE ) ? US_BULK_FLAG_IN : 0 ;
2011-01-14 11:53:34 +03:00
bcb - > Tag = + + us - > tag ;
bcb - > Lun = lun ;
bcb - > Length = cmd_len ;
/* copy the command payload */
memset ( bcb - > CDB , 0 , sizeof ( bcb - > CDB ) ) ;
memcpy ( bcb - > CDB , cmd , bcb - > Length ) ;
/* send it to out endpoint */
result = usb_stor_bulk_transfer_buf ( us , us - > send_bulk_pipe ,
2011-06-17 15:35:09 +04:00
bcb , cbwlen , NULL ) ;
2011-01-14 11:53:34 +03:00
if ( result ! = USB_STOR_XFER_GOOD )
return USB_STOR_TRANSPORT_ERROR ;
/* DATA STAGE */
/* send/receive data payload, if there is any */
if ( buf & & buf_len ) {
unsigned int pipe = ( dir = = DMA_FROM_DEVICE ) ?
2011-06-17 15:35:09 +04:00
us - > recv_bulk_pipe : us - > send_bulk_pipe ;
2011-01-14 11:53:34 +03:00
result = usb_stor_bulk_transfer_buf ( us , pipe ,
2011-06-17 15:35:09 +04:00
buf , buf_len , NULL ) ;
2011-01-14 11:53:34 +03:00
if ( result = = USB_STOR_XFER_ERROR )
return USB_STOR_TRANSPORT_ERROR ;
}
/* get CSW for device status */
result = usb_stor_bulk_transfer_buf ( us , us - > recv_bulk_pipe ,
2011-06-17 15:35:09 +04:00
bcs , US_BULK_CS_WRAP_LEN , & cswlen ) ;
2011-01-14 11:53:34 +03:00
if ( result ! = USB_STOR_XFER_GOOD )
return USB_STOR_TRANSPORT_ERROR ;
/* check bulk status */
if ( bcs - > Signature ! = cpu_to_le32 ( US_BULK_CS_SIGN ) ) {
2013-04-19 22:44:00 +04:00
usb_stor_dbg ( us , " Signature mismatch: got %08X, expecting %08X \n " ,
le32_to_cpu ( bcs - > Signature ) , US_BULK_CS_SIGN ) ;
2011-01-14 11:53:34 +03:00
return USB_STOR_TRANSPORT_ERROR ;
}
residue = bcs - > Residue ;
if ( bcs - > Tag ! = us - > tag )
return USB_STOR_TRANSPORT_ERROR ;
2016-04-18 13:09:11 +03:00
/*
* try to compute the actual residue , based on how much data
* was really transferred and what the device tells us
*/
2011-01-14 11:53:34 +03:00
if ( residue )
residue = residue < buf_len ? residue : buf_len ;
if ( act_len )
* act_len = buf_len - residue ;
/* based on the status code, we report good or bad */
switch ( bcs - > Status ) {
case US_BULK_STAT_OK :
/* command good -- note that data could be short */
return USB_STOR_TRANSPORT_GOOD ;
case US_BULK_STAT_FAIL :
/* command failed */
return USB_STOR_TRANSPORT_FAILED ;
case US_BULK_STAT_PHASE :
2016-04-18 13:09:11 +03:00
/*
* phase error - - note that a transport reset will be
2011-06-17 15:35:09 +04:00
* invoked by the invoke_transport ( ) function
*/
2011-01-14 11:53:34 +03:00
return USB_STOR_TRANSPORT_ERROR ;
}
/* we should never get here, but if we do, we're in trouble */
return USB_STOR_TRANSPORT_ERROR ;
}
2011-09-16 12:53:37 +04:00
static int rts51x_bulk_transport_special ( struct us_data * us , u8 lun ,
u8 * cmd , int cmd_len , u8 * buf , int buf_len ,
enum dma_data_direction dir , int * act_len )
{
struct bulk_cb_wrap * bcb = ( struct bulk_cb_wrap * ) us - > iobuf ;
struct bulk_cs_wrap * bcs = ( struct bulk_cs_wrap * ) us - > iobuf ;
int result ;
unsigned int cswlen ;
unsigned int cbwlen = US_BULK_CB_WRAP_LEN ;
/* set up the command wrapper */
bcb - > Signature = cpu_to_le32 ( US_BULK_CB_SIGN ) ;
bcb - > DataTransferLength = cpu_to_le32 ( buf_len ) ;
2012-02-25 21:28:10 +04:00
bcb - > Flags = ( dir = = DMA_FROM_DEVICE ) ? US_BULK_FLAG_IN : 0 ;
2011-09-16 12:53:37 +04:00
bcb - > Tag = + + us - > tag ;
bcb - > Lun = lun ;
bcb - > Length = cmd_len ;
/* copy the command payload */
memset ( bcb - > CDB , 0 , sizeof ( bcb - > CDB ) ) ;
memcpy ( bcb - > CDB , cmd , bcb - > Length ) ;
/* send it to out endpoint */
result = usb_stor_bulk_transfer_buf ( us , us - > send_bulk_pipe ,
bcb , cbwlen , NULL ) ;
if ( result ! = USB_STOR_XFER_GOOD )
return USB_STOR_TRANSPORT_ERROR ;
/* DATA STAGE */
/* send/receive data payload, if there is any */
if ( buf & & buf_len ) {
unsigned int pipe = ( dir = = DMA_FROM_DEVICE ) ?
us - > recv_bulk_pipe : us - > send_bulk_pipe ;
result = usb_stor_bulk_transfer_buf ( us , pipe ,
buf , buf_len , NULL ) ;
if ( result = = USB_STOR_XFER_ERROR )
return USB_STOR_TRANSPORT_ERROR ;
}
/* get CSW for device status */
result = usb_bulk_msg ( us - > pusb_dev , us - > recv_bulk_pipe , bcs ,
US_BULK_CS_WRAP_LEN , & cswlen , 250 ) ;
return result ;
}
2011-01-14 11:53:34 +03:00
/* Determine what the maximum LUN supported is */
static int rts51x_get_max_lun ( struct us_data * us )
{
int result ;
/* issue the command */
us - > iobuf [ 0 ] = 0 ;
result = usb_stor_control_msg ( us , us - > recv_ctrl_pipe ,
2011-06-17 15:35:09 +04:00
US_BULK_GET_MAX_LUN ,
USB_DIR_IN | USB_TYPE_CLASS |
USB_RECIP_INTERFACE ,
0 , us - > ifnum , us - > iobuf , 1 , 10 * HZ ) ;
2011-01-14 11:53:34 +03:00
2013-04-19 22:44:00 +04:00
usb_stor_dbg ( us , " GetMaxLUN command result is %d, data is %d \n " ,
result , us - > iobuf [ 0 ] ) ;
2011-01-14 11:53:34 +03:00
/* if we have a successful request, return the result */
if ( result > 0 )
return us - > iobuf [ 0 ] ;
return 0 ;
}
static int rts51x_read_mem ( struct us_data * us , u16 addr , u8 * data , u16 len )
{
int retval ;
2011-06-17 15:35:09 +04:00
u8 cmnd [ 12 ] = { 0 } ;
2011-08-24 22:22:37 +04:00
u8 * buf ;
buf = kmalloc ( len , GFP_NOIO ) ;
if ( buf = = NULL )
return USB_STOR_TRANSPORT_ERROR ;
2011-01-14 11:53:34 +03:00
2013-04-19 22:44:00 +04:00
usb_stor_dbg ( us , " addr = 0x%x, len = %d \n " , addr , len ) ;
2011-01-14 11:53:34 +03:00
cmnd [ 0 ] = 0xF0 ;
cmnd [ 1 ] = 0x0D ;
2011-06-17 15:35:09 +04:00
cmnd [ 2 ] = ( u8 ) ( addr > > 8 ) ;
cmnd [ 3 ] = ( u8 ) addr ;
cmnd [ 4 ] = ( u8 ) ( len > > 8 ) ;
cmnd [ 5 ] = ( u8 ) len ;
2011-01-14 11:53:34 +03:00
retval = rts51x_bulk_transport ( us , 0 , cmnd , 12 ,
2011-08-24 22:22:37 +04:00
buf , len , DMA_FROM_DEVICE , NULL ) ;
if ( retval ! = USB_STOR_TRANSPORT_GOOD ) {
kfree ( buf ) ;
2011-01-14 11:53:34 +03:00
return - EIO ;
2011-08-24 22:22:37 +04:00
}
2011-01-14 11:53:34 +03:00
2011-08-24 22:22:37 +04:00
memcpy ( data , buf , len ) ;
kfree ( buf ) ;
2011-01-14 11:53:34 +03:00
return 0 ;
}
static int rts51x_write_mem ( struct us_data * us , u16 addr , u8 * data , u16 len )
{
int retval ;
2011-06-17 15:35:09 +04:00
u8 cmnd [ 12 ] = { 0 } ;
2011-08-24 22:22:37 +04:00
u8 * buf ;
2011-11-10 22:27:42 +04:00
buf = kmemdup ( data , len , GFP_NOIO ) ;
2011-08-24 22:22:37 +04:00
if ( buf = = NULL )
return USB_STOR_TRANSPORT_ERROR ;
2011-01-14 11:53:34 +03:00
2013-04-19 22:44:00 +04:00
usb_stor_dbg ( us , " addr = 0x%x, len = %d \n " , addr , len ) ;
2011-01-14 11:53:34 +03:00
cmnd [ 0 ] = 0xF0 ;
cmnd [ 1 ] = 0x0E ;
2011-06-17 15:35:09 +04:00
cmnd [ 2 ] = ( u8 ) ( addr > > 8 ) ;
cmnd [ 3 ] = ( u8 ) addr ;
cmnd [ 4 ] = ( u8 ) ( len > > 8 ) ;
cmnd [ 5 ] = ( u8 ) len ;
2011-01-14 11:53:34 +03:00
retval = rts51x_bulk_transport ( us , 0 , cmnd , 12 ,
2011-08-24 22:22:37 +04:00
buf , len , DMA_TO_DEVICE , NULL ) ;
kfree ( buf ) ;
2011-01-14 11:53:34 +03:00
if ( retval ! = USB_STOR_TRANSPORT_GOOD )
return - EIO ;
return 0 ;
}
static int rts51x_read_status ( struct us_data * us ,
u8 lun , u8 * status , int len , int * actlen )
{
int retval ;
2011-06-17 15:35:09 +04:00
u8 cmnd [ 12 ] = { 0 } ;
2011-08-24 22:22:37 +04:00
u8 * buf ;
buf = kmalloc ( len , GFP_NOIO ) ;
if ( buf = = NULL )
return USB_STOR_TRANSPORT_ERROR ;
2011-01-14 11:53:34 +03:00
2013-04-19 22:44:00 +04:00
usb_stor_dbg ( us , " lun = %d \n " , lun ) ;
2011-01-14 11:53:34 +03:00
cmnd [ 0 ] = 0xF0 ;
cmnd [ 1 ] = 0x09 ;
retval = rts51x_bulk_transport ( us , lun , cmnd , 12 ,
2011-08-24 22:22:37 +04:00
buf , len , DMA_FROM_DEVICE , actlen ) ;
if ( retval ! = USB_STOR_TRANSPORT_GOOD ) {
kfree ( buf ) ;
2011-01-14 11:53:34 +03:00
return - EIO ;
2011-08-24 22:22:37 +04:00
}
2011-01-14 11:53:34 +03:00
2011-08-24 22:22:37 +04:00
memcpy ( status , buf , len ) ;
kfree ( buf ) ;
2011-01-14 11:53:34 +03:00
return 0 ;
}
static int rts51x_check_status ( struct us_data * us , u8 lun )
{
struct rts51x_chip * chip = ( struct rts51x_chip * ) ( us - > extra ) ;
int retval ;
u8 buf [ 16 ] ;
retval = rts51x_read_status ( us , lun , buf , 16 , & ( chip - > status_len ) ) ;
2012-10-30 06:22:26 +04:00
if ( retval ! = STATUS_SUCCESS )
2011-01-14 11:53:34 +03:00
return - EIO ;
2013-04-19 22:44:00 +04:00
usb_stor_dbg ( us , " chip->status_len = %d \n " , chip - > status_len ) ;
2011-01-14 11:53:34 +03:00
2011-06-17 15:35:09 +04:00
chip - > status [ lun ] . vid = ( ( u16 ) buf [ 0 ] < < 8 ) | buf [ 1 ] ;
chip - > status [ lun ] . pid = ( ( u16 ) buf [ 2 ] < < 8 ) | buf [ 3 ] ;
2011-01-14 11:53:34 +03:00
chip - > status [ lun ] . cur_lun = buf [ 4 ] ;
chip - > status [ lun ] . card_type = buf [ 5 ] ;
chip - > status [ lun ] . total_lun = buf [ 6 ] ;
2011-06-17 15:35:09 +04:00
chip - > status [ lun ] . fw_ver = ( ( u16 ) buf [ 7 ] < < 8 ) | buf [ 8 ] ;
2011-01-14 11:53:34 +03:00
chip - > status [ lun ] . phy_exist = buf [ 9 ] ;
chip - > status [ lun ] . multi_flag = buf [ 10 ] ;
chip - > status [ lun ] . multi_card = buf [ 11 ] ;
chip - > status [ lun ] . log_exist = buf [ 12 ] ;
if ( chip - > status_len = = 16 ) {
chip - > status [ lun ] . detailed_type . detailed_type1 = buf [ 13 ] ;
chip - > status [ lun ] . function [ 0 ] = buf [ 14 ] ;
chip - > status [ lun ] . function [ 1 ] = buf [ 15 ] ;
}
return 0 ;
}
static int enable_oscillator ( struct us_data * us )
{
int retval ;
u8 value ;
retval = rts51x_read_mem ( us , 0xFE77 , & value , 1 ) ;
if ( retval < 0 )
return - EIO ;
value | = 0x04 ;
retval = rts51x_write_mem ( us , 0xFE77 , & value , 1 ) ;
if ( retval < 0 )
return - EIO ;
retval = rts51x_read_mem ( us , 0xFE77 , & value , 1 ) ;
if ( retval < 0 )
return - EIO ;
if ( ! ( value & 0x04 ) )
return - EIO ;
return 0 ;
}
2011-09-16 12:53:37 +04:00
static int __do_config_autodelink ( struct us_data * us , u8 * data , u16 len )
{
int retval ;
u8 cmnd [ 12 ] = { 0 } ;
2012-02-21 00:34:34 +04:00
u8 * buf ;
2011-09-16 12:53:37 +04:00
2013-04-19 22:44:00 +04:00
usb_stor_dbg ( us , " addr = 0xfe47, len = %d \n " , len ) ;
2011-09-16 12:53:37 +04:00
2012-02-21 00:34:34 +04:00
buf = kmemdup ( data , len , GFP_NOIO ) ;
if ( ! buf )
return USB_STOR_TRANSPORT_ERROR ;
2011-09-16 12:53:37 +04:00
cmnd [ 0 ] = 0xF0 ;
cmnd [ 1 ] = 0x0E ;
2011-11-15 11:53:37 +04:00
cmnd [ 2 ] = 0xfe ;
cmnd [ 3 ] = 0x47 ;
2011-09-16 12:53:37 +04:00
cmnd [ 4 ] = ( u8 ) ( len > > 8 ) ;
cmnd [ 5 ] = ( u8 ) len ;
2012-02-21 00:34:34 +04:00
retval = rts51x_bulk_transport_special ( us , 0 , cmnd , 12 , buf , len , DMA_TO_DEVICE , NULL ) ;
kfree ( buf ) ;
2011-09-16 12:53:37 +04:00
if ( retval ! = USB_STOR_TRANSPORT_GOOD ) {
return - EIO ;
}
return 0 ;
}
2011-01-14 11:53:34 +03:00
static int do_config_autodelink ( struct us_data * us , int enable , int force )
{
int retval ;
u8 value ;
retval = rts51x_read_mem ( us , 0xFE47 , & value , 1 ) ;
if ( retval < 0 )
return - EIO ;
if ( enable ) {
if ( force )
value | = 0x03 ;
else
value | = 0x01 ;
} else {
value & = ~ 0x03 ;
}
2013-04-19 22:44:00 +04:00
usb_stor_dbg ( us , " set 0xfe47 to 0x%x \n " , value ) ;
2011-01-14 11:53:34 +03:00
2011-09-16 12:53:37 +04:00
/* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */
retval = __do_config_autodelink ( us , & value , 1 ) ;
2011-01-14 11:53:34 +03:00
if ( retval < 0 )
return - EIO ;
return 0 ;
}
static int config_autodelink_after_power_on ( struct us_data * us )
{
struct rts51x_chip * chip = ( struct rts51x_chip * ) ( us - > extra ) ;
int retval ;
u8 value ;
if ( ! CHK_AUTO_DELINK ( chip ) )
return 0 ;
retval = rts51x_read_mem ( us , 0xFE47 , & value , 1 ) ;
if ( retval < 0 )
return - EIO ;
if ( auto_delink_en ) {
CLR_BIT ( value , 0 ) ;
CLR_BIT ( value , 1 ) ;
SET_BIT ( value , 2 ) ;
if ( CHECK_ID ( chip , 0x0138 , 0x3882 ) )
CLR_BIT ( value , 2 ) ;
SET_BIT ( value , 7 ) ;
2011-09-16 12:53:37 +04:00
/* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */
retval = __do_config_autodelink ( us , & value , 1 ) ;
2011-01-14 11:53:34 +03:00
if ( retval < 0 )
return - EIO ;
retval = enable_oscillator ( us ) ;
if ( retval = = 0 )
( void ) do_config_autodelink ( us , 1 , 0 ) ;
} else {
/* Autodelink controlled by firmware */
SET_BIT ( value , 2 ) ;
if ( CHECK_ID ( chip , 0x0138 , 0x3882 ) )
CLR_BIT ( value , 2 ) ;
if ( CHECK_ID ( chip , 0x0159 , 0x5889 ) | |
2011-06-17 15:35:09 +04:00
CHECK_ID ( chip , 0x0138 , 0x3880 ) ) {
2011-01-14 11:53:34 +03:00
CLR_BIT ( value , 0 ) ;
CLR_BIT ( value , 7 ) ;
}
2011-09-16 12:53:37 +04:00
/* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */
retval = __do_config_autodelink ( us , & value , 1 ) ;
2011-01-14 11:53:34 +03:00
if ( retval < 0 )
return - EIO ;
if ( CHECK_ID ( chip , 0x0159 , 0x5888 ) ) {
value = 0xFF ;
retval = rts51x_write_mem ( us , 0xFE79 , & value , 1 ) ;
if ( retval < 0 )
return - EIO ;
value = 0x01 ;
retval = rts51x_write_mem ( us , 0x48 , & value , 1 ) ;
if ( retval < 0 )
return - EIO ;
}
}
return 0 ;
}
2014-10-02 03:10:57 +04:00
# ifdef CONFIG_PM
2011-01-14 11:53:34 +03:00
static int config_autodelink_before_power_down ( struct us_data * us )
{
struct rts51x_chip * chip = ( struct rts51x_chip * ) ( us - > extra ) ;
int retval ;
u8 value ;
if ( ! CHK_AUTO_DELINK ( chip ) )
return 0 ;
if ( auto_delink_en ) {
retval = rts51x_read_mem ( us , 0xFE77 , & value , 1 ) ;
if ( retval < 0 )
return - EIO ;
SET_BIT ( value , 2 ) ;
retval = rts51x_write_mem ( us , 0xFE77 , & value , 1 ) ;
if ( retval < 0 )
return - EIO ;
if ( CHECK_ID ( chip , 0x0159 , 0x5888 ) ) {
value = 0x01 ;
retval = rts51x_write_mem ( us , 0x48 , & value , 1 ) ;
if ( retval < 0 )
return - EIO ;
}
retval = rts51x_read_mem ( us , 0xFE47 , & value , 1 ) ;
if ( retval < 0 )
return - EIO ;
SET_BIT ( value , 0 ) ;
if ( CHECK_ID ( chip , 0x0138 , 0x3882 ) )
SET_BIT ( value , 2 ) ;
retval = rts51x_write_mem ( us , 0xFE77 , & value , 1 ) ;
if ( retval < 0 )
return - EIO ;
} else {
if ( CHECK_ID ( chip , 0x0159 , 0x5889 ) | |
2011-06-17 15:35:09 +04:00
CHECK_ID ( chip , 0x0138 , 0x3880 ) | |
CHECK_ID ( chip , 0x0138 , 0x3882 ) ) {
2011-01-14 11:53:34 +03:00
retval = rts51x_read_mem ( us , 0xFE47 , & value , 1 ) ;
if ( retval < 0 )
return - EIO ;
if ( CHECK_ID ( chip , 0x0159 , 0x5889 ) | |
2011-06-17 15:35:09 +04:00
CHECK_ID ( chip , 0x0138 , 0x3880 ) ) {
2011-01-14 11:53:34 +03:00
SET_BIT ( value , 0 ) ;
SET_BIT ( value , 7 ) ;
}
if ( CHECK_ID ( chip , 0x0138 , 0x3882 ) )
SET_BIT ( value , 2 ) ;
2011-09-16 12:53:37 +04:00
/* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */
retval = __do_config_autodelink ( us , & value , 1 ) ;
2011-01-14 11:53:34 +03:00
if ( retval < 0 )
return - EIO ;
}
if ( CHECK_ID ( chip , 0x0159 , 0x5888 ) ) {
value = 0x01 ;
retval = rts51x_write_mem ( us , 0x48 , & value , 1 ) ;
if ( retval < 0 )
return - EIO ;
}
}
2011-06-17 15:35:11 +04:00
return 0 ;
}
static void fw5895_init ( struct us_data * us )
{
struct rts51x_chip * chip = ( struct rts51x_chip * ) ( us - > extra ) ;
int retval ;
u8 val ;
if ( ( PRODUCT_ID ( chip ) ! = 0x0158 ) | | ( FW_VERSION ( chip ) ! = 0x5895 ) ) {
2013-04-19 22:44:00 +04:00
usb_stor_dbg ( us , " Not the specified device, return immediately! \n " ) ;
2011-06-17 15:35:11 +04:00
} else {
retval = rts51x_read_mem ( us , 0xFD6F , & val , 1 ) ;
if ( retval = = STATUS_SUCCESS & & ( val & 0x1F ) = = 0 ) {
val = 0x1F ;
retval = rts51x_write_mem ( us , 0xFD70 , & val , 1 ) ;
if ( retval ! = STATUS_SUCCESS )
2013-04-19 22:44:00 +04:00
usb_stor_dbg ( us , " Write memory fail \n " ) ;
2011-06-17 15:35:11 +04:00
} else {
2013-04-19 22:44:00 +04:00
usb_stor_dbg ( us , " Read memory fail, OR (val & 0x1F) != 0 \n " ) ;
2011-06-17 15:35:11 +04:00
}
}
}
2014-10-02 03:10:57 +04:00
# endif
2011-06-17 15:35:11 +04:00
# ifdef CONFIG_REALTEK_AUTOPM
static void fw5895_set_mmc_wp ( struct us_data * us )
{
struct rts51x_chip * chip = ( struct rts51x_chip * ) ( us - > extra ) ;
int retval ;
u8 buf [ 13 ] ;
if ( ( PRODUCT_ID ( chip ) ! = 0x0158 ) | | ( FW_VERSION ( chip ) ! = 0x5895 ) ) {
2013-04-19 22:44:00 +04:00
usb_stor_dbg ( us , " Not the specified device, return immediately! \n " ) ;
2011-06-17 15:35:11 +04:00
} else {
retval = rts51x_read_mem ( us , 0xFD6F , buf , 1 ) ;
if ( retval = = STATUS_SUCCESS & & ( buf [ 0 ] & 0x24 ) = = 0x24 ) {
/* SD Exist and SD WP */
retval = rts51x_read_mem ( us , 0xD04E , buf , 1 ) ;
if ( retval = = STATUS_SUCCESS ) {
buf [ 0 ] | = 0x04 ;
retval = rts51x_write_mem ( us , 0xFD70 , buf , 1 ) ;
if ( retval ! = STATUS_SUCCESS )
2013-04-19 22:44:00 +04:00
usb_stor_dbg ( us , " Write memory fail \n " ) ;
2011-06-17 15:35:11 +04:00
} else {
2013-04-19 22:44:00 +04:00
usb_stor_dbg ( us , " Read memory fail \n " ) ;
2011-06-17 15:35:11 +04:00
}
} else {
2013-04-19 22:44:00 +04:00
usb_stor_dbg ( us , " Read memory fail, OR (buf[0]&0x24)!=0x24 \n " ) ;
2011-06-17 15:35:11 +04:00
}
}
}
static void rts51x_modi_suspend_timer ( struct rts51x_chip * chip )
{
2013-04-19 22:44:00 +04:00
struct us_data * us = chip - > us ;
usb_stor_dbg ( us , " state:%d \n " , rts51x_get_stat ( chip ) ) ;
2011-06-17 15:35:11 +04:00
chip - > timer_expires = jiffies + msecs_to_jiffies ( 1000 * ss_delay ) ;
mod_timer ( & chip - > rts51x_suspend_timer , chip - > timer_expires ) ;
}
static void rts51x_suspend_timer_fn ( unsigned long data )
{
struct rts51x_chip * chip = ( struct rts51x_chip * ) data ;
struct us_data * us = chip - > us ;
switch ( rts51x_get_stat ( chip ) ) {
case RTS51X_STAT_INIT :
case RTS51X_STAT_RUN :
rts51x_modi_suspend_timer ( chip ) ;
break ;
case RTS51X_STAT_IDLE :
case RTS51X_STAT_SS :
2013-04-19 22:44:00 +04:00
usb_stor_dbg ( us , " RTS51X_STAT_SS, intf->pm_usage_cnt:%d, power.usage:%d \n " ,
atomic_read ( & us - > pusb_intf - > pm_usage_cnt ) ,
atomic_read ( & us - > pusb_intf - > dev . power . usage_count ) ) ;
2011-06-17 15:35:11 +04:00
if ( atomic_read ( & us - > pusb_intf - > pm_usage_cnt ) > 0 ) {
2013-04-19 22:44:00 +04:00
usb_stor_dbg ( us , " Ready to enter SS state \n " ) ;
2011-06-17 15:35:11 +04:00
rts51x_set_stat ( chip , RTS51X_STAT_SS ) ;
/* ignore mass storage interface's children */
pm_suspend_ignore_children ( & us - > pusb_intf - > dev , true ) ;
2012-01-26 15:29:42 +04:00
usb_autopm_put_interface_async ( us - > pusb_intf ) ;
2013-04-19 22:44:00 +04:00
usb_stor_dbg ( us , " RTS51X_STAT_SS 01, intf->pm_usage_cnt:%d, power.usage:%d \n " ,
atomic_read ( & us - > pusb_intf - > pm_usage_cnt ) ,
atomic_read ( & us - > pusb_intf - > dev . power . usage_count ) ) ;
2011-06-17 15:35:11 +04:00
}
break ;
default :
2013-04-19 22:44:00 +04:00
usb_stor_dbg ( us , " Unknown state !!! \n " ) ;
2011-06-17 15:35:11 +04:00
break ;
}
}
static inline int working_scsi ( struct scsi_cmnd * srb )
{
if ( ( srb - > cmnd [ 0 ] = = TEST_UNIT_READY ) | |
( srb - > cmnd [ 0 ] = = ALLOW_MEDIUM_REMOVAL ) ) {
return 0 ;
}
return 1 ;
}
2011-11-15 11:53:37 +04:00
static void rts51x_invoke_transport ( struct scsi_cmnd * srb , struct us_data * us )
2011-06-17 15:35:11 +04:00
{
struct rts51x_chip * chip = ( struct rts51x_chip * ) ( us - > extra ) ;
static int card_first_show = 1 ;
static u8 media_not_present [ ] = { 0x70 , 0 , 0x02 , 0 , 0 , 0 , 0 ,
10 , 0 , 0 , 0 , 0 , 0x3A , 0 , 0 , 0 , 0 , 0
} ;
static u8 invalid_cmd_field [ ] = { 0x70 , 0 , 0x05 , 0 , 0 , 0 , 0 ,
10 , 0 , 0 , 0 , 0 , 0x24 , 0 , 0 , 0 , 0 , 0
} ;
int ret ;
if ( working_scsi ( srb ) ) {
2013-04-19 22:44:00 +04:00
usb_stor_dbg ( us , " working scsi, intf->pm_usage_cnt:%d, power.usage:%d \n " ,
atomic_read ( & us - > pusb_intf - > pm_usage_cnt ) ,
atomic_read ( & us - > pusb_intf - > dev . power . usage_count ) ) ;
2011-06-17 15:35:11 +04:00
if ( atomic_read ( & us - > pusb_intf - > pm_usage_cnt ) < = 0 ) {
ret = usb_autopm_get_interface ( us - > pusb_intf ) ;
2013-04-19 22:44:00 +04:00
usb_stor_dbg ( us , " working scsi, ret=%d \n " , ret ) ;
2011-06-17 15:35:11 +04:00
}
if ( rts51x_get_stat ( chip ) ! = RTS51X_STAT_RUN )
rts51x_set_stat ( chip , RTS51X_STAT_RUN ) ;
chip - > proto_handler_backup ( srb , us ) ;
} else {
if ( rts51x_get_stat ( chip ) = = RTS51X_STAT_SS ) {
2013-04-19 22:44:00 +04:00
usb_stor_dbg ( us , " NOT working scsi \n " ) ;
2011-06-17 15:35:11 +04:00
if ( ( srb - > cmnd [ 0 ] = = TEST_UNIT_READY ) & &
( chip - > pwr_state = = US_SUSPEND ) ) {
if ( TST_LUN_READY ( chip , srb - > device - > lun ) ) {
srb - > result = SAM_STAT_GOOD ;
} else {
srb - > result = SAM_STAT_CHECK_CONDITION ;
memcpy ( srb - > sense_buffer ,
media_not_present ,
US_SENSE_SIZE ) ;
}
2013-04-19 22:44:00 +04:00
usb_stor_dbg ( us , " TEST_UNIT_READY \n " ) ;
2011-06-17 15:35:11 +04:00
goto out ;
}
if ( srb - > cmnd [ 0 ] = = ALLOW_MEDIUM_REMOVAL ) {
int prevent = srb - > cmnd [ 4 ] & 0x1 ;
if ( prevent ) {
srb - > result = SAM_STAT_CHECK_CONDITION ;
memcpy ( srb - > sense_buffer ,
invalid_cmd_field ,
US_SENSE_SIZE ) ;
} else {
srb - > result = SAM_STAT_GOOD ;
}
2013-04-19 22:44:00 +04:00
usb_stor_dbg ( us , " ALLOW_MEDIUM_REMOVAL \n " ) ;
2011-06-17 15:35:11 +04:00
goto out ;
}
} else {
2013-04-19 22:44:00 +04:00
usb_stor_dbg ( us , " NOT working scsi, not SS \n " ) ;
2011-06-17 15:35:11 +04:00
chip - > proto_handler_backup ( srb , us ) ;
2012-09-20 05:48:00 +04:00
/* Check whether card is plugged in */
2011-06-17 15:35:11 +04:00
if ( srb - > cmnd [ 0 ] = = TEST_UNIT_READY ) {
if ( srb - > result = = SAM_STAT_GOOD ) {
SET_LUN_READY ( chip , srb - > device - > lun ) ;
if ( card_first_show ) {
card_first_show = 0 ;
fw5895_set_mmc_wp ( us ) ;
}
} else {
CLR_LUN_READY ( chip , srb - > device - > lun ) ;
card_first_show = 1 ;
}
}
if ( rts51x_get_stat ( chip ) ! = RTS51X_STAT_IDLE )
rts51x_set_stat ( chip , RTS51X_STAT_IDLE ) ;
}
}
out :
2013-04-19 22:44:00 +04:00
usb_stor_dbg ( us , " state:%d \n " , rts51x_get_stat ( chip ) ) ;
2011-06-17 15:35:11 +04:00
if ( rts51x_get_stat ( chip ) = = RTS51X_STAT_RUN )
rts51x_modi_suspend_timer ( chip ) ;
}
static int realtek_cr_autosuspend_setup ( struct us_data * us )
{
struct rts51x_chip * chip ;
struct rts51x_status * status = NULL ;
u8 buf [ 16 ] ;
int retval ;
chip = ( struct rts51x_chip * ) us - > extra ;
chip - > support_auto_delink = 0 ;
chip - > pwr_state = US_RESUME ;
chip - > lun_ready = 0 ;
rts51x_set_stat ( chip , RTS51X_STAT_INIT ) ;
retval = rts51x_read_status ( us , 0 , buf , 16 , & ( chip - > status_len ) ) ;
if ( retval ! = STATUS_SUCCESS ) {
2013-04-19 22:44:00 +04:00
usb_stor_dbg ( us , " Read status fail \n " ) ;
2011-06-17 15:35:11 +04:00
return - EIO ;
}
status = chip - > status ;
status - > vid = ( ( u16 ) buf [ 0 ] < < 8 ) | buf [ 1 ] ;
status - > pid = ( ( u16 ) buf [ 2 ] < < 8 ) | buf [ 3 ] ;
status - > cur_lun = buf [ 4 ] ;
status - > card_type = buf [ 5 ] ;
status - > total_lun = buf [ 6 ] ;
status - > fw_ver = ( ( u16 ) buf [ 7 ] < < 8 ) | buf [ 8 ] ;
status - > phy_exist = buf [ 9 ] ;
status - > multi_flag = buf [ 10 ] ;
status - > multi_card = buf [ 11 ] ;
status - > log_exist = buf [ 12 ] ;
if ( chip - > status_len = = 16 ) {
status - > detailed_type . detailed_type1 = buf [ 13 ] ;
status - > function [ 0 ] = buf [ 14 ] ;
status - > function [ 1 ] = buf [ 15 ] ;
}
/* back up the proto_handler in us->extra */
chip = ( struct rts51x_chip * ) ( us - > extra ) ;
chip - > proto_handler_backup = us - > proto_handler ;
/* Set the autosuspend_delay to 0 */
pm_runtime_set_autosuspend_delay ( & us - > pusb_dev - > dev , 0 ) ;
/* override us->proto_handler setted in get_protocol() */
us - > proto_handler = rts51x_invoke_transport ;
chip - > timer_expires = 0 ;
setup_timer ( & chip - > rts51x_suspend_timer , rts51x_suspend_timer_fn ,
( unsigned long ) chip ) ;
fw5895_init ( us ) ;
2014-09-18 13:25:04 +04:00
/* enable autosuspend function of the usb device */
2011-06-17 15:35:11 +04:00
usb_enable_autosuspend ( us - > pusb_dev ) ;
2011-01-14 11:53:34 +03:00
return 0 ;
}
2011-06-17 15:35:11 +04:00
# endif
2011-01-14 11:53:34 +03:00
static void realtek_cr_destructor ( void * extra )
{
2013-05-01 02:26:44 +04:00
struct rts51x_chip * chip = extra ;
2011-06-17 15:35:11 +04:00
2011-01-14 11:53:34 +03:00
if ( ! chip )
return ;
2013-04-19 22:44:00 +04:00
2011-06-17 15:35:11 +04:00
# ifdef CONFIG_REALTEK_AUTOPM
if ( ss_en ) {
del_timer ( & chip - > rts51x_suspend_timer ) ;
chip - > timer_expires = 0 ;
}
# endif
2011-01-14 11:53:34 +03:00
kfree ( chip - > status ) ;
}
# ifdef CONFIG_PM
2011-11-15 11:53:37 +04:00
static int realtek_cr_suspend ( struct usb_interface * iface , pm_message_t message )
2011-01-14 11:53:34 +03:00
{
2011-06-17 15:35:11 +04:00
struct us_data * us = usb_get_intfdata ( iface ) ;
/* wait until no command is running */
mutex_lock ( & us - > dev_mutex ) ;
config_autodelink_before_power_down ( us ) ;
mutex_unlock ( & us - > dev_mutex ) ;
return 0 ;
2011-01-14 11:53:34 +03:00
}
2011-06-17 15:35:11 +04:00
static int realtek_cr_resume ( struct usb_interface * iface )
{
struct us_data * us = usb_get_intfdata ( iface ) ;
fw5895_init ( us ) ;
config_autodelink_after_power_on ( us ) ;
return 0 ;
}
# else
# define realtek_cr_suspend NULL
# define realtek_cr_resume NULL
2011-01-14 11:53:34 +03:00
# endif
static int init_realtek_cr ( struct us_data * us )
{
struct rts51x_chip * chip ;
int size , i , retval ;
chip = kzalloc ( sizeof ( struct rts51x_chip ) , GFP_KERNEL ) ;
if ( ! chip )
return - ENOMEM ;
us - > extra = chip ;
us - > extra_destructor = realtek_cr_destructor ;
us - > max_lun = chip - > max_lun = rts51x_get_max_lun ( us ) ;
2013-04-27 06:49:13 +04:00
chip - > us = us ;
2011-01-14 11:53:34 +03:00
2013-04-19 22:44:00 +04:00
usb_stor_dbg ( us , " chip->max_lun = %d \n " , chip - > max_lun ) ;
2011-01-14 11:53:34 +03:00
size = ( chip - > max_lun + 1 ) * sizeof ( struct rts51x_status ) ;
chip - > status = kzalloc ( size , GFP_KERNEL ) ;
if ( ! chip - > status )
goto INIT_FAIL ;
for ( i = 0 ; i < = ( int ) ( chip - > max_lun ) ; i + + ) {
2011-06-17 15:35:09 +04:00
retval = rts51x_check_status ( us , ( u8 ) i ) ;
2011-01-14 11:53:34 +03:00
if ( retval < 0 )
goto INIT_FAIL ;
}
if ( CHECK_FW_VER ( chip , 0x5888 ) | | CHECK_FW_VER ( chip , 0x5889 ) | |
2011-06-17 15:35:09 +04:00
CHECK_FW_VER ( chip , 0x5901 ) )
2011-01-14 11:53:34 +03:00
SET_AUTO_DELINK ( chip ) ;
if ( STATUS_LEN ( chip ) = = 16 ) {
if ( SUPPORT_AUTO_DELINK ( chip ) )
SET_AUTO_DELINK ( chip ) ;
}
2011-06-17 15:35:11 +04:00
# ifdef CONFIG_REALTEK_AUTOPM
2013-04-27 06:49:13 +04:00
if ( ss_en )
2011-06-17 15:35:11 +04:00
realtek_cr_autosuspend_setup ( us ) ;
# endif
2011-01-14 11:53:34 +03:00
2013-04-19 22:44:00 +04:00
usb_stor_dbg ( us , " chip->flag = 0x%x \n " , chip - > flag ) ;
2011-01-14 11:53:34 +03:00
( void ) config_autodelink_after_power_on ( us ) ;
return 0 ;
INIT_FAIL :
if ( us - > extra ) {
kfree ( chip - > status ) ;
kfree ( us - > extra ) ;
us - > extra = NULL ;
}
return - EIO ;
}
2015-05-06 12:24:21 +03:00
static struct scsi_host_template realtek_cr_host_template ;
2011-01-14 11:53:34 +03:00
static int realtek_cr_probe ( struct usb_interface * intf ,
2011-06-17 15:35:09 +04:00
const struct usb_device_id * id )
2011-01-14 11:53:34 +03:00
{
struct us_data * us ;
int result ;
2013-04-19 22:44:00 +04:00
dev_dbg ( & intf - > dev , " Probe Realtek Card Reader! \n " ) ;
2011-01-14 11:53:34 +03:00
result = usb_stor_probe1 ( & us , intf , id ,
2011-06-17 15:35:09 +04:00
( id - realtek_cr_ids ) +
2015-05-06 12:24:21 +03:00
realtek_cr_unusual_dev_list ,
& realtek_cr_host_template ) ;
2011-01-14 11:53:34 +03:00
if ( result )
return result ;
result = usb_stor_probe2 ( us ) ;
2011-06-17 15:35:09 +04:00
2011-01-14 11:53:34 +03:00
return result ;
}
static struct usb_driver realtek_cr_driver = {
2015-05-06 12:24:21 +03:00
. name = DRV_NAME ,
2011-06-17 15:35:09 +04:00
. probe = realtek_cr_probe ,
. disconnect = usb_stor_disconnect ,
2011-06-17 15:35:11 +04:00
/* .suspend = usb_stor_suspend, */
/* .resume = usb_stor_resume, */
2011-06-17 15:35:09 +04:00
. reset_resume = usb_stor_reset_resume ,
2011-06-17 15:35:11 +04:00
. suspend = realtek_cr_suspend ,
. resume = realtek_cr_resume ,
2011-06-17 15:35:09 +04:00
. pre_reset = usb_stor_pre_reset ,
. post_reset = usb_stor_post_reset ,
. id_table = realtek_cr_ids ,
. soft_unbind = 1 ,
2011-06-17 15:35:11 +04:00
. supports_autosuspend = 1 ,
2012-01-14 06:15:21 +04:00
. no_dynamic_id = 1 ,
2011-01-14 11:53:34 +03:00
} ;
2015-05-06 12:24:21 +03:00
module_usb_stor_driver ( realtek_cr_driver , realtek_cr_host_template , DRV_NAME ) ;