Annotation of src/sys/arch/i386/eisa/aha1742.c, Revision 1.12
1.1 cgd 1: /*
2: * Written by Julian Elischer (julian@tfs.com)
3: * for TRW Financial Systems for use under the MACH(2.5) operating system.
4: *
5: * TRW Financial Systems, in accordance with their agreement with Carnegie
6: * Mellon University, makes this software available to CMU to distribute
7: * or use in any manner that they see fit as long as this message is kept with
8: * the software. For this reason TFS also grants any other persons or
9: * organisations permission to use or modify this software.
10: *
11: * TFS supplies this software to be publicly redistributed
12: * on the understanding that TFS is not responsible for the correct
13: * functioning of this software in any circumstances.
1.11 cgd 14: *
1.12 ! deraadt 15: * $Id: aha1742.c,v 1.11 1993/05/22 08:01:00 cgd Exp $
1.1 cgd 16: */
17:
1.5 deraadt 18: #include "ahb.h"
1.1 cgd 19:
1.5 deraadt 20: #include "sys/types.h"
21: #include "sys/param.h"
22: #include "sys/systm.h"
23: #include "sys/errno.h"
24: #include "sys/ioctl.h"
25: #include "sys/buf.h"
26: #include "sys/proc.h"
27: #include "sys/user.h"
28:
29: #include "i386/include/pio.h"
30: #include "i386/isa/isa_device.h"
31: #include "sys/dkbad.h"
32: #include "sys/disklabel.h"
33: #include "scsi/scsi_all.h"
34: #include "scsi/scsiconf.h"
35:
1.6 glass 36: #ifdef DDB
1.5 deraadt 37: int Debugger();
1.6 glass 38: #else DDB
1.5 deraadt 39: #define Debugger() panic("should call debugger here (adaptec.c)")
1.6 glass 40: #endif DDB
1.1 cgd 41:
42: typedef unsigned long int physaddr;
43:
44: #define PHYSTOKV(x) (x | 0xFE000000)
45: #define KVTOPHYS(x) vtophys(x)
46:
47: extern int delaycount; /* from clock setup code */
48: #define NUM_CONCURRENT 16 /* number of concurrent ops per board */
1.5 deraadt 49: #define AHB_NSEG 33 /* number of dma segments supported */
1.1 cgd 50: #define FUDGE(X) (X>>1) /* our loops are slower than spinwait() */
1.5 deraadt 51:
52:
53: /*
54: * AHA1740 standard EISA Host ID regs (Offset from slot base)
55: */
56: #define HID0 0xC80 /* 0,1: msb of ID2, 3-7: ID1 */
57: #define HID1 0xC81 /* 0-4: ID3, 4-7: LSB ID2 */
58: #define HID2 0xC82 /* product, 0=174[20] 1 = 1744 */
59: #define HID3 0xC83 /* firmware revision */
1.1 cgd 60:
61: #define CHAR1(B1,B2) (((B1>>2) & 0x1F) | '@')
62: #define CHAR2(B1,B2) (((B1<<3) & 0x18) | ((B2>>5) & 0x7)|'@')
63: #define CHAR3(B1,B2) ((B2 & 0x1F) | '@')
64:
65: /* AHA1740 EISA board control registers (Offset from slot base) */
66: #define EBCTRL 0xC84
67: #define CDEN 0x01
1.5 deraadt 68: /*
69: * AHA1740 EISA board mode registers (Offset from slot base)
70: */
1.1 cgd 71: #define PORTADDR 0xCC0
72: #define PORTADDR_ENHANCED 0x80
73: #define BIOSADDR 0xCC1
74: #define INTDEF 0xCC2
75: #define SCSIDEF 0xCC3
76: #define BUSDEF 0xCC4
77: #define RESV0 0xCC5
78: #define RESV1 0xCC6
79: #define RESV2 0xCC7
1.5 deraadt 80:
81: /* bit definitions for INTDEF */
1.1 cgd 82: #define INT9 0x00
83: #define INT10 0x01
84: #define INT11 0x02
85: #define INT12 0x03
86: #define INT14 0x05
87: #define INT15 0x06
88: #define INTHIGH 0x08 /* int high=ACTIVE (else edge) */
89: #define INTEN 0x10
1.5 deraadt 90:
91: /* bit definitions for SCSIDEF */
1.1 cgd 92: #define HSCSIID 0x0F /* our SCSI ID */
93: #define RSTPWR 0x10 /* reset scsi bus on power up or reset */
1.5 deraadt 94:
95: /* bit definitions for BUSDEF */
1.1 cgd 96: #define B0uS 0x00 /* give up bus immediatly */
97: #define B4uS 0x01 /* delay 4uSec. */
98: #define B8uS 0x02
1.5 deraadt 99:
100: /*
101: * AHA1740 ENHANCED mode mailbox control regs (Offset from slot base)
102: */
1.1 cgd 103: #define MBOXOUT0 0xCD0
104: #define MBOXOUT1 0xCD1
105: #define MBOXOUT2 0xCD2
106: #define MBOXOUT3 0xCD3
107:
108: #define ATTN 0xCD4
109: #define G2CNTRL 0xCD5
110: #define G2INTST 0xCD6
111: #define G2STAT 0xCD7
112:
113: #define MBOXIN0 0xCD8
114: #define MBOXIN1 0xCD9
115: #define MBOXIN2 0xCDA
116: #define MBOXIN3 0xCDB
117:
118: #define G2STAT2 0xCDC
119:
1.5 deraadt 120: /*
121: * Bit definitions for the 5 control/status registers
122: */
1.1 cgd 123: #define ATTN_TARGET 0x0F
124: #define ATTN_OPCODE 0xF0
125: #define OP_IMMED 0x10
126: #define AHB_TARG_RESET 0x80
127: #define OP_START_ECB 0x40
128: #define OP_ABORT_ECB 0x50
129:
130: #define G2CNTRL_SET_HOST_READY 0x20
131: #define G2CNTRL_CLEAR_EISA_INT 0x40
132: #define G2CNTRL_HARD_RESET 0x80
133:
134: #define G2INTST_TARGET 0x0F
135: #define G2INTST_INT_STAT 0xF0
136: #define AHB_ECB_OK 0x10
137: #define AHB_ECB_RECOVERED 0x50
138: #define AHB_HW_ERR 0x70
139: #define AHB_IMMED_OK 0xA0
140: #define AHB_ECB_ERR 0xC0
141: #define AHB_ASN 0xD0 /* for target mode */
142: #define AHB_IMMED_ERR 0xE0
143:
144: #define G2STAT_BUSY 0x01
145: #define G2STAT_INT_PEND 0x02
146: #define G2STAT_MBOX_EMPTY 0x04
147:
148: #define G2STAT2_HOST_READY 0x01
1.5 deraadt 149:
1.1 cgd 150:
1.5 deraadt 151: struct ahb_dma_seg {
1.1 cgd 152: physaddr addr;
153: long len;
154: };
155:
1.5 deraadt 156: struct ahb_ecb_status {
1.1 cgd 157: u_short status;
158: # define ST_DON 0x0001
159: # define ST_DU 0x0002
160: # define ST_QF 0x0008
161: # define ST_SC 0x0010
162: # define ST_DO 0x0020
163: # define ST_CH 0x0040
164: # define ST_INT 0x0080
165: # define ST_ASA 0x0100
166: # define ST_SNS 0x0200
167: # define ST_INI 0x0800
168: # define ST_ME 0x1000
169: # define ST_ECA 0x4000
170: u_char ha_status;
171: # define HS_OK 0x00
172: # define HS_CMD_ABORTED_HOST 0x04
173: # define HS_CMD_ABORTED_ADAPTER 0x05
174: # define HS_TIMED_OUT 0x11
175: # define HS_HARDWARE_ERR 0x20
176: # define HS_SCSI_RESET_ADAPTER 0x22
177: # define HS_SCSI_RESET_INCOMING 0x23
178: u_char targ_status;
179: # define TS_OK 0x00
180: # define TS_CHECK_CONDITION 0x02
181: # define TS_BUSY 0x08
182: u_long resid_count;
183: u_long resid_addr;
184: u_short addit_status;
185: u_char sense_len;
186: u_char unused[9];
187: u_char cdb[6];
188: };
189:
190:
1.5 deraadt 191: struct ecb {
1.1 cgd 192: u_char opcode;
193: # define ECB_SCSI_OP 0x01
194: u_char :4;
195: u_char options:3;
196: u_char :1;
197: short opt1;
198: # define ECB_CNE 0x0001
199: # define ECB_DI 0x0080
200: # define ECB_SES 0x0400
201: # define ECB_S_G 0x1000
202: # define ECB_DSB 0x4000
203: # define ECB_ARS 0x8000
204: short opt2;
205: # define ECB_LUN 0x0007
206: # define ECB_TAG 0x0008
207: # define ECB_TT 0x0030
208: # define ECB_ND 0x0040
209: # define ECB_DAT 0x0100
210: # define ECB_DIR 0x0200
211: # define ECB_ST 0x0400
212: # define ECB_CHK 0x0800
213: # define ECB_REC 0x4000
214: # define ECB_NRB 0x8000
215: u_short unused1;
216: physaddr data;
217: u_long datalen;
218: physaddr status;
219: physaddr chain;
220: short unused2;
221: short unused3;
222: physaddr sense;
223: u_char senselen;
224: u_char cdblen;
225: short cksum;
226: u_char cdb[12];
227: /*-----------------end of hardware supported fields----------------*/
1.5 deraadt 228: struct ecb *next; /* in free list */
229: struct scsi_xfer *xs; /* the scsi_xfer for this cmd */
1.1 cgd 230: long int delta; /* difference from previous*/
1.5 deraadt 231: struct ecb *later,*sooner;
1.1 cgd 232: int flags;
233: #define ECB_FREE 0
234: #define ECB_ACTIVE 1
235: #define ECB_ABORTED 2
236: #define ECB_IMMED 4
237: #define ECB_IMMED_FAIL 8
1.5 deraadt 238: struct ahb_dma_seg ahb_dma[AHB_NSEG];
239: struct ahb_ecb_status ecb_status;
240: struct scsi_sense_data ecb_sense;
1.1 cgd 241: };
242:
1.5 deraadt 243: struct ecb *ahb_soonest = (struct ecb *)0;
244: struct ecb *ahb_latest = (struct ecb *)0;
245: long int ahb_furtherest = 0; /* longest time in the timeout queue */
1.1 cgd 246:
1.5 deraadt 247: struct ahb_data {
1.1 cgd 248: int flags;
249: #define AHB_INIT 0x01;
250: int baseport;
1.5 deraadt 251: struct ecb ecbs[NUM_CONCURRENT];
252: struct ecb *free_ecb;
1.1 cgd 253: int our_id; /* our scsi id */
254: int vect;
1.5 deraadt 255: struct ecb *immed_ecb; /* an outstanding immediete command */
1.1 cgd 256: } ahb_data[NAHB];
257:
1.5 deraadt 258: struct ecb *cheat;
1.1 cgd 259:
260: #define MAX_SLOTS 8
261: static ahb_slot = 0; /* slot last board was found in */
262: static ahb_unit = 0;
263: int ahb_debug = 0;
264: #define AHB_SHOWECBS 0x01
265: #define AHB_SHOWINTS 0x02
266: #define AHB_SHOWCMDS 0x04
267: #define AHB_SHOWMISC 0x08
268: #define FAIL 1
269: #define SUCCESS 0
270: #define PAGESIZ 4096
271:
1.5 deraadt 272:
273: int ahbprobe(struct isa_device *);
274: int ahbprobe1(struct isa_device *);
275: int ahb_attach(struct isa_device *);
276: long int ahb_adapter_info(int);
277: int ahbintr(int);
278: void ahb_done(int, struct ecb *, int);
279: void ahb_free_ecb(int, struct ecb *, int);
280: struct ecb * ahb_get_ecb(int, int);
281: int ahb_init(int);
282: void ahbminphys(struct buf *);
283: int ahb_scsi_cmd(struct scsi_xfer *);
284: void ahb_add_timeout(struct ecb *, int);
285: void ahb_remove_timeout(struct ecb *);
286: void ahb_timeout(int);
287: void ahb_show_scsi_cmd(struct scsi_xfer *);
288: void ahb_print_ecb(struct ecb *);
289: void ahb_print_active_ecb(void);
290:
291:
292: struct isa_driver ahbdriver = {
293: ahbprobe,
294: ahb_attach,
295: "ahb"
296: };
297:
298: struct scsi_switch ahb_switch = {
299: "ahb",
1.1 cgd 300: ahb_scsi_cmd,
301: ahbminphys,
302: 0,
303: 0,
304: ahb_adapter_info,
1.5 deraadt 305: 0, 0, 0
1.1 cgd 306: };
307:
1.5 deraadt 308:
309: /*
310: * Function to send a command out through a mailbox
311: */
312: void
313: ahb_send_mbox(int unit, int opcode, int target, struct ecb *ecb)
1.1 cgd 314: {
1.5 deraadt 315: int port = ahb_data[unit].baseport;
316: int spincount = FUDGE(delaycount) * 1; /* 1ms should be enough */
317: int stport = port + G2STAT, s;
318:
319: s = splbio();
320: while( ((inb(stport) &
321: (G2STAT_BUSY | G2STAT_MBOX_EMPTY)) != G2STAT_MBOX_EMPTY)
322: && spincount--)
323: ;
324: if(spincount == -1) {
1.1 cgd 325: printf("ahb%d: board not responding\n",unit);
326: Debugger();
327: }
328:
1.5 deraadt 329: outl(port+MBOXOUT0, KVTOPHYS(ecb)); /* don't know this will work */
330: outb(port+ATTN, opcode|target);
1.1 cgd 331: splx(s);
332: }
333:
1.5 deraadt 334: /*
335: * Function to poll for command completion when in poll mode
336: * wait is in msec
337: */
338: int
339: ahb_poll(int unit, int wait)
1.1 cgd 340: {
1.5 deraadt 341: int port = ahb_data[unit].baseport;
342: int spincount = FUDGE(delaycount) * wait; /* in msec */
343: int stport = port + G2STAT;
344: int start = spincount;
1.1 cgd 345:
346: retry:
1.5 deraadt 347: while( spincount-- && (!(inb(stport) & G2STAT_INT_PEND)))
348: ;
349: if(spincount == -1) {
1.1 cgd 350: printf("ahb%d: board not responding\n",unit);
351: return(EIO);
352: }
1.5 deraadt 353: if( (int)cheat != PHYSTOKV(inl(port+MBOXIN0)) ) {
354: printf("discarding %x ", inl(port+MBOXIN0));
355: outb(port + G2CNTRL, G2CNTRL_CLEAR_EISA_INT);
356: spinwait(50);
357: goto retry;
358: }
1.1 cgd 359: ahbintr(unit);
360: return(0);
361: }
1.5 deraadt 362: /*
363: * Function to send an immediate type command to the adapter
364: */
365: void
366: ahb_send_immed(int unit, int target, u_long cmd)
1.1 cgd 367: {
1.5 deraadt 368: int port = ahb_data[unit].baseport;
369: int spincount = FUDGE(delaycount) * 1; /* 1ms should be enough */
370: int s = splbio();
371: int stport = port + G2STAT;
372:
373: while( ((inb(stport) &
374: (G2STAT_BUSY | G2STAT_MBOX_EMPTY)) != (G2STAT_MBOX_EMPTY)) &&
375: spincount--)
376: ;
377: if(spincount == -1) {
1.1 cgd 378: printf("ahb%d: board not responding\n",unit);
379: Debugger();
380: }
381:
1.5 deraadt 382: outl(port + MBOXOUT0, cmd); /* don't know this will work */
1.1 cgd 383: outb(port + G2CNTRL, G2CNTRL_SET_HOST_READY);
384: outb(port + ATTN, OP_IMMED | target);
385: splx(s);
386: }
387:
1.5 deraadt 388: /*
389: * Check the slots looking for a board we recognise
390: * If we find one, note it's address (slot) and call
391: * the actual probe routine to check it out.
392: */
393: int
394: ahbprobe(struct isa_device *dev)
1.1 cgd 395: {
1.5 deraadt 396: int port;
1.1 cgd 397: u_char byte1,byte2,byte3;
1.5 deraadt 398:
1.1 cgd 399: ahb_slot++;
1.5 deraadt 400: while (ahb_slot<8) {
1.1 cgd 401: port = 0x1000 * ahb_slot;
402: byte1 = inb(port + HID0);
403: byte2 = inb(port + HID1);
404: byte3 = inb(port + HID2);
1.5 deraadt 405: if(byte1 == 0xff) {
1.1 cgd 406: ahb_slot++;
407: continue;
408: }
409: if ((CHAR1(byte1,byte2) == 'A')
1.5 deraadt 410: && (CHAR2(byte1,byte2) == 'D')
411: && (CHAR3(byte1,byte2) == 'P')
412: && ((byte3 == 0 ) || (byte3 == 1))) {
413: dev->id_iobase = port;
414: return ahbprobe1(dev);
1.1 cgd 415: }
416: ahb_slot++;
417: }
1.5 deraadt 418: return 0;
1.1 cgd 419: }
1.5 deraadt 420:
421: /*
422: * Check if the device can be found at the port given *
423: * and if so, set it up ready for further work *
424: * as an argument, takes the isa_device structure from *
425: * autoconf.c *
426: */
427: int
428: ahbprobe1(struct isa_device *dev)
1.1 cgd 429: {
1.5 deraadt 430: int unit = ahb_unit;
431:
432: dev->id_unit = unit;
433: ahb_data[unit].baseport = dev->id_iobase;
434: if(unit >= NAHB) {
1.1 cgd 435: printf("ahb: unit number (%d) too high\n",unit);
1.5 deraadt 436: return 0;
1.1 cgd 437: }
1.5 deraadt 438:
439: /*
440: * Try initialise a unit at this location
441: * sets up dma and bus speed, loads ahb_data[unit].vect*
442: */
1.1 cgd 443: if (ahb_init(unit) != 0)
1.5 deraadt 444: return 0;
1.1 cgd 445:
1.5 deraadt 446: /* If it's there, put in it's interrupt vectors */
1.1 cgd 447: dev->id_irq = (1 << ahb_data[unit].vect);
1.5 deraadt 448: dev->id_drq = -1; /* using EISA dma */
1.1 cgd 449: ahb_unit++;
1.5 deraadt 450: return 0x1000;
1.1 cgd 451: }
452:
1.5 deraadt 453: /*
454: * Attach all the sub-devices we can find
455: */
456: int
457: ahb_attach(struct isa_device *dev)
1.1 cgd 458: {
1.7 deraadt 459: static int firsttime;
460: int masunit = dev->id_masunit;
1.10 deraadt 461: int r;
1.1 cgd 462:
1.10 deraadt 463: r = scsi_attach(masunit, ahb_data[masunit].our_id, &ahb_switch,
464: &dev->id_physid, &dev->id_unit, dev->id_flags);
1.1 cgd 465:
1.5 deraadt 466: /* only one for all boards */
1.12 ! deraadt 467: if(firsttime==0) {
1.9 deraadt 468: firsttime = 1;
1.1 cgd 469: ahb_timeout(0);
1.7 deraadt 470: }
1.10 deraadt 471: return r;
1.1 cgd 472: }
473:
1.5 deraadt 474: /*
475: * Return some information to the caller about *
476: * the adapter and it's capabilities *
477: * 2 outstanding requests at a time per device
478: */
479: long int
480: ahb_adapter_info(int unit)
1.1 cgd 481: {
1.5 deraadt 482: return 2;
1.1 cgd 483: }
484:
1.5 deraadt 485: /*
486: * Catch an interrupt from the adaptor
487: */
488: int
489: ahbintr(int unit)
1.1 cgd 490: {
1.5 deraadt 491: struct ecb *ecb;
1.1 cgd 492: unsigned char stat;
493: register i;
494: u_char ahbstat;
495: int target;
496: long int mboxval;
497:
1.5 deraadt 498: int port = ahb_data[unit].baseport;
1.1 cgd 499:
500: if(scsi_debug & PRINTROUTINES)
501: printf("ahbintr ");
502:
1.5 deraadt 503: while(inb(port + G2STAT) & G2STAT_INT_PEND) {
504: /*
505: * First get all the information and then
506: * acknowlege the interrupt
507: */
1.1 cgd 508: ahbstat = inb(port + G2INTST);
509: target = ahbstat & G2INTST_TARGET;
510: stat = ahbstat & G2INTST_INT_STAT;
511: mboxval = inl(port + MBOXIN0);/* don't know this will work */
512: outb(port + G2CNTRL, G2CNTRL_CLEAR_EISA_INT);
513: if(scsi_debug & TRACEINTERRUPTS)
514: printf("status = 0x%x ",stat);
1.5 deraadt 515:
516: /*
517: * Process the completed operation
518: */
519: if(stat == AHB_ECB_OK)
1.1 cgd 520: ecb = (struct ecb *)PHYSTOKV(mboxval);
1.5 deraadt 521: else {
522: switch(stat) {
523: case AHB_IMMED_OK:
1.1 cgd 524: ecb = ahb_data[unit].immed_ecb;
525: ahb_data[unit].immed_ecb = 0;
526: break;
1.5 deraadt 527: case AHB_IMMED_ERR:
1.1 cgd 528: ecb = ahb_data[unit].immed_ecb;
529: ecb->flags |= ECB_IMMED_FAIL;
530: ahb_data[unit].immed_ecb = 0;
531: break;
1.5 deraadt 532: case AHB_ASN: /* for target mode */
1.1 cgd 533: ecb = 0;
534: break;
1.5 deraadt 535: case AHB_HW_ERR:
1.1 cgd 536: ecb = 0;
537: break;
1.5 deraadt 538: case AHB_ECB_RECOVERED:
1.1 cgd 539: ecb = (struct ecb *)PHYSTOKV(mboxval);
540: break;
1.5 deraadt 541: case AHB_ECB_ERR:
1.1 cgd 542: ecb = (struct ecb *)PHYSTOKV(mboxval);
543: break;
544: default:
545: printf(" Unknown return from ahb%d(%x)\n",unit,ahbstat);
546: ecb=0;
547: }
548: }
1.5 deraadt 549: if(ecb) {
1.1 cgd 550: if(ahb_debug & AHB_SHOWCMDS )
551: ahb_show_scsi_cmd(ecb->xs);
552: if((ahb_debug & AHB_SHOWECBS) && ecb)
553: printf("<int ecb(%x)>",ecb);
554: ahb_remove_timeout(ecb);
1.5 deraadt 555: ahb_done(unit, ecb, (stat==AHB_ECB_OK)? SUCCESS: FAIL);
1.1 cgd 556: }
557: }
1.5 deraadt 558: return 1;
1.1 cgd 559: }
560:
1.5 deraadt 561: /*
562: * We have a ecb which has been processed by the
563: * adaptor, now we look to see how the operation
564: * went.
565: */
566: void
567: ahb_done(int unit, struct ecb *ecb, int state)
1.1 cgd 568: {
1.5 deraadt 569: struct ahb_ecb_status *stat = &ecb->ecb_status;
570: struct scsi_sense_data *s1,*s2;
571: struct scsi_xfer *xs = ecb->xs;
1.1 cgd 572:
573: if(scsi_debug & (PRINTROUTINES | TRACEINTERRUPTS))
574: printf("ahb_done ");
1.5 deraadt 575:
576: /*
577: * Otherwise, put the results of the operation
578: * into the xfer and call whoever started it
579: */
580: if(ecb->flags & ECB_IMMED) {
1.1 cgd 581: if(ecb->flags & ECB_IMMED_FAIL)
582: xs->error = XS_DRIVER_STUFFUP;
583: goto done;
584: }
1.5 deraadt 585: if ( (state == SUCCESS) || (xs->flags & SCSI_ERR_OK)) {
586: /* All went correctly OR errors expected */
1.1 cgd 587: xs->resid = 0;
588: xs->error = 0;
1.5 deraadt 589: } else {
1.1 cgd 590: s1 = &(ecb->ecb_sense);
591: s2 = &(xs->sense);
592:
1.5 deraadt 593: if(stat->ha_status) {
594: switch(stat->ha_status) {
595: case HS_SCSI_RESET_ADAPTER:
1.1 cgd 596: break;
1.5 deraadt 597: case HS_SCSI_RESET_INCOMING:
1.1 cgd 598: break;
1.5 deraadt 599: case HS_CMD_ABORTED_HOST: /* No response */
600: case HS_CMD_ABORTED_ADAPTER: /* No response */
1.1 cgd 601: break;
1.5 deraadt 602: case HS_TIMED_OUT: /* No response */
1.1 cgd 603: if (ahb_debug & AHB_SHOWMISC)
604: printf("timeout reported back\n");
605: xs->error = XS_TIMEOUT;
606: break;
1.5 deraadt 607: default:
608: /* Other scsi protocol messes */
1.1 cgd 609: xs->error = XS_DRIVER_STUFFUP;
610: if (ahb_debug & AHB_SHOWMISC)
611: printf("unexpected ha_status: %x\n",
612: stat->ha_status);
613: }
614:
1.5 deraadt 615: } else {
616: switch(stat->targ_status) {
1.1 cgd 617: case TS_CHECK_CONDITION:
1.5 deraadt 618: *s2 = *s1;
1.1 cgd 619: xs->error = XS_SENSE;
620: break;
621: case TS_BUSY:
622: xs->error = XS_BUSY;
623: break;
624: default:
625: if (ahb_debug & AHB_SHOWMISC)
626: printf("unexpected targ_status: %x\n",
627: stat->targ_status);
628: xs->error = XS_DRIVER_STUFFUP;
629: }
630: }
631: }
1.5 deraadt 632:
633: done:
634: xs->flags |= ITSDONE;
635: ahb_free_ecb(unit, ecb, xs->flags);
1.1 cgd 636: if(xs->when_done)
637: (*(xs->when_done))(xs->done_arg,xs->done_arg2);
638: }
639:
1.5 deraadt 640: /*
641: * A ecb (and hence a mbx-out is put onto the
642: * free list.
643: */
644: void
645: ahb_free_ecb(int unit, struct ecb *ecb, int flags)
1.1 cgd 646: {
647: unsigned int opri;
1.5 deraadt 648:
1.1 cgd 649: if(scsi_debug & PRINTROUTINES)
650: printf("ecb%d(0x%x)> ",unit,flags);
651: if (!(flags & SCSI_NOMASK))
652: opri = splbio();
653:
654: ecb->next = ahb_data[unit].free_ecb;
655: ahb_data[unit].free_ecb = ecb;
656: ecb->flags = ECB_FREE;
1.5 deraadt 657:
658: /*
659: * If there were none, wake abybody waiting for
660: * one to come free, starting with queued entries*
661: */
662: if (!ecb->next)
1.1 cgd 663: wakeup(&ahb_data[unit].free_ecb);
1.5 deraadt 664:
1.1 cgd 665: if (!(flags & SCSI_NOMASK))
666: splx(opri);
667: }
668:
1.5 deraadt 669: /*
670: * Get a free ecb (and hence mbox-out entry)
671: */
1.1 cgd 672: struct ecb *
1.5 deraadt 673: ahb_get_ecb(int unit, int flags)
1.1 cgd 674: {
675: unsigned opri;
676: struct ecb *rc;
677:
678: if(scsi_debug & PRINTROUTINES)
1.5 deraadt 679: printf("<ecb%d(0x%x) ", unit, flags);
1.1 cgd 680: if (!(flags & SCSI_NOMASK))
681: opri = splbio();
1.5 deraadt 682:
683: /*
684: * If we can and have to, sleep waiting for one
685: * to come free
686: */
1.1 cgd 687: while ((!(rc = ahb_data[unit].free_ecb)) && (!(flags & SCSI_NOSLEEP)))
688: sleep(&ahb_data[unit].free_ecb, PRIBIO);
1.5 deraadt 689:
690: if (rc) {
1.1 cgd 691: ahb_data[unit].free_ecb = rc->next;
692: rc->flags = ECB_ACTIVE;
693: }
1.5 deraadt 694:
1.1 cgd 695: if (!(flags & SCSI_NOMASK))
696: splx(opri);
1.5 deraadt 697: return rc;
1.1 cgd 698: }
699:
1.5 deraadt 700: /*
701: * Start the board, ready for normal operation
702: */
703: int
704: ahb_init(int unit)
705: {
706: int port = ahb_data[unit].baseport;
707: int intdef;
708: int spincount = FUDGE(delaycount) * 1000; /* 1 sec enough? */
709: int i;
710: int stport = port + G2STAT;
1.1 cgd 711:
712: #define NO_NO 1
713: #ifdef NO_NO
1.5 deraadt 714: /*
715: * reset board, If it doesn't respond, assume
716: * that it's not there.. good for the probe
717: */
1.1 cgd 718: outb(port + EBCTRL,CDEN); /* enable full card */
719: outb(port + PORTADDR,PORTADDR_ENHANCED);
720:
721: outb(port + G2CNTRL,G2CNTRL_HARD_RESET);
722: spinwait(1);
723: outb(port + G2CNTRL,0);
724: spinwait(10);
1.5 deraadt 725: while( (inb(stport) & G2STAT_BUSY ) && spincount--)
726: ;
727: if(spincount == -1) {
1.1 cgd 728: if (ahb_debug & AHB_SHOWMISC)
729: printf("ahb_init: No answer from bt742a board\n");
730: return(ENXIO);
731: }
1.5 deraadt 732:
1.1 cgd 733: i = inb(port + MBOXIN0) & 0xff;
1.5 deraadt 734: if(i) {
1.1 cgd 735: printf("self test failed, val = 0x%x\n",i);
736: return(EIO);
737: }
738: #endif
1.5 deraadt 739:
740: while( inb(stport) & G2STAT_INT_PEND) {
1.1 cgd 741: printf(".");
742: outb(port + G2CNTRL, G2CNTRL_CLEAR_EISA_INT);
743: spinwait(10);
744: }
745: outb(port + EBCTRL,CDEN); /* enable full card */
746: outb(port + PORTADDR,PORTADDR_ENHANCED);
747:
1.5 deraadt 748: /*
749: * Assume we have a board at this stage
750: * setup dma channel from jumpers and save int
751: * level
752: */
753:
1.1 cgd 754: intdef = inb(port + INTDEF);
1.5 deraadt 755: switch(intdef & 0x07) {
756: case INT9:
1.1 cgd 757: ahb_data[unit].vect = 9;
758: break;
1.5 deraadt 759: case INT10:
1.1 cgd 760: ahb_data[unit].vect = 10;
761: break;
1.5 deraadt 762: case INT11:
1.1 cgd 763: ahb_data[unit].vect = 11;
764: break;
1.5 deraadt 765: case INT12:
1.1 cgd 766: ahb_data[unit].vect = 12;
767: break;
1.5 deraadt 768: case INT14:
1.1 cgd 769: ahb_data[unit].vect = 14;
770: break;
1.5 deraadt 771: case INT15:
1.1 cgd 772: ahb_data[unit].vect = 15;
773: break;
774: default:
1.5 deraadt 775: ahb_data[unit].vect = -1;
776: printf("ahb%d: illegal irq setting\n", unit);
1.1 cgd 777: return(EIO);
778: }
1.5 deraadt 779:
780: outb(port + INTDEF, intdef|INTEN); /* make sure we can interrupt */
1.1 cgd 781: ahb_data[unit].our_id = (inb(port + SCSIDEF) & HSCSIID);
782:
1.5 deraadt 783: /*
784: * link up all our ECBs into a free list
785: */
786: for (i=0; i < NUM_CONCURRENT; i++) {
1.1 cgd 787: ahb_data[unit].ecbs[i].next = ahb_data[unit].free_ecb;
788: ahb_data[unit].free_ecb = &ahb_data[unit].ecbs[i];
789: ahb_data[unit].free_ecb->flags = ECB_FREE;
790: }
791:
1.5 deraadt 792: /*
793: * Note that we are going and return (to probe)
794: */
1.1 cgd 795: ahb_data[unit].flags |= AHB_INIT;
1.5 deraadt 796: return 0;
1.1 cgd 797: }
798:
1.5 deraadt 799: void
800: ahbminphys(struct buf *bp)
1.1 cgd 801: {
802: if(bp->b_bcount > ((AHB_NSEG-1) * PAGESIZ))
803: bp->b_bcount = ((AHB_NSEG-1) * PAGESIZ);
804: }
1.5 deraadt 805:
806: /*
807: * start a scsi operation given the command and
808: * the data address. Also needs the unit, target
809: * and lu
810: */
811: int
812: ahb_scsi_cmd(struct scsi_xfer *xs)
1.1 cgd 813: {
1.5 deraadt 814: struct scsi_sense_data *s1,*s2;
1.1 cgd 815: struct ecb *ecb;
816: struct ahb_dma_seg *sg;
817: int seg; /* scatter gather seg being worked on */
818: int i = 0;
819: int rc = 0;
820: int thiskv;
821: physaddr thisphys,nextphys;
822: int unit =xs->adapter;
823: int bytes_this_seg,bytes_this_page,datalen,flags;
1.5 deraadt 824: struct iovec *iovp;
1.1 cgd 825: int s;
826: if(scsi_debug & PRINTROUTINES)
827: printf("ahb_scsi_cmd ");
1.5 deraadt 828: /*
829: * get a ecb (mbox-out) to use. If the transfer
830: * is from a buf (possibly from interrupt time)
831: * then we can't allow it to sleep
832: */
1.1 cgd 833: flags = xs->flags;
834: if(xs->bp) flags |= (SCSI_NOSLEEP); /* just to be sure */
1.5 deraadt 835: if(flags & ITSDONE) {
1.1 cgd 836: printf("Already done?");
837: xs->flags &= ~ITSDONE;
838: }
1.5 deraadt 839: if( !(flags & INUSE) ) {
1.1 cgd 840: printf("Not in use?");
841: xs->flags |= INUSE;
842: }
1.5 deraadt 843: if (!(ecb = ahb_get_ecb(unit,flags))) {
1.1 cgd 844: xs->error = XS_DRIVER_STUFFUP;
845: return(TRY_AGAIN_LATER);
846: }
847:
848: cheat = ecb;
1.5 deraadt 849:
1.1 cgd 850: if(ahb_debug & AHB_SHOWECBS)
1.5 deraadt 851: printf("<start ecb(%x)>",ecb);
1.1 cgd 852: if(scsi_debug & SHOWCOMMANDS)
853: ahb_show_scsi_cmd(xs);
1.5 deraadt 854:
1.1 cgd 855: ecb->xs = xs;
1.5 deraadt 856: /*
857: * If it's a reset, we need to do an 'immediate'
858: * command, and store it's ccb for later
859: * if there is already an immediate waiting,
860: * then WE must wait
861: */
862: if(flags & SCSI_RESET) {
1.1 cgd 863: ecb->flags |= ECB_IMMED;
864: if(ahb_data[unit].immed_ecb)
865: return(TRY_AGAIN_LATER);
1.5 deraadt 866:
1.1 cgd 867: ahb_data[unit].immed_ecb = ecb;
1.5 deraadt 868: if (!(flags & SCSI_NOMASK)) {
1.1 cgd 869: s = splbio();
870: ahb_send_immed(unit,xs->targ,AHB_TARG_RESET);
871: ahb_add_timeout(ecb,xs->timeout);
872: splx(s);
873: return(SUCCESSFULLY_QUEUED);
1.5 deraadt 874: } else {
1.1 cgd 875: ahb_send_immed(unit,xs->targ,AHB_TARG_RESET);
1.5 deraadt 876: /*
877: * If we can't use interrupts, poll on completion*
878: */
1.1 cgd 879: if(scsi_debug & TRACEINTERRUPTS)
880: printf("wait ");
1.5 deraadt 881: if( ahb_poll(unit,xs->timeout)) {
1.1 cgd 882: ahb_free_ecb(unit,ecb,flags);
883: xs->error = XS_TIMEOUT;
884: return(HAD_ERROR);
885: }
886: return(COMPLETE);
887: }
1.5 deraadt 888: }
889: /*
890: * Put all the arguments for the xfer in the ecb
891: */
1.1 cgd 892: ecb->opcode = ECB_SCSI_OP;
893: ecb->opt1 = ECB_SES|ECB_DSB|ECB_ARS;
894: if(xs->datalen)
895: ecb->opt1 |= ECB_S_G;
896: ecb->opt2 = xs->lu | ECB_NRB;
897: ecb->cdblen = xs->cmdlen;
898: ecb->sense = KVTOPHYS(&(ecb->ecb_sense));
899: ecb->senselen = sizeof(ecb->ecb_sense);
900: ecb->status = KVTOPHYS(&(ecb->ecb_status));
901:
1.5 deraadt 902: if(xs->datalen) {
903: /* should use S/G only if not zero length */
1.1 cgd 904: ecb->data = KVTOPHYS(ecb->ahb_dma);
905: sg = ecb->ahb_dma ;
906: seg = 0;
1.5 deraadt 907: if(flags & SCSI_DATA_UIO) {
1.1 cgd 908: iovp = ((struct uio *)xs->data)->uio_iov;
909: datalen = ((struct uio *)xs->data)->uio_iovcnt;
910: xs->datalen = 0;
1.5 deraadt 911: while ((datalen) && (seg < AHB_NSEG)) {
1.1 cgd 912: sg->addr = (physaddr)iovp->iov_base;
1.5 deraadt 913: xs->datalen += sg->len = iovp->iov_len;
1.1 cgd 914: if(scsi_debug & SHOWSCATGATH)
1.5 deraadt 915: printf("(0x%x@0x%x)", iovp->iov_len,
916: iovp->iov_base);
1.1 cgd 917: sg++;
918: iovp++;
919: seg++;
920: datalen--;
921: }
1.5 deraadt 922: } else {
923: /* Set up the scatter gather block */
924:
1.1 cgd 925: if(scsi_debug & SHOWSCATGATH)
1.5 deraadt 926: printf("%d @0x%x:- ", xs->datalen, xs->data);
1.1 cgd 927: datalen = xs->datalen;
928: thiskv = (int)xs->data;
929: thisphys = KVTOPHYS(thiskv);
1.5 deraadt 930:
931: while ((datalen) && (seg < AHB_NSEG)) {
1.1 cgd 932: bytes_this_seg = 0;
1.5 deraadt 933:
1.1 cgd 934: /* put in the base address */
935: sg->addr = thisphys;
1.5 deraadt 936:
1.1 cgd 937: if(scsi_debug & SHOWSCATGATH)
938: printf("0x%x",thisphys);
1.5 deraadt 939:
1.1 cgd 940: /* do it at least once */
1.5 deraadt 941: nextphys = thisphys;
942: while ((datalen) && (thisphys == nextphys)) {
943: /*
944: * This page is contiguous (physically) with *
945: * the the last, just extend the length *
946: */
1.1 cgd 947: nextphys= (thisphys & (~(PAGESIZ - 1)))
948: + PAGESIZ;
1.5 deraadt 949: bytes_this_page = min(nextphys - thisphys,
950: datalen);
1.1 cgd 951: bytes_this_seg += bytes_this_page;
952: datalen -= bytes_this_page;
1.5 deraadt 953:
1.1 cgd 954: /* get more ready for the next page */
955: thiskv = (thiskv & (~(PAGESIZ - 1)))
956: + PAGESIZ;
957: if(datalen)
958: thisphys = KVTOPHYS(thiskv);
959: }
1.5 deraadt 960: /*
961: * next page isn't contiguous, finish the seg *
962: */
1.1 cgd 963: if(scsi_debug & SHOWSCATGATH)
964: printf("(0x%x)",bytes_this_seg);
1.5 deraadt 965: sg->len = bytes_this_seg;
1.1 cgd 966: sg++;
967: seg++;
968: }
1.5 deraadt 969: }
1.1 cgd 970: ecb->datalen = seg * sizeof(struct ahb_dma_seg);
971: if(scsi_debug & SHOWSCATGATH)
972: printf("\n");
1.5 deraadt 973: if (datalen) {
974: /* there's still data, must have run out of segs! */
1.1 cgd 975: printf("ahb_scsi_cmd%d: more than %d DMA segs\n",
1.5 deraadt 976: unit, AHB_NSEG);
1.1 cgd 977: xs->error = XS_DRIVER_STUFFUP;
978: ahb_free_ecb(unit,ecb,flags);
979: return(HAD_ERROR);
980: }
981:
1.5 deraadt 982: } else {
983: /* No data xfer, use non S/G values */
1.1 cgd 984: ecb->data = (physaddr)0;
985: ecb->datalen = 0;
986: }
1.5 deraadt 987:
1.1 cgd 988: ecb->chain = (physaddr)0;
1.5 deraadt 989: /*
990: * Put the scsi command in the ecb and start it
991: */
1.1 cgd 992: bcopy(xs->cmd, ecb->cdb, xs->cmdlen);
1.5 deraadt 993:
994: /* Usually return SUCCESSFULLY QUEUED */
995: if( !(flags & SCSI_NOMASK) ) {
1.1 cgd 996: s = splbio();
997: ahb_send_mbox(unit,OP_START_ECB,xs->targ,ecb);
998: ahb_add_timeout(ecb,xs->timeout);
999: splx(s);
1000: if(scsi_debug & TRACEINTERRUPTS)
1001: printf("cmd_sent ");
1002: return(SUCCESSFULLY_QUEUED);
1003: }
1.5 deraadt 1004:
1005: /* If we can't use interrupts, poll on completion */
1.1 cgd 1006: ahb_send_mbox(unit,OP_START_ECB,xs->targ,ecb);
1007: if(scsi_debug & TRACEINTERRUPTS)
1008: printf("cmd_wait ");
1.5 deraadt 1009:
1010: do {
1011: if(ahb_poll(unit,xs->timeout)) {
1.1 cgd 1012: if (!(xs->flags & SCSI_SILENT)) printf("cmd fail\n");
1013: ahb_send_mbox(unit,OP_ABORT_ECB,xs->targ,ecb);
1.5 deraadt 1014: if(ahb_poll(unit, 2000)) {
1.1 cgd 1015: printf("abort failed in wait\n");
1016: ahb_free_ecb(unit,ecb,flags);
1017: }
1018: xs->error = XS_DRIVER_STUFFUP;
1019: splx(s);
1020: return(HAD_ERROR);
1021: }
1.5 deraadt 1022: } while (!(xs->flags & ITSDONE));
1023:
1.1 cgd 1024: splx(s);
1.5 deraadt 1025: scsi_debug = 0;
1026: ahb_debug = 0;
1.1 cgd 1027: if(xs->error)
1.5 deraadt 1028: return HAD_ERROR;
1029: return COMPLETE;
1.1 cgd 1030: }
1031:
1032: /*
1033: * +----------+ +----------+ +----------+
1034: * ahb_soonest--->| later |--->| later|--->| later|--->0
1035: * | [Delta] | | [Delta] | | [Delta] |
1036: * 0<---|sooner |<---|sooner |<---|sooner |<---ahb_latest
1037: * +----------+ +----------+ +----------+
1038: *
1039: * ahb_furtherest = sum(Delta[1..n])
1040: */
1.5 deraadt 1041: void
1042: ahb_add_timeout(struct ecb *ecb, int time)
1.1 cgd 1043: {
1044: int timeprev;
1045: struct ecb *prev;
1046: int s = splbio();
1047:
1.5 deraadt 1048: prev = ahb_latest;
1049: if(prev)
1.1 cgd 1050: timeprev = ahb_furtherest;
1051: else
1052: timeprev = 0;
1.5 deraadt 1053:
1054: while(prev && (timeprev > time)) {
1.1 cgd 1055: timeprev -= prev->delta;
1056: prev = prev->sooner;
1057: }
1.5 deraadt 1058: if(prev) {
1.1 cgd 1059: ecb->delta = time - timeprev;
1.5 deraadt 1060: ecb->later = prev->later;
1061: if(ecb->later) {
1.1 cgd 1062: ecb->later->sooner = ecb;
1063: ecb->later->delta -= ecb->delta;
1.5 deraadt 1064: } else {
1.1 cgd 1065: ahb_furtherest = time;
1066: ahb_latest = ecb;
1067: }
1068: ecb->sooner = prev;
1069: prev->later = ecb;
1070: }
1071: else
1072: {
1.5 deraadt 1073: ecb->later = ahb_soonest;
1074: if(ahb_soonest) {
1.1 cgd 1075: ecb->later->sooner = ecb;
1076: ecb->later->delta -= time;
1.5 deraadt 1077: } else {
1.1 cgd 1078: ahb_furtherest = time;
1079: ahb_latest = ecb;
1080: }
1081: ecb->delta = time;
1082: ecb->sooner = (struct ecb *)0;
1083: ahb_soonest = ecb;
1084: }
1085: splx(s);
1086: }
1087:
1.5 deraadt 1088: void
1089: ahb_remove_timeout(struct ecb *ecb)
1.1 cgd 1090: {
1091: int s = splbio();
1092:
1093: if(ecb->sooner)
1094: ecb->sooner->later = ecb->later;
1095: else
1096: ahb_soonest = ecb->later;
1.5 deraadt 1097:
1098: if(ecb->later) {
1.1 cgd 1099: ecb->later->sooner = ecb->sooner;
1100: ecb->later->delta += ecb->delta;
1.5 deraadt 1101: } else {
1.1 cgd 1102: ahb_latest = ecb->sooner;
1103: ahb_furtherest -= ecb->delta;
1104: }
1105: ecb->sooner = ecb->later = (struct ecb *)0;
1106: splx(s);
1107: }
1108:
1109:
1110: extern int hz;
1111: #define ONETICK 500 /* milliseconds */
1112: #define SLEEPTIME ((hz * 1000) / ONETICK)
1.5 deraadt 1113:
1114: void
1115: ahb_timeout(int arg)
1.1 cgd 1116: {
1.5 deraadt 1117: struct ecb *ecb;
1118: int unit;
1119: int s = splbio();
1.1 cgd 1120:
1.5 deraadt 1121: while( ecb = ahb_soonest ) {
1122: if(ecb->delta <= ONETICK) {
1123: /* It has timed out, we need to do some work */
1.1 cgd 1124: unit = ecb->xs->adapter;
1.5 deraadt 1125: printf("ahb%d targ %d: device timed out\n", unit,
1126: ecb->xs->targ);
1.1 cgd 1127: if(ahb_debug & AHB_SHOWECBS)
1128: ahb_print_active_ecb();
1129:
1.5 deraadt 1130: /* Unlink it from the queue */
1.1 cgd 1131: ahb_remove_timeout(ecb);
1132:
1.5 deraadt 1133: /*
1134: * If it's immediate, don't try abort it *
1135: */
1136: if(ecb->flags & ECB_IMMED) {
1.1 cgd 1137: ecb->xs->retries = 0; /* I MEAN IT ! */
1138: ecb->flags |= ECB_IMMED_FAIL;
1139: ahb_done(unit,ecb,FAIL);
1140: continue;
1141: }
1.5 deraadt 1142:
1143: /*
1144: * If it has been through before, then
1145: * a previous abort has failed, don't
1146: * try abort again
1147: */
1148: if(ecb->flags == ECB_ABORTED) {
1.1 cgd 1149: printf("AGAIN");
1150: ecb->xs->retries = 0; /* I MEAN IT ! */
1151: ecb->ecb_status.ha_status = HS_CMD_ABORTED_HOST;
1152: ahb_done(unit,ecb,FAIL);
1.5 deraadt 1153: } else {
1.1 cgd 1154: printf("\n");
1155: ahb_send_mbox(unit,OP_ABORT_ECB,ecb->xs->targ,ecb);
1156: /* 2 secs for the abort */
1157: ahb_add_timeout(ecb,2000 + ONETICK);
1158: ecb->flags = ECB_ABORTED;
1159: }
1.5 deraadt 1160: } else {
1.1 cgd 1161: ecb->delta -= ONETICK;
1162: ahb_furtherest -= ONETICK;
1163: break;
1164: }
1165: }
1166: splx(s);
1167: timeout(ahb_timeout,arg,SLEEPTIME);
1168: }
1169:
1.5 deraadt 1170: void
1.1 cgd 1171: ahb_show_scsi_cmd(struct scsi_xfer *xs)
1172: {
1.5 deraadt 1173: u_char *b = (u_char *)xs->cmd;
1.1 cgd 1174: int i = 0;
1.5 deraadt 1175:
1176: if( !(xs->flags & SCSI_RESET) ) {
1177: printf("ahb%d targ %d lun %d:", xs->adapter,
1178: xs->targ, xs->lu);
1179: while(i < xs->cmdlen ) {
1180: if(i)
1181: printf(",");
1182: printf("%x", b[i++]);
1183: }
1184: printf("\n");
1185: } else {
1186: printf("ahb%d targ %d lun%d: RESET\n", xs->adapter,
1187: xs->targ, xs->lu);
1.1 cgd 1188: }
1189: }
1.5 deraadt 1190:
1191: void
1192: ahb_print_ecb(struct ecb *ecb)
1.1 cgd 1193: {
1.5 deraadt 1194: printf("ecb:%x op:%x cmdlen:%d senlen:%d\n", ecb, ecb->opcode,
1195: ecb->cdblen, ecb->senselen);
1196: printf(" datlen:%d hstat:%x tstat:%x delta:%d flags:%x\n",
1197: ecb->datalen, ecb->ecb_status.ha_status,
1198: ecb->ecb_status.targ_status, ecb->delta, ecb->flags);
1.1 cgd 1199: ahb_show_scsi_cmd(ecb->xs);
1200: }
1201:
1.5 deraadt 1202: void
1203: ahb_print_active_ecb(void)
1.1 cgd 1204: {
1.5 deraadt 1205: struct ecb *ecb;
1.1 cgd 1206: ecb = ahb_soonest;
1207:
1.5 deraadt 1208: while(ecb) {
1.1 cgd 1209: ahb_print_ecb(ecb);
1210: ecb = ecb->later;
1211: }
1.5 deraadt 1212: printf("Furtherest = %d\n", ahb_furtherest);
1.1 cgd 1213: }
CVSweb <webmaster@jp.NetBSD.org>