Kernel  |  4.1

下载     查看原文件
C++程序  |  3545行  |  76.67 KB
/* Driver for Realtek PCI-Express card reader
 *
 * Copyright(c) 2009-2013 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:
 *   Wei WANG (wei_wang@realsil.com.cn)
 *   Micky Ching (micky_ching@realsil.com.cn)
 */

#include <linux/blkdev.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/vmalloc.h>

#include "rtsx.h"
#include "sd.h"
#include "ms.h"
#include "spi.h"

void scsi_show_command(struct rtsx_chip *chip)
{
	struct scsi_cmnd *srb = chip->srb;
	char *what = NULL;
	bool unknown_cmd = false;
	int len;

	switch (srb->cmnd[0]) {
	case TEST_UNIT_READY:
		what = "TEST_UNIT_READY";
		break;
	case REZERO_UNIT:
		what = "REZERO_UNIT";
		break;
	case REQUEST_SENSE:
		what = "REQUEST_SENSE";
		break;
	case FORMAT_UNIT:
		what = "FORMAT_UNIT";
		break;
	case READ_BLOCK_LIMITS:
		what = "READ_BLOCK_LIMITS";
		break;
	case REASSIGN_BLOCKS:
		what = "REASSIGN_BLOCKS";
		break;
	case READ_6:
		what = "READ_6";
		break;
	case WRITE_6:
		what = "WRITE_6";
		break;
	case SEEK_6:
		what = "SEEK_6";
		break;
	case READ_REVERSE:
		what = "READ_REVERSE";
		break;
	case WRITE_FILEMARKS:
		what = "WRITE_FILEMARKS";
		break;
	case SPACE:
		what = "SPACE";
		break;
	case INQUIRY:
		what = "INQUIRY";
		break;
	case RECOVER_BUFFERED_DATA:
		what = "RECOVER_BUFFERED_DATA";
		break;
	case MODE_SELECT:
		what = "MODE_SELECT";
		break;
	case RESERVE:
		what = "RESERVE";
		break;
	case RELEASE:
		what = "RELEASE";
		break;
	case COPY:
		what = "COPY";
		break;
	case ERASE:
		what = "ERASE";
		break;
	case MODE_SENSE:
		what = "MODE_SENSE";
		break;
	case START_STOP:
		what = "START_STOP";
		break;
	case RECEIVE_DIAGNOSTIC:
		what = "RECEIVE_DIAGNOSTIC";
		break;
	case SEND_DIAGNOSTIC:
		what = "SEND_DIAGNOSTIC";
		break;
	case ALLOW_MEDIUM_REMOVAL:
		what = "ALLOW_MEDIUM_REMOVAL";
		break;
	case SET_WINDOW:
		what = "SET_WINDOW";
		break;
	case READ_CAPACITY:
		what = "READ_CAPACITY";
		break;
	case READ_10:
		what = "READ_10";
		break;
	case WRITE_10:
		what = "WRITE_10";
		break;
	case SEEK_10:
		what = "SEEK_10";
		break;
	case WRITE_VERIFY:
		what = "WRITE_VERIFY";
		break;
	case VERIFY:
		what = "VERIFY";
		break;
	case SEARCH_HIGH:
		what = "SEARCH_HIGH";
		break;
	case SEARCH_EQUAL:
		what = "SEARCH_EQUAL";
		break;
	case SEARCH_LOW:
		what = "SEARCH_LOW";
		break;
	case SET_LIMITS:
		what = "SET_LIMITS";
		break;
	case READ_POSITION:
		what = "READ_POSITION";
		break;
	case SYNCHRONIZE_CACHE:
		what = "SYNCHRONIZE_CACHE";
		break;
	case LOCK_UNLOCK_CACHE:
		what = "LOCK_UNLOCK_CACHE";
		break;
	case READ_DEFECT_DATA:
		what = "READ_DEFECT_DATA";
		break;
	case MEDIUM_SCAN:
		what = "MEDIUM_SCAN";
		break;
	case COMPARE:
		what = "COMPARE";
		break;
	case COPY_VERIFY:
		what = "COPY_VERIFY";
		break;
	case WRITE_BUFFER:
		what = "WRITE_BUFFER";
		break;
	case READ_BUFFER:
		what = "READ_BUFFER";
		break;
	case UPDATE_BLOCK:
		what = "UPDATE_BLOCK";
		break;
	case READ_LONG:
		what = "READ_LONG";
		break;
	case WRITE_LONG:
		what = "WRITE_LONG";
		break;
	case CHANGE_DEFINITION:
		what = "CHANGE_DEFINITION";
		break;
	case WRITE_SAME:
		what = "WRITE_SAME";
		break;
	case GPCMD_READ_SUBCHANNEL:
		what = "READ SUBCHANNEL";
		break;
	case READ_TOC:
		what = "READ_TOC";
		break;
	case GPCMD_READ_HEADER:
		what = "READ HEADER";
		break;
	case GPCMD_PLAY_AUDIO_10:
		what = "PLAY AUDIO (10)";
		break;
	case GPCMD_PLAY_AUDIO_MSF:
		what = "PLAY AUDIO MSF";
		break;
	case GPCMD_GET_EVENT_STATUS_NOTIFICATION:
		what = "GET EVENT/STATUS NOTIFICATION";
		break;
	case GPCMD_PAUSE_RESUME:
		what = "PAUSE/RESUME";
		break;
	case LOG_SELECT:
		what = "LOG_SELECT";
		break;
	case LOG_SENSE:
		what = "LOG_SENSE";
		break;
	case GPCMD_STOP_PLAY_SCAN:
		what = "STOP PLAY/SCAN";
		break;
	case GPCMD_READ_DISC_INFO:
		what = "READ DISC INFORMATION";
		break;
	case GPCMD_READ_TRACK_RZONE_INFO:
		what = "READ TRACK INFORMATION";
		break;
	case GPCMD_RESERVE_RZONE_TRACK:
		what = "RESERVE TRACK";
		break;
	case GPCMD_SEND_OPC:
		what = "SEND OPC";
		break;
	case MODE_SELECT_10:
		what = "MODE_SELECT_10";
		break;
	case GPCMD_REPAIR_RZONE_TRACK:
		what = "REPAIR TRACK";
		break;
	case 0x59:
		what = "READ MASTER CUE";
		break;
	case MODE_SENSE_10:
		what = "MODE_SENSE_10";
		break;
	case GPCMD_CLOSE_TRACK:
		what = "CLOSE TRACK/SESSION";
		break;
	case 0x5C:
		what = "READ BUFFER CAPACITY";
		break;
	case 0x5D:
		what = "SEND CUE SHEET";
		break;
	case GPCMD_BLANK:
		what = "BLANK";
		break;
	case REPORT_LUNS:
		what = "REPORT LUNS";
		break;
	case MOVE_MEDIUM:
		what = "MOVE_MEDIUM or PLAY AUDIO (12)";
		break;
	case READ_12:
		what = "READ_12";
		break;
	case WRITE_12:
		what = "WRITE_12";
		break;
	case WRITE_VERIFY_12:
		what = "WRITE_VERIFY_12";
		break;
	case SEARCH_HIGH_12:
		what = "SEARCH_HIGH_12";
		break;
	case SEARCH_EQUAL_12:
		what = "SEARCH_EQUAL_12";
		break;
	case SEARCH_LOW_12:
		what = "SEARCH_LOW_12";
		break;
	case SEND_VOLUME_TAG:
		what = "SEND_VOLUME_TAG";
		break;
	case READ_ELEMENT_STATUS:
		what = "READ_ELEMENT_STATUS";
		break;
	case GPCMD_READ_CD_MSF:
		what = "READ CD MSF";
		break;
	case GPCMD_SCAN:
		what = "SCAN";
		break;
	case GPCMD_SET_SPEED:
		what = "SET CD SPEED";
		break;
	case GPCMD_MECHANISM_STATUS:
		what = "MECHANISM STATUS";
		break;
	case GPCMD_READ_CD:
		what = "READ CD";
		break;
	case 0xE1:
		what = "WRITE CONTINUE";
		break;
	case WRITE_LONG_2:
		what = "WRITE_LONG_2";
		break;
	case VENDOR_CMND:
		what = "Realtek's vendor command";
		break;
	default:
		what = "(unknown command)";
		unknown_cmd = true;
		break;
	}

	if (srb->cmnd[0] != TEST_UNIT_READY)
		dev_dbg(rtsx_dev(chip), "Command %s (%d bytes)\n",
			what, srb->cmd_len);

	if (unknown_cmd) {
		len = min_t(unsigned short, srb->cmd_len, 16);
		dev_dbg(rtsx_dev(chip), "%*ph\n", len, srb->cmnd);
	}
}

void set_sense_type(struct rtsx_chip *chip, unsigned int lun, int sense_type)
{
	switch (sense_type) {
	case SENSE_TYPE_MEDIA_CHANGE:
		set_sense_data(chip, lun, CUR_ERR, 0x06, 0, 0x28, 0, 0, 0);
		break;

	case SENSE_TYPE_MEDIA_NOT_PRESENT:
		set_sense_data(chip, lun, CUR_ERR, 0x02, 0, 0x3A, 0, 0, 0);
		break;

	case SENSE_TYPE_MEDIA_LBA_OVER_RANGE:
		set_sense_data(chip, lun, CUR_ERR, 0x05, 0, 0x21, 0, 0, 0);
		break;

	case SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT:
		set_sense_data(chip, lun, CUR_ERR, 0x05, 0, 0x25, 0, 0, 0);
		break;

	case SENSE_TYPE_MEDIA_WRITE_PROTECT:
		set_sense_data(chip, lun, CUR_ERR, 0x07, 0, 0x27, 0, 0, 0);
		break;

	case SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR:
		set_sense_data(chip, lun, CUR_ERR, 0x03, 0, 0x11, 0, 0, 0);
		break;

	case SENSE_TYPE_MEDIA_WRITE_ERR:
		set_sense_data(chip, lun, CUR_ERR, 0x03, 0, 0x0C, 0x02, 0, 0);
		break;

	case SENSE_TYPE_MEDIA_INVALID_CMD_FIELD:
		set_sense_data(chip, lun, CUR_ERR, ILGAL_REQ, 0,
				ASC_INVLD_CDB, ASCQ_INVLD_CDB, CDB_ILLEGAL, 1);
		break;

	case SENSE_TYPE_FORMAT_IN_PROGRESS:
		set_sense_data(chip, lun, CUR_ERR, 0x02, 0, 0x04, 0x04, 0, 0);
		break;

	case SENSE_TYPE_FORMAT_CMD_FAILED:
		set_sense_data(chip, lun, CUR_ERR, 0x03, 0, 0x31, 0x01, 0, 0);
		break;

#ifdef SUPPORT_MAGIC_GATE
	case SENSE_TYPE_MG_KEY_FAIL_NOT_ESTAB:
		set_sense_data(chip, lun, CUR_ERR, 0x05, 0, 0x6F, 0x02, 0, 0);
		break;

	case SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN:
		set_sense_data(chip, lun, CUR_ERR, 0x05, 0, 0x6F, 0x00, 0, 0);
		break;

	case SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM:
		set_sense_data(chip, lun, CUR_ERR, 0x02, 0, 0x30, 0x00, 0, 0);
		break;

	case SENSE_TYPE_MG_WRITE_ERR:
		set_sense_data(chip, lun, CUR_ERR, 0x03, 0, 0x0C, 0x00, 0, 0);
		break;
#endif

#ifdef SUPPORT_SD_LOCK
	case SENSE_TYPE_MEDIA_READ_FORBIDDEN:
		set_sense_data(chip, lun, CUR_ERR, 0x07, 0, 0x11, 0x13, 0, 0);
		break;
#endif

	case SENSE_TYPE_NO_SENSE:
	default:
		set_sense_data(chip, lun, CUR_ERR, 0, 0, 0, 0, 0, 0);
		break;
	}
}

void set_sense_data(struct rtsx_chip *chip, unsigned int lun, u8 err_code,
		u8 sense_key, u32 info, u8 asc, u8 ascq, u8 sns_key_info0,
		u16 sns_key_info1)
{
	struct sense_data_t *sense = &(chip->sense_buffer[lun]);

	sense->err_code = err_code;
	sense->sense_key = sense_key;
	sense->info[0] = (u8)(info >> 24);
	sense->info[1] = (u8)(info >> 16);
	sense->info[2] = (u8)(info >> 8);
	sense->info[3] = (u8)info;

	sense->ad_sense_len = sizeof(struct sense_data_t) - 8;
	sense->asc = asc;
	sense->ascq = ascq;
	if (sns_key_info0 != 0) {
		sense->sns_key_info[0] = SKSV | sns_key_info0;
		sense->sns_key_info[1] = (sns_key_info1 & 0xf0) >> 8;
		sense->sns_key_info[2] = sns_key_info1 & 0x0f;
	}
}

static int test_unit_ready(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	unsigned int lun = SCSI_LUN(srb);

	if (!check_card_ready(chip, lun)) {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
		return TRANSPORT_FAILED;
	}

	if (!(CHK_BIT(chip->lun_mc, lun))) {
		SET_BIT(chip->lun_mc, lun);
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE);
		return TRANSPORT_FAILED;
	}

#ifdef SUPPORT_SD_LOCK
	if (get_lun_card(chip, SCSI_LUN(srb)) == SD_CARD) {
		struct sd_info *sd_card = &(chip->sd_card);

		if (sd_card->sd_lock_notify) {
			sd_card->sd_lock_notify = 0;
			set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE);
			return TRANSPORT_FAILED;
		} else if (sd_card->sd_lock_status & SD_LOCKED) {
			set_sense_type(chip, lun,
				SENSE_TYPE_MEDIA_READ_FORBIDDEN);
			return TRANSPORT_FAILED;
		}
	}
#endif

	return TRANSPORT_GOOD;
}

static unsigned char formatter_inquiry_str[20] = {
	'M', 'E', 'M', 'O', 'R', 'Y', 'S', 'T', 'I', 'C', 'K',
#ifdef SUPPORT_MAGIC_GATE
	'-', 'M', 'G', /* Byte[47:49] */
#else
	0x20, 0x20, 0x20,  /* Byte[47:49] */
#endif

#ifdef SUPPORT_MAGIC_GATE
	0x0B,  /* Byte[50]: MG, MS, MSPro, MSXC */
#else
	0x09,  /* Byte[50]: MS, MSPro, MSXC */
#endif
	0x00,  /* Byte[51]: Category Specific Commands */
	0x00,  /* Byte[52]: Access Control and feature */
	0x20, 0x20, 0x20, /* Byte[53:55] */
};

static int inquiry(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	unsigned int lun = SCSI_LUN(srb);
	char *inquiry_default = (char *)"Generic-xD/SD/M.S.      1.00 ";
	char *inquiry_sdms =    (char *)"Generic-SD/MemoryStick  1.00 ";
	char *inquiry_sd =      (char *)"Generic-SD/MMC          1.00 ";
	char *inquiry_ms =      (char *)"Generic-MemoryStick     1.00 ";
	char *inquiry_string;
	unsigned char sendbytes;
	unsigned char *buf;
	u8 card = get_lun_card(chip, lun);
	bool pro_formatter_flag = false;
	unsigned char inquiry_buf[] = {
		QULIFIRE|DRCT_ACCESS_DEV,
		RMB_DISC|0x0D,
		0x00,
		0x01,
		0x1f,
		0x02,
		0,
		REL_ADR|WBUS_32|WBUS_16|SYNC|LINKED|CMD_QUE|SFT_RE,
	};

	if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
		if (chip->lun2card[lun] == SD_CARD)
			inquiry_string = inquiry_sd;
		else
			inquiry_string = inquiry_ms;

	} else if (CHECK_LUN_MODE(chip, SD_MS_1LUN)) {
		inquiry_string = inquiry_sdms;
	} else {
		inquiry_string = inquiry_default;
	}

	buf = vmalloc(scsi_bufflen(srb));
	if (buf == NULL) {
		rtsx_trace(chip);
		return TRANSPORT_ERROR;
	}

#ifdef SUPPORT_MAGIC_GATE
	if ((chip->mspro_formatter_enable) &&
			(chip->lun2card[lun] & MS_CARD))
#else
	if (chip->mspro_formatter_enable)
#endif
		if (!card || (card == MS_CARD))
			pro_formatter_flag = true;

	if (pro_formatter_flag) {
		if (scsi_bufflen(srb) < 56)
			sendbytes = (unsigned char)(scsi_bufflen(srb));
		else
			sendbytes = 56;

	} else {
		if (scsi_bufflen(srb) < 36)
			sendbytes = (unsigned char)(scsi_bufflen(srb));
		else
			sendbytes = 36;
	}

	if (sendbytes > 8) {
		memcpy(buf, inquiry_buf, 8);
		memcpy(buf + 8, inquiry_string,	sendbytes - 8);
		if (pro_formatter_flag) {
			/* Additional Length */
			buf[4] = 0x33;
		}
	} else {
		memcpy(buf, inquiry_buf, sendbytes);
	}

	if (pro_formatter_flag) {
		if (sendbytes > 36)
			memcpy(buf + 36, formatter_inquiry_str, sendbytes - 36);
	}

	scsi_set_resid(srb, 0);

	rtsx_stor_set_xfer_buf(buf, scsi_bufflen(srb), srb);
	vfree(buf);

	return TRANSPORT_GOOD;
}


static int start_stop_unit(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	unsigned int lun = SCSI_LUN(srb);

	scsi_set_resid(srb, scsi_bufflen(srb));

	if (srb->cmnd[1] == 1)
		return TRANSPORT_GOOD;

	switch (srb->cmnd[0x4]) {
	case STOP_MEDIUM:
		/* Media disabled */
		return TRANSPORT_GOOD;

	case UNLOAD_MEDIUM:
		/* Media shall be unload */
		if (check_card_ready(chip, lun))
			eject_card(chip, lun);
		return TRANSPORT_GOOD;

	case MAKE_MEDIUM_READY:
	case LOAD_MEDIUM:
		if (check_card_ready(chip, lun))
			return TRANSPORT_GOOD;
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;

		break;
	}

	rtsx_trace(chip);
	return TRANSPORT_ERROR;
}


static int allow_medium_removal(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	int prevent;

	prevent = srb->cmnd[4] & 0x1;

	scsi_set_resid(srb, 0);

	if (prevent) {
		set_sense_type(chip, SCSI_LUN(srb),
			SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	return TRANSPORT_GOOD;
}


static int request_sense(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	struct sense_data_t *sense;
	unsigned int lun = SCSI_LUN(srb);
	struct ms_info *ms_card = &(chip->ms_card);
	unsigned char *tmp, *buf;

	sense = &(chip->sense_buffer[lun]);

	if ((get_lun_card(chip, lun) == MS_CARD) &&
		ms_card->pro_under_formatting) {
		if (ms_card->format_status == FORMAT_SUCCESS) {
			set_sense_type(chip, lun, SENSE_TYPE_NO_SENSE);
			ms_card->pro_under_formatting = 0;
			ms_card->progress = 0;
		} else if (ms_card->format_status == FORMAT_IN_PROGRESS) {
			/* Logical Unit Not Ready Format in Progress */
			set_sense_data(chip, lun, CUR_ERR, 0x02, 0, 0x04, 0x04,
					0, (u16)(ms_card->progress));
		} else {
			/* Format Command Failed */
			set_sense_type(chip, lun, SENSE_TYPE_FORMAT_CMD_FAILED);
			ms_card->pro_under_formatting = 0;
			ms_card->progress = 0;
		}

		rtsx_set_stat(chip, RTSX_STAT_RUN);
	}

	buf = vmalloc(scsi_bufflen(srb));
	if (buf == NULL) {
		rtsx_trace(chip);
		return TRANSPORT_ERROR;
	}

	tmp = (unsigned char *)sense;
	memcpy(buf, tmp, scsi_bufflen(srb));

	rtsx_stor_set_xfer_buf(buf, scsi_bufflen(srb), srb);
	vfree(buf);

	scsi_set_resid(srb, 0);
	/* Reset Sense Data */
	set_sense_type(chip, lun, SENSE_TYPE_NO_SENSE);
	return TRANSPORT_GOOD;
}

static void ms_mode_sense(struct rtsx_chip *chip, u8 cmd,
		int lun, u8 *buf, int buf_len)
{
	struct ms_info *ms_card = &(chip->ms_card);
	int sys_info_offset;
	int data_size = buf_len;
	bool support_format = false;
	int i = 0;

	if (cmd == MODE_SENSE) {
		sys_info_offset = 8;
		if (data_size > 0x68)
			data_size = 0x68;

		buf[i++] = 0x67;  /* Mode Data Length */
	} else {
		sys_info_offset = 12;
		if (data_size > 0x6C)
			data_size = 0x6C;

		buf[i++] = 0x00;  /* Mode Data Length (MSB) */
		buf[i++] = 0x6A;  /* Mode Data Length (LSB) */
	}

	/* Medium Type Code */
	if (check_card_ready(chip, lun)) {
		if (CHK_MSXC(ms_card)) {
			support_format = true;
			buf[i++] = 0x40;
		} else if (CHK_MSPRO(ms_card)) {
			support_format = true;
			buf[i++] = 0x20;
		} else {
			buf[i++] = 0x10;
		}

		/* WP */
		if (check_card_wp(chip, lun))
			buf[i++] = 0x80;
		else
			buf[i++] = 0x00;

	} else {
		buf[i++] = 0x00;	/* MediaType */
		buf[i++] = 0x00;	/* WP */
	}

	buf[i++] = 0x00;		/* Reserved */

	if (cmd == MODE_SENSE_10) {
		buf[i++] = 0x00;  /* Reserved */
		buf[i++] = 0x00;  /* Block descriptor length(MSB) */
		buf[i++] = 0x00;  /* Block descriptor length(LSB) */

		/* The Following Data is the content of "Page 0x20" */
		if (data_size >= 9)
			buf[i++] = 0x20;		/* Page Code */
		if (data_size >= 10)
			buf[i++] = 0x62;		/* Page Length */
		if (data_size >= 11)
			buf[i++] = 0x00;		/* No Access Control */
		if (data_size >= 12) {
			if (support_format)
				buf[i++] = 0xC0;	/* SF, SGM */
			else
				buf[i++] = 0x00;
		}
	} else {
		/* The Following Data is the content of "Page 0x20" */
		if (data_size >= 5)
			buf[i++] = 0x20;		/* Page Code */
		if (data_size >= 6)
			buf[i++] = 0x62;		/* Page Length */
		if (data_size >= 7)
			buf[i++] = 0x00;		/* No Access Control */
		if (data_size >= 8) {
			if (support_format)
				buf[i++] = 0xC0;	/* SF, SGM */
			else
				buf[i++] = 0x00;
		}
	}

	if (data_size > sys_info_offset) {
		/* 96 Bytes Attribute Data */
		int len = data_size - sys_info_offset;

		len = (len < 96) ? len : 96;

		memcpy(buf + sys_info_offset, ms_card->raw_sys_info, len);
	}
}

static int mode_sense(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	unsigned int lun = SCSI_LUN(srb);
	unsigned int dataSize;
	int status;
	bool pro_formatter_flag;
	unsigned char pageCode, *buf;
	u8 card = get_lun_card(chip, lun);

#ifndef SUPPORT_MAGIC_GATE
	if (!check_card_ready(chip, lun)) {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
		scsi_set_resid(srb, scsi_bufflen(srb));
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}
#endif

	pro_formatter_flag = false;
	dataSize = 8;
#ifdef SUPPORT_MAGIC_GATE
	if ((chip->lun2card[lun] & MS_CARD)) {
		if (!card || (card == MS_CARD)) {
			dataSize = 108;
			if (chip->mspro_formatter_enable)
				pro_formatter_flag = true;
		}
	}
#else
	if (card == MS_CARD) {
		if (chip->mspro_formatter_enable) {
			pro_formatter_flag = true;
			dataSize = 108;
		}
	}
#endif

	buf = kmalloc(dataSize, GFP_KERNEL);
	if (buf == NULL) {
		rtsx_trace(chip);
		return TRANSPORT_ERROR;
	}

	pageCode = srb->cmnd[2] & 0x3f;

	if ((pageCode == 0x3F) || (pageCode == 0x1C) ||
		(pageCode == 0x00) ||
		(pro_formatter_flag && (pageCode == 0x20))) {
		if (srb->cmnd[0] == MODE_SENSE) {
			if ((pageCode == 0x3F) || (pageCode == 0x20)) {
				ms_mode_sense(chip, srb->cmnd[0],
					      lun, buf, dataSize);
			} else {
				dataSize = 4;
				buf[0] = 0x03;
				buf[1] = 0x00;
				if (check_card_wp(chip, lun))
					buf[2] = 0x80;
				else
					buf[2] = 0x00;

				buf[3] = 0x00;
			}
		} else {
			if ((pageCode == 0x3F) || (pageCode == 0x20)) {
				ms_mode_sense(chip, srb->cmnd[0],
					      lun, buf, dataSize);
			} else {
				dataSize = 8;
				buf[0] = 0x00;
				buf[1] = 0x06;
				buf[2] = 0x00;
				if (check_card_wp(chip, lun))
					buf[3] = 0x80;
				else
					buf[3] = 0x00;
				buf[4] = 0x00;
				buf[5] = 0x00;
				buf[6] = 0x00;
				buf[7] = 0x00;
			}
		}
		status = TRANSPORT_GOOD;
	} else {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
		scsi_set_resid(srb, scsi_bufflen(srb));
		status = TRANSPORT_FAILED;
	}

	if (status == TRANSPORT_GOOD) {
		unsigned int len = min_t(unsigned int, scsi_bufflen(srb),
					dataSize);
		rtsx_stor_set_xfer_buf(buf, len, srb);
		scsi_set_resid(srb, scsi_bufflen(srb) - len);
	}
	kfree(buf);

	return status;
}

static int read_write(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
#ifdef SUPPORT_SD_LOCK
	struct sd_info *sd_card = &(chip->sd_card);
#endif
	unsigned int lun = SCSI_LUN(srb);
	int retval;
	u32 start_sec;
	u16 sec_cnt;

	rtsx_disable_aspm(chip);

	if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
		rtsx_exit_ss(chip);
		wait_timeout(100);
	}
	rtsx_set_stat(chip, RTSX_STAT_RUN);

	if (!check_card_ready(chip, lun) || (get_card_size(chip, lun) == 0)) {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	if (!(CHK_BIT(chip->lun_mc, lun))) {
		SET_BIT(chip->lun_mc, lun);
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE);
		return TRANSPORT_FAILED;
	}

#ifdef SUPPORT_SD_LOCK
	if (sd_card->sd_erase_status) {
		/* Accessing to any card is forbidden
		 * until the erase procedure of SD is completed
		 */
		dev_dbg(rtsx_dev(chip), "SD card being erased!\n");
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_READ_FORBIDDEN);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	if (get_lun_card(chip, lun) == SD_CARD) {
		if (sd_card->sd_lock_status & SD_LOCKED) {
			dev_dbg(rtsx_dev(chip), "SD card locked!\n");
			set_sense_type(chip, lun,
				SENSE_TYPE_MEDIA_READ_FORBIDDEN);
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}
	}
#endif

	if ((srb->cmnd[0] == READ_10) || (srb->cmnd[0] == WRITE_10)) {
		start_sec = ((u32)srb->cmnd[2] << 24) |
			((u32)srb->cmnd[3] << 16) |
			((u32)srb->cmnd[4] << 8) | ((u32)srb->cmnd[5]);
		sec_cnt = ((u16)(srb->cmnd[7]) << 8) | srb->cmnd[8];
	} else if ((srb->cmnd[0] == READ_6) || (srb->cmnd[0] == WRITE_6)) {
		start_sec = ((u32)(srb->cmnd[1] & 0x1F) << 16) |
			((u32)srb->cmnd[2] << 8) | ((u32)srb->cmnd[3]);
		sec_cnt = srb->cmnd[4];
	} else if ((srb->cmnd[0] == VENDOR_CMND) &&
		(srb->cmnd[1] == SCSI_APP_CMD) &&
		((srb->cmnd[2] == PP_READ10) || (srb->cmnd[2] == PP_WRITE10))) {
		start_sec = ((u32)srb->cmnd[4] << 24) |
			((u32)srb->cmnd[5] << 16) |
			((u32)srb->cmnd[6] << 8) | ((u32)srb->cmnd[7]);
		sec_cnt = ((u16)(srb->cmnd[9]) << 8) | srb->cmnd[10];
	} else {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	/* In some test, we will receive a start_sec like 0xFFFFFFFF.
	 * In this situation, start_sec + sec_cnt will overflow, so we
	 * need to judge start_sec at first
	 */
	if ((start_sec > get_card_size(chip, lun)) ||
			((start_sec + sec_cnt) > get_card_size(chip, lun))) {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LBA_OVER_RANGE);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	if (sec_cnt == 0) {
		scsi_set_resid(srb, 0);
		return TRANSPORT_GOOD;
	}

	if (chip->rw_fail_cnt[lun] == 3) {
		dev_dbg(rtsx_dev(chip), "read/write fail three times in succession\n");
		if (srb->sc_data_direction == DMA_FROM_DEVICE)
			set_sense_type(chip, lun,
				SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
		else
			set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_ERR);

		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	if (srb->sc_data_direction == DMA_TO_DEVICE) {
		if (check_card_wp(chip, lun)) {
			dev_dbg(rtsx_dev(chip), "Write protected card!\n");
			set_sense_type(chip, lun,
				SENSE_TYPE_MEDIA_WRITE_PROTECT);
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}
	}

	retval = card_rw(srb, chip, start_sec, sec_cnt);
	if (retval != STATUS_SUCCESS) {
		if (chip->need_release & chip->lun2card[lun]) {
			chip->rw_fail_cnt[lun] = 0;
			set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
		} else {
			chip->rw_fail_cnt[lun]++;
			if (srb->sc_data_direction == DMA_FROM_DEVICE)
				set_sense_type(chip, lun,
					SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
			else
				set_sense_type(chip, lun,
					SENSE_TYPE_MEDIA_WRITE_ERR);
		}
		retval = TRANSPORT_FAILED;
		rtsx_trace(chip);
		goto Exit;
	} else {
		chip->rw_fail_cnt[lun] = 0;
		retval = TRANSPORT_GOOD;
	}

	scsi_set_resid(srb, 0);

Exit:
	return retval;
}

static int read_format_capacity(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	unsigned char *buf;
	unsigned int lun = SCSI_LUN(srb);
	unsigned int buf_len;
	u8 card = get_lun_card(chip, lun);
	u32 card_size;
	int desc_cnt;
	int i = 0;

	if (!check_card_ready(chip, lun)) {
		if (!chip->mspro_formatter_enable) {
			set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}
	}

	buf_len = (scsi_bufflen(srb) > 12) ? 0x14 : 12;

	buf = kmalloc(buf_len, GFP_KERNEL);
	if (buf == NULL) {
		rtsx_trace(chip);
		return TRANSPORT_ERROR;
	}

	buf[i++] = 0;
	buf[i++] = 0;
	buf[i++] = 0;

	/* Capacity List Length */
	if ((buf_len > 12) && chip->mspro_formatter_enable &&
			(chip->lun2card[lun] & MS_CARD) &&
			(!card || (card == MS_CARD))) {
		buf[i++] = 0x10;
		desc_cnt = 2;
	} else {
		buf[i++] = 0x08;
		desc_cnt = 1;
	}

	while (desc_cnt) {
		if (check_card_ready(chip, lun)) {
			card_size = get_card_size(chip, lun);
			buf[i++] = (unsigned char)(card_size >> 24);
			buf[i++] = (unsigned char)(card_size >> 16);
			buf[i++] = (unsigned char)(card_size >> 8);
			buf[i++] = (unsigned char)card_size;

			if (desc_cnt == 2)
				buf[i++] = 2;
			else
				buf[i++] = 0;
		} else {
			buf[i++] = 0xFF;
			buf[i++] = 0xFF;
			buf[i++] = 0xFF;
			buf[i++] = 0xFF;

			if (desc_cnt == 2)
				buf[i++] = 3;
			else
				buf[i++] = 0;
		}

		buf[i++] = 0x00;
		buf[i++] = 0x02;
		buf[i++] = 0x00;

		desc_cnt--;
	}

	buf_len = min_t(unsigned int, scsi_bufflen(srb), buf_len);
	rtsx_stor_set_xfer_buf(buf, buf_len, srb);
	kfree(buf);

	scsi_set_resid(srb, scsi_bufflen(srb) - buf_len);

	return TRANSPORT_GOOD;
}

static int read_capacity(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	unsigned char *buf;
	unsigned int lun = SCSI_LUN(srb);
	u32 card_size;

	if (!check_card_ready(chip, lun)) {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	if (!(CHK_BIT(chip->lun_mc, lun))) {
		SET_BIT(chip->lun_mc, lun);
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE);
		return TRANSPORT_FAILED;
	}

	buf = kmalloc(8, GFP_KERNEL);
	if (buf == NULL) {
		rtsx_trace(chip);
		return TRANSPORT_ERROR;
	}

	card_size = get_card_size(chip, lun);
	buf[0] = (unsigned char)((card_size - 1) >> 24);
	buf[1] = (unsigned char)((card_size - 1) >> 16);
	buf[2] = (unsigned char)((card_size - 1) >> 8);
	buf[3] = (unsigned char)(card_size - 1);

	buf[4] = 0x00;
	buf[5] = 0x00;
	buf[6] = 0x02;
	buf[7] = 0x00;

	rtsx_stor_set_xfer_buf(buf, scsi_bufflen(srb), srb);
	kfree(buf);

	scsi_set_resid(srb, 0);

	return TRANSPORT_GOOD;
}

static int read_eeprom(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	unsigned short len, i;
	int retval;
	u8 *buf;

	rtsx_disable_aspm(chip);

	if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
		rtsx_exit_ss(chip);
		wait_timeout(100);
	}
	rtsx_set_stat(chip, RTSX_STAT_RUN);

	len = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];

	buf = vmalloc(len);
	if (!buf) {
		rtsx_trace(chip);
		return TRANSPORT_ERROR;
	}

	retval = rtsx_force_power_on(chip, SSC_PDCTL);
	if (retval != STATUS_SUCCESS) {
		vfree(buf);
		set_sense_type(chip, SCSI_LUN(srb),
			SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	for (i = 0; i < len; i++) {
		retval = spi_read_eeprom(chip, i, buf + i);
		if (retval != STATUS_SUCCESS) {
			vfree(buf);
			set_sense_type(chip, SCSI_LUN(srb),
				SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}
	}

	len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), len);
	rtsx_stor_set_xfer_buf(buf, len, srb);
	scsi_set_resid(srb, scsi_bufflen(srb) - len);

	vfree(buf);

	return TRANSPORT_GOOD;
}

static int write_eeprom(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	unsigned short len, i;
	int retval;
	u8 *buf;

	rtsx_disable_aspm(chip);

	if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
		rtsx_exit_ss(chip);
		wait_timeout(100);
	}
	rtsx_set_stat(chip, RTSX_STAT_RUN);

	len = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];

	retval = rtsx_force_power_on(chip, SSC_PDCTL);
	if (retval != STATUS_SUCCESS) {
		set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_WRITE_ERR);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	if (len == 511) {
		retval = spi_erase_eeprom_chip(chip);
		if (retval != STATUS_SUCCESS) {
			set_sense_type(chip, SCSI_LUN(srb),
				SENSE_TYPE_MEDIA_WRITE_ERR);
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}
	} else {
		len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb),
					len);
		buf = vmalloc(len);
		if (buf == NULL) {
			rtsx_trace(chip);
			return TRANSPORT_ERROR;
		}

		rtsx_stor_get_xfer_buf(buf, len, srb);
		scsi_set_resid(srb, scsi_bufflen(srb) - len);

		for (i = 0; i < len; i++) {
			retval = spi_write_eeprom(chip, i, buf[i]);
			if (retval != STATUS_SUCCESS) {
				vfree(buf);
				set_sense_type(chip, SCSI_LUN(srb),
					SENSE_TYPE_MEDIA_WRITE_ERR);
				rtsx_trace(chip);
				return TRANSPORT_FAILED;
			}
		}

		vfree(buf);
	}

	return TRANSPORT_GOOD;
}

static int read_mem(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	unsigned short addr, len, i;
	int retval;
	u8 *buf;

	rtsx_disable_aspm(chip);

	if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
		rtsx_exit_ss(chip);
		wait_timeout(100);
	}
	rtsx_set_stat(chip, RTSX_STAT_RUN);

	addr = ((u16)srb->cmnd[2] << 8) | srb->cmnd[3];
	len = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];

	if (addr < 0xFC00) {
		set_sense_type(chip, SCSI_LUN(srb),
			SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	buf = vmalloc(len);
	if (!buf) {
		rtsx_trace(chip);
		return TRANSPORT_ERROR;
	}

	retval = rtsx_force_power_on(chip, SSC_PDCTL);
	if (retval != STATUS_SUCCESS) {
		vfree(buf);
		set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_WRITE_ERR);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	for (i = 0; i < len; i++) {
		retval = rtsx_read_register(chip, addr + i, buf + i);
		if (retval != STATUS_SUCCESS) {
			vfree(buf);
			set_sense_type(chip, SCSI_LUN(srb),
				SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}
	}

	len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), len);
	rtsx_stor_set_xfer_buf(buf, len, srb);
	scsi_set_resid(srb, scsi_bufflen(srb) - len);

	vfree(buf);

	return TRANSPORT_GOOD;
}

static int write_mem(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	unsigned short addr, len, i;
	int retval;
	u8 *buf;

	rtsx_disable_aspm(chip);

	if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
		rtsx_exit_ss(chip);
		wait_timeout(100);
	}
	rtsx_set_stat(chip, RTSX_STAT_RUN);

	addr = ((u16)srb->cmnd[2] << 8) | srb->cmnd[3];
	len = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];

	if (addr < 0xFC00) {
		set_sense_type(chip, SCSI_LUN(srb),
			SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), len);
	buf = vmalloc(len);
	if (buf == NULL) {
		rtsx_trace(chip);
		return TRANSPORT_ERROR;
	}

	rtsx_stor_get_xfer_buf(buf, len, srb);
	scsi_set_resid(srb, scsi_bufflen(srb) - len);

	retval = rtsx_force_power_on(chip, SSC_PDCTL);
	if (retval != STATUS_SUCCESS) {
		vfree(buf);
		set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_WRITE_ERR);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	for (i = 0; i < len; i++) {
		retval = rtsx_write_register(chip, addr + i, 0xFF, buf[i]);
		if (retval != STATUS_SUCCESS) {
			vfree(buf);
			set_sense_type(chip, SCSI_LUN(srb),
				SENSE_TYPE_MEDIA_WRITE_ERR);
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}
	}

	vfree(buf);

	return TRANSPORT_GOOD;
}

static int get_sd_csd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	struct sd_info *sd_card = &(chip->sd_card);
	unsigned int lun = SCSI_LUN(srb);

	if (!check_card_ready(chip, lun)) {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	if (get_lun_card(chip, lun) != SD_CARD) {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	scsi_set_resid(srb, 0);
	rtsx_stor_set_xfer_buf(sd_card->raw_csd, scsi_bufflen(srb), srb);

	return TRANSPORT_GOOD;
}

static int toggle_gpio_cmd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	u8 gpio = srb->cmnd[2];

	rtsx_disable_aspm(chip);

	if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
		rtsx_exit_ss(chip);
		wait_timeout(100);
	}
	rtsx_set_stat(chip, RTSX_STAT_RUN);

	if (gpio > 3)
		gpio = 1;
	toggle_gpio(chip, gpio);

	return TRANSPORT_GOOD;
}

#ifdef _MSG_TRACE
static int trace_msg_cmd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	unsigned char *ptr, *buf = NULL;
	int i, msg_cnt;
	u8 clear;
	unsigned int buf_len;

	buf_len = 4 + ((2 + MSG_FUNC_LEN + MSG_FILE_LEN + TIME_VAL_LEN) *
		TRACE_ITEM_CNT);

	if ((scsi_bufflen(srb) < buf_len) || (scsi_sglist(srb) == NULL)) {
		set_sense_type(chip, SCSI_LUN(srb),
			SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	clear = srb->cmnd[2];

	buf = vmalloc(scsi_bufflen(srb));
	if (buf == NULL) {
		rtsx_trace(chip);
		return TRANSPORT_ERROR;
	}
	ptr = buf;

	if (chip->trace_msg[chip->msg_idx].valid)
		msg_cnt = TRACE_ITEM_CNT;
	else
		msg_cnt = chip->msg_idx;

	*(ptr++) = (u8)(msg_cnt >> 24);
	*(ptr++) = (u8)(msg_cnt >> 16);
	*(ptr++) = (u8)(msg_cnt >> 8);
	*(ptr++) = (u8)msg_cnt;
	dev_dbg(rtsx_dev(chip), "Trace message count is %d\n", msg_cnt);

	for (i = 1; i <= msg_cnt; i++) {
		int j, idx;

		idx = chip->msg_idx - i;
		if (idx < 0)
			idx += TRACE_ITEM_CNT;

		*(ptr++) = (u8)(chip->trace_msg[idx].line >> 8);
		*(ptr++) = (u8)(chip->trace_msg[idx].line);
		for (j = 0; j < MSG_FUNC_LEN; j++)
			*(ptr++) = chip->trace_msg[idx].func[j];

		for (j = 0; j < MSG_FILE_LEN; j++)
			*(ptr++) = chip->trace_msg[idx].file[j];

		for (j = 0; j < TIME_VAL_LEN; j++)
			*(ptr++) = chip->trace_msg[idx].timeval_buf[j];
	}

	rtsx_stor_set_xfer_buf(buf, scsi_bufflen(srb), srb);
	vfree(buf);

	if (clear) {
		chip->msg_idx = 0;
		for (i = 0; i < TRACE_ITEM_CNT; i++)
			chip->trace_msg[i].valid = 0;
	}

	scsi_set_resid(srb, 0);
	return TRANSPORT_GOOD;
}
#endif

static int read_host_reg(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	u8 addr, buf[4];
	u32 val;
	unsigned int len;

	rtsx_disable_aspm(chip);

	if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
		rtsx_exit_ss(chip);
		wait_timeout(100);
	}
	rtsx_set_stat(chip, RTSX_STAT_RUN);

	addr = srb->cmnd[4];

	val = rtsx_readl(chip, addr);
	dev_dbg(rtsx_dev(chip), "Host register (0x%x): 0x%x\n", addr, val);

	buf[0] = (u8)(val >> 24);
	buf[1] = (u8)(val >> 16);
	buf[2] = (u8)(val >> 8);
	buf[3] = (u8)val;

	len = min_t(unsigned int, scsi_bufflen(srb), 4);
	rtsx_stor_set_xfer_buf(buf, len, srb);
	scsi_set_resid(srb, scsi_bufflen(srb) - len);

	return TRANSPORT_GOOD;
}

static int write_host_reg(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	u8 addr, buf[4];
	u32 val;
	unsigned int len;

	rtsx_disable_aspm(chip);

	if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
		rtsx_exit_ss(chip);
		wait_timeout(100);
	}
	rtsx_set_stat(chip, RTSX_STAT_RUN);

	addr = srb->cmnd[4];

	len = min_t(unsigned int, scsi_bufflen(srb), 4);
	rtsx_stor_get_xfer_buf(buf, len, srb);
	scsi_set_resid(srb, scsi_bufflen(srb) - len);

	val = ((u32)buf[0] << 24) | ((u32)buf[1] << 16) | ((u32)buf[2]
							<< 8) | buf[3];

	rtsx_writel(chip, addr, val);

	return TRANSPORT_GOOD;
}

static int set_variable(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	unsigned lun = SCSI_LUN(srb);

	if (srb->cmnd[3] == 1) {
		/* Variable Clock */
		struct xd_info *xd_card = &(chip->xd_card);
		struct sd_info *sd_card = &(chip->sd_card);
		struct ms_info *ms_card = &(chip->ms_card);

		switch (srb->cmnd[4]) {
		case XD_CARD:
			xd_card->xd_clock = srb->cmnd[5];
			break;

		case SD_CARD:
			sd_card->sd_clock = srb->cmnd[5];
			break;

		case MS_CARD:
			ms_card->ms_clock = srb->cmnd[5];
			break;

		default:
			set_sense_type(chip, lun,
				SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}
	} else if (srb->cmnd[3] == 2) {
		if (srb->cmnd[4]) {
			chip->blink_led = 1;
		} else {
			int retval;

			chip->blink_led = 0;

			rtsx_disable_aspm(chip);

			if (chip->ss_en &&
				(rtsx_get_stat(chip) == RTSX_STAT_SS)) {
				rtsx_exit_ss(chip);
				wait_timeout(100);
			}
			rtsx_set_stat(chip, RTSX_STAT_RUN);

			retval = rtsx_force_power_on(chip, SSC_PDCTL);
			if (retval != STATUS_SUCCESS) {
				set_sense_type(chip, SCSI_LUN(srb),
					SENSE_TYPE_MEDIA_WRITE_ERR);
				rtsx_trace(chip);
				return TRANSPORT_FAILED;
			}

			turn_off_led(chip, LED_GPIO);
		}
	} else {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	return TRANSPORT_GOOD;
}

static int get_variable(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	unsigned int lun = SCSI_LUN(srb);

	if (srb->cmnd[3] == 1) {
		struct xd_info *xd_card = &(chip->xd_card);
		struct sd_info *sd_card = &(chip->sd_card);
		struct ms_info *ms_card = &(chip->ms_card);
		u8 tmp;

		switch (srb->cmnd[4]) {
		case XD_CARD:
			tmp = (u8)(xd_card->xd_clock);
			break;

		case SD_CARD:
			tmp = (u8)(sd_card->sd_clock);
			break;

		case MS_CARD:
			tmp = (u8)(ms_card->ms_clock);
			break;

		default:
			set_sense_type(chip, lun,
				SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}

		rtsx_stor_set_xfer_buf(&tmp, 1, srb);
	} else if (srb->cmnd[3] == 2) {
		u8 tmp = chip->blink_led;

		rtsx_stor_set_xfer_buf(&tmp, 1, srb);
	} else {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	return TRANSPORT_GOOD;
}

static int dma_access_ring_buffer(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	int retval;
	unsigned int lun = SCSI_LUN(srb);
	u16 len;

	rtsx_disable_aspm(chip);

	if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
		rtsx_exit_ss(chip);
		wait_timeout(100);
	}
	rtsx_set_stat(chip, RTSX_STAT_RUN);

	len = ((u16)(srb->cmnd[4]) << 8) | srb->cmnd[5];
	len = min_t(u16, len, scsi_bufflen(srb));

	if (srb->sc_data_direction == DMA_FROM_DEVICE)
		dev_dbg(rtsx_dev(chip), "Read from device\n");
	else
		dev_dbg(rtsx_dev(chip), "Write to device\n");

	retval = rtsx_transfer_data(chip, 0, scsi_sglist(srb), len,
			scsi_sg_count(srb), srb->sc_data_direction, 1000);
	if (retval < 0) {
		if (srb->sc_data_direction == DMA_FROM_DEVICE)
			set_sense_type(chip, lun,
				SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
		else
			set_sense_type(chip, lun,
				SENSE_TYPE_MEDIA_WRITE_ERR);

		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}
	scsi_set_resid(srb, 0);

	return TRANSPORT_GOOD;
}

static int get_dev_status(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	struct sd_info *sd_card = &(chip->sd_card);
	struct ms_info *ms_card = &(chip->ms_card);
	int buf_len;
	unsigned int lun = SCSI_LUN(srb);
	u8 card = get_lun_card(chip, lun);
	u8 status[32];
#ifdef SUPPORT_OCP
	u8 oc_now_mask = 0, oc_ever_mask = 0;
#endif

	memset(status, 0, 32);

	status[0] = (u8)(chip->product_id);
	status[1] = chip->ic_version;

	if (chip->auto_delink_en)
		status[2] = 0x10;
	else
		status[2] = 0x00;

	status[3] = 20;
	status[4] = 10;
	status[5] = 05;
	status[6] = 21;

	if (chip->card_wp)
		status[7] = 0x20;
	else
		status[7] = 0x00;

#ifdef SUPPORT_OCP
	status[8] = 0;
	if (CHECK_LUN_MODE(chip,
		SD_MS_2LUN) && (chip->lun2card[lun] == MS_CARD)) {
		oc_now_mask = MS_OC_NOW;
		oc_ever_mask = MS_OC_EVER;
	} else {
		oc_now_mask = SD_OC_NOW;
		oc_ever_mask = SD_OC_EVER;
	}

	if (chip->ocp_stat & oc_now_mask)
		status[8] |= 0x02;

	if (chip->ocp_stat & oc_ever_mask)
		status[8] |= 0x01;
#endif

	if (card == SD_CARD) {
		if (CHK_SD(sd_card)) {
			if (CHK_SD_HCXC(sd_card)) {
				if (sd_card->capacity > 0x4000000)
					status[0x0E] = 0x02;
				else
					status[0x0E] = 0x01;
			} else {
				status[0x0E] = 0x00;
			}

			if (CHK_SD_SDR104(sd_card))
				status[0x0F] = 0x03;
			else if (CHK_SD_DDR50(sd_card))
				status[0x0F] = 0x04;
			else if (CHK_SD_SDR50(sd_card))
				status[0x0F] = 0x02;
			else if (CHK_SD_HS(sd_card))
				status[0x0F] = 0x01;
			else
				status[0x0F] = 0x00;
		} else {
			if (CHK_MMC_SECTOR_MODE(sd_card))
				status[0x0E] = 0x01;
			else
				status[0x0E] = 0x00;

			if (CHK_MMC_DDR52(sd_card))
				status[0x0F] = 0x03;
			else if (CHK_MMC_52M(sd_card))
				status[0x0F] = 0x02;
			else if (CHK_MMC_26M(sd_card))
				status[0x0F] = 0x01;
			else
				status[0x0F] = 0x00;
		}
	} else if (card == MS_CARD) {
		if (CHK_MSPRO(ms_card)) {
			if (CHK_MSXC(ms_card))
				status[0x0E] = 0x01;
			else
				status[0x0E] = 0x00;

			if (CHK_HG8BIT(ms_card))
				status[0x0F] = 0x01;
			else
				status[0x0F] = 0x00;
		}
	}

#ifdef SUPPORT_SD_LOCK
	if (card == SD_CARD) {
		status[0x17] = 0x80;
		if (sd_card->sd_erase_status)
			status[0x17] |= 0x01;
		if (sd_card->sd_lock_status & SD_LOCKED) {
			status[0x17] |= 0x02;
			status[0x07] |= 0x40;
		}
		if (sd_card->sd_lock_status & SD_PWD_EXIST)
			status[0x17] |= 0x04;
	} else {
		status[0x17] = 0x00;
	}

	dev_dbg(rtsx_dev(chip), "status[0x17] = 0x%x\n", status[0x17]);
#endif

	status[0x18] = 0x8A;
	status[0x1A] = 0x28;
#ifdef SUPPORT_SD_LOCK
	status[0x1F] = 0x01;
#endif

	buf_len = min_t(unsigned int, scsi_bufflen(srb), sizeof(status));
	rtsx_stor_set_xfer_buf(status, buf_len, srb);
	scsi_set_resid(srb, scsi_bufflen(srb) - buf_len);

	return TRANSPORT_GOOD;
}

static int set_chip_mode(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	int phy_debug_mode;
	int retval;
	u16 reg;

	if (!CHECK_PID(chip, 0x5208)) {
		set_sense_type(chip, SCSI_LUN(srb),
			SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	phy_debug_mode = (int)(srb->cmnd[3]);

	if (phy_debug_mode) {
		chip->phy_debug_mode = 1;
		retval = rtsx_write_register(chip, CDRESUMECTL, 0x77, 0);
		if (retval != STATUS_SUCCESS) {
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}

		rtsx_disable_bus_int(chip);

		retval = rtsx_read_phy_register(chip, 0x1C, &reg);
		if (retval != STATUS_SUCCESS) {
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}

		reg |= 0x0001;
		retval = rtsx_write_phy_register(chip, 0x1C, reg);
		if (retval != STATUS_SUCCESS) {
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}
	} else {
		chip->phy_debug_mode = 0;
		retval = rtsx_write_register(chip, CDRESUMECTL, 0x77, 0x77);
		if (retval != STATUS_SUCCESS) {
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}

		rtsx_enable_bus_int(chip);

		retval = rtsx_read_phy_register(chip, 0x1C, &reg);
		if (retval != STATUS_SUCCESS) {
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}

		reg &= 0xFFFE;
		retval = rtsx_write_phy_register(chip, 0x1C, reg);
		if (retval != STATUS_SUCCESS) {
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}
	}

	return TRANSPORT_GOOD;
}

static int rw_mem_cmd_buf(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	int retval =  STATUS_SUCCESS;
	unsigned int lun = SCSI_LUN(srb);
	u8 cmd_type, mask, value, idx;
	u16 addr;

	rtsx_disable_aspm(chip);

	if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
		rtsx_exit_ss(chip);
		wait_timeout(100);
	}
	rtsx_set_stat(chip, RTSX_STAT_RUN);

	switch (srb->cmnd[3]) {
	case INIT_BATCHCMD:
		rtsx_init_cmd(chip);
		break;

	case ADD_BATCHCMD:
		cmd_type = srb->cmnd[4];
		if (cmd_type > 2) {
			set_sense_type(chip, lun,
				SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}
		addr = (srb->cmnd[5] << 8) | srb->cmnd[6];
		mask = srb->cmnd[7];
		value = srb->cmnd[8];
		rtsx_add_cmd(chip, cmd_type, addr, mask, value);
		break;

	case SEND_BATCHCMD:
		retval = rtsx_send_cmd(chip, 0, 1000);
		break;

	case GET_BATCHRSP:
		idx = srb->cmnd[4];
		value = *(rtsx_get_cmd_data(chip) + idx);
		if (scsi_bufflen(srb) < 1) {
			set_sense_type(chip, lun,
				SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}
		rtsx_stor_set_xfer_buf(&value, 1, srb);
		scsi_set_resid(srb, 0);
		break;

	default:
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	if (retval != STATUS_SUCCESS) {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_ERR);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	return TRANSPORT_GOOD;
}

static int suit_cmd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	int result;

	switch (srb->cmnd[3]) {
	case INIT_BATCHCMD:
	case ADD_BATCHCMD:
	case SEND_BATCHCMD:
	case GET_BATCHRSP:
		result = rw_mem_cmd_buf(srb, chip);
		break;
	default:
		result = TRANSPORT_ERROR;
	}

	return result;
}

static int read_phy_register(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	unsigned short addr, len, i;
	int retval;
	u8 *buf;
	u16 val;

	rtsx_disable_aspm(chip);

	if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
		rtsx_exit_ss(chip);
		wait_timeout(100);
	}
	rtsx_set_stat(chip, RTSX_STAT_RUN);

	addr = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];
	len = ((u16)srb->cmnd[6] << 8) | srb->cmnd[7];

	if (len % 2)
		len -= len % 2;

	if (len) {
		buf = vmalloc(len);
		if (!buf) {
			rtsx_trace(chip);
			return TRANSPORT_ERROR;
		}

		retval = rtsx_force_power_on(chip, SSC_PDCTL);
		if (retval != STATUS_SUCCESS) {
			vfree(buf);
			set_sense_type(chip, SCSI_LUN(srb),
				SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}

		for (i = 0; i < len / 2; i++) {
			retval = rtsx_read_phy_register(chip, addr + i, &val);
			if (retval != STATUS_SUCCESS) {
				vfree(buf);
				set_sense_type(chip, SCSI_LUN(srb),
					SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
				rtsx_trace(chip);
				return TRANSPORT_FAILED;
			}

			buf[2*i] = (u8)(val >> 8);
			buf[2*i+1] = (u8)val;
		}

		len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb),
					len);
		rtsx_stor_set_xfer_buf(buf, len, srb);
		scsi_set_resid(srb, scsi_bufflen(srb) - len);

		vfree(buf);
	}

	return TRANSPORT_GOOD;
}

static int write_phy_register(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	unsigned short addr, len, i;
	int retval;
	u8 *buf;
	u16 val;

	rtsx_disable_aspm(chip);

	if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
		rtsx_exit_ss(chip);
		wait_timeout(100);
	}
	rtsx_set_stat(chip, RTSX_STAT_RUN);

	addr = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];
	len = ((u16)srb->cmnd[6] << 8) | srb->cmnd[7];

	if (len % 2)
		len -= len % 2;

	if (len) {
		len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb),
					len);

		buf = vmalloc(len);
		if (buf == NULL) {
			rtsx_trace(chip);
			return TRANSPORT_ERROR;
		}

		rtsx_stor_get_xfer_buf(buf, len, srb);
		scsi_set_resid(srb, scsi_bufflen(srb) - len);

		retval = rtsx_force_power_on(chip, SSC_PDCTL);
		if (retval != STATUS_SUCCESS) {
			vfree(buf);
			set_sense_type(chip, SCSI_LUN(srb),
				SENSE_TYPE_MEDIA_WRITE_ERR);
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}

		for (i = 0; i < len / 2; i++) {
			val = ((u16)buf[2*i] << 8) | buf[2*i+1];
			retval = rtsx_write_phy_register(chip, addr + i, val);
			if (retval != STATUS_SUCCESS) {
				vfree(buf);
				set_sense_type(chip, SCSI_LUN(srb),
					SENSE_TYPE_MEDIA_WRITE_ERR);
				rtsx_trace(chip);
				return TRANSPORT_FAILED;
			}
		}

		vfree(buf);
	}

	return TRANSPORT_GOOD;
}

static int erase_eeprom2(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	unsigned short addr;
	int retval;
	u8 mode;

	rtsx_disable_aspm(chip);

	if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
		rtsx_exit_ss(chip);
		wait_timeout(100);
	}
	rtsx_set_stat(chip, RTSX_STAT_RUN);

	retval = rtsx_force_power_on(chip, SSC_PDCTL);
	if (retval != STATUS_SUCCESS) {
		set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_WRITE_ERR);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	mode = srb->cmnd[3];
	addr = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];

	if (mode == 0) {
		retval = spi_erase_eeprom_chip(chip);
		if (retval != STATUS_SUCCESS) {
			set_sense_type(chip, SCSI_LUN(srb),
				SENSE_TYPE_MEDIA_WRITE_ERR);
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}
	} else if (mode == 1) {
		retval = spi_erase_eeprom_byte(chip, addr);
		if (retval != STATUS_SUCCESS) {
			set_sense_type(chip, SCSI_LUN(srb),
				SENSE_TYPE_MEDIA_WRITE_ERR);
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}
	} else {
		set_sense_type(chip, SCSI_LUN(srb),
			SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	return TRANSPORT_GOOD;
}

static int read_eeprom2(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	unsigned short addr, len, i;
	int retval;
	u8 *buf;

	rtsx_disable_aspm(chip);

	if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
		rtsx_exit_ss(chip);
		wait_timeout(100);
	}
	rtsx_set_stat(chip, RTSX_STAT_RUN);

	addr = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];
	len = ((u16)srb->cmnd[6] << 8) | srb->cmnd[7];

	buf = vmalloc(len);
	if (!buf) {
		rtsx_trace(chip);
		return TRANSPORT_ERROR;
	}

	retval = rtsx_force_power_on(chip, SSC_PDCTL);
	if (retval != STATUS_SUCCESS) {
		vfree(buf);
		set_sense_type(chip, SCSI_LUN(srb),
			SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	for (i = 0; i < len; i++) {
		retval = spi_read_eeprom(chip, addr + i, buf + i);
		if (retval != STATUS_SUCCESS) {
			vfree(buf);
			set_sense_type(chip, SCSI_LUN(srb),
				SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}
	}

	len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), len);
	rtsx_stor_set_xfer_buf(buf, len, srb);
	scsi_set_resid(srb, scsi_bufflen(srb) - len);

	vfree(buf);

	return TRANSPORT_GOOD;
}

static int write_eeprom2(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	unsigned short addr, len, i;
	int retval;
	u8 *buf;

	rtsx_disable_aspm(chip);

	if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
		rtsx_exit_ss(chip);
		wait_timeout(100);
	}
	rtsx_set_stat(chip, RTSX_STAT_RUN);

	addr = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];
	len = ((u16)srb->cmnd[6] << 8) | srb->cmnd[7];

	len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), len);
	buf = vmalloc(len);
	if (buf == NULL) {
		rtsx_trace(chip);
		return TRANSPORT_ERROR;
	}

	rtsx_stor_get_xfer_buf(buf, len, srb);
	scsi_set_resid(srb, scsi_bufflen(srb) - len);

	retval = rtsx_force_power_on(chip, SSC_PDCTL);
	if (retval != STATUS_SUCCESS) {
		vfree(buf);
		set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_WRITE_ERR);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	for (i = 0; i < len; i++) {
		retval = spi_write_eeprom(chip, addr + i, buf[i]);
		if (retval != STATUS_SUCCESS) {
			vfree(buf);
			set_sense_type(chip, SCSI_LUN(srb),
				SENSE_TYPE_MEDIA_WRITE_ERR);
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}
	}

	vfree(buf);

	return TRANSPORT_GOOD;
}

static int read_efuse(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	int retval;
	u8 addr, len, i;
	u8 *buf;

	rtsx_disable_aspm(chip);

	if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
		rtsx_exit_ss(chip);
		wait_timeout(100);
	}
	rtsx_set_stat(chip, RTSX_STAT_RUN);

	addr = srb->cmnd[4];
	len = srb->cmnd[5];

	buf = vmalloc(len);
	if (!buf) {
		rtsx_trace(chip);
		return TRANSPORT_ERROR;
	}

	retval = rtsx_force_power_on(chip, SSC_PDCTL);
	if (retval != STATUS_SUCCESS) {
		vfree(buf);
		set_sense_type(chip, SCSI_LUN(srb),
			SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	for (i = 0; i < len; i++) {
		retval = rtsx_read_efuse(chip, addr + i, buf + i);
		if (retval != STATUS_SUCCESS) {
			vfree(buf);
			set_sense_type(chip, SCSI_LUN(srb),
				SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}
	}

	len = (u8)min_t(unsigned int, scsi_bufflen(srb), len);
	rtsx_stor_set_xfer_buf(buf, len, srb);
	scsi_set_resid(srb, scsi_bufflen(srb) - len);

	vfree(buf);

	return TRANSPORT_GOOD;
}

static int write_efuse(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	int retval, result = TRANSPORT_GOOD;
	u16 val;
	u8 addr, len, i;
	u8 *buf;

	rtsx_disable_aspm(chip);

	if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
		rtsx_exit_ss(chip);
		wait_timeout(100);
	}
	rtsx_set_stat(chip, RTSX_STAT_RUN);

	addr = srb->cmnd[4];
	len = srb->cmnd[5];

	len = (u8)min_t(unsigned int, scsi_bufflen(srb), len);
	buf = vmalloc(len);
	if (buf == NULL) {
		rtsx_trace(chip);
		return TRANSPORT_ERROR;
	}

	rtsx_stor_get_xfer_buf(buf, len, srb);
	scsi_set_resid(srb, scsi_bufflen(srb) - len);

	retval = rtsx_force_power_on(chip, SSC_PDCTL);
	if (retval != STATUS_SUCCESS) {
		vfree(buf);
		rtsx_trace(chip);
		return TRANSPORT_ERROR;
	}

	if (chip->asic_code) {
		retval = rtsx_read_phy_register(chip, 0x08, &val);
		if (retval != STATUS_SUCCESS) {
			vfree(buf);
			rtsx_trace(chip);
			return TRANSPORT_ERROR;
		}

		retval = rtsx_write_register(chip, PWR_GATE_CTRL,
					LDO3318_PWR_MASK, LDO_OFF);
		if (retval != STATUS_SUCCESS) {
			vfree(buf);
			rtsx_trace(chip);
			return TRANSPORT_ERROR;
		}

		wait_timeout(600);

		retval = rtsx_write_phy_register(chip, 0x08,
						0x4C00 | chip->phy_voltage);
		if (retval != STATUS_SUCCESS) {
			vfree(buf);
			rtsx_trace(chip);
			return TRANSPORT_ERROR;
		}

		retval = rtsx_write_register(chip, PWR_GATE_CTRL,
					LDO3318_PWR_MASK, LDO_ON);
		if (retval != STATUS_SUCCESS) {
			vfree(buf);
			rtsx_trace(chip);
			return TRANSPORT_ERROR;
		}

		wait_timeout(600);
	}

	retval = card_power_on(chip, SPI_CARD);
	if (retval != STATUS_SUCCESS) {
		vfree(buf);
		rtsx_trace(chip);
		return TRANSPORT_ERROR;
	}

	wait_timeout(50);

	for (i = 0; i < len; i++) {
		retval = rtsx_write_efuse(chip, addr + i, buf[i]);
		if (retval != STATUS_SUCCESS) {
			set_sense_type(chip, SCSI_LUN(srb),
				SENSE_TYPE_MEDIA_WRITE_ERR);
			result = TRANSPORT_FAILED;
			rtsx_trace(chip);
			goto Exit;
		}
	}

Exit:
	vfree(buf);

	retval = card_power_off(chip, SPI_CARD);
	if (retval != STATUS_SUCCESS) {
		rtsx_trace(chip);
		return TRANSPORT_ERROR;
	}

	if (chip->asic_code) {
		retval = rtsx_write_register(chip, PWR_GATE_CTRL,
					LDO3318_PWR_MASK, LDO_OFF);
		if (retval != STATUS_SUCCESS) {
			rtsx_trace(chip);
			return TRANSPORT_ERROR;
		}

		wait_timeout(600);

		retval = rtsx_write_phy_register(chip, 0x08, val);
		if (retval != STATUS_SUCCESS) {
			rtsx_trace(chip);
			return TRANSPORT_ERROR;
		}

		retval = rtsx_write_register(chip, PWR_GATE_CTRL,
					LDO3318_PWR_MASK, LDO_ON);
		if (retval != STATUS_SUCCESS) {
			rtsx_trace(chip);
			return TRANSPORT_ERROR;
		}
	}

	return result;
}

static int read_cfg_byte(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	int retval;
	bool func_max;
	u8 func;
	u16 addr, len;
	u8 *buf;

	rtsx_disable_aspm(chip);

	if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
		rtsx_exit_ss(chip);
		wait_timeout(100);
	}
	rtsx_set_stat(chip, RTSX_STAT_RUN);

	func = srb->cmnd[3];
	addr = ((u16)(srb->cmnd[4]) << 8) | srb->cmnd[5];
	len = ((u16)(srb->cmnd[6]) << 8) | srb->cmnd[7];

	dev_dbg(rtsx_dev(chip), "%s: func = %d, addr = 0x%x, len = %d\n",
		__func__, func, addr, len);

	if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip))
		func_max = true;
	else
		func_max = false;

	if (func > func_max) {
		set_sense_type(chip, SCSI_LUN(srb),
			SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	buf = vmalloc(len);
	if (!buf) {
		rtsx_trace(chip);
		return TRANSPORT_ERROR;
	}

	retval = rtsx_read_cfg_seq(chip, func, addr, buf, len);
	if (retval != STATUS_SUCCESS) {
		set_sense_type(chip, SCSI_LUN(srb),
			SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
		vfree(buf);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	len = (u16)min_t(unsigned int, scsi_bufflen(srb), len);
	rtsx_stor_set_xfer_buf(buf, len, srb);
	scsi_set_resid(srb, scsi_bufflen(srb) - len);

	vfree(buf);

	return TRANSPORT_GOOD;
}

static int write_cfg_byte(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	int retval;
	bool func_max;
	u8 func;
	u16 addr, len;
	u8 *buf;

	rtsx_disable_aspm(chip);

	if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
		rtsx_exit_ss(chip);
		wait_timeout(100);
	}
	rtsx_set_stat(chip, RTSX_STAT_RUN);

	func = srb->cmnd[3];
	addr = ((u16)(srb->cmnd[4]) << 8) | srb->cmnd[5];
	len = ((u16)(srb->cmnd[6]) << 8) | srb->cmnd[7];

	dev_dbg(rtsx_dev(chip), "%s: func = %d, addr = 0x%x\n",
		__func__, func, addr);

	if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip))
		func_max = true;
	else
		func_max = false;

	if (func > func_max) {
		set_sense_type(chip, SCSI_LUN(srb),
			SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), len);
	buf = vmalloc(len);
	if (!buf) {
		rtsx_trace(chip);
		return TRANSPORT_ERROR;
	}

	rtsx_stor_get_xfer_buf(buf, len, srb);
	scsi_set_resid(srb, scsi_bufflen(srb) - len);

	retval = rtsx_write_cfg_seq(chip, func, addr, buf, len);
	if (retval != STATUS_SUCCESS) {
		set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_WRITE_ERR);
		vfree(buf);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	vfree(buf);

	return TRANSPORT_GOOD;
}

static int app_cmd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	int result;

	switch (srb->cmnd[2]) {
	case PP_READ10:
	case PP_WRITE10:
		result = read_write(srb, chip);
		break;

	case READ_HOST_REG:
		result = read_host_reg(srb, chip);
		break;

	case WRITE_HOST_REG:
		result = write_host_reg(srb, chip);
		break;

	case GET_VAR:
		result = get_variable(srb, chip);
		break;

	case SET_VAR:
		result = set_variable(srb, chip);
		break;

	case DMA_READ:
	case DMA_WRITE:
		result = dma_access_ring_buffer(srb, chip);
		break;

	case READ_PHY:
		result = read_phy_register(srb, chip);
		break;

	case WRITE_PHY:
		result = write_phy_register(srb, chip);
		break;

	case ERASE_EEPROM2:
		result = erase_eeprom2(srb, chip);
		break;

	case READ_EEPROM2:
		result = read_eeprom2(srb, chip);
		break;

	case WRITE_EEPROM2:
		result = write_eeprom2(srb, chip);
		break;

	case READ_EFUSE:
		result = read_efuse(srb, chip);
		break;

	case WRITE_EFUSE:
		result = write_efuse(srb, chip);
		break;

	case READ_CFG:
		result = read_cfg_byte(srb, chip);
		break;

	case WRITE_CFG:
		result = write_cfg_byte(srb, chip);
		break;

	case SET_CHIP_MODE:
		result = set_chip_mode(srb, chip);
		break;

	case SUIT_CMD:
		result = suit_cmd(srb, chip);
		break;

	case GET_DEV_STATUS:
		result = get_dev_status(srb, chip);
		break;

	default:
		set_sense_type(chip, SCSI_LUN(srb),
			SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	return result;
}


static int read_status(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	u8 rtsx_status[16];
	int buf_len;
	unsigned int lun = SCSI_LUN(srb);

	rtsx_status[0] = (u8)(chip->vendor_id >> 8);
	rtsx_status[1] = (u8)(chip->vendor_id);

	rtsx_status[2] = (u8)(chip->product_id >> 8);
	rtsx_status[3] = (u8)(chip->product_id);

	rtsx_status[4] = (u8)lun;

	if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
		if (chip->lun2card[lun] == SD_CARD)
			rtsx_status[5] = 2;
		else
			rtsx_status[5] = 3;
	} else {
		if (chip->card_exist) {
			if (chip->card_exist & XD_CARD)
				rtsx_status[5] = 4;
			else if (chip->card_exist & SD_CARD)
				rtsx_status[5] = 2;
			else if (chip->card_exist & MS_CARD)
				rtsx_status[5] = 3;
			else
				rtsx_status[5] = 7;
		} else {
			rtsx_status[5] = 7;
		}
	}

	if (CHECK_LUN_MODE(chip, SD_MS_2LUN))
		rtsx_status[6] = 2;
	else
		rtsx_status[6] = 1;

	rtsx_status[7] = (u8)(chip->product_id);
	rtsx_status[8] = chip->ic_version;

	if (check_card_exist(chip, lun))
		rtsx_status[9] = 1;
	else
		rtsx_status[9] = 0;

	if (CHECK_LUN_MODE(chip, SD_MS_2LUN))
		rtsx_status[10] = 0;
	else
		rtsx_status[10] = 1;

	if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
		if (chip->lun2card[lun] == SD_CARD)
			rtsx_status[11] = SD_CARD;
		else
			rtsx_status[11] = MS_CARD;
	} else {
		rtsx_status[11] = XD_CARD | SD_CARD | MS_CARD;
	}

	if (check_card_ready(chip, lun))
		rtsx_status[12] = 1;
	else
		rtsx_status[12] = 0;

	if (get_lun_card(chip, lun) == XD_CARD) {
		rtsx_status[13] = 0x40;
	} else if (get_lun_card(chip, lun) == SD_CARD) {
		struct sd_info *sd_card = &(chip->sd_card);

		rtsx_status[13] = 0x20;
		if (CHK_SD(sd_card)) {
			if (CHK_SD_HCXC(sd_card))
				rtsx_status[13] |= 0x04;
			if (CHK_SD_HS(sd_card))
				rtsx_status[13] |= 0x02;
		} else {
			rtsx_status[13] |= 0x08;
			if (CHK_MMC_52M(sd_card))
				rtsx_status[13] |= 0x02;
			if (CHK_MMC_SECTOR_MODE(sd_card))
				rtsx_status[13] |= 0x04;
		}
	} else if (get_lun_card(chip, lun) == MS_CARD) {
		struct ms_info *ms_card = &(chip->ms_card);

		if (CHK_MSPRO(ms_card)) {
			rtsx_status[13] = 0x38;
			if (CHK_HG8BIT(ms_card))
				rtsx_status[13] |= 0x04;
#ifdef SUPPORT_MSXC
			if (CHK_MSXC(ms_card))
				rtsx_status[13] |= 0x01;
#endif
		} else {
			rtsx_status[13] = 0x30;
		}
	} else {
		if (CHECK_LUN_MODE(chip, DEFAULT_SINGLE)) {
#ifdef SUPPORT_SDIO
			if (chip->sd_io && chip->sd_int)
				rtsx_status[13] = 0x60;
			else
				rtsx_status[13] = 0x70;
#else
			rtsx_status[13] = 0x70;
#endif
		} else {
			if (chip->lun2card[lun] == SD_CARD)
				rtsx_status[13] = 0x20;
			else
				rtsx_status[13] = 0x30;
		}
	}

	rtsx_status[14] = 0x78;
	if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip))
		rtsx_status[15] = 0x83;
	else
		rtsx_status[15] = 0x82;

	buf_len = min_t(unsigned int, scsi_bufflen(srb), sizeof(rtsx_status));
	rtsx_stor_set_xfer_buf(rtsx_status, buf_len, srb);
	scsi_set_resid(srb, scsi_bufflen(srb) - buf_len);

	return TRANSPORT_GOOD;
}

static int get_card_bus_width(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	unsigned int lun = SCSI_LUN(srb);
	u8 card, bus_width;

	if (!check_card_ready(chip, lun)) {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	card = get_lun_card(chip, lun);
	if ((card == SD_CARD) || (card == MS_CARD)) {
		bus_width = chip->card_bus_width[lun];
	} else {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	scsi_set_resid(srb, 0);
	rtsx_stor_set_xfer_buf(&bus_width, scsi_bufflen(srb), srb);

	return TRANSPORT_GOOD;
}

static int spi_vendor_cmd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	int result;
	unsigned int lun = SCSI_LUN(srb);
	u8 gpio_dir;

	if (CHECK_PID(chip, 0x5208) || CHECK_PID(chip, 0x5288)) {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	rtsx_disable_aspm(chip);

	if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
		rtsx_exit_ss(chip);
		wait_timeout(100);
	}
	rtsx_set_stat(chip, RTSX_STAT_RUN);

	rtsx_force_power_on(chip, SSC_PDCTL);

	rtsx_read_register(chip, CARD_GPIO_DIR, &gpio_dir);
	rtsx_write_register(chip, CARD_GPIO_DIR, 0x07, gpio_dir & 0x06);

	switch (srb->cmnd[2]) {
	case SCSI_SPI_GETSTATUS:
		result = spi_get_status(srb, chip);
		break;

	case SCSI_SPI_SETPARAMETER:
		result = spi_set_parameter(srb, chip);
		break;

	case SCSI_SPI_READFALSHID:
		result = spi_read_flash_id(srb, chip);
		break;

	case SCSI_SPI_READFLASH:
		result = spi_read_flash(srb, chip);
		break;

	case SCSI_SPI_WRITEFLASH:
		result = spi_write_flash(srb, chip);
		break;

	case SCSI_SPI_WRITEFLASHSTATUS:
		result = spi_write_flash_status(srb, chip);
		break;

	case SCSI_SPI_ERASEFLASH:
		result = spi_erase_flash(srb, chip);
		break;

	default:
		rtsx_write_register(chip, CARD_GPIO_DIR, 0x07, gpio_dir);

		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	rtsx_write_register(chip, CARD_GPIO_DIR, 0x07, gpio_dir);

	if (result != STATUS_SUCCESS) {
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	return TRANSPORT_GOOD;
}

static int vendor_cmnd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	int result;

	switch (srb->cmnd[1]) {
	case READ_STATUS:
		result = read_status(srb, chip);
		break;

	case READ_MEM:
		result = read_mem(srb, chip);
		break;

	case WRITE_MEM:
		result = write_mem(srb, chip);
		break;

	case READ_EEPROM:
		result = read_eeprom(srb, chip);
		break;

	case WRITE_EEPROM:
		result = write_eeprom(srb, chip);
		break;

	case TOGGLE_GPIO:
		result = toggle_gpio_cmd(srb, chip);
		break;

	case GET_SD_CSD:
		result = get_sd_csd(srb, chip);
		break;

	case GET_BUS_WIDTH:
		result = get_card_bus_width(srb, chip);
		break;

#ifdef _MSG_TRACE
	case TRACE_MSG:
		result = trace_msg_cmd(srb, chip);
		break;
#endif

	case SCSI_APP_CMD:
		result = app_cmd(srb, chip);
		break;

	case SPI_VENDOR_COMMAND:
		result = spi_vendor_cmd(srb, chip);
		break;

	default:
		set_sense_type(chip, SCSI_LUN(srb),
			SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	return result;
}

#if !defined(LED_AUTO_BLINK) && !defined(REGULAR_BLINK)
void led_shine(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	unsigned int lun = SCSI_LUN(srb);
	u16 sec_cnt;

	if ((srb->cmnd[0] == READ_10) || (srb->cmnd[0] == WRITE_10))
		sec_cnt = ((u16)(srb->cmnd[7]) << 8) | srb->cmnd[8];
	else if ((srb->cmnd[0] == READ_6) || (srb->cmnd[0] == WRITE_6))
		sec_cnt = srb->cmnd[4];
	else
		return;

	if (chip->rw_cap[lun] >= GPIO_TOGGLE_THRESHOLD) {
		toggle_gpio(chip, LED_GPIO);
		chip->rw_cap[lun] = 0;
	} else {
		chip->rw_cap[lun] += sec_cnt;
	}
}
#endif

static int ms_format_cmnd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	struct ms_info *ms_card = &(chip->ms_card);
	unsigned int lun = SCSI_LUN(srb);
	bool quick_format;
	int retval;

	if (get_lun_card(chip, lun) != MS_CARD) {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	if ((srb->cmnd[3] != 0x4D) || (srb->cmnd[4] != 0x47) ||
		(srb->cmnd[5] != 0x66) || (srb->cmnd[6] != 0x6D) ||
		(srb->cmnd[7] != 0x74)) {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	rtsx_disable_aspm(chip);

	if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
		rtsx_exit_ss(chip);
		wait_timeout(100);

		if (!check_card_ready(chip, lun) ||
				(get_card_size(chip, lun) == 0)) {
			set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}
	}
	rtsx_set_stat(chip, RTSX_STAT_RUN);

	if (srb->cmnd[8] & 0x01)
		quick_format = false;
	else
		quick_format = true;

	if (!(chip->card_ready & MS_CARD)) {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	if (chip->card_wp & MS_CARD) {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_PROTECT);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	if (!CHK_MSPRO(ms_card)) {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	retval = mspro_format(srb, chip, MS_SHORT_DATA_LEN, quick_format);
	if (retval != STATUS_SUCCESS) {
		set_sense_type(chip, lun, SENSE_TYPE_FORMAT_CMD_FAILED);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	scsi_set_resid(srb, 0);
	return TRANSPORT_GOOD;
}

#ifdef SUPPORT_PCGL_1P18
static int get_ms_information(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	struct ms_info *ms_card = &(chip->ms_card);
	unsigned int lun = SCSI_LUN(srb);
	u8 dev_info_id, data_len;
	u8 *buf;
	unsigned int buf_len;
	int i;

	if (!check_card_ready(chip, lun)) {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}
	if (get_lun_card(chip, lun) != MS_CARD) {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	if ((srb->cmnd[2] != 0xB0) || (srb->cmnd[4] != 0x4D) ||
		(srb->cmnd[5] != 0x53) || (srb->cmnd[6] != 0x49) ||
		(srb->cmnd[7] != 0x44)) {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	dev_info_id = srb->cmnd[3];
	if ((CHK_MSXC(ms_card) && (dev_info_id == 0x10)) ||
			(!CHK_MSXC(ms_card) && (dev_info_id == 0x13)) ||
			!CHK_MSPRO(ms_card)) {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	if (dev_info_id == 0x15)
		buf_len = data_len = 0x3A;
	else
		buf_len = data_len = 0x6A;

	buf = kmalloc(buf_len, GFP_KERNEL);
	if (!buf) {
		rtsx_trace(chip);
		return TRANSPORT_ERROR;
	}

	i = 0;
	/*  GET Memory Stick Media Information Response Header */
	buf[i++] = 0x00;		/* Data length MSB */
	buf[i++] = data_len;		/* Data length LSB */
	/* Device Information Type Code */
	if (CHK_MSXC(ms_card))
		buf[i++] = 0x03;
	else
		buf[i++] = 0x02;

	/* SGM bit */
	buf[i++] = 0x01;
	/* Reserved */
	buf[i++] = 0x00;
	buf[i++] = 0x00;
	buf[i++] = 0x00;
	/* Number of Device Information */
	buf[i++] = 0x01;

	/*  Device Information Body */

	/* Device Information ID Number */
	buf[i++] = dev_info_id;
	/* Device Information Length */
	if (dev_info_id == 0x15)
		data_len = 0x31;
	else
		data_len = 0x61;

	buf[i++] = 0x00;		/* Data length MSB */
	buf[i++] = data_len;		/* Data length LSB */
	/* Valid Bit */
	buf[i++] = 0x80;
	if ((dev_info_id == 0x10) || (dev_info_id == 0x13)) {
		/* System Information */
		memcpy(buf+i, ms_card->raw_sys_info, 96);
	} else {
		/* Model Name */
		memcpy(buf+i, ms_card->raw_model_name, 48);
	}

	rtsx_stor_set_xfer_buf(buf, buf_len, srb);

	if (dev_info_id == 0x15)
		scsi_set_resid(srb, scsi_bufflen(srb)-0x3C);
	else
		scsi_set_resid(srb, scsi_bufflen(srb)-0x6C);

	kfree(buf);
	return STATUS_SUCCESS;
}
#endif

static int ms_sp_cmnd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	int retval = TRANSPORT_ERROR;

	if (srb->cmnd[2] == MS_FORMAT)
		retval = ms_format_cmnd(srb, chip);
#ifdef SUPPORT_PCGL_1P18
	else if (srb->cmnd[2] == GET_MS_INFORMATION)
		retval = get_ms_information(srb, chip);
#endif

	return retval;
}

#ifdef SUPPORT_CPRM
static int sd_extention_cmnd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	unsigned int lun = SCSI_LUN(srb);
	int result;

	rtsx_disable_aspm(chip);

	if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
		rtsx_exit_ss(chip);
		wait_timeout(100);
	}
	rtsx_set_stat(chip, RTSX_STAT_RUN);

	sd_cleanup_work(chip);

	if (!check_card_ready(chip, lun)) {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}
	if (get_lun_card(chip, lun) != SD_CARD) {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	switch (srb->cmnd[0]) {
	case SD_PASS_THRU_MODE:
		result = sd_pass_thru_mode(srb, chip);
		break;

	case SD_EXECUTE_NO_DATA:
		result = sd_execute_no_data(srb, chip);
		break;

	case SD_EXECUTE_READ:
		result = sd_execute_read_data(srb, chip);
		break;

	case SD_EXECUTE_WRITE:
		result = sd_execute_write_data(srb, chip);
		break;

	case SD_GET_RSP:
		result = sd_get_cmd_rsp(srb, chip);
		break;

	case SD_HW_RST:
		result = sd_hw_rst(srb, chip);
		break;

	default:
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	return result;
}
#endif

#ifdef SUPPORT_MAGIC_GATE
static int mg_report_key(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	struct ms_info *ms_card = &(chip->ms_card);
	unsigned int lun = SCSI_LUN(srb);
	int retval;
	u8 key_format;

	rtsx_disable_aspm(chip);

	if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
		rtsx_exit_ss(chip);
		wait_timeout(100);
	}
	rtsx_set_stat(chip, RTSX_STAT_RUN);

	ms_cleanup_work(chip);

	if (!check_card_ready(chip, lun)) {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}
	if (get_lun_card(chip, lun) != MS_CARD) {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	if (srb->cmnd[7] != KC_MG_R_PRO) {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	if (!CHK_MSPRO(ms_card)) {
		set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	key_format = srb->cmnd[10] & 0x3F;
	dev_dbg(rtsx_dev(chip), "key_format = 0x%x\n", key_format);

	switch (key_format) {
	case KF_GET_LOC_EKB:
		if ((scsi_bufflen(srb) == 0x41C) &&
			(srb->cmnd[8] == 0x04) &&
			(srb->cmnd[9] == 0x1C)) {
			retval = mg_get_local_EKB(srb, chip);
			if (retval != STATUS_SUCCESS) {
				rtsx_trace(chip);
				return TRANSPORT_FAILED;
			}

		} else {
			set_sense_type(chip, lun,
				SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}
		break;

	case KF_RSP_CHG:
		if ((scsi_bufflen(srb) == 0x24) &&
			(srb->cmnd[8] == 0x00) &&
			(srb->cmnd[9] == 0x24)) {
			retval = mg_get_rsp_chg(srb, chip);
			if (retval != STATUS_SUCCESS) {
				rtsx_trace(chip);
				return TRANSPORT_FAILED;
			}

		} else {
			set_sense_type(chip, lun,
				SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}
		break;

	case KF_GET_ICV:
		ms_card->mg_entry_num = srb->cmnd[5];
		if ((scsi_bufflen(srb) == 0x404) &&
			(srb->cmnd[8] == 0x04) &&
			(srb->cmnd[9] == 0x04) &&
			(srb->cmnd[2] == 0x00) &&
			(srb->cmnd[3] == 0x00) &&
			(srb->cmnd[4] == 0x00) &&
			(srb->cmnd[5] < 32)) {
			retval = mg_get_ICV(srb, chip);
			if (retval != STATUS_SUCCESS) {
				rtsx_trace(chip);
				return TRANSPORT_FAILED;
			}

		} else {
			set_sense_type(chip, lun,
				SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}
		break;

	default:
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	scsi_set_resid(srb, 0);
	return TRANSPORT_GOOD;
}

static int mg_send_key(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
	struct ms_info *ms_card = &(chip->ms_card);
	unsigned int lun = SCSI_LUN(srb);
	int retval;
	u8 key_format;

	rtsx_disable_aspm(chip);

	if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
		rtsx_exit_ss(chip);
		wait_timeout(100);
	}
	rtsx_set_stat(chip, RTSX_STAT_RUN);

	ms_cleanup_work(chip);

	if (!check_card_ready(chip, lun)) {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}
	if (check_card_wp(chip, lun)) {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_PROTECT);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}
	if (get_lun_card(chip, lun) != MS_CARD) {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	if (srb->cmnd[7] != KC_MG_R_PRO) {
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	if (!CHK_MSPRO(ms_card)) {
		set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	key_format = srb->cmnd[10] & 0x3F;
	dev_dbg(rtsx_dev(chip), "key_format = 0x%x\n", key_format);

	switch (key_format) {
	case KF_SET_LEAF_ID:
		if ((scsi_bufflen(srb) == 0x0C) &&
			(srb->cmnd[8] == 0x00) &&
			(srb->cmnd[9] == 0x0C)) {
			retval = mg_set_leaf_id(srb, chip);
			if (retval != STATUS_SUCCESS) {
				rtsx_trace(chip);
				return TRANSPORT_FAILED;
			}

		} else {
			set_sense_type(chip, lun,
				SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}
		break;

	case KF_CHG_HOST:
		if ((scsi_bufflen(srb) == 0x0C) &&
			(srb->cmnd[8] == 0x00) &&
			(srb->cmnd[9] == 0x0C)) {
			retval = mg_chg(srb, chip);
			if (retval != STATUS_SUCCESS) {
				rtsx_trace(chip);
				return TRANSPORT_FAILED;
			}

		} else {
			set_sense_type(chip, lun,
				SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}
		break;

	case KF_RSP_HOST:
		if ((scsi_bufflen(srb) == 0x0C) &&
			(srb->cmnd[8] == 0x00) &&
			(srb->cmnd[9] == 0x0C)) {
			retval = mg_rsp(srb, chip);
			if (retval != STATUS_SUCCESS) {
				rtsx_trace(chip);
				return TRANSPORT_FAILED;
			}

		} else {
			set_sense_type(chip, lun,
				SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}
		break;

	case KF_SET_ICV:
		ms_card->mg_entry_num = srb->cmnd[5];
		if ((scsi_bufflen(srb) == 0x404) &&
			(srb->cmnd[8] == 0x04) &&
			(srb->cmnd[9] == 0x04) &&
			(srb->cmnd[2] == 0x00) &&
			(srb->cmnd[3] == 0x00) &&
			(srb->cmnd[4] == 0x00) &&
			(srb->cmnd[5] < 32)) {
			retval = mg_set_ICV(srb, chip);
			if (retval != STATUS_SUCCESS) {
				rtsx_trace(chip);
				return TRANSPORT_FAILED;
			}

		} else {
			set_sense_type(chip, lun,
				SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}
		break;

	default:
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
		rtsx_trace(chip);
		return TRANSPORT_FAILED;
	}

	scsi_set_resid(srb, 0);
	return TRANSPORT_GOOD;
}
#endif

int rtsx_scsi_handler(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
#ifdef SUPPORT_SD_LOCK
	struct sd_info *sd_card = &(chip->sd_card);
#endif
	struct ms_info *ms_card = &(chip->ms_card);
	unsigned int lun = SCSI_LUN(srb);
	int result;

#ifdef SUPPORT_SD_LOCK
	if (sd_card->sd_erase_status) {
		/* Block all SCSI command except for
		 * REQUEST_SENSE and rs_ppstatus
		 */
		if (!((srb->cmnd[0] == VENDOR_CMND) &&
				(srb->cmnd[1] == SCSI_APP_CMD) &&
				(srb->cmnd[2] == GET_DEV_STATUS)) &&
				(srb->cmnd[0] != REQUEST_SENSE)) {
			/* Logical Unit Not Ready Format in Progress */
			set_sense_data(chip, lun, CUR_ERR,
				       0x02, 0, 0x04, 0x04, 0, 0);
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}
	}
#endif

	if ((get_lun_card(chip, lun) == MS_CARD) &&
			(ms_card->format_status == FORMAT_IN_PROGRESS)) {
		if ((srb->cmnd[0] != REQUEST_SENSE) &&
			(srb->cmnd[0] != INQUIRY)) {
			/* Logical Unit Not Ready Format in Progress */
			set_sense_data(chip, lun, CUR_ERR, 0x02, 0, 0x04, 0x04,
					0, (u16)(ms_card->progress));
			rtsx_trace(chip);
			return TRANSPORT_FAILED;
		}
	}

	switch (srb->cmnd[0]) {
	case READ_10:
	case WRITE_10:
	case READ_6:
	case WRITE_6:
		result = read_write(srb, chip);
#if !defined(LED_AUTO_BLINK) && !defined(REGULAR_BLINK)
		led_shine(srb, chip);
#endif
		break;

	case TEST_UNIT_READY:
		result = test_unit_ready(srb, chip);
		break;

	case INQUIRY:
		result = inquiry(srb, chip);
		break;

	case READ_CAPACITY:
		result = read_capacity(srb, chip);
		break;

	case START_STOP:
		result = start_stop_unit(srb, chip);
		break;

	case ALLOW_MEDIUM_REMOVAL:
		result = allow_medium_removal(srb, chip);
		break;

	case REQUEST_SENSE:
		result = request_sense(srb, chip);
		break;

	case MODE_SENSE:
	case MODE_SENSE_10:
		result = mode_sense(srb, chip);
		break;

	case 0x23:
		result = read_format_capacity(srb, chip);
		break;

	case VENDOR_CMND:
		result = vendor_cmnd(srb, chip);
		break;

	case MS_SP_CMND:
		result = ms_sp_cmnd(srb, chip);
		break;

#ifdef SUPPORT_CPRM
	case SD_PASS_THRU_MODE:
	case SD_EXECUTE_NO_DATA:
	case SD_EXECUTE_READ:
	case SD_EXECUTE_WRITE:
	case SD_GET_RSP:
	case SD_HW_RST:
		result = sd_extention_cmnd(srb, chip);
		break;
#endif

#ifdef SUPPORT_MAGIC_GATE
	case CMD_MSPRO_MG_RKEY:
		result = mg_report_key(srb, chip);
		break;

	case CMD_MSPRO_MG_SKEY:
		result = mg_send_key(srb, chip);
		break;
#endif

	case FORMAT_UNIT:
	case MODE_SELECT:
	case VERIFY:
		result = TRANSPORT_GOOD;
		break;

	default:
		set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
		result = TRANSPORT_FAILED;
	}

	return result;
}