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