diff options
Diffstat (limited to 'src/hddtemp_scsi/scsicmds.c')
-rw-r--r-- | src/hddtemp_scsi/scsicmds.c | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/src/hddtemp_scsi/scsicmds.c b/src/hddtemp_scsi/scsicmds.c new file mode 100644 index 0000000..3449203 --- /dev/null +++ b/src/hddtemp_scsi/scsicmds.c @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2002 Emmanuel VARAGNAT <hddtemp@guzu.net> + * + * 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 of the License, 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +// Include file generated by ./configure +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +// Gettext includes +#if ENABLE_NLS +#include <libintl.h> +#define _(String) gettext (String) +#else +#define _(String) (String) +#endif + +// Standard includes +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <scsi/scsi.h> +#include <scsi/sg.h> +#include <scsi/scsi_ioctl.h> + +// Application specific includes +#include "scsicmds.h" + +static void scsi_fixstring(unsigned char *s, int bytecount) +{ + unsigned char *p; + unsigned char *end; + + p = s; + end = s + bytecount; + + /* strip leading blanks */ + while (s != end && *s == ' ') + ++s; + /* compress internal blanks and strip trailing blanks */ + while (s != end && *s) { + if (*s++ != ' ' || (s != end && *s && *s != ' ')) + *p++ = *(s-1); + } + /* wipe out trailing garbage */ + while (p != end) + *p++ = '\0'; +} + +int scsi_SG_IO(int device, unsigned char *cdb, int cdb_len, unsigned char *buffer, int buffer_len, unsigned char *sense, unsigned char sense_len, int dxfer_direction) { + struct sg_io_hdr io_hdr; + + memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); + io_hdr.interface_id = 'S'; + io_hdr.cmdp = cdb; + io_hdr.cmd_len = cdb_len; + io_hdr.dxfer_len = buffer_len; + io_hdr.dxferp = buffer; + io_hdr.mx_sb_len = sense_len; + io_hdr.sbp = sense; + io_hdr.dxfer_direction = dxfer_direction; + io_hdr.timeout = 3000; /* 3 seconds should be ample */ + + return ioctl(device, SG_IO, &io_hdr); +} + +int scsi_SEND_COMMAND(int device, unsigned char *cdb, int cdb_len, unsigned char *buffer, int buffer_len, int dxfer_direction) +{ + unsigned char buf[2048]; + unsigned int inbufsize, outbufsize, ret; + + switch(dxfer_direction) { + case SG_DXFER_FROM_DEV: + inbufsize = 0; + outbufsize = buffer_len; + break; + case SG_DXFER_TO_DEV: + inbufsize = buffer_len; + outbufsize = 0; + break; + default: + inbufsize = 0; + outbufsize = 0; + break; + } + memcpy(buf, &inbufsize , sizeof(inbufsize)); + memcpy(buf + sizeof(inbufsize), &outbufsize , sizeof(outbufsize)); + memcpy(buf + sizeof(inbufsize) + sizeof(outbufsize), cdb, cdb_len); + memcpy(buf + sizeof(inbufsize) + sizeof(outbufsize) + cdb_len, buffer, buffer_len); + + ret = ioctl(device, SCSI_IOCTL_SEND_COMMAND, buf); + + memcpy(buffer, buf + sizeof(inbufsize) + sizeof(outbufsize), buffer_len); + + return ret; +} + +int scsi_command(int device, unsigned char *cdb, int cdb_len, unsigned char *buffer, int buffer_len, int dxfer_direction) +{ + static int SG_IO_supported = -1; + int ret; + + if (SG_IO_supported == 1) + return scsi_SG_IO(device, cdb, cdb_len, buffer, buffer_len, NULL, 0, dxfer_direction); + else if (SG_IO_supported == 0) + return scsi_SEND_COMMAND(device, cdb, cdb_len, buffer, buffer_len, dxfer_direction); + else { + ret = scsi_SG_IO(device, cdb, cdb_len, buffer, buffer_len, NULL, 0, dxfer_direction); + if (ret == 0) { + SG_IO_supported = 1; + return ret; + } else { + SG_IO_supported = 0; + return scsi_SEND_COMMAND(device, cdb, cdb_len, buffer, buffer_len, dxfer_direction); + } + } +} + +int scsi_inquiry(int device, unsigned char *buffer) +{ + unsigned char cdb[6]; + + memset(cdb, 0, sizeof(cdb)); + cdb[0] = INQUIRY; + cdb[4] = 36; /* should be 36 for unsafe devices (like USB mass storage stuff) + * otherwise they can lock up! SPC sections 7.4 and 8.6 */ + + if (scsi_command(device, cdb, sizeof(cdb), buffer, cdb[4], SG_DXFER_FROM_DEV) != 0) + return 1; + else { + scsi_fixstring(buffer + 8, 24); + return 0; + } +} + +int scsi_modesense(int device, unsigned char pagenum, unsigned char *buffer, int buffer_len) { + unsigned char cdb[6]; + int ret; + + memset(cdb, 0, sizeof(cdb)); + cdb[0] = MODE_SENSE; + cdb[2] = pagenum; + cdb[4] = 0xff; + + ret = scsi_command(device, cdb, sizeof(cdb), buffer, buffer_len, SG_DXFER_FROM_DEV); + if (ret == 0) { + if ((buffer[3] + 5) > buffer[0]) /* response length too short */ + return -1; + } + return ret; +} + +int scsi_modeselect(int device, char *buffer) { + unsigned char cdb[6]; + + memset(cdb, 0, sizeof(cdb)); + cdb[0] = MODE_SELECT; + cdb[1] = 0x11; + cdb[4] = buffer[0] + 1; + + memset(buffer, 0, 12); + buffer[3] = 0x08; + buffer[10] = 0x02; + buffer[12] &= 0x3f; + + return scsi_command(device, cdb, sizeof(cdb), buffer, cdb[4], SG_DXFER_TO_DEV); +} + +int scsi_logsense(int device, int pagenum, unsigned char *buffer, int buffer_len) { + unsigned char cdb[10]; + + memset(cdb, 0, sizeof(cdb)); + cdb[0] = LOG_SENSE; + cdb[2] = 0x40 | pagenum; + cdb[7] = 0x04; + + return scsi_command(device, cdb, sizeof(cdb), buffer, buffer_len, SG_DXFER_FROM_DEV); +} + +int scsi_smartsupport(int device) { + unsigned char buf[255]; + + if (scsi_modesense (device, EXCEPTIONS_CONTROL_PAGE, buf, sizeof(buf)) != 0) + return 0; + else + return (buf[14] & 0x08) == 0; +} + +int scsi_smartDEXCPTdisable(int device) { + unsigned char buf[255]; + + if (scsi_modesense (device, EXCEPTIONS_CONTROL_PAGE, buf, sizeof(buf)) != 0) + return 1; + + if (buf[14] & 0x08) { + buf[14] &= 0xf7; + buf[15] = 0x04; + return scsi_modeselect (device, buf); + } + else + return 0; +} + |