Xref: lysator.liu.se comp.periphs.scsi:7877 comp.sys.sun.misc:6968 Newsgroups: comp.periphs.scsi,comp.sys.sun.misc Path: lysator.liu.se!isy.liu.se!liuida!sunic!pipex!uunet!island!hue From: hue@island.com (Pond Scum) Subject: Loadable SCSI driver skeleton for SunOS 4.1.3 Message-ID: Summary: example loadable SCSI driver Keywords: scsi scsa loadable driver Sender: usenet@island.COM (The Usenet mail target) Organization: Island Graphics Corp. Date: Tue, 9 Nov 1993 04:34:29 GMT Lines: 712 I had a couple days to do anything I wanted while waiting for my Macintosh to show up, so I decided to try to see if I could get loadable SCSI drivers to work under SunOS 4.1.3. This is a skeleton SCSI driver, with only the load/unload parts fleshed out. It may be useful as an example however. This code is only useful if you haven't yet switched to Solaris 2.x. -Jonathan hue@island.COM ---cut-here-----cut-here-----cut-here-----cut-here-----cut-here-----cut-here--- #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'README' <<'END_OF_FILE' X XThis is a skeleton for a SunOS 4.1.3 loadable SCSI driver. Loadable XSCSI drivers are not documented or supported by Sun under SunOS 4.x. XThis driver doesn't actually do anything, but is intended to show Xyou how you can take an existing driver and convert it into a loadable Xdriver. This driver will only work on architectures which support SCSA X(sun4c and sun4m). Since the rest of the driver has been left out, it Xdoes not show you how to write SCSA drivers, but if you already have Xone, you can use this to help you make it loadable. However, it can Xbe compiled and loaded. X X XFiles: X XREADME This file. Xsptskel.c Skeleton of a loadable SCSI driver. Xsptskeldef.h Header file containing some driver definitions. X XThis is what I came up with after looking over some hints which were Xemailed to me and after some poking around with adb on a running Xkernel. It seems to work ok, I've built a loadable "/dev/scsi" X(command block pass-thru) driver with it and was able to scan Xfrom a Canon bubblejet color copier with it. X XDISCLAIMER X XThis code is provided as an example only. It may have bugs in it. In Xfact, it probably does. Your machine will probably crash if you load Xthis driver into it. You better hit the 'n' key now and forget you ever Xsaw this. If you're foolish enough to want to try this out, compile Xit with "cc -DKERNEL -D`arch` sptskel.c", and then use "modload" to Xload it. Then you can check to see if it is loaded with "modstat". X Xducie# modload spt.o Xmodule loaded; id = 10 Xducie# modstat XId Type Loadaddr Size B-major C-major Sysnum Mod Name X10 Drv ff1a9000 4000 60. SCSI Pass-Thru Driver X 2 User ff195000 6000 netatalk X 1 Drv ff04c000 4000 59. cgeight X XAt load time it probes all the unused target IDs on the SCSI bus. So Xmake sure you don't already have a driver linked into the kernel for Xa SCSI device for which you are trying to write a loadable driver. If Xyou plan to use the skeleton as is, you need to know how the driver Xinterprets the minor number. It is used as follows: X X --------------------------------- X | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | X --------------------------------- X ^ ^ ^ ^ ^ ^ ^ ^ X | | | | | | | | X | | | | | |-------------------- Logical Unit 0-7 X | | | | | X | | | | | X | | |-------------------------------- SCSI Target ID 0-6 X | | (7 reserved for host) X | | X |---------------------------------------- Host Adapter 0-3 X X XSo if the device you want to talk to is on the built-in host adapter, Xtarget ID four, logical unit zero, the minor number would be: X X (0 << 6) | (4 << 3) | 0 X Xor 32 decimal. You would then need to make a character special device Xnode in the file system using the major number which was assigned to the Xdriver, minor number of 32, and whatever name you want. For instance, Xif modstat tells me the following: X X[312]ducie:hue: modstat XId Type Loadaddr Size B-major C-major Sysnum Mod Name X 5 Drv ff19d000 4000 60. SCSI pass-thru driver X 2 User ff197000 6000 netatalk X 1 Drv ff04f000 4000 59. cgeight X Xthen I would make the node as follows: X X# mknod /dev/whatever c 60 32 X XYou can actually do this in a script which is called by modload, it passes Xthe driver's major numbers as command line arguments to the script. X XThen when you need the scsi_device in the driver, you just code something Xlike: X X sptopen(dev, flags) X dev_t dev; X int flags; X { X struct scsi_device *sc_devp; X struct spt_device *un; X X if (!(sc_devp = sptunits[SPT_DEV(dev)])) X return(ENODEV); X un = PRIV_DATA(sc_devp); X X XThe one thing I haven't figured out is why the slave and attach functions Xget called ten times when the module is loaded. I just pay attention to Xthe first call to xxslave() and xxattach() and ignore the others (this Xmay be wrong in a big way). X X-Jonathan hue@island.COM END_OF_FILE if test 4226 -ne `wc -c <'README'`; then echo shar: \"'README'\" unpacked with wrong size! fi # end of 'README' fi if test -f 'sptskel.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'sptskel.c'\" else echo shar: Extracting \"'sptskel.c'\" \(11123 characters\) sed "s/^X//" >'sptskel.c' <<'END_OF_FILE' X/* X * X * sptskel.c: A skeleton SCSI driver for SunOS 4.1.3 which is loadable. X * X * Copyright (c) 1993 Jonathan F. Hue All Rights Reserved X * X * This example is provided without warranties, express or implied, X * including those for performance, merchantability or fitness for X * a particular purpose. The user assumes the entire risk of using X * this software, in no event shall Jonathan F. Hue be liable for any X * damages, direct, indirect, or other which may arise from use of X * this software. X * X * X * This example shows only the parts needed to make a SCSI driver loadable X * under 4.1.3. You'll want to probably rename your xxslave() and xxattach() X * functions xxsubslave() and xxsubattach() because of how things get called X * when the module is loaded. Since your per-unit data is probably different X * from mine, be sure that you allocate, initialize, and free resources X * correctly at module load and unload time. Also take a look at the X * unload() function, so you can return EBUSY if your driver is still in use. X * X * To compile, use "cc -DKERNEL -D`arch` sptskel.c" X * X */ X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include "sptskeldef.h" X X#ifndef LINT Xstatic char rcsid[] = "$Id: spt.c,v 1.7 1993/11/09 03:29:51 hue Exp hue $"; X#endif X X#define NORMAL_CMD_TIMEOUT 10 X Xint sptdebug = 0; X Xextern struct dev_info *top_devinfo; Xextern struct scsi_device *sd_root; Xextern char *scsi_dname(); Xextern int nscsi_devices, scsibuscookies[]; Xextern int nodev(); Xextern int hz; X Xstatic int sptslave(), sptattach(), sptopen(), sptclose(); Xstatic int sptioctl(), sptsubslave(); Xstatic void sptsubattach(), sptintr(); Xstatic int spt_pri; /* interrupt priority */ Xstatic void remove_from_scsi_chain(), remove_devinfo_nodes(); X Xstatic struct dev_ops spt_ops = X{ X 1, sptslave, sptattach, sptopen, sptclose, nodev, X nodev, nodev, nodev, 0, sptioctl X}; X Xstatic struct cdevsw sptcdev = X{ X sptopen, sptclose, nodev, nodev, sptioctl, nodev, nodev, 0 X}; X Xstatic struct vdldrv vd = X{ X VDMAGIC_DRV, X "SCSI Pass-Thru Driver", X &spt_ops, X NULL, X &sptcdev, X 0, X 0 X}; X X#define MAX_HOST_ADAPTERS_SUPPORTED 4 X#define MAX_SPTUNITS MAX_HOST_ADAPTERS_SUPPORTED * 8 * 8 X Xstatic struct scsi_device *sptunits[MAX_SPTUNITS]; X Xxxxinit(fc, vdp, vdi, vds) Xu_int fc; Xstruct vddrv *vdp; Xaddr_t vdi; Xstruct vdstat *vds; X{ X switch(fc) X { X case VDLOAD: vdp->vdd_vdtab = (struct vdlinkage *) &vd; X return(0); X case VDUNLOAD: return(unload(vdp, vdi)); X case VDSTAT: return(0); X default: return(EIO); X } X} X X X/* X * If all units are not in use, allow driver to be unloaded X */ Xstatic int Xunload(vdp, vdi) X{ X int i; X struct scsi_device *sd; X struct spt_device *sp; X X for (i = 0; i < MAX_SPTUNITS; i++) X { X if (!(sd = sptunits[i])) X continue; X sp = PRIV_DATA(sd); X if (sp && sp->md_omap) X return(EBUSY); X } X remove_from_scsi_chain(); X remove_devinfo_nodes(top_devinfo); X return(0); X} X X X/* X * Remove all the scsi_devices called "spt" from the scsi_device chain, X * freeing all resources associated with the scsi_device. X */ Xstatic void Xremove_from_scsi_chain() X{ X struct scsi_device *prev, *p, *tmp; X X prev = NULL; X p = sd_root; X X while (p) X { X if (strcmp(p->sd_dev->devi_name, "spt") == 0) X { X if (p == sd_root) /* at front of list, adjust list head */ X sd_root = p->sd_next; X else /* not at front, skip over this one */ X prev->sd_next = p->sd_next; X tmp = p->sd_next; X if (PRIV_DATA(p)->md_rqspkt) X free_pktiopb(PRIV_DATA(p)->md_rqspkt, (caddr_t) p->sd_sense, X SENSE_LENGTH); X kmem_free(p, sizeof(struct scsi_device)); X p = tmp; X nscsi_devices--; X } X else X { X prev = p; X p = p->sd_next; X } X } X} X X X/* X * Remove all the dev_info nodes for our driver from the tree rooted at p. X */ Xstatic void Xremove_devinfo_nodes(p) Xstruct dev_info *p; X{ X struct dev_info *childp, *prev, *tmp; X X if (!p) X return; X if (p->devi_name && (strcmp("esp", p->devi_name) == 0)) X { X childp = p->devi_slaves; X while (childp) X { X if (strcmp(childp->devi_name, "spt") == 0) X { X if (childp == p->devi_slaves) /* at front, adjust head */ X p->devi_slaves = childp->devi_next; X else /* not at front, skip over */ X prev->devi_next = childp->devi_next; X kmem_free(childp->devi_intr, sizeof(struct dev_intr)); X tmp = childp->devi_next; X kmem_free(childp, sizeof(struct dev_info)); X childp = tmp; X } X else X { X prev = childp; X childp = childp->devi_next; X } X } X } X else X { X remove_devinfo_nodes(p->devi_slaves); X remove_devinfo_nodes(p->devi_next); X } X} X X X/* X * Return a byte where each set bit represents a target already in use by X * another driver on host adapter ha_num. X */ Xstatic u_char Xfind_used_targets(ha_num) Xint ha_num; X{ X struct scsi_device *p; X u_char ret; X X ret = 0; X p = sd_root; X while (p) X { X if (p->sd_address.a_cookie == scsibuscookies[ha_num]) X ret |= 1 << p->sd_address.a_target; X p = p->sd_next; X } X return(ret); X} X X X/* X * Walk the dev_info tree, and wherever you see an "esp", figure out which X * targets are unused and add a dev_info node for each device that responds. X */ Xstatic void Xadd_devinfo_nodes(p) Xstruct dev_info *p; X{ X int targ, lun, i; X struct dev_info *newp; X struct scsi_device *sd; X static int unit = 0; X u_char devs_used; X X if (!p) X return; X if (strcmp(p->devi_name, "esp") == 0) X { X if (p->devi_unit >= MAX_HOST_ADAPTERS_SUPPORTED) X { X printf("add_devinfo_nodes: can't support this many \ Xhost adapters\n"); X return; X } X devs_used = find_used_targets(p->devi_unit); X X /* X * Allocate a dev_info node and a scsi_device, but don't hook them X * into the dev_info tree and scsi_device list until we've probed X * that address and seen if something responds. X */ X for (targ = 0; targ < 7; targ++) X { X if (devs_used & (1 << targ)) X continue; X for (lun = 0; lun < 8; lun++) X { X i = (p->devi_unit << 6) | (targ << 3) | lun; X newp = (struct dev_info *) kmem_zalloc(sizeof(struct dev_info)); X newp->devi_parent = p; X newp->devi_next = NULL; X newp->devi_slaves = NULL; X newp->devi_name = "spt"; X newp->devi_nreg = 0; X newp->devi_reg = NULL; X newp->devi_nintr = 0; X newp->devi_intr = (struct dev_intr *) X kmem_zalloc(sizeof(struct dev_intr)); X bcopy((caddr_t) p->devi_intr, (caddr_t) newp->devi_intr, X sizeof(struct dev_intr)); X newp->devi_driver = &spt_ops; X newp->devi_data = NULL; X newp->devi_nodeid = 0; X newp->devi_unit = unit; X newp->devi_nrng = 0; X newp->devi_rng = NULL; X X sd = (struct scsi_device *) X kmem_zalloc(sizeof(struct scsi_device)); X sd->sd_address.a_cookie = scsibuscookies[p->devi_unit]; X sd->sd_address.a_target = targ; X sd->sd_address.a_lun = lun; X sd->sd_address.a_sublun = 0; X sd->sd_dev = newp; X sd->sd_inq = NULL; X sd->sd_sense = NULL; X sd->sd_private = NULL; X sd->sd_present = 0; X X /* see if anything is out there */ X if (sptsubslave(sd) == 1) X { X /* hook into devinfo tree */ X newp->devi_next = p->devi_slaves; X p->devi_slaves = newp; X X /* hook into scsi_device chain */ X sd->sd_next = sd_root; X sd_root = sd; X X /* stick ptr to scsi_device in our pvt. array */ X sptunits[i] = sd; X unit++; /* want units consecutively numbered */ X } X else X { X /* free everything */ X kmem_free(newp->devi_intr, sizeof(struct dev_intr)); X kmem_free(newp, sizeof(struct dev_info)); X kmem_free(sd, sizeof(struct scsi_device)); X } X } X } X } X else X { X add_devinfo_nodes(p->devi_slaves); X add_devinfo_nodes(p->devi_next); X } X} X X Xstatic int Xsptslave() X{ X static int first_time = 1; X X if (first_time) X { X printf("SCSI Pass-Thru Driver $Revision: 1.7 $ Copyright (c) 1993 \ XJonathan F. Hue All Rights Reserved.\n"); X add_devinfo_nodes(top_devinfo); X first_time = 0; X } X return(1); X} X X Xstatic int Xsptattach() X{ X static int first_time = 1; X int i; X X if (first_time) X { X first_time = 0; X for (i = 0; i < MAX_SPTUNITS; i++) X { X if (sptunits[i]) X sptsubattach(sptunits[i]); X } X } X return(0); X} X X Xstatic int Xsptsubslave(sc_devp) Xstruct scsi_device *sc_devp; X{ X int rval, targ, lun; X struct spt_device *un; X struct scsi_pkt *rqpkt; X char *data; X static char s[] = "Vendor: ........ Product: ................ Rev. Level: ...."; X X targ = sc_devp->sd_address.a_target; X lun = sc_devp->sd_address.a_lun; X switch(rval = scsi_slave(sc_devp, NULL_FUNC)) X { X case SCSIPROBE_EXISTS: X if (sc_devp->sd_inq->inq_dtype != 0x7f) X { X bcopy(sc_devp->sd_inq->inq_vid, s + 8, 8); X bcopy(sc_devp->sd_inq->inq_pid, s + 27, 16); X bcopy(sc_devp->sd_inq->inq_revision, s + 57, 4); X printf("%s device at target %d lun %d\n%s\n", X scsi_dname(sc_devp->sd_inq->inq_dtype), targ, lun, s); X sc_devp->sd_present = 1; X } X break; X case SCSIPROBE_NONCCS: X printf("Non-CCS device at target %d lun %d\n", targ, lun); X sc_devp->sd_present = 1; X break; X case SCSIPROBE_NORESP: X break; X case SCSIPROBE_NOMEM: X printf("scsi_slave failed for targ %d lun %d, resource shortage\n", X targ, lun); X break; X case SCSIPROBE_FAILURE: X printf("scsi_slave failed for targ %d lun %d, command failure\n", X targ, lun); X break; X } X IOPBFREE(sc_devp->sd_inq, SUN_INQSIZE); X if (!sc_devp->sd_present) X return(0); X X /* X * Allocate whatever resources we need X */ X rqpkt = get_pktiopb(ROUTE(sc_devp), (caddr_t *) &sc_devp->sd_sense, X CDB_GROUP0, 1, SENSE_LENGTH, B_READ, NULL_FUNC); X if (!rqpkt) X { X printf("%s%d: cannot allocate memory for SCSI REQUEST SENSE command \ Xpacket\n", X DEV_NAME(sc_devp), DEV_UNIT(sc_devp)); X return(0); X } X makecom_g0(rqpkt, sc_devp, FLAG_NODISCON | FLAG_NOINTR, SCMD_REQUEST_SENSE, X 0, SENSE_LENGTH); X rqpkt->pkt_pmon = -1; X rqpkt->pkt_comp = sptintr; X rqpkt->pkt_time = NORMAL_CMD_TIMEOUT; X X un = (struct spt_device *) kmem_zalloc(sizeof(struct spt_device)); X if (!un) X { X printf("%s%d: cannot allocate memory for private data\n", X DEV_NAME(sc_devp), DEV_UNIT(sc_devp)); X free_pktiopb(rqpkt, (caddr_t) sc_devp->sd_sense, SENSE_LENGTH); X return(0); X } X sc_devp->sd_private = (caddr_t) un; X un->md_rqspkt = rqpkt; X un->md_sd = sc_devp; X un->md_ubuf_busy = 0; X spt_pri = MAX(spt_pri, ipltospl(sc_devp->sd_dev->devi_intr->int_pri)); X return(1); X} X X X/* X * Not much gets done here X */ Xstatic void Xsptsubattach(sc_devp) Xstruct scsi_device *sc_devp; X{ X struct spt_device *un; X X un = PRIV_DATA(sc_devp); X un->md_omap = 0; /* mark everything closed */ X New_state(un, SPT_STATE_CLOSED); X return; X} X X Xstatic int Xsptopen(dev, flags) Xdev_t dev; Xint flags; X{ X} X X Xstatic int Xsptclose(dev, flags) Xdev_t dev; Xint flags; X{ X} X X Xstatic int Xsptioctl(dev, cmd, data, flags) Xdev_t dev; Xint cmd, flags; Xcaddr_t data; X{ X} X X Xstatic void Xsptintr(pkt) Xstruct scsi_pkt *pkt; X{ X} END_OF_FILE if test 11123 -ne `wc -c <'sptskel.c'`; then echo shar: \"'sptskel.c'\" unpacked with wrong size! fi # end of 'sptskel.c' fi if test -f 'sptskeldef.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'sptskeldef.h'\" else echo shar: Extracting \"'sptskeldef.h'\" \(1883 characters\) sed "s/^X//" >'sptskeldef.h' <<'END_OF_FILE' X#ifndef _SPTDEF_H_ X#define _SPTDEF_H_ X X/* X * X */ X X#include X#include X X#ifdef KERNEL /* keeps the application weenies out... */ X X/* X * Per-device private information X */ Xstruct spt_device X{ X struct scsi_device *md_sd; /* back pointer to scsi_device */ X struct scsi_pkt *md_rqspkt; /* preallocated pkt for rqst sns */ X struct buf md_ubuf; /* buf for uscsi */ X u_int md_ubuf_busy; /* lock for ubuf */ X struct uscsi_cmd md_uscsi_cmd; /* user-scsi command */ X struct diskhd md_utab; /* buf queue head */ X u_char md_state; /* current state of driver */ X u_char md_last_state; /* previoius state of driver */ X u_long md_omap; /* map of opens on raw & blk dev */ X int md_retry_count; /* # of retries so far on error */ X}; X X X#define SPT_STATE_VIRGIN 0 /* untouched (unsoiled?) */ X#define SPT_STATE_CLOSED 1 /* initialized but not in use */ X#define SPT_STATE_INUSE 2 /* open */ X#define SPT_STATE_RWAIT 3 /* waiting for resource */ X X X#define SPT_DEV(dev) (minor(dev)) X#define DEV_NAME(devp) ((devp)->sd_dev->devi_name) X#define DEV_UNIT(devp) ((devp)->sd_dev->devi_unit) X#define CTRL_NAME(devp) ((devp)->sd_dev->devi_parent->devi_name) X#define CTRL_UNIT(devp) ((devp)->sd_dev->devi_parent->devi_unit) X X#define PRIV_DATA(devp) ((struct spt_device *) (devp)->sd_private) X#define ROUTE(devp) (&((devp)->sd_address)) X#define TARGET(devp) ((devp)->sd_address.a_target) X#define SCBP(pkt) ((struct scsi_status *) (pkt)->pkt_scbp) X#define SCBP_C(pkt) ((*(pkt)->pkt_scbp) & STATUS_MASK) X#define CDBP(pkt) ((union scsi_cdb *) (pkt)->pkt_cdbp) X#define BP_PKT(bp) ((struct scsi_pkt *) (bp)->av_back) X X X#define New_state(un, s) \ X { (un)->md_last_state=(un)->md_state; (un)->md_state=(s); } X X#define Restore_state(un) \ X { u_char tmp = (un)->md_last_state; New_state((un), tmp); } X X X X#endif KERNEL X#endif _SPTDEF_H_ END_OF_FILE if test 1883 -ne `wc -c <'sptskeldef.h'`; then echo shar: \"'sptskeldef.h'\" unpacked with wrong size! fi # end of 'sptskeldef.h' fi echo shar: End of shell archive. exit 0