Revised drivers from Julian.
[unix-history] / sys / scsi / scsiconf.c
CommitLineData
15637ed4
RG
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 *
15637ed4 15 * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
98639498
RG
16 *
17 * $Id$
15637ed4
RG
18 */
19
15637ed4
RG
20#include <sys/types.h>
21#include "st.h"
22#include "sd.h"
23#include "ch.h"
24#include "cd.h"
25
26#ifdef MACH
27#include <i386/machparam.h>
28#endif MACH
29#include <scsi/scsi_all.h>
30#include <scsi/scsiconf.h>
31
32#if !defined(OSF) && !defined(__386BSD__)
33#include "bll.h"
34#include "cals.h"
35#include "kil.h"
36#else
37#define NBLL 0
38#define NCALS 0
39#define NKIL 0
40#endif /* !defined(OSF) && !defined(__386BSD__) */
41
42#if NSD > 0
43extern sdattach();
44#endif NSD
45#if NST > 0
46extern stattach();
47#endif NST
48#if NCH > 0
49extern chattach();
50#endif NCH
51#if NCD > 0
52extern cdattach();
53#endif NCD
54#if NBLL > 0
55extern bllattach();
56#endif NBLL
57#if NCALS > 0
58extern calsattach();
59#endif NCALS
60#if NKIL > 0
61extern kil_attach();
62#endif NKIL
63
64/***************************************************************\
65* The structure of pre-configured devices that might be turned *
66* off and therefore may not show up *
67\***************************************************************/
68struct predefined
69{
70 u_char scsibus;
71 u_char dev;
72 u_char lu;
73 int (*attach_rtn)();
74 char *devname;
75 char flags;
76}
77pd[] =
78{
79#ifdef EXAMPLE_PREDEFINE
80#if NSD > 0
81 {0,0,0,sdattach,"sd",0},/* define a disk at scsibus=0 dev=0 lu=0 */
82#endif NSD
83#endif EXAMPLE_PREDEFINE
84 {0,9,9} /*illegal dummy end entry */
85};
86
87
88/***************************************************************\
89* The structure of known drivers for autoconfiguration *
90\***************************************************************/
91static struct scsidevs
92{
93 int type;
94 int removable;
95 char *manufacturer;
96 char *model;
97 char *version;
98 int (*attach_rtn)();
99 char *devname;
100 char flags; /* 1 show my comparisons during boot(debug) */
101}
102#define SC_SHOWME 0x01
103#define SC_ONE_LU 0x00
104#define SC_MORE_LUS 0x02
105knowndevs[] = {
106#if NSD > 0
107 { T_DIRECT,T_FIXED,"standard","any"
108 ,"any",sdattach,"sd",SC_ONE_LU },
109 { T_DIRECT,T_FIXED,"MAXTOR ","XT-4170S "
110 ,"B5A ",sdattach,"mx1",SC_ONE_LU },
111#endif NSD
112#if NST > 0
113 { T_SEQUENTIAL,T_REMOV,"standard","any"
114 ,"any",stattach,"st",SC_ONE_LU },
115#endif NST
116#if NCALS > 0
117 { T_PROCESSOR,T_FIXED,"standard","any"
118 ,"any",calsattach,"cals",SC_MORE_LUS },
119#endif NCALS
120#if NCH > 0
121 { T_CHANGER,T_REMOV,"standard","any"
122 ,"any",chattach,"ch",SC_ONE_LU },
123#endif NCH
124#if NCD > 0
125 { T_READONLY,T_REMOV,"SONY ","CD-ROM CDU-8012 "
126 ,"3.1a",cdattach,"cd",SC_ONE_LU },
98639498
RG
127 { T_READONLY,T_REMOV,"PIONEER ","CD-ROM DRM-600 "
128 ,"any",cdattach,"cd",SC_MORE_LUS },
15637ed4
RG
129#endif NCD
130#if NBLL > 0
131 { T_PROCESSOR,T_FIXED,"AEG ","READER "
132 ,"V1.0",bllattach,"bll",SC_MORE_LUS },
133#endif NBLL
134#if NKIL > 0
135 { T_SCANNER,T_FIXED,"KODAK ","IL Scanner 900 "
136 ,"any",kil_attach,"kil",SC_ONE_LU },
137#endif NKIL
138
139{0}
140};
141/***************************************************************\
142* Declarations *
143\***************************************************************/
144struct predefined *scsi_get_predef();
145struct scsidevs *scsi_probedev();
146struct scsidevs *selectdev();
147
148/* controls debug level within the scsi subsystem */
149/* see scsiconf.h for values */
150int scsi_debug = 0x0;
151int scsibus = 0x0; /* This is the Nth scsibus */
152
153/***************************************************************\
154* The routine called by the adapter boards to get all their *
155* devices configured in. *
156\***************************************************************/
157scsi_attachdevs( unit, scsi_addr, scsi_switch)
158int unit,scsi_addr;
159struct scsi_switch *scsi_switch;
160{
161 int targ,lun;
162 struct scsidevs *bestmatch = (struct scsidevs *)0;
163 struct predefined *predef;
164 int maybe_more;
165
166#ifdef SCSI_DELAY
167#if SCSI_DELAY > 2
168 printf("waiting for scsi devices to settle\n");
169#else SCSI_DELAY > 2
869c4419 170#define SCSI_DELAY 2
15637ed4
RG
171#endif SCSI_DELAY > 2
172#else
173#define SCSI_DELAY 2
174#endif SCSI_DELAY
175 spinwait(1000 * SCSI_DELAY);
176 targ = 0;
177 while(targ < 8)
178 {
179 maybe_more = 0; /* by default only check 1 lun */
180 if (targ == scsi_addr)
181 {
182 targ++;
183 continue;
184 }
185 lun = 0;
186 while(lun < 8)
187 {
188 predef = scsi_get_predef(scsibus
189 ,targ
190 ,lun
98639498 191 ,scsi_switch
15637ed4
RG
192 ,&maybe_more);
193 bestmatch = scsi_probedev(unit
194 ,targ
195 ,lun
196 ,scsi_switch
197 ,&maybe_more);
198 if((bestmatch) && (predef)) /* both exist */
199 {
200 if(bestmatch->attach_rtn
201 != predef->attach_rtn)
202 {
203 printf("Clash in found/expected devices\n");
204 printf("will link in FOUND\n");
205 }
206 (*(bestmatch->attach_rtn))(unit,
207 targ,
208 lun,
209 scsi_switch);
210 }
211 if((bestmatch) && (!predef)) /* just FOUND */
212 {
213 (*(bestmatch->attach_rtn))(unit,
214 targ,
215 lun,
216 scsi_switch);
217 }
218 if((!bestmatch) && (predef)) /* just predef */
219 {
220 (*(predef->attach_rtn))(unit,
221 targ,
222 lun,
223 scsi_switch);
224 }
225 if(!(maybe_more)) /* nothing suggests we'll find more */
226 {
227 break; /* nothing here, skip to next targ */
228 }
229 /* otherwise something says we should look further*/
230 lun++;
231 }
232 targ++;
233 }
234#if NGENSCSI > 0
235 /***************************************************************\
236 * If available hook up the generic scsi driver, letting it *
237 * know which target is US. (i.e. illegal or at least special) *
238 \***************************************************************/
239 genscsi_attach(unit,scsi_addr,0,scsi_switch);
240#endif
241 scsibus++; /* next time we are on the NEXT scsi bus */
242}
243
244/***********************************************\
245* given a target and lu, check if there is a *
246* predefined device for that address *
247\***********************************************/
98639498 248struct predefined *scsi_get_predef(unit,target,lu,scsi_switch,maybe_more)
15637ed4 249int unit,target,lu,*maybe_more;
98639498 250struct scsi_switch *scsi_switch;
15637ed4
RG
251{
252 int upto,numents;
253
254 numents = (sizeof(pd)/sizeof(struct predefined)) - 1;
255
256 for(upto = 0;upto < numents;upto++)
257 {
258 if(pd[upto].scsibus != unit)
259 continue;
260 if(pd[upto].dev != target)
261 continue;
262 if(pd[upto].lu != lu)
263 continue;
264
98639498
RG
265 printf("%s%d targ %d lun %d: <%s> - PRECONFIGURED -\n"
266 ,scsi_switch->name
267 ,unit
15637ed4
RG
268 ,target
269 ,lu
270 ,pd[upto].devname);
271 *maybe_more = pd[upto].flags & SC_MORE_LUS;
272 return(&(pd[upto]));
273 }
274 return((struct predefined *)0);
275}
276
277/***********************************************\
278* given a target and lu, ask the device what *
279* it is, and find the correct driver table *
280* entry. *
281\***********************************************/
282struct scsidevs *scsi_probedev(unit,target,lu,scsi_switch, maybe_more)
283
284struct scsi_switch *scsi_switch;
285int unit,target,lu;
286int *maybe_more;
287{
288 struct scsidevs *bestmatch = (struct scsidevs *)0;
289 char *dtype=(char *)0,*desc;
290 char *qtype;
291 static struct scsi_inquiry_data inqbuf;
292 int len,qualifier,type,remov;
293 char manu[32];
294 char model[32];
295 char version[32];
296
297
298 bzero(&inqbuf,sizeof(inqbuf));
299 /***********************************************\
300 * Ask the device what it is *
301 \***********************************************/
302#ifdef DEBUG
303 if((target == 0) && (lu == 0))
304 scsi_debug = 0xfff;
305 else
306 scsi_debug = 0;
307#endif DEBUG
308 if(scsi_ready( unit,
309 target,
310 lu,
311 scsi_switch,
312 SCSI_NOSLEEP | SCSI_NOMASK) != COMPLETE)
313 {
314 return(struct scsidevs *)0;
315 }
316 if(scsi_inquire(unit,
317 target,
318 lu,
319 scsi_switch,
320 &inqbuf,
321 SCSI_NOSLEEP | SCSI_NOMASK) != COMPLETE)
322 {
323 return(struct scsidevs *)0;
324 }
325
326 /***********************************************\
327 * note what BASIC type of device it is *
328 \***********************************************/
329 if(scsi_debug & SHOWINQUIRY)
330 {
331 desc=(char *)&inqbuf;
332 printf("inq: %x %x %x %x %x %x %x %x %x %x %x %x %x\n",
333 desc[0], desc[1], desc[2], desc[3],
334 desc[4], desc[5], desc[6], desc[7],
335 desc[8], desc[9], desc[10], desc[11],
336 desc[12]);
337 }
338
869c4419
RG
339 type = inqbuf.device & SID_TYPE;
340 qualifier = inqbuf.device & SID_QUAL;
341 remov = inqbuf.dev_qual2 & SID_REMOVABLE;
15637ed4 342
15637ed4
RG
343
344 /* Any device qualifier that has
345 * the top bit set (qualifier&4 != 0) is vendor specific and
346 * won't match in this switch.
347 */
348
349 switch(qualifier)
350 {
869c4419 351 case SID_QUAL_LU_OK:
15637ed4
RG
352 qtype="";
353 break;
869c4419
RG
354
355 case SID_QUAL_LU_OFFLINE:
15637ed4
RG
356 qtype=", Unit not Connected!";
357 break;
869c4419
RG
358
359 case SID_QUAL_RSVD:
15637ed4 360 qtype=", Reserved Peripheral Qualifier!";
869c4419
RG
361 *maybe_more = 1;
362 return (struct scsidevs *)0;
15637ed4 363 break;
869c4419
RG
364
365 case SID_QUAL_BAD_LU:
366 /*
367 * Check for a non-existent unit. If the device is returning
368 * this much, then we must set the flag that has
369 * the searcher keep looking on other luns.
370 */
15637ed4 371 qtype=", The Target can't support this Unit!";
869c4419
RG
372 *maybe_more = 1;
373 return (struct scsidevs *)0;
15637ed4 374
869c4419 375 default:
15637ed4
RG
376 dtype="vendor specific";
377 qtype="";
378 *maybe_more = 1;
379 break;
380 }
381
382 if (dtype == 0)
869c4419 383 {
15637ed4
RG
384 switch(type)
385 {
869c4419
RG
386 case T_DIRECT:
387 dtype="direct";
388 break;
389 case T_SEQUENTIAL:
390 dtype="sequential";
391 break;
392 case T_PRINTER:
393 dtype="printer";
394 break;
395 case T_PROCESSOR:
396 dtype="processor";
397 break;
398 case T_READONLY:
399 dtype="readonly";
400 break;
401 case T_WORM:
402 dtype="worm";
403 break;
404 case T_SCANNER:
405 dtype="scanner";
406 break;
407 case T_OPTICAL:
408 dtype="optical";
409 break;
410 case T_CHANGER:
411 dtype="changer";
412 break;
413 case T_COMM:
414 dtype="communication";
415 break;
416 case T_NODEVICE:
417 *maybe_more = 1;
418 return (struct scsidevs *)0;
419 default:
420 dtype="unknown";
421 break;
15637ed4
RG
422 }
423
869c4419 424 }
15637ed4
RG
425 /***********************************************\
426 * Then if it's advanced enough, more detailed *
427 * information *
428 \***********************************************/
869c4419 429 if((inqbuf.version & SID_ANSII) > 0)
15637ed4
RG
430 {
431 if ((len = inqbuf.additional_length
432 + ( (char *)inqbuf.unused
433 - (char *)&inqbuf))
434 > (sizeof(struct scsi_inquiry_data) - 1))
435 len = sizeof(struct scsi_inquiry_data) - 1;
436 desc=inqbuf.vendor;
437 desc[len-(desc - (char *)&inqbuf)] = 0;
438 strncpy(manu,inqbuf.vendor,8);manu[8]=0;
439 strncpy(model,inqbuf.product,16);model[16]=0;
440 strncpy(version,inqbuf.revision,4);version[4]=0;
441 }
442 else
443 /***********************************************\
444 * If not advanced enough, use default values *
445 \***********************************************/
446 {
447 desc="early protocol device";
448 strncpy(manu,"unknown",8);
449 strncpy(model,"unknown",16);
450 strncpy(version,"????",4);
451 }
98639498
RG
452 printf("%s%d targ %d lun %d: type %d(%s) %s <%s%s%s> SCSI%d\n"
453 ,scsi_switch->name
454 ,unit
15637ed4
RG
455 ,target
456 ,lu
98639498
RG
457 ,type
458 ,dtype
15637ed4
RG
459 ,remov?"removable":"fixed"
460 ,manu
461 ,model
462 ,version
869c4419 463 ,inqbuf.version & SID_ANSII
15637ed4 464 );
98639498
RG
465 if(qtype[0])
466 {
467 printf("%s%d targ %d lun %d: qulaifier %d(%s)\n"
468 ,scsi_switch->name
469 ,unit
470 ,target
471 ,lu
472 ,qualifier
473 ,qtype
474 );
475 }
15637ed4
RG
476 /***********************************************\
477 * Try make as good a match as possible with *
478 * available sub drivers *
479 \***********************************************/
480 bestmatch = (selectdev(unit,target,lu,&scsi_switch,
869c4419 481 qualifier,type,remov?T_REMOV:T_FIXED,manu,model,version));
15637ed4
RG
482 if((bestmatch) && (bestmatch->flags & SC_MORE_LUS))
483 {
484 *maybe_more = 1;
485 }
486 return(bestmatch);
487}
488
489/***********************************************\
490* Try make as good a match as possible with *
491* available sub drivers *
492\***********************************************/
493struct scsidevs
494*selectdev(unit,target,lu,dvr_switch,qualifier,type,remov,manu,model,rev)
495int unit,target,lu;
496struct scsi_switch *dvr_switch;
497int qualifier,type,remov;
498char *manu,*model,*rev;
499{
500 int numents = (sizeof(knowndevs)/sizeof(struct scsidevs)) - 1;
501 int count = 0;
502 int bestmatches = 0;
503 struct scsidevs *bestmatch = (struct scsidevs *)0;
504 struct scsidevs *thisentry = knowndevs;
505
869c4419 506 type |= qualifier; /* why? */
15637ed4
RG
507
508 thisentry--;
509 while( count++ < numents)
510 {
511 thisentry++;
512 if(type != thisentry->type)
513 {
514 continue;
515 }
516 if(bestmatches < 1)
517 {
518 bestmatches = 1;
519 bestmatch = thisentry;
520 }
521 if(remov != thisentry->removable)
522 {
523 continue;
524 }
525 if(bestmatches < 2)
526 {
527 bestmatches = 2;
528 bestmatch = thisentry;
529 }
530 if(thisentry->flags & SC_SHOWME)
531 printf("\n%s-\n%s-",thisentry->manufacturer, manu);
532 if(strcmp(thisentry->manufacturer, manu))
533 {
534 continue;
535 }
536 if(bestmatches < 3)
537 {
538 bestmatches = 3;
539 bestmatch = thisentry;
540 }
541 if(thisentry->flags & SC_SHOWME)
542 printf("\n%s-\n%s-",thisentry->model, model);
543 if(strcmp(thisentry->model, model))
544 {
545 continue;
546 }
547 if(bestmatches < 4)
548 {
549 bestmatches = 4;
550 bestmatch = thisentry;
551 }
552 if(thisentry->flags & SC_SHOWME)
553 printf("\n%s-\n%s-",thisentry->version, rev);
554 if(strcmp(thisentry->version, rev))
555 {
556 continue;
557 }
558 if(bestmatches < 5)
559 {
560 bestmatches = 5;
561 bestmatch = thisentry;
562 break;
563 }
564 }
565
566 if (bestmatch == (struct scsidevs *)0)
567 printf(" No explicit device driver match for \"%s %s\".\n",
568 manu, model);
569
570 return(bestmatch);
571}
572
573static int recurse = 0;
574/***********************************************\
575* Do a scsi operation asking a device if it is *
576* ready. Use the scsi_cmd routine in the switch *
577* table. *
578\***********************************************/
579scsi_ready(unit,target,lu,scsi_switch, flags)
580struct scsi_switch *scsi_switch;
581{
582 struct scsi_test_unit_ready scsi_cmd;
583 struct scsi_xfer scsi_xfer;
584 volatile int rval;
585 int key;
586
587 bzero(&scsi_cmd, sizeof(scsi_cmd));
588 bzero(&scsi_xfer, sizeof(scsi_xfer));
589 scsi_cmd.op_code = TEST_UNIT_READY;
590
591 scsi_xfer.flags=flags | INUSE;
592 scsi_xfer.adapter=unit;
593 scsi_xfer.targ=target;
594 scsi_xfer.lu=lu;
595 scsi_xfer.cmd=(struct scsi_generic *)&scsi_cmd;
596 scsi_xfer.retries=8;
597 scsi_xfer.timeout=10000;
598 scsi_xfer.cmdlen=sizeof(scsi_cmd);
599 scsi_xfer.data=0;
600 scsi_xfer.datalen=0;
601 scsi_xfer.resid=0;
602 scsi_xfer.when_done=0;
603 scsi_xfer.done_arg=0;
604retry: scsi_xfer.error=0;
605 /*******************************************************\
606 * do not use interrupts *
607 \*******************************************************/
608 rval = (*(scsi_switch->scsi_cmd))(&scsi_xfer);
609 if (rval != COMPLETE)
610 {
611 if(scsi_debug)
612 {
613 printf("scsi error, rval = 0x%x\n",rval);
614 printf("code from driver: 0x%x\n",scsi_xfer.error);
615 }
616 switch(scsi_xfer.error)
617 {
618 case XS_SENSE:
619 /*******************************************************\
620 * Any sense value is illegal except UNIT ATTENTION *
621 * In which case we need to check again to get the *
622 * correct response. *
623 *( especially exabytes) *
624 \*******************************************************/
869c4419
RG
625 if(((scsi_xfer.sense.error_code & SSD_ERRCODE) == 0x70 )
626 ||((scsi_xfer.sense.error_code & SSD_ERRCODE) == 0x71 ))
15637ed4 627 {
869c4419 628 key = scsi_xfer.sense.ext.extended.flags & SSD_KEY ;
15637ed4
RG
629 switch(key)
630 {
631 case 2: /* not ready BUT PRESENT! */
632 return(COMPLETE);
633 case 6:
634 spinwait(1000);
635 if(scsi_xfer.retries--)
636 {
637 scsi_xfer.flags &= ~ITSDONE;
638 goto retry;
639 }
640 return(COMPLETE);
641 default:
642 if(scsi_debug)
643 printf("%d:%d,key=%x.",
644 target,lu,key);
645 }
646 }
647 return(HAD_ERROR);
648 case XS_BUSY:
649 spinwait(1000);
650 if(scsi_xfer.retries--)
651 {
652 scsi_xfer.flags &= ~ITSDONE;
653 goto retry;
654 }
655 return(COMPLETE); /* it's busy so it's there */
656 case XS_TIMEOUT:
657 default:
658 return(HAD_ERROR);
659 }
660 }
661 return(COMPLETE);
662}
663/***********************************************\
664* Do a scsi operation asking a device what it is*
665* Use the scsi_cmd routine in the switch table. *
666\***********************************************/
667scsi_inquire(unit,target,lu,scsi_switch,inqbuf, flags)
668struct scsi_switch *scsi_switch;
669u_char *inqbuf;
670{
671 struct scsi_inquiry scsi_cmd;
672 struct scsi_xfer scsi_xfer;
673 volatile int rval;
674
675 bzero(&scsi_cmd, sizeof(scsi_cmd));
676 bzero(&scsi_xfer, sizeof(scsi_xfer));
677 scsi_cmd.op_code = INQUIRY;
678 scsi_cmd.length = sizeof(struct scsi_inquiry_data);
679
680 scsi_xfer.flags=flags | SCSI_DATA_IN | INUSE;
681 scsi_xfer.adapter=unit;
682 scsi_xfer.targ=target;
683 scsi_xfer.lu=lu;
684 scsi_xfer.retries=8;
685 scsi_xfer.timeout=10000;
686 scsi_xfer.cmd=(struct scsi_generic *)&scsi_cmd;
687 scsi_xfer.cmdlen= sizeof(struct scsi_inquiry);
688 scsi_xfer.data=inqbuf;
689 scsi_xfer.datalen=sizeof(struct scsi_inquiry_data);
690 scsi_xfer.resid=sizeof(struct scsi_inquiry_data);
691 scsi_xfer.when_done=0;
692 scsi_xfer.done_arg=0;
693retry: scsi_xfer.error=0;
694 /*******************************************************\
695 * do not use interrupts *
696 \*******************************************************/
697 if ((*(scsi_switch->scsi_cmd))(&scsi_xfer) != COMPLETE)
698 {
699 if(scsi_debug) printf("inquiry had error(0x%x) ",scsi_xfer.error);
700 switch(scsi_xfer.error)
701 {
702 case XS_NOERROR:
703 break;
704 case XS_SENSE:
705 /*******************************************************\
706 * Any sense value is illegal except UNIT ATTENTION *
707 * In which case we need to check again to get the *
708 * correct response. *
709 *( especially exabytes) *
710 \*******************************************************/
869c4419
RG
711 if(((scsi_xfer.sense.error_code & SSD_ERRCODE) == 0x70 )
712 && ((scsi_xfer.sense.ext.extended.flags & SSD_KEY) == 6))
15637ed4
RG
713 { /* it's changed so it's there */
714 spinwait(1000);
715 {
716 if(scsi_xfer.retries--)
717 {
718 scsi_xfer.flags &= ~ITSDONE;
719 goto retry;
720 }
721 }
722 return( COMPLETE);
723 }
724 return(HAD_ERROR);
725 case XS_BUSY:
726 spinwait(1000);
727 if(scsi_xfer.retries--)
728 {
729 scsi_xfer.flags &= ~ITSDONE;
730 goto retry;
731 }
732 case XS_TIMEOUT:
733 default:
734 return(HAD_ERROR);
735 }
736 }
737 return(COMPLETE);
738}
739
740
741
742
743/***********************************************\
744* Utility routines often used in SCSI stuff *
745\***********************************************/
746
747/***********************************************\
748* convert a physical address to 3 bytes, *
749* MSB at the lowest address, *
750* LSB at the highest. *
751\***********************************************/
752
753lto3b(val, bytes)
754u_char *bytes;
755{
756 *bytes++ = (val&0xff0000)>>16;
757 *bytes++ = (val&0xff00)>>8;
758 *bytes = val&0xff;
759}
760
761/***********************************************\
762* The reverse of lto3b *
763\***********************************************/
764_3btol(bytes)
765u_char *bytes;
766{
767 int rc;
768 rc = (*bytes++ << 16);
769 rc += (*bytes++ << 8);
770 rc += *bytes;
771 return(rc);
772}
773