/* 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 <linux/blkdev.h> #include <linux/kthread.h> #include <linux/sched.h> #include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/export.h> #include <scsi/scsi.h> #include <scsi/scsi_eh.h> #include <scsi/scsi_device.h> #include "debug.h" #include "rts51x.h" #include "rts51x_chip.h" #include "rts51x_scsi.h" #include "rts51x_card.h" #include "rts51x_transport.h" #include "sd_cprm.h" #include "ms_mg.h" #include "trace.h" void rts51x_scsi_show_command(struct scsi_cmnd *srb) { char *what = NULL; int i, unknown_cmd = 0; switch (srb->cmnd[0]) { case TEST_UNIT_READY: what = (char *)"TEST_UNIT_READY"; break; case REZERO_UNIT: what = (char *)"REZERO_UNIT"; break; case REQUEST_SENSE: what = (char *)"REQUEST_SENSE"; break; case FORMAT_UNIT: what = (char *)"FORMAT_UNIT"; break; case READ_BLOCK_LIMITS: what = (char *)"READ_BLOCK_LIMITS"; break; case 0x07: what = (char *)"REASSIGN_BLOCKS"; break; case READ_6: what = (char *)"READ_6"; break; case WRITE_6: what = (char *)"WRITE_6"; break; case SEEK_6: what = (char *)"SEEK_6"; break; case READ_REVERSE: what = (char *)"READ_REVERSE"; break; case WRITE_FILEMARKS: what = (char *)"WRITE_FILEMARKS"; break; case SPACE: what = (char *)"SPACE"; break; case INQUIRY: what = (char *)"INQUIRY"; break; case RECOVER_BUFFERED_DATA: what = (char *)"RECOVER_BUFFERED_DATA"; break; case MODE_SELECT: what = (char *)"MODE_SELECT"; break; case RESERVE: what = (char *)"RESERVE"; break; case RELEASE: what = (char *)"RELEASE"; break; case COPY: what = (char *)"COPY"; break; case ERASE: what = (char *)"ERASE"; break; case MODE_SENSE: what = (char *)"MODE_SENSE"; break; case START_STOP: what = (char *)"START_STOP"; break; case RECEIVE_DIAGNOSTIC: what = (char *)"RECEIVE_DIAGNOSTIC"; break; case SEND_DIAGNOSTIC: what = (char *)"SEND_DIAGNOSTIC"; break; case ALLOW_MEDIUM_REMOVAL: what = (char *)"ALLOW_MEDIUM_REMOVAL"; break; case SET_WINDOW: what = (char *)"SET_WINDOW"; break; case READ_CAPACITY: what = (char *)"READ_CAPACITY"; break; case READ_10: what = (char *)"READ_10"; break; case WRITE_10: what = (char *)"WRITE_10"; break; case SEEK_10: what = (char *)"SEEK_10"; break; case WRITE_VERIFY: what = (char *)"WRITE_VERIFY"; break; case VERIFY: what = (char *)"VERIFY"; break; case SEARCH_HIGH: what = (char *)"SEARCH_HIGH"; break; case SEARCH_EQUAL: what = (char *)"SEARCH_EQUAL"; break; case SEARCH_LOW: what = (char *)"SEARCH_LOW"; break; case SET_LIMITS: what = (char *)"SET_LIMITS"; break; case READ_POSITION: what = (char *)"READ_POSITION"; break; case SYNCHRONIZE_CACHE: what = (char *)"SYNCHRONIZE_CACHE"; break; case LOCK_UNLOCK_CACHE: what = (char *)"LOCK_UNLOCK_CACHE"; break; case READ_DEFECT_DATA: what = (char *)"READ_DEFECT_DATA"; break; case MEDIUM_SCAN: what = (char *)"MEDIUM_SCAN"; break; case COMPARE: what = (char *)"COMPARE"; break; case COPY_VERIFY: what = (char *)"COPY_VERIFY"; break; case WRITE_BUFFER: what = (char *)"WRITE_BUFFER"; break; case READ_BUFFER: what = (char *)"READ_BUFFER"; break; case UPDATE_BLOCK: what = (char *)"UPDATE_BLOCK"; break; case READ_LONG: what = (char *)"READ_LONG"; break; case WRITE_LONG: what = (char *)"WRITE_LONG"; break; case CHANGE_DEFINITION: what = (char *)"CHANGE_DEFINITION"; break; case WRITE_SAME: what = (char *)"WRITE_SAME"; break; case GPCMD_READ_SUBCHANNEL: what = (char *)"READ SUBCHANNEL"; break; case READ_TOC: what = (char *)"READ_TOC"; break; case GPCMD_READ_HEADER: what = (char *)"READ HEADER"; break; case GPCMD_PLAY_AUDIO_10: what = (char *)"PLAY AUDIO (10)"; break; case GPCMD_PLAY_AUDIO_MSF: what = (char *)"PLAY AUDIO MSF"; break; case GPCMD_GET_EVENT_STATUS_NOTIFICATION: what = (char *)"GET EVENT/STATUS NOTIFICATION"; break; case GPCMD_PAUSE_RESUME: what = (char *)"PAUSE/RESUME"; break; case LOG_SELECT: what = (char *)"LOG_SELECT"; break; case LOG_SENSE: what = (char *)"LOG_SENSE"; break; case GPCMD_STOP_PLAY_SCAN: what = (char *)"STOP PLAY/SCAN"; break; case GPCMD_READ_DISC_INFO: what = (char *)"READ DISC INFORMATION"; break; case GPCMD_READ_TRACK_RZONE_INFO: what = (char *)"READ TRACK INFORMATION"; break; case GPCMD_RESERVE_RZONE_TRACK: what = (char *)"RESERVE TRACK"; break; case GPCMD_SEND_OPC: what = (char *)"SEND OPC"; break; case MODE_SELECT_10: what = (char *)"MODE_SELECT_10"; break; case GPCMD_REPAIR_RZONE_TRACK: what = (char *)"REPAIR TRACK"; break; case 0x59: what = (char *)"READ MASTER CUE"; break; case MODE_SENSE_10: what = (char *)"MODE_SENSE_10"; break; case GPCMD_CLOSE_TRACK: what = (char *)"CLOSE TRACK/SESSION"; break; case 0x5C: what = (char *)"READ BUFFER CAPACITY"; break; case 0x5D: what = (char *)"SEND CUE SHEET"; break; case GPCMD_BLANK: what = (char *)"BLANK"; break; case REPORT_LUNS: what = (char *)"REPORT LUNS"; break; case MOVE_MEDIUM: what = (char *)"MOVE_MEDIUM or PLAY AUDIO (12)"; break; case READ_12: what = (char *)"READ_12"; break; case WRITE_12: what = (char *)"WRITE_12"; break; case WRITE_VERIFY_12: what = (char *)"WRITE_VERIFY_12"; break; case SEARCH_HIGH_12: what = (char *)"SEARCH_HIGH_12"; break; case SEARCH_EQUAL_12: what = (char *)"SEARCH_EQUAL_12"; break; case SEARCH_LOW_12: what = (char *)"SEARCH_LOW_12"; break; case SEND_VOLUME_TAG: what = (char *)"SEND_VOLUME_TAG"; break; case READ_ELEMENT_STATUS: what = (char *)"READ_ELEMENT_STATUS"; break; case GPCMD_READ_CD_MSF: what = (char *)"READ CD MSF"; break; case GPCMD_SCAN: what = (char *)"SCAN"; break; case GPCMD_SET_SPEED: what = (char *)"SET CD SPEED"; break; case GPCMD_MECHANISM_STATUS: what = (char *)"MECHANISM STATUS"; break; case GPCMD_READ_CD: what = (char *)"READ CD"; break; case 0xE1: what = (char *)"WRITE CONTINUE"; break; case WRITE_LONG_2: what = (char *)"WRITE_LONG_2"; break; case VENDOR_CMND: what = (char *)"Realtek's vendor command"; break; default: what = (char *)"(unknown command)"; unknown_cmd = 1; break; } if (srb->cmnd[0] != TEST_UNIT_READY) RTS51X_DEBUGP("Command %s (%d bytes)\n", what, srb->cmd_len); if (unknown_cmd) { RTS51X_DEBUGP(""); for (i = 0; i < srb->cmd_len && i < 16; i++) RTS51X_DEBUGPN(" %02x", srb->cmnd[i]); RTS51X_DEBUGPN("\n"); } } void rts51x_set_sense_type(struct rts51x_chip *chip, unsigned int lun, int sense_type) { switch (sense_type) { case SENSE_TYPE_MEDIA_CHANGE: rts51x_set_sense_data(chip, lun, CUR_ERR, 0x06, 0, 0x28, 0, 0, 0); break; case SENSE_TYPE_MEDIA_NOT_PRESENT: rts51x_set_sense_data(chip, lun, CUR_ERR, 0x02, 0, 0x3A, 0, 0, 0); break; case SENSE_TYPE_MEDIA_LBA_OVER_RANGE: rts51x_set_sense_data(chip, lun, CUR_ERR, 0x05, 0, 0x21, 0, 0, 0); break; case SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT: rts51x_set_sense_data(chip, lun, CUR_ERR, 0x05, 0, 0x25, 0, 0, 0); break; case SENSE_TYPE_MEDIA_WRITE_PROTECT: rts51x_set_sense_data(chip, lun, CUR_ERR, 0x07, 0, 0x27, 0, 0, 0); break; case SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR: rts51x_set_sense_data(chip, lun, CUR_ERR, 0x03, 0, 0x11, 0, 0, 0); break; case SENSE_TYPE_MEDIA_WRITE_ERR: rts51x_set_sense_data(chip, lun, CUR_ERR, 0x03, 0, 0x0C, 0x02, 0, 0); break; case SENSE_TYPE_MEDIA_INVALID_CMD_FIELD: rts51x_set_sense_data(chip, lun, CUR_ERR, ILGAL_REQ, 0, ASC_INVLD_CDB, ASCQ_INVLD_CDB, CDB_ILLEGAL, 1); break; case SENSE_TYPE_FORMAT_CMD_FAILED: rts51x_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: rts51x_set_sense_data(chip, lun, CUR_ERR, 0x05, 0, 0x6F, 0x02, 0, 0); break; case SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN: rts51x_set_sense_data(chip, lun, CUR_ERR, 0x05, 0, 0x6F, 0x00, 0, 0); break; case SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM: rts51x_set_sense_data(chip, lun, CUR_ERR, 0x02, 0, 0x30, 0x00, 0, 0); break; case SENSE_TYPE_MG_WRITE_ERR: rts51x_set_sense_data(chip, lun, CUR_ERR, 0x03, 0, 0x0C, 0x00, 0, 0); break; #endif case SENSE_TYPE_NO_SENSE: default: rts51x_set_sense_data(chip, lun, CUR_ERR, 0, 0, 0, 0, 0, 0); break; } } void rts51x_set_sense_data(struct rts51x_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 rts51x_chip *chip) { unsigned int lun = SCSI_LUN(srb); rts51x_init_cards(chip); if (!check_card_ready(chip, lun)) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); return TRANSPORT_FAILED; } if (!check_lun_mc(chip, lun)) { set_lun_mc(chip, lun); rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE); return TRANSPORT_FAILED; } return TRANSPORT_GOOD; } unsigned char formatter_inquiry_str[20] = { 'M', 'E', 'M', 'O', 'R', 'Y', 'S', 'T', 'I', 'C', 'K', '-', 'M', 'G', /* Byte[47:49] */ 0x0B, /* Byte[50]: MG, MS, MSPro, MSXC */ 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 rts51x_chip *chip) { unsigned int lun = SCSI_LUN(srb); char *inquiry_default = (char *)"Generic-xD/SD/M.S. 1.00 "; char *inquiry_string; unsigned char sendbytes; unsigned char *buf; u8 card = rts51x_get_lun_card(chip, lun); int pro_formatter_flag = 0; 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, }; inquiry_string = inquiry_default; buf = vmalloc(scsi_bufflen(srb)); if (buf == NULL) TRACE_RET(chip, TRANSPORT_ERROR); if (MS_FORMATTER_ENABLED(chip) && (get_lun2card(chip, lun) & MS_CARD)) { if (!card || (card == MS_CARD)) pro_formatter_flag = 1; } 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) buf[4] = 0x33; /* Additional Length */ } 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); rts51x_set_xfer_buf(buf, scsi_bufflen(srb), srb); vfree(buf); return TRANSPORT_GOOD; } static int start_stop_unit(struct scsi_cmnd *srb, struct rts51x_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)) rts51x_eject_card(chip, lun); return TRANSPORT_GOOD; case MAKE_MEDIUM_READY: case LOAD_MEDIUM: if (check_card_ready(chip, lun)) { return TRANSPORT_GOOD; } else { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); TRACE_RET(chip, TRANSPORT_FAILED); } break; } TRACE_RET(chip, TRANSPORT_ERROR); } static int allow_medium_removal(struct scsi_cmnd *srb, struct rts51x_chip *chip) { int prevent; prevent = srb->cmnd[4] & 0x1; scsi_set_resid(srb, 0); if (prevent) { rts51x_set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); TRACE_RET(chip, TRANSPORT_FAILED); } return TRANSPORT_GOOD; } static void ms_mode_sense(struct rts51x_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; int support_format = 0; 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 = 1; buf[i++] = 0x40; } else if (CHK_MSPRO(ms_card)) { support_format = 1; 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 rts51x_chip *chip) { unsigned int lun = SCSI_LUN(srb); unsigned int dataSize; int status; int pro_formatter_flag; unsigned char pageCode, *buf; u8 card = rts51x_get_lun_card(chip, lun); if (!check_card_ready(chip, lun)) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); scsi_set_resid(srb, scsi_bufflen(srb)); TRACE_RET(chip, TRANSPORT_FAILED); } pro_formatter_flag = 0; dataSize = 8; /* In Combo mode, device responses ModeSense command as a MS LUN * when no card is inserted */ if ((get_lun2card(chip, lun) & MS_CARD)) { if (!card || (card == MS_CARD)) { dataSize = 108; if (chip->option.rts51x_mspro_formatter_enable) pro_formatter_flag = 1; } } buf = kmalloc(dataSize, GFP_KERNEL); if (buf == NULL) TRACE_RET(chip, 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[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 { rts51x_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(scsi_bufflen(srb), dataSize); rts51x_set_xfer_buf(buf, len, srb); scsi_set_resid(srb, scsi_bufflen(srb) - len); } kfree(buf); return status; } static int request_sense(struct scsi_cmnd *srb, struct rts51x_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 ((rts51x_get_lun_card(chip, lun) == MS_CARD) && PRO_UNDER_FORMATTING(ms_card)) { rts51x_mspro_format_sense(chip, lun); } buf = vmalloc(scsi_bufflen(srb)); if (buf == NULL) TRACE_RET(chip, TRANSPORT_ERROR); tmp = (unsigned char *)sense; memcpy(buf, tmp, scsi_bufflen(srb)); rts51x_set_xfer_buf(buf, scsi_bufflen(srb), srb); vfree(buf); scsi_set_resid(srb, 0); /* Reset Sense Data */ rts51x_set_sense_type(chip, lun, SENSE_TYPE_NO_SENSE); return TRANSPORT_GOOD; } static int read_write(struct scsi_cmnd *srb, struct rts51x_chip *chip) { unsigned int lun = SCSI_LUN(srb); int retval; u32 start_sec; u16 sec_cnt; if (!check_card_ready(chip, lun) || (chip->capacity[lun] == 0)) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); TRACE_RET(chip, TRANSPORT_FAILED); } if (!check_lun_mc(chip, lun)) { set_lun_mc(chip, lun); rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE); return TRANSPORT_FAILED; } rts51x_prepare_run(chip); RTS51X_SET_STAT(chip, STAT_RUN); 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 { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); TRACE_RET(chip, TRANSPORT_FAILED); } if ((start_sec > chip->capacity[lun]) || ((start_sec + sec_cnt) > chip->capacity[lun])) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LBA_OVER_RANGE); TRACE_RET(chip, TRANSPORT_FAILED); } if (sec_cnt == 0) { scsi_set_resid(srb, 0); return TRANSPORT_GOOD; } if ((srb->sc_data_direction == DMA_TO_DEVICE) && check_card_wp(chip, lun)) { RTS51X_DEBUGP("Write protected card!\n"); rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_PROTECT); TRACE_RET(chip, TRANSPORT_FAILED); } retval = rts51x_card_rw(srb, chip, start_sec, sec_cnt); if (retval != STATUS_SUCCESS) { if (srb->sc_data_direction == DMA_FROM_DEVICE) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); } else { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_ERR); } TRACE_RET(chip, TRANSPORT_FAILED); } scsi_set_resid(srb, 0); return TRANSPORT_GOOD; } static int read_format_capacity(struct scsi_cmnd *srb, struct rts51x_chip *chip) { unsigned char *buf; unsigned int lun = SCSI_LUN(srb); unsigned int buf_len; u8 card = rts51x_get_lun_card(chip, lun); int desc_cnt; int i = 0; if (!check_card_ready(chip, lun)) { if (!chip->option.rts51x_mspro_formatter_enable) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); TRACE_RET(chip, TRANSPORT_FAILED); } } buf_len = (scsi_bufflen(srb) > 12) ? 0x14 : 12; buf = kmalloc(buf_len, GFP_KERNEL); if (buf == NULL) TRACE_RET(chip, TRANSPORT_ERROR); buf[i++] = 0; buf[i++] = 0; buf[i++] = 0; /* Capacity List Length */ if ((buf_len > 12) && chip->option.rts51x_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)) { buf[i++] = (unsigned char)((chip->capacity[lun]) >> 24); buf[i++] = (unsigned char)((chip->capacity[lun]) >> 16); buf[i++] = (unsigned char)((chip->capacity[lun]) >> 8); buf[i++] = (unsigned char)(chip->capacity[lun]); if (desc_cnt == 2) /* Byte[8]: Descriptor Type: Formatted medium */ buf[i++] = 2; else buf[i++] = 0; /* Byte[16] */ } else { buf[i++] = 0xFF; buf[i++] = 0xFF; buf[i++] = 0xFF; buf[i++] = 0xFF; if (desc_cnt == 2) /* Byte[8]: Descriptor Type: No medium */ buf[i++] = 3; else buf[i++] = 0; /*Byte[16] */ } buf[i++] = 0x00; buf[i++] = 0x02; buf[i++] = 0x00; desc_cnt--; } buf_len = min(scsi_bufflen(srb), buf_len); rts51x_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 rts51x_chip *chip) { unsigned char *buf; unsigned int lun = SCSI_LUN(srb); if (!check_card_ready(chip, lun)) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); TRACE_RET(chip, TRANSPORT_FAILED); } if (!check_lun_mc(chip, lun)) { set_lun_mc(chip, lun); rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE); return TRANSPORT_FAILED; } buf = kmalloc(8, GFP_KERNEL); if (buf == NULL) TRACE_RET(chip, TRANSPORT_ERROR); buf[0] = (unsigned char)((chip->capacity[lun] - 1) >> 24); buf[1] = (unsigned char)((chip->capacity[lun] - 1) >> 16); buf[2] = (unsigned char)((chip->capacity[lun] - 1) >> 8); buf[3] = (unsigned char)(chip->capacity[lun] - 1); buf[4] = 0x00; buf[5] = 0x00; buf[6] = 0x02; buf[7] = 0x00; rts51x_set_xfer_buf(buf, scsi_bufflen(srb), srb); kfree(buf); scsi_set_resid(srb, 0); return TRANSPORT_GOOD; } static int get_dev_status(struct scsi_cmnd *srb, struct rts51x_chip *chip) { unsigned int lun = SCSI_LUN(srb); unsigned int buf_len; u8 status[32] = { 0 }; rts51x_pp_status(chip, lun, status, 32); buf_len = min(scsi_bufflen(srb), (unsigned int)sizeof(status)); rts51x_set_xfer_buf(status, buf_len, srb); scsi_set_resid(srb, scsi_bufflen(srb) - buf_len); return TRANSPORT_GOOD; } static int read_status(struct scsi_cmnd *srb, struct rts51x_chip *chip) { u8 rts51x_status[16]; unsigned int buf_len; unsigned int lun = SCSI_LUN(srb); rts51x_read_status(chip, lun, rts51x_status, 16); buf_len = min(scsi_bufflen(srb), (unsigned int)sizeof(rts51x_status)); rts51x_set_xfer_buf(rts51x_status, buf_len, srb); scsi_set_resid(srb, scsi_bufflen(srb) - buf_len); return TRANSPORT_GOOD; } static int read_mem(struct scsi_cmnd *srb, struct rts51x_chip *chip) { unsigned int lun = SCSI_LUN(srb); unsigned short addr, len, i; int retval; u8 *buf; rts51x_prepare_run(chip); RTS51X_SET_STAT(chip, STAT_RUN); addr = ((u16) srb->cmnd[2] << 8) | srb->cmnd[3]; len = ((u16) srb->cmnd[4] << 8) | srb->cmnd[5]; if (addr < 0xe000) { RTS51X_DEBUGP("filter!addr=0x%x\n", addr); return TRANSPORT_GOOD; } buf = vmalloc(len); if (!buf) TRACE_RET(chip, TRANSPORT_ERROR); for (i = 0; i < len; i++) { retval = rts51x_ep0_read_register(chip, addr + i, buf + i); if (retval != STATUS_SUCCESS) { vfree(buf); rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); TRACE_RET(chip, TRANSPORT_FAILED); } } len = (unsigned short)min(scsi_bufflen(srb), (unsigned int)len); rts51x_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 rts51x_chip *chip) { unsigned int lun = SCSI_LUN(srb); unsigned short addr, len, i; int retval; u8 *buf; rts51x_prepare_run(chip); RTS51X_SET_STAT(chip, STAT_RUN); addr = ((u16) srb->cmnd[2] << 8) | srb->cmnd[3]; len = ((u16) srb->cmnd[4] << 8) | srb->cmnd[5]; if (addr < 0xe000) { RTS51X_DEBUGP("filter!addr=0x%x\n", addr); return TRANSPORT_GOOD; } len = (unsigned short)min(scsi_bufflen(srb), (unsigned int)len); buf = vmalloc(len); if (!buf) TRACE_RET(chip, TRANSPORT_ERROR); rts51x_get_xfer_buf(buf, len, srb); for (i = 0; i < len; i++) { retval = rts51x_ep0_write_register(chip, addr + i, 0xFF, buf[i]); if (retval != STATUS_SUCCESS) { vfree(buf); rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_ERR); TRACE_RET(chip, TRANSPORT_FAILED); } } vfree(buf); scsi_set_resid(srb, scsi_bufflen(srb) - len); return TRANSPORT_GOOD; } static int get_sd_csd(struct scsi_cmnd *srb, struct rts51x_chip *chip) { struct sd_info *sd_card = &(chip->sd_card); unsigned int lun = SCSI_LUN(srb); if (!check_card_ready(chip, lun)) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); TRACE_RET(chip, TRANSPORT_FAILED); } if (rts51x_get_lun_card(chip, lun) != SD_CARD) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); TRACE_RET(chip, TRANSPORT_FAILED); } scsi_set_resid(srb, 0); rts51x_set_xfer_buf(sd_card->raw_csd, scsi_bufflen(srb), srb); return TRANSPORT_GOOD; } static int read_phy_register(struct scsi_cmnd *srb, struct rts51x_chip *chip) { int retval; u8 addr, len, i; u8 *buf; rts51x_prepare_run(chip); RTS51X_SET_STAT(chip, STAT_RUN); addr = srb->cmnd[5]; len = srb->cmnd[7]; if (len) { buf = vmalloc(len); if (!buf) TRACE_RET(chip, TRANSPORT_ERROR); for (i = 0; i < len; i++) { retval = rts51x_read_phy_register(chip, addr + i, buf + i); if (retval != STATUS_SUCCESS) { vfree(buf); rts51x_set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); TRACE_RET(chip, TRANSPORT_FAILED); } } len = min(scsi_bufflen(srb), (unsigned int)len); rts51x_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 rts51x_chip *chip) { int retval; u8 addr, len, i; u8 *buf; rts51x_prepare_run(chip); RTS51X_SET_STAT(chip, STAT_RUN); addr = srb->cmnd[5]; len = srb->cmnd[7]; if (len) { len = min(scsi_bufflen(srb), (unsigned int)len); buf = vmalloc(len); if (buf == NULL) TRACE_RET(chip, TRANSPORT_ERROR); rts51x_get_xfer_buf(buf, len, srb); scsi_set_resid(srb, scsi_bufflen(srb) - len); for (i = 0; i < len; i++) { retval = rts51x_write_phy_register(chip, addr + i, buf[i]); if (retval != STATUS_SUCCESS) { vfree(buf); rts51x_set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_WRITE_ERR); TRACE_RET(chip, TRANSPORT_FAILED); } } vfree(buf); } return TRANSPORT_GOOD; } static int get_card_bus_width(struct scsi_cmnd *srb, struct rts51x_chip *chip) { unsigned int lun = SCSI_LUN(srb); u8 card, bus_width; if (!check_card_ready(chip, lun)) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); TRACE_RET(chip, TRANSPORT_FAILED); } card = rts51x_get_lun_card(chip, lun); if ((card == SD_CARD) || (card == MS_CARD)) { bus_width = chip->card_bus_width[lun]; } else { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); TRACE_RET(chip, TRANSPORT_FAILED); } scsi_set_resid(srb, 0); rts51x_set_xfer_buf(&bus_width, scsi_bufflen(srb), srb); return TRANSPORT_GOOD; } #ifdef _MSG_TRACE static int trace_msg_cmd(struct scsi_cmnd *srb, struct rts51x_chip *chip) { unsigned char *buf = NULL; 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)) { rts51x_set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); TRACE_RET(chip, TRANSPORT_FAILED); } clear = srb->cmnd[2]; buf = vmalloc(scsi_bufflen(srb)); if (buf == NULL) TRACE_RET(chip, TRANSPORT_ERROR); rts51x_trace_msg(chip, buf, clear); rts51x_set_xfer_buf(buf, scsi_bufflen(srb), srb); vfree(buf); scsi_set_resid(srb, 0); return TRANSPORT_GOOD; } #endif static int rw_mem_cmd_buf(struct scsi_cmnd *srb, struct rts51x_chip *chip) { int retval = STATUS_SUCCESS; unsigned int lun = SCSI_LUN(srb); u8 cmd_type, mask, value, idx, mode, len; u16 addr; u32 timeout; rts51x_prepare_run(chip); RTS51X_SET_STAT(chip, STAT_RUN); switch (srb->cmnd[3]) { case INIT_BATCHCMD: rts51x_init_cmd(chip); break; case ADD_BATCHCMD: cmd_type = srb->cmnd[4]; if (cmd_type > 2) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); TRACE_RET(chip, TRANSPORT_FAILED); } addr = (srb->cmnd[5] << 8) | srb->cmnd[6]; mask = srb->cmnd[7]; value = srb->cmnd[8]; rts51x_add_cmd(chip, cmd_type, addr, mask, value); break; case SEND_BATCHCMD: mode = srb->cmnd[4]; len = srb->cmnd[5]; timeout = ((u32) srb->cmnd[6] << 24) | ((u32) srb-> cmnd[7] << 16) | ((u32) srb-> cmnd[8] << 8) | ((u32) srb-> cmnd [9]); retval = rts51x_send_cmd(chip, mode, 1000); if (retval != STATUS_SUCCESS) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_ERR); TRACE_RET(chip, TRANSPORT_FAILED); } if (mode & STAGE_R) { retval = rts51x_get_rsp(chip, len, timeout); if (retval != STATUS_SUCCESS) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); TRACE_RET(chip, TRANSPORT_FAILED); } } break; case GET_BATCHRSP: idx = srb->cmnd[4]; value = chip->rsp_buf[idx]; if (scsi_bufflen(srb) < 1) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); TRACE_RET(chip, TRANSPORT_FAILED); } rts51x_set_xfer_buf(&value, 1, srb); scsi_set_resid(srb, 0); break; default: rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); TRACE_RET(chip, TRANSPORT_FAILED); } if (retval != STATUS_SUCCESS) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_ERR); TRACE_RET(chip, TRANSPORT_FAILED); } return TRANSPORT_GOOD; } static int suit_cmd(struct scsi_cmnd *srb, struct rts51x_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 app_cmd(struct scsi_cmnd *srb, struct rts51x_chip *chip) { int result; switch (srb->cmnd[2]) { case PP_READ10: case PP_WRITE10: result = read_write(srb, chip); break; case SUIT_CMD: result = suit_cmd(srb, chip); break; case READ_PHY: result = read_phy_register(srb, chip); break; case WRITE_PHY: result = write_phy_register(srb, chip); break; case GET_DEV_STATUS: result = get_dev_status(srb, chip); break; default: rts51x_set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); TRACE_RET(chip, TRANSPORT_FAILED); } return result; } static int vendor_cmnd(struct scsi_cmnd *srb, struct rts51x_chip *chip) { int result = TRANSPORT_GOOD; 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 GET_BUS_WIDTH: result = get_card_bus_width(srb, chip); break; case GET_SD_CSD: result = get_sd_csd(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; default: rts51x_set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); TRACE_RET(chip, TRANSPORT_FAILED); } return result; } static int ms_format_cmnd(struct scsi_cmnd *srb, struct rts51x_chip *chip) { struct ms_info *ms_card = &(chip->ms_card); unsigned int lun = SCSI_LUN(srb); int retval, quick_format; if (rts51x_get_lun_card(chip, lun) != MS_CARD) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT); TRACE_RET(chip, TRANSPORT_FAILED); } if ((srb->cmnd[3] != 0x4D) || (srb->cmnd[4] != 0x47) || (srb->cmnd[5] != 0x66) || (srb->cmnd[6] != 0x6D) || (srb->cmnd[7] != 0x74)) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); TRACE_RET(chip, TRANSPORT_FAILED); } if (srb->cmnd[8] & 0x01) quick_format = 0; else quick_format = 1; if (!(chip->card_ready & MS_CARD)) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); TRACE_RET(chip, TRANSPORT_FAILED); } if (chip->card_wp & MS_CARD) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_PROTECT); TRACE_RET(chip, TRANSPORT_FAILED); } if (!CHK_MSPRO(ms_card)) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT); TRACE_RET(chip, TRANSPORT_FAILED); } rts51x_prepare_run(chip); RTS51X_SET_STAT(chip, STAT_RUN); retval = rts51x_mspro_format(srb, chip, MS_SHORT_DATA_LEN, quick_format); if (retval != STATUS_SUCCESS) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_FORMAT_CMD_FAILED); TRACE_RET(chip, TRANSPORT_FAILED); } scsi_set_resid(srb, 0); return TRANSPORT_GOOD; } #ifdef SUPPORT_PCGL_1P18 static int get_ms_information(struct scsi_cmnd *srb, struct rts51x_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)) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); TRACE_RET(chip, TRANSPORT_FAILED); } if ((rts51x_get_lun_card(chip, lun) != MS_CARD)) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT); TRACE_RET(chip, TRANSPORT_FAILED); } if ((srb->cmnd[2] != 0xB0) || (srb->cmnd[4] != 0x4D) || (srb->cmnd[5] != 0x53) || (srb->cmnd[6] != 0x49) || (srb->cmnd[7] != 0x44)) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); TRACE_RET(chip, 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)) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); TRACE_RET(chip, 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) TRACE_RET(chip, 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); } rts51x_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 rts51x_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 rts51x_chip *chip) { unsigned int lun = SCSI_LUN(srb); int result; rts51x_prepare_run(chip); RTS51X_SET_STAT(chip, STAT_RUN); rts51x_sd_cleanup_work(chip); if (!check_card_ready(chip, lun)) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); TRACE_RET(chip, TRANSPORT_FAILED); } if ((rts51x_get_lun_card(chip, lun) != SD_CARD)) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT); TRACE_RET(chip, TRANSPORT_FAILED); } switch (srb->cmnd[0]) { case SD_PASS_THRU_MODE: result = rts51x_sd_pass_thru_mode(srb, chip); break; case SD_EXECUTE_NO_DATA: result = rts51x_sd_execute_no_data(srb, chip); break; case SD_EXECUTE_READ: result = rts51x_sd_execute_read_data(srb, chip); break; case SD_EXECUTE_WRITE: result = rts51x_sd_execute_write_data(srb, chip); break; case SD_GET_RSP: result = rts51x_sd_get_cmd_rsp(srb, chip); break; case SD_HW_RST: result = rts51x_sd_hw_rst(srb, chip); break; default: rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); TRACE_RET(chip, TRANSPORT_FAILED); } return result; } #endif #ifdef SUPPORT_MAGIC_GATE static int mg_report_key(struct scsi_cmnd *srb, struct rts51x_chip *chip) { struct ms_info *ms_card = &(chip->ms_card); unsigned int lun = SCSI_LUN(srb); int retval; u8 key_format; rts51x_prepare_run(chip); RTS51X_SET_STAT(chip, STAT_RUN); rts51x_ms_cleanup_work(chip); if (!check_card_ready(chip, lun)) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); TRACE_RET(chip, TRANSPORT_FAILED); } if ((rts51x_get_lun_card(chip, lun) != MS_CARD)) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT); TRACE_RET(chip, TRANSPORT_FAILED); } if (srb->cmnd[7] != KC_MG_R_PRO) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); TRACE_RET(chip, TRANSPORT_FAILED); } if (!CHK_MSPRO(ms_card)) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM); TRACE_RET(chip, TRANSPORT_FAILED); } key_format = srb->cmnd[10] & 0x3F; switch (key_format) { case KF_GET_LOC_EKB: if ((scsi_bufflen(srb) == 0x41C) && (srb->cmnd[8] == 0x04) && (srb->cmnd[9] == 0x1C)) { retval = rts51x_mg_get_local_EKB(srb, chip); if (retval != STATUS_SUCCESS) TRACE_RET(chip, TRANSPORT_FAILED); } else { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); TRACE_RET(chip, TRANSPORT_FAILED); } break; case KF_RSP_CHG: if ((scsi_bufflen(srb) == 0x24) && (srb->cmnd[8] == 0x00) && (srb->cmnd[9] == 0x24)) { retval = rts51x_mg_get_rsp_chg(srb, chip); if (retval != STATUS_SUCCESS) TRACE_RET(chip, TRANSPORT_FAILED); } else { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); TRACE_RET(chip, 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 = rts51x_mg_get_ICV(srb, chip); if (retval != STATUS_SUCCESS) TRACE_RET(chip, TRANSPORT_FAILED); } else { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); TRACE_RET(chip, TRANSPORT_FAILED); } break; default: rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); TRACE_RET(chip, TRANSPORT_FAILED); } scsi_set_resid(srb, 0); return TRANSPORT_GOOD; } static int mg_send_key(struct scsi_cmnd *srb, struct rts51x_chip *chip) { struct ms_info *ms_card = &(chip->ms_card); unsigned int lun = SCSI_LUN(srb); int retval; u8 key_format; rts51x_prepare_run(chip); RTS51X_SET_STAT(chip, STAT_RUN); rts51x_ms_cleanup_work(chip); if (!check_card_ready(chip, lun)) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); TRACE_RET(chip, TRANSPORT_FAILED); } if (check_card_wp(chip, lun)) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_PROTECT); TRACE_RET(chip, TRANSPORT_FAILED); } if ((rts51x_get_lun_card(chip, lun) != MS_CARD)) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT); TRACE_RET(chip, TRANSPORT_FAILED); } if (srb->cmnd[7] != KC_MG_R_PRO) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); TRACE_RET(chip, TRANSPORT_FAILED); } if (!CHK_MSPRO(ms_card)) { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM); TRACE_RET(chip, TRANSPORT_FAILED); } key_format = srb->cmnd[10] & 0x3F; switch (key_format) { case KF_SET_LEAF_ID: if ((scsi_bufflen(srb) == 0x0C) && (srb->cmnd[8] == 0x00) && (srb->cmnd[9] == 0x0C)) { retval = rts51x_mg_set_leaf_id(srb, chip); if (retval != STATUS_SUCCESS) TRACE_RET(chip, TRANSPORT_FAILED); } else { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); TRACE_RET(chip, TRANSPORT_FAILED); } break; case KF_CHG_HOST: if ((scsi_bufflen(srb) == 0x0C) && (srb->cmnd[8] == 0x00) && (srb->cmnd[9] == 0x0C)) { retval = rts51x_mg_chg(srb, chip); if (retval != STATUS_SUCCESS) TRACE_RET(chip, TRANSPORT_FAILED); } else { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); TRACE_RET(chip, TRANSPORT_FAILED); } break; case KF_RSP_HOST: if ((scsi_bufflen(srb) == 0x0C) && (srb->cmnd[8] == 0x00) && (srb->cmnd[9] == 0x0C)) { retval = rts51x_mg_rsp(srb, chip); if (retval != STATUS_SUCCESS) TRACE_RET(chip, TRANSPORT_FAILED); } else { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); TRACE_RET(chip, 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 = rts51x_mg_set_ICV(srb, chip); if (retval != STATUS_SUCCESS) TRACE_RET(chip, TRANSPORT_FAILED); } else { rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); TRACE_RET(chip, TRANSPORT_FAILED); } break; default: rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); TRACE_RET(chip, TRANSPORT_FAILED); } scsi_set_resid(srb, 0); return TRANSPORT_GOOD; } #endif int rts51x_scsi_handler(struct scsi_cmnd *srb, struct rts51x_chip *chip) { struct ms_info *ms_card = &(chip->ms_card); unsigned int lun = SCSI_LUN(srb); int result = TRANSPORT_GOOD; if ((rts51x_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 */ rts51x_set_sense_data(chip, lun, CUR_ERR, 0x02, 0, 0x04, 0x04, 0, (u16) (ms_card->progress)); TRACE_RET(chip, TRANSPORT_FAILED); } } switch (srb->cmnd[0]) { case READ_10: case WRITE_10: case READ_6: case WRITE_6: result = read_write(srb, chip); 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: rts51x_set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); result = TRANSPORT_FAILED; } return result; } /*********************************************************************** * Host functions ***********************************************************************/ int slave_alloc(struct scsi_device *sdev) { /* * Set the INQUIRY transfer length to 36. We don't use any of * the extra data and many devices choke if asked for more or * less than 36 bytes. */ sdev->inquiry_len = 36; return 0; } int slave_configure(struct scsi_device *sdev) { /* Scatter-gather buffers (all but the last) must have a length * divisible by the bulk maxpacket size. Otherwise a data packet * would end up being short, causing a premature end to the data * transfer. Since high-speed bulk pipes have a maxpacket size * of 512, we'll use that as the scsi device queue's DMA alignment * mask. Guaranteeing proper alignment of the first buffer will * have the desired effect because, except at the beginning and * the end, scatter-gather buffers follow page boundaries. */ blk_queue_dma_alignment(sdev->request_queue, (512 - 1)); /* Set the SCSI level to at least 2. We'll leave it at 3 if that's * what is originally reported. We need this to avoid confusing * the SCSI layer with devices that report 0 or 1, but need 10-byte * commands (ala ATAPI devices behind certain bridges, or devices * which simply have broken INQUIRY data). * * NOTE: This means /dev/sg programs (ala cdrecord) will get the * actual information. This seems to be the preference for * programs like that. * * NOTE: This also means that /proc/scsi/scsi and sysfs may report * the actual value or the modified one, depending on where the * data comes from. */ if (sdev->scsi_level < SCSI_2) sdev->scsi_level = sdev->sdev_target->scsi_level = SCSI_2; return 0; } /*********************************************************************** * /proc/scsi/ functions ***********************************************************************/ /* we use this macro to help us write into the buffer */ #undef SPRINTF #define SPRINTF(args...) seq_printf(m, ##args) static int write_info(struct Scsi_Host *host, char *buffer, int length) { /* if someone is sending us data, just throw it away */ return length; } static int show_info(struct seq_file *m, struct Scsi_Host *host) { /* print the controller name */ SPRINTF(" Host scsi%d: %s\n", host->host_no, RTS51X_NAME); /* print product, vendor, and driver version strings */ SPRINTF(" Vendor: Realtek Corp.\n"); SPRINTF(" Product: RTS51xx USB Card Reader\n"); SPRINTF(" Version: %s\n", DRIVER_VERSION); SPRINTF(" Build: %s\n", __TIME__); return 0; } /* queue a command */ /* This is always called with scsi_lock(host) held */ int queuecommand_lck(struct scsi_cmnd *srb, void (*done) (struct scsi_cmnd *)) { struct rts51x_chip *chip = host_to_rts51x(srb->device->host); /* check for state-transition errors */ if (chip->srb != NULL) { RTS51X_DEBUGP("Error in %s: chip->srb = %p\n", __func__, chip->srb); return SCSI_MLQUEUE_HOST_BUSY; } /* fail the command if we are disconnecting */ if (test_bit(FLIDX_DISCONNECTING, &chip->usb->dflags)) { RTS51X_DEBUGP("Fail command during disconnect\n"); srb->result = DID_NO_CONNECT << 16; done(srb); return 0; } /* enqueue the command and wake up the control thread */ srb->scsi_done = done; chip->srb = srb; complete(&chip->usb->cmnd_ready); return 0; } DEF_SCSI_QCMD(queuecommand) /*********************************************************************** * Error handling functions ***********************************************************************/ /* Command timeout and abort */ int command_abort(struct scsi_cmnd *srb) { struct rts51x_chip *chip = host_to_rts51x(srb->device->host); RTS51X_DEBUGP("%s called\n", __func__); /* us->srb together with the TIMED_OUT, RESETTING, and ABORTING * bits are protected by the host lock. */ scsi_lock(rts51x_to_host(chip)); /* Is this command still active? */ if (chip->srb != srb) { scsi_unlock(rts51x_to_host(chip)); RTS51X_DEBUGP("-- nothing to abort\n"); return FAILED; } /* Set the TIMED_OUT bit. Also set the ABORTING bit, but only if * a device reset isn't already in progress (to avoid interfering * with the reset). Note that we must retain the host lock while * calling usb_stor_stop_transport(); otherwise it might interfere * with an auto-reset that begins as soon as we release the lock. */ set_bit(FLIDX_TIMED_OUT, &chip->usb->dflags); if (!test_bit(FLIDX_RESETTING, &chip->usb->dflags)) { set_bit(FLIDX_ABORTING, &chip->usb->dflags); /* rts51x_stop_transport(us); */ } scsi_unlock(rts51x_to_host(chip)); /* Wait for the aborted command to finish */ wait_for_completion(&chip->usb->notify); return SUCCESS; } /* This invokes the transport reset mechanism to reset the state of the * device */ static int device_reset(struct scsi_cmnd *srb) { int result = 0; RTS51X_DEBUGP("%s called\n", __func__); return result < 0 ? FAILED : SUCCESS; } /* Simulate a SCSI bus reset by resetting the device's USB port. */ int bus_reset(struct scsi_cmnd *srb) { int result = 0; RTS51X_DEBUGP("%s called\n", __func__); return result < 0 ? FAILED : SUCCESS; } static const char *rts5139_info(struct Scsi_Host *host) { return "SCSI emulation for RTS5139 USB card reader"; } struct scsi_host_template rts51x_host_template = { /* basic userland interface stuff */ .name = RTS51X_NAME, .proc_name = RTS51X_NAME, .show_info = show_info, .write_info = write_info, .info = rts5139_info, /* command interface -- queued only */ .queuecommand = queuecommand, /* error and abort handlers */ .eh_abort_handler = command_abort, .eh_device_reset_handler = device_reset, .eh_bus_reset_handler = bus_reset, /* queue commands only, only one command per LUN */ .can_queue = 1, .cmd_per_lun = 1, /* unknown initiator id */ .this_id = -1, .slave_alloc = slave_alloc, .slave_configure = slave_configure, /* lots of sg segments can be handled */ .sg_tablesize = SG_ALL, /* limit the total size of a transfer to 120 KB */ .max_sectors = 240, /* merge commands... this seems to help performance, but * periodically someone should test to see which setting is more * optimal. */ .use_clustering = 1, /* emulated HBA */ .emulated = 1, /* we do our own delay after a device or bus reset */ .skip_settle_delay = 1, /* sysfs device attributes */ /* .sdev_attrs = sysfs_device_attr_list, */ /* module management */ .module = THIS_MODULE };