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)/