summaryrefslogtreecommitdiff
path: root/libusal/scsi-linux-ata.c
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2012-12-31 05:04:42 +0400
committerIgor Pashev <pashev.igor@gmail.com>2012-12-31 05:04:42 +0400
commit71dc8760ff4de5f365330d1bc571d934deb54af9 (patch)
tree7346d42a282562a3937d82307012b5857d642ce6 /libusal/scsi-linux-ata.c
downloadcdrkit-941fb342494d2b61ef5fd1870a4fa695d1c7fc69.tar.gz
Imported Upstream version 1.1.11upstream/1.1.11upstream
Diffstat (limited to 'libusal/scsi-linux-ata.c')
-rw-r--r--libusal/scsi-linux-ata.c1188
1 files changed, 1188 insertions, 0 deletions
diff --git a/libusal/scsi-linux-ata.c b/libusal/scsi-linux-ata.c
new file mode 100644
index 0000000..3ec18b7
--- /dev/null
+++ b/libusal/scsi-linux-ata.c
@@ -0,0 +1,1188 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)scsi-linux-ata.c 1.7 04/06/12 Copyright 2002 J. Schilling */
+/*
+ * 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 usal driver.
+ *
+ * Warning: you may change this source, but if you do that
+ * you need to change the _usal_version and _usal_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) 2002 J. Schilling
+ *
+ * Thanks to Alexander Kern <alex.kern@gmx.de> for the idea and first
+ * code fragments for supporting the CDROM_SEND_PACKET ioctl() from
+ * the cdrom.c kernel driver. Please note that this interface in priciple
+ * is completely unneeded but the Linux kernel is just a cluster of
+ * code and does not support planned orthogonal interface systems.
+ * For this reason we need CDROM_SEND_PACKET in order to work around a
+ * bug in the linux kernel that prevents to use PCATA drives because
+ * the kernel panics if you try to put ide-scsi on top of the PCATA
+ * driver.
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef USE_OLD_ATAPI
+
+#define ata_sccsid "obsolete ATAPI driver in cdrkit"
+
+static char _usal_atrans_version[] = "scsi-linux-ata.c-1.7"; /* The version for ATAPI transport*/
+
+static char *usalo_aversion(SCSI *usalp, int what);
+static int usalo_ahelp(SCSI *usalp, FILE *f);
+static int usalo_aopen(SCSI *usalp, char *device);
+static int usalo_aclose(SCSI *usalp);
+static long usalo_amaxdma(SCSI *usalp, long amt);
+static BOOL usalo_ahavebus(SCSI *usalp, int);
+static int usalo_afileno(SCSI *usalp, int, int, int);
+static int usalo_ainitiator_id(SCSI *usalp);
+static int usalo_aisatapi(SCSI *usalp);
+static int usalo_areset(SCSI *usalp, int what);
+static int usalo_asend(SCSI *usalp);
+
+static usal_ops_t ata_ops = {
+ usalo_asend,
+ usalo_aversion,
+ usalo_ahelp,
+ usalo_aopen,
+ usalo_aclose,
+ usalo_amaxdma,
+ usalo_getbuf, /* Shared with SG driver */
+ usalo_freebuf, /* Shared with SG driver */
+ usalo_ahavebus,
+ usalo_afileno,
+ usalo_ainitiator_id,
+ usalo_aisatapi,
+ usalo_areset,
+};
+
+#define HOST_EMPTY 0xF
+#define HOST_SCSI 0x0
+#define HOST_IDE 0x1
+#define HOST_USB 0x2
+#define HOST_IEEE1389 0x3
+#define HOST_PARALLEL 0x4
+#define HOST_OTHER 0xE
+
+
+#define typlocal(p, schillybus) usallocal(p)->bc[schillybus].typ
+#define buslocal(p, schillybus) usallocal(p)->bc[schillybus].bus
+#define hostlocal(p, schillybus) usallocal(p)->bc[schillybus].host
+
+#define MAX_DMA_ATA (131072-1) /* EINVAL (hart) ENOMEM (weich) bei mehr ... */
+ /* Bei fehlerhaftem Sense Pointer kommt EFAULT */
+
+static int usalo_send(SCSI * usalp);
+static BOOL sg_amapdev(SCSI * usalp, int f, char *device, int *bus,
+ int *target, int *lun);
+static BOOL sg_amapdev_scsi(SCSI * usalp, int f, int *busp, int *tgtp,
+ int *lunp, int *chanp, int *inop);
+static int usalo_aget_first_free_shillybus(SCSI * usalp, int subsystem,
+ int host, int bus);
+static int usalo_amerge(char *path, char *readedlink, char *buffer, int buflen);
+
+/*
+ * uncomment this when you will get a debug file #define DEBUG
+ */
+#ifdef DEBUG
+#define LOGFILE "scsi-linux-ata.log"
+#define log(a) sglog a
+
+static void sglog(const char *fmt, ...);
+
+#include <vadefs.h>
+
+/* VARARGS1 */
+static void
+sglog(const char *fmt, ...)
+{
+ va_list args;
+ FILE *f = fopen(LOGFILE, "a");
+
+ if (f == NULL)
+ return;
+
+ va_start(args, fmt);
+ vfprintf(f, fmt, args);
+ va_end(args);
+ fclose(f);
+}
+#else
+#define log(a)
+#endif /* DEBUG */
+
+static int scan_internal(SCSI * usalp, int *fatal);
+
+/*
+ * Return version information for the low level SCSI transport code.
+ * This has been introduced to make it easier to trace down problems
+ * in applications.
+ */
+static char *
+usalo_aversion(SCSI *usalp, int what)
+{
+ if (usalp != (SCSI *)0) {
+ switch (what) {
+
+ case SCG_VERSION:
+ return (_usal_atrans_version);
+ /*
+ * If you changed this source, you are not allowed to
+ * return "schily" for the SCG_AUTHOR request.
+ */
+ case SCG_AUTHOR:
+ return (_usal_auth_cdrkit);
+ case SCG_SCCS_ID:
+ return (ata_sccsid);
+ }
+ }
+ return ((char *)0);
+}
+
+static int
+usalo_ahelp(SCSI *usalp, FILE *f)
+{
+ __usal_help(f, "ATA", "ATA Packet specific SCSI transport",
+ "ATAPI:", "bus,target,lun", "ATAPI:1,2,0", TRUE, FALSE);
+ return (0);
+}
+
+static int
+usalo_aopen(SCSI *usalp, char *device)
+{
+ int bus = usal_scsibus(usalp);
+ int target = usal_target(usalp);
+ int lun = usal_lun(usalp);
+
+ register int f;
+ register int b;
+ register int t;
+ register int l;
+ int nopen = 0;
+
+ if (usalp->overbose)
+ fprintf(stderr, "Warning: Using ATA Packet interface.\n");
+ if (usalp->overbose) {
+ fprintf(stderr, "Warning: The related Linux kernel interface code seems to be unmaintained.\n");
+ fprintf(stderr, "Warning: There is absolutely NO DMA, operations thus are slow.\n");
+ }
+
+ log(("\n<<<<<<<<<<<<<<<< LOGGING ON >>>>>>>>>>>>>>>>>\n"));
+ if (bus >= MAX_SCHILLY_HOSTS || target >= MAX_TGT || lun >= MAX_LUN) {
+ errno = EINVAL;
+ if (usalp->errstr)
+ snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
+ "Illegal value for bus, target or lun '%d,%d,%d'",
+ bus, target, lun);
+
+ return (-1);
+ }
+
+ if (usalp->local == NULL) {
+ usalp->local = malloc(sizeof (struct usal_local));
+ if (usalp->local == NULL) {
+ return (0);
+ }
+
+ usallocal(usalp)->usalfile = -1;
+ usallocal(usalp)->pgbus = -2;
+ usallocal(usalp)->SCSIbuf = (char *)-1;
+ usallocal(usalp)->pack_id = 5;
+ usallocal(usalp)->drvers = -1;
+ usallocal(usalp)->isold = -1;
+ usallocal(usalp)->xbufsize = 0L;
+ usallocal(usalp)->xbuf = NULL;
+
+
+ for (b = 0; b < MAX_SCHILLY_HOSTS; b++) {
+ typlocal(usalp, b) = HOST_EMPTY;
+ for (t = 0; t < MAX_TGT; t++) {
+ for (l = 0; l < MAX_LUN; l++)
+ usallocal(usalp)->usalfiles[b][t][l] = (short) -1;
+ }
+ }
+ }
+
+ if (device != NULL && strcmp(device, "ATAPI") == 0)
+ goto atascan;
+
+ /* if not scanning */
+ if ((device != NULL && *device != '\0') || (bus == -2 && target == -2))
+ goto openbydev;
+
+atascan:
+ if (scan_internal(usalp, &nopen)) {
+ if (usalp->errstr)
+ printf(usalp->errstr, "INFO: scan_internal(...) failed");
+ return (-1);
+ }
+ return (nopen);
+
+openbydev:
+ if (device != NULL && strncmp(device, "ATAPI:", 6) == 0)
+ device += 6;
+ if (usalp->debug > 3) {
+ fprintf((FILE *) usalp->errfile, "INFO: do usalo_open openbydev");
+ }
+ if (device != NULL && *device != '\0') {
+ int schilly_bus,
+ starget,
+ slun;
+
+ f = sg_open_excl(device, O_RDONLY | O_NONBLOCK, FALSE);
+
+ if (f < 0) {
+ if (usalp->errstr)
+ snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
+ "Cannot open '%s'", device);
+ return (0);
+ }
+ if (sg_amapdev(usalp, f, device, &schilly_bus, &starget, &slun)) {
+ usal_settarget(usalp, schilly_bus, starget, slun);
+ return (++nopen);
+ }
+ }
+ return (nopen);
+}
+
+static int
+scan_internal(SCSI *usalp, int *nopen)
+{
+ int i,
+ f;
+ int schilly_bus,
+ target,
+ lun;
+ char device[128];
+ /*
+ * try always with devfs
+ * unfortunatelly the solution with test of existing
+ * of '/dev/.devfsd' don't work, because it root.root 700
+ * and i don't like run suid root
+ */
+ BOOL DEVFS = TRUE;
+
+ if (DEVFS) {
+ for (i = 0; ; i++) {
+ sprintf(device, "/dev/cdroms/cdrom%i", i);
+ if ((f = open(device, O_RDONLY | O_NONBLOCK)) < 0) {
+ if (errno != ENOENT && errno != ENXIO && errno != ENODEV && errno != EACCES) {
+ if (usalp->debug > 4) {
+ fprintf((FILE *) usalp->errfile,
+ "try open(%s) return %i, errno %i, cancel\n", device, f, errno);
+ }
+ return (-2);
+ } else if (errno == ENOENT || errno == ENODEV) {
+ if (usalp->debug > 4) {
+ fprintf((FILE *) usalp->errfile,
+ "try open(%s) return %i, errno %i\n", device, f, errno);
+ }
+ if (0 == i) {
+ DEVFS = FALSE;
+ if (usalp->debug > 4) {
+ fprintf((FILE *) usalp->errfile,
+ "DEVFS not detected, continuing with old dev\n");
+ }
+ }
+ break;
+ }
+ if (usalp->debug > 4) {
+ if (errno == EACCES) {
+ fprintf((FILE *) usalp->errfile,
+ "errno (EACCESS), you don't have the needed rights for %s\n",
+ device);
+ }
+ fprintf((FILE *) usalp->errfile,
+ "try open(%s) return %i, errno %i, trying next cdrom\n",
+ device, f, errno);
+ }
+ } else {
+ if (usalp->debug > 4) {
+ fprintf((FILE *) usalp->errfile,
+ "try open(%s) return %i errno %i calling sg_mapdev(...)\n",
+ device, f, errno);
+ }
+ if (sg_amapdev(usalp, f, device, &schilly_bus, &target, &lun)) {
+ (++(*nopen));
+ } else {
+ close(f);
+ }
+ }
+ }
+ }
+ if (!DEVFS) {
+ /* for /dev/sr0 - /dev/sr? */
+ for (i = 0; i<16 ; i++) {
+ sprintf(device, "/dev/sr%i", i);
+ if ((f = open(device, O_RDONLY | O_NONBLOCK)) < 0) {
+ if (errno != ENOENT && errno != ENXIO && errno != ENODEV && errno != EACCES) {
+ if (usalp->debug > 4) {
+ fprintf((FILE *) usalp->errfile,
+ "try open(%s) return %i, errno %i, cancel\n",
+ device, f, errno);
+ }
+ return (-2);
+ }
+ } else {
+ if (sg_amapdev(usalp, f, device, &schilly_bus, &target, &lun)) {
+ (++(*nopen));
+ } else {
+ close(f);
+ }
+ }
+ }
+
+ /* for /dev/hda - /dev/hdz */
+ for (i = 'a'; i <= 'z'; i++) {
+ sprintf(device, "/dev/hd%c", i);
+ if ((f = open(device, O_RDONLY | O_NONBLOCK)) < 0) {
+ if (errno != ENOENT && errno != ENXIO && errno != EACCES) {
+ if (usalp->debug > 4) {
+ fprintf((FILE *) usalp->errfile,
+ "try open(%s) return %i, errno %i, cancel\n",
+ device, f, errno);
+ }
+ return (-2);
+ }
+ } else {
+ /* ugly hack, make better, when you can. Alex */
+ if (0 > ioctl(f, CDROM_DRIVE_STATUS, CDSL_CURRENT)) {
+ if (usalp->debug > 4) {
+ fprintf((FILE *) usalp->errfile,
+ "%s is not a cdrom, skipping\n",
+ device);
+ }
+ close(f);
+ } else if (sg_amapdev(usalp, f, device, &schilly_bus, &target, &lun)) {
+ (++(*nopen));
+ } else {
+ close(f);
+ }
+ }
+ }
+ }
+ return (0);
+}
+
+static int
+usalo_aclose(SCSI *usalp)
+{
+ register int f;
+ register int h;
+ register int t;
+ register int l;
+
+ if (usalp->local == NULL)
+ return (-1);
+
+ for (h = 0; h < MAX_SCHILLY_HOSTS; h++) {
+ typlocal(usalp, h) = (HOST_EMPTY);
+ for (t = 0; t < MAX_TGT; t++) {
+ for (l = 0; l < MAX_LUN; l++) {
+ f = usallocal(usalp)->usalfiles[h][t][l];
+ if (f >= 0)
+ close(f);
+ usallocal(usalp)->usalfiles[h][t][l] = (short) -1;
+ }
+ }
+ }
+
+ if (usallocal(usalp)->xbuf != NULL) {
+ free(usallocal(usalp)->xbuf);
+ usallocal(usalp)->xbufsize = 0L;
+ usallocal(usalp)->xbuf = NULL;
+ }
+ log(("<<<<<<<<<<<<<<<< LOGGING OFF >>>>>>>>>>>>>>>>>\n\n"));
+ return (0);
+}
+
+static int
+usalo_aget_first_free_shillybus(SCSI *usalp, int subsystem, int host, int bus)
+{
+ int first_free_schilly_bus;
+
+ for (first_free_schilly_bus = 0;
+ first_free_schilly_bus < MAX_SCHILLY_HOSTS;
+ first_free_schilly_bus++) {
+
+ if (typlocal(usalp, first_free_schilly_bus) == HOST_EMPTY ||
+ (typlocal(usalp, first_free_schilly_bus) == subsystem &&
+ hostlocal(usalp, first_free_schilly_bus) == host &&
+ buslocal(usalp, first_free_schilly_bus) == bus))
+ break;
+ }
+
+ if (first_free_schilly_bus >= MAX_SCHILLY_HOSTS) {
+ errmsgno(EX_BAD, "ERROR: in usalo_get_first_free_shillybus(...). Too many CDROMs, more than %i",
+ MAX_SCHILLY_HOSTS);
+ errmsgno(EX_BAD, "Increase MAX_SCHILLY_HOSTS in scsi-linux-ata.c and recompile!");
+ return (-1);
+ }
+ return (first_free_schilly_bus);
+}
+
+static int
+usalo_amerge(char *path, char *readedlink, char *buffer, int buflen)
+{
+ char *aa;
+
+#define TOKEN_ARRAY 20
+#define LAST_CHAR(x) (x)[strlen((x))-1]
+#define ONE_CHAR_BACK(x) (x)[strlen((x))-1] = '\0'
+ char *ppa[TOKEN_ARRAY];
+ char *pa;
+
+ int i;
+ int len;
+ char seps[] = "/";
+ char *last_slash;
+
+ if (!path || !readedlink || !buffer)
+ return (-EINVAL);
+
+ if ('/' == readedlink[0]) {
+ aa = (char *) malloc(strlen(readedlink) + 1);
+ if (!aa)
+ return (-ENOMEM);
+
+ strcpy(aa, readedlink);
+ } else {
+ aa = (char *) malloc(strlen(path) + strlen(readedlink) + 1);
+ if (!aa)
+ return (-ENOMEM);
+
+ strcpy(aa, path);
+ if (LAST_CHAR(aa) == '/') {
+ ONE_CHAR_BACK(aa);
+ }
+ last_slash = strrchr(aa, '/');
+ if (last_slash == NULL)
+ strcpy(aa, "/");
+ else
+ *(++last_slash) = '\0';
+ strcat(aa, readedlink);
+ }
+ memset(ppa, 0x00, sizeof (ppa));
+
+ for (i = 0, pa = strtok(aa, seps);
+ i < TOKEN_ARRAY && pa != NULL;
+ ++i, pa = strtok(NULL, seps)) {
+ ppa[i] = pa;
+ }
+
+ if (i == TOKEN_ARRAY) {
+ free(aa);
+ return (-ENOMEM);
+ }
+ for (i = 0; i < TOKEN_ARRAY && ppa[i]; i++) {
+ if (strcmp(ppa[i], "..") == 0) {
+ ppa[i] = NULL;
+ if (i > 1)
+ ppa[i - 1] = NULL;
+ }
+ }
+
+ /* dry run */
+ len = 0;
+ for (i = 0; i < TOKEN_ARRAY; i++) {
+ if (ppa[i]) {
+ len += 1;
+ len += strlen(ppa[i]);
+ }
+ }
+ if (0 == len)
+ len = 1;
+
+ if (len + 1 <= buflen) {
+ strcpy(buffer, "");
+ for (i = 0; i < TOKEN_ARRAY; i++) {
+ if (ppa[i]) {
+ strcat(buffer, "/");
+ strcat(buffer, ppa[i]);
+ }
+ }
+
+ if (strlen(buffer) == 0)
+ strcpy(buffer, "/");
+ }
+ free(aa);
+
+ return (len + 1);
+}
+
+/*
+ * /dev/cdroms/cdrom0 first CD-ROM
+ * /dev/cdroms/cdrom1 second CD-ROM
+ *
+ *
+ * SCSI Devices
+ *
+ * To uniquely identify any SCSI device requires the following information:
+ *
+ * controller (host adapter)
+ * bus (SCSI channel)
+ * target (SCSI ID)
+ * unit (Logical Unit Number)
+ *
+ * All SCSI devices are placed under /dev/scsi (assuming devfs is mounted on /dev).
+ * Hence, a SCSI device with the following parameters:
+ * c=1,b=2,t=3,u=4 would appear as:
+ *
+ * /dev/scsi/host1/bus2/target3/lun4 device directory
+ *
+ * Inside this directory, a number of device entries may be created,
+ * depending on which SCSI device-type drivers were installed.
+ *
+ * See the section on the disc naming scheme to see what entries
+ * the SCSI disc driver creates.
+ *
+ * See the section on the tape naming scheme to see what entries
+ * the SCSI tape driver creates.
+ *
+ * The SCSI CD-ROM driver creates: cd
+ * The SCSI generic driver creates: generic
+ *
+ * IDE Devices
+ *
+ * To uniquely identify any IDE device requires the following information:
+ *
+ * controller
+ * bus (0/1 aka. primary/secondary)
+ * target (0/1 aka. master/slave)
+ * unit
+ *
+ * All IDE devices are placed under /dev/ide, and uses a similar
+ * naming scheme to the SCSI subsystem.
+ *
+ *
+ * Example /dev/cdroms/cdrom0 -> /dev/scsi/host1/bus2/target3/lun4/cd
+ * Example /dev/cdroms/cdrom1 -> /dev/ide/host1/bus0/target1/lun4/cd
+ *
+ */
+static BOOL
+sg_amapdev(SCSI *usalp, int f, char *device, int *schillybus, int *target,
+ int *lun)
+{
+ struct host {
+ char host[4];
+ char host_no;
+ };
+ struct bus {
+ char bus[3];
+ char bus_no;
+ };
+ struct target {
+ char target[6];
+ char target_no;
+ };
+ struct lun {
+ char lun[3];
+ char lun_no;
+ };
+
+ int h,
+ b,
+ t,
+ l;
+
+#define TOKEN_DEV "dev"
+#define TOKEN_SUBSYSTEM_SCSI "scsi"
+#define TOKEN_SUBSYSTEM_IDE "ide"
+#define TOKEN_HOST "host"
+#define TOKEN_BUS "bus"
+#define TOKEN_TARGET "target"
+#define TOKEN_LUN "lun"
+#define TOKEN_CD "cd"
+
+#define ID_TOKEN_DEV 0
+#define ID_TOKEN_SUBSYSTEM 1
+#define ID_TOKEN_HOST 2
+#define ID_TOKEN_BUS 3
+#define ID_TOKEN_TARGET 4
+#define ID_TOKEN_LUN 5
+#define ID_TOKEN_CD 6
+#define ID_TOKEN_LAST ID_TOKEN_CD
+#define ID_TOKEN_MAX ID_TOKEN_LAST + 2
+#define CHARTOINT(x) (abs(atoi(&x)))
+
+ char *token[ID_TOKEN_MAX],
+ *seps = "/";
+ int i,
+ result;
+ struct stat buf;
+
+#ifndef MAX_PATH
+#define MAX_PATH 260
+#endif
+#define LOCAL_MAX_PATH MAX_PATH
+ char tmp[LOCAL_MAX_PATH],
+ tmp1[LOCAL_MAX_PATH];
+ int first_free_schilly_bus;
+ int subsystem = HOST_EMPTY;
+
+ /* old DEV */
+ typedef struct {
+ char prefix[2];
+ char device;
+ } old_dev;
+ /* strtok need char* instead of const char* */
+ result = stat(device, &buf);
+ if (result || !S_ISBLK(buf.st_mode))
+ return (FALSE);
+
+ result = lstat(device, &buf);
+ if (!result && S_ISLNK(buf.st_mode)) {
+ result = readlink(device, tmp, LOCAL_MAX_PATH);
+ if (result > 0 && result < LOCAL_MAX_PATH) {
+ tmp[result] = '\0';
+
+ result = usalo_amerge(device, tmp, tmp1, LOCAL_MAX_PATH);
+ if (result > 0 && result < LOCAL_MAX_PATH) {
+ tmp1[result] = '\0';
+ strcpy(tmp, tmp1);
+ } else {
+ errmsgno(EX_BAD,
+ "ERROR: with link merging! base %s link %s, result of merging %i\n",
+ device, tmp, result);
+ return (FALSE);
+ }
+ } else {
+ errmsgno(EX_BAD,
+ "ERROR: with link reading! link %s, result of readlink %i\n",
+ device, result);
+ return (FALSE);
+ }
+ } else {
+ strncpy(tmp, device, sizeof (tmp));
+ }
+ if (usalp->debug > 3) {
+ fprintf((FILE *) usalp->errfile, "INFO: %s -> %s\n", device, tmp);
+ }
+ memset(token, 0x00, sizeof (token));
+ i = 0;
+ token[i] = strtok(tmp, seps);
+ while (token[i] != NULL && (++i) && i < ID_TOKEN_MAX) {
+ token[i] = strtok(NULL, seps);
+ }
+
+ if (i == ID_TOKEN_MAX ||
+ !(token[ID_TOKEN_DEV]) ||
+ strcmp(token[ID_TOKEN_DEV], TOKEN_DEV)) {
+
+ errmsgno(EX_BAD, "ERROR: unknown format\n");
+ errmsgno(EX_BAD, "EXAMPLE: /dev/scsi/host1/bus2/target3/lun4/cd\n");
+ errmsgno(EX_BAD, "EXAMPLE: /dev/ide/host0/bus0/target1/lun0/cd\n");
+ errmsgno(EX_BAD, "EXAMPLE: /dev/hda or /dev/sr0\n");
+ return (FALSE);
+ }
+ if (!(strcmp(token[ID_TOKEN_SUBSYSTEM], TOKEN_SUBSYSTEM_SCSI)) ||
+ !(strcmp(token[ID_TOKEN_SUBSYSTEM], TOKEN_SUBSYSTEM_IDE))) {
+ h = CHARTOINT(((struct host *) token[ID_TOKEN_HOST])->host_no);
+ b = CHARTOINT(((struct bus *) token[ID_TOKEN_BUS])->bus_no);
+ t = CHARTOINT(((struct target *) token[ID_TOKEN_TARGET])->target_no);
+ l = CHARTOINT(((struct lun *) token[ID_TOKEN_LUN])->lun_no);
+#ifdef PARANOID
+ if (strncmp(token[ID_TOKEN_HOST], TOKEN_HOST, strlen(TOKEN_HOST))) {
+ log(("ERROR: invalid host specified\n"));
+ return (FALSE);
+ }
+ if (strncmp(token[ID_TOKEN_BUS], TOKEN_BUS, strlen(TOKEN_BUS))) {
+ log(("ERROR: invalid bus specified\n"));
+ return (FALSE);
+ }
+ if (strncmp(token[ID_TOKEN_TARGET], TOKEN_TARGET, strlen(TOKEN_TARGET))) {
+ log(("ERROR: invalid target specified\n"));
+ return (FALSE);
+ }
+ if (strncmp(token[ID_TOKEN_LUN], TOKEN_LUN, strlen(TOKEN_LUN))) {
+ log(("ERROR: invalid lun specified\n"));
+ return (FALSE);
+ }
+ if (!(strcmp(token[ID_TOKEN_SUBSYSTEM], TOKEN_SUBSYSTEM_IDE))) {
+ if (b > 1 || t > 1) {
+ log(("ERROR: invalid bus or target for IDE specified\n"));
+ return (FALSE);
+ }
+ }
+#endif /* PARANOID */
+
+ if (!(strcmp(token[ID_TOKEN_SUBSYSTEM], TOKEN_SUBSYSTEM_IDE))) {
+ subsystem = HOST_IDE;
+ } else if (!(strcmp(token[ID_TOKEN_SUBSYSTEM], TOKEN_SUBSYSTEM_SCSI))) {
+ subsystem = HOST_SCSI;
+ } else {
+ subsystem = HOST_OTHER;
+ }
+ } else if (!token[ID_TOKEN_HOST] &&
+ strlen(token[ID_TOKEN_SUBSYSTEM]) == sizeof (old_dev)) {
+ char j;
+
+ old_dev *pDev = (old_dev *) token[ID_TOKEN_SUBSYSTEM];
+
+ if (strncmp(pDev->prefix, "hd", 2) == 0) {
+ j = pDev->device - ('a');
+
+ subsystem = HOST_IDE;
+ h = j / 4;
+ b = (j % 4) / 2;
+ t = (j % 4) % 2;
+ l = 0;
+ } else if (strncmp(pDev->prefix, "sr", 2) == 0) {
+#ifdef nonono
+ if (pDev->device >= '0' && pDev->device <= '9')
+ j = pDev->device - ('0');
+ else
+ j = pDev->device - ('a');
+
+
+ h = j / 4;
+ b = (j % 4) / 2;
+ t = (j % 4) % 2;
+ l = 0;
+#endif /* nonono */
+ /* other solution, with ioctl */
+ int Chan = 0,
+ Ino = 0,
+ Bus = 0,
+ Target = 0,
+ Lun = 0;
+
+ subsystem = HOST_SCSI;
+ sg_amapdev_scsi(usalp, f, &Bus, &Target, &Lun, &Chan, &Ino);
+
+ /* For old kernels try to make the best guess. */
+#ifdef nonono
+ int n;
+ Ino |= Chan << 8;
+ n = sg_mapbus(usalp, Bus, Ino);
+ if (Bus == -1) {
+ Bus = n;
+ if (usalp->debug > 0) {
+ fprintf((FILE *)usalp->errfile,
+ "SCSI Bus: %d (mapped from %d)\n",
+ Bus, Ino);
+ }
+ }
+/* It is me too high ;-()*/
+#endif /* nonono */
+ h = Ino;
+ b = Chan;
+ t = Target;
+ l = Lun;
+ } else {
+ errmsgno(EX_BAD, "ERROR: unknow subsystem (%s) in (%s)\n",
+ token[ID_TOKEN_SUBSYSTEM], device);
+ return (FALSE);
+ }
+ } else {
+ errmsgno(EX_BAD, "ERROR: unknow subsystem (%s) in (%s)\n",
+ token[ID_TOKEN_SUBSYSTEM], device);
+ return (FALSE);
+ }
+
+ if (usalp->verbose)
+ printf(usalp->errstr, "INFO: subsystem %s: h %i, b %i, t %i, l %i",
+ token[ID_TOKEN_SUBSYSTEM], h, b, t, l);
+
+ first_free_schilly_bus = usalo_aget_first_free_shillybus(usalp, subsystem, h, b);
+ if (-1 == first_free_schilly_bus) {
+ return (FALSE);
+ }
+ if (usallocal(usalp)->usalfiles[first_free_schilly_bus][t][l] != (-1)) {
+ errmsgno(EX_BAD, "ERROR: this cdrom is already mapped %s(%d,%d,%d)\n",
+ device, first_free_schilly_bus, t, l);
+ return (FALSE);
+ } else {
+ usallocal(usalp)->usalfiles[first_free_schilly_bus][t][l] = f;
+ typlocal(usalp, first_free_schilly_bus) = subsystem;
+ hostlocal(usalp, first_free_schilly_bus) = h;
+ buslocal(usalp, first_free_schilly_bus) = b;
+ *schillybus = first_free_schilly_bus;
+ *target = t;
+ *lun = l;
+
+ if (usalp->debug > 1) {
+ fprintf((FILE *) usalp->errfile,
+ "INFO: /dev/%s, (host%d/bus%d/target%d/lun%d) will be mapped on the schilly bus No %d (%d,%d,%d)\n",
+ token[ID_TOKEN_SUBSYSTEM], h, b, t, l,
+ first_free_schilly_bus, first_free_schilly_bus, t, l);
+ }
+ }
+ return (TRUE);
+}
+
+static BOOL
+sg_amapdev_scsi(SCSI *usalp, int f, int *busp, int *tgtp, int *lunp,
+ int *chanp, int *inop)
+{
+ 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 (ioctl(f, SCSI_IOCTL_GET_IDLUN, &sg_id))
+ return (FALSE);
+
+ if (usalp->debug > 0) {
+ fprintf((FILE *) usalp->errfile,
+ "INFO: 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 (usalp->debug > 0) {
+ fprintf((FILE *) usalp->errfile,
+ "INFO: 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);
+}
+
+static long
+usalo_amaxdma(SCSI *usalp, long amt)
+{
+ /*
+ * EINVAL (hart) ENOMEM (weich) bei mehr ...
+ * Bei fehlerhaftem Sense Pointer kommt EFAULT
+ */
+ return (MAX_DMA_ATA);
+}
+
+static BOOL
+usalo_ahavebus(SCSI *usalp, int busno)
+{
+ register int t;
+ register int l;
+
+ if (busno < 0 || busno >= MAX_SCHILLY_HOSTS)
+ return (FALSE);
+
+ if (usalp->local == NULL)
+ return (FALSE);
+
+ for (t = 0; t < MAX_TGT; t++) {
+ for (l = 0; l < MAX_LUN; l++)
+ if (usallocal(usalp)->usalfiles[busno][t][l] >= 0)
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+static int
+usalo_afileno(SCSI *usalp, int busno, int tgt, int tlun)
+{
+ if (busno < 0 || busno >= MAX_SCHILLY_HOSTS ||
+ tgt < 0 || tgt >= MAX_TGT ||
+ tlun < 0 || tlun >= MAX_LUN)
+ return (-1);
+
+ if (usalp->local == NULL)
+ return (-1);
+
+ return ((int) usallocal(usalp)->usalfiles[busno][tgt][tlun]);
+}
+
+static int
+usalo_ainitiator_id(SCSI *usalp)
+{
+ printf(usalp->errstr, "NOT IMPELEMENTED: usalo_initiator_id");
+ return (-1);
+}
+
+static int
+usalo_aisatapi(SCSI *usalp)
+{
+ int schillybus = usalp->addr.scsibus;
+ int typ = typlocal(usalp, schillybus);
+ if (typ == HOST_EMPTY)
+ return (-1);
+ if (typ != HOST_SCSI)
+ return (1);
+ else
+ return (0);
+}
+
+static int
+usalo_areset(SCSI *usalp, int what)
+{
+ if (what == SCG_RESET_NOP)
+ return (0);
+
+ if (what == SCG_RESET_TGT || what == SCG_RESET_BUS)
+ return (ioctl(what, CDROMRESET));
+
+ return (-1);
+}
+
+static int
+usalo_asend(SCSI *usalp)
+{
+ struct usal_cmd *sp = usalp->scmd;
+ int ret,
+ i;
+ struct cdrom_generic_command sg_cgc;
+ struct request_sense sense_cgc;
+
+#ifdef DEBUG
+ char tmp_send[340],
+ tmp_read[340],
+ tmp_sense[340],
+ tmp1[30];
+ int j;
+ char *p;
+#endif
+
+ if (usalp->fd < 0) {
+ sp->error = SCG_FATAL;
+ sp->ux_errno = EIO;
+ return (0);
+ }
+ if (sp->cdb_len > CDROM_PACKET_SIZE) {
+ sp->error = SCG_FATAL;
+ sp->ux_errno = EIO;
+ return (0);
+ }
+ /* initialize */
+ fillbytes((caddr_t) & sg_cgc, sizeof (sg_cgc), '\0');
+ fillbytes((caddr_t) & sense_cgc, sizeof (sense_cgc), '\0');
+
+ if (sp->flags & SCG_RECV_DATA) {
+ sg_cgc.data_direction = CGC_DATA_READ;
+ } else if (sp->size > 0) {
+ sg_cgc.data_direction = CGC_DATA_WRITE;
+ } else {
+ sg_cgc.data_direction = CGC_DATA_NONE;
+ }
+#if LINUX_VERSION_CODE >= 0x020403
+ if (sp->flags & SCG_SILENT) {
+ sg_cgc.quiet = 1;
+ }
+#endif
+ for (i = 0; i < sp->cdb_len; i++) {
+ sg_cgc.cmd[i] = sp->cdb.cmd_cdb[i];
+ }
+
+ sg_cgc.buflen = sp->size;
+ sg_cgc.buffer = (unsigned char *)sp->addr;
+
+ if (sp->sense_len > sizeof (sense_cgc))
+ sense_cgc.add_sense_len = sizeof (sense_cgc) - 8;
+ else
+ sense_cgc.add_sense_len = sp->sense_len - 8;
+
+ sg_cgc.sense = &sense_cgc;
+#if LINUX_VERSION_CODE >= 0x020403
+ sg_cgc.timeout = sp->timeout * 1000;
+#endif
+#ifdef DEBUG
+ strcpy(tmp_send, "send cmd:\n");
+ for (j = 0; j < sp->cdb_len; j++) {
+ sprintf(tmp1, " %02X", sp->cdb.cmd_cdb[j]);
+ strcat(tmp_send, tmp1);
+ }
+ strcat(tmp_send, "\n");
+
+ if (sg_cgc.data_direction == CGC_DATA_WRITE) {
+ int z;
+
+ sprintf(tmp1, "data_write: %i bytes\n", sp->size);
+ strcat(tmp_send, tmp1);
+ for (j = 0, z = 1; j < 80 && j < sp->size; j++, z++) {
+ if (z > 16) {
+ z = 1;
+ strcat(tmp_send, "\n");
+ }
+ sprintf(tmp1, " %02X", (unsigned char) (sp->addr[j]));
+ strcat(tmp_send, tmp1);
+ }
+ strcat(tmp_send, "\n");
+
+ if (sp->size > 80) {
+ strcat(tmp_send, "...\n");
+ }
+ }
+#endif /* DEBUG */
+ if ((ret = ioctl(usalp->fd, CDROM_SEND_PACKET, &sg_cgc)) < 0)
+ sp->ux_errno = geterrno();
+
+ if (ret < 0 && usalp->debug > 4) {
+ fprintf((FILE *) usalp->errfile,
+ "ioctl(CDROM_SEND_PACKET) ret: %d\n", ret);
+ }
+ /*
+ * copy scsi data back
+ */
+ if (sp->flags & SCG_RECV_DATA && ((void *) sp->addr != (void *) sg_cgc.buffer)) {
+ memcpy(sp->addr, sg_cgc.buffer, (sp->size < sg_cgc.buflen) ? sp->size : sg_cgc.buflen);
+ if (sg_cgc.buflen > sp->size)
+ sp->resid = sg_cgc.buflen - sp->size;
+ }
+ sp->error = SCG_NO_ERROR;
+#ifdef DEBUG
+ if (ret < 0) {
+ switch (sp->ux_errno) {
+ case ENOTTY:
+ p = "ENOTTY";
+ break;
+ case EINVAL:
+ p = "EINVAL";
+ break;
+ case ENXIO:
+ p = "ENXIO";
+ break;
+ case EACCES:
+ p = "EACCES";
+ break;
+ case EIO:
+ p = "EIO";
+ break;
+ case ENOMEDIUM:
+ p = "ENOMEDIUM";
+ break;
+ case EDRIVE_CANT_DO_THIS:
+ p = "EDRIVE_CANT_DO_THIS";
+ break;
+ default:
+ p = "UNKNOW";
+ };
+ log(("%s", tmp_send));
+ log(("ERROR: returns %i errno %i(%s)\n", ret, sp->ux_errno, p));
+ }
+#endif /* DEBUG */
+ if (ret < 0) {
+ /*
+ * Check if SCSI command cound not be send at all.
+ * Linux usually returns EINVAL for an unknoen 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 == EINVAL) {
+ /*
+ * Try to work around broken Linux kernel design...
+ * If SCSI Sense Key is 0x05 (Illegal request), Linux
+ * returns a useless EINVAL making it close to
+ * impossible distinct from "Illegal ioctl()" or
+ * "Invalid parameter".
+ */
+ if ((((Uchar *)sg_cgc.sense)[0] != 0) ||
+ (((Uchar *)sg_cgc.sense)[2] != 0))
+ sp->ux_errno = EIO;
+
+ } else if ((sp->ux_errno == ENOTTY || sp->ux_errno == EINVAL)) {
+ /*
+ * May be "Illegal ioctl()".
+ */
+ return (-1);
+ }
+ if (sp->ux_errno == ENXIO || sp->ux_errno == EACCES) {
+ return (-1);
+ }
+ } else if (ret == 0) {
+#ifdef DEBUG
+ if (sg_cgc.data_direction == CGC_DATA_READ) {
+ int z;
+
+ sprintf(tmp_read, "data_read: %i bytes\n", sp->size);
+ for (j = 0, z = 1; j < 80 && j < sp->size; j++, z++) {
+ if (z > 16) {
+ z = 1;
+ strcat(tmp_read, "\n");
+ }
+ sprintf(tmp1, " %02X", (unsigned char) (sp->addr[j]));
+ strcat(tmp_read, tmp1);
+ }
+ strcat(tmp_read, "\n");
+ if (sp->size > 80) {
+ strcat(tmp_read, "...\n");
+ }
+ }
+#endif /* DEBUG */
+ }
+ /*
+ * copy sense back
+ */
+ if (ret < 0 && sg_cgc.sense->error_code) {
+ sp->sense_count = sense_cgc.add_sense_len + 8;
+#ifdef DEBUG
+ sprintf(tmp_sense, "sense_data: length %i\n", sp->sense_count);
+ for (j = 0; j < sp->sense_count; j++) {
+ sprintf(tmp1, " %02X", (((unsigned char *) (&sense_cgc))[j]));
+ strcat(tmp_sense, tmp1);
+ }
+ log(("%s\n", tmp_sense));
+
+ sprintf(tmp_sense, "sense_data: error code 0x%02X, sense key 0x%02X,"
+ " additional length %i, ASC 0x%02X, ASCQ 0x%02X\n",
+ sg_cgc.sense->error_code, sg_cgc.sense->sense_key,
+ sg_cgc.sense->add_sense_len, sg_cgc.sense->asc,
+ sg_cgc.sense->ascq);
+
+ log(("%s\n", tmp_sense));
+#endif /* DEBUG */
+ memcpy(sp->u_sense.cmd_sense, /* (caddr_t) */ &sense_cgc, SCG_MAX_SENSE);
+ sp->u_scb.cmd_scb[0] = ST_CHK_COND;
+
+ switch (sg_cgc.sense->sense_key) {
+ case SC_UNIT_ATTENTION:
+ case SC_NOT_READY:
+ sp->error = SCG_RETRYABLE; /* may be BUS_BUSY */
+ sp->u_scb.cmd_scb[0] |= ST_BUSY;
+ break;
+ case SC_ILLEGAL_REQUEST:
+ break;
+ default:
+ break;
+ }
+ } else {
+ sp->u_scb.cmd_scb[0] = 0x00;
+ }
+
+ sp->resid = 0;
+ return (0);
+}
+#endif /* USE_OLD_ATAPI */