[comp.unix.aux] YA /dev/scsi for low-level user SCSI access

roadman@portia.Stanford.EDU (arthur walker) (09/19/90)

Here is a simple /dev/scsi driver for A/UX 1.1 and 2.0 
which allows user-level access to the SCSI bus.
It is written by Tony Cooper, who for the time being
is unable to post for northern hemisphere distribution.

art walker
ahw@egret0.stanford.edu
walker@meggie.stanford.edu


Send mail to Tony Cooper at the address below.

-----

From: SRAMTRC@albert.dsir.govt.nz (Tony Cooper)
Newsgroups: comp.unix.aux
Subject: /dev/scsi - Here it is
Message-ID: <10742@albert.dsir.govt.nz>
Date: 13 Sep 90 13:39:15 GMT
Organization: DSIR, Auckland, New Zealand
Lines: 448

Since some interest has been shown in this newsgroup in /dev/scsi and since
it is easy to write one, I did. Included in the shar file below are the
kernel driver and an example user-level application. Do what you wish with
the code. 

Someone who is keen could write a SCSIDispatch routine to emulate the MacOS
one. This shouldn't be very difficult. The tricky part would be to get 
MacOS applications to execute your bit of code when they do a SCSIDispatch.
I don't know how that's done.

Cheers,
Tony Cooper
sramtrc@albert.dsir.govt.nz (NOT tony@popserver.stanford.edu anymore)

------------------------------- cut here ---------------------------------------
cat > README << \END_OF_SHAR_FOR_README
**** /dev/scsi Notes

This driver implements user level access to the scsi bus. Any user program can
use the bus by sending SCSI commands using the userscsireq structure defined
in dev_scsi.h. This structure is a simplified version of Apple's scsireq
structure.

See the dev_scsi.h file and the demo program inquiry.c to see how to use
/dev/scsi. My experience is that it is fairly safe to play around with the
SCSI Manager as long as you don't write data to devices. The worst that seems
to happen is that you hang the SCSI bus and have to reset. Make sure you do
syncs to avoid losing recently written data. If you do write data to devices
then make sure you know what you are doing. Else you gonna trash your file
system. /dev/scsi accesses the SCSI Manager so you are not likely to crash
the kernel unless you come across a bug in the Manager.

The demo program inquiry.c scans the bus looking for devices and does an
inquiry on devices it finds.


**** Installation

As root type

make install

This will compile the driver and copy the files to the right places. To compile
the demo program called inquiry type make.

Then type

newconfig dev_scsi		(under A/UX 1.1 use newunix and autoconfig )

to build a new kernel with the driver installed. Then reboot to use it. Run
the demo program to check the installation.

The final thing you may wish to do is to change the protections on the device
file /dev/scsi. You may wish to restrict it to root only else your security
goes out the door.

**** Restrictions

The /dev/scsi driver has restricted the use of the device to one user at a time.
Also the length of the databuf buffer must be 8192 bytes or less. These are
arbitrary restrictions and can be removed or increased. For more than one user
at a time, more than one request structure is required. Use kmem_alloc() to
allocate more. Feel free to change the driver to suit.


Have fun

Tony Cooper
sramtrc@albert.dsir.govt.nz
END_OF_SHAR_FOR_README
cat > Makefile << \END_OF_SHAR_FOR_Makefile
all:		dev_scsi inquiry

dev_scsi:	dev_scsi.o
		ld -o dev_scsi -x -r dev_scsi.o /usr/lib/unshared.ld

inquiry:	inquiry.o
		cc -o inquiry inquiry.o

install:	/etc/install.d/dev_scsi /etc/uninstall.d/dev_scsi /etc/install.d/boot.d/dev_scsi

/etc/install.d/dev_scsi:	dev_scsi.install
		cp dev_scsi.install /etc/install.d/dev_scsi

/etc/uninstall.d/dev_scsi:	dev_scsi.uninstall
		cp dev_scsi.uninstall /etc/uninstall.d/dev_scsi

/etc/install.d/boot.d/dev_scsi: dev_scsi
		cp dev_scsi /etc/install.d/boot.d/dev_scsi

clean:
		rm -f *.o dev_scsi inquiry

dev_scsi.o:	dev_scsi.h
inquiry.o:	dev_scsi.h
END_OF_SHAR_FOR_Makefile
cat > dev_scsi.c << \END_OF_SHAR_FOR_dev_scsi.c
/*
 * /dev/scsi
 * User access to the SCSI Manager
 */

#include "dev_scsi.h"
#define COPYIN(x, n)   errno = copyin((char *)userreq->x,(char *)req.x,req.n);\
		       if (errno) return (errno)
#define COPYOUT(x, n)  errno = copyout((char *)req.x,(char *)userreq->x,req.n);\
		       if (errno) return (errno)
			

static int scsi_opened;
static struct scsireq      req;                /* scsi request packet */
static u_char              cmdbuf[10];         /* cmd block for scsi commands */
static u_char              sensebuf[SENSEBUF]; /* sense buffer */
static u_char              databuf[DATABUF];   /* internal data buffer */

void
dev_scsi_init()
{
    int dev_scsi_ret();

    req.faddr = dev_scsi_ret;
    req.cmdbuf = (caddr_t) cmdbuf;
    req.databuf = (caddr_t) databuf;
    req.sensebuf = (caddr_t) sensebuf;
    printf("/dev/scsi: User access to SCSI Manager installed v1.00\n");
}

int
dev_scsi_open(dev, flags)
dev_t dev;
int flags;
{
    if (scsi_opened) {
	printf("/dev/scsi already open\n");
	return(EBUSY);
    }
    else {
	scsi_opened = 1;
	return(0);
    }
}

void
dev_scsi_close(dev,flag)
dev_t dev;
int flag;
{
    if (scsi_opened)
	scsi_opened = 0;
    else
	printf("/dev/scsi not open\n");
}

int
dev_scsi_ioctl(dev, cmd, addr, mode)
dev_t dev;
int cmd;
caddr_t addr;
int mode;
{
    register struct userscsireq *userreq;
    register int errno;
    register int ret;


    if (! scsi_opened) {
	printf("/dev/scsi not open\n");
	return(ENXIO);
    }
    if (cmd != SCSISTART) {
	printf("/dev/scsi: invalid command\n");
	return(ENXIO);
    }

    userreq = (struct userscsireq *) addr;

    req.cmdlen	    = userreq->cmdlen;
    req.datalen	    = userreq->datalen;
    req.senselen    = userreq->senselen;
    req.flags	    = userreq->flags;
    req.timeout	    = userreq->timeout;
    COPYIN(cmdbuf,cmdlen);
    if (req.flags & SRQ_READ != SRQ_READ) COPYIN(databuf,datalen);


    ret = scsireq(userreq->id, &req, (struct vio *)0);       /* fire it off */
    if (ret) return(EINVAL);
    sleep(&req, PZERO);			/* wait for the request to finish */

    userreq->datasent	= req.datasent;
    userreq->sensesent	= req.sensesent;
    userreq->msg	= req.msg;
    userreq->stat	= req.stat;
    userreq->ret	= req.ret;
    if (req.flags & SRQ_READ == SRQ_READ) COPYOUT(databuf,datasent);
    COPYOUT(sensebuf,sensesent);
    return(0);
}

int
dev_scsi_ret()
{
    wakeup(&req);
    return(0);
}
END_OF_SHAR_FOR_dev_scsi.c
cat > dev_scsi.h << \END_OF_SHAR_FOR_dev_scsi.h
#include <sys/types.h>
#include <sys/vio.h>
#include <sys/scsiccs.h>
#include <sys/scsireq.h>
#include <sys/errno.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <fcntl.h>

#define	NST		8		/* max devices allowed */
#define DATABUF		8192		/* size of data buffer */
#define SENSEBUF	100		/* size of sense buffer */

struct	userscsireq {
	u_long	id;		/* scsi id for request */
	caddr_t cmdbuf;		/* buffer containing command block */
	u_char	cmdlen;		/* length of command buffer */
	caddr_t	databuf;	/* buffer containing the data to send or rcv */
	u_long  datalen;	/* length of the data buffer */
	u_long  datasent;	/* length of data actually sent or received */
	caddr_t	sensebuf;	/* optional error result from sense cmd */
	u_char	senselen;	/* optional length of the sense buffer */
	u_char	sensesent;	/* length of sense data recieved */
	u_short flags;		/* Read or Write flags */
	u_char	msg;		/* mode byte returned on cmd complete */
	u_char	stat;		/* completion status byte */
	u_char	ret;		/* return code from SCSI Manager */
	short	timeout;	/* maximum seconds inactivity for request */
	};

static int dev_scsi_fildes;

#define SCSISTART	_IOWR(S, 1, struct userscsireq)	/* call the Manager */

#define OPEN_SCSI       if ((dev_scsi_fildes = open("/dev/scsi",O_RDWR)) < 0) {\
                            perror("OPEN_SCSI");\
                            exit(1);\
                        }
#define CLOSE_SCSI      if (close(dev_scsi_fildes) < 0) {\
                            perror("CLOSE_SCSI");\
                            exit(1);\
                        }
#define DO_SCSI(req)    if (ioctl(dev_scsi_fildes, SCSISTART, &req) < 0) {\
                            perror("DO_SCSI");\
                            exit(1);\
                        }
END_OF_SHAR_FOR_dev_scsi.h
cat > dev_scsi.install << \END_OF_SHAR_FOR_dev_scsi.install
:

PATH=/bin:/usr/bin:/etc:/usr/etc

	name=dev_scsi

#
#	Install the driver object file
#
	rm -f /etc/boot.d/$name
	ln /etc/install.d/boot.d/$name /etc/boot.d/$name

#
#	Install the driver master file
#
	echo 'include .' >/etc/master.d/$name
	echo 'cpn	-	dev_scsi_	-	8	1' >>/etc/master.d/$name
	chown bin /etc/master.d/$name
	chgrp bin /etc/master.d/$name
	chmod 644 /etc/master.d/$name

#
#	Install the driver init file
#

cat >/etc/init.d/$name << EOF
:
# Build the $name devices based upon minor number passed in via autoconfig.
#
PATH=/bin:/usr/bin:/etc:/usr/etc

#
#       Parse "-k kernelfile" flag
#

KERNEL=""
if [ "\$1" = "-k" ]; then
    shift
    KERNEL=\$1
    shift
fi;

if [ "\$1" = "-M" ]
then
    /bin/rm -f /dev/scsi
    /etc/mknod /dev/scsi c \$2 0
    /bin/chmod 666 /dev/scsi
fi
EOF

chown bin /etc/init.d/$name
chgrp bin /etc/init.d/$name
chmod 744 /etc/init.d/$name
END_OF_SHAR_FOR_dev_scsi.install
chmod a+x dev_scsi.install
cat > dev_scsi.uninstall << \END_OF_SHAR_FOR_dev_scsi.uninstall
:

PATH=/bin:/usr/bin:/etc:/usr/etc

	name=dev_scsi

#	Delete the object file
 
	rm -f /etc/boot.d/$name

#	Delete the master file
 
	rm -f /etc/master.d/$name

#	Delete the init file

	rm -f /etc/init.d/$name
END_OF_SHAR_FOR_dev_scsi.uninstall
chmod a+x dev_scsi.uninstall
cat > inquiry.c << \END_OF_SHAR_FOR_inquiry.c
#include "dev_scsi.h"

#define INQ_LEN	24		/* length of inquiry data */

main()
{
    struct userscsireq req;	/* the user's request structure */
    u_char cmdbuf[10];		/* SCSI command bytes go here (6 or 10 bytes) */
    u_char databuf[INQ_LEN];	/* data to or from the device goes into here */
    u_char sensebuf[100];	/* sense data from the device goes here */
    register int id,j,l;

    /*
     * Now fille in the values of our request structure 
     */
    req.cmdbuf = (caddr_t) cmdbuf;
    req.databuf = (caddr_t) databuf;
    req.sensebuf = (caddr_t) sensebuf;
    req.cmdlen = 6;		/* a group 0 command */
    req.datalen = INQ_LEN;
    req.senselen = 100;
    req.timeout = 4;		/* give this command 4 seconds to execute */
    req.flags = SRQ_READ;	/* use 0 for writes, SRQ_READ for reads */

    /*
     * Now create the SCSI command
     */
    cmdbuf[0] = 0x12;		/* inquiry header value */
    cmdbuf[1] = 0;
    cmdbuf[2] = 0;
    cmdbuf[3] = 0;
    cmdbuf[4] = INQ_LEN;	/* make sure this value equals datalen */
    cmdbuf[5] = 0;		/* don't use link or flag bits */

    OPEN_SCSI;			/* Have to open the device first */

    /*
     * Now do through each SCSI id and do an inquiry
     */
    for (id = 0; id < 7; id++) {
	req.id = id;
	DO_SCSI(req);		/* Fire off the SCSI request */
	printf("SCSI id = %d ", id);
	switch (req.ret) {	/* Have a look at the request's return value */
	    case 0:
		printf("Inquiry string = ");
		for (j = 8; j < req.datasent; j++) {
		    if (j == 16) printf(" ");
		    printf("%c", databuf[j]);
		}
		printf("\n");
		break;
	    case SST_BSY:
		printf("Device dropped busy unexpectedly\n");
		printf("Try checking your cables\n");
		break;
	    case SST_CMD:
		printf("An error happened during command transmission\n");
		printf("Try checking your request values\n");
		break;
	    case SST_COMP:
		printf("An error occured during the status phase\n");
		printf("Try checking your cables\n");
		break;
	    case SST_SENSE:
		printf("An error occured obtaining sense data\n");
		printf("Try checking your request values\n");
		break;
	    case SST_SEL:
		printf("No unit reponded to the given id\n");
		/*printf("Buy another device\n");*/
		break;
	    case SST_TIMEOUT:
		printf("Request was idle longer than timeout value\n");
		printf("The SCSI bus has been reset. Try again\n");
		break;
	    case SST_MULT:
		printf("Multiple requests were placed for this id\n");
		printf("Check your program. One request at a time please\n");
		break;
	    case SST_PROT:
		printf("A problem occured in SCSI protocol\n");
		printf("Try checking your cables\n");
		break;
	    case SST_MORE:
		printf("There was more/less data than the device expected\n");
		printf("Try checking your requested data sizes\n");
		break;
	    case SST_STAT:
		printf("Status word was non zero, sense data was requested\n");
		printf("Sense data is ");
		for (j = 0; j < req.sensesent; j++) {
		    printf("%02x", sensebuf[j]);
		}
		printf("\n");
		break;
	    case SST_AGAIN:
		printf("A transfer error occured\n");
		printf("Try placing request again\n");
		break;
	    case SST_REVCODE:
		printf("Caller or Manager out-of-date\n");
		printf("Try recompiling dev_scsi\n");
		break;
	    case SST_CANCEL:
		printf("Request cancelled by call to scsicancel\n");
		printf("If you didn't do it, someone else did\n");
		break;
	    default:
		printf("Major internal error\n");
		printf("Bail out, bail out, go go go!\n");
		break;
	}
    }
    CLOSE_SCSI;			/* Have to close the device */
}
END_OF_SHAR_FOR_inquiry.c

urlichs@smurf.sub.org (Matthias Urlichs) (09/20/90)

In comp.unix.aux, article <1990Sep19.021148.17831@portia.Stanford.EDU>,
  roadman@portia.Stanford.EDU (arthur walker) writes:
< Here is a simple /dev/scsi driver for A/UX 1.1 and 2.0 
< which allows user-level access to the SCSI bus.
< It is written by Tony Cooper, who for the time being
< is unable to post for northern hemisphere distribution.
< 
< Send mail to Tony Cooper at the address below.
< 
Thanks, Tony, for sharing this code.

Now for something more ambitious ...
- Changing the code so that you _can't_ trash the kernel (at least, not that
  easily ;-). Probably do some dynamic allocation while we're at it.
- Implementing a patch to _SCSIDispatch so that things like Retrospect,
  or your favorite scanner, will work again.

The only real difficulty is, of course, MacOS programs which access the data
they're trying to read before calling _SCSIComplete.
I hope there aren't too many of these...

I'll implement that, if no one else beats me to it.
(Probably going to take some time, though.)

-- 
Matthias Urlichs -- urlichs@smurf.sub.org -- urlichs@smurf.ira.uka.de     /(o\
Humboldtstrasse 7 - 7500 Karlsruhe 1 - FRG -- +49+721+621127(0700-2330)   \o)/