/* @(#)scsi-linux-ata.c 1.7 04/06/12 Copyright 2002 J. Schilling */
#ifndef lint
static char ata_sccsid[] =
"@(#)scsi-linux-ata.c 1.7 04/06/12 Copyright 2002 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.
*
* 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) 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 as published by
* the Free Software Foundation; either version 2, 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; see the file COPYING. If not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef USE_ATA
LOCAL char _scg_atrans_version[] = "scsi-linux-ata.c-1.7"; /* The version for ATAPI transport*/
LOCAL char * scgo_aversion __PR((SCSI *scgp, int what));
LOCAL int scgo_ahelp __PR((SCSI *scgp, FILE *f));
LOCAL int scgo_aopen __PR((SCSI *scgp, char *device));
LOCAL int scgo_aclose __PR((SCSI *scgp));
LOCAL long scgo_amaxdma __PR((SCSI *scgp, long amt));
LOCAL BOOL scgo_ahavebus __PR((SCSI *scgp, int));
LOCAL int scgo_afileno __PR((SCSI *scgp, int, int, int));
LOCAL int scgo_ainitiator_id __PR((SCSI *scgp));
LOCAL int scgo_aisatapi __PR((SCSI *scgp));
LOCAL int scgo_areset __PR((SCSI *scgp, int what));
LOCAL int scgo_asend __PR((SCSI *scgp));
LOCAL scg_ops_t ata_ops = {
scgo_asend,
scgo_aversion,
scgo_ahelp,
scgo_aopen,
scgo_aclose,
scgo_amaxdma,
scgo_getbuf, /* Shared with SG driver */
scgo_freebuf, /* Shared with SG driver */
scgo_ahavebus,
scgo_afileno,
scgo_ainitiator_id,
scgo_aisatapi,
scgo_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) scglocal(p)->bc[schillybus].typ
#define buslocal(p, schillybus) scglocal(p)->bc[schillybus].bus
#define hostlocal(p, schillybus) scglocal(p)->bc[schillybus].host
#define MAX_DMA_ATA (131072-1) /* EINVAL (hart) ENOMEM (weich) bei mehr ... */
/* Bei fehlerhaftem Sense Pointer kommt EFAULT */
LOCAL int scgo_send __PR((SCSI * scgp));
LOCAL BOOL sg_amapdev __PR((SCSI * scgp, int f, char *device, int *bus,
int *target, int *lun));
LOCAL BOOL sg_amapdev_scsi __PR((SCSI * scgp, int f, int *busp, int *tgtp,
int *lunp, int *chanp, int *inop));
LOCAL int scgo_aget_first_free_shillybus __PR((SCSI * scgp, int subsystem,
int host, int bus));
LOCAL int scgo_amerge __PR((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
LOCAL void sglog __PR((const char *fmt, ...));
#include <vadefs.h>
/* VARARGS1 */
#ifdef PROTOTYPES
LOCAL void
sglog(const char *fmt, ...)
#else
LOCAL void
error(fmt, va_alist)
char *fmt;
va_dcl
#endif
{
va_list args;
FILE *f = fopen(LOGFILE, "a");
if (f == NULL)
return;
#ifdef PROTOTYPES
va_start(args, fmt);
#else
va_start(args);
#endif
js_fprintf(f, "%r", fmt, args);
va_end(args);
fclose(f);
}
#else
#define log(a)
#endif /* DEBUG */
LOCAL int scan_internal __PR((SCSI * scgp, 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.
*/
LOCAL char *
scgo_aversion(scgp, what)
SCSI *scgp;
int what;
{
if (scgp != (SCSI *)0) {
switch (what) {
case SCG_VERSION:
return (_scg_atrans_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 (ata_sccsid);
}
}
return ((char *)0);
}
LOCAL int
scgo_ahelp(scgp, f)
SCSI *scgp;
FILE *f;
{
__scg_help(f, "ATA", "ATA Packet specific SCSI transport",
"ATAPI:", "bus,target,lun", "ATAPI:1,2,0", TRUE, FALSE);
return (0);
}
LOCAL int
scgo_aopen(scgp, device)
SCSI *scgp;
char *device;
{
int bus = scg_scsibus(scgp);
int target = scg_target(scgp);
int lun = scg_lun(scgp);
register int f;
register int b;
register int t;
register int l;
int nopen = 0;
if (scgp->overbose)
error("Warning: Using ATA Packet interface.\n");
if (scgp->overbose) {
error("Warning: The related Linux kernel interface code seems to be unmaintained.\n");
error("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 (scgp->errstr)
js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
"Illegal value for bus, target or lun '%d,%d,%d'",
bus, target, lun);
return (-1);
}
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)->xbufsize = 0L;
scglocal(scgp)->xbuf = NULL;
for (b = 0; b < MAX_SCHILLY_HOSTS; b++) {
typlocal(scgp, b) = HOST_EMPTY;
for (t = 0; t < MAX_TGT; t++) {
for (l = 0; l < MAX_LUN; l++)
scglocal(scgp)->scgfiles[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(scgp, &nopen)) {
if (scgp->errstr)
js_printf(scgp->errstr, "INFO: scan_internal(...) failed");
return (-1);
}
return (nopen);
openbydev:
if (device != NULL && strncmp(device, "ATAPI:", 6) == 0)
device += 6;
if (scgp->debug > 3) {
js_fprintf((FILE *) scgp->errfile, "INFO: do scgo_open openbydev");
}
if (device != NULL && *device != '\0') {
int schilly_bus,
starget,
slun;
f = open(device, O_RDWR | O_NONBLOCK);
if (f < 0) {
if (scgp->errstr)
js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
"Cannot open '%s'", device);
return (0);
}
if (sg_amapdev(scgp, f, device, &schilly_bus, &starget, &slun)) {
scg_settarget(scgp, schilly_bus, starget, slun);
return (++nopen);
}
}
return (nopen);
}
#include <glob.h>
LOCAL int
scan_internal(scgp, nopen)
SCSI *scgp;
int *nopen;
{
int i,
f;
int schilly_bus,
target,
lun;
char *device;
glob_t globbuf;
/*
* 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 = FALSE;
glob("/dev/cdroms/cdrom*",
GLOB_NOSORT,
NULL, &globbuf);
glob("/dev/hd[a-z]",
GLOB_NOSORT|GLOB_APPEND,
NULL, &globbuf);
/*glob("/dev/scd*",
GLOB_NOSORT|GLOB_APPEND,
NULL, &globbuf);*/
for (i = 0; globbuf.gl_pathv && globbuf.gl_pathv[i] != NULL ; i++) {
device = globbuf.gl_pathv[i];
if ((f = open(device, O_RDWR | O_NONBLOCK)) < 0) {
if (errno != ENOENT && errno != ENXIO && errno != ENODEV && errno != EACCES) {
if (scgp->debug > 4) {
js_fprintf((FILE *) scgp->errfile,
"try open(%s) return %i, errno %i, cancel\n", device, f, errno);
}
globfree(&globbuf);
return (-2);
}
if (scgp->debug > 4) {
if (errno == EACCES) {
js_fprintf((FILE *) scgp->errfile,
"errno (EACCESS), you don't have the needed rights for %s\n",
device);
}
js_fprintf((FILE *) scgp->errfile,
"try open(%s) return %i, errno %i, trying next cdrom\n",
device, f, errno);
}
} else {
if (scgp->debug > 4) {
js_fprintf((FILE *) scgp->errfile,
"try open(%s) return %i errno %i calling sg_mapdev(...)\n",
device, f, errno);
}
if (sg_amapdev(scgp, f, device, &schilly_bus, &target, &lun)) {
(++(*nopen));
} else {
close(f);
}
}
}
globfree(&globbuf);
return (0);
}
LOCAL int
scgo_aclose(scgp)
SCSI *scgp;
{
register int f;
register int h;
register int t;
register int l;
if (scgp->local == NULL)
return (-1);
for (h = 0; h < MAX_SCHILLY_HOSTS; h++) {
typlocal(scgp, h) = (HOST_EMPTY);
for (t = 0; t < MAX_TGT; t++) {
for (l = 0; l < MAX_LUN; l++) {
f = scglocal(scgp)->scgfiles[h][t][l];
if (f >= 0)
close(f);
scglocal(scgp)->scgfiles[h][t][l] = (short) -1;
}
}
}
if (scglocal(scgp)->xbuf != NULL) {
free(scglocal(scgp)->xbuf);
scglocal(scgp)->xbufsize = 0L;
scglocal(scgp)->xbuf = NULL;
}
log(("<<<<<<<<<<<<<<<< LOGGING OFF >>>>>>>>>>>>>>>>>\n\n"));
return (0);
}
LOCAL int
scgo_aget_first_free_shillybus(scgp, subsystem, host, bus)
SCSI *scgp;
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(scgp, first_free_schilly_bus) == HOST_EMPTY ||
(typlocal(scgp, first_free_schilly_bus) == subsystem &&
hostlocal(scgp, first_free_schilly_bus) == host &&
buslocal(scgp, first_free_schilly_bus) == bus))
break;
}
if (first_free_schilly_bus >= MAX_SCHILLY_HOSTS) {
errmsgno(EX_BAD, "ERROR: in scgo_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);
}
LOCAL int
scgo_amerge(path, readedlink, buffer, buflen)
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
*
*/
LOCAL BOOL
sg_amapdev(scgp, f, device, schillybus, target, lun)
SCSI *scgp;
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 (0 && !result && S_ISLNK(buf.st_mode)) {
result = readlink(device, tmp, LOCAL_MAX_PATH);
if (result > 0 && result < LOCAL_MAX_PATH) {
tmp[result] = '\0';
result = scgo_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 (scgp->debug > 3) {
js_fprintf((FILE *) scgp->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: unknow 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,
Ino,
Bus,
Target,
Lun;
subsystem = HOST_SCSI;
sg_amapdev_scsi(scgp, 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(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);
}
}
/* 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 (scgp->verbose)
js_printf(scgp->errstr, "INFO: subsystem %s: h %i, b %i, t %i, l %i",
token[ID_TOKEN_SUBSYSTEM], h, b, t, l);
first_free_schilly_bus = scgo_aget_first_free_shillybus(scgp, subsystem, h, b);
if (-1 == first_free_schilly_bus) {
return (FALSE);
}
if (scglocal(scgp)->scgfiles[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 {
scglocal(scgp)->scgfiles[first_free_schilly_bus][t][l] = f;
typlocal(scgp, first_free_schilly_bus) = subsystem;
hostlocal(scgp, first_free_schilly_bus) = h;
buslocal(scgp, first_free_schilly_bus) = b;
*schillybus = first_free_schilly_bus;
*target = t;
*lun = l;
if (scgp->debug > 1) {
js_fprintf((FILE *) scgp->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);
}
LOCAL BOOL
sg_amapdev_scsi(scgp, f, busp, tgtp, lunp, chanp, inop)
SCSI *scgp;
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 (scgp->debug > 0) {
js_fprintf((FILE *) scgp->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 (scgp->debug > 0) {
js_fprintf((FILE *) scgp->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);
}
LOCAL long
scgo_amaxdma(scgp, amt)
SCSI *scgp;
long amt;
{
/*
* EINVAL (hart) ENOMEM (weich) bei mehr ...
* Bei fehlerhaftem Sense Pointer kommt EFAULT
*/
return (MAX_DMA_ATA);
}
LOCAL BOOL
scgo_ahavebus(scgp, busno)
SCSI *scgp;
int busno;
{
register int t;
register int l;
if (busno < 0 || busno >= MAX_SCHILLY_HOSTS)
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_afileno(scgp, busno, tgt, tlun)
SCSI *scgp;
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 (scgp->local == NULL)
return (-1);
return ((int) scglocal(scgp)->scgfiles[busno][tgt][tlun]);
}
LOCAL int
scgo_ainitiator_id(scgp)
SCSI *scgp;
{
js_printf(scgp->errstr, "NOT IMPELEMENTED: scgo_initiator_id");
return (-1);
}
LOCAL int
scgo_aisatapi(scgp)
SCSI *scgp;
{
int schillybus = scgp->addr.scsibus;
int typ = typlocal(scgp, schillybus);
if (typ == HOST_EMPTY)
return (-1);
if (typ != HOST_SCSI)
return (1);
else
return (0);
}
LOCAL int
scgo_areset(scgp, what)
SCSI *scgp;
int what;
{
if (what == SCG_RESET_NOP)
return (0);
if (what == SCG_RESET_TGT || what == SCG_RESET_BUS)
return (ioctl(what, CDROMRESET));
return (-1);
}
LOCAL int
scgo_asend(scgp)
SCSI *scgp;
{
struct scg_cmd *sp = scgp->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 (scgp->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 = 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(scgp->fd, CDROM_SEND_PACKET, &sg_cgc)) < 0)
sp->ux_errno = geterrno();
if (ret < 0 && scgp->debug > 4) {
js_fprintf((FILE *) scgp->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_ATA */
syntax highlighted by Code2HTML, v. 0.9.1