/* @(#)scsi-linux-sg.c 1.98 16/01/11 Copyright 1997-2016 J. Schilling */ #ifndef lint static char __sccsid[] = "@(#)scsi-linux-sg.c 1.98 16/01/11 Copyright 1997-2016 J. Schilling"; #endif /* * Interface for Linux generic SCSI implementation (sg). * * This is the interface for the broken Linux SCSI generic driver. * This is a hack, that tries to emulate the functionality * of the scg driver. * * Design flaws of the sg driver: * - cannot see if SCSI command could not be send * - cannot get SCSI status byte * - cannot get real dma count of tranfer * - cannot get number of bytes valid in auto sense data * - to few data in auto sense (CCS/SCSI-2/SCSI-3 needs >= 18) * * This code contains support for the sg driver version 2 by * H. Eißfeld & J. Schilling * Although this enhanced version has been announced to Linus and Alan, * there was no reaction at all. * * About half a year later there occured a version in the official * Linux that was also called version 2. The interface of this version * looks like a playground - the enhancements from this version are * more or less useless for a portable real-world program. * * With Linux 2.4 the official version of the sg driver is called 3.x * and seems to be usable again. The main problem now is the curious * interface that is provided to raise the DMA limit from 32 kB to a * more reasonable value. To do this in a reliable way, a lot of actions * are required. * * Warning: you may change this source, but if you do that * you need to change the _scg_version and _scg_auth* string below. * You may not return "schily" for an SCG_AUTHOR request anymore. * Choose your name instead of "schily" and make clear that the version * string is related to a modified source. * * Copyright (c) 1997-2016 J. Schilling */ /* * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * See the file CDDL.Schily.txt in this distribution for details. * A copy of the CDDL is also available via the Internet at * http://www.opensource.org/licenses/cddl1.txt * * The following exceptions apply: * CDDL §3.6 needs to be replaced by: "You may create a Larger Work by * combining Covered Software with other code if all other code is governed by * the terms of a license that is OSI approved (see www.opensource.org) and * you may distribute the Larger Work as a single product. In such a case, * You must make sure the requirements of this License are fulfilled for * the Covered Software." * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file CDDL.Schily.txt from this distribution. */ #include #ifndef LINUX_VERSION_CODE /* Very old kernel? */ # define LINUX_VERSION_CODE 0 #endif #if LINUX_VERSION_CODE >= 0x01031a /* introduced in 1.3.26 */ #if LINUX_VERSION_CODE >= 0x020000 /* introduced somewhere. */ /* Need to fine tune the ifdef so we get the transition point right. */ #if defined(HAVE_BROKEN_SCSI_SCSI_H) || \ defined(HAVE_BROKEN_SRC_SCSI_SCSI_H) /* * Be very careful in case that the Linux Kernel maintainers * unexpectedly fix the bugs in the Linux Lernel include files. * Only introduce the attempt for a workaround in case the include * files are broken anyway. */ #define __KERNEL__ #include #undef __KERNEL__ #endif #include #else #include #endif #else /* LINUX_VERSION_CODE == 0 Very old kernel? */ #define __KERNEL__ /* Some Linux Include files are inconsistent */ #include /* From ancient versions, really needed? */ #undef __KERNEL__ #include "block/blk.h" /* From ancient versions, really needed? */ #include "scsi/scsi.h" #endif #include #include #if defined(HAVE_BROKEN_SCSI_SG_H) || \ defined(HAVE_BROKEN_SRC_SCSI_SG_H) /* * Be very careful in case that the Linux Kernel maintainers * unexpectedly fix the bugs in the Linux Lernel include files. * Only introduce the attempt for a workaround in case the include * files are broken anyway. */ #define __user #endif #include "scsi/sg.h" #if defined(HAVE_BROKEN_SCSI_SG_H) || \ defined(HAVE_BROKEN_SRC_SCSI_SG_H) #undef __user #endif #undef sense /* conflict in struct cdrom_generic_command */ #include #if defined(CDROM_PACKET_SIZE) && defined(CDROM_SEND_PACKET) #define USE_ATAPI #endif /* * Warning: you may change this source, but if you do that * you need to change the _scg_version and _scg_auth* string below. * You may not return "schily" for an SCG_AUTHOR request anymore. * Choose your name instead of "schily" and make clear that the version * string is related to a modified source. */ LOCAL char _scg_trans_version[] = "scsi-linux-sg.c-1.98"; /* The version for this transport*/ #ifndef SCSI_IOCTL_GET_BUS_NUMBER #define SCSI_IOCTL_GET_BUS_NUMBER 0x5386 #endif /* * XXX There must be a better way than duplicating things from system include * XXX files. This is stolen from /usr/src/linux/drivers/scsi/scsi.h */ #ifndef DID_OK #define DID_OK 0x00 /* NO error */ #define DID_NO_CONNECT 0x01 /* Couldn't connect before timeout period */ #define DID_BUS_BUSY 0x02 /* BUS stayed busy through time out period */ #define DID_TIME_OUT 0x03 /* TIMED OUT for other reason */ #define DID_BAD_TARGET 0x04 /* BAD target. */ #define DID_ABORT 0x05 /* Told to abort for some other reason */ #define DID_PARITY 0x06 /* Parity error */ #define DID_ERROR 0x07 /* Internal error */ #define DID_RESET 0x08 /* Reset by somebody. */ #define DID_BAD_INTR 0x09 /* Got an interrupt we weren't expecting. */ #endif /* * These indicate the error that occurred, and what is available. */ #ifndef DRIVER_BUSY #define DRIVER_BUSY 0x01 #define DRIVER_SOFT 0x02 #define DRIVER_MEDIA 0x03 #define DRIVER_ERROR 0x04 #define DRIVER_INVALID 0x05 #define DRIVER_TIMEOUT 0x06 #define DRIVER_HARD 0x07 #define DRIVER_SENSE 0x08 #endif /* * XXX Should add extra space in buscookies and scgfiles for a "PP bus" * XXX and for two or more "ATAPI busses". */ #define MAX_SCG (512-MAX_ATA) /* Max # of SCSI controllers */ #define MAX_ATA 13 /* Max # of ATA devices */ #define MAX_TGT 16 #define MAX_LUN 8 #if (MAX_SCG+MAX_ATA) >= 1000 error too many SCSI busses #endif #ifdef USE_ATAPI /* * # of virtual buses (schilly_host number) */ #define MAX_ATAPI_HOSTS MAX_SCG typedef struct { Uchar typ:4; Uchar bus:4; Uchar host:8; } ata_buscookies; #endif struct scg_local { int scgfile; /* Used for SG_GET_BUFSIZE ioctl()*/ short scgfiles[MAX_SCG + MAX_ATA][MAX_TGT][MAX_LUN]; short buscookies[MAX_SCG + MAX_ATA]; int pgbus; int pack_id; /* Should be a random number */ int drvers; short isold; short flags; long xbufsize; char *xbuf; char *SCSIbuf; #ifdef USE_ATAPI ata_buscookies bc[MAX_ATAPI_HOSTS]; #endif }; #define scglocal(p) ((struct scg_local *)((p)->local)) /* * Flag definitions */ #define LF_ATA 0x01 /* Using /dev/hd* ATA interface */ #ifdef SG_BIG_BUFF #define MAX_DMA_LINUX SG_BIG_BUFF /* Defined in include/scsi/sg.h */ #else #define MAX_DMA_LINUX (4*1024) /* Old Linux versions */ #endif #ifndef SG_MAX_SENSE # define SG_MAX_SENSE 16 /* Too small for CCS / SCSI-2 */ #endif /* But cannot be changed */ #if !defined(__i386) && !defined(i386) && !defined(mc68000) #define MISALIGN #endif /*#define MISALIGN*/ /*#undef SG_GET_BUFSIZE*/ #if defined(USE_PG) && !defined(USE_PG_ONLY) #include "scsi-linux-pg.c" #endif #ifdef USE_ATAPI #include "scsi-linux-ata.c" #endif /* * Work around some broken Linux distributions with defective include files. */ #if !defined(HZ) && !defined(USER_HZ) #define USER_HZ 100 #endif #ifdef MISALIGN LOCAL int sg_getint __PR((int *ip)); #endif LOCAL int scgo_send __PR((SCSI *scgp)); #ifdef SG_IO LOCAL int sg_rwsend __PR((SCSI *scgp)); #endif LOCAL void sg_clearnblock __PR((int f)); LOCAL BOOL sg_setup __PR((SCSI *scgp, int f, int busno, int tgt, int tlun, int ataidx)); LOCAL void sg_initdev __PR((SCSI *scgp, int f)); LOCAL int sg_mapbus __PR((SCSI *scgp, int busno, int ino)); LOCAL BOOL sg_mapdev __PR((SCSI *scgp, int f, int *busp, int *tgtp, int *lunp, int *chanp, int *inop, int ataidx)); #if defined(SG_SET_RESERVED_SIZE) && defined(SG_GET_RESERVED_SIZE) LOCAL long sg_raisedma __PR((SCSI *scgp, long newmax)); LOCAL void sg_checkdma __PR((int f, int *valp)); #endif LOCAL void sg_settimeout __PR((int f, int timeout)); /* * Return version information for the low level SCSI transport code. * This has been introduced to make it easier to trace down problems * in applications. */ LOCAL char * scgo_version(scgp, what) SCSI *scgp; int what; { if (scgp != (SCSI *)0) { #ifdef USE_PG /* * If we only have a Parallel port or only opened a handle * for PP, just return PP version. */ if (scglocal(scgp)->pgbus == 0 || (scg_scsibus(scgp) >= 0 && scg_scsibus(scgp) == scglocal(scgp)->pgbus)) return (pg_version(scgp, what)); #endif switch (what) { case SCG_VERSION: return (_scg_trans_version); /* * If you changed this source, you are not allowed to * return "schily" for the SCG_AUTHOR request. */ case SCG_AUTHOR: return (_scg_auth_schily); case SCG_SCCS_ID: return (__sccsid); case SCG_KVERSION: { static char kv[16]; int n; if (scglocal(scgp)->drvers >= 0) { n = scglocal(scgp)->drvers; js_snprintf(kv, sizeof (kv), "%d.%d.%d", n/10000, (n%10000)/100, n%100); return (kv); } } } } return ((char *)0); } LOCAL int scgo_help(scgp, f) SCSI *scgp; FILE *f; { __scg_help(f, "sg", "Generic transport independent SCSI", "", "bus,target,lun", "1,2,0", TRUE, FALSE); #ifdef USE_PG pg_help(scgp, f); #endif #ifdef USE_ATAPI scgo_ahelp(scgp, f); #endif __scg_help(f, "ATA", "ATA Packet specific SCSI transport using sg interface", "ATA:", "bus,target,lun", "1,2,0", TRUE, FALSE); return (0); } LOCAL int scgo_open(scgp, device) SCSI *scgp; char *device; { int busno = scg_scsibus(scgp); int tgt = scg_target(scgp); int tlun = scg_lun(scgp); register int f; register int i; register int b; register int t; register int l; register int nopen = 0; int xopen = 0; char devname[64]; BOOL use_ata = FALSE; if ((busno >= MAX_SCG && busno < 1000) || busno >= (1000+MAX_ATA) || tgt >= MAX_TGT || tlun >= MAX_LUN) { errno = EINVAL; if (scgp->errstr) js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE, "Illegal value for busno, target or lun '%d,%d,%d'", busno, tgt, tlun); return (-1); } if (busno >= 1000) busno = busno - 1000 + MAX_SCG; if (device != NULL && *device != '\0') { #ifdef USE_ATAPI if (strncmp(device, "ATAPI", 5) == 0) { scgp->ops = &atapi_ops; return (SCGO_OPEN(scgp, device)); } #endif if (strcmp(device, "ATA") == 0) { /* * Sending generic SCSI commands via /dev/hd* is a * really bad idea when there also is a generic * SCSI driver interface - it breaks the protocol * layering model. A better idea would be to * have a SCSI host bus adapter driver that sends * the SCSI commands via the ATA hardware. This way, * the layering model would be honored. * * People like Jens Axboe should finally fix the DMA * bugs in the ide-scsi hostadaptor emulation module * from Linux instead of publishing childish patches * to the comment above. */ use_ata = TRUE; device = NULL; if (scgp->overbose) { /* * I strongly encourage people who believe that * they need to patch this message away to read * the messages in the Solaris USCSI libscg * layer instead of wetting their tissues while * being unwilling to look besides their * own belly button. */ js_fprintf((FILE *)scgp->errfile, "Warning: Using badly designed ATAPI via /dev/hd* interface.\n"); } } } if (scgp->local == NULL) { scgp->local = malloc(sizeof (struct scg_local)); if (scgp->local == NULL) return (0); scglocal(scgp)->scgfile = -1; scglocal(scgp)->pgbus = -2; scglocal(scgp)->SCSIbuf = (char *)-1; scglocal(scgp)->pack_id = 5; scglocal(scgp)->drvers = -1; scglocal(scgp)->isold = -1; scglocal(scgp)->flags = 0; if (use_ata) scglocal(scgp)->flags |= LF_ATA; scglocal(scgp)->xbufsize = 0L; scglocal(scgp)->xbuf = NULL; for (b = 0; b < MAX_SCG+MAX_ATA; b++) { scglocal(scgp)->buscookies[b] = (short)-1; for (t = 0; t < MAX_TGT; t++) { for (l = 0; l < MAX_LUN; l++) scglocal(scgp)->scgfiles[b][t][l] = (short)-1; } } } if (use_ata) goto scanopen; if ((device != NULL && *device != '\0') || (busno == -2 && tgt == -2)) goto openbydev; scanopen: /* * Note that it makes no sense to scan less than all /dev/hd* devices * as even /dev/hda may be a device that talks SCSI (e.g. a ATAPI * notebook disk or a CD/DVD writer). The CD/DVD writer case may * look silly but there may be users that did boot from a SCSI hdd * and connected 4 CD/DVD writers to both IDE cables in the PC. */ #if LINUX_VERSION_CODE <= 0x020600 if (use_ata) #endif for (i = 0; i <= 25; i++) { js_snprintf(devname, sizeof (devname), "/dev/hd%c", i+'a'); /* O_NONBLOCK is dangerous */ f = open(devname, O_RDWR | O_NONBLOCK); if (f < 0) { /* * Set up error string but let us clear it later * if at least one open succeeded. */ if (scgp->errstr) js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE, "Cannot open '/dev/hd*'"); #ifdef EROFS if (errno == EROFS) /* should we rather open RO? */ continue; #endif if (errno != ENOENT && errno != ENXIO && errno != ENODEV) { if (scgp->errstr) js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE, "Cannot open '%s'", devname); return (0); } } else { int iparm; if (ioctl(f, SG_GET_TIMEOUT, &iparm) < 0) { if (scgp->errstr) js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE, "SCSI unsupported with '/dev/hd*'"); close(f); continue; } sg_clearnblock(f); /* Be very proper about this */ if (sg_setup(scgp, f, busno, tgt, tlun, i)) return (++nopen); if (busno < 0 && tgt < 0 && tlun < 0) nopen++; } } if (nopen > 0 && scgp->errstr) scgp->errstr[0] = '\0'; xopen = nopen; if (!use_ata) for (i = 0; i < 32; i++) { js_snprintf(devname, sizeof (devname), "/dev/sg%d", i); /* O_NONBLOCK is dangerous */ f = open(devname, O_RDWR | O_NONBLOCK); if (f < 0) { /* * Set up error string but let us clear it later * if at least one open succeeded. */ if (scgp->errstr) js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE, "Cannot open '/dev/sg*'"); if (errno != ENOENT && errno != ENXIO && errno != ENODEV) { if (scgp->errstr) js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE, "Cannot open '%s'", devname); return (0); } } else { sg_clearnblock(f); /* Be very proper about this */ if (sg_setup(scgp, f, busno, tgt, tlun, -1)) return (++nopen); if (busno < 0 && tgt < 0 && tlun < 0) nopen++; } } if (nopen > 0 && scgp->errstr) scgp->errstr[0] = '\0'; if (!use_ata && nopen == xopen) for (i = 0; i <= 25; i++) { js_snprintf(devname, sizeof (devname), "/dev/sg%c", i+'a'); /* O_NONBLOCK is dangerous */ f = open(devname, O_RDWR | O_NONBLOCK); if (f < 0) { /* * Set up error string but let us clear it later * if at least one open succeeded. */ if (scgp->errstr) js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE, "Cannot open '/dev/sg*'"); if (errno != ENOENT && errno != ENXIO && errno != ENODEV) { if (scgp->errstr) js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE, "Cannot open '%s'", devname); return (0); } } else { sg_clearnblock(f); /* Be very proper about this */ if (sg_setup(scgp, f, busno, tgt, tlun, -1)) return (++nopen); if (busno < 0 && tgt < 0 && tlun < 0) nopen++; } } if (nopen > 0 && scgp->errstr) scgp->errstr[0] = '\0'; openbydev: if (device != NULL && *device != '\0') { b = -1; if (strlen(device) == 8 && strncmp(device, "/dev/hd", 7) == 0) { b = device[7] - 'a'; if (b < 0 || b > 25) b = -1; } if (scgp->overbose) { /* * Before you patch this away, are you sure that you * know what you are going to to? * * Note that this is a warning that helps users from * cdda2wav, mkisofs and other programs (that * distinguish SCSI addresses from file names) from * getting unexpected results. */ js_fprintf((FILE *)scgp->errfile, "Warning: Open by 'devname' is unintentional and not supported.\n"); } /* O_NONBLOCK is dangerous */ f = open(device, O_RDWR | O_NONBLOCK); /* if (f < 0 && errno == ENOENT)*/ /* goto openpg;*/ if (f < 0) { /* * The pg driver has the same rules to decide whether * to use openbydev. If we cannot open the device, it * makes no sense to try the /dev/pg* driver. */ if (scgp->errstr) js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE, "Cannot open '%s'", device); return (0); } sg_clearnblock(f); /* Be very proper about this */ if (!sg_mapdev(scgp, f, &busno, &tgt, &tlun, 0, 0, b)) { close(f); /* * If sg_mapdev() failes, this may be /dev/pg* device. */ goto openpg; } #ifdef OOO if (scg_scsibus(scgp) < 0) scg_scsibus(scgp) = busno; if (scg_target(scgp) < 0) scg_target(scgp) = tgt; if (scg_lun(scgp) < 0) scg_lun(scgp) = tlun; #endif scg_settarget(scgp, busno, tgt, tlun); if (sg_setup(scgp, f, busno, tgt, tlun, b)) return (++nopen); } openpg: #ifdef USE_PG nopen += pg_open(scgp, device); #endif if (scgp->debug > 0) for (b = 0; b < MAX_SCG+MAX_ATA; b++) { js_fprintf((FILE *)scgp->errfile, "Bus: %d cookie: %X\n", b, scglocal(scgp)->buscookies[b]); for (t = 0; t < MAX_TGT; t++) { for (l = 0; l < MAX_LUN; l++) { if (scglocal(scgp)->scgfiles[b][t][l] != (short)-1) { js_fprintf((FILE *)scgp->errfile, "file (%d,%d,%d): %d\n", b, t, l, scglocal(scgp)->scgfiles[b][t][l]); } } } } return (nopen); } LOCAL int scgo_close(scgp) SCSI *scgp; { register int f; register int b; register int t; register int l; if (scgp->local == NULL) return (-1); for (b = 0; b < MAX_SCG+MAX_ATA; b++) { if (b == scglocal(scgp)->pgbus) continue; scglocal(scgp)->buscookies[b] = (short)-1; for (t = 0; t < MAX_TGT; t++) { for (l = 0; l < MAX_LUN; l++) { f = scglocal(scgp)->scgfiles[b][t][l]; if (f >= 0) close(f); scglocal(scgp)->scgfiles[b][t][l] = (short)-1; } } } if (scglocal(scgp)->xbuf != NULL) { free(scglocal(scgp)->xbuf); scglocal(scgp)->xbufsize = 0L; scglocal(scgp)->xbuf = NULL; } #ifdef USE_PG pg_close(scgp); #endif return (0); } /* * The Linux kernel becomes more and more unmaintainable. * Every year, a new incompatible SCSI transport interface is added. * Each of them has it's own contradictory constraints. * While you cannot have O_NONBLOCK set during operation, at least one * of the drivers requires O_NONBLOCK to be set during open(). * This is used to clear O_NONBLOCK immediately after open() succeeded. */ LOCAL void sg_clearnblock(f) int f; { int n; n = fcntl(f, F_GETFL); n &= ~O_NONBLOCK; fcntl(f, F_SETFL, n); } LOCAL BOOL sg_setup(scgp, f, busno, tgt, tlun, ataidx) SCSI *scgp; int f; int busno; int tgt; int tlun; int ataidx; { int n; int Chan; int Ino; int Bus; int Target; int Lun; BOOL onetarget = FALSE; #ifdef SG_GET_VERSION_NUM if (scglocal(scgp)->drvers < 0) { scglocal(scgp)->drvers = 0; if (ioctl(f, SG_GET_VERSION_NUM, &n) >= 0) { scglocal(scgp)->drvers = n; if (scgp->overbose) { js_fprintf((FILE *)scgp->errfile, "Linux sg driver version: %d.%d.%d\n", n/10000, (n%10000)/100, n%100); } } } #endif if (scg_scsibus(scgp) >= 0 && scg_target(scgp) >= 0 && scg_lun(scgp) >= 0) onetarget = TRUE; sg_mapdev(scgp, f, &Bus, &Target, &Lun, &Chan, &Ino, ataidx); /* * For old kernels try to make the best guess. */ Ino |= Chan << 8; n = sg_mapbus(scgp, Bus, Ino); if (Bus == -1) { Bus = n; if (scgp->debug > 0) { js_fprintf((FILE *)scgp->errfile, "SCSI Bus: %d (mapped from %d)\n", Bus, Ino); } } if (Bus < 0 || Bus >= (MAX_SCG+MAX_ATA) || Target < 0 || Target >= MAX_TGT || Lun < 0 || Lun >= MAX_LUN) { return (FALSE); } if (scglocal(scgp)->scgfiles[Bus][Target][Lun] == (short)-1) scglocal(scgp)->scgfiles[Bus][Target][Lun] = (short)f; if (onetarget) { if (Bus == busno && Target == tgt && Lun == tlun) { sg_initdev(scgp, f); scglocal(scgp)->scgfile = f; /* remember file for ioctl's */ return (TRUE); } else { scglocal(scgp)->scgfiles[Bus][Target][Lun] = (short)-1; close(f); } } else { /* * SCSI bus scanning may cause other generic SCSI activities to * fail because we set the default timeout and clear command * queues (in case of the old sg driver interface). */ sg_initdev(scgp, f); if (scglocal(scgp)->scgfile < 0) scglocal(scgp)->scgfile = f; /* remember file for ioctl's */ } return (FALSE); } LOCAL void sg_initdev(scgp, f) SCSI *scgp; int f; { struct sg_rep { struct sg_header hd; unsigned char rbuf[100]; } sg_rep; int n; int i; struct stat sb; sg_settimeout(f, scgp->deftimeout); /* * If it's a block device, don't read.... pre Linux-2.4 /dev/sg* * definitely is a character device and we only need to clear the * queue for old /dev/sg* versions. If somebody ever implements * raw disk access for Linux, this test may fail. */ if (fstat(f, &sb) >= 0 && S_ISBLK(sb.st_mode)) return; /* Eat any unwanted garbage from prior use of this device */ n = fcntl(f, F_GETFL); /* Be very proper about this */ fcntl(f, F_SETFL, n|O_NONBLOCK); fillbytes((caddr_t)&sg_rep, sizeof (struct sg_header), '\0'); sg_rep.hd.reply_len = sizeof (struct sg_header); /* * This is really ugly. * We come here if 'f' is related to a raw device. If Linux * will ever have raw devices for /dev/hd* we may get problems. * As long as there is no clean way to find out whether the * filedescriptor 'f' is related to an old /dev/sg* or to * /dev/hd*, we must assume that we found an old /dev/sg* and * clean it up. Unfortunately, reading from /dev/hd* will * Access the medium. */ for (i = 0; i < 1000; i++) { /* Read at least 32k from /dev/sg* */ int ret; ret = read(f, &sg_rep, sizeof (sg_rep)); if (ret > 0) continue; if (ret == 0 || errno == EAGAIN || errno == EIO) break; if (ret < 0 && i > 10) /* Stop on repeated unknown error */ break; } fcntl(f, F_SETFL, n); } LOCAL int sg_mapbus(scgp, busno, ino) SCSI *scgp; int busno; int ino; { register int i; if (busno >= 0 && busno < (MAX_SCG+MAX_ATA)) { /* * SCSI_IOCTL_GET_BUS_NUMBER worked. * Now we have the problem that Linux does not properly number * SCSI busses. The Bus number that Linux creates really is * the controller (card) number. I case of multi SCSI bus * cards we are lost. */ if (scglocal(scgp)->buscookies[busno] == (short)-1) { scglocal(scgp)->buscookies[busno] = ino; return (busno); } if (scglocal(scgp)->buscookies[busno] != (short)ino) errmsgno(EX_BAD, "Warning Linux Bus mapping botch.\n"); return (busno); } else for (i = 0; i < (MAX_SCG+MAX_ATA); i++) { if (scglocal(scgp)->buscookies[i] == (short)-1) { scglocal(scgp)->buscookies[i] = ino; return (i); } if (scglocal(scgp)->buscookies[i] == ino) return (i); } return (0); } LOCAL BOOL sg_mapdev(scgp, f, busp, tgtp, lunp, chanp, inop, ataidx) SCSI *scgp; int f; int *busp; int *tgtp; int *lunp; int *chanp; int *inop; int ataidx; { struct sg_id { long l1; /* target | lun << 8 | channel << 16 | low_ino << 24 */ long l2; /* Unique id */ } sg_id; int Chan; int Ino; int Bus; int Target; int Lun; if (ataidx >= 0) { /* * The badly designed /dev/hd* interface maps everything * to 0,0,0 so we need to do the mapping ourselves. */ *busp = (ataidx / 2) + MAX_SCG; *tgtp = ataidx % 2; *lunp = 0; if (chanp) *chanp = 0; if (inop) *inop = 0; return (TRUE); } if (ioctl(f, SCSI_IOCTL_GET_IDLUN, &sg_id)) return (FALSE); if (scgp->debug > 0) { js_fprintf((FILE *)scgp->errfile, "l1: 0x%lX l2: 0x%lX\n", sg_id.l1, sg_id.l2); } if (ioctl(f, SCSI_IOCTL_GET_BUS_NUMBER, &Bus) < 0) { Bus = -1; } Target = sg_id.l1 & 0xFF; Lun = (sg_id.l1 >> 8) & 0xFF; Chan = (sg_id.l1 >> 16) & 0xFF; Ino = (sg_id.l1 >> 24) & 0xFF; if (scgp->debug > 0) { js_fprintf((FILE *)scgp->errfile, "Bus: %d Target: %d Lun: %d Chan: %d Ino: %d\n", Bus, Target, Lun, Chan, Ino); } *busp = Bus; *tgtp = Target; *lunp = Lun; if (chanp) *chanp = Chan; if (inop) *inop = Ino; return (TRUE); } #if defined(SG_SET_RESERVED_SIZE) && defined(SG_GET_RESERVED_SIZE) /* * The way Linux does DMA resouce management is a bit curious. * It totally deviates from all other OS and forces long ugly code. * If we are opening all drivers for a SCSI bus scan operation, we need * to set the limit for all open devices. * This may use up all kernel memory ... so do the job carefully. * * A big problem is that SG_SET_RESERVED_SIZE does not return any hint * on whether the request did fail. The only way to find if it worked * is to use SG_GET_RESERVED_SIZE to read back the current values. */ LOCAL long sg_raisedma(scgp, newmax) SCSI *scgp; long newmax; { register int b; register int t; register int l; register int f; int val; int old; /* * First try to raise the DMA limit to a moderate value that * most likely does not use up all kernel memory. */ val = 126*1024; if (val > MAX_DMA_LINUX) { for (b = 0; b < (MAX_SCG+MAX_ATA); b++) { for (t = 0; t < MAX_TGT; t++) { for (l = 0; l < MAX_LUN; l++) { if ((f = SCGO_FILENO(scgp, b, t, l)) < 0) continue; old = 0; if (ioctl(f, SG_GET_RESERVED_SIZE, &old) < 0) continue; if (val > old) ioctl(f, SG_SET_RESERVED_SIZE, &val); } } } } /* * Now to raise the DMA limit to what we really need. */ if (newmax > val) { val = newmax; for (b = 0; b < (MAX_SCG+MAX_ATA); b++) { for (t = 0; t < MAX_TGT; t++) { for (l = 0; l < MAX_LUN; l++) { if ((f = SCGO_FILENO(scgp, b, t, l)) < 0) continue; old = 0; if (ioctl(f, SG_GET_RESERVED_SIZE, &old) < 0) continue; if (val > old) ioctl(f, SG_SET_RESERVED_SIZE, &val); } } } } /* * To make sure we did not fail (the ioctl does not report errors) * we need to check the DMA limits. We return the smallest value. */ for (b = 0; b < (MAX_SCG+MAX_ATA); b++) { for (t = 0; t < MAX_TGT; t++) { for (l = 0; l < MAX_LUN; l++) { /* * XXX Remove the SG_GET_MAX_TRANSFER_LENGTH after the * XXX SG_GET_RESERVED_SIZE call returns the real max DMA. */ #ifdef SG_GET_MAX_TRANSFER_LENGTH int v2; #endif if ((f = SCGO_FILENO(scgp, b, t, l)) < 0) continue; if (ioctl(f, SG_GET_RESERVED_SIZE, &val) < 0) continue; #ifdef SG_GET_MAX_TRANSFER_LENGTH if (ioctl(f, SG_GET_MAX_TRANSFER_LENGTH, &v2) >= 0) { if (v2 < val) val = v2; } #else sg_checkdma(f, &val); /* This does not work */ #endif if (scgp->debug > 0) { js_fprintf((FILE *)scgp->errfile, "Target (%d,%d,%d): DMA max %d old max: %ld\n", b, t, l, val, newmax); } if (val < newmax) newmax = val; } } } return ((long)newmax); } /* * This is extremely ugly code. It would be nice if Linux could * provide an ioctl to retrieve the max DMA size... * XXX Remove this function completely at the same time when * XXX SG_GET_MAX_TRANSFER_LENGTHis removed. */ LOCAL void sg_checkdma(f, valp) int f; int *valp; { #ifdef SG_GET_PACK_ID struct stat sb; int val; char nbuf[80]; int fx; if (ioctl(f, SG_GET_PACK_ID, &val) < 0) /* Only works with /dev/sg* */ return; val = -1; if (fstat(f, &sb) >= 0) /* Try to get instance # */ val = minor(sb.st_rdev); if (val < 0) return; js_snprintf(nbuf, sizeof (nbuf), "/sys/class/scsi_generic/sg%d/device/block/queue/max_hw_sectors_kb", val); fx = open(nbuf, O_RDONLY | O_NDELAY); if (fx >= 0) { if (read(fx, nbuf, sizeof (nbuf)) > 0) { val = -1; astoi(nbuf, &val); if (val > 0) { val *= 1024; if (*valp > val) *valp = val; } } close(fx); } #endif /* SG_GET_PACK_ID */ } #endif LOCAL long scgo_maxdma(scgp, amt) SCSI *scgp; long amt; { long maxdma = MAX_DMA_LINUX; #if defined(SG_SET_RESERVED_SIZE) && defined(SG_GET_RESERVED_SIZE) /* * Use the curious new kernel interface found on Linux >= 2.2.10 * This interface first appeared in 2.2.6 but it was not working. */ if (scglocal(scgp)->drvers >= 20134) maxdma = sg_raisedma(scgp, amt); #endif #ifdef SG_GET_BUFSIZE /* * We assume that all /dev/sg instances use the same * maximum buffer size. */ maxdma = ioctl(scglocal(scgp)->scgfile, SG_GET_BUFSIZE, 0); #endif if (maxdma < 0) { #ifdef USE_PG /* * If we only have a Parallel port, just return PP maxdma. */ if (scglocal(scgp)->pgbus == 0) return (pg_maxdma(scgp, amt)); #endif if (scglocal(scgp)->scgfile >= 0) maxdma = MAX_DMA_LINUX; } #ifdef USE_PG if (scg_scsibus(scgp) == scglocal(scgp)->pgbus) return (pg_maxdma(scgp, amt)); if ((scg_scsibus(scgp) < 0) && (pg_maxdma(scgp, amt) < maxdma)) return (pg_maxdma(scgp, amt)); #endif return (maxdma); } LOCAL void * scgo_getbuf(scgp, amt) SCSI *scgp; long amt; { char *ret; if (scgp->debug > 0) { js_fprintf((FILE *)scgp->errfile, "scgo_getbuf: %ld bytes\n", amt); } /* * For performance reason, we allocate pagesize() * bytes before the SCSI buffer to avoid * copying the whole buffer contents when * setting up the /dev/sg data structures. */ ret = valloc((size_t)(amt+getpagesize())); if (ret == NULL) return (ret); scgp->bufbase = ret; ret += getpagesize(); scglocal(scgp)->SCSIbuf = ret; return ((void *)ret); } LOCAL void scgo_freebuf(scgp) SCSI *scgp; { if (scgp->bufbase) free(scgp->bufbase); scgp->bufbase = NULL; } LOCAL int scgo_numbus(scgp) SCSI *scgp; { return (1000+MAX_ATA); return (MAX_SCG+MAX_ATA); } LOCAL BOOL scgo_havebus(scgp, busno) SCSI *scgp; int busno; { register int t; register int l; if (busno >= 1000) { if (busno >= (1000+MAX_ATA)) return (FALSE); busno = busno - 1000 + MAX_SCG; } else if (busno < 0 || busno >= MAX_SCG) return (FALSE); if (scgp->local == NULL) return (FALSE); for (t = 0; t < MAX_TGT; t++) { for (l = 0; l < MAX_LUN; l++) if (scglocal(scgp)->scgfiles[busno][t][l] >= 0) return (TRUE); } return (FALSE); } LOCAL int scgo_fileno(scgp, busno, tgt, tlun) SCSI *scgp; int busno; int tgt; int tlun; { if (busno >= 1000) busno = busno - 1000 + MAX_SCG; if (busno < 0 || busno >= (MAX_SCG+MAX_ATA) || tgt < 0 || tgt >= MAX_TGT || tlun < 0 || tlun >= MAX_LUN) return (-1); if (scgp->local == NULL) return (-1); return ((int)scglocal(scgp)->scgfiles[busno][tgt][tlun]); } LOCAL int scgo_initiator_id(scgp) SCSI *scgp; { #ifdef USE_PG if (scg_scsibus(scgp) == scglocal(scgp)->pgbus) return (pg_initiator_id(scgp)); #endif return (-1); } LOCAL int scgo_isatapi(scgp) SCSI *scgp; { #ifdef USE_PG if (scg_scsibus(scgp) == scglocal(scgp)->pgbus) return (pg_isatapi(scgp)); #endif /* * The /dev/hd* interface always returns TRUE for SG_EMULATED_HOST. * So this is completely useless. */ if (scglocal(scgp)->flags & LF_ATA) return (-1); #ifdef SG_EMULATED_HOST { int emulated = FALSE; /* * XXX Should we use this at all? * XXX The badly designed /dev/hd* interface always * XXX returns TRUE, even when used with e.g. /dev/sr0. */ if (ioctl(scgp->fd, SG_EMULATED_HOST, &emulated) >= 0) return (emulated != 0); } #endif return (-1); } LOCAL int scgo_reset(scgp, what) SCSI *scgp; int what; { #ifdef SG_SCSI_RESET int f = scgp->fd; int func = -1; #endif #ifdef USE_PG if (scg_scsibus(scgp) == scglocal(scgp)->pgbus) return (pg_reset(scgp, what)); #endif /* * Do we have a SCSI reset in the Linux sg driver? */ #ifdef SG_SCSI_RESET /* * Newer Linux sg driver seem to finally implement it... */ #ifdef SG_SCSI_RESET_NOTHING func = SG_SCSI_RESET_NOTHING; if (ioctl(f, SG_SCSI_RESET, &func) >= 0) { if (what == SCG_RESET_NOP) return (0); #ifdef SG_SCSI_RESET_DEVICE if (what == SCG_RESET_TGT) { func = SG_SCSI_RESET_DEVICE; if (ioctl(f, SG_SCSI_RESET, &func) >= 0) return (0); } #endif #ifdef SG_SCSI_RESET_BUS if (what == SCG_RESET_BUS) { func = SG_SCSI_RESET_BUS; if (ioctl(f, SG_SCSI_RESET, &func) >= 0) return (0); } #endif } #endif #endif return (-1); } LOCAL void sg_settimeout(f, tmo) int f; int tmo; { #ifdef USER_HZ tmo *= USER_HZ; if (tmo) tmo += USER_HZ/2; #else tmo *= HZ; if (tmo) tmo += HZ/2; #endif if (ioctl(f, SG_SET_TIMEOUT, &tmo) < 0) comerr("Cannot set SG_SET_TIMEOUT.\n"); } /* * Get misaligned int. * Needed for all recent processors (sparc/ppc/alpha) * because the /dev/sg design forces us to do misaligned * reads of integers. */ #ifdef MISALIGN LOCAL int sg_getint(ip) int *ip; { int ret; register char *cp = (char *)ip; register char *tp = (char *)&ret; register int i; for (i = sizeof (int); --i >= 0; ) *tp++ = *cp++; return (ret); } #define GETINT(a) sg_getint(&(a)) #else #define GETINT(a) (a) #endif #ifdef SG_IO LOCAL int scgo_send(scgp) SCSI *scgp; { struct scg_cmd *sp = scgp->scmd; int ret; sg_io_hdr_t sg_io; struct timeval to; static uid_t cureuid = 0; /* XXX Hack until we have uid management */ if (scgp->fd < 0) { sp->error = SCG_FATAL; sp->ux_errno = EIO; return (0); } if (scglocal(scgp)->isold > 0) { return (sg_rwsend(scgp)); } fillbytes((caddr_t)&sg_io, sizeof (sg_io), '\0'); sg_io.interface_id = 'S'; if (sp->flags & SCG_RECV_DATA) { sg_io.dxfer_direction = SG_DXFER_FROM_DEV; } else if (sp->size > 0) { sg_io.dxfer_direction = SG_DXFER_TO_DEV; } else { sg_io.dxfer_direction = SG_DXFER_NONE; } sg_io.cmd_len = sp->cdb_len; if (sp->sense_len > SG_MAX_SENSE) sg_io.mx_sb_len = SG_MAX_SENSE; else sg_io.mx_sb_len = sp->sense_len; sg_io.dxfer_len = sp->size; sg_io.dxferp = sp->addr; sg_io.cmdp = sp->cdb.cmd_cdb; sg_io.sbp = sp->u_sense.cmd_sense; sg_io.timeout = sp->timeout*1000; sg_io.flags |= SG_FLAG_DIRECT_IO; if (cureuid != 0) seteuid(0); again: errno = 0; ret = ioctl(scgp->fd, SG_IO, &sg_io); if (ret < 0 && geterrno() == EPERM) { /* XXX Hack until we have uid management */ cureuid = geteuid(); if (seteuid(0) >= 0) goto again; } if (cureuid != 0) seteuid(cureuid); if (scgp->debug > 0) { js_fprintf((FILE *)scgp->errfile, "ioctl ret: %d\n", ret); } if (ret < 0) { sp->ux_errno = geterrno(); /* * Check if SCSI command could not be send at all. * Linux usually returns EINVAL for an unknown ioctl. * In case somebody from the Linux kernel team learns that the * corect errno would be ENOTTY, we check for this errno too. */ if ((sp->ux_errno == ENOTTY || sp->ux_errno == EINVAL) && scglocal(scgp)->isold < 0) { scglocal(scgp)->isold = 1; return (sg_rwsend(scgp)); } if (sp->ux_errno == ENXIO || sp->ux_errno == EPERM || sp->ux_errno == EINVAL || sp->ux_errno == EACCES) { return (-1); } } sp->u_scb.cmd_scb[0] = sg_io.status; sp->sense_count = sg_io.sb_len_wr; if (scgp->debug > 0) { js_fprintf((FILE *)scgp->errfile, "host_status: %02X driver_status: %02X\n", sg_io.host_status, sg_io.driver_status); } switch (sg_io.host_status) { case DID_OK: /* * If there is no DMA overrun and there is a * SCSI Status byte != 0 then the SCSI cdb transport * was OK and sp->error must be SCG_NO_ERROR. */ if ((sg_io.driver_status & DRIVER_SENSE) != 0) { if (sp->ux_errno == 0) sp->ux_errno = EIO; if (sp->u_sense.cmd_sense[0] != 0 && sp->u_scb.cmd_scb[0] == 0) { /* * The Linux SCSI system up to 2.4.xx * trashes the status byte in the * kernel. This is true at least for * ide-scsi emulation. Until this gets * fixed, we need this hack. */ sp->u_scb.cmd_scb[0] = ST_CHK_COND; if (sp->sense_count == 0) sp->sense_count = SG_MAX_SENSE; if ((sp->u_sense.cmd_sense[2] == 0) && (sp->u_sense.cmd_sense[12] == 0) && (sp->u_sense.cmd_sense[13] == 0)) { /* * The Linux SCSI system will * send a request sense for * even a dma underrun error. * Clear CHECK CONDITION state * in case of No Sense. */ sp->u_scb.cmd_scb[0] = 0; sp->u_sense.cmd_sense[0] = 0; sp->sense_count = 0; } } } break; case DID_NO_CONNECT: /* Arbitration won, retry NO_CONNECT? */ sp->error = SCG_RETRYABLE; break; case DID_BAD_TARGET: sp->error = SCG_FATAL; break; case DID_TIME_OUT: __scg_times(scgp); if (sp->timeout > 1 && scgp->cmdstop->tv_sec == 0) { sp->u_scb.cmd_scb[0] = 0; sp->error = SCG_FATAL; /* a selection timeout */ } else { sp->error = SCG_TIMEOUT; } break; default: to.tv_sec = sp->timeout; to.tv_usec = 500000; __scg_times(scgp); if (scgp->cmdstop->tv_sec > to.tv_sec || (scgp->cmdstop->tv_sec == to.tv_sec && scgp->cmdstop->tv_usec > to.tv_usec)) { sp->ux_errno = 0; sp->error = SCG_TIMEOUT; /* a timeout */ } else { sp->error = SCG_RETRYABLE; if (!scgp->silent) { js_fprintf((FILE *)scgp->errfile, "Unknown sg_io.host_status %X\n", sg_io.host_status); } } break; } if (sp->error && sp->ux_errno == 0) sp->ux_errno = EIO; sp->resid = sg_io.resid; return (0); } #else # define sg_rwsend scgo_send #endif LOCAL int sg_rwsend(scgp) SCSI *scgp; { int f = scgp->fd; struct scg_cmd *sp = scgp->scmd; struct sg_rq *sgp; struct sg_rq *sgp2; int i; int pack_len; int reply_len; int amt = sp->cdb_len; struct sg_rq { struct sg_header hd; unsigned char buf[MAX_DMA_LINUX+SCG_MAX_CMD]; } sg_rq; #ifdef SG_GET_BUFSIZE /* We may use a 'sg' version 2 driver */ char driver_byte; char host_byte; char msg_byte; char status_byte; #endif if (f < 0) { sp->error = SCG_FATAL; sp->ux_errno = EIO; return (0); } #ifdef USE_PG if (scg_scsibus(scgp) == scglocal(scgp)->pgbus) return (pg_send(scgp)); #endif if (sp->timeout != scgp->deftimeout) sg_settimeout(f, sp->timeout); sgp2 = sgp = &sg_rq; if (sp->addr == scglocal(scgp)->SCSIbuf) { sgp = (struct sg_rq *) (scglocal(scgp)->SCSIbuf - (sizeof (struct sg_header) + amt)); sgp2 = (struct sg_rq *) (scglocal(scgp)->SCSIbuf - (sizeof (struct sg_header))); } else { if (scgp->debug > 0) { js_fprintf((FILE *)scgp->errfile, "DMA addr: 0x%8.8lX size: %d - using copy buffer\n", (long)sp->addr, sp->size); } if (sp->size > (int)(sizeof (sg_rq.buf) - SCG_MAX_CMD)) { if (scglocal(scgp)->xbuf == NULL) { scglocal(scgp)->xbufsize = scgp->maxbuf; scglocal(scgp)->xbuf = malloc(scglocal(scgp)->xbufsize + SCG_MAX_CMD + sizeof (struct sg_header)); if (scgp->debug > 0) { js_fprintf((FILE *)scgp->errfile, "Allocated DMA copy buffer, addr: 0x%8.8lX size: %ld\n", (long)scglocal(scgp)->xbuf, scgp->maxbuf); } } if (scglocal(scgp)->xbuf == NULL || sp->size > scglocal(scgp)->xbufsize) { errno = ENOMEM; return (-1); } sgp2 = sgp = (struct sg_rq *)scglocal(scgp)->xbuf; } } /* * This is done to avoid misaligned access of sgp->some_int */ pack_len = sizeof (struct sg_header) + amt; reply_len = sizeof (struct sg_header); if (sp->flags & SCG_RECV_DATA) { reply_len += sp->size; } else { pack_len += sp->size; } #ifdef MISALIGN /* * sgp->some_int may be misaligned if (sp->addr == SCSIbuf) * This is no problem on Intel porocessors, however * all other processors don't like it. * sizeof (struct sg_header) + amt is usually not a multiple of * sizeof (int). For this reason, we fill in the values into sg_rq * which is always corectly aligned and then copy it to the real * location if this location differs from sg_rq. * Never read/write directly to sgp->some_int !!!!! */ fillbytes((caddr_t)&sg_rq, sizeof (struct sg_header), '\0'); sg_rq.hd.pack_len = pack_len; sg_rq.hd.reply_len = reply_len; sg_rq.hd.pack_id = scglocal(scgp)->pack_id++; /* sg_rq.hd.result = 0; not needed because of fillbytes() */ if ((caddr_t)&sg_rq != (caddr_t)sgp) movebytes((caddr_t)&sg_rq, (caddr_t)sgp, sizeof (struct sg_header)); #else fillbytes((caddr_t)sgp, sizeof (struct sg_header), '\0'); sgp->hd.pack_len = pack_len; sgp->hd.reply_len = reply_len; sgp->hd.pack_id = scglocal(scgp)->pack_id++; /* sgp->hd.result = 0; not needed because of fillbytes() */ #endif if (amt == 12) sgp->hd.twelve_byte = 1; for (i = 0; i < amt; i++) { sgp->buf[i] = sp->cdb.cmd_cdb[i]; } if (!(sp->flags & SCG_RECV_DATA)) { if ((void *)sp->addr != (void *)&sgp->buf[amt]) movebytes(sp->addr, &sgp->buf[amt], sp->size); amt += sp->size; } #ifdef SG_GET_BUFSIZE sgp->hd.want_new = 1; /* Order new behaviour */ sgp->hd.cdb_len = sp->cdb_len; /* Set CDB length */ if (sp->sense_len > SG_MAX_SENSE) sgp->hd.sense_len = SG_MAX_SENSE; else sgp->hd.sense_len = sp->sense_len; #endif i = sizeof (struct sg_header) + amt; if ((amt = write(f, sgp, i)) < 0) { /* write */ sg_settimeout(f, scgp->deftimeout); return (-1); } else if (amt != i) { errmsg("scgo_send(%s) wrote %d bytes (expected %d).\n", scgp->cmdname, amt, i); } if (sp->addr == scglocal(scgp)->SCSIbuf) { movebytes(sgp, sgp2, sizeof (struct sg_header)); sgp = sgp2; } sgp->hd.sense_buffer[0] = 0; if ((amt = read(f, sgp, reply_len)) < 0) { /* read */ sg_settimeout(f, scgp->deftimeout); return (-1); } if (sp->flags & SCG_RECV_DATA && ((void *)sgp->buf != (void *)sp->addr)) { movebytes(sgp->buf, sp->addr, sp->size); } sp->ux_errno = GETINT(sgp->hd.result); /* Unaligned read */ sp->error = SCG_NO_ERROR; #ifdef SG_GET_BUFSIZE if (sgp->hd.grant_new) { sp->sense_count = sgp->hd.sense_len; pack_len = GETINT(sgp->hd.sg_cmd_status); /* Unaligned read */ driver_byte = (pack_len >> 24) & 0xFF; host_byte = (pack_len >> 16) & 0xFF; msg_byte = (pack_len >> 8) & 0xFF; status_byte = pack_len & 0xFF; switch (host_byte) { case DID_OK: if ((driver_byte & DRIVER_SENSE || sgp->hd.sense_buffer[0] != 0) && status_byte == 0) { /* * The Linux SCSI system up to 2.4.xx * trashes the status byte in the * kernel. This is true at least for * ide-scsi emulation. Until this gets * fixed, we need this hack. */ status_byte = ST_CHK_COND; if (sgp->hd.sense_len == 0) sgp->hd.sense_len = SG_MAX_SENSE; if ((sp->u_sense.cmd_sense[2] == 0) && (sp->u_sense.cmd_sense[12] == 0) && (sp->u_sense.cmd_sense[13] == 0)) { /* * The Linux SCSI system will * send a request sense for * even a dma underrun error. * Clear CHECK CONDITION state * in case of No Sense. */ sp->u_scb.cmd_scb[0] = 0; sp->u_sense.cmd_sense[0] = 0; sp->sense_count = 0; } } break; case DID_NO_CONNECT: /* Arbitration won, retry NO_CONNECT? */ sp->error = SCG_RETRYABLE; break; case DID_BAD_TARGET: sp->error = SCG_FATAL; break; case DID_TIME_OUT: sp->error = SCG_TIMEOUT; break; default: sp->error = SCG_RETRYABLE; if ((driver_byte & DRIVER_SENSE || sgp->hd.sense_buffer[0] != 0) && status_byte == 0) { status_byte = ST_CHK_COND; sp->error = SCG_NO_ERROR; } if (status_byte != 0 && sgp->hd.sense_len == 0) { sgp->hd.sense_len = SG_MAX_SENSE; sp->error = SCG_NO_ERROR; } break; } if ((host_byte != DID_OK || status_byte != 0) && sp->ux_errno == 0) sp->ux_errno = EIO; sp->u_scb.cmd_scb[0] = status_byte; if (status_byte & ST_CHK_COND) { sp->sense_count = sgp->hd.sense_len; movebytes(sgp->hd.sense_buffer, sp->u_sense.cmd_sense, sp->sense_count); } } else #endif { if (GETINT(sgp->hd.result) == EBUSY) { /* Unaligned read */ struct timeval to; to.tv_sec = sp->timeout; to.tv_usec = 500000; __scg_times(scgp); if (sp->timeout > 1 && scgp->cmdstop->tv_sec == 0) { sp->u_scb.cmd_scb[0] = 0; sp->ux_errno = EIO; sp->error = SCG_FATAL; /* a selection timeout */ } else if (scgp->cmdstop->tv_sec < to.tv_sec || (scgp->cmdstop->tv_sec == to.tv_sec && scgp->cmdstop->tv_usec < to.tv_usec)) { sp->ux_errno = EIO; sp->error = SCG_TIMEOUT; /* a timeout */ } else { sp->error = SCG_RETRYABLE; /* may be BUS_BUSY */ } } if (sp->flags & SCG_RECV_DATA) sp->resid = (sp->size + sizeof (struct sg_header)) - amt; else sp->resid = 0; /* sg version1 cannot return DMA resid count */ if (sgp->hd.sense_buffer[0] != 0) { sp->scb.chk = 1; sp->sense_count = SG_MAX_SENSE; movebytes(sgp->hd.sense_buffer, sp->u_sense.cmd_sense, sp->sense_count); if (sp->ux_errno == 0) sp->ux_errno = EIO; } } if (scgp->verbose > 0 && scgp->debug > 0) { #ifdef SG_GET_BUFSIZE js_fprintf((FILE *)scgp->errfile, "status: 0x%08X pack_len: %d, reply_len: %d pack_id: %d result: %d wn: %d gn: %d cdb_len: %d sense_len: %d sense[0]: %02X\n", GETINT(sgp->hd.sg_cmd_status), GETINT(sgp->hd.pack_len), GETINT(sgp->hd.reply_len), GETINT(sgp->hd.pack_id), GETINT(sgp->hd.result), sgp->hd.want_new, sgp->hd.grant_new, sgp->hd.cdb_len, sgp->hd.sense_len, sgp->hd.sense_buffer[0]); #else js_fprintf((FILE *)scgp->errfile, "pack_len: %d, reply_len: %d pack_id: %d result: %d sense[0]: %02X\n", GETINT(sgp->hd.pack_len), GETINT(sgp->hd.reply_len), GETINT(sgp->hd.pack_id), GETINT(sgp->hd.result), sgp->hd.sense_buffer[0]); #endif #ifdef DEBUG js_fprintf((FILE *)scgp->errfile, "sense: "); for (i = 0; i < 16; i++) js_fprintf((FILE *)scgp->errfile, "%02X ", sgp->hd.sense_buffer[i]); js_fprintf((FILE *)scgp->errfile, "\n"); #endif } if (sp->timeout != scgp->deftimeout) sg_settimeout(f, scgp->deftimeout); return (0); }