18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Support for emulating SAT (ata pass through) on devices based 48c2ecf20Sopenharmony_ci * on the Cypress USB/ATA bridge supporting ATACB. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (c) 2008 Matthieu Castet (castet.matthieu@free.fr) 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <scsi/scsi.h> 118c2ecf20Sopenharmony_ci#include <scsi/scsi_cmnd.h> 128c2ecf20Sopenharmony_ci#include <scsi/scsi_eh.h> 138c2ecf20Sopenharmony_ci#include <linux/ata.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "usb.h" 168c2ecf20Sopenharmony_ci#include "protocol.h" 178c2ecf20Sopenharmony_ci#include "scsiglue.h" 188c2ecf20Sopenharmony_ci#include "debug.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define DRV_NAME "ums-cypress" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SAT support for Cypress USB/ATA bridges with ATACB"); 238c2ecf20Sopenharmony_ciMODULE_AUTHOR("Matthieu Castet <castet.matthieu@free.fr>"); 248c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 258c2ecf20Sopenharmony_ciMODULE_IMPORT_NS(USB_STORAGE); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* 288c2ecf20Sopenharmony_ci * The table of devices 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_ci#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ 318c2ecf20Sopenharmony_ci vendorName, productName, useProtocol, useTransport, \ 328c2ecf20Sopenharmony_ci initFunction, flags) \ 338c2ecf20Sopenharmony_ci{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ 348c2ecf20Sopenharmony_ci .driver_info = (flags) } 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic struct usb_device_id cypress_usb_ids[] = { 378c2ecf20Sopenharmony_ci# include "unusual_cypress.h" 388c2ecf20Sopenharmony_ci { } /* Terminating entry */ 398c2ecf20Sopenharmony_ci}; 408c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, cypress_usb_ids); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#undef UNUSUAL_DEV 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* 458c2ecf20Sopenharmony_ci * The flags table 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_ci#define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \ 488c2ecf20Sopenharmony_ci vendor_name, product_name, use_protocol, use_transport, \ 498c2ecf20Sopenharmony_ci init_function, Flags) \ 508c2ecf20Sopenharmony_ci{ \ 518c2ecf20Sopenharmony_ci .vendorName = vendor_name, \ 528c2ecf20Sopenharmony_ci .productName = product_name, \ 538c2ecf20Sopenharmony_ci .useProtocol = use_protocol, \ 548c2ecf20Sopenharmony_ci .useTransport = use_transport, \ 558c2ecf20Sopenharmony_ci .initFunction = init_function, \ 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic struct us_unusual_dev cypress_unusual_dev_list[] = { 598c2ecf20Sopenharmony_ci# include "unusual_cypress.h" 608c2ecf20Sopenharmony_ci { } /* Terminating entry */ 618c2ecf20Sopenharmony_ci}; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#undef UNUSUAL_DEV 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* 678c2ecf20Sopenharmony_ci * ATACB is a protocol used on cypress usb<->ata bridge to 688c2ecf20Sopenharmony_ci * send raw ATA command over mass storage 698c2ecf20Sopenharmony_ci * There is a ATACB2 protocol that support LBA48 on newer chip. 708c2ecf20Sopenharmony_ci * More info that be found on cy7c68310_8.pdf and cy7c68300c_8.pdf 718c2ecf20Sopenharmony_ci * datasheet from cypress.com. 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_cistatic void cypress_atacb_passthrough(struct scsi_cmnd *srb, struct us_data *us) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci unsigned char save_cmnd[MAX_COMMAND_SIZE]; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (likely(srb->cmnd[0] != ATA_16 && srb->cmnd[0] != ATA_12)) { 788c2ecf20Sopenharmony_ci usb_stor_transparent_scsi_command(srb, us); 798c2ecf20Sopenharmony_ci return; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci memcpy(save_cmnd, srb->cmnd, sizeof(save_cmnd)); 838c2ecf20Sopenharmony_ci memset(srb->cmnd, 0, MAX_COMMAND_SIZE); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci /* check if we support the command */ 868c2ecf20Sopenharmony_ci if (save_cmnd[1] >> 5) /* MULTIPLE_COUNT */ 878c2ecf20Sopenharmony_ci goto invalid_fld; 888c2ecf20Sopenharmony_ci /* check protocol */ 898c2ecf20Sopenharmony_ci switch ((save_cmnd[1] >> 1) & 0xf) { 908c2ecf20Sopenharmony_ci case 3: /*no DATA */ 918c2ecf20Sopenharmony_ci case 4: /* PIO in */ 928c2ecf20Sopenharmony_ci case 5: /* PIO out */ 938c2ecf20Sopenharmony_ci break; 948c2ecf20Sopenharmony_ci default: 958c2ecf20Sopenharmony_ci goto invalid_fld; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* first build the ATACB command */ 998c2ecf20Sopenharmony_ci srb->cmd_len = 16; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci srb->cmnd[0] = 0x24; /* 1028c2ecf20Sopenharmony_ci * bVSCBSignature : vendor-specific command 1038c2ecf20Sopenharmony_ci * this value can change, but most(all ?) manufacturers 1048c2ecf20Sopenharmony_ci * keep the cypress default : 0x24 1058c2ecf20Sopenharmony_ci */ 1068c2ecf20Sopenharmony_ci srb->cmnd[1] = 0x24; /* bVSCBSubCommand : 0x24 for ATACB */ 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci srb->cmnd[3] = 0xff - 1; /* 1098c2ecf20Sopenharmony_ci * features, sector count, lba low, lba med 1108c2ecf20Sopenharmony_ci * lba high, device, command are valid 1118c2ecf20Sopenharmony_ci */ 1128c2ecf20Sopenharmony_ci srb->cmnd[4] = 1; /* TransferBlockCount : 512 */ 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (save_cmnd[0] == ATA_16) { 1158c2ecf20Sopenharmony_ci srb->cmnd[ 6] = save_cmnd[ 4]; /* features */ 1168c2ecf20Sopenharmony_ci srb->cmnd[ 7] = save_cmnd[ 6]; /* sector count */ 1178c2ecf20Sopenharmony_ci srb->cmnd[ 8] = save_cmnd[ 8]; /* lba low */ 1188c2ecf20Sopenharmony_ci srb->cmnd[ 9] = save_cmnd[10]; /* lba med */ 1198c2ecf20Sopenharmony_ci srb->cmnd[10] = save_cmnd[12]; /* lba high */ 1208c2ecf20Sopenharmony_ci srb->cmnd[11] = save_cmnd[13]; /* device */ 1218c2ecf20Sopenharmony_ci srb->cmnd[12] = save_cmnd[14]; /* command */ 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (save_cmnd[1] & 0x01) {/* extended bit set for LBA48 */ 1248c2ecf20Sopenharmony_ci /* this could be supported by atacb2 */ 1258c2ecf20Sopenharmony_ci if (save_cmnd[3] || save_cmnd[5] || save_cmnd[7] || save_cmnd[9] 1268c2ecf20Sopenharmony_ci || save_cmnd[11]) 1278c2ecf20Sopenharmony_ci goto invalid_fld; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci } else { /* ATA12 */ 1308c2ecf20Sopenharmony_ci srb->cmnd[ 6] = save_cmnd[3]; /* features */ 1318c2ecf20Sopenharmony_ci srb->cmnd[ 7] = save_cmnd[4]; /* sector count */ 1328c2ecf20Sopenharmony_ci srb->cmnd[ 8] = save_cmnd[5]; /* lba low */ 1338c2ecf20Sopenharmony_ci srb->cmnd[ 9] = save_cmnd[6]; /* lba med */ 1348c2ecf20Sopenharmony_ci srb->cmnd[10] = save_cmnd[7]; /* lba high */ 1358c2ecf20Sopenharmony_ci srb->cmnd[11] = save_cmnd[8]; /* device */ 1368c2ecf20Sopenharmony_ci srb->cmnd[12] = save_cmnd[9]; /* command */ 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci /* Filter SET_FEATURES - XFER MODE command */ 1408c2ecf20Sopenharmony_ci if ((srb->cmnd[12] == ATA_CMD_SET_FEATURES) 1418c2ecf20Sopenharmony_ci && (srb->cmnd[6] == SETFEATURES_XFER)) 1428c2ecf20Sopenharmony_ci goto invalid_fld; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (srb->cmnd[12] == ATA_CMD_ID_ATA || srb->cmnd[12] == ATA_CMD_ID_ATAPI) 1458c2ecf20Sopenharmony_ci srb->cmnd[2] |= (1<<7); /* set IdentifyPacketDevice for these cmds */ 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci usb_stor_transparent_scsi_command(srb, us); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* if the device doesn't support ATACB */ 1518c2ecf20Sopenharmony_ci if (srb->result == SAM_STAT_CHECK_CONDITION && 1528c2ecf20Sopenharmony_ci memcmp(srb->sense_buffer, usb_stor_sense_invalidCDB, 1538c2ecf20Sopenharmony_ci sizeof(usb_stor_sense_invalidCDB)) == 0) { 1548c2ecf20Sopenharmony_ci usb_stor_dbg(us, "cypress atacb not supported ???\n"); 1558c2ecf20Sopenharmony_ci goto end; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* 1598c2ecf20Sopenharmony_ci * if ck_cond flags is set, and there wasn't critical error, 1608c2ecf20Sopenharmony_ci * build the special sense 1618c2ecf20Sopenharmony_ci */ 1628c2ecf20Sopenharmony_ci if ((srb->result != (DID_ERROR << 16) && 1638c2ecf20Sopenharmony_ci srb->result != (DID_ABORT << 16)) && 1648c2ecf20Sopenharmony_ci save_cmnd[2] & 0x20) { 1658c2ecf20Sopenharmony_ci struct scsi_eh_save ses; 1668c2ecf20Sopenharmony_ci unsigned char regs[8]; 1678c2ecf20Sopenharmony_ci unsigned char *sb = srb->sense_buffer; 1688c2ecf20Sopenharmony_ci unsigned char *desc = sb + 8; 1698c2ecf20Sopenharmony_ci int tmp_result; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* build the command for reading the ATA registers */ 1728c2ecf20Sopenharmony_ci scsi_eh_prep_cmnd(srb, &ses, NULL, 0, sizeof(regs)); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* 1758c2ecf20Sopenharmony_ci * we use the same command as before, but we set 1768c2ecf20Sopenharmony_ci * the read taskfile bit, for not executing atacb command, 1778c2ecf20Sopenharmony_ci * but reading register selected in srb->cmnd[4] 1788c2ecf20Sopenharmony_ci */ 1798c2ecf20Sopenharmony_ci srb->cmd_len = 16; 1808c2ecf20Sopenharmony_ci srb->cmnd = ses.cmnd; 1818c2ecf20Sopenharmony_ci srb->cmnd[2] = 1; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci usb_stor_transparent_scsi_command(srb, us); 1848c2ecf20Sopenharmony_ci memcpy(regs, srb->sense_buffer, sizeof(regs)); 1858c2ecf20Sopenharmony_ci tmp_result = srb->result; 1868c2ecf20Sopenharmony_ci scsi_eh_restore_cmnd(srb, &ses); 1878c2ecf20Sopenharmony_ci /* we fail to get registers, report invalid command */ 1888c2ecf20Sopenharmony_ci if (tmp_result != SAM_STAT_GOOD) 1898c2ecf20Sopenharmony_ci goto invalid_fld; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* build the sense */ 1928c2ecf20Sopenharmony_ci memset(sb, 0, SCSI_SENSE_BUFFERSIZE); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* set sk, asc for a good command */ 1958c2ecf20Sopenharmony_ci sb[1] = RECOVERED_ERROR; 1968c2ecf20Sopenharmony_ci sb[2] = 0; /* ATA PASS THROUGH INFORMATION AVAILABLE */ 1978c2ecf20Sopenharmony_ci sb[3] = 0x1D; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* 2008c2ecf20Sopenharmony_ci * XXX we should generate sk, asc, ascq from status and error 2018c2ecf20Sopenharmony_ci * regs 2028c2ecf20Sopenharmony_ci * (see 11.1 Error translation ATA device error to SCSI error 2038c2ecf20Sopenharmony_ci * map, and ata_to_sense_error from libata.) 2048c2ecf20Sopenharmony_ci */ 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* Sense data is current and format is descriptor. */ 2078c2ecf20Sopenharmony_ci sb[0] = 0x72; 2088c2ecf20Sopenharmony_ci desc[0] = 0x09; /* ATA_RETURN_DESCRIPTOR */ 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci /* set length of additional sense data */ 2118c2ecf20Sopenharmony_ci sb[7] = 14; 2128c2ecf20Sopenharmony_ci desc[1] = 12; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* Copy registers into sense buffer. */ 2158c2ecf20Sopenharmony_ci desc[ 2] = 0x00; 2168c2ecf20Sopenharmony_ci desc[ 3] = regs[1]; /* features */ 2178c2ecf20Sopenharmony_ci desc[ 5] = regs[2]; /* sector count */ 2188c2ecf20Sopenharmony_ci desc[ 7] = regs[3]; /* lba low */ 2198c2ecf20Sopenharmony_ci desc[ 9] = regs[4]; /* lba med */ 2208c2ecf20Sopenharmony_ci desc[11] = regs[5]; /* lba high */ 2218c2ecf20Sopenharmony_ci desc[12] = regs[6]; /* device */ 2228c2ecf20Sopenharmony_ci desc[13] = regs[7]; /* command */ 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci srb->result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci goto end; 2278c2ecf20Sopenharmony_ciinvalid_fld: 2288c2ecf20Sopenharmony_ci srb->result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci memcpy(srb->sense_buffer, 2318c2ecf20Sopenharmony_ci usb_stor_sense_invalidCDB, 2328c2ecf20Sopenharmony_ci sizeof(usb_stor_sense_invalidCDB)); 2338c2ecf20Sopenharmony_ciend: 2348c2ecf20Sopenharmony_ci memcpy(srb->cmnd, save_cmnd, sizeof(save_cmnd)); 2358c2ecf20Sopenharmony_ci if (srb->cmnd[0] == ATA_12) 2368c2ecf20Sopenharmony_ci srb->cmd_len = 12; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic struct scsi_host_template cypress_host_template; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic int cypress_probe(struct usb_interface *intf, 2428c2ecf20Sopenharmony_ci const struct usb_device_id *id) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci struct us_data *us; 2458c2ecf20Sopenharmony_ci int result; 2468c2ecf20Sopenharmony_ci struct usb_device *device; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci result = usb_stor_probe1(&us, intf, id, 2498c2ecf20Sopenharmony_ci (id - cypress_usb_ids) + cypress_unusual_dev_list, 2508c2ecf20Sopenharmony_ci &cypress_host_template); 2518c2ecf20Sopenharmony_ci if (result) 2528c2ecf20Sopenharmony_ci return result; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* 2558c2ecf20Sopenharmony_ci * Among CY7C68300 chips, the A revision does not support Cypress ATACB 2568c2ecf20Sopenharmony_ci * Filter out this revision from EEPROM default descriptor values 2578c2ecf20Sopenharmony_ci */ 2588c2ecf20Sopenharmony_ci device = interface_to_usbdev(intf); 2598c2ecf20Sopenharmony_ci if (device->descriptor.iManufacturer != 0x38 || 2608c2ecf20Sopenharmony_ci device->descriptor.iProduct != 0x4e || 2618c2ecf20Sopenharmony_ci device->descriptor.iSerialNumber != 0x64) { 2628c2ecf20Sopenharmony_ci us->protocol_name = "Transparent SCSI with Cypress ATACB"; 2638c2ecf20Sopenharmony_ci us->proto_handler = cypress_atacb_passthrough; 2648c2ecf20Sopenharmony_ci } else { 2658c2ecf20Sopenharmony_ci us->protocol_name = "Transparent SCSI"; 2668c2ecf20Sopenharmony_ci us->proto_handler = usb_stor_transparent_scsi_command; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci result = usb_stor_probe2(us); 2708c2ecf20Sopenharmony_ci return result; 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic struct usb_driver cypress_driver = { 2748c2ecf20Sopenharmony_ci .name = DRV_NAME, 2758c2ecf20Sopenharmony_ci .probe = cypress_probe, 2768c2ecf20Sopenharmony_ci .disconnect = usb_stor_disconnect, 2778c2ecf20Sopenharmony_ci .suspend = usb_stor_suspend, 2788c2ecf20Sopenharmony_ci .resume = usb_stor_resume, 2798c2ecf20Sopenharmony_ci .reset_resume = usb_stor_reset_resume, 2808c2ecf20Sopenharmony_ci .pre_reset = usb_stor_pre_reset, 2818c2ecf20Sopenharmony_ci .post_reset = usb_stor_post_reset, 2828c2ecf20Sopenharmony_ci .id_table = cypress_usb_ids, 2838c2ecf20Sopenharmony_ci .soft_unbind = 1, 2848c2ecf20Sopenharmony_ci .no_dynamic_id = 1, 2858c2ecf20Sopenharmony_ci}; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cimodule_usb_stor_driver(cypress_driver, cypress_host_template, DRV_NAME); 288