| /* 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 <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 "rts51x_sys.h" |
| #include "sd_cprm.h" |
| #include "ms_mg.h" |
| #include "trace.h" |
| |
| void 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 set_sense_type(struct rts51x_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 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)) { |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); |
| return TRANSPORT_FAILED; |
| } |
| |
| if (!check_lun_mc(chip, lun)) { |
| set_lun_mc(chip, 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; |
| } |
| |
| 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 = 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)) |
| eject_card(chip, lun); |
| return TRANSPORT_GOOD; |
| |
| case MAKE_MEDIUM_READY: |
| case LOAD_MEDIUM: |
| if (check_card_ready(chip, lun)) { |
| return TRANSPORT_GOOD; |
| } else { |
| 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) { |
| 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 = get_lun_card(chip, lun); |
| |
| if (!check_card_ready(chip, lun)) { |
| 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.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 { |
| 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 ((get_lun_card(chip, lun) == MS_CARD) |
| && PRO_UNDER_FORMATTING(ms_card)) { |
| 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 */ |
| set_sense_type(chip, lun, SENSE_TYPE_NO_SENSE); |
| return TRANSPORT_GOOD; |
| } |
| |
| static int read_write(struct scsi_cmnd *srb, struct rts51x_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; |
| |
| if (!check_card_ready(chip, lun) || (chip->capacity[lun] == 0)) { |
| 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); |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE); |
| return TRANSPORT_FAILED; |
| } |
| |
| rts51x_prepare_run(chip); |
| RTS51X_SET_STAT(chip, STAT_RUN); |
| |
| #ifdef SUPPORT_SD_LOCK |
| if (sd_card->sd_erase_status) { |
| /* Accessing to any card is forbidden |
| * until the erase procedure of SD is completed */ |
| RTS51X_DEBUGP("SD card being erased!\n"); |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_READ_FORBIDDEN); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| |
| if (get_lun_card(chip, lun) == SD_CARD) { |
| if (sd_card->sd_lock_status & SD_LOCKED) { |
| RTS51X_DEBUGP("SD card locked!\n"); |
| set_sense_type(chip, lun, |
| SENSE_TYPE_MEDIA_READ_FORBIDDEN); |
| TRACE_RET(chip, 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); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| |
| if ((start_sec > chip->capacity[lun]) || |
| ((start_sec + sec_cnt) > chip->capacity[lun])) { |
| 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"); |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_PROTECT); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| |
| retval = card_rw(srb, chip, start_sec, sec_cnt); |
| if (retval != STATUS_SUCCESS) { |
| #if 0 |
| if (chip->need_release & chip->lun2card[lun]) { |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); |
| } else { |
| #endif |
| 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); |
| } |
| #if 0 |
| } |
| #endif |
| 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 = get_lun_card(chip, lun); |
| int desc_cnt; |
| int i = 0; |
| |
| if (!check_card_ready(chip, lun)) { |
| if (!chip->option.mspro_formatter_enable) { |
| 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.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)) { |
| 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); |
| 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); |
| 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); |
| 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)) { |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| |
| if (get_lun_card(chip, lun) != SD_CARD) { |
| 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); |
| 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); |
| 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)) { |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); |
| TRACE_RET(chip, 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); |
| 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)) { |
| 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) { |
| 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) { |
| 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) { |
| 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) { |
| 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: |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| |
| if (retval != STATUS_SUCCESS) { |
| 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: |
| 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: |
| 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 (get_lun_card(chip, lun) != MS_CARD) { |
| 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)) { |
| 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)) { |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| |
| if (chip->card_wp & MS_CARD) { |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_PROTECT); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| |
| if (!CHK_MSPRO(ms_card)) { |
| 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 = mspro_format(srb, chip, MS_SHORT_DATA_LEN, quick_format); |
| if (retval != STATUS_SUCCESS) { |
| 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 |
| 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)) { |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| if ((get_lun_card(chip, lun) != MS_CARD)) { |
| 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)) { |
| 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)) { |
| 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); |
| |
| sd_cleanup_work(chip); |
| |
| if (!check_card_ready(chip, lun)) { |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| if ((get_lun_card(chip, lun) != SD_CARD)) { |
| 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 = 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); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| |
| return result; |
| } |
| #endif |
| |
| #ifdef SUPPORT_MAGIC_GATE |
| 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); |
| |
| ms_cleanup_work(chip); |
| |
| if (!check_card_ready(chip, lun)) { |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| if ((get_lun_card(chip, lun) != MS_CARD)) { |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| |
| if (srb->cmnd[7] != KC_MG_R_PRO) { |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| |
| if (!CHK_MSPRO(ms_card)) { |
| 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 = mg_get_local_EKB(srb, chip); |
| if (retval != STATUS_SUCCESS) |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } else { |
| 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 = mg_get_rsp_chg(srb, chip); |
| if (retval != STATUS_SUCCESS) |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } else { |
| 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 = mg_get_ICV(srb, chip); |
| if (retval != STATUS_SUCCESS) |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } else { |
| set_sense_type(chip, lun, |
| SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| break; |
| |
| default: |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| |
| scsi_set_resid(srb, 0); |
| return TRANSPORT_GOOD; |
| } |
| |
| 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); |
| |
| ms_cleanup_work(chip); |
| |
| if (!check_card_ready(chip, lun)) { |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| if (check_card_wp(chip, lun)) { |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_PROTECT); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| if ((get_lun_card(chip, lun) != MS_CARD)) { |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| |
| if (srb->cmnd[7] != KC_MG_R_PRO) { |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| |
| if (!CHK_MSPRO(ms_card)) { |
| 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 = mg_set_leaf_id(srb, chip); |
| if (retval != STATUS_SUCCESS) |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } else { |
| 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 = mg_chg(srb, chip); |
| if (retval != STATUS_SUCCESS) |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } else { |
| 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 = mg_rsp(srb, chip); |
| if (retval != STATUS_SUCCESS) |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } else { |
| 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 = mg_set_ICV(srb, chip); |
| if (retval != STATUS_SUCCESS) |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } else { |
| set_sense_type(chip, lun, |
| SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| break; |
| |
| default: |
| 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) |
| { |
| #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 = TRANSPORT_GOOD; |
| |
| #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); |
| TRACE_RET(chip, 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)); |
| 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: |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); |
| result = TRANSPORT_FAILED; |
| } |
| |
| return result; |
| } |
| |
| /*********************************************************************** |
| * Host functions |
| ***********************************************************************/ |
| |
| const char *host_info(struct Scsi_Host *host) |
| { |
| return "SCSI emulation for RTS51xx USB driver-based card reader"; |
| } |
| |
| 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...) \ |
| do { if (pos < buffer+length) pos += sprintf(pos, ## args); } while (0) |
| |
| int proc_info(struct Scsi_Host *host, char *buffer, |
| char **start, off_t offset, int length, int inout) |
| { |
| char *pos = buffer; |
| |
| /* if someone is sending us data, just throw it away */ |
| if (inout) |
| return length; |
| |
| /* 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__); |
| |
| /* |
| * Calculate start of next buffer, and return value. |
| */ |
| *start = buffer + offset; |
| |
| if ((pos - buffer) < offset) |
| return 0; |
| else if ((pos - buffer - offset) < length) |
| return pos - buffer - offset; |
| else |
| return length; |
| } |
| |
| /* 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; |
| } |
| |
| #if 0 /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) */ |
| int queuecommand(struct scsi_cmnd *srb, void (*done) (struct scsi_cmnd *)) |
| { |
| return queuecommand_lck(srb, done); |
| } |
| #else |
| DEF_SCSI_QCMD(queuecommand) |
| #endif |
| /*********************************************************************** |
| * 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 */ |
| 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, |
| .proc_info = proc_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 |
| }; |
| |