+ int passthru_size = DEF_SAT_ATA_PASSTHRU_SIZE;
+
+ memset(cdb, 0, sizeof(cdb));
+ memset(sense, 0, sizeof(sense));
+
+ // Set data direction
+ // TODO: This works only for commands where sector_count holds count!
+ switch (in.direction) {
+ case ata_cmd_in::no_data:
+ break;
+ case ata_cmd_in::data_in:
+ protocol = 4; // PIO data-in
+ t_length = 2; // sector_count holds count
+ break;
+ case ata_cmd_in::data_out:
+ protocol = 5; // PIO data-out
+ t_length = 2; // sector_count holds count
+ t_dir = 0; // to device
+ break;
+ default:
+ return set_err(EINVAL, "sat_device::ata_pass_through: invalid direction=%d",
+ (int)in.direction);
+ }
+
+ // Check condition if any output register needed
+ if (in.out_needed.is_set())
+ ck_cond = 1;
+
+ if ((SAT_ATA_PASSTHROUGH_12LEN == m_passthrulen) ||
+ (SAT_ATA_PASSTHROUGH_16LEN == m_passthrulen))
+ passthru_size = m_passthrulen;
+
+ // Set extend bit on 48-bit ATA command
+ if (in.in_regs.is_48bit_cmd()) {
+ if (passthru_size != SAT_ATA_PASSTHROUGH_16LEN)
+ return set_err(ENOSYS, "48-bit ATA commands require SAT ATA PASS-THROUGH (16)");
+ extend = 1;
+ }
+
+ cdb[0] = (SAT_ATA_PASSTHROUGH_12LEN == passthru_size) ?
+ SAT_ATA_PASSTHROUGH_12 : SAT_ATA_PASSTHROUGH_16;
+
+ cdb[1] = (protocol << 1) | extend;
+ cdb[2] = (ck_cond << 5) | (t_dir << 3) |
+ (byte_block << 2) | t_length;
+
+ if (passthru_size == SAT_ATA_PASSTHROUGH_12LEN) {
+ // ATA PASS-THROUGH (12)
+ const ata_in_regs & lo = in.in_regs;
+ cdb[3] = lo.features;
+ cdb[4] = lo.sector_count;
+ cdb[5] = lo.lba_low;
+ cdb[6] = lo.lba_mid;
+ cdb[7] = lo.lba_high;
+ cdb[8] = lo.device;
+ cdb[9] = lo.command;
+ }
+ else {
+ // ATA PASS-THROUGH (16)
+ const ata_in_regs & lo = in.in_regs;
+ const ata_in_regs & hi = in.in_regs.prev;
+ // Note: all 'in.in_regs.prev.*' are always zero for 28-bit commands
+ cdb[ 3] = hi.features;
+ cdb[ 4] = lo.features;
+ cdb[ 5] = hi.sector_count;
+ cdb[ 6] = lo.sector_count;
+ cdb[ 7] = hi.lba_low;
+ cdb[ 8] = lo.lba_low;
+ cdb[ 9] = hi.lba_mid;
+ cdb[10] = lo.lba_mid;
+ cdb[11] = hi.lba_high;
+ cdb[12] = lo.lba_high;
+ cdb[13] = lo.device;
+ cdb[14] = lo.command;
+ }
+
+ memset(&io_hdr, 0, sizeof(io_hdr));
+ if (0 == t_length) {
+ io_hdr.dxfer_dir = DXFER_NONE;
+ io_hdr.dxfer_len = 0;
+ } else if (t_dir) { /* from device */
+ io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
+ io_hdr.dxfer_len = in.size;
+ io_hdr.dxferp = (unsigned char *)in.buffer;
+ memset(in.buffer, 0, in.size); // prefill with zeroes
+ } else { /* to device */
+ io_hdr.dxfer_dir = DXFER_TO_DEVICE;
+ io_hdr.dxfer_len = in.size;
+ io_hdr.dxferp = (unsigned char *)in.buffer;
+ }
+ io_hdr.cmnd = cdb;
+ io_hdr.cmnd_len = passthru_size;
+ io_hdr.sensep = sense;
+ io_hdr.max_sense_len = sizeof(sense);
+ io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
+
+ scsi_device * scsidev = get_tunnel_dev();
+ if (!scsidev->scsi_pass_through(&io_hdr)) {
+ if (scsi_debugmode > 0)
+ pout("sat_device::ata_pass_through: scsi_pass_through() failed, "
+ "errno=%d [%s]\n", scsidev->get_errno(), scsidev->get_errmsg());
+ return set_err(scsidev->get_err());
+ }
+ ardp = NULL;
+ ard_len = 0;
+ have_sense = sg_scsi_normalize_sense(io_hdr.sensep, io_hdr.resp_sense_len,
+ &ssh);
+ if (have_sense) {
+ /* look for SAT ATA Return Descriptor */
+ ardp = sg_scsi_sense_desc_find(io_hdr.sensep,
+ io_hdr.resp_sense_len,
+ ATA_RETURN_DESCRIPTOR);
+ if (ardp) {
+ ard_len = ardp[1] + 2;
+ if (ard_len < 12)
+ ard_len = 12;
+ else if (ard_len > 14)
+ ard_len = 14;
+ }
+ scsi_do_sense_disect(&io_hdr, &sinfo);
+ status = scsiSimpleSenseFilter(&sinfo);
+ if (0 != status) {
+ if (scsi_debugmode > 0) {
+ pout("sat_device::ata_pass_through: scsi error: %s\n",
+ scsiErrString(status));
+ if (ardp && (scsi_debugmode > 1)) {
+ pout("Values from ATA Return Descriptor are:\n");
+ dStrHex((const char *)ardp, ard_len, 1);
+ }
+ }
+ if (t_dir && (t_length > 0) && (in.direction == ata_cmd_in::data_in))
+ memset(in.buffer, 0, in.size);
+ return set_err(EIO, "scsi error %s", scsiErrString(status));
+ }
+ }
+ if (ck_cond) { /* expecting SAT specific sense data */
+ if (have_sense) {
+ if (ardp) {
+ if (scsi_debugmode > 1) {
+ pout("Values from ATA Return Descriptor are:\n");
+ dStrHex((const char *)ardp, ard_len, 1);
+ }
+ // Set output registers
+ ata_out_regs & lo = out.out_regs;
+ lo.error = ardp[ 3];
+ lo.sector_count = ardp[ 5];
+ lo.lba_low = ardp[ 7];
+ lo.lba_mid = ardp[ 9];
+ lo.lba_high = ardp[11];
+ lo.device = ardp[12];
+ lo.status = ardp[13];
+ if (in.in_regs.is_48bit_cmd()) {
+ ata_out_regs & hi = out.out_regs.prev;
+ hi.sector_count = ardp[ 4];
+ hi.lba_low = ardp[ 6];
+ hi.lba_mid = ardp[ 8];
+ hi.lba_high = ardp[10];
+ }
+ }
+ }
+ if (ardp == NULL)
+ ck_cond = 0; /* not the type of sense data expected */
+ }
+ if (0 == ck_cond) {
+ if (have_sense) {
+ if ((ssh.response_code >= 0x72) &&
+ ((SCSI_SK_NO_SENSE == ssh.sense_key) ||
+ (SCSI_SK_RECOVERED_ERR == ssh.sense_key)) &&
+ (0 == ssh.asc) &&
+ (SCSI_ASCQ_ATA_PASS_THROUGH == ssh.ascq)) {
+ if (ardp) {
+ if (scsi_debugmode > 0) {
+ pout("Values from ATA Return Descriptor are:\n");
+ dStrHex((const char *)ardp, ard_len, 1);
+ }
+ return set_err(EIO, "SAT command failed");
+ }
+ }
+ }
+ }
+ return true;
+}
+
+bool sat_device::scsi_pass_through(scsi_cmnd_io * iop)
+{
+ scsi_device * scsidev = get_tunnel_dev();
+ if (!scsidev->scsi_pass_through(iop)) {
+ set_err(scsidev->get_err());
+ return false;
+ }
+ return true;
+}
+
+smart_device * sat_device::autodetect_open()
+{
+ if (!open() || !m_enable_auto)
+ return this;
+
+ scsi_device * scsidev = get_tunnel_dev();
+
+ unsigned char inqdata[36] = {0, };
+ if (scsiStdInquiry(scsidev, inqdata, sizeof(inqdata))) {
+ smart_device::error_info err = scsidev->get_err();
+ close();
+ set_err(err.no, "INQUIRY [SAT]: %s", err.msg.c_str());
+ return this;
+ }
+
+ // Check for SAT "VENDOR"
+ int inqsize = inqdata[4] + 5;
+ bool sat = (inqsize >= 36 && !memcmp(inqdata + 8, "ATA ", 8));
+
+ // Change interface
+ hide_ata(!sat);
+ hide_scsi(sat);
+
+ set_info().dev_type = (sat ? "sat" : scsidev->get_dev_type());
+ set_info().info_name = strprintf("%s [%s]", scsidev->get_info_name(),
+ (sat ? "SAT" : "SCSI"));
+ return this;
+}
+
+} // namespace
+
+/////////////////////////////////////////////////////////////////////////////
+
+/* Attempt an IDENTIFY DEVICE ATA command via SATL when packet_interface
+ is false otherwise attempt IDENTIFY PACKET DEVICE. If successful
+ return true, else false */
+
+static bool has_sat_pass_through(ata_device * dev, bool packet_interface = false)
+{
+ /* Note: malloc() ensures the read buffer lands on a single
+ page. This avoids some bugs seen on LSI controlers under
+ FreeBSD */
+ char *data = (char *)malloc(512);
+ ata_cmd_in in;
+ in.in_regs.command = (packet_interface ? ATA_IDENTIFY_PACKET_DEVICE : ATA_IDENTIFY_DEVICE);
+ in.set_data_in(data, 1);
+ bool ret = dev->ata_pass_through(in);
+ free(data);
+ return ret;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+/* Next two functions are borrowed from sg_lib.c in the sg3_utils
+ package. Same copyrght owner, same license as this file. */
+static int sg_scsi_normalize_sense(const unsigned char * sensep, int sb_len,
+ struct sg_scsi_sense_hdr * sshp)
+{
+ if (sshp)
+ memset(sshp, 0, sizeof(struct sg_scsi_sense_hdr));
+ if ((NULL == sensep) || (0 == sb_len) || (0x70 != (0x70 & sensep[0])))
+ return 0;
+ if (sshp) {
+ sshp->response_code = (0x7f & sensep[0]);
+ if (sshp->response_code >= 0x72) { /* descriptor format */
+ if (sb_len > 1)
+ sshp->sense_key = (0xf & sensep[1]);
+ if (sb_len > 2)
+ sshp->asc = sensep[2];
+ if (sb_len > 3)
+ sshp->ascq = sensep[3];
+ if (sb_len > 7)
+ sshp->additional_length = sensep[7];
+ } else { /* fixed format */
+ if (sb_len > 2)
+ sshp->sense_key = (0xf & sensep[2]);
+ if (sb_len > 7) {
+ sb_len = (sb_len < (sensep[7] + 8)) ? sb_len :
+ (sensep[7] + 8);
+ if (sb_len > 12)
+ sshp->asc = sensep[12];
+ if (sb_len > 13)
+ sshp->ascq = sensep[13];
+ }
+ }
+ }
+ return 1;
+}
+
+
+// Call scsi_pass_through and check sense.
+// TODO: Provide as member function of class scsi_device (?)
+static bool scsi_pass_through_and_check(scsi_device * scsidev, scsi_cmnd_io * iop,
+ const char * msg = "")
+{
+ // Provide sense buffer
+ unsigned char sense[32] = {0, };
+ iop->sensep = sense;
+ iop->max_sense_len = sizeof(sense);
+ iop->timeout = SCSI_TIMEOUT_DEFAULT;
+
+ // Run cmd
+ if (!scsidev->scsi_pass_through(iop)) {
+ if (scsi_debugmode > 0)
+ pout("%sscsi_pass_through() failed, errno=%d [%s]\n",
+ msg, scsidev->get_errno(), scsidev->get_errmsg());
+ return false;
+ }
+
+ // Check sense
+ scsi_sense_disect sinfo;
+ scsi_do_sense_disect(iop, &sinfo);
+ int err = scsiSimpleSenseFilter(&sinfo);
+ if (err) {
+ if (scsi_debugmode > 0)
+ pout("%sscsi error: %s\n", msg, scsiErrString(err));
+ return scsidev->set_err(EIO, "scsi error %s", scsiErrString(err));
+ }
+
+ return true;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+namespace sat {
+
+/// Cypress USB Brigde support.
+
+class usbcypress_device
+: public tunnelled_device<
+ /*implements*/ ata_device_with_command_set
+ /*by tunnelling through a*/, scsi_device
+ >
+{
+public:
+ usbcypress_device(smart_interface * intf, scsi_device * scsidev,
+ const char * req_type, unsigned char signature);
+
+ virtual ~usbcypress_device() throw();
+
+protected:
+ virtual int ata_command_interface(smart_command_set command, int select, char * data);
+
+ unsigned char m_signature;
+};
+
+
+usbcypress_device::usbcypress_device(smart_interface * intf, scsi_device * scsidev,
+ const char * req_type, unsigned char signature)
+: smart_device(intf, scsidev->get_dev_name(), "sat", req_type),
+ tunnelled_device<ata_device_with_command_set, scsi_device>(scsidev),
+ m_signature(signature)
+{
+ set_info().info_name = strprintf("%s [USB Cypress]", scsidev->get_info_name());
+}
+
+usbcypress_device::~usbcypress_device() throw()
+{
+}
+
+
+/* see cy7c68300c_8.pdf for more information */
+#define USBCYPRESS_PASSTHROUGH_LEN 16
+int usbcypress_device::ata_command_interface(smart_command_set command, int select, char *data)
+{
+ struct scsi_cmnd_io io_hdr;
+ unsigned char cdb[USBCYPRESS_PASSTHROUGH_LEN];
+ unsigned char sense[32];
+ int copydata = 0;
+ int outlen = 0;
+ int ck_cond = 0; /* set to 1 to read register(s) back */
+ int t_dir = 1; /* 0 -> to device, 1 -> from device */
+ int byte_block = 1; /* 0 -> bytes, 1 -> 512 byte blocks */
+ int t_length = 0; /* 0 -> no data transferred */