Linux Audio

Check our new training course

Embedded Linux Audio

Check our new training course
with Creative Commons CC-BY-SA
lecture materials

Bootlin logo

Elixir Cross Referencer

Loading...
/* 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 */
		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