#include #include #include #include #include #include #include #include #include #include #include #include "hdparm.h" #include "sgio.h" /* * apt - Support for ATA PASS THROUGH devices. Currently supported only * JMicron devices. * * Copyright (c) 2009 Jan Friesse * * Magic numbers are taken from smartmontools source code * (http://smartmontools.sourceforge.net/) * * You may use/distribute this freely, under the terms of either * (your choice) the GNU General Public License version 2, * or a BSD style license. */ #ifdef SG_IO /* Device initialization functions */ static int apt_jmicron_int_init(int fd); /* Device sg16 functions*/ static int apt_jmicron_sg16(int fd, int rw, int dma, struct ata_tf *tf, void *data, unsigned int data_bytes, unsigned int timeout_secs); /* Structs */ struct apt_usb_id_entry { int vendor_id; int product_id; int version; const char *type; int (*init_func)(int fd); int (*sg16_func)(int fd, int rw, int dma, struct ata_tf *tf, void *data, unsigned int data_bytes, unsigned int timeout_secs); }; struct apt_data_struct { int is_apt; struct apt_usb_id_entry id; int verbose; union { struct { int port; } jmicron; }; }; static struct apt_data_struct apt_data; const char apt_ds_jmicron[] = "jmicron"; const char apt_ds_unsup[] = "unsupported"; const struct apt_usb_id_entry apt_usb_id_map[] = { {0x152d, 0x2329, 0x0100, apt_ds_jmicron, apt_jmicron_int_init, apt_jmicron_sg16}, /* JMicron JM20329 (USB->SATA) */ {0x152d, 0x2336, 0x0100, apt_ds_jmicron, apt_jmicron_int_init, apt_jmicron_sg16}, /* JMicron JM20336 (USB+SATA->SATA, USB->2xSATA) */ {0x152d, 0x2338, 0x0100, apt_ds_jmicron, apt_jmicron_int_init, apt_jmicron_sg16}, /* JMicron JM20337/8 (USB->SATA+PATA, USB+SATA->PATA) */ {0x152d, 0x2339, 0x0100, apt_ds_jmicron, apt_jmicron_int_init, apt_jmicron_sg16}, /* JMicron JM20339 (USB->SATA) */ {0x0c0b, 0xb157, 0x0100, apt_ds_jmicron, apt_jmicron_int_init, apt_jmicron_sg16} /* ioSafe Solo */ }; int apt_detect (int fd, int verbose) { int err; unsigned int i; apt_data.is_apt = 0; err = sysfs_get_attr_recursive(fd, "idVendor", "%x", &apt_data.id.vendor_id, NULL, verbose); if (err) { if (verbose) printf("APT: No idVendor found -> not USB bridge device\n"); return 0; } err = sysfs_get_attr_recursive(fd, "idProduct", "%x", &apt_data.id.product_id, NULL, verbose); if (err) return 0; err = sysfs_get_attr_recursive(fd, "bcdDevice", "%x", &apt_data.id.version, NULL, verbose); if (err) return 0; if (verbose) printf("APT: USB ID = 0x%04x:0x%04x (0x%03x)\n", apt_data.id.vendor_id, apt_data.id.product_id, apt_data.id.version); /* We have all needed informations, let's find if we support that device*/ for (i = 0; i < sizeof(apt_usb_id_map)/sizeof(*apt_usb_id_map); i++) { if (apt_data.id.vendor_id == apt_usb_id_map[i].vendor_id && apt_data.id.product_id == apt_usb_id_map[i].product_id) { /* Maybe two devices with same vendor and product id -> use version*/ if (apt_usb_id_map[i].version > 0 && apt_data.id.type && apt_usb_id_map[i].version == apt_data.id.version) { apt_data.id.type = apt_usb_id_map[i].type; apt_data.id.init_func = apt_usb_id_map[i].init_func; apt_data.id.sg16_func = apt_usb_id_map[i].sg16_func; } /* We don't have type -> set it (don't care about version) */ if (!apt_data.id.type) { apt_data.id.type = apt_usb_id_map[i].type; apt_data.id.init_func = apt_usb_id_map[i].init_func; apt_data.id.sg16_func = apt_usb_id_map[i].sg16_func; } } } if (!apt_data.id.type || apt_data.id.type == apt_ds_unsup) { if (verbose) printf("APT: Unsupported device\n"); return 0; } apt_data.is_apt = 1; if (verbose) printf("APT: Found supported device %s\n", apt_data.id.type); apt_data.verbose = verbose; return (apt_data.id.init_func(fd)); } int apt_is_apt (void) { return apt_data.is_apt; } int apt_sg16(int fd, int rw, int dma, struct ata_tf *tf, void *data, unsigned int data_bytes, unsigned int timeout_secs) { return apt_data.id.sg16_func(fd, rw, dma, tf, data, data_bytes, timeout_secs); } static void dump_bytes (const char *prefix, unsigned char *p, int len) { int i; if (prefix) fprintf(stderr, "%s: ", prefix); for (i = 0; i < len; ++i) fprintf(stderr, " %02x", p[i]); fprintf(stderr, "\n"); } /***** JMicron support ********/ static int apt_jmicron_int_sg(int fd, int rw, int dma, struct ata_tf *tf, void *data, unsigned int data_bytes, unsigned int timeout_secs, int port) { unsigned char cdb[12]; struct scsi_sg_io_hdr io_hdr; if (dma && apt_data.verbose) printf("APT: JMicron doesn't support DMA\n"); if (tf->is_lba48) { if (apt_data.verbose) fprintf(stderr, "APT: JMicron doesn't support 48-bit ATA commands\n"); errno = EBADE; return -1; } memset(&cdb, 0, sizeof(cdb)); memset(&io_hdr, 0, sizeof(struct scsi_sg_io_hdr)); // Build pass through command cdb[ 0] = 0xdf; cdb[ 1] = (rw ? 0x00 : 0x10); cdb[ 2] = 0x00; cdb[ 3] = (unsigned char)((data ? data_bytes : 0) >> 8); cdb[ 4] = (unsigned char)((data ? data_bytes : 0) ); cdb[ 5] = tf->lob.feat; cdb[ 6] = tf->lob.nsect; cdb[ 7] = tf->lob.lbal; cdb[ 8] = tf->lob.lbam; cdb[ 9] = tf->lob.lbah; cdb[10] = (port ? port : apt_data.jmicron.port); cdb[11] = tf->command; io_hdr.interface_id = 'S'; io_hdr.mx_sb_len = 0; io_hdr.dxfer_direction = data ? (rw ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV) : SG_DXFER_NONE; io_hdr.dxfer_len = data ? data_bytes : 0; io_hdr.dxferp = data; io_hdr.cmdp = cdb; io_hdr.pack_id = tf_to_lba(tf); io_hdr.timeout = (timeout_secs ? timeout_secs : 5) * 1000; /* msecs */ io_hdr.cmd_len = sizeof(cdb); if (apt_data.verbose) dump_bytes("outgoing cdb", cdb, sizeof(cdb)); if (ioctl(fd, SG_IO, &io_hdr) == -1) { if (apt_data.verbose) perror("ioctl(fd,SG_IO)"); return -1; /* SG_IO not supported */ } if (apt_data.verbose) fprintf(stderr, "SG_IO: ATA_%u status=0x%x, host_status=0x%x, driver_status=0x%x\n", io_hdr.cmd_len, io_hdr.status, io_hdr.host_status, io_hdr.driver_status); if (io_hdr.host_status || io_hdr.driver_status) { errno = EBADE; return -1; } return 0; } static int apt_jmicron_int_get_registers(int fd, unsigned short addr, unsigned char * buf, unsigned short size) { struct ata_tf tf; memset(&tf, 0, sizeof(tf)); tf.lob.feat = 0x00; tf.lob.nsect = (unsigned char)(addr >> 8); tf.lob.lbal = (unsigned char)(addr); tf.lob.lbam = 0x00; tf.lob.lbah = 0x00; tf.command = 0xfd; return apt_jmicron_int_sg(fd, 0, 0, &tf, buf, (unsigned int)size, 0, 0x00); } static int apt_jmicron_int_init(int fd) { unsigned char regbuf = 0; int res; if ((res = apt_jmicron_int_get_registers(fd, 0x720F, ®buf, 1)) == -1) { return res; } if (regbuf & 0x04) { apt_data.jmicron.port = 0xa0; } else if (regbuf & 0x40) { apt_data.jmicron.port = 0xb0; } else { perror("APT: No JMicron device connected"); errno = ENODEV; return -1; } if (apt_data.verbose) printf("APT: JMicron Port: 0x%X\n", apt_data.jmicron.port); return 0; } static int apt_jmicron_sg16(int fd, int rw, int dma, struct ata_tf *tf, void *data, unsigned int data_bytes, unsigned int timeout_secs) { return apt_jmicron_int_sg(fd, rw, dma, tf, data, data_bytes, timeout_secs, 0); } #else /* No SGIO -> no support*/ int apt_detect (int fd, int verbose) { if (verbose) printf("APT: SGIO Support needed for fd %d\n", fd); return 0; } int apt_is_apt (void) { return 0; } int apt_sg16(int fd, int rw, int dma, struct ata_tf *tf, void *data, unsigned int data_bytes, unsigned int timeout_secs) { printf("APT: SG16 fd %d rw %d dma %d tf %p data %p data_bytes %d timeout %d need SGIO\n", fd, rw, dma, tf, data, data_bytes, timeout_secs); return -1; } #endif