/* 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 . * * 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 #include #include #include #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 */ retval = ext_rts51x_sd_execute_no_data(chip, chip->card2lun[SD_CARD], cmd_idx, standby, acmd, rsp_code, arg); if (retval != TRANSPORT_GOOD) TRACE_RET(chip, STATUS_FAIL); break; case 1: /* Read from card */ buf = kzalloc(cmnd->buf_len, GFP_KERNEL); if (!buf) TRACE_RET(chip, STATUS_NOMEM); retval = ext_rts51x_sd_execute_read_data(chip, chip->card2lun[SD_CARD], 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 = ext_rts51x_sd_execute_write_data(chip, chip->card2lun[SD_CARD], 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