aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/rts5139/rts51x_fop.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/rts5139/rts51x_fop.c')
-rw-r--r--drivers/staging/rts5139/rts51x_fop.c298
1 files changed, 298 insertions, 0 deletions
diff --git a/drivers/staging/rts5139/rts51x_fop.c b/drivers/staging/rts5139/rts51x_fop.c
new file mode 100644
index 000000000000..6eaebb6223c9
--- /dev/null
+++ b/drivers/staging/rts5139/rts51x_fop.c
@@ -0,0 +1,298 @@
+/* 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"
+#include "rts51x.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_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 = kmalloc(cmnd->buf_len, GFP_KERNEL);
+ if (!buf)
+ TRACE_RET(chip, STATUS_NOMEM);
+
+ retval = ext_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_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;
+}
+
+#if 0 /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) */
+int rts51x_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg)
+#else
+long rts51x_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+#endif
+{
+ 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