2 * $Id: gscdd.c,v 1.1 2001/04/15 11:12:37 ant Exp $
3 * Copyright (c) 1996, 1997 by Matthew Jacob
5 * This software is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; version 2.
9 * This software is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this software; if not, write to the Free
16 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 * The author may be reached via electronic communications at
22 * or, via United States Postal Address
26 * San Francisco, CA, 94131
30 #include <sys/types.h>
31 #include <sys/errno.h>
32 #include <sys/sysmacros.h>
33 #include <sys/syspest.h>
34 #include <sys/ioctl.h>
35 #include <sys/i_machine.h>
36 #include <sys/systm.h>
38 #include <sys/devinfo.h>
39 #include <sys/lockl.h>
40 #include <sys/device.h>
42 #include <sys/watchdog.h>
43 #include <sys/errids.h>
44 #include <sys/trchkid.h>
46 #include <sys/iostat.h>
47 #include <sys/bootrecord.h>
49 #include <sys/malloc.h>
50 #include <sys/sleep.h>
51 #include <sys/fp_io.h>
53 #include <sys/lock_alloc.h>
57 static int strlen(char *s) { char *p = s; while (*p) p++; return p - s; }
58 static void memset(void *x, int val, size_t amt)
59 { char *p = (char *)x; while (--amt) *p++ = (char) val; }
60 static void memcpy(void *dst, void *src, size_t amt)
61 { char *dest = dst, *source = src; while (--amt) *dest++ = *source++; }
62 #define bcopy(src, dst, nbytes) memcpy(dst, src, nbytes)
68 #define HKWD_GSC_DD 0x66600000
70 #define _COM_TRACE(b, var) \
71 var = strlen(b); trcgenk(0, HKWD_GSC_DD, var, var, b)
73 #define Trace0(val, str) \
74 if (scudebug >= val) { \
76 _COM_TRACE(str, icxq); \
78 #define Trace1(val, fmt, arg1) \
79 if (scudebug >= val) { \
80 int icxq; char buf[256]; \
81 (void) sprintf(buf, fmt, arg1); \
82 _COM_TRACE(buf, icxq); \
84 #define Trace2(val, fmt, arg1, arg2) \
85 if (scudebug >= val) { \
86 int icxq; char buf[256]; \
87 (void) sprintf(buf, fmt, arg1, arg2); \
88 _COM_TRACE(buf, icxq); \
90 #define Trace3(val, fmt, arg1, arg2, arg3) \
91 if (scudebug >= val) { \
92 int icxq; char buf[256]; \
93 (void) sprintf(buf, fmt, arg1, arg2, arg3); \
94 _COM_TRACE(buf, icxq); \
96 #define Trace4(val, fmt, arg1, arg2, arg3, arg4) \
97 if (scudebug >= val) { \
98 int icxq; char buf[256]; \
99 (void) sprintf(buf, fmt, arg1, arg2, arg3, arg4); \
100 _COM_TRACE(buf, icxq); \
103 #define Trace5(val, fmt, arg1, arg2, arg3, arg4, arg5) \
104 if (scudebug >= val) { \
105 int icxq; char buf[256]; \
106 (void) sprintf(buf, fmt, arg1, arg2, arg3, arg4, arg5); \
107 _COM_TRACE(buf, icxq); \
110 #define Trace6(val, fmt, arg1, arg2, arg3, arg4, arg5, arg6) \
111 if (scudebug >= val) { \
112 int icxq; char buf[256]; \
113 (void) sprintf(buf, fmt, arg1, arg2, arg3, arg4, arg5, arg6); \
114 _COM_TRACE(buf, icxq); \
117 #define MJ_RTN(VAL) simple_unlock(&sp->dd_lock); return (VAL)
120 struct sc_buf scsibuf;
126 Simple_lock buf_lock;
127 struct file *fp; /* file pointer */
129 #define cmdbuf cbuf.scsibuf /* buffer for command */
131 #define rqsbuf rbuf.scsibuf /* buffer for request sense */
132 dev_t dev; /* Adapter dev */
133 u_char tgt; /* target ID */
134 u_char lun; /* logical unit */
135 u_char isopen; /* device is open */
136 u_char iscfg; /* non-zero to show this as configured */
137 u_char unstart; /* stop device on unconfigure */
138 u_char needresume; /* needs an SC_RESUME with next command */
142 * External References
144 extern int copyin(void *, void *, int);
145 extern int copyout(void *, void *, int);
146 extern int devstrat(struct buf *);
147 extern int nodev(void);
148 extern void setuerror(int);
152 * Device Driver Entry Points
154 int gsc_config(dev_t, int, struct uio *);
155 static int gsc_open(dev_t);
156 static int gsc_close(dev_t);
157 static int gsc_ioctl(dev_t, int, void *, ulong);
158 static void gscdd_intr(struct buf *);
163 static int scudebug = 10;
164 static int nunits = 0;
165 static gsc_softc_t softinfo[MAX_UNITS] = { 0 };
166 lock_t config_lock = { LOCK_AVAIL };
170 * Local Function Prototypes
173 static int gsopen(gsc_softc_t *);
174 static void gsclose(gsc_softc_t *, dev_t);
175 static int gsccmd(dev_t, scmd_t *, ulong);
176 static int make_rqs(gsc_softc_t *, char, char *, int, int);
179 * Configuration Routines
183 gsc_config(dev_t devno, int cmd, struct uio * uiop)
185 struct gsc_ddsinfo ddsinfo;
189 static struct devsw gsc_dsw = {
190 gsc_open, /* entry point for open routine */
191 gsc_close, /* entry point for close routine */
192 nodev, /* entry point for read routine */
193 nodev, /* entry point for write routine */
194 gsc_ioctl, /* entry point for ioctl routine */
195 nodev, /* entry point for strategy routine */
196 0, /* pointer to tty device structure */
197 nodev, /* entry point for select routine */
198 gsc_config, /* entry point for config routine */
199 nodev, /* entry point for print routine */
200 nodev, /* entry point for dump routine */
201 nodev, /* entry point for mpx routine */
202 nodev, /* entry point for revoke routine */
203 NULL, /* pointer to device specific data */
204 NULL, /* select pointer */
208 if (lockl(&config_lock, LOCK_SHORT) != LOCK_SUCC) {
212 if (unit < 0 || unit >= MAX_UNITS) {
213 Trace2(0, "%d: bad unit %d", __LINE__, unit);
215 unlockl(&config_lock);
221 Trace2(2, "CFG_INIT: unit %d nunit %d\n", unit, nunits);
223 * Initialize softinfo, first time around.
226 memset(softinfo, 0, sizeof (softinfo));
229 * Copy in DDS information
231 uiomove((caddr_t) &ddsinfo, sizeof ddsinfo, UIO_WRITE, uiop);
232 sp = &softinfo[unit];
234 Trace1(0, "CFG_INIT: unit %d already configd", unit);
238 lock_alloc(&sp->dd_lock, LOCK_ALLOC_PIN, DD_LOCK, -1);
239 lock_alloc(&sp->buf_lock, LOCK_ALLOC_PIN, DD_LOCK, -1);
240 simple_lock_init(&sp->dd_lock);
241 sp->dev = ddsinfo.busid;
242 sp->tgt = ddsinfo.target;
243 sp->lun = ddsinfo.lun;
244 sp->cbuf.index = sp->rbuf.index = unit;
246 * If this is the first time through:
247 * Add entry to the device switch table to call this driver
251 result = devswadd(devno, &gsc_dsw);
253 Trace1(0, "CFG_INIT: devswadd result: %d", result);
256 result = pincode((int (*) ()) gscdd_intr);
258 Trace1(0, "CFG_INIT: pincode result: %d", result);
266 Trace2(0, "CFG_INIT: gsopen returns %d for unit %d", result, unit);
277 Trace1(2, "CFG_TERM unit %d", unit);
279 sp = &softinfo[unit];
280 if (sp->iscfg == 0) {
281 Trace1(0, "CFG_TERM: unit %d not already configd", unit);
284 } else if (sp->isopen) {
285 Trace1(0, "CFG_TERM: unit %d open", unit);
289 sp->iscfg = 0; /* block further actions */
297 unlockl(&config_lock);
302 * Validate that devno is indeed for a SCSI adapter, and set up stuff for it.
305 gsopen(gsc_softc_t * sp)
311 Trace2(2, "gsopen: %d.%d", major(sp->dev), minor(sp->dev));
313 r = fp_opendev(sp->dev, DREAD|DWRITE|DKERNEL, NULL, 0, &fp);
315 Trace3(0, "%d: fp_opendev unit %d=%d", __LINE__, sp->cbuf.index, r);
318 r = fp_ioctl(fp, IOCINFO, (caddr_t) &di, NULL);
320 Trace3(0, "%d: fp_ioctl unit %d=%d", __LINE__, sp->cbuf.index, r);
324 if (di.devtype != DD_BUS || di.devsubtype != DS_SCSI) {
325 Trace2(0, "%d: not SCSI bus on unit %d", __LINE__, sp->cbuf.index);
331 if (fp_ioctl(sp->fp, SCIOSTART, (caddr_t) IDLUN(sp->tgt, sp->lun), NULL)) {
341 gsclose(gsc_softc_t *sp, dev_t devno)
344 if (sp->fp != NULL && sp->unstart) {
345 (void) fp_ioctl(sp->fp, SCIOSTOP, (caddr_t) IDLUN(sp->tgt, sp->lun), NULL);
349 (void) fp_close(sp->fp);
352 for (i = 0; i < MAX_UNITS; i++) {
353 if (softinfo[i].iscfg) {
354 Trace1(0, "gsclose: unit %d still confd", i);
358 if (i == MAX_UNITS) {
359 Trace0(0, "gsclose: All unconfigured now");
360 (void) devswdel(devno);
361 unpincode((int (*) ()) gscdd_intr);
370 gsc_open(dev_t devno)
373 int unit = minor(devno);
375 Trace1(2, "gsc_open: open unit %d", unit);
376 if (unit < 0 || unit >= MAX_UNITS) {
379 sp = &softinfo[unit];
380 if (sp->iscfg == 0 || sp->fp == NULL) {
381 Trace2(0, "%d: bad unit (%d)", __LINE__, unit);
384 simple_lock(&sp->dd_lock);
386 simple_unlock(&sp->dd_lock);
390 simple_unlock(&sp->dd_lock);
398 int unit = minor(dev);
400 Trace1(2, "gsc_close: close unit %d", unit);
401 if (unit < 0 || unit >= MAX_UNITS) {
404 sp = &softinfo[unit];
405 if (sp->iscfg == 0) {
408 simple_lock(&sp->dd_lock);
410 simple_unlock(&sp->dd_lock);
415 gsc_ioctl(dev_t dev, int cmd, void *arg, ulong dflag)
419 return (gsccmd(dev, arg, dflag));
424 cmd = copyin(arg, (caddr_t) &i, sizeof (int));
430 return (copyout((caddr_t) &cmd, arg, sizeof (int)));
438 /****************************************************************************/
441 gsccmd(dev_t dev, scmd_t *argcmd, ulong dflag)
448 int r, r2, ival, upin, unit, rqvalid, once;
451 Trace2(1, "%d: cmd for unit %d", __LINE__, minor(dev));
452 if (unit < 0 || unit >= MAX_UNITS) {
456 sp = &softinfo[unit];
457 if (sp->iscfg == 0 || sp->fp == NULL) {
458 Trace2(0, "gsccmd: bad unit %d (cfg=%d)", unit, sp->iscfg);
463 simple_lock(&sp->dd_lock);
465 if (dflag & DKERNEL) {
468 r = copyin((caddr_t) argcmd, (caddr_t) l, sizeof (scmd_t));
470 Trace2(0, "%d: copyin=%d", __LINE__, r);
475 Trace6(1, "%d: cdblen%d datalen%d snslen%d rw=%d tv=%d", __LINE__,
476 l->cdblen, l->datalen, l->senselen, l->rw, l->timeval);
478 rqvalid = upin = r = r2 = 0;
480 Ubp = &usc->bufstruct;
481 memset(usc, 0, sizeof (struct sc_buf));
484 * Check some parameters...
487 if (l->cdblen > sizeof (struct sc_cmd)) {
493 * Setup sc_buf structure
495 Ubp->b_iodone = gscdd_intr;
496 Ubp->b_dev = sp->dev;
497 Ubp->b_flags = B_BUSY | B_MPSAFE;
498 Ubp->b_resid = Ubp->b_bcount = l->datalen;
499 Ubp->b_xmemd.aspace_id = XMEM_INVAL;
500 Ubp->b_event = EVENT_NULL;
503 Ubp->b_un.b_addr = l->data_buf;
505 Ubp->b_flags |= B_READ;
507 if (dflag & DKERNEL) {
508 r = pinu(l->data_buf, l->datalen, UIO_SYSSPACE);
510 r = pinu(l->data_buf, l->datalen, UIO_USERSPACE);
513 Trace2(0, "%d: pinu buf %d", __LINE__, r);
517 if (dflag & DKERNEL) {
518 r = xmattach(l->data_buf, l->datalen, &Ubp->b_xmemd, SYS_ADSPACE);
520 r = xmattach(l->data_buf, l->datalen, &Ubp->b_xmemd, USER_ADSPACE);
522 if (r != XMEM_SUCC) {
523 Trace2(0, "%d: xmattach %d", __LINE__, r);
528 r = xmemdma(&Ubp->b_xmemd, l->data_buf, XMEM_UNHIDE);
529 if (r == XMEM_FAIL) {
530 Trace2(0, "%d: xmemdma %d", __LINE__, r);
536 usc->scsi_command.scsi_id = sp->tgt;
537 usc->scsi_command.scsi_length = l->cdblen;
538 if (dflag & DKERNEL) {
539 bcopy(l->cdb, (caddr_t)&usc->scsi_command.scsi_cmd, l->cdblen);
541 r = copyin(l->cdb, (caddr_t) & usc->scsi_command.scsi_cmd, l->cdblen);
546 /* Setting lun in SCSI CDB as well as sc_buf structure */
548 usc->scsi_command.scsi_cmd.lun &= 0x1F;
549 usc->scsi_command.scsi_cmd.lun |= (sp->lun << 5) & 0xE0;
550 albits = usc->scsi_command.scsi_cmd.lun;
551 usc->timeout_value = l->timeval;
552 if (sp->needresume) {
553 usc->flags |= SC_RESUME;
558 char *c = (char *) &usc->scsi_command.scsi_cmd;
560 (void) sprintf(cdbuf,
561 "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x "
562 "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x",
563 c[0], c[1], c[2], c[3], c[4], c[5],
564 c[6], c[7], c[8], c[9], c[10], c[11]);
565 Trace2(0, "%d: cdb=%s", __LINE__, cdbuf);
570 Ubp->b_flags &= ~B_DONE;
573 ival = disable_lock(INTCLASS1, &sp->buf_lock);
574 while ((Ubp->b_flags & B_DONE) == 0) {
575 e_sleep_thread(&Ubp->b_event, &sp->buf_lock, LOCK_HANDLER);
577 unlock_enable(ival, &sp->buf_lock);
580 * If ENXIO, We never actually got started.
582 if (r == ENXIO && once == 0) {
584 usc->flags |= SC_RESUME|SC_DELAY_CMD;
588 Trace2(1, "%d: devstrat=%d", __LINE__, r);
592 Trace4(1, "%d: b_flags %x b_error %d b_resid %d", __LINE__,
593 Ubp->b_flags, Ubp->b_error, Ubp->b_resid);
594 Trace5(1, "%d: sv %x st %x gc %x as %x", __LINE__,
595 usc->status_validity, usc->scsi_status,
596 usc->general_card_status, usc->adap_q_status);
598 if (Ubp->b_flags & B_ERROR) {
603 if (usc->status_validity & SC_SCSI_ERROR) {
604 sbyte = (usc->scsi_status & SCSI_STATUS_MASK);
606 if (sbyte == SC_CHECK_CONDITION && l->senselen) {
610 r = make_rqs(sp, albits, l->sense_buf, l->senselen,
611 (dflag & DKERNEL) != 0);
613 Trace2(0, "%d: make_rqs=%d", __LINE__, r);
617 Sbp = &usl->bufstruct;
620 ival = disable_lock(INTCLASS1, &sp->buf_lock);
621 while ((Sbp->b_flags & B_DONE) == 0) {
622 e_sleep_thread(&Sbp->b_event, &sp->buf_lock, LOCK_HANDLER);
624 unlock_enable(ival, &sp->buf_lock);
626 Trace2(0, "%d:ds=%d for rqs", __LINE__, r);
629 xmdetach(&Sbp->b_xmemd);
630 if (dflag & DKERNEL) {
631 (void) unpinu(l->sense_buf, l->senselen, UIO_SYSSPACE);
633 (void) unpinu(l->sense_buf, l->senselen, UIO_USERSPACE);
635 Trace4(1, "%d SENSE: b_flags %x b_error %d b_resid %d",
636 __LINE__, Sbp->b_flags, Sbp->b_error,
638 Trace5(1, "%d: sv %x st %x gc %x as %x", __LINE__,
639 usl->status_validity, usl->scsi_status,
640 usl->general_card_status, usl->adap_q_status);
641 if (usl->scsi_status || usl->general_card_status) {
649 if (usc->status_validity & SC_ADAPTER_ERROR) {
651 Trace2(0, "%d: adapter error 0x%x", __LINE__,
652 usc->general_card_status);
653 Ubp->b_flags |= B_ERROR;
654 switch (usc->general_card_status) {
655 case SC_NO_DEVICE_RESPONSE:
656 case SC_HOST_IO_BUS_ERR:
657 case SC_SCSI_BUS_FAULT:
659 case SC_ADAPTER_HDW_FAILURE:
660 case SC_ADAPTER_SFW_FAILURE:
661 case SC_FUSE_OR_TERMINAL_PWR:
662 case SC_SCSI_BUS_RESET:
670 * Log errors through errsave function
672 if (usc->status_validity & (SC_SCSI_ERROR|SC_ADAPTER_ERROR)) {
673 struct sc_error_log_df log;
675 memset(&log, 0, sizeof (log));
677 * All errors are 'temporary unknown driver error'
679 log.error_id = ERRID_SCSI_ERR6;
680 (void) sprintf(log.resource_name, "gsc%d", unit);
681 memcpy(&log.scsi_command, &usc->scsi_command, sizeof (struct scsi));
682 log.status_validity = usc->status_validity;
683 log.scsi_status = usc->scsi_status;
684 log.general_card_status = usc->general_card_status;
687 if (l->senselen > 128)
691 (void) copyin(l->sense_buf, log.req_sense_data, amt);
693 errsave(&log, sizeof (struct sc_error_log_df));
696 if (dflag & DKERNEL) {
699 r2 = copyout(&sbyte, l->statusp, 1);
709 xmdetach(&Ubp->b_xmemd);
713 if (dflag & DKERNEL) {
714 (void) unpinu(l->data_buf, l->datalen, UIO_SYSSPACE);
716 (void) unpinu(l->data_buf, l->datalen, UIO_USERSPACE);
721 Trace2(1, "%d: returning %d", __LINE__, r);
728 make_rqs(gsc_softc_t * sp, char albits, char *uaddr, int ulen, int isk)
738 Sbp = &usl->bufstruct;
739 memset(usl, 0, sizeof (struct sc_buf));
741 Sbp->b_un.b_addr = uaddr;
742 Sbp->b_resid = Sbp->b_bcount = ulen;
743 Sbp->b_iodone = gscdd_intr;
744 Sbp->b_dev = sp->dev;
745 Sbp->b_flags = B_BUSY | B_READ | B_MPSAFE;
746 Sbp->b_event = EVENT_NULL;
747 Sbp->b_xmemd.aspace_id = XMEM_INVAL;
750 err = pinu(uaddr, ulen, UIO_SYSSPACE);
752 err = pinu(uaddr, ulen, UIO_USERSPACE);
757 err = xmattach(uaddr, ulen, &Sbp->b_xmemd, SYS_ADSPACE);
759 err = xmattach(uaddr, ulen, &Sbp->b_xmemd, USER_ADSPACE);
760 if (err != XMEM_SUCC) {
765 err = xmemdma(&Sbp->b_xmemd, Sbp->b_un.b_addr, XMEM_UNHIDE);
766 if (err == XMEM_FAIL) {
768 (void) xmdetach(&Sbp->b_xmemd);
773 usl->lun = sp->lun; /* Setting lun in sc_buf structure */
774 usl->scsi_command.scsi_id = sp->tgt;
775 usl->scsi_command.scsi_length = 6;
776 usl->scsi_command.scsi_cmd.scsi_op_code = 0x3;
777 usl->scsi_command.scsi_cmd.lun = albits & 0xE0; /*ONLY copy the lun bits*/
778 usl->scsi_command.scsi_cmd.scsi_bytes[2] = ulen;
779 usl->timeout_value = 2;
780 usl->flags = SC_RESUME;
786 xmdetach(&Sbp->b_xmemd);
791 (void) unpinu(uaddr, ulen, UIO_SYSSPACE);
793 (void) unpinu(uaddr, ulen, UIO_USERSPACE);
801 gscdd_intr(struct buf * bp)
804 gsc_softc_t *sp = &softinfo[((gsc_buf_t *)bp)->index];
806 lv = disable_lock(INTIODONE, &sp->buf_lock);
807 bp->b_flags |= B_DONE;
808 unlock_enable(lv, &sp->buf_lock);
809 e_wakeup(&bp->b_event);
815 * c-brace-imaginary-offset: 0
817 * c-argdecl-indent: 4
819 * c-continued-statement-offset: 4
820 * c-continued-brace-offset: 0