2011-07-19 11:10:35 +02:00
|
|
|
/* Driver for Realtek RTS51xx USB card reader
|
|
|
|
*
|
|
|
|
* Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License as published by the
|
|
|
|
* Free Software Foundation; either version 2, or (at your option) any
|
|
|
|
* later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
* Author:
|
|
|
|
* wwang (wei_wang@realsil.com.cn)
|
|
|
|
* No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
|
|
|
|
* Maintainer:
|
|
|
|
* Edwin Rong (edwin_rong@realsil.com.cn)
|
|
|
|
* No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "rts51x.h"
|
|
|
|
|
|
|
|
#ifdef SUPPORT_FILE_OP
|
|
|
|
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/stat.h>
|
|
|
|
#include <linux/kref.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
|
|
|
|
#include "rts51x_chip.h"
|
|
|
|
#include "rts51x_card.h"
|
|
|
|
#include "rts51x_fop.h"
|
|
|
|
#include "sd_cprm.h"
|
|
|
|
|
|
|
|
#define RTS5139_IOC_MAGIC 0x39
|
|
|
|
|
|
|
|
#define RTS5139_IOC_SD_DIRECT _IOWR(RTS5139_IOC_MAGIC, 0xA0, int)
|
|
|
|
#define RTS5139_IOC_SD_GET_RSP _IOWR(RTS5139_IOC_MAGIC, 0xA1, int)
|
|
|
|
|
|
|
|
static int rts51x_sd_direct_cmnd(struct rts51x_chip *chip,
|
|
|
|
struct sd_direct_cmnd *cmnd)
|
|
|
|
{
|
|
|
|
int retval;
|
|
|
|
u8 dir, cmd12, standby, acmd, cmd_idx, rsp_code;
|
|
|
|
u8 *buf;
|
|
|
|
u32 arg, len;
|
|
|
|
|
|
|
|
dir = (cmnd->cmnd[0] >> 3) & 0x03;
|
|
|
|
cmd12 = (cmnd->cmnd[0] >> 2) & 0x01;
|
|
|
|
standby = (cmnd->cmnd[0] >> 1) & 0x01;
|
|
|
|
acmd = cmnd->cmnd[0] & 0x01;
|
|
|
|
cmd_idx = cmnd->cmnd[1];
|
|
|
|
arg = ((u32) (cmnd->cmnd[2]) << 24) | ((u32) (cmnd->cmnd[3]) << 16) |
|
|
|
|
((u32) (cmnd->cmnd[4]) << 8) | cmnd->cmnd[5];
|
|
|
|
len =
|
|
|
|
((u32) (cmnd->cmnd[6]) << 16) | ((u32) (cmnd->cmnd[7]) << 8) |
|
|
|
|
cmnd->cmnd[8];
|
|
|
|
rsp_code = cmnd->cmnd[9];
|
|
|
|
|
|
|
|
if (dir) {
|
|
|
|
if (!cmnd->buf || (cmnd->buf_len < len))
|
|
|
|
TRACE_RET(chip, STATUS_FAIL);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (dir) {
|
|
|
|
case 0:
|
|
|
|
/* No data */
|
2012-10-19 11:43:34 +02:00
|
|
|
retval = ext_rts51x_sd_execute_no_data(chip, chip->card2lun[SD_CARD],
|
2011-07-19 11:10:35 +02:00
|
|
|
cmd_idx, standby, acmd,
|
|
|
|
rsp_code, arg);
|
|
|
|
if (retval != TRANSPORT_GOOD)
|
|
|
|
TRACE_RET(chip, STATUS_FAIL);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
/* Read from card */
|
2012-09-23 18:31:30 +02:00
|
|
|
buf = kzalloc(cmnd->buf_len, GFP_KERNEL);
|
2011-07-19 11:10:35 +02:00
|
|
|
if (!buf)
|
|
|
|
TRACE_RET(chip, STATUS_NOMEM);
|
|
|
|
|
2012-10-19 11:43:34 +02:00
|
|
|
retval = ext_rts51x_sd_execute_read_data(chip, chip->card2lun[SD_CARD],
|
2011-07-19 11:10:35 +02:00
|
|
|
cmd_idx, cmd12, standby, acmd,
|
|
|
|
rsp_code, arg, len, buf,
|
|
|
|
cmnd->buf_len, 0);
|
|
|
|
if (retval != TRANSPORT_GOOD) {
|
|
|
|
kfree(buf);
|
|
|
|
TRACE_RET(chip, STATUS_FAIL);
|
|
|
|
}
|
|
|
|
|
|
|
|
retval =
|
|
|
|
copy_to_user((void *)cmnd->buf, (void *)buf, cmnd->buf_len);
|
|
|
|
if (retval) {
|
|
|
|
kfree(buf);
|
|
|
|
TRACE_RET(chip, STATUS_NOMEM);
|
|
|
|
}
|
|
|
|
|
|
|
|
kfree(buf);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
/* Write to card */
|
|
|
|
buf = kmalloc(cmnd->buf_len, GFP_KERNEL);
|
|
|
|
if (!buf)
|
|
|
|
TRACE_RET(chip, STATUS_NOMEM);
|
|
|
|
|
|
|
|
retval =
|
|
|
|
copy_from_user((void *)buf, (void *)cmnd->buf,
|
|
|
|
cmnd->buf_len);
|
|
|
|
if (retval) {
|
|
|
|
kfree(buf);
|
|
|
|
TRACE_RET(chip, STATUS_NOMEM);
|
|
|
|
}
|
|
|
|
|
|
|
|
retval =
|
2012-10-19 11:43:34 +02:00
|
|
|
ext_rts51x_sd_execute_write_data(chip, chip->card2lun[SD_CARD],
|
2011-07-19 11:10:35 +02:00
|
|
|
cmd_idx, cmd12, standby, acmd,
|
|
|
|
rsp_code, arg, len, buf,
|
|
|
|
cmnd->buf_len, 0);
|
|
|
|
if (retval != TRANSPORT_GOOD) {
|
|
|
|
kfree(buf);
|
|
|
|
TRACE_RET(chip, STATUS_FAIL);
|
|
|
|
}
|
|
|
|
|
|
|
|
kfree(buf);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
TRACE_RET(chip, STATUS_FAIL);
|
|
|
|
}
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int rts51x_sd_get_rsp(struct rts51x_chip *chip, struct sd_rsp *rsp)
|
|
|
|
{
|
|
|
|
struct sd_info *sd_card = &(chip->sd_card);
|
|
|
|
int count = 0, retval;
|
|
|
|
|
|
|
|
if (sd_card->pre_cmd_err) {
|
|
|
|
sd_card->pre_cmd_err = 0;
|
|
|
|
TRACE_RET(chip, STATUS_FAIL);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sd_card->last_rsp_type == SD_RSP_TYPE_R0)
|
|
|
|
TRACE_RET(chip, STATUS_FAIL);
|
|
|
|
else if (sd_card->last_rsp_type == SD_RSP_TYPE_R2)
|
|
|
|
count = (rsp->rsp_len < 17) ? rsp->rsp_len : 17;
|
|
|
|
else
|
|
|
|
count = (rsp->rsp_len < 6) ? rsp->rsp_len : 6;
|
|
|
|
|
|
|
|
retval = copy_to_user((void *)rsp->rsp, (void *)sd_card->rsp, count);
|
|
|
|
if (retval)
|
|
|
|
TRACE_RET(chip, STATUS_NOMEM);
|
|
|
|
|
|
|
|
RTS51X_DEBUGP("Response length: %d\n", count);
|
|
|
|
RTS51X_DEBUGP("Response: 0x%x 0x%x 0x%x 0x%x\n",
|
|
|
|
sd_card->rsp[0], sd_card->rsp[1], sd_card->rsp[2],
|
|
|
|
sd_card->rsp[3]);
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
int rts51x_open(struct inode *inode, struct file *filp)
|
|
|
|
{
|
|
|
|
struct rts51x_chip *chip;
|
|
|
|
struct usb_interface *interface;
|
|
|
|
int subminor;
|
|
|
|
int retval = 0;
|
|
|
|
|
|
|
|
subminor = iminor(inode);
|
|
|
|
|
|
|
|
interface = usb_find_interface(&rts51x_driver, subminor);
|
|
|
|
if (!interface) {
|
|
|
|
RTS51X_DEBUGP("%s - error, can't find device for minor %d\n",
|
|
|
|
__func__, subminor);
|
|
|
|
retval = -ENODEV;
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
chip = (struct rts51x_chip *)usb_get_intfdata(interface);
|
|
|
|
if (!chip) {
|
|
|
|
RTS51X_DEBUGP("Can't find chip\n");
|
|
|
|
retval = -ENODEV;
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Increase our reference to the host */
|
|
|
|
scsi_host_get(rts51x_to_host(chip));
|
|
|
|
|
|
|
|
/* lock the device pointers */
|
|
|
|
mutex_lock(&(chip->usb->dev_mutex));
|
|
|
|
|
|
|
|
/* save our object in the file's private structure */
|
|
|
|
filp->private_data = chip;
|
|
|
|
|
|
|
|
/* unlock the device pointers */
|
|
|
|
mutex_unlock(&chip->usb->dev_mutex);
|
|
|
|
|
|
|
|
exit:
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
int rts51x_release(struct inode *inode, struct file *filp)
|
|
|
|
{
|
|
|
|
struct rts51x_chip *chip;
|
|
|
|
|
|
|
|
chip = (struct rts51x_chip *)filp->private_data;
|
|
|
|
if (chip == NULL)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
/* Drop our reference to the host; the SCSI core will free it
|
|
|
|
* (and "chip" along with it) when the refcount becomes 0. */
|
|
|
|
scsi_host_put(rts51x_to_host(chip));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t rts51x_read(struct file *filp, char __user *buf, size_t count,
|
|
|
|
loff_t *f_pos)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t rts51x_write(struct file *filp, const char __user *buf, size_t count,
|
|
|
|
loff_t *f_pos)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
long rts51x_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|
|
|
{
|
|
|
|
struct rts51x_chip *chip;
|
|
|
|
struct sd_direct_cmnd cmnd;
|
|
|
|
struct sd_rsp rsp;
|
|
|
|
int retval = 0;
|
|
|
|
|
|
|
|
chip = (struct rts51x_chip *)filp->private_data;
|
|
|
|
if (chip == NULL)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
/* lock the device pointers */
|
|
|
|
mutex_lock(&(chip->usb->dev_mutex));
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case RTS5139_IOC_SD_DIRECT:
|
|
|
|
retval =
|
|
|
|
copy_from_user((void *)&cmnd, (void *)arg,
|
|
|
|
sizeof(struct sd_direct_cmnd));
|
|
|
|
if (retval) {
|
|
|
|
retval = -ENOMEM;
|
|
|
|
TRACE_GOTO(chip, exit);
|
|
|
|
}
|
|
|
|
retval = rts51x_sd_direct_cmnd(chip, &cmnd);
|
|
|
|
if (retval != STATUS_SUCCESS) {
|
|
|
|
retval = -EIO;
|
|
|
|
TRACE_GOTO(chip, exit);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RTS5139_IOC_SD_GET_RSP:
|
|
|
|
retval =
|
|
|
|
copy_from_user((void *)&rsp, (void *)arg,
|
|
|
|
sizeof(struct sd_rsp));
|
|
|
|
if (retval) {
|
|
|
|
retval = -ENOMEM;
|
|
|
|
TRACE_GOTO(chip, exit);
|
|
|
|
}
|
|
|
|
retval = rts51x_sd_get_rsp(chip, &rsp);
|
|
|
|
if (retval != STATUS_SUCCESS) {
|
|
|
|
retval = -EIO;
|
|
|
|
TRACE_GOTO(chip, exit);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
exit:
|
|
|
|
/* unlock the device pointers */
|
|
|
|
mutex_unlock(&chip->usb->dev_mutex);
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|