summaryrefslogtreecommitdiffstats
path: root/src/hddtemp_scsi/scsicmds.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/hddtemp_scsi/scsicmds.c')
-rw-r--r--src/hddtemp_scsi/scsicmds.c221
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;
+}
+