sd.c revision 1.1 1 /*
2 * Written by Julian Elischer (julian (at) 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 * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
16 * -------------------- ----- ----------------------
17 * CURRENT PATCH LEVEL: 1 00098
18 * -------------------- ----- ----------------------
19 *
20 * 16 Feb 93 Julian Elischer ADDED for SCSI system
21 *
22 */
23
24 /*
25 * Ported to run under 386BSD by Julian Elischer (julian (at) tfs.com) Sept 1992
26 */
27
28 #define SPLSD splbio
29 #define ESUCCESS 0
30 #include <sd.h>
31 #include <sys/types.h>
32 #include <sys/param.h>
33 #include <sys/dkbad.h>
34 #include <sys/systm.h>
35 #include <sys/conf.h>
36 #include <sys/file.h>
37 #include <sys/stat.h>
38 #include <sys/ioctl.h>
39 #include <sys/buf.h>
40 #include <sys/uio.h>
41 #include <sys/malloc.h>
42 #include <sys/errno.h>
43 #include <sys/disklabel.h>
44 #include <scsi/scsi_all.h>
45 #include <scsi/scsi_disk.h>
46 #include <scsi/scsiconf.h>
47
48 long int sdstrats,sdqueues;
49
50
51 #include <ddb.h>
52 #if NDDB > 0
53 int Debugger();
54 #else NDDB > 0
55 #define Debugger()
56 #endif NDDB > 0
57
58
59 #define PAGESIZ 4096
60 #define SECSIZE 512
61 #define PDLOCATION 29
62 #define BOOTRECORDSIGNATURE (0x55aa & 0x00ff)
63 #define SDOUTSTANDING 2
64 #define SDQSIZE 4
65 #define SD_RETRIES 4
66
67 #define MAKESDDEV(maj, unit, part) (makedev(maj,((unit<<3)+part)))
68 #define UNITSHIFT 3
69 #define PARTITION(z) (minor(z) & 0x07)
70 #define RAW_PART 3
71 #define UNIT(z) ( (minor(z) >> UNITSHIFT) )
72
73 #define WHOLE_DISK(unit) ( (unit << UNITSHIFT) + RAW_PART )
74
75 struct buf sd_buf_queue[NSD];
76 int sd_done();
77 int sdstrategy();
78
79 int sd_debug = 0;
80
81 struct scsi_xfer *sd_free_xfer[NSD];
82 int sd_xfer_block_wait[NSD];
83
84 struct sd_data
85 {
86 int flags;
87 #define SDVALID 0x02 /* PARAMS LOADED */
88 #define SDINIT 0x04 /* device has been init'd */
89 #define SDWAIT 0x08 /* device has someone waiting */
90 #define SDHAVELABEL 0x10 /* have read the label */
91 #define SDDOSPART 0x20 /* Have read the DOS partition table */
92 #define SDWRITEPROT 0x40 /* Device in readonly mode (S/W)*/
93 struct scsi_switch *sc_sw; /* address of scsi low level switch */
94 int ctlr; /* so they know which one we want */
95 int targ; /* our scsi target ID */
96 int lu; /* out scsi lu */
97 long int ad_info; /* info about the adapter */
98 int cmdscount; /* cmds allowed outstanding by board*/
99 int wlabel; /* label is writable */
100 struct disk_parms
101 {
102 u_char heads; /* Number of heads */
103 u_short cyls; /* Number of cylinders */
104 u_char sectors;/*dubious*/ /* Number of sectors/track */
105 u_short secsiz; /* Number of bytes/sector */
106 u_long disksize; /* total number sectors */
107 }params;
108 struct disklabel disklabel;
109 struct dos_partition dosparts[NDOSPART]; /* DOS view of disk */
110 int partflags[MAXPARTITIONS]; /* per partition flags */
111 #define SDOPEN 0x01
112 int openparts; /* one bit for each open partition */
113 unsigned int sd_start_of_unix; /* unix vs dos partitions */
114 }sd_data[NSD];
115
116
117 static int next_sd_unit = 0;
118 /***********************************************************************\
119 * The routine called by the low level scsi routine when it discovers *
120 * A device suitable for this driver *
121 \***********************************************************************/
122
123 int sdattach(ctlr,targ,lu,scsi_switch)
124 struct scsi_switch *scsi_switch;
125 {
126 int unit,i;
127 unsigned char *tbl;
128 struct sd_data *sd;
129 struct disk_parms *dp;
130 long int ad_info;
131 struct scsi_xfer *sd_scsi_xfer;
132
133 unit = next_sd_unit++;
134 sd = sd_data + unit;
135 dp = &(sd->params);
136 if(scsi_debug & PRINTROUTINES) printf("sdattach: ");
137 /*******************************************************\
138 * Check we have the resources for another drive *
139 \*******************************************************/
140 if( unit >= NSD)
141 {
142 printf("Too many scsi disks..(%d > %d) reconfigure kernel",(unit + 1),NSD);
143 return(0);
144 }
145 /*******************************************************\
146 * Store information needed to contact our base driver *
147 \*******************************************************/
148 sd->sc_sw = scsi_switch;
149 sd->ctlr = ctlr;
150 sd->targ = targ;
151 sd->lu = lu;
152 if(sd->sc_sw->adapter_info)
153 {
154 sd->ad_info = ( (*(sd->sc_sw->adapter_info))(ctlr));
155 sd->cmdscount = sd->ad_info & AD_INF_MAX_CMDS;
156 if(sd->cmdscount > SDOUTSTANDING)
157 {
158 sd->cmdscount = SDOUTSTANDING;
159 }
160 }
161 else
162 {
163 sd->ad_info = 1;
164 sd->cmdscount = 1;
165 }
166
167 i = sd->cmdscount;
168 sd_scsi_xfer = (struct scsi_xfer *)malloc(sizeof(struct scsi_xfer) * i
169 ,M_TEMP, M_NOWAIT);
170 while(i-- )
171 {
172 sd_scsi_xfer->next = sd_free_xfer[unit];
173 sd_free_xfer[unit] = sd_scsi_xfer;
174 sd_scsi_xfer++;
175 }
176 /*******************************************************\
177 * Use the subdriver to request information regarding *
178 * the drive. We cannot use interrupts yet, so the *
179 * request must specify this. *
180 \*******************************************************/
181 sd_get_parms(unit, SCSI_NOSLEEP | SCSI_NOMASK);
182 printf(" sd%d: %dMB, cyls %d, heads %d, secs %d, bytes/sec %d\n",
183 unit,
184 ( dp->cyls
185 * dp->heads
186 * dp->sectors
187 * dp->secsiz
188 )
189 / (1024 * 1024),
190 dp->cyls,
191 dp->heads,
192 dp->sectors,
193 dp->secsiz);
194 /*******************************************************\
195 * Set up the bufs for this device *
196 \*******************************************************/
197 sd->flags |= SDINIT;
198 return;
199
200 }
201
202
203
204 /*******************************************************\
205 * open the device. Make sure the partition info *
206 * is a up-to-date as can be. *
207 \*******************************************************/
208 sdopen(dev)
209 {
210 int errcode = 0;
211 int unit, part;
212 struct disk_parms disk_parms;
213 struct sd_data *sd ;
214
215 unit = UNIT(dev);
216 part = PARTITION(dev);
217 sd = sd_data + unit;
218 if(scsi_debug & (PRINTROUTINES | TRACEOPENS))
219 printf("sdopen: dev=0x%x (unit %d (of %d),partition %d)\n"
220 , dev, unit, NSD, part);
221 /*******************************************************\
222 * Check the unit is legal *
223 \*******************************************************/
224 if ( unit >= NSD )
225 {
226 return(ENXIO);
227 }
228 /*******************************************************\
229 * Make sure the disk has been initialised *
230 * At some point in the future, get the scsi driver *
231 * to look for a new device if we are not initted *
232 \*******************************************************/
233 if (! (sd->flags & SDINIT))
234 {
235 return(ENXIO);
236 }
237
238 /*******************************************************\
239 * If it's been invalidated, and not everybody has *
240 * closed it then forbid re-entry. *
241 \*******************************************************/
242 if ((! (sd->flags & SDVALID))
243 && ( sd->openparts))
244 return(ENXIO);
245 /*******************************************************\
246 * Check that it is still responding and ok. *
247 * "unit attention errors should occur here if the drive *
248 * has been restarted or the pack changed *
249 \*******************************************************/
250
251 if(scsi_debug & TRACEOPENS)
252 printf("device is ");
253 if (sd_test_unit_ready(unit,0))
254 {
255 if(scsi_debug & TRACEOPENS) printf("not reponding\n");
256 return(ENXIO);
257 }
258 if(scsi_debug & TRACEOPENS)
259 printf("ok\n");
260 /*******************************************************\
261 * In case it is a funny one, tell it to start *
262 * not needed for most hard drives (ignore failure) *
263 \*******************************************************/
264 sd_start_unit(unit,SCSI_ERR_OK|SCSI_SILENT);
265 if(scsi_debug & TRACEOPENS)
266 printf("started ");
267 /*******************************************************\
268 * Load the physical device parameters *
269 \*******************************************************/
270 sd_get_parms(unit, 0); /* sets SDVALID */
271 if (sd->params.secsiz != SECSIZE)
272 {
273 printf("sd%d: Can't deal with %d bytes logical blocks\n"
274 ,unit, sd->params.secsiz);
275 Debugger();
276 return(ENXIO);
277 }
278 if(scsi_debug & TRACEOPENS)
279 printf("Params loaded ");
280 /*******************************************************\
281 * Load the partition info if not already loaded *
282 \*******************************************************/
283 sd_prevent(unit,PR_PREVENT,SCSI_ERR_OK|SCSI_SILENT); /* who cares if it fails? */
284 if((errcode = sdgetdisklabel(unit)) && (part != RAW_PART))
285 {
286 sd_prevent(unit,PR_ALLOW,SCSI_ERR_OK|SCSI_SILENT); /* who cares if it fails? */
287 return(errcode);
288 }
289 if(scsi_debug & TRACEOPENS)
290 printf("Disklabel loaded ");
291 /*******************************************************\
292 * Check the partition is legal *
293 \*******************************************************/
294 if ( part >= MAXPARTITIONS ) {
295 sd_prevent(unit,PR_ALLOW,SCSI_ERR_OK|SCSI_SILENT); /* who cares if it fails? */
296 return(ENXIO);
297 }
298 if(scsi_debug & TRACEOPENS)
299 printf("ok");
300 /*******************************************************\
301 * Check that the partition exists *
302 \*******************************************************/
303 if (( sd->disklabel.d_partitions[part].p_size == 0 )
304 && (part != RAW_PART))
305 {
306 sd_prevent(unit,PR_ALLOW,SCSI_ERR_OK|SCSI_SILENT); /* who cares if it fails? */
307 return(ENXIO);
308 }
309 sd->partflags[part] |= SDOPEN;
310 sd->openparts |= (1 << part);
311 if(scsi_debug & TRACEOPENS)
312 printf("open %d %d\n",sdstrats,sdqueues);
313 return(0);
314 }
315
316 /*******************************************************\
317 * Get ownership of a scsi_xfer *
318 * If need be, sleep on it, until it comes free *
319 \*******************************************************/
320 struct scsi_xfer *sd_get_xs(unit,flags)
321 int flags;
322 int unit;
323 {
324 struct scsi_xfer *xs;
325 int s;
326
327 if(flags & (SCSI_NOSLEEP | SCSI_NOMASK))
328 {
329 if (xs = sd_free_xfer[unit])
330 {
331 sd_free_xfer[unit] = xs->next;
332 xs->flags = 0;
333 }
334 }
335 else
336 {
337 s = SPLSD();
338 while (!(xs = sd_free_xfer[unit]))
339 {
340 sd_xfer_block_wait[unit]++; /* someone waiting! */
341 sleep((caddr_t)&sd_free_xfer[unit], PRIBIO+1);
342 sd_xfer_block_wait[unit]--;
343 }
344 sd_free_xfer[unit] = xs->next;
345 splx(s);
346 xs->flags = 0;
347 }
348 return(xs);
349 }
350
351 /*******************************************************\
352 * Free a scsi_xfer, wake processes waiting for it *
353 \*******************************************************/
354 sd_free_xs(unit,xs,flags)
355 struct scsi_xfer *xs;
356 int unit;
357 int flags;
358 {
359 int s;
360
361 if(flags & SCSI_NOMASK)
362 {
363 if (sd_xfer_block_wait[unit])
364 {
365 printf("doing a wakeup from NOMASK mode\n");
366 wakeup((caddr_t)&sd_free_xfer[unit]);
367 }
368 xs->next = sd_free_xfer[unit];
369 sd_free_xfer[unit] = xs;
370 }
371 else
372 {
373 s = SPLSD();
374 if (sd_xfer_block_wait[unit])
375 wakeup((caddr_t)&sd_free_xfer[unit]);
376 xs->next = sd_free_xfer[unit];
377 sd_free_xfer[unit] = xs;
378 splx(s);
379 }
380 }
381
382 /*******************************************************\
383 * trim the size of the transfer if needed, *
384 * called by physio *
385 * basically the smaller of our max and the scsi driver's*
386 * minphys (note we have no max) *
387 \*******************************************************/
388 /* Trim buffer length if buffer-size is bigger than page size */
389 void sdminphys(bp)
390 struct buf *bp;
391 {
392 (*(sd_data[UNIT(bp->b_dev)].sc_sw->scsi_minphys))(bp);
393 }
394
395 /*******************************************************\
396 * Actually translate the requested transfer into *
397 * one the physical driver can understand *
398 * The transfer is described by a buf and will include *
399 * only one physical transfer. *
400 \*******************************************************/
401
402 int sdstrategy(bp)
403 struct buf *bp;
404 {
405 struct buf *dp;
406 unsigned int opri;
407 struct sd_data *sd ;
408 int unit;
409
410 sdstrats++;
411 unit = UNIT((bp->b_dev));
412 sd = sd_data + unit;
413 if(scsi_debug & PRINTROUTINES) printf("\nsdstrategy ");
414 if(scsi_debug & SHOWREQUESTS) printf("sd%d: %d bytes @ blk%d\n",
415 unit,bp->b_bcount,bp->b_blkno);
416 sdminphys(bp);
417 /*******************************************************\
418 * If the device has been made invalid, error out *
419 \*******************************************************/
420 if(!(sd->flags & SDVALID))
421 {
422 bp->b_error = EIO;
423 goto bad;
424 }
425 /*******************************************************\
426 * "soft" write protect check *
427 \*******************************************************/
428 if ((sd->flags & SDWRITEPROT) && (bp->b_flags & B_READ) == 0) {
429 bp->b_error = EROFS;
430 goto bad;
431 }
432 /*******************************************************\
433 * If it's a null transfer, return immediatly *
434 \*******************************************************/
435 if (bp->b_bcount == 0)
436 {
437 goto done;
438 }
439
440 /*******************************************************\
441 * Decide which unit and partition we are talking about *
442 * only raw is ok if no label *
443 \*******************************************************/
444 if(PARTITION(bp->b_dev) != RAW_PART)
445 {
446 if (!(sd->flags & SDHAVELABEL))
447 {
448 bp->b_error = EIO;
449 goto bad;
450 }
451
452 /*
453 * do bounds checking, adjust transfer. if error, process.
454 * if end of partition, just return
455 */
456 if (bounds_check_with_label(bp,&sd->disklabel,sd->wlabel) <= 0)
457 goto done;
458 /* otherwise, process transfer request */
459 }
460
461 opri = SPLSD();
462 dp = &sd_buf_queue[unit];
463
464 /*******************************************************\
465 * Place it in the queue of disk activities for this disk*
466 \*******************************************************/
467 disksort(dp, bp);
468
469 /*******************************************************\
470 * Tell the device to get going on the transfer if it's *
471 * not doing anything, otherwise just wait for completion*
472 \*******************************************************/
473 sdstart(unit);
474
475 splx(opri);
476 return;
477 bad:
478 bp->b_flags |= B_ERROR;
479 done:
480
481 /*******************************************************\
482 * Correctly set the buf to indicate a completed xfer *
483 \*******************************************************/
484 bp->b_resid = bp->b_bcount;
485 biodone(bp);
486 return;
487 }
488
489 /***************************************************************\
490 * sdstart looks to see if there is a buf waiting for the device *
491 * and that the device is not already busy. If both are true, *
492 * It deques the buf and creates a scsi command to perform the *
493 * transfer in the buf. The transfer request will call sd_done *
494 * on completion, which will in turn call this routine again *
495 * so that the next queued transfer is performed. *
496 * The bufs are queued by the strategy routine (sdstrategy) *
497 * *
498 * This routine is also called after other non-queued requests *
499 * have been made of the scsi driver, to ensure that the queue *
500 * continues to be drained. *
501 * *
502 * must be called at the correct (highish) spl level *
503 \***************************************************************/
504 /* sdstart() is called at SPLSD from sdstrategy and sd_done*/
505 sdstart(unit)
506 int unit;
507 {
508 int drivecount;
509 register struct buf *bp = 0;
510 register struct buf *dp;
511 struct scsi_xfer *xs;
512 struct scsi_rw_big cmd;
513 int blkno, nblk;
514 struct sd_data *sd = sd_data + unit;
515 struct partition *p ;
516
517 if(scsi_debug & PRINTROUTINES) printf("sdstart%d ",unit);
518 /*******************************************************\
519 * See if there is a buf to do and we are not already *
520 * doing one *
521 \*******************************************************/
522 if(!sd_free_xfer[unit])
523 {
524 return; /* none for us, unit already underway */
525 }
526
527 if(sd_xfer_block_wait[unit]) /* there is one, but a special waits */
528 {
529 return; /* give the special that's waiting a chance to run */
530 }
531
532
533 dp = &sd_buf_queue[unit];
534 if ((bp = dp->b_actf) != NULL) /* yes, an assign */
535 {
536 dp->b_actf = bp->av_forw;
537 }
538 else
539 {
540 return;
541 }
542
543 xs=sd_get_xs(unit,0); /* ok we can grab it */
544 xs->flags = INUSE; /* Now ours */
545 /*******************************************************\
546 * If the device has become invalid, abort all the *
547 * reads and writes until all files have been closed and *
548 * re-openned *
549 \*******************************************************/
550 if(!(sd->flags & SDVALID))
551 {
552 xs->error = XS_DRIVER_STUFFUP;
553 sd_done(unit,xs); /* clean up (calls sdstart) */
554 return ;
555 }
556 /*******************************************************\
557 * We have a buf, now we should move the data into *
558 * a scsi_xfer definition and try start it *
559 \*******************************************************/
560 /*******************************************************\
561 * First, translate the block to absolute *
562 \*******************************************************/
563 p = sd->disklabel.d_partitions + PARTITION(bp->b_dev);
564 blkno = bp->b_blkno + p->p_offset;
565 nblk = (bp->b_bcount + 511) >> 9;
566
567 /*******************************************************\
568 * Fill out the scsi command *
569 \*******************************************************/
570 bzero(&cmd, sizeof(cmd));
571 cmd.op_code = (bp->b_flags & B_READ)
572 ? READ_BIG : WRITE_BIG;
573 cmd.addr_3 = (blkno & 0xff000000) >> 24;
574 cmd.addr_2 = (blkno & 0xff0000) >> 16;
575 cmd.addr_1 = (blkno & 0xff00) >> 8;
576 cmd.addr_0 = blkno & 0xff;
577 cmd.length2 = (nblk & 0xff00) >> 8;
578 cmd.length1 = (nblk & 0xff);
579 /*******************************************************\
580 * Fill out the scsi_xfer structure *
581 * Note: we cannot sleep as we may be an interrupt *
582 \*******************************************************/
583 xs->flags |= SCSI_NOSLEEP;
584 xs->adapter = sd->ctlr;
585 xs->targ = sd->targ;
586 xs->lu = sd->lu;
587 xs->retries = SD_RETRIES;
588 xs->timeout = 10000;/* 10000 millisecs for a disk !*/
589 xs->cmd = (struct scsi_generic *)&cmd;
590 xs->cmdlen = sizeof(cmd);
591 xs->resid = bp->b_bcount;
592 xs->when_done = sd_done;
593 xs->done_arg = unit;
594 xs->done_arg2 = (int)xs;
595 xs->error = XS_NOERROR;
596 xs->bp = bp;
597 xs->data = (u_char *)bp->b_un.b_addr;
598 xs->datalen = bp->b_bcount;
599 /*******************************************************\
600 * Pass all this info to the scsi driver. *
601 \*******************************************************/
602
603
604
605 if ( (*(sd->sc_sw->scsi_cmd))(xs) != SUCCESSFULLY_QUEUED)
606 {
607 printf("sd%d: oops not queued",unit);
608 xs->error = XS_DRIVER_STUFFUP;
609 sd_done(unit,xs); /* clean up (calls sdstart) */
610 }
611 sdqueues++;
612 }
613
614 /*******************************************************\
615 * This routine is called by the scsi interrupt when *
616 * the transfer is complete.
617 \*******************************************************/
618 int sd_done(unit,xs)
619 int unit;
620 struct scsi_xfer *xs;
621 {
622 struct buf *bp;
623 int retval;
624 int retries = 0;
625
626 if(scsi_debug & PRINTROUTINES) printf("sd_done%d ",unit);
627 if (! (xs->flags & INUSE))
628 panic("scsi_xfer not in use!");
629 if(bp = xs->bp)
630 {
631 switch(xs->error)
632 {
633 case XS_NOERROR:
634 bp->b_error = 0;
635 bp->b_resid = 0;
636 break;
637
638 case XS_SENSE:
639 retval = (sd_interpret_sense(unit,xs));
640 if(retval)
641 {
642 bp->b_flags |= B_ERROR;
643 bp->b_error = retval;
644 }
645 break;
646
647 case XS_TIMEOUT:
648 printf("sd%d timeout\n",unit);
649
650 case XS_BUSY: /* should retry */ /* how? */
651 /************************************************/
652 /* SHOULD put buf back at head of queue */
653 /* and decrement retry count in (*xs) */
654 /* HOWEVER, this should work as a kludge */
655 /************************************************/
656 if(xs->retries--)
657 {
658 xs->error = XS_NOERROR;
659 xs->flags &= ~ITSDONE;
660 if ( (*(sd_data[unit].sc_sw->scsi_cmd))(xs)
661 == SUCCESSFULLY_QUEUED)
662 { /* don't wake the job, ok? */
663 return;
664 }
665 xs->flags |= ITSDONE;
666 } /* fall through */
667
668 case XS_DRIVER_STUFFUP:
669 bp->b_flags |= B_ERROR;
670 bp->b_error = EIO;
671 break;
672 default:
673 printf("sd%d: unknown error category from scsi driver\n"
674 ,unit);
675 }
676 biodone(bp);
677 sd_free_xs(unit,xs,0);
678 sdstart(unit); /* If there's anything waiting.. do it */
679 }
680 else /* special has finished */
681 {
682 wakeup(xs);
683 }
684 }
685 /*******************************************************\
686 * Perform special action on behalf of the user *
687 * Knows about the internals of this device *
688 \*******************************************************/
689 sdioctl(dev_t dev, int cmd, caddr_t addr, int flag)
690 {
691 /* struct sd_cmd_buf *args;*/
692 int error = 0;
693 unsigned int opri;
694 unsigned char unit, part;
695 register struct sd_data *sd;
696
697
698 /*******************************************************\
699 * Find the device that the user is talking about *
700 \*******************************************************/
701 unit = UNIT(dev);
702 part = PARTITION(dev);
703 sd = &sd_data[unit];
704 if(scsi_debug & PRINTROUTINES) printf("sdioctl%d ",unit);
705
706 /*******************************************************\
707 * If the device is not valid.. abandon ship *
708 \*******************************************************/
709 if (!(sd_data[unit].flags & SDVALID))
710 return(EIO);
711 switch(cmd)
712 {
713
714 case DIOCSBAD:
715 error = EINVAL;
716 break;
717
718 case DIOCGDINFO:
719 *(struct disklabel *)addr = sd->disklabel;
720 break;
721
722 case DIOCGPART:
723 ((struct partinfo *)addr)->disklab = &sd->disklabel;
724 ((struct partinfo *)addr)->part =
725 &sd->disklabel.d_partitions[PARTITION(dev)];
726 break;
727
728 case DIOCSDINFO:
729 if ((flag & FWRITE) == 0)
730 error = EBADF;
731 else
732 error = setdisklabel(&sd->disklabel,
733 (struct disklabel *)addr,
734 /*(sd->flags & DKFL_BSDLABEL) ? sd->openparts : */0,
735 sd->dosparts);
736 if (error == 0) {
737 sd->flags |= SDHAVELABEL;
738 }
739 break;
740
741 case DIOCWLABEL:
742 sd->flags &= ~SDWRITEPROT;
743 if ((flag & FWRITE) == 0)
744 error = EBADF;
745 else
746 sd->wlabel = *(int *)addr;
747 break;
748
749 case DIOCWDINFO:
750 sd->flags &= ~SDWRITEPROT;
751 if ((flag & FWRITE) == 0)
752 error = EBADF;
753 else
754 {
755 if ((error = setdisklabel(&sd->disklabel
756 , (struct disklabel *)addr
757 , /*(sd->flags & SDHAVELABEL) ? sd->openparts :*/ 0
758 , sd->dosparts)) == 0)
759 {
760 int wlab;
761
762 sd->flags |= SDHAVELABEL; /* ok write will succeed */
763
764 /* simulate opening partition 0 so write succeeds */
765 sd->openparts |= (1 << 0); /* XXX */
766 wlab = sd->wlabel;
767 sd->wlabel = 1;
768 error = writedisklabel(dev, sdstrategy,
769 &sd->disklabel, sd->dosparts);
770 sd->wlabel = wlab;
771 }
772 }
773 break;
774
775
776 default:
777 error = ENOTTY;
778 break;
779 }
780 return (error);
781 }
782
783
784 /*******************************************************\
785 * Load the label information on the named device *
786 \*******************************************************/
787 int sdgetdisklabel(unit)
788 unsigned char unit;
789 {
790 /*unsigned int n, m;*/
791 char *errstring;
792 struct dos_partition *dos_partition_p;
793 struct sd_data *sd = sd_data + unit;
794
795 /*******************************************************\
796 * If the inflo is already loaded, use it *
797 \*******************************************************/
798 if(sd->flags & SDHAVELABEL) return(ESUCCESS);
799
800 bzero(&sd->disklabel,sizeof(struct disklabel));
801 /*******************************************************\
802 * make partition 3 the whole disk in case of failure *
803 * then get pdinfo *
804 \*******************************************************/
805 sd->disklabel.d_partitions[0].p_offset = 0;
806 sd->disklabel.d_partitions[0].p_size = sd->params.disksize;
807 sd->disklabel.d_partitions[RAW_PART].p_offset = 0;
808 sd->disklabel.d_partitions[RAW_PART].p_size = sd->params.disksize;
809 sd->disklabel.d_npartitions = MAXPARTITIONS;
810 sd->disklabel.d_secsize = 512; /* as long as it's not 0 */
811 sd->disklabel.d_ntracks = sd->params.heads;
812 sd->disklabel.d_nsectors = sd->params.sectors;
813 sd->disklabel.d_ncylinders = sd->params.cyls;
814 sd->disklabel.d_secpercyl = sd->params.heads * sd->params.sectors;
815 if (sd->disklabel.d_secpercyl == 0)
816 {
817 sd->disklabel.d_secpercyl = 100;
818 /* as long as it's not 0 */
819 /* readdisklabel divides by it */
820 }
821
822 /*******************************************************\
823 * all the generic bisklabel extraction routine *
824 \*******************************************************/
825 if(errstring = readdisklabel(makedev(0 ,(unit<<UNITSHIFT )+3)
826 , sdstrategy
827 , &sd->disklabel
828 , sd->dosparts
829 , 0
830 , 0))
831 {
832 printf("sd%d: %s\n",unit, errstring);
833 return(ENXIO);
834 }
835 /*******************************************************\
836 * leave partition 2 "open" for raw I/O *
837 \*******************************************************/
838
839 sd->flags |= SDHAVELABEL; /* WE HAVE IT ALL NOW */
840 return(ESUCCESS);
841 }
842
843 /*******************************************************\
844 * Find out from the device what it's capacity is *
845 \*******************************************************/
846 sd_size(unit, flags)
847 {
848 struct scsi_read_cap_data rdcap;
849 struct scsi_read_capacity scsi_cmd;
850 int size;
851
852 /*******************************************************\
853 * make up a scsi command and ask the scsi driver to do *
854 * it for you. *
855 \*******************************************************/
856 bzero(&scsi_cmd, sizeof(scsi_cmd));
857 scsi_cmd.op_code = READ_CAPACITY;
858
859 /*******************************************************\
860 * If the command works, interpret the result as a 4 byte*
861 * number of blocks *
862 \*******************************************************/
863 if (sd_scsi_cmd(unit,
864 &scsi_cmd,
865 sizeof(scsi_cmd),
866 &rdcap,
867 sizeof(rdcap),
868 2000,
869 flags) != 0)
870 {
871 printf("could not get size of unit %d\n", unit);
872 return(0);
873 } else {
874 size = rdcap.addr_0 + 1 ;
875 size += rdcap.addr_1 << 8;
876 size += rdcap.addr_2 << 16;
877 size += rdcap.addr_3 << 24;
878 }
879 return(size);
880 }
881
882 /*******************************************************\
883 * Get scsi driver to send a "are you ready?" command *
884 \*******************************************************/
885 sd_test_unit_ready(unit,flags)
886 int unit,flags;
887 {
888 struct scsi_test_unit_ready scsi_cmd;
889
890 bzero(&scsi_cmd, sizeof(scsi_cmd));
891 scsi_cmd.op_code = TEST_UNIT_READY;
892
893 return (sd_scsi_cmd(unit,
894 &scsi_cmd,
895 sizeof(scsi_cmd),
896 0,
897 0,
898 100000,
899 flags));
900 }
901
902 /*******************************************************\
903 * Prevent or allow the user to remove the tape *
904 \*******************************************************/
905 sd_prevent(unit,type,flags)
906 int unit,type,flags;
907 {
908 struct scsi_prevent scsi_cmd;
909
910 bzero(&scsi_cmd, sizeof(scsi_cmd));
911 scsi_cmd.op_code = PREVENT_ALLOW;
912 scsi_cmd.prevent=type;
913 return (sd_scsi_cmd(unit,
914 &scsi_cmd,
915 sizeof(scsi_cmd),
916 0,
917 0,
918 5000,
919 flags) );
920 }
921 /*******************************************************\
922 * Get scsi driver to send a "start up" command *
923 \*******************************************************/
924 sd_start_unit(unit,flags)
925 int unit,flags;
926 {
927 struct scsi_start_stop scsi_cmd;
928
929 bzero(&scsi_cmd, sizeof(scsi_cmd));
930 scsi_cmd.op_code = START_STOP;
931 scsi_cmd.start = 1;
932
933 return (sd_scsi_cmd(unit,
934 &scsi_cmd,
935 sizeof(scsi_cmd),
936 0,
937 0,
938 2000,
939 flags));
940 }
941
942 /*******************************************************\
943 * Tell the device to map out a defective block *
944 \*******************************************************/
945 sd_reassign_blocks(unit,block)
946 {
947 struct scsi_reassign_blocks scsi_cmd;
948 struct scsi_reassign_blocks_data rbdata;
949
950
951 bzero(&scsi_cmd, sizeof(scsi_cmd));
952 bzero(&rbdata, sizeof(rbdata));
953 scsi_cmd.op_code = REASSIGN_BLOCKS;
954
955 rbdata.length_msb = 0;
956 rbdata.length_lsb = sizeof(rbdata.defect_descriptor[0]);
957 rbdata.defect_descriptor[0].dlbaddr_3 = ((block >> 24) & 0xff);
958 rbdata.defect_descriptor[0].dlbaddr_2 = ((block >> 16) & 0xff);
959 rbdata.defect_descriptor[0].dlbaddr_1 = ((block >> 8) & 0xff);
960 rbdata.defect_descriptor[0].dlbaddr_0 = ((block ) & 0xff);
961
962 return(sd_scsi_cmd(unit,
963 &scsi_cmd,
964 sizeof(scsi_cmd),
965 &rbdata,
966 sizeof(rbdata),
967 5000,
968 0));
969 }
970
971 #define b2tol(a) (((unsigned)(a##_1) << 8) + (unsigned)a##_0 )
972
973 /*******************************************************\
974 * Get the scsi driver to send a full inquiry to the *
975 * device and use the results to fill out the disk *
976 * parameter structure. *
977 \*******************************************************/
978
979 int sd_get_parms(unit, flags)
980 {
981 struct sd_data *sd = sd_data + unit;
982 struct disk_parms *disk_parms = &sd->params;
983 struct scsi_mode_sense scsi_cmd;
984 struct scsi_mode_sense_data
985 {
986 struct scsi_mode_header header;
987 struct blk_desc blk_desc;
988 union disk_pages pages;
989 }scsi_sense;
990 int sectors;
991
992 /*******************************************************\
993 * First check if we have it all loaded *
994 \*******************************************************/
995 if(sd->flags & SDVALID) return(0);
996 /*******************************************************\
997 * First do a mode sense page 3 *
998 \*******************************************************/
999 if (sd_debug)
1000 {
1001 bzero(&scsi_cmd, sizeof(scsi_cmd));
1002 scsi_cmd.op_code = MODE_SENSE;
1003 scsi_cmd.page_code = 3;
1004 scsi_cmd.length = 0x24;
1005 /*******************************************************\
1006 * do the command, but we don't need the results *
1007 * just print them for our interest's sake *
1008 \*******************************************************/
1009 if (sd_scsi_cmd(unit,
1010 &scsi_cmd,
1011 sizeof(scsi_cmd),
1012 &scsi_sense,
1013 sizeof(scsi_sense),
1014 2000,
1015 flags) != 0)
1016 {
1017 printf("could not mode sense (3) for unit %d\n", unit);
1018 return(ENXIO);
1019 }
1020 printf("unit %d: %d trk/zone, %d alt_sec/zone, %d alt_trk/zone, %d alt_trk/lun\n",
1021 unit,
1022 b2tol(scsi_sense.pages.disk_format.trk_z),
1023 b2tol(scsi_sense.pages.disk_format.alt_sec),
1024 b2tol(scsi_sense.pages.disk_format.alt_trk_z),
1025 b2tol(scsi_sense.pages.disk_format.alt_trk_v));
1026 printf(" %d sec/trk, %d bytes/sec, %d interleave, %d %d bytes/log_blk\n",
1027 b2tol(scsi_sense.pages.disk_format.ph_sec_t),
1028 b2tol(scsi_sense.pages.disk_format.bytes_s),
1029 b2tol(scsi_sense.pages.disk_format.interleave),
1030 sd_size(unit, flags),
1031 _3btol(scsi_sense.blk_desc.blklen));
1032 }
1033
1034
1035 /*******************************************************\
1036 * do a "mode sense page 4" *
1037 \*******************************************************/
1038 bzero(&scsi_cmd, sizeof(scsi_cmd));
1039 scsi_cmd.op_code = MODE_SENSE;
1040 scsi_cmd.page_code = 4;
1041 scsi_cmd.length = 0x20;
1042 /*******************************************************\
1043 * If the command worked, use the results to fill out *
1044 * the parameter structure *
1045 \*******************************************************/
1046 if (sd_scsi_cmd(unit,
1047 &scsi_cmd,
1048 sizeof(scsi_cmd),
1049 &scsi_sense,
1050 sizeof(scsi_sense),
1051 2000,
1052 flags) != 0)
1053 {
1054 printf("could not mode sense (4) for unit %d\n", unit);
1055 printf(" using ficticious geometry\n");
1056 /* use adaptec standard ficticious geometry */
1057 sectors = sd_size(unit, flags);
1058 disk_parms->heads = 64;
1059 disk_parms->sectors = 32;
1060 disk_parms->cyls = sectors/(64 * 32);
1061 disk_parms->secsiz = SECSIZE;
1062 }
1063 else
1064 {
1065
1066 if (sd_debug)
1067 {
1068 printf(" %d cyls, %d heads, %d precomp, %d red_write, %d land_zone\n",
1069 _3btol(&scsi_sense.pages.rigid_geometry.ncyl_2),
1070 scsi_sense.pages.rigid_geometry.nheads,
1071 b2tol(scsi_sense.pages.rigid_geometry.st_cyl_wp),
1072 b2tol(scsi_sense.pages.rigid_geometry.st_cyl_rwc),
1073 b2tol(scsi_sense.pages.rigid_geometry.land_zone));
1074 }
1075
1076 /*******************************************************\
1077 * KLUDGE!!(for zone recorded disks) *
1078 * give a number of sectors so that sec * trks * cyls *
1079 * is <= disk_size *
1080 \*******************************************************/
1081 disk_parms->heads = scsi_sense.pages.rigid_geometry.nheads;
1082 disk_parms->cyls = _3btol(&scsi_sense.pages.rigid_geometry.ncyl_2);
1083 disk_parms->secsiz = _3btol(&scsi_sense.blk_desc.blklen);
1084
1085 sectors = sd_size(unit, flags);
1086 sectors /= disk_parms->cyls;
1087 sectors /= disk_parms->heads;
1088 disk_parms->sectors = sectors; /* dubious on SCSI*/
1089 }
1090
1091 sd->flags |= SDVALID;
1092 return(0);
1093 }
1094
1095 /*******************************************************\
1096 * close the device.. only called if we are the LAST *
1097 * occurence of an open device *
1098 \*******************************************************/
1099 sdclose(dev)
1100 dev_t dev;
1101 {
1102 unsigned char unit, part;
1103 unsigned int old_priority;
1104
1105 unit = UNIT(dev);
1106 part = PARTITION(dev);
1107 sd_data[unit].partflags[part] &= ~SDOPEN;
1108 sd_data[unit].openparts &= ~(1 << part);
1109 if(sd_data[unit].openparts == 0) /* if all partitions closed */
1110 {
1111 sd_prevent(unit,PR_ALLOW,SCSI_SILENT|SCSI_ERR_OK);
1112 }
1113 return(0);
1114 }
1115
1116 /*******************************************************\
1117 * ask the scsi driver to perform a command for us. *
1118 * Call it through the switch table, and tell it which *
1119 * sub-unit we want, and what target and lu we wish to *
1120 * talk to. Also tell it where to find the command *
1121 * how long int is. *
1122 * Also tell it where to read/write the data, and how *
1123 * long the data is supposed to be *
1124 \*******************************************************/
1125 int sd_scsi_cmd(unit,scsi_cmd,cmdlen,data_addr,datalen,timeout,flags)
1126
1127 int unit,flags;
1128 struct scsi_generic *scsi_cmd;
1129 int cmdlen;
1130 int timeout;
1131 u_char *data_addr;
1132 int datalen;
1133 {
1134 struct scsi_xfer *xs;
1135 int retval;
1136 int s;
1137 struct sd_data *sd = sd_data + unit;
1138
1139 if(scsi_debug & PRINTROUTINES) printf("\nsd_scsi_cmd%d ",unit);
1140 if(sd->sc_sw) /* If we have a scsi driver */
1141 {
1142 xs = sd_get_xs(unit,flags); /* should wait unless booting */
1143 if(!xs)
1144 {
1145 printf("sd_scsi_cmd%d: controller busy"
1146 " (this should never happen)\n",unit);
1147 return(EBUSY);
1148 }
1149 xs->flags |= INUSE;
1150 /*******************************************************\
1151 * Fill out the scsi_xfer structure *
1152 \*******************************************************/
1153 xs->flags |= flags;
1154 xs->adapter = sd->ctlr;
1155 xs->targ = sd->targ;
1156 xs->lu = sd->lu;
1157 xs->retries = SD_RETRIES;
1158 xs->timeout = timeout;
1159 xs->cmd = scsi_cmd;
1160 xs->cmdlen = cmdlen;
1161 xs->data = data_addr;
1162 xs->datalen = datalen;
1163 xs->resid = datalen;
1164 xs->when_done = (flags & SCSI_NOMASK)
1165 ?(int (*)())0
1166 :sd_done;
1167 xs->done_arg = unit;
1168 xs->done_arg2 = (int)xs;
1169 retry: xs->error = XS_NOERROR;
1170 xs->bp = 0;
1171 retval = (*(sd->sc_sw->scsi_cmd))(xs);
1172 switch(retval)
1173 {
1174 case SUCCESSFULLY_QUEUED:
1175 s = splbio();
1176 while(!(xs->flags & ITSDONE))
1177 sleep(xs,PRIBIO+1);
1178 splx(s);
1179
1180 case HAD_ERROR:
1181 /*printf("err = %d ",xs->error);*/
1182 switch(xs->error)
1183 {
1184 case XS_NOERROR:
1185 retval = ESUCCESS;
1186 break;
1187 case XS_SENSE:
1188 retval = (sd_interpret_sense(unit,xs));
1189 break;
1190 case XS_DRIVER_STUFFUP:
1191 retval = EIO;
1192 break;
1193 case XS_TIMEOUT:
1194 if(xs->retries-- )
1195 {
1196 xs->flags &= ~ITSDONE;
1197 goto retry;
1198 }
1199 retval = EIO;
1200 break;
1201 case XS_BUSY:
1202 if(xs->retries-- )
1203 {
1204 xs->flags &= ~ITSDONE;
1205 goto retry;
1206 }
1207 retval = EIO;
1208 break;
1209 default:
1210 retval = EIO;
1211 printf("sd%d: unknown error category from scsi driver\n"
1212 ,unit);
1213 }
1214 break;
1215 case COMPLETE:
1216 retval = ESUCCESS;
1217 break;
1218 case TRY_AGAIN_LATER:
1219 if(xs->retries-- )
1220 {
1221 xs->flags &= ~ITSDONE;
1222 goto retry;
1223 }
1224 retval = EIO;
1225 break;
1226 default:
1227 retval = EIO;
1228 }
1229 sd_free_xs(unit,xs,flags);
1230 sdstart(unit); /* check if anything is waiting fr the xs */
1231 }
1232 else
1233 {
1234 printf("sd%d: not set up\n",unit);
1235 return(EINVAL);
1236 }
1237 return(retval);
1238 }
1239 /***************************************************************\
1240 * Look at the returned sense and act on the error and detirmine *
1241 * The unix error number to pass back... (0 = report no error) *
1242 \***************************************************************/
1243
1244 int sd_interpret_sense(unit,xs)
1245 int unit;
1246 struct scsi_xfer *xs;
1247 {
1248 struct scsi_sense_data *sense;
1249 int key;
1250 int silent;
1251
1252 /***************************************************************\
1253 * If the flags say errs are ok, then always return ok. *
1254 \***************************************************************/
1255 if (xs->flags & SCSI_ERR_OK) return(ESUCCESS);
1256 silent = (xs->flags & SCSI_SILENT);
1257
1258 sense = &(xs->sense);
1259 switch(sense->error_class)
1260 {
1261 case 7:
1262 {
1263 key=sense->ext.extended.sense_key;
1264 switch(key)
1265 {
1266 case 0x0:
1267 return(ESUCCESS);
1268 case 0x1:
1269 if(!silent)
1270 {
1271 printf("sd%d: soft error(corrected) ", unit);
1272 if(sense->valid)
1273 {
1274 printf("block no. %d (decimal)",
1275 (sense->ext.extended.info[0] <<24),
1276 (sense->ext.extended.info[1] <<16),
1277 (sense->ext.extended.info[2] <<8),
1278 (sense->ext.extended.info[3] ));
1279 }
1280 printf("\n");
1281 }
1282 return(ESUCCESS);
1283 case 0x2:
1284 if(!silent)printf("sd%d: not ready\n ",
1285 unit);
1286 return(ENODEV);
1287 case 0x3:
1288 if(!silent)
1289 {
1290 printf("sd%d: medium error ", unit);
1291 if(sense->valid)
1292 {
1293 printf("block no. %d (decimal)",
1294 (sense->ext.extended.info[0] <<24),
1295 (sense->ext.extended.info[1] <<16),
1296 (sense->ext.extended.info[2] <<8),
1297 (sense->ext.extended.info[3] ));
1298 }
1299 printf("\n");
1300 }
1301 return(EIO);
1302 case 0x4:
1303 if(!silent)printf("sd%d: non-media hardware failure\n ",
1304 unit);
1305 return(EIO);
1306 case 0x5:
1307 if(!silent)printf("sd%d: illegal request\n ",
1308 unit);
1309 return(EINVAL);
1310 case 0x6:
1311 /***********************************************\
1312 * If we are not open, then this is not an error *
1313 * as we don't have state yet. Either way, make *
1314 * sure that we don't have any residual state *
1315 \***********************************************/
1316 if(!silent)printf("sd%d: Unit attention.\n ", unit);
1317 sd_data[unit].flags &= ~(SDVALID | SDHAVELABEL);
1318 if (sd_data[unit].openparts)
1319 {
1320 return(EIO);
1321 }
1322 return(ESUCCESS); /* not an error if nothing's open */
1323 case 0x7:
1324 if(!silent)
1325 {
1326 printf("sd%d: attempted protection violation ",
1327 unit);
1328 if(sense->valid)
1329 {
1330 printf("block no. %d (decimal)\n",
1331 (sense->ext.extended.info[0] <<24),
1332 (sense->ext.extended.info[1] <<16),
1333 (sense->ext.extended.info[2] <<8),
1334 (sense->ext.extended.info[3] ));
1335 }
1336 printf("\n");
1337 }
1338 return(EACCES);
1339 case 0x8:
1340 if(!silent)
1341 {
1342 printf("sd%d: block wrong state (worm)\n ",
1343 unit);
1344 if(sense->valid)
1345 {
1346 printf("block no. %d (decimal)\n",
1347 (sense->ext.extended.info[0] <<24),
1348 (sense->ext.extended.info[1] <<16),
1349 (sense->ext.extended.info[2] <<8),
1350 (sense->ext.extended.info[3] ));
1351 }
1352 printf("\n");
1353 }
1354 return(EIO);
1355 case 0x9:
1356 if(!silent)printf("sd%d: vendor unique\n",
1357 unit);
1358 return(EIO);
1359 case 0xa:
1360 if(!silent)printf("sd%d: copy aborted\n ",
1361 unit);
1362 return(EIO);
1363 case 0xb:
1364 if(!silent)printf("sd%d: command aborted\n ",
1365 unit);
1366 return(EIO);
1367 case 0xc:
1368 if(!silent)
1369 {
1370 printf("sd%d: search returned\n ",
1371 unit);
1372 if(sense->valid)
1373 {
1374 printf("block no. %d (decimal)\n",
1375 (sense->ext.extended.info[0] <<24),
1376 (sense->ext.extended.info[1] <<16),
1377 (sense->ext.extended.info[2] <<8),
1378 (sense->ext.extended.info[3] ));
1379 }
1380 printf("\n");
1381 }
1382 return(ESUCCESS);
1383 case 0xd:
1384 if(!silent)printf("sd%d: volume overflow\n ",
1385 unit);
1386 return(ENOSPC);
1387 case 0xe:
1388 if(!silent)
1389 {
1390 printf("sd%d: verify miscompare\n ",
1391 unit);
1392 if(sense->valid)
1393 {
1394 printf("block no. %d (decimal)\n",
1395 (sense->ext.extended.info[0] <<24),
1396 (sense->ext.extended.info[1] <<16),
1397 (sense->ext.extended.info[2] <<8),
1398 (sense->ext.extended.info[3] ));
1399 }
1400 printf("\n");
1401 }
1402 return(EIO);
1403 case 0xf:
1404 if(!silent)printf("sd%d: unknown error key\n ",
1405 unit);
1406 return(EIO);
1407 }
1408 break;
1409 }
1410 case 0:
1411 case 1:
1412 case 2:
1413 case 3:
1414 case 4:
1415 case 5:
1416 case 6:
1417 {
1418 if(!silent)printf("sd%d: error class %d code %d\n",
1419 unit,
1420 sense->error_class,
1421 sense->error_code);
1422 if(sense->valid)
1423 if(!silent)printf("block no. %d (decimal)\n",
1424 (sense->ext.unextended.blockhi <<16),
1425 + (sense->ext.unextended.blockmed <<8),
1426 + (sense->ext.unextended.blocklow ));
1427 }
1428 return(EIO);
1429 }
1430 }
1431
1432
1433
1434
1435 int
1436 sdsize(dev_t dev)
1437 {
1438 int unit = UNIT(dev), part = PARTITION(dev), val;
1439 struct sd_data *sd;
1440
1441 if (unit >= NSD)
1442 return(-1);
1443
1444 sd = &sd_data[unit];
1445 if((sd->flags & SDINIT) == 0) return(-1);
1446 if (sd == 0 || (sd->flags & SDHAVELABEL) == 0)
1447 val = sdopen (MAKESDDEV(major(dev), unit, RAW_PART), FREAD, S_IFBLK, 0);
1448 if ( val != 0 || sd->flags & SDWRITEPROT)
1449 return (-1);
1450
1451 return((int)sd->disklabel.d_partitions[part].p_size);
1452 }
1453
1454 sddump()
1455 {
1456 printf("sddump() -- not implemented\n");
1457 return(-1);
1458 }
1459
1460
1461
1462
1463
1464