root/drivers/scsi/scsi_debug.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. scsi_dump
  2. scsi_debug_queuecommand
  3. internal_done
  4. scsi_debug_command
  5. scsi_debug_intr_handle
  6. scsi_debug_detect
  7. scsi_debug_abort
  8. scsi_debug_biosparam
  9. scsi_debug_reset
  10. scsi_debug_info

   1 /* $Id: scsi_debug.c,v 1.1 1992/07/24 06:27:38 root Exp root $
   2  *  linux/kernel/scsi_debug.c
   3  *
   4  *  Copyright (C) 1992  Eric Youngdale
   5  *  Simulate a host adapter with 2 disks attached.  Do a lot of checking
   6  *  to make sure that we are not getting blocks mixed up, and panic if
   7  *  anything out of the ordinary is seen.
   8  */
   9 
  10 #include <linux/kernel.h>
  11 #include <linux/sched.h>
  12 #include <linux/timer.h>
  13 #include <linux/head.h>
  14 #include <linux/types.h>
  15 #include <linux/string.h>
  16 #include <linux/genhd.h>
  17 #include <linux/fs.h>
  18 
  19 #include <asm/system.h>
  20 #include <asm/io.h>
  21 #include "../block/blk.h"
  22 #include "scsi.h"
  23 #include "hosts.h"
  24 
  25 #include "sd.h"
  26 
  27 /* A few options that we want selected */
  28 
  29 /* Do not attempt to use a timer to simulate a real disk with latency */
  30 /* Only use this in the actual kernel, not in the simulator. */
  31 #define IMMEDIATE
  32 
  33 /* Skip some consistency checking.  Good for benchmarking */
  34 #define SPEEDY
  35 
  36 /* Number of real scsi disks that will be detected ahead of time */
  37 static int NR_REAL=-1;
  38 
  39 #define NR_BLK_DEV      12
  40 #ifndef MAJOR_NR
  41 #define MAJOR_NR 8
  42 #endif
  43 #define START_PARTITION 4
  44 #define SCSI_DEBUG_TIMER 20
  45 /* Number of jiffies to wait before completing a command */
  46 #define DISK_SPEED     10
  47 #define CAPACITY (0x80000)
  48 
  49 static int starts[] = {4, 1000, 50000, CAPACITY, 0};
  50 static int npart = 0;
  51 
  52 #include "scsi_debug.h"
  53 #ifdef DEBUG
  54 #define DEB(x) x
  55 #else
  56 #define DEB(x)
  57 #endif
  58 
  59 #ifdef SPEEDY
  60 #define VERIFY1_DEBUG(RW) 1
  61 #define VERIFY_DEBUG(RW) 1
  62 #else
  63 
  64 #define VERIFY1_DEBUG(RW)                                               \
  65       if (bufflen != 1024) {printk("%d", bufflen); panic("(1)Bad bufflen");};                   \
  66       start = 0;                                                        \
  67       if ((SCpnt->request.dev & 0xf) != 0) start = starts[(SCpnt->request.dev & 0xf) - 1];              \
  68       if (bh){                                                  \
  69         if (bh->b_size != 1024) panic ("Wrong bh size");        \
  70         if ((bh->b_blocknr << 1) + start != block)              \
  71           {  printk("Wrong bh block# %d %d ",bh->b_blocknr, block);  \
  72           panic ("Wrong bh block#");};  \
  73         if (bh->b_dev != SCpnt->request.dev) panic ("Bad bh target");\
  74       };
  75 
  76 #if 0
  77 /* This had been in the VERIFY_DEBUG macro, but it fails if there is already
  78    a disk on the system */
  79       if ((SCpnt->request.dev & 0xfff0) != ((target + NR_REAL) << 4) +(MAJOR_NR << 8)){ \
  80         printk("Dev #s %x %x ",SCpnt->request.dev, target);                     \
  81         panic ("Bad target");};                                         \
  82 
  83 #endif
  84 
  85 #define VERIFY_DEBUG(RW)                                                \
  86       if (bufflen != 1024 && (!SCpnt->use_sg)) {printk("%x %d\n ",bufflen, SCpnt->use_sg); panic("Bad bufflen");};      \
  87       start = 0;                                                        \
  88       if ((SCpnt->request.dev & 0xf) > npart) panic ("Bad partition");  \
  89       if ((SCpnt->request.dev & 0xf) != 0) start = starts[(SCpnt->request.dev & 0xf) - 1];              \
  90       if (SCpnt->request.cmd != RW) panic ("Wrong  operation");         \
  91       if (SCpnt->request.sector + start != block) panic("Wrong block.");        \
  92       if (SCpnt->request.current_nr_sectors != 2 && (!SCpnt->use_sg)) panic ("Wrong # blocks"); \
  93       if (SCpnt->request.bh){                                                   \
  94         if (SCpnt->request.bh->b_size != 1024) panic ("Wrong bh size"); \
  95         if ((SCpnt->request.bh->b_blocknr << 1) + start != block)               \
  96           {  printk("Wrong bh block# %d %d ",SCpnt->request.bh->b_blocknr, block);  \
  97           panic ("Wrong bh block#");};  \
  98         if (SCpnt->request.bh->b_dev != SCpnt->request.dev) panic ("Bad bh target");\
  99       };
 100 #endif
 101 
 102 static volatile void (*do_done[SCSI_DEBUG_MAILBOXES])(Scsi_Cmnd *) = {NULL, };
 103 extern void scsi_debug_interrupt();
 104 
 105 volatile Scsi_Cmnd * SCint[SCSI_DEBUG_MAILBOXES] = {NULL,};
 106 static volatile unsigned int timeout[SCSI_DEBUG_MAILBOXES] ={0,};
 107 
 108 static char sense_buffer[128] = {0,};
 109 
 110 static void scsi_dump(Scsi_Cmnd * SCpnt, int flag){
     /* [previous][next][first][last][top][bottom][index][help] */
 111   int i;
 112 #if 0
 113   unsigned char * pnt;
 114 #endif
 115   unsigned int * lpnt;
 116   struct scatterlist * sgpnt = NULL;
 117   printk("use_sg: %d",SCpnt->use_sg);
 118   if (SCpnt->use_sg){
 119     sgpnt = (struct scatterlist *) SCpnt->buffer;
 120     for(i=0; i<SCpnt->use_sg; i++) {
 121       lpnt = (int *) sgpnt[i].alt_address;
 122       printk(":%x %x %d\n",sgpnt[i].alt_address, sgpnt[i].address, sgpnt[i].length);
 123       if (lpnt) printk(" (Alt %x) ",lpnt[15]);
 124     };
 125   } else {
 126     printk("nosg: %x %x %d\n",SCpnt->request.buffer, SCpnt->buffer,
 127            SCpnt->bufflen);
 128     lpnt = (int *) SCpnt->request.buffer;
 129     if (lpnt) printk(" (Alt %x) ",lpnt[15]);
 130   };
 131   lpnt = (unsigned int *) SCpnt;
 132   for (i=0;i<sizeof(Scsi_Cmnd)/4+1; i++) {
 133     if ((i & 7) == 0) printk("\n");
 134     printk("%x ",*lpnt++);
 135   };
 136   printk("\n");
 137   if (flag == 0) return;
 138   lpnt = (unsigned int *) sgpnt[0].alt_address;
 139   for (i=0;i<sizeof(Scsi_Cmnd)/4+1; i++) {
 140     if ((i & 7) == 0) printk("\n");
 141     printk("%x ",*lpnt++);
 142   };
 143 #if 0
 144   printk("\n");
 145   lpnt = (unsigned int *) sgpnt[0].address;
 146   for (i=0;i<sizeof(Scsi_Cmnd)/4+1; i++) {
 147     if ((i & 7) == 0) printk("\n");
 148     printk("%x ",*lpnt++);
 149   };
 150   printk("\n");
 151 #endif
 152   printk("DMA free %d sectors.\n", dma_free_sectors);
 153 }
 154 
 155 int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
     /* [previous][next][first][last][top][bottom][index][help] */
 156 {
 157     unchar *cmd = (unchar *) SCpnt->cmnd;
 158     struct partition * p;
 159     int block, start;
 160     struct buffer_head * bh = NULL;
 161     unsigned char * buff;
 162     int nbytes, sgcount;
 163     int scsi_debug_errsts;
 164     struct scatterlist * sgpnt;
 165     int target = SCpnt->target;
 166     int bufflen = SCpnt->request_bufflen;
 167     unsigned long flags;
 168     int i;
 169     sgcount = 0;
 170     sgpnt = NULL;
 171 
 172     DEB(if (target > 1) { SCpnt->result = DID_TIME_OUT << 16;done(SCpnt);return 0;});
 173     
 174     buff = (unsigned char *) SCpnt->request_buffer;
 175 
 176     if(target>=1 || SCpnt->lun != 0) {
 177       SCpnt->result =  DID_NO_CONNECT << 16;
 178       done(SCpnt);
 179       return 0;
 180     };
 181     
 182     switch(*cmd){
 183     case REQUEST_SENSE:
 184       printk("Request sense...\n");
 185 #ifndef DEBUG
 186       { int i;
 187         printk("scsi_debug: Requesting sense buffer (%x %x %x %d):", SCpnt, buff, done, bufflen);
 188         for(i=0;i<12;i++) printk("%d ",sense_buffer[i]);
 189         printk("\n");
 190       };
 191 #endif
 192       memset(buff, 0, bufflen);
 193       memcpy(buff, sense_buffer, bufflen);
 194       memset(sense_buffer, 0, sizeof(sense_buffer));
 195       SCpnt->result = 0;
 196       done(SCpnt); 
 197       return 0;
 198     case ALLOW_MEDIUM_REMOVAL:
 199       if(cmd[4]) printk("Medium removal inhibited...");
 200       else printk("Medium removal enabled...");
 201       scsi_debug_errsts = 0;
 202       break;
 203     case INQUIRY:
 204       printk("Inquiry...(%x %d)\n", buff, bufflen);
 205       memset(buff, 0, bufflen);
 206       buff[0] = TYPE_DISK;
 207       buff[1] = 0x80;  /* Removable disk */
 208       buff[2] = 1;
 209       buff[4] = 33 - 5;
 210       memcpy(&buff[8],"Foo Inc",7);
 211       memcpy(&buff[16],"XYZZY",5);
 212       memcpy(&buff[32],"1",1);
 213       scsi_debug_errsts = 0;
 214       break;
 215     case TEST_UNIT_READY:
 216       printk("Test unit ready.\n");
 217       if (buff)
 218         memset(buff, 0, bufflen);
 219       scsi_debug_errsts = 0;
 220       break;
 221     case READ_CAPACITY:
 222       printk("Read Capacity\n");
 223       if(NR_REAL < 0) NR_REAL = (SCpnt->request.dev >> 4) & 0x0f;
 224       memset(buff, 0, bufflen);
 225       buff[0] = (CAPACITY >> 24);
 226       buff[1] = (CAPACITY >> 16) & 0xff;
 227       buff[2] = (CAPACITY >> 8) & 0xff;
 228       buff[3] = CAPACITY & 0xff;
 229       buff[6] = 2; /* 512 byte sectors */
 230       scsi_debug_errsts = 0;
 231       break;
 232     case READ_10:
 233     case READ_6:
 234 #ifdef DEBUG
 235       printk("Read...");
 236 #endif
 237       if ((*cmd) == READ_10)
 238         block = cmd[5] + (cmd[4] << 8) + (cmd[3] << 16) + (cmd[2] << 24); 
 239       else 
 240         block = cmd[3] + (cmd[2] << 8) + ((cmd[1] & 0x1f) << 16);
 241       VERIFY_DEBUG(READ);
 242 #if defined(SCSI_SETUP_LATENCY) || defined(SCSI_DATARATE)
 243       {
 244         int delay = SCSI_SETUP_LATENCY;
 245         double usec;
 246 
 247         usec = 0.0;
 248         usec = (SCpnt->request.nr_sectors << 9) * 1.0e6 / SCSI_DATARATE;
 249         delay += usec;
 250         if(delay) usleep(delay);
 251       };
 252 #endif
 253 
 254 #ifdef DEBUG
 255       printk("(r%d)",SCpnt->request.nr_sectors);
 256 #endif
 257       nbytes = bufflen;
 258       if(SCpnt->use_sg){
 259         sgcount = 0;
 260         sgpnt = (struct scatterlist *) buff;
 261         buff = sgpnt[sgcount].address;
 262         bufflen = sgpnt[sgcount].length;
 263         bh = SCpnt->request.bh;
 264       };
 265       scsi_debug_errsts = 0;
 266       do{
 267         VERIFY1_DEBUG(READ);
 268 /* For the speedy test, we do not even want to fill the buffer with anything */
 269 #ifndef SPEEDY
 270         memset(buff, 0, bufflen);
 271 #endif
 272 /* If this is block 0, then we want to read the partition table for this
 273    device.  Let's make one up */
 274         if(block == 0 && target == 0) {
 275           *((unsigned short *) (buff+510)) = 0xAA55;
 276           p = (struct partition* ) (buff + 0x1be);
 277           npart = 0;
 278           while(starts[npart+1]){
 279             p->start_sect = starts[npart];
 280             p->nr_sects = starts[npart+1] - starts [npart];
 281             p->sys_ind = 0x81;  /* Linux partition */
 282             p++;
 283             npart++;
 284           };
 285           scsi_debug_errsts = 0;
 286           break;
 287         };
 288 #ifdef DEBUG
 289         if (SCpnt->use_sg) printk("Block %x (%d %d)\n",block, SCpnt->request.nr_sectors,
 290                SCpnt->request.current_nr_sectors);
 291 #endif
 292 
 293 #if 0
 294         /* Simulate a disk change */
 295         if(block == 0xfff0) {
 296           sense_buffer[0] = 0x70;
 297           sense_buffer[2] = UNIT_ATTENTION;
 298           starts[0] += 10;
 299           starts[1] += 10;
 300           starts[2] += 10;
 301          
 302 #ifdef DEBUG
 303       { int i;
 304         printk("scsi_debug: Filling sense buffer:");
 305         for(i=0;i<12;i++) printk("%d ",sense_buffer[i]);
 306         printk("\n");
 307       };
 308 #endif
 309           scsi_debug_errsts = (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1);
 310           break;
 311         } /* End phony disk change code */
 312 #endif
 313 
 314 #ifndef SPEEDY
 315         memcpy(buff, &target, sizeof(target));
 316         memcpy(buff+sizeof(target), cmd, 24);
 317         memcpy(buff+60, &block, sizeof(block));
 318         memcpy(buff+64, SCpnt, sizeof(Scsi_Cmnd));
 319 #endif
 320         nbytes -= bufflen;
 321         if(SCpnt->use_sg){
 322 #ifndef SPEEDY
 323           memcpy(buff+128, bh, sizeof(struct buffer_head));
 324 #endif
 325           block += bufflen >> 9;
 326           bh = bh->b_reqnext;
 327           sgcount++;
 328           if (nbytes) {
 329             if(!bh) panic("Too few blocks for linked request.");
 330             buff = sgpnt[sgcount].address;
 331             bufflen = sgpnt[sgcount].length;
 332           };
 333         }
 334       } while(nbytes);
 335 
 336       SCpnt->result = 0;
 337       (done)(SCpnt);
 338       return;
 339 
 340       if (SCpnt->use_sg && !scsi_debug_errsts)
 341         if(bh) scsi_dump(SCpnt, 0);
 342       break;
 343     case WRITE_10:
 344     case WRITE_6:
 345 #ifdef DEBUG
 346       printk("Write\n");
 347 #endif
 348       if ((*cmd) == WRITE_10)
 349         block = cmd[5] + (cmd[4] << 8) + (cmd[3] << 16) + (cmd[2] << 24); 
 350       else 
 351         block = cmd[3] + (cmd[2] << 8) + ((cmd[1] & 0x1f) << 16);
 352       VERIFY_DEBUG(WRITE);
 353 /*      printk("(w%d)",SCpnt->request.nr_sectors); */
 354       if (SCpnt->use_sg){
 355         if ((bufflen >> 9) != SCpnt->request.nr_sectors)
 356           panic ("Trying to write wrong number of blocks\n");
 357         sgpnt = (struct scatterlist *) buff;
 358         buff = sgpnt[sgcount].address;
 359       };
 360 #if 0
 361       if (block != *((unsigned long *) (buff+60))) {
 362         printk("%x %x :",block,  *((unsigned long *) (buff+60)));
 363         scsi_dump(SCpnt,1);
 364         panic("Bad block written.\n");
 365       };
 366 #endif
 367       scsi_debug_errsts = 0;
 368       break;
 369      default:
 370       printk("Unknown command %d\n",*cmd);
 371       SCpnt->result =  DID_NO_CONNECT << 16;
 372       done(SCpnt);
 373       return 0;
 374     };
 375 
 376    save_flags(flags); 
 377    cli();
 378     for(i=0;i<SCSI_DEBUG_MAILBOXES; i++){
 379       if (SCint[i] == 0) break;
 380     };
 381 
 382     if (i >= SCSI_DEBUG_MAILBOXES || SCint[i] != 0) 
 383       panic("Unable to find empty SCSI_DEBUG command slot.\n");
 384 
 385     SCint[i] = SCpnt;
 386 
 387     if (done) {
 388         DEB(printk("scsi_debug_queuecommand: now waiting for interrupt "););
 389         if (do_done[i])
 390           printk("scsi_debug_queuecommand: Two concurrent queuecommand?\n");
 391         else
 392           do_done[i] = done;
 393     }
 394     else
 395       printk("scsi_debug_queuecommand: done cant be NULL\n");
 396 
 397 #ifdef IMMEDIATE
 398     SCpnt->result = scsi_debug_errsts;
 399     scsi_debug_intr_handle();  /* No timer - do this one right away */
 400 #else
 401     timeout[i] = jiffies+DISK_SPEED;
 402 
 403 /* If no timers active, then set this one */
 404     if ((timer_active & (1 << SCSI_DEBUG_TIMER)) == 0) {
 405       timer_table[SCSI_DEBUG_TIMER].expires = timeout[i];
 406       timer_active |= 1 << SCSI_DEBUG_TIMER;
 407     };
 408 
 409     SCpnt->result = scsi_debug_errsts;
 410     restore_flags(flags);
 411 
 412 #if 0
 413     printk("Sending command (%d %x %d %d)...", i, done, timeout[i],jiffies);
 414 #endif
 415 #endif
 416 
 417     return 0;
 418 }
 419 
 420 volatile static int internal_done_flag = 0;
 421 volatile static int internal_done_errcode = 0;
 422 static void internal_done(Scsi_Cmnd * SCpnt)
     /* [previous][next][first][last][top][bottom][index][help] */
 423 {
 424     internal_done_errcode = SCpnt->result;
 425     ++internal_done_flag;
 426 }
 427 
 428 int scsi_debug_command(Scsi_Cmnd * SCpnt)
     /* [previous][next][first][last][top][bottom][index][help] */
 429 {
 430     DEB(printk("scsi_debug_command: ..calling scsi_debug_queuecommand\n"));
 431     scsi_debug_queuecommand(SCpnt, internal_done);
 432 
 433     while (!internal_done_flag);
 434     internal_done_flag = 0;
 435     return internal_done_errcode;
 436 }
 437 
 438 /* A "high" level interrupt handler.  This should be called once per jiffy
 439  to simulate a regular scsi disk.  We use a timer to do this. */
 440 
 441 static void scsi_debug_intr_handle(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 442 {
 443     Scsi_Cmnd * SCtmp;
 444     int i, pending;
 445     void (*my_done)(Scsi_Cmnd *); 
 446     unsigned long flags;
 447     int to;
 448 
 449 #ifndef IMMEDIATE
 450     timer_table[SCSI_DEBUG_TIMER].expires = 0;
 451     timer_active &= ~(1 << SCSI_DEBUG_TIMER);
 452 #endif
 453 
 454   repeat:
 455     save_flags(flags);
 456     cli();
 457     for(i=0;i<SCSI_DEBUG_MAILBOXES; i++) {
 458       if (SCint[i] == 0) continue;
 459 #ifndef IMMEDIATE
 460       if (timeout[i] == 0) continue;
 461       if (timeout[i] <= jiffies) break;
 462 #else
 463       break;
 464 #endif
 465     };
 466 
 467     if(i == SCSI_DEBUG_MAILBOXES){
 468 #ifndef IMMEDIATE
 469       pending = INT_MAX;
 470       for(i=0;i<SCSI_DEBUG_MAILBOXES; i++) {
 471         if (SCint[i] == 0) continue;
 472         if (timeout[i] == 0) continue;
 473         if (timeout[i] <= jiffies) {restore_flags(flags); goto repeat;};
 474         if (timeout[i] > jiffies) {
 475           if (pending > timeout[i]) pending = timeout[i];
 476           continue;
 477         };
 478       };
 479       if (pending && pending != INT_MAX) {
 480         timer_table[SCSI_DEBUG_TIMER].expires = 
 481           (pending <= jiffies ? jiffies+1 : pending);
 482         timer_active |= 1 << SCSI_DEBUG_TIMER;
 483       };
 484       restore_flags(flags);
 485 #endif
 486       return;
 487     };
 488 
 489     if(i < SCSI_DEBUG_MAILBOXES){
 490       timeout[i] = 0;
 491       my_done = do_done[i];
 492       do_done[i] = NULL;
 493       to = timeout[i];
 494       timeout[i] = 0;
 495       SCtmp = (Scsi_Cmnd *) SCint[i];
 496       SCint[i] = NULL;
 497       restore_flags(flags);
 498 
 499       if (!my_done) {
 500         printk("scsi_debug_intr_handle: Unexpected interrupt\n"); 
 501         return;
 502       }
 503       
 504 #ifdef DEBUG
 505       printk("In intr_handle...");
 506       printk("...done %d %x %d %d\n",i , my_done, to, jiffies);
 507       printk("In intr_handle: %d %x %x\n",i, SCtmp, my_done);
 508 #endif
 509 
 510       my_done(SCtmp);
 511 #ifdef DEBUG
 512       printk("Called done.\n");
 513 #endif
 514     };
 515     goto repeat;
 516 }
 517 
 518 
 519 int scsi_debug_detect(Scsi_Host_Template * tpnt)
     /* [previous][next][first][last][top][bottom][index][help] */
 520 {
 521 #ifndef IMMEDIATE
 522     timer_table[SCSI_DEBUG_TIMER].fn = scsi_debug_intr_handle;
 523     timer_table[SCSI_DEBUG_TIMER].expires = 0;
 524 #endif
 525     return 1;
 526 }
 527 
 528 int scsi_debug_abort(Scsi_Cmnd * SCpnt)
     /* [previous][next][first][last][top][bottom][index][help] */
 529 {
 530     int j;
 531     void (*my_done)(Scsi_Cmnd *);
 532     unsigned long flags;
 533 
 534     DEB(printk("scsi_debug_abort\n"));
 535     SCpnt->result = SCpnt->abort_reason << 16;
 536     for(j=0;j<SCSI_DEBUG_MAILBOXES; j++) {
 537       if(SCpnt == SCint[j]) {
 538         my_done = do_done[j];
 539         my_done(SCpnt);
 540         save_flags(flags);
 541         cli();
 542         timeout[j] = 0;
 543         SCint[j] = NULL;
 544         do_done[j] = NULL;
 545         restore_flags(flags);
 546       };
 547     };
 548     return 0;
 549 }
 550 
 551 int scsi_debug_biosparam(Disk * disk, int dev, int* info){
     /* [previous][next][first][last][top][bottom][index][help] */
 552   int size = disk->capacity;
 553   info[0] = 32;
 554   info[1] = 64;
 555   info[2] = (size + 2047) >> 11;
 556   if (info[2] >= 1024) info[2] = 1024;
 557   return 0;
 558 }
 559 
 560 int scsi_debug_reset(Scsi_Cmnd * SCpnt)
     /* [previous][next][first][last][top][bottom][index][help] */
 561 {
 562     int i;
 563     unsigned long flags;
 564 
 565     void (*my_done)(Scsi_Cmnd *);
 566     DEB(printk("scsi_debug_reset called\n"));
 567     for(i=0;i<SCSI_DEBUG_MAILBOXES; i++) {
 568       if (SCint[i] == NULL) continue;
 569       SCint[i]->result = DID_ABORT << 16;
 570       my_done = do_done[i];
 571       my_done(SCint[i]);
 572       save_flags(flags);
 573       cli();
 574       SCint[i] = NULL;
 575       do_done[i] = NULL;
 576       timeout[i] = 0;
 577       restore_flags(flags);
 578     };
 579     return 0;
 580 }
 581 
 582 const char *scsi_debug_info(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 583 {
 584     static char buffer[] = " ";                 /* looks nicer without anything here */
 585     return buffer;
 586 }
 587 
 588 

/* [previous][next][first][last][top][bottom][index][help] */