2017-11-03 18:28:30 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
2016-04-18 18:09:11 +08:00
|
|
|
/*
|
|
|
|
* Driver for Realtek RTS51xx USB card reader
|
2011-01-14 16:53:34 +08:00
|
|
|
*
|
|
|
|
* 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"
|
2015-05-06 17:24:21 +08:00
|
|
|
#include "scsiglue.h"
|
|
|
|
|
|
|
|
#define DRV_NAME "ums-realtek"
|
2011-01-14 16:53:34 +08:00
|
|
|
|
|
|
|
MODULE_DESCRIPTION("Driver for Realtek USB Card Reader");
|
|
|
|
MODULE_AUTHOR("wwang <wei_wang@realsil.com.cn>");
|
|
|
|
MODULE_LICENSE("GPL");
|
2019-09-06 18:32:35 +08:00
|
|
|
MODULE_IMPORT_NS(USB_STORAGE);
|
2011-01-14 16:53:34 +08:00
|
|
|
|
|
|
|
static int auto_delink_en = 1;
|
|
|
|
module_param(auto_delink_en, int, S_IRUGO | S_IWUSR);
|
2019-08-28 01:34:49 +08:00
|
|
|
MODULE_PARM_DESC(auto_delink_en, "auto delink mode (0=firmware, 1=software [default])");
|
2011-01-14 16:53:34 +08:00
|
|
|
|
2011-06-17 19:35:11 +08: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 16:53:34 +08: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 19:35:09 +08:00
|
|
|
u16 vendor_id;
|
|
|
|
u16 product_id;
|
|
|
|
char max_lun;
|
2011-01-14 16:53:34 +08:00
|
|
|
|
2011-06-17 19:35:09 +08:00
|
|
|
struct rts51x_status *status;
|
|
|
|
int status_len;
|
2011-01-14 16:53:34 +08:00
|
|
|
|
2011-06-17 19:35:11 +08:00
|
|
|
u32 flag;
|
|
|
|
struct us_data *us;
|
2013-04-27 10:49:13 +08:00
|
|
|
|
|
|
|
#ifdef CONFIG_REALTEK_AUTOPM
|
2011-06-17 19:35:11 +08: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 17:25:04 +08:00
|
|
|
/* used to back up the protocol chosen in probe1 phase */
|
2011-06-17 19:35:11 +08:00
|
|
|
proto_cmnd proto_handler_backup;
|
2011-01-14 16:53:34 +08: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 19:35:11 +08:00
|
|
|
#define VENDOR_ID(chip) ((chip)->status[0].vid)
|
|
|
|
#define PRODUCT_ID(chip) ((chip)->status[0].pid)
|
2011-01-14 16:53:34 +08:00
|
|
|
#define FW_VERSION(chip) ((chip)->status[0].fw_ver)
|
|
|
|
#define STATUS_LEN(chip) ((chip)->status_len)
|
|
|
|
|
2011-06-17 19:35:11 +08:00
|
|
|
#define STATUS_SUCCESS 0
|
|
|
|
#define STATUS_FAIL 1
|
|
|
|
|
2011-01-14 16:53:34 +08: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 04:37:13 +08:00
|
|
|
.driver_info = (flags) \
|
2011-01-14 16:53:34 +08:00
|
|
|
}
|
|
|
|
|
2011-02-15 17:02:47 +08:00
|
|
|
static const struct usb_device_id realtek_cr_ids[] = {
|
2011-01-14 16:53:34 +08:00
|
|
|
# include "unusual_realtek.h"
|
2011-06-17 19:35:09 +08:00
|
|
|
{} /* Terminating entry */
|
2011-01-14 16:53:34 +08:00
|
|
|
};
|
2011-06-17 19:35:09 +08:00
|
|
|
|
2011-01-14 16:53:34 +08: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 19:35:09 +08:00
|
|
|
{} /* Terminating entry */
|
2011-01-14 16:53:34 +08: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 19:35:09 +08: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 16:53:34 +08: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-26 01:28:10 +08:00
|
|
|
bcb->Flags = (dir == DMA_FROM_DEVICE) ? US_BULK_FLAG_IN : 0;
|
2011-01-14 16:53:34 +08: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 19:35:09 +08:00
|
|
|
bcb, cbwlen, NULL);
|
2011-01-14 16:53:34 +08: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 19:35:09 +08:00
|
|
|
us->recv_bulk_pipe : us->send_bulk_pipe;
|
2011-01-14 16:53:34 +08:00
|
|
|
result = usb_stor_bulk_transfer_buf(us, pipe,
|
2011-06-17 19:35:09 +08:00
|
|
|
buf, buf_len, NULL);
|
2011-01-14 16:53:34 +08: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 19:35:09 +08:00
|
|
|
bcs, US_BULK_CS_WRAP_LEN, &cswlen);
|
2011-01-14 16:53:34 +08: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-20 02:44:00 +08:00
|
|
|
usb_stor_dbg(us, "Signature mismatch: got %08X, expecting %08X\n",
|
|
|
|
le32_to_cpu(bcs->Signature), US_BULK_CS_SIGN);
|
2011-01-14 16:53:34 +08:00
|
|
|
return USB_STOR_TRANSPORT_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
residue = bcs->Residue;
|
|
|
|
if (bcs->Tag != us->tag)
|
|
|
|
return USB_STOR_TRANSPORT_ERROR;
|
|
|
|
|
2016-04-18 18:09:11 +08:00
|
|
|
/*
|
|
|
|
* try to compute the actual residue, based on how much data
|
|
|
|
* was really transferred and what the device tells us
|
|
|
|
*/
|
2011-01-14 16:53:34 +08: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 18:09:11 +08:00
|
|
|
/*
|
|
|
|
* phase error -- note that a transport reset will be
|
2011-06-17 19:35:09 +08:00
|
|
|
* invoked by the invoke_transport() function
|
|
|
|
*/
|
2011-01-14 16:53:34 +08: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 16:53:37 +08: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-26 01:28:10 +08:00
|
|
|
bcb->Flags = (dir == DMA_FROM_DEVICE) ? US_BULK_FLAG_IN : 0;
|
2011-09-16 16:53:37 +08: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 16:53:34 +08: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 19:35:09 +08: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 16:53:34 +08:00
|
|
|
|
2013-04-20 02:44:00 +08:00
|
|
|
usb_stor_dbg(us, "GetMaxLUN command result is %d, data is %d\n",
|
|
|
|
result, us->iobuf[0]);
|
2011-01-14 16:53:34 +08: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 19:35:09 +08:00
|
|
|
u8 cmnd[12] = { 0 };
|
2011-08-25 02:22:37 +08:00
|
|
|
u8 *buf;
|
|
|
|
|
|
|
|
buf = kmalloc(len, GFP_NOIO);
|
|
|
|
if (buf == NULL)
|
|
|
|
return USB_STOR_TRANSPORT_ERROR;
|
2011-01-14 16:53:34 +08:00
|
|
|
|
2013-04-20 02:44:00 +08:00
|
|
|
usb_stor_dbg(us, "addr = 0x%x, len = %d\n", addr, len);
|
2011-01-14 16:53:34 +08:00
|
|
|
|
|
|
|
cmnd[0] = 0xF0;
|
|
|
|
cmnd[1] = 0x0D;
|
2011-06-17 19:35:09 +08:00
|
|
|
cmnd[2] = (u8) (addr >> 8);
|
|
|
|
cmnd[3] = (u8) addr;
|
|
|
|
cmnd[4] = (u8) (len >> 8);
|
|
|
|
cmnd[5] = (u8) len;
|
2011-01-14 16:53:34 +08:00
|
|
|
|
|
|
|
retval = rts51x_bulk_transport(us, 0, cmnd, 12,
|
2011-08-25 02:22:37 +08:00
|
|
|
buf, len, DMA_FROM_DEVICE, NULL);
|
|
|
|
if (retval != USB_STOR_TRANSPORT_GOOD) {
|
|
|
|
kfree(buf);
|
2011-01-14 16:53:34 +08:00
|
|
|
return -EIO;
|
2011-08-25 02:22:37 +08:00
|
|
|
}
|
2011-01-14 16:53:34 +08:00
|
|
|
|
2011-08-25 02:22:37 +08:00
|
|
|
memcpy(data, buf, len);
|
|
|
|
kfree(buf);
|
2011-01-14 16:53:34 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int rts51x_write_mem(struct us_data *us, u16 addr, u8 *data, u16 len)
|
|
|
|
{
|
|
|
|
int retval;
|
2011-06-17 19:35:09 +08:00
|
|
|
u8 cmnd[12] = { 0 };
|
2011-08-25 02:22:37 +08:00
|
|
|
u8 *buf;
|
|
|
|
|
2011-11-11 02:27:42 +08:00
|
|
|
buf = kmemdup(data, len, GFP_NOIO);
|
2011-08-25 02:22:37 +08:00
|
|
|
if (buf == NULL)
|
|
|
|
return USB_STOR_TRANSPORT_ERROR;
|
2011-01-14 16:53:34 +08:00
|
|
|
|
2013-04-20 02:44:00 +08:00
|
|
|
usb_stor_dbg(us, "addr = 0x%x, len = %d\n", addr, len);
|
2011-01-14 16:53:34 +08:00
|
|
|
|
|
|
|
cmnd[0] = 0xF0;
|
|
|
|
cmnd[1] = 0x0E;
|
2011-06-17 19:35:09 +08:00
|
|
|
cmnd[2] = (u8) (addr >> 8);
|
|
|
|
cmnd[3] = (u8) addr;
|
|
|
|
cmnd[4] = (u8) (len >> 8);
|
|
|
|
cmnd[5] = (u8) len;
|
2011-01-14 16:53:34 +08:00
|
|
|
|
|
|
|
retval = rts51x_bulk_transport(us, 0, cmnd, 12,
|
2011-08-25 02:22:37 +08:00
|
|
|
buf, len, DMA_TO_DEVICE, NULL);
|
|
|
|
kfree(buf);
|
2011-01-14 16:53:34 +08: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 19:35:09 +08:00
|
|
|
u8 cmnd[12] = { 0 };
|
2011-08-25 02:22:37 +08:00
|
|
|
u8 *buf;
|
|
|
|
|
|
|
|
buf = kmalloc(len, GFP_NOIO);
|
|
|
|
if (buf == NULL)
|
|
|
|
return USB_STOR_TRANSPORT_ERROR;
|
2011-01-14 16:53:34 +08:00
|
|
|
|
2013-04-20 02:44:00 +08:00
|
|
|
usb_stor_dbg(us, "lun = %d\n", lun);
|
2011-01-14 16:53:34 +08:00
|
|
|
|
|
|
|
cmnd[0] = 0xF0;
|
|
|
|
cmnd[1] = 0x09;
|
|
|
|
|
|
|
|
retval = rts51x_bulk_transport(us, lun, cmnd, 12,
|
2011-08-25 02:22:37 +08:00
|
|
|
buf, len, DMA_FROM_DEVICE, actlen);
|
|
|
|
if (retval != USB_STOR_TRANSPORT_GOOD) {
|
|
|
|
kfree(buf);
|
2011-01-14 16:53:34 +08:00
|
|
|
return -EIO;
|
2011-08-25 02:22:37 +08:00
|
|
|
}
|
2011-01-14 16:53:34 +08:00
|
|
|
|
2011-08-25 02:22:37 +08:00
|
|
|
memcpy(status, buf, len);
|
|
|
|
kfree(buf);
|
2011-01-14 16:53:34 +08: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 10:22:26 +08:00
|
|
|
if (retval != STATUS_SUCCESS)
|
2011-01-14 16:53:34 +08:00
|
|
|
return -EIO;
|
|
|
|
|
2013-04-20 02:44:00 +08:00
|
|
|
usb_stor_dbg(us, "chip->status_len = %d\n", chip->status_len);
|
2011-01-14 16:53:34 +08:00
|
|
|
|
2011-06-17 19:35:09 +08:00
|
|
|
chip->status[lun].vid = ((u16) buf[0] << 8) | buf[1];
|
|
|
|
chip->status[lun].pid = ((u16) buf[2] << 8) | buf[3];
|
2011-01-14 16:53:34 +08: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 19:35:09 +08:00
|
|
|
chip->status[lun].fw_ver = ((u16) buf[7] << 8) | buf[8];
|
2011-01-14 16:53:34 +08: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 16:53:37 +08:00
|
|
|
static int __do_config_autodelink(struct us_data *us, u8 *data, u16 len)
|
|
|
|
{
|
|
|
|
int retval;
|
|
|
|
u8 cmnd[12] = {0};
|
2012-02-21 04:34:34 +08:00
|
|
|
u8 *buf;
|
2011-09-16 16:53:37 +08:00
|
|
|
|
2013-04-20 02:44:00 +08:00
|
|
|
usb_stor_dbg(us, "addr = 0xfe47, len = %d\n", len);
|
2011-09-16 16:53:37 +08:00
|
|
|
|
2012-02-21 04:34:34 +08:00
|
|
|
buf = kmemdup(data, len, GFP_NOIO);
|
|
|
|
if (!buf)
|
|
|
|
return USB_STOR_TRANSPORT_ERROR;
|
|
|
|
|
2011-09-16 16:53:37 +08:00
|
|
|
cmnd[0] = 0xF0;
|
|
|
|
cmnd[1] = 0x0E;
|
2011-11-15 15:53:37 +08:00
|
|
|
cmnd[2] = 0xfe;
|
|
|
|
cmnd[3] = 0x47;
|
2011-09-16 16:53:37 +08:00
|
|
|
cmnd[4] = (u8)(len >> 8);
|
|
|
|
cmnd[5] = (u8)len;
|
|
|
|
|
2012-02-21 04:34:34 +08:00
|
|
|
retval = rts51x_bulk_transport_special(us, 0, cmnd, 12, buf, len, DMA_TO_DEVICE, NULL);
|
|
|
|
kfree(buf);
|
2011-09-16 16:53:37 +08:00
|
|
|
if (retval != USB_STOR_TRANSPORT_GOOD) {
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-01-14 16:53:34 +08: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-20 02:44:00 +08:00
|
|
|
usb_stor_dbg(us, "set 0xfe47 to 0x%x\n", value);
|
2011-01-14 16:53:34 +08:00
|
|
|
|
2011-09-16 16:53:37 +08:00
|
|
|
/* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */
|
|
|
|
retval = __do_config_autodelink(us, &value, 1);
|
2011-01-14 16:53:34 +08: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 16:53:37 +08:00
|
|
|
/* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */
|
|
|
|
retval = __do_config_autodelink(us, &value, 1);
|
2011-01-14 16:53:34 +08: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 19:35:09 +08:00
|
|
|
CHECK_ID(chip, 0x0138, 0x3880)) {
|
2011-01-14 16:53:34 +08:00
|
|
|
CLR_BIT(value, 0);
|
|
|
|
CLR_BIT(value, 7);
|
|
|
|
}
|
|
|
|
|
2011-09-16 16:53:37 +08:00
|
|
|
/* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */
|
|
|
|
retval = __do_config_autodelink(us, &value, 1);
|
2011-01-14 16:53:34 +08: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 07:10:57 +08:00
|
|
|
#ifdef CONFIG_PM
|
2011-01-14 16:53:34 +08: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 19:35:09 +08:00
|
|
|
CHECK_ID(chip, 0x0138, 0x3880) ||
|
|
|
|
CHECK_ID(chip, 0x0138, 0x3882)) {
|
2011-01-14 16:53:34 +08:00
|
|
|
retval = rts51x_read_mem(us, 0xFE47, &value, 1);
|
|
|
|
if (retval < 0)
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
if (CHECK_ID(chip, 0x0159, 0x5889) ||
|
2011-06-17 19:35:09 +08:00
|
|
|
CHECK_ID(chip, 0x0138, 0x3880)) {
|
2011-01-14 16:53:34 +08:00
|
|
|
SET_BIT(value, 0);
|
|
|
|
SET_BIT(value, 7);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (CHECK_ID(chip, 0x0138, 0x3882))
|
|
|
|
SET_BIT(value, 2);
|
|
|
|
|
2011-09-16 16:53:37 +08:00
|
|
|
/* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */
|
|
|
|
retval = __do_config_autodelink(us, &value, 1);
|
2011-01-14 16:53:34 +08: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 19:35:11 +08: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-20 02:44:00 +08:00
|
|
|
usb_stor_dbg(us, "Not the specified device, return immediately!\n");
|
2011-06-17 19:35:11 +08: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-20 02:44:00 +08:00
|
|
|
usb_stor_dbg(us, "Write memory fail\n");
|
2011-06-17 19:35:11 +08:00
|
|
|
} else {
|
2013-04-20 02:44:00 +08:00
|
|
|
usb_stor_dbg(us, "Read memory fail, OR (val & 0x1F) != 0\n");
|
2011-06-17 19:35:11 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-10-02 07:10:57 +08:00
|
|
|
#endif
|
2011-06-17 19:35:11 +08: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-20 02:44:00 +08:00
|
|
|
usb_stor_dbg(us, "Not the specified device, return immediately!\n");
|
2011-06-17 19:35:11 +08: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-20 02:44:00 +08:00
|
|
|
usb_stor_dbg(us, "Write memory fail\n");
|
2011-06-17 19:35:11 +08:00
|
|
|
} else {
|
2013-04-20 02:44:00 +08:00
|
|
|
usb_stor_dbg(us, "Read memory fail\n");
|
2011-06-17 19:35:11 +08:00
|
|
|
}
|
|
|
|
} else {
|
2013-04-20 02:44:00 +08:00
|
|
|
usb_stor_dbg(us, "Read memory fail, OR (buf[0]&0x24)!=0x24\n");
|
2011-06-17 19:35:11 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void rts51x_modi_suspend_timer(struct rts51x_chip *chip)
|
|
|
|
{
|
2013-04-20 02:44:00 +08:00
|
|
|
struct us_data *us = chip->us;
|
|
|
|
|
|
|
|
usb_stor_dbg(us, "state:%d\n", rts51x_get_stat(chip));
|
2011-06-17 19:35:11 +08:00
|
|
|
|
|
|
|
chip->timer_expires = jiffies + msecs_to_jiffies(1000*ss_delay);
|
|
|
|
mod_timer(&chip->rts51x_suspend_timer, chip->timer_expires);
|
|
|
|
}
|
|
|
|
|
treewide: setup_timer() -> timer_setup()
This converts all remaining cases of the old setup_timer() API into using
timer_setup(), where the callback argument is the structure already
holding the struct timer_list. These should have no behavioral changes,
since they just change which pointer is passed into the callback with
the same available pointers after conversion. It handles the following
examples, in addition to some other variations.
Casting from unsigned long:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
setup_timer(&ptr->my_timer, my_callback, ptr);
and forced object casts:
void my_callback(struct something *ptr)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, (unsigned long)ptr);
become:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
Direct function assignments:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
ptr->my_timer.function = my_callback;
have a temporary cast added, along with converting the args:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
ptr->my_timer.function = (TIMER_FUNC_TYPE)my_callback;
And finally, callbacks without a data assignment:
void my_callback(unsigned long data)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, 0);
have their argument renamed to verify they're unused during conversion:
void my_callback(struct timer_list *unused)
{
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
The conversion is done with the following Coccinelle script:
spatch --very-quiet --all-includes --include-headers \
-I ./arch/x86/include -I ./arch/x86/include/generated \
-I ./include -I ./arch/x86/include/uapi \
-I ./arch/x86/include/generated/uapi -I ./include/uapi \
-I ./include/generated/uapi --include ./include/linux/kconfig.h \
--dir . \
--cocci-file ~/src/data/timer_setup.cocci
@fix_address_of@
expression e;
@@
setup_timer(
-&(e)
+&e
, ...)
// Update any raw setup_timer() usages that have a NULL callback, but
// would otherwise match change_timer_function_usage, since the latter
// will update all function assignments done in the face of a NULL
// function initialization in setup_timer().
@change_timer_function_usage_NULL@
expression _E;
identifier _timer;
type _cast_data;
@@
(
-setup_timer(&_E->_timer, NULL, _E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E->_timer, NULL, (_cast_data)_E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, &_E);
+timer_setup(&_E._timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, (_cast_data)&_E);
+timer_setup(&_E._timer, NULL, 0);
)
@change_timer_function_usage@
expression _E;
identifier _timer;
struct timer_list _stl;
identifier _callback;
type _cast_func, _cast_data;
@@
(
-setup_timer(&_E->_timer, _callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
_E->_timer@_stl.function = _callback;
|
_E->_timer@_stl.function = &_callback;
|
_E->_timer@_stl.function = (_cast_func)_callback;
|
_E->_timer@_stl.function = (_cast_func)&_callback;
|
_E._timer@_stl.function = _callback;
|
_E._timer@_stl.function = &_callback;
|
_E._timer@_stl.function = (_cast_func)_callback;
|
_E._timer@_stl.function = (_cast_func)&_callback;
)
// callback(unsigned long arg)
@change_callback_handle_cast
depends on change_timer_function_usage@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
identifier _handle;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
(
... when != _origarg
_handletype *_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
)
}
// callback(unsigned long arg) without existing variable
@change_callback_handle_cast_no_arg
depends on change_timer_function_usage &&
!change_callback_handle_cast@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
+ _handletype *_origarg = from_timer(_origarg, t, _timer);
+
... when != _origarg
- (_handletype *)_origarg
+ _origarg
... when != _origarg
}
// Avoid already converted callbacks.
@match_callback_converted
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier t;
@@
void _callback(struct timer_list *t)
{ ... }
// callback(struct something *handle)
@change_callback_handle_arg
depends on change_timer_function_usage &&
!match_callback_converted &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
@@
void _callback(
-_handletype *_handle
+struct timer_list *t
)
{
+ _handletype *_handle = from_timer(_handle, t, _timer);
...
}
// If change_callback_handle_arg ran on an empty function, remove
// the added handler.
@unchange_callback_handle_arg
depends on change_timer_function_usage &&
change_callback_handle_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
identifier t;
@@
void _callback(struct timer_list *t)
{
- _handletype *_handle = from_timer(_handle, t, _timer);
}
// We only want to refactor the setup_timer() data argument if we've found
// the matching callback. This undoes changes in change_timer_function_usage.
@unchange_timer_function_usage
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg &&
!change_callback_handle_arg@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type change_timer_function_usage._cast_data;
@@
(
-timer_setup(&_E->_timer, _callback, 0);
+setup_timer(&_E->_timer, _callback, (_cast_data)_E);
|
-timer_setup(&_E._timer, _callback, 0);
+setup_timer(&_E._timer, _callback, (_cast_data)&_E);
)
// If we fixed a callback from a .function assignment, fix the
// assignment cast now.
@change_timer_function_assignment
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_func;
typedef TIMER_FUNC_TYPE;
@@
(
_E->_timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-&_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
)
// Sometimes timer functions are called directly. Replace matched args.
@change_timer_function_calls
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression _E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_data;
@@
_callback(
(
-(_cast_data)_E
+&_E->_timer
|
-(_cast_data)&_E
+&_E._timer
|
-_E
+&_E->_timer
)
)
// If a timer has been configured without a data argument, it can be
// converted without regard to the callback argument, since it is unused.
@match_timer_function_unused_data@
expression _E;
identifier _timer;
identifier _callback;
@@
(
-setup_timer(&_E->_timer, _callback, 0);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0L);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0UL);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0L);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0UL);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0L);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0UL);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0L);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0UL);
+timer_setup(_timer, _callback, 0);
)
@change_callback_unused_data
depends on match_timer_function_unused_data@
identifier match_timer_function_unused_data._callback;
type _origtype;
identifier _origarg;
@@
void _callback(
-_origtype _origarg
+struct timer_list *unused
)
{
... when != _origarg
}
Signed-off-by: Kees Cook <keescook@chromium.org>
2017-10-17 05:43:17 +08:00
|
|
|
static void rts51x_suspend_timer_fn(struct timer_list *t)
|
2011-06-17 19:35:11 +08:00
|
|
|
{
|
treewide: setup_timer() -> timer_setup()
This converts all remaining cases of the old setup_timer() API into using
timer_setup(), where the callback argument is the structure already
holding the struct timer_list. These should have no behavioral changes,
since they just change which pointer is passed into the callback with
the same available pointers after conversion. It handles the following
examples, in addition to some other variations.
Casting from unsigned long:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
setup_timer(&ptr->my_timer, my_callback, ptr);
and forced object casts:
void my_callback(struct something *ptr)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, (unsigned long)ptr);
become:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
Direct function assignments:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
ptr->my_timer.function = my_callback;
have a temporary cast added, along with converting the args:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
ptr->my_timer.function = (TIMER_FUNC_TYPE)my_callback;
And finally, callbacks without a data assignment:
void my_callback(unsigned long data)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, 0);
have their argument renamed to verify they're unused during conversion:
void my_callback(struct timer_list *unused)
{
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
The conversion is done with the following Coccinelle script:
spatch --very-quiet --all-includes --include-headers \
-I ./arch/x86/include -I ./arch/x86/include/generated \
-I ./include -I ./arch/x86/include/uapi \
-I ./arch/x86/include/generated/uapi -I ./include/uapi \
-I ./include/generated/uapi --include ./include/linux/kconfig.h \
--dir . \
--cocci-file ~/src/data/timer_setup.cocci
@fix_address_of@
expression e;
@@
setup_timer(
-&(e)
+&e
, ...)
// Update any raw setup_timer() usages that have a NULL callback, but
// would otherwise match change_timer_function_usage, since the latter
// will update all function assignments done in the face of a NULL
// function initialization in setup_timer().
@change_timer_function_usage_NULL@
expression _E;
identifier _timer;
type _cast_data;
@@
(
-setup_timer(&_E->_timer, NULL, _E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E->_timer, NULL, (_cast_data)_E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, &_E);
+timer_setup(&_E._timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, (_cast_data)&_E);
+timer_setup(&_E._timer, NULL, 0);
)
@change_timer_function_usage@
expression _E;
identifier _timer;
struct timer_list _stl;
identifier _callback;
type _cast_func, _cast_data;
@@
(
-setup_timer(&_E->_timer, _callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
_E->_timer@_stl.function = _callback;
|
_E->_timer@_stl.function = &_callback;
|
_E->_timer@_stl.function = (_cast_func)_callback;
|
_E->_timer@_stl.function = (_cast_func)&_callback;
|
_E._timer@_stl.function = _callback;
|
_E._timer@_stl.function = &_callback;
|
_E._timer@_stl.function = (_cast_func)_callback;
|
_E._timer@_stl.function = (_cast_func)&_callback;
)
// callback(unsigned long arg)
@change_callback_handle_cast
depends on change_timer_function_usage@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
identifier _handle;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
(
... when != _origarg
_handletype *_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
)
}
// callback(unsigned long arg) without existing variable
@change_callback_handle_cast_no_arg
depends on change_timer_function_usage &&
!change_callback_handle_cast@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
+ _handletype *_origarg = from_timer(_origarg, t, _timer);
+
... when != _origarg
- (_handletype *)_origarg
+ _origarg
... when != _origarg
}
// Avoid already converted callbacks.
@match_callback_converted
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier t;
@@
void _callback(struct timer_list *t)
{ ... }
// callback(struct something *handle)
@change_callback_handle_arg
depends on change_timer_function_usage &&
!match_callback_converted &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
@@
void _callback(
-_handletype *_handle
+struct timer_list *t
)
{
+ _handletype *_handle = from_timer(_handle, t, _timer);
...
}
// If change_callback_handle_arg ran on an empty function, remove
// the added handler.
@unchange_callback_handle_arg
depends on change_timer_function_usage &&
change_callback_handle_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
identifier t;
@@
void _callback(struct timer_list *t)
{
- _handletype *_handle = from_timer(_handle, t, _timer);
}
// We only want to refactor the setup_timer() data argument if we've found
// the matching callback. This undoes changes in change_timer_function_usage.
@unchange_timer_function_usage
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg &&
!change_callback_handle_arg@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type change_timer_function_usage._cast_data;
@@
(
-timer_setup(&_E->_timer, _callback, 0);
+setup_timer(&_E->_timer, _callback, (_cast_data)_E);
|
-timer_setup(&_E._timer, _callback, 0);
+setup_timer(&_E._timer, _callback, (_cast_data)&_E);
)
// If we fixed a callback from a .function assignment, fix the
// assignment cast now.
@change_timer_function_assignment
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_func;
typedef TIMER_FUNC_TYPE;
@@
(
_E->_timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-&_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
)
// Sometimes timer functions are called directly. Replace matched args.
@change_timer_function_calls
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression _E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_data;
@@
_callback(
(
-(_cast_data)_E
+&_E->_timer
|
-(_cast_data)&_E
+&_E._timer
|
-_E
+&_E->_timer
)
)
// If a timer has been configured without a data argument, it can be
// converted without regard to the callback argument, since it is unused.
@match_timer_function_unused_data@
expression _E;
identifier _timer;
identifier _callback;
@@
(
-setup_timer(&_E->_timer, _callback, 0);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0L);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0UL);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0L);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0UL);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0L);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0UL);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0L);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0UL);
+timer_setup(_timer, _callback, 0);
)
@change_callback_unused_data
depends on match_timer_function_unused_data@
identifier match_timer_function_unused_data._callback;
type _origtype;
identifier _origarg;
@@
void _callback(
-_origtype _origarg
+struct timer_list *unused
)
{
... when != _origarg
}
Signed-off-by: Kees Cook <keescook@chromium.org>
2017-10-17 05:43:17 +08:00
|
|
|
struct rts51x_chip *chip = from_timer(chip, t, rts51x_suspend_timer);
|
2011-06-17 19:35:11 +08:00
|
|
|
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: 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-20 01:52:38 +08:00
|
|
|
usb_stor_dbg(us, "RTS51X_STAT_SS, power.usage:%d\n",
|
2013-04-20 02:44:00 +08:00
|
|
|
atomic_read(&us->pusb_intf->dev.power.usage_count));
|
2011-06-17 19:35:11 +08:00
|
|
|
|
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-20 01:52:38 +08:00
|
|
|
if (atomic_read(&us->pusb_intf->dev.power.usage_count) > 0) {
|
2013-04-20 02:44:00 +08:00
|
|
|
usb_stor_dbg(us, "Ready to enter SS state\n");
|
2011-06-17 19:35:11 +08: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 19:29:42 +08:00
|
|
|
usb_autopm_put_interface_async(us->pusb_intf);
|
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-20 01:52:38 +08:00
|
|
|
usb_stor_dbg(us, "RTS51X_STAT_SS 01, power.usage:%d\n",
|
2013-04-20 02:44:00 +08:00
|
|
|
atomic_read(&us->pusb_intf->dev.power.usage_count));
|
2011-06-17 19:35:11 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2013-04-20 02:44:00 +08:00
|
|
|
usb_stor_dbg(us, "Unknown state !!!\n");
|
2011-06-17 19:35:11 +08: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 15:53:37 +08:00
|
|
|
static void rts51x_invoke_transport(struct scsi_cmnd *srb, struct us_data *us)
|
2011-06-17 19:35:11 +08: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)) {
|
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-20 01:52:38 +08:00
|
|
|
usb_stor_dbg(us, "working scsi, power.usage:%d\n",
|
2013-04-20 02:44:00 +08:00
|
|
|
atomic_read(&us->pusb_intf->dev.power.usage_count));
|
2011-06-17 19:35:11 +08:00
|
|
|
|
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-20 01:52:38 +08:00
|
|
|
if (atomic_read(&us->pusb_intf->dev.power.usage_count) <= 0) {
|
2011-06-17 19:35:11 +08:00
|
|
|
ret = usb_autopm_get_interface(us->pusb_intf);
|
2013-04-20 02:44:00 +08:00
|
|
|
usb_stor_dbg(us, "working scsi, ret=%d\n", ret);
|
2011-06-17 19:35:11 +08: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-20 02:44:00 +08:00
|
|
|
usb_stor_dbg(us, "NOT working scsi\n");
|
2011-06-17 19:35:11 +08: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-20 02:44:00 +08:00
|
|
|
usb_stor_dbg(us, "TEST_UNIT_READY\n");
|
2011-06-17 19:35:11 +08: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-20 02:44:00 +08:00
|
|
|
usb_stor_dbg(us, "ALLOW_MEDIUM_REMOVAL\n");
|
2011-06-17 19:35:11 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
} else {
|
2013-04-20 02:44:00 +08:00
|
|
|
usb_stor_dbg(us, "NOT working scsi, not SS\n");
|
2011-06-17 19:35:11 +08:00
|
|
|
chip->proto_handler_backup(srb, us);
|
2012-09-20 09:48:00 +08:00
|
|
|
/* Check whether card is plugged in */
|
2011-06-17 19:35:11 +08: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-20 02:44:00 +08:00
|
|
|
usb_stor_dbg(us, "state:%d\n", rts51x_get_stat(chip));
|
2011-06-17 19:35:11 +08: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-20 02:44:00 +08:00
|
|
|
usb_stor_dbg(us, "Read status fail\n");
|
2011-06-17 19:35:11 +08: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;
|
treewide: setup_timer() -> timer_setup()
This converts all remaining cases of the old setup_timer() API into using
timer_setup(), where the callback argument is the structure already
holding the struct timer_list. These should have no behavioral changes,
since they just change which pointer is passed into the callback with
the same available pointers after conversion. It handles the following
examples, in addition to some other variations.
Casting from unsigned long:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
setup_timer(&ptr->my_timer, my_callback, ptr);
and forced object casts:
void my_callback(struct something *ptr)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, (unsigned long)ptr);
become:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
Direct function assignments:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
ptr->my_timer.function = my_callback;
have a temporary cast added, along with converting the args:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
ptr->my_timer.function = (TIMER_FUNC_TYPE)my_callback;
And finally, callbacks without a data assignment:
void my_callback(unsigned long data)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, 0);
have their argument renamed to verify they're unused during conversion:
void my_callback(struct timer_list *unused)
{
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
The conversion is done with the following Coccinelle script:
spatch --very-quiet --all-includes --include-headers \
-I ./arch/x86/include -I ./arch/x86/include/generated \
-I ./include -I ./arch/x86/include/uapi \
-I ./arch/x86/include/generated/uapi -I ./include/uapi \
-I ./include/generated/uapi --include ./include/linux/kconfig.h \
--dir . \
--cocci-file ~/src/data/timer_setup.cocci
@fix_address_of@
expression e;
@@
setup_timer(
-&(e)
+&e
, ...)
// Update any raw setup_timer() usages that have a NULL callback, but
// would otherwise match change_timer_function_usage, since the latter
// will update all function assignments done in the face of a NULL
// function initialization in setup_timer().
@change_timer_function_usage_NULL@
expression _E;
identifier _timer;
type _cast_data;
@@
(
-setup_timer(&_E->_timer, NULL, _E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E->_timer, NULL, (_cast_data)_E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, &_E);
+timer_setup(&_E._timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, (_cast_data)&_E);
+timer_setup(&_E._timer, NULL, 0);
)
@change_timer_function_usage@
expression _E;
identifier _timer;
struct timer_list _stl;
identifier _callback;
type _cast_func, _cast_data;
@@
(
-setup_timer(&_E->_timer, _callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
_E->_timer@_stl.function = _callback;
|
_E->_timer@_stl.function = &_callback;
|
_E->_timer@_stl.function = (_cast_func)_callback;
|
_E->_timer@_stl.function = (_cast_func)&_callback;
|
_E._timer@_stl.function = _callback;
|
_E._timer@_stl.function = &_callback;
|
_E._timer@_stl.function = (_cast_func)_callback;
|
_E._timer@_stl.function = (_cast_func)&_callback;
)
// callback(unsigned long arg)
@change_callback_handle_cast
depends on change_timer_function_usage@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
identifier _handle;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
(
... when != _origarg
_handletype *_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
)
}
// callback(unsigned long arg) without existing variable
@change_callback_handle_cast_no_arg
depends on change_timer_function_usage &&
!change_callback_handle_cast@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
+ _handletype *_origarg = from_timer(_origarg, t, _timer);
+
... when != _origarg
- (_handletype *)_origarg
+ _origarg
... when != _origarg
}
// Avoid already converted callbacks.
@match_callback_converted
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier t;
@@
void _callback(struct timer_list *t)
{ ... }
// callback(struct something *handle)
@change_callback_handle_arg
depends on change_timer_function_usage &&
!match_callback_converted &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
@@
void _callback(
-_handletype *_handle
+struct timer_list *t
)
{
+ _handletype *_handle = from_timer(_handle, t, _timer);
...
}
// If change_callback_handle_arg ran on an empty function, remove
// the added handler.
@unchange_callback_handle_arg
depends on change_timer_function_usage &&
change_callback_handle_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
identifier t;
@@
void _callback(struct timer_list *t)
{
- _handletype *_handle = from_timer(_handle, t, _timer);
}
// We only want to refactor the setup_timer() data argument if we've found
// the matching callback. This undoes changes in change_timer_function_usage.
@unchange_timer_function_usage
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg &&
!change_callback_handle_arg@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type change_timer_function_usage._cast_data;
@@
(
-timer_setup(&_E->_timer, _callback, 0);
+setup_timer(&_E->_timer, _callback, (_cast_data)_E);
|
-timer_setup(&_E._timer, _callback, 0);
+setup_timer(&_E._timer, _callback, (_cast_data)&_E);
)
// If we fixed a callback from a .function assignment, fix the
// assignment cast now.
@change_timer_function_assignment
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_func;
typedef TIMER_FUNC_TYPE;
@@
(
_E->_timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-&_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
)
// Sometimes timer functions are called directly. Replace matched args.
@change_timer_function_calls
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression _E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_data;
@@
_callback(
(
-(_cast_data)_E
+&_E->_timer
|
-(_cast_data)&_E
+&_E._timer
|
-_E
+&_E->_timer
)
)
// If a timer has been configured without a data argument, it can be
// converted without regard to the callback argument, since it is unused.
@match_timer_function_unused_data@
expression _E;
identifier _timer;
identifier _callback;
@@
(
-setup_timer(&_E->_timer, _callback, 0);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0L);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0UL);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0L);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0UL);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0L);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0UL);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0L);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0UL);
+timer_setup(_timer, _callback, 0);
)
@change_callback_unused_data
depends on match_timer_function_unused_data@
identifier match_timer_function_unused_data._callback;
type _origtype;
identifier _origarg;
@@
void _callback(
-_origtype _origarg
+struct timer_list *unused
)
{
... when != _origarg
}
Signed-off-by: Kees Cook <keescook@chromium.org>
2017-10-17 05:43:17 +08:00
|
|
|
timer_setup(&chip->rts51x_suspend_timer, rts51x_suspend_timer_fn, 0);
|
2011-06-17 19:35:11 +08:00
|
|
|
fw5895_init(us);
|
|
|
|
|
2014-09-18 17:25:04 +08:00
|
|
|
/* enable autosuspend function of the usb device */
|
2011-06-17 19:35:11 +08:00
|
|
|
usb_enable_autosuspend(us->pusb_dev);
|
|
|
|
|
2011-01-14 16:53:34 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2011-06-17 19:35:11 +08:00
|
|
|
#endif
|
2011-01-14 16:53:34 +08:00
|
|
|
|
|
|
|
static void realtek_cr_destructor(void *extra)
|
|
|
|
{
|
2013-05-01 06:26:44 +08:00
|
|
|
struct rts51x_chip *chip = extra;
|
2011-06-17 19:35:11 +08:00
|
|
|
|
2011-01-14 16:53:34 +08:00
|
|
|
if (!chip)
|
|
|
|
return;
|
2013-04-20 02:44:00 +08:00
|
|
|
|
2011-06-17 19:35:11 +08:00
|
|
|
#ifdef CONFIG_REALTEK_AUTOPM
|
|
|
|
if (ss_en) {
|
|
|
|
del_timer(&chip->rts51x_suspend_timer);
|
|
|
|
chip->timer_expires = 0;
|
|
|
|
}
|
|
|
|
#endif
|
2011-01-14 16:53:34 +08:00
|
|
|
kfree(chip->status);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_PM
|
2011-11-15 15:53:37 +08:00
|
|
|
static int realtek_cr_suspend(struct usb_interface *iface, pm_message_t message)
|
2011-01-14 16:53:34 +08:00
|
|
|
{
|
2011-06-17 19:35:11 +08: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 16:53:34 +08:00
|
|
|
}
|
2011-06-17 19:35:11 +08: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 16:53:34 +08: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 10:49:13 +08:00
|
|
|
chip->us = us;
|
2011-01-14 16:53:34 +08:00
|
|
|
|
2013-04-20 02:44:00 +08:00
|
|
|
usb_stor_dbg(us, "chip->max_lun = %d\n", chip->max_lun);
|
2011-01-14 16:53:34 +08: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 19:35:09 +08:00
|
|
|
retval = rts51x_check_status(us, (u8) i);
|
2011-01-14 16:53:34 +08:00
|
|
|
if (retval < 0)
|
|
|
|
goto INIT_FAIL;
|
|
|
|
}
|
|
|
|
|
2019-08-28 01:34:50 +08:00
|
|
|
if (CHECK_PID(chip, 0x0138) || CHECK_PID(chip, 0x0158) ||
|
|
|
|
CHECK_PID(chip, 0x0159)) {
|
|
|
|
if (CHECK_FW_VER(chip, 0x5888) || CHECK_FW_VER(chip, 0x5889) ||
|
|
|
|
CHECK_FW_VER(chip, 0x5901))
|
2011-01-14 16:53:34 +08:00
|
|
|
SET_AUTO_DELINK(chip);
|
2019-08-28 01:34:50 +08:00
|
|
|
if (STATUS_LEN(chip) == 16) {
|
|
|
|
if (SUPPORT_AUTO_DELINK(chip))
|
|
|
|
SET_AUTO_DELINK(chip);
|
|
|
|
}
|
2011-01-14 16:53:34 +08:00
|
|
|
}
|
2011-06-17 19:35:11 +08:00
|
|
|
#ifdef CONFIG_REALTEK_AUTOPM
|
2013-04-27 10:49:13 +08:00
|
|
|
if (ss_en)
|
2011-06-17 19:35:11 +08:00
|
|
|
realtek_cr_autosuspend_setup(us);
|
|
|
|
#endif
|
2011-01-14 16:53:34 +08:00
|
|
|
|
2013-04-20 02:44:00 +08:00
|
|
|
usb_stor_dbg(us, "chip->flag = 0x%x\n", chip->flag);
|
2011-01-14 16:53:34 +08: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 17:24:21 +08:00
|
|
|
static struct scsi_host_template realtek_cr_host_template;
|
|
|
|
|
2011-01-14 16:53:34 +08:00
|
|
|
static int realtek_cr_probe(struct usb_interface *intf,
|
2011-06-17 19:35:09 +08:00
|
|
|
const struct usb_device_id *id)
|
2011-01-14 16:53:34 +08:00
|
|
|
{
|
|
|
|
struct us_data *us;
|
|
|
|
int result;
|
|
|
|
|
2013-04-20 02:44:00 +08:00
|
|
|
dev_dbg(&intf->dev, "Probe Realtek Card Reader!\n");
|
2011-01-14 16:53:34 +08:00
|
|
|
|
|
|
|
result = usb_stor_probe1(&us, intf, id,
|
2011-06-17 19:35:09 +08:00
|
|
|
(id - realtek_cr_ids) +
|
2015-05-06 17:24:21 +08:00
|
|
|
realtek_cr_unusual_dev_list,
|
|
|
|
&realtek_cr_host_template);
|
2011-01-14 16:53:34 +08:00
|
|
|
if (result)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
result = usb_stor_probe2(us);
|
2011-06-17 19:35:09 +08:00
|
|
|
|
2011-01-14 16:53:34 +08:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct usb_driver realtek_cr_driver = {
|
2015-05-06 17:24:21 +08:00
|
|
|
.name = DRV_NAME,
|
2011-06-17 19:35:09 +08:00
|
|
|
.probe = realtek_cr_probe,
|
|
|
|
.disconnect = usb_stor_disconnect,
|
2011-06-17 19:35:11 +08:00
|
|
|
/* .suspend = usb_stor_suspend, */
|
|
|
|
/* .resume = usb_stor_resume, */
|
2011-06-17 19:35:09 +08:00
|
|
|
.reset_resume = usb_stor_reset_resume,
|
2011-06-17 19:35:11 +08:00
|
|
|
.suspend = realtek_cr_suspend,
|
|
|
|
.resume = realtek_cr_resume,
|
2011-06-17 19:35:09 +08:00
|
|
|
.pre_reset = usb_stor_pre_reset,
|
|
|
|
.post_reset = usb_stor_post_reset,
|
|
|
|
.id_table = realtek_cr_ids,
|
|
|
|
.soft_unbind = 1,
|
2011-06-17 19:35:11 +08:00
|
|
|
.supports_autosuspend = 1,
|
2012-01-14 10:15:21 +08:00
|
|
|
.no_dynamic_id = 1,
|
2011-01-14 16:53:34 +08:00
|
|
|
};
|
|
|
|
|
2015-05-06 17:24:21 +08:00
|
|
|
module_usb_stor_driver(realtek_cr_driver, realtek_cr_host_template, DRV_NAME);
|