linux/drivers/usb/storage/realtek_cr.c
Alan Stern c2b71462d2 USB: core: Fix bug caused by duplicate interface PM usage counter
The syzkaller fuzzer reported a bug in the USB hub driver which turned
out to be caused by a negative runtime-PM usage counter.  This allowed
a hub to be runtime suspended at a time when the driver did not expect
it.  The symptom is a WARNING issued because the hub's status URB is
submitted while it is already active:

	URB 0000000031fb463e submitted while active
	WARNING: CPU: 0 PID: 2917 at drivers/usb/core/urb.c:363

The negative runtime-PM usage count was caused by an unfortunate
design decision made when runtime PM was first implemented for USB.
At that time, USB class drivers were allowed to unbind from their
interfaces without balancing the usage counter (i.e., leaving it with
a positive count).  The core code would take care of setting the
counter back to 0 before allowing another driver to bind to the
interface.

Later on when runtime PM was implemented for the entire kernel, the
opposite decision was made: Drivers were required to balance their
runtime-PM get and put calls.  In order to maintain backward
compatibility, however, the USB subsystem adapted to the new
implementation by keeping an independent usage counter for each
interface and using it to automatically adjust the normal usage
counter back to 0 whenever a driver was unbound.

This approach involves duplicating information, but what is worse, it
doesn't work properly in cases where a USB class driver delays
decrementing the usage counter until after the driver's disconnect()
routine has returned and the counter has been adjusted back to 0.
Doing so would cause the usage counter to become negative.  There's
even a warning about this in the USB power management documentation!

As it happens, this is exactly what the hub driver does.  The
kick_hub_wq() routine increments the runtime-PM usage counter, and the
corresponding decrement is carried out by hub_event() in the context
of the hub_wq work-queue thread.  This work routine may sometimes run
after the driver has been unbound from its interface, and when it does
it causes the usage counter to go negative.

It is not possible for hub_disconnect() to wait for a pending
hub_event() call to finish, because hub_disconnect() is called with
the device lock held and hub_event() acquires that lock.  The only
feasible fix is to reverse the original design decision: remove the
duplicate interface-specific usage counter and require USB drivers to
balance their runtime PM gets and puts.  As far as I know, all
existing drivers currently do this.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Reported-and-tested-by: syzbot+7634edaea4d0b341c625@syzkaller.appspotmail.com
CC: <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2019-04-19 21:15:13 +02:00

1067 lines
26 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Driver for Realtek RTS51xx USB card reader
*
* Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved.
*
* 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"
#include "scsiglue.h"
#define DRV_NAME "ums-realtek"
MODULE_DESCRIPTION("Driver for Realtek USB Card Reader");
MODULE_AUTHOR("wwang <wei_wang@realsil.com.cn>");
MODULE_LICENSE("GPL");
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");
#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
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 {
u16 vendor_id;
u16 product_id;
char max_lun;
struct rts51x_status *status;
int status_len;
u32 flag;
struct us_data *us;
#ifdef CONFIG_REALTEK_AUTOPM
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
/* used to back up the protocol chosen in probe1 phase */
proto_cmnd proto_handler_backup;
};
/* 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)
#define VENDOR_ID(chip) ((chip)->status[0].vid)
#define PRODUCT_ID(chip) ((chip)->status[0].pid)
#define FW_VERSION(chip) ((chip)->status[0].fw_ver)
#define STATUS_LEN(chip) ((chip)->status_len)
#define STATUS_SUCCESS 0
#define STATUS_FAIL 1
/* 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), \
.driver_info = (flags) \
}
static const struct usb_device_id realtek_cr_ids[] = {
# include "unusual_realtek.h"
{} /* Terminating entry */
};
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"
{} /* Terminating entry */
};
#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)
{
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 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);
bcb->Flags = (dir == DMA_FROM_DEVICE) ? US_BULK_FLAG_IN : 0;
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_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
bcs, US_BULK_CS_WRAP_LEN, &cswlen);
if (result != USB_STOR_XFER_GOOD)
return USB_STOR_TRANSPORT_ERROR;
/* check bulk status */
if (bcs->Signature != cpu_to_le32(US_BULK_CS_SIGN)) {
usb_stor_dbg(us, "Signature mismatch: got %08X, expecting %08X\n",
le32_to_cpu(bcs->Signature), US_BULK_CS_SIGN);
return USB_STOR_TRANSPORT_ERROR;
}
residue = bcs->Residue;
if (bcs->Tag != us->tag)
return USB_STOR_TRANSPORT_ERROR;
/*
* try to compute the actual residue, based on how much data
* was really transferred and what the device tells us
*/
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:
/*
* phase error -- note that a transport reset will be
* invoked by the invoke_transport() function
*/
return USB_STOR_TRANSPORT_ERROR;
}
/* we should never get here, but if we do, we're in trouble */
return USB_STOR_TRANSPORT_ERROR;
}
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);
bcb->Flags = (dir == DMA_FROM_DEVICE) ? US_BULK_FLAG_IN : 0;
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;
}
/* 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,
US_BULK_GET_MAX_LUN,
USB_DIR_IN | USB_TYPE_CLASS |
USB_RECIP_INTERFACE,
0, us->ifnum, us->iobuf, 1, 10 * HZ);
usb_stor_dbg(us, "GetMaxLUN command result is %d, data is %d\n",
result, us->iobuf[0]);
/* 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;
u8 cmnd[12] = { 0 };
u8 *buf;
buf = kmalloc(len, GFP_NOIO);
if (buf == NULL)
return USB_STOR_TRANSPORT_ERROR;
usb_stor_dbg(us, "addr = 0x%x, len = %d\n", addr, len);
cmnd[0] = 0xF0;
cmnd[1] = 0x0D;
cmnd[2] = (u8) (addr >> 8);
cmnd[3] = (u8) addr;
cmnd[4] = (u8) (len >> 8);
cmnd[5] = (u8) len;
retval = rts51x_bulk_transport(us, 0, cmnd, 12,
buf, len, DMA_FROM_DEVICE, NULL);
if (retval != USB_STOR_TRANSPORT_GOOD) {
kfree(buf);
return -EIO;
}
memcpy(data, buf, len);
kfree(buf);
return 0;
}
static int rts51x_write_mem(struct us_data *us, u16 addr, u8 *data, u16 len)
{
int retval;
u8 cmnd[12] = { 0 };
u8 *buf;
buf = kmemdup(data, len, GFP_NOIO);
if (buf == NULL)
return USB_STOR_TRANSPORT_ERROR;
usb_stor_dbg(us, "addr = 0x%x, len = %d\n", addr, len);
cmnd[0] = 0xF0;
cmnd[1] = 0x0E;
cmnd[2] = (u8) (addr >> 8);
cmnd[3] = (u8) addr;
cmnd[4] = (u8) (len >> 8);
cmnd[5] = (u8) len;
retval = rts51x_bulk_transport(us, 0, cmnd, 12,
buf, len, DMA_TO_DEVICE, NULL);
kfree(buf);
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;
u8 cmnd[12] = { 0 };
u8 *buf;
buf = kmalloc(len, GFP_NOIO);
if (buf == NULL)
return USB_STOR_TRANSPORT_ERROR;
usb_stor_dbg(us, "lun = %d\n", lun);
cmnd[0] = 0xF0;
cmnd[1] = 0x09;
retval = rts51x_bulk_transport(us, lun, cmnd, 12,
buf, len, DMA_FROM_DEVICE, actlen);
if (retval != USB_STOR_TRANSPORT_GOOD) {
kfree(buf);
return -EIO;
}
memcpy(status, buf, len);
kfree(buf);
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));
if (retval != STATUS_SUCCESS)
return -EIO;
usb_stor_dbg(us, "chip->status_len = %d\n", chip->status_len);
chip->status[lun].vid = ((u16) buf[0] << 8) | buf[1];
chip->status[lun].pid = ((u16) buf[2] << 8) | buf[3];
chip->status[lun].cur_lun = buf[4];
chip->status[lun].card_type = buf[5];
chip->status[lun].total_lun = buf[6];
chip->status[lun].fw_ver = ((u16) buf[7] << 8) | buf[8];
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;
}
static int __do_config_autodelink(struct us_data *us, u8 *data, u16 len)
{
int retval;
u8 cmnd[12] = {0};
u8 *buf;
usb_stor_dbg(us, "addr = 0xfe47, len = %d\n", len);
buf = kmemdup(data, len, GFP_NOIO);
if (!buf)
return USB_STOR_TRANSPORT_ERROR;
cmnd[0] = 0xF0;
cmnd[1] = 0x0E;
cmnd[2] = 0xfe;
cmnd[3] = 0x47;
cmnd[4] = (u8)(len >> 8);
cmnd[5] = (u8)len;
retval = rts51x_bulk_transport_special(us, 0, cmnd, 12, buf, len, DMA_TO_DEVICE, NULL);
kfree(buf);
if (retval != USB_STOR_TRANSPORT_GOOD) {
return -EIO;
}
return 0;
}
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;
}
usb_stor_dbg(us, "set 0xfe47 to 0x%x\n", value);
/* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */
retval = __do_config_autodelink(us, &value, 1);
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);
/* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */
retval = __do_config_autodelink(us, &value, 1);
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) ||
CHECK_ID(chip, 0x0138, 0x3880)) {
CLR_BIT(value, 0);
CLR_BIT(value, 7);
}
/* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */
retval = __do_config_autodelink(us, &value, 1);
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;
}
#ifdef CONFIG_PM
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) ||
CHECK_ID(chip, 0x0138, 0x3880) ||
CHECK_ID(chip, 0x0138, 0x3882)) {
retval = rts51x_read_mem(us, 0xFE47, &value, 1);
if (retval < 0)
return -EIO;
if (CHECK_ID(chip, 0x0159, 0x5889) ||
CHECK_ID(chip, 0x0138, 0x3880)) {
SET_BIT(value, 0);
SET_BIT(value, 7);
}
if (CHECK_ID(chip, 0x0138, 0x3882))
SET_BIT(value, 2);
/* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */
retval = __do_config_autodelink(us, &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;
}
}
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)) {
usb_stor_dbg(us, "Not the specified device, return immediately!\n");
} 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)
usb_stor_dbg(us, "Write memory fail\n");
} else {
usb_stor_dbg(us, "Read memory fail, OR (val & 0x1F) != 0\n");
}
}
}
#endif
#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)) {
usb_stor_dbg(us, "Not the specified device, return immediately!\n");
} 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)
usb_stor_dbg(us, "Write memory fail\n");
} else {
usb_stor_dbg(us, "Read memory fail\n");
}
} else {
usb_stor_dbg(us, "Read memory fail, OR (buf[0]&0x24)!=0x24\n");
}
}
}
static void rts51x_modi_suspend_timer(struct rts51x_chip *chip)
{
struct us_data *us = chip->us;
usb_stor_dbg(us, "state:%d\n", rts51x_get_stat(chip));
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(struct timer_list *t)
{
struct rts51x_chip *chip = from_timer(chip, t, rts51x_suspend_timer);
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:
usb_stor_dbg(us, "RTS51X_STAT_SS, power.usage:%d\n",
atomic_read(&us->pusb_intf->dev.power.usage_count));
if (atomic_read(&us->pusb_intf->dev.power.usage_count) > 0) {
usb_stor_dbg(us, "Ready to enter SS state\n");
rts51x_set_stat(chip, RTS51X_STAT_SS);
/* ignore mass storage interface's children */
pm_suspend_ignore_children(&us->pusb_intf->dev, true);
usb_autopm_put_interface_async(us->pusb_intf);
usb_stor_dbg(us, "RTS51X_STAT_SS 01, power.usage:%d\n",
atomic_read(&us->pusb_intf->dev.power.usage_count));
}
break;
default:
usb_stor_dbg(us, "Unknown state !!!\n");
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;
}
static void rts51x_invoke_transport(struct scsi_cmnd *srb, struct us_data *us)
{
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)) {
usb_stor_dbg(us, "working scsi, power.usage:%d\n",
atomic_read(&us->pusb_intf->dev.power.usage_count));
if (atomic_read(&us->pusb_intf->dev.power.usage_count) <= 0) {
ret = usb_autopm_get_interface(us->pusb_intf);
usb_stor_dbg(us, "working scsi, ret=%d\n", ret);
}
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) {
usb_stor_dbg(us, "NOT working scsi\n");
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);
}
usb_stor_dbg(us, "TEST_UNIT_READY\n");
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;
}
usb_stor_dbg(us, "ALLOW_MEDIUM_REMOVAL\n");
goto out;
}
} else {
usb_stor_dbg(us, "NOT working scsi, not SS\n");
chip->proto_handler_backup(srb, us);
/* Check whether card is plugged in */
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:
usb_stor_dbg(us, "state:%d\n", rts51x_get_stat(chip));
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) {
usb_stor_dbg(us, "Read status fail\n");
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;
timer_setup(&chip->rts51x_suspend_timer, rts51x_suspend_timer_fn, 0);
fw5895_init(us);
/* enable autosuspend function of the usb device */
usb_enable_autosuspend(us->pusb_dev);
return 0;
}
#endif
static void realtek_cr_destructor(void *extra)
{
struct rts51x_chip *chip = extra;
if (!chip)
return;
#ifdef CONFIG_REALTEK_AUTOPM
if (ss_en) {
del_timer(&chip->rts51x_suspend_timer);
chip->timer_expires = 0;
}
#endif
kfree(chip->status);
}
#ifdef CONFIG_PM
static int realtek_cr_suspend(struct usb_interface *iface, pm_message_t message)
{
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;
}
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
#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);
chip->us = us;
usb_stor_dbg(us, "chip->max_lun = %d\n", chip->max_lun);
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++) {
retval = rts51x_check_status(us, (u8) i);
if (retval < 0)
goto INIT_FAIL;
}
if (CHECK_FW_VER(chip, 0x5888) || CHECK_FW_VER(chip, 0x5889) ||
CHECK_FW_VER(chip, 0x5901))
SET_AUTO_DELINK(chip);
if (STATUS_LEN(chip) == 16) {
if (SUPPORT_AUTO_DELINK(chip))
SET_AUTO_DELINK(chip);
}
#ifdef CONFIG_REALTEK_AUTOPM
if (ss_en)
realtek_cr_autosuspend_setup(us);
#endif
usb_stor_dbg(us, "chip->flag = 0x%x\n", chip->flag);
(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;
}
static struct scsi_host_template realtek_cr_host_template;
static int realtek_cr_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct us_data *us;
int result;
dev_dbg(&intf->dev, "Probe Realtek Card Reader!\n");
result = usb_stor_probe1(&us, intf, id,
(id - realtek_cr_ids) +
realtek_cr_unusual_dev_list,
&realtek_cr_host_template);
if (result)
return result;
result = usb_stor_probe2(us);
return result;
}
static struct usb_driver realtek_cr_driver = {
.name = DRV_NAME,
.probe = realtek_cr_probe,
.disconnect = usb_stor_disconnect,
/* .suspend = usb_stor_suspend, */
/* .resume = usb_stor_resume, */
.reset_resume = usb_stor_reset_resume,
.suspend = realtek_cr_suspend,
.resume = realtek_cr_resume,
.pre_reset = usb_stor_pre_reset,
.post_reset = usb_stor_post_reset,
.id_table = realtek_cr_ids,
.soft_unbind = 1,
.supports_autosuspend = 1,
.no_dynamic_id = 1,
};
module_usb_stor_driver(realtek_cr_driver, realtek_cr_host_template, DRV_NAME);