root/kernel/blk_drv/scsi/ultrastor.c

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

DEFINITIONS

This source file includes following definitions.
  1. ultrastor_detect
  2. ultrastor_info
  3. build_sg_list
  4. ultrastor_queuecommand
  5. ultrastor_abort
  6. ultrastor_reset
  7. ultrastor_biosparam
  8. ultrastor_interrupt

   1 /*
   2  *      ultrastor.c     Copyright (C) 1992 David B. Gentzel
   3  *      Low-level SCSI driver for UltraStor 14F
   4  *      by David B. Gentzel, Whitfield Software Services, Carnegie, PA
   5  *          (gentzel@nova.enet.dec.com)
   6  *  scatter/gather added by Scott Taylor (n217cg@tamuts.tamu.edu)
   7  *      Thanks to UltraStor for providing the necessary documentation
   8  */
   9 
  10 /*
  11  * TODO:
  12  *      1. Cleanup error handling & reporting.
  13  *      2. Find out why scatter/gather is limited to 16 requests per command.
  14  *      3. Add multiple outstanding requests.
  15  *      4. See if we can make good use of having more than one command per lun.
  16  *      5. Test/improve/fix abort & reset functions.
  17  *      6. Look at command linking (mscp.command_link and
  18  *         mscp.command_link_id).
  19  */
  20 
  21 /*
  22  * NOTES:
  23  *    The UltraStor 14F is one of a family of intelligent, high performance
  24  *    SCSI-2 host adapters.  They all support command queueing and
  25  *    scatter/gather I/O.  Some of them can also emulate the standard
  26  *    WD1003 interface for use with OS's which don't support SCSI.
  27  *    Here is the scoop on the various models:
  28  *      14F - ISA first-party DMA HA with floppy support and WD1003 emulation.
  29  *      14N - ISA HA with floppy support.  I think that this is a non-DMA
  30  *            HA.  Nothing further known.
  31  *      24F - EISA Bus Master HA with floppy support and WD1003 emulation.
  32  *      34F - VL-Bus Bus Master HA with floppy support (no WD1003 emulation).
  33  *
  34  *    The 14F is supported by this driver.  An effort has been made to support
  35  *    the 34F.  It should work, but is untested.  The 24F does not work at
  36  *    present.
  37  *
  38  *    Places flagged with a triple question-mark are things which are either
  39  *    unfinished, questionable, or wrong.
  40  */
  41 
  42 #include <linux/stddef.h>
  43 #include <linux/string.h>
  44 #include <linux/sched.h>
  45 #include <linux/kernel.h>
  46 
  47 #include <asm/io.h>
  48 #include <asm/system.h>
  49 #include <asm/dma.h>
  50 
  51 #define ULTRASTOR_PRIVATE       /* Get the private stuff from ultrastor.h */
  52 #include "../blk.h"
  53 #include "scsi.h"
  54 #include "hosts.h"
  55 #include "ultrastor.h"
  56 
  57 #define ULTRASTOR_DEBUG 0
  58 
  59 #define VERSION "1.1 alpha"
  60 
  61 #define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr)[0])
  62 #define BYTE(num, n) ((unsigned char)((unsigned int)(num) >> ((n) * 8)))
  63 
  64 /* Simply using "unsigned long" in these structures won't work as it causes
  65    alignment.  Perhaps the "aligned" attribute may be used in GCC 2.0 to get
  66    around this, but for now I use this hack. */
  67 typedef struct {
  68     unsigned char bytes[4];
  69 } Longword;
  70 
  71 /* Used to fetch the configuration info from the config i/o registers.  We
  72    then store (in a friendlier format) in config. */
  73 struct config_1 {
  74     unsigned char bios_segment: 3;
  75     unsigned char removable_disks_as_fixed: 1;
  76     unsigned char interrupt: 2;
  77     unsigned char dma_channel: 2;
  78 };
  79 struct config_2 {
  80     unsigned char ha_scsi_id: 3;
  81     unsigned char mapping_mode: 2;
  82     unsigned char bios_drive_number: 1;
  83     unsigned char tfr_port: 2;
  84 };
  85 
  86 /* Used to store configuration info read from config i/o registers.  Most of
  87    this is not used yet, but might as well save it. */
  88 struct config {
  89     const void *bios_segment;
  90     unsigned short port_address;
  91     unsigned char interrupt: 4;
  92     unsigned char dma_channel: 3;
  93     unsigned char bios_drive_number: 1;
  94     unsigned char heads;
  95     unsigned char sectors;
  96     unsigned char ha_scsi_id: 3;
  97     unsigned char subversion: 4;
  98 };
  99 
 100 /* MailBox SCSI Command Packet.  Basic command structure for communicating
 101    with controller. */
 102 struct mscp {
 103     unsigned char opcode: 3;            /* type of command */
 104     unsigned char xdir: 2;              /* data transfer direction */
 105     unsigned char dcn: 1;               /* disable disconnect */
 106     unsigned char ca: 1;                /* use cache (if available) */
 107     unsigned char sg: 1;                /* scatter/gather operation */
 108     unsigned char target_id: 3;         /* target SCSI id */
 109     unsigned char ch_no: 2;             /* SCSI channel (always 0 for 14f) */
 110     unsigned char lun: 3;               /* logical unit number */
 111     Longword transfer_data;             /* transfer data pointer */
 112     Longword transfer_data_length;      /* length in bytes */
 113     Longword command_link;              /* for linking command chains */
 114     unsigned char scsi_command_link_id; /* identifies command in chain */
 115     unsigned char number_of_sg_list;    /* (if sg is set) 8 bytes per list */
 116     unsigned char length_of_sense_byte;
 117     unsigned char length_of_scsi_cdbs;  /* 6, 10, or 12 */
 118     unsigned char scsi_cdbs[12];        /* SCSI commands */
 119     unsigned char adapter_status;       /* non-zero indicates HA error */
 120     unsigned char target_status;        /* non-zero indicates target error */
 121     Longword sense_data;
 122 };
 123 
 124 /* The 14F uses an array of unaligned 4-byte ints for its scatter/gather list. */
 125 typedef struct {
 126         unsigned long address;
 127         unsigned long num_bytes;
 128 } ultrastor_sg_list;
 129 
 130 /* This is our semaphore for mscp block availability */
 131 int mscp_free = TRUE;
 132 
 133 /* Allowed BIOS base addresses for 14f (NULL indicates reserved) */
 134 static const void *const bios_segment_table_14f[8] = {
 135     NULL,            (void *)0xC4000, (void *)0xC8000, (void *)0xCC000,
 136     (void *)0xD0000, (void *)0xD4000, (void *)0xD8000, (void *)0xDC000,
 137 };
 138 
 139 /* Allowed IRQs for 14f */
 140 static const unsigned char interrupt_table_14f[4] = { 15, 14, 11, 10 };
 141 
 142 /* Allowed DMA channels for 14f (0 indicates reserved) */
 143 static const unsigned char dma_channel_table_14f[4] = { 5, 6, 7, 0 };
 144 
 145 /* Head/sector mappings allowed by 14f */
 146 static const struct {
 147     unsigned char heads;
 148     unsigned char sectors;
 149 } mapping_table_14f[4] = { { 16, 63 }, { 64, 32 }, { 64, 63 }, { 0, 0 } };
 150 
 151 /* Subversions of the 14F */
 152 static const char *const subversion_names[] = { "14F", "34F" };
 153 
 154 /* Config info */
 155 static struct config config;
 156 
 157 /* Our index in the host adapter array maintained by higher-level driver */
 158 static int host_number;
 159 
 160 /* PORT_ADDRESS is first port address used for i/o of messages. */
 161 #ifdef PORT_OVERRIDE
 162 # define PORT_ADDRESS PORT_OVERRIDE
 163 #else
 164 # define PORT_ADDRESS (config.port_address)
 165 #endif
 166 
 167 static volatile int aborted = 0;
 168 
 169 #ifndef PORT_OVERRIDE
 170 /* ??? A probe of address 0x310 screws up NE2000 cards */
 171 static const unsigned short ultrastor_ports_14f[] = {
 172     0x330, 0x340, /*0x310,*/ 0x230, 0x240, 0x210, 0x130, 0x140,
 173 };
 174 #endif
 175 
 176 static void ultrastor_interrupt(int cpl);
 177 static inline void build_sg_list(Scsi_Cmnd *SCpnt);
 178 
 179 static void (*ultrastor_done)(Scsi_Cmnd *) = 0;
 180 static Scsi_Cmnd *SCint = NULL;
 181 
 182 int ultrastor_detect(int hostnum)
     /* [previous][next][first][last][top][bottom][index][help] */
 183 {
 184     size_t i;
 185     unsigned char in_byte, version_byte = 0;
 186     struct config_1 config_1;
 187     struct config_2 config_2;
 188 
 189 #if (ULTRASTOR_DEBUG & UD_DETECT)
 190     printk("US14F: detect: called\n");
 191 #endif
 192 
 193 #ifndef PORT_OVERRIDE
 194     PORT_ADDRESS = 0;
 195     for (i = 0; i < ARRAY_SIZE(ultrastor_ports_14f); i++) {
 196         PORT_ADDRESS = ultrastor_ports_14f[i];
 197 #endif
 198 
 199 #if (ULTRASTOR_DEBUG & UD_DETECT)
 200         printk("US14F: detect: testing port address %03X\n", PORT_ADDRESS);
 201 #endif
 202 
 203         in_byte = inb(PRODUCT_ID(PORT_ADDRESS + 0));
 204         if (in_byte != US14F_PRODUCT_ID_0) {
 205 #if (ULTRASTOR_DEBUG & UD_DETECT)
 206 # ifdef PORT_OVERRIDE
 207             printk("US14F: detect: wrong product ID 0 - %02X\n", in_byte);
 208 # else
 209             printk("US14F: detect: no adapter at port %03X\n", PORT_ADDRESS);
 210 # endif
 211 #endif
 212 #ifdef PORT_OVERRIDE
 213             return FALSE;
 214 #else
 215             continue;
 216 #endif
 217         }
 218         in_byte = inb(PRODUCT_ID(PORT_ADDRESS + 1));
 219         /* Only upper nibble is significant for Product ID 1 */
 220         if ((in_byte & 0xF0) != US14F_PRODUCT_ID_1) {
 221 #if (ULTRASTOR_DEBUG & UD_DETECT)
 222 # ifdef PORT_OVERRIDE
 223             printk("US14F: detect: wrong product ID 1 - %02X\n", in_byte);
 224 # else
 225             printk("US14F: detect: no adapter at port %03X\n", PORT_ADDRESS);
 226 # endif
 227 #endif
 228 #ifdef PORT_OVERRIDE
 229             return FALSE;
 230 #else
 231             continue;
 232 #endif
 233         }
 234         version_byte = in_byte;
 235 #ifndef PORT_OVERRIDE
 236         break;
 237     }
 238     if (i == ARRAY_SIZE(ultrastor_ports_14f)) {
 239 # if (ULTRASTOR_DEBUG & UD_DETECT)
 240         printk("US14F: detect: no port address found!\n");
 241 # endif
 242         return FALSE;
 243     }
 244 #endif
 245 
 246 #if (ULTRASTOR_DEBUG & UD_DETECT)
 247     printk("US14F: detect: adapter found at port address %03X\n",
 248            PORT_ADDRESS);
 249 #endif
 250 
 251     /* All above tests passed, must be the right thing.  Get some useful
 252        info. */
 253     *(char *)&config_1 = inb(CONFIG(PORT_ADDRESS + 0));
 254     *(char *)&config_2 = inb(CONFIG(PORT_ADDRESS + 1));
 255     config.bios_segment = bios_segment_table_14f[config_1.bios_segment];
 256     config.interrupt = interrupt_table_14f[config_1.interrupt];
 257     config.ha_scsi_id = config_2.ha_scsi_id;
 258     config.heads = mapping_table_14f[config_2.mapping_mode].heads;
 259     config.sectors = mapping_table_14f[config_2.mapping_mode].sectors;
 260     config.bios_drive_number = config_2.bios_drive_number;
 261     config.subversion = (version_byte & 0x0F);
 262     if (config.subversion == U34F)
 263         config.dma_channel = 0;
 264     else
 265         config.dma_channel = dma_channel_table_14f[config_1.dma_channel];
 266 
 267     if (!config.bios_segment) {
 268 #if (ULTRASTOR_DEBUG & UD_DETECT)
 269         printk("US14F: detect: not detected.\n");
 270 #endif
 271         return FALSE;
 272     }
 273 
 274     /* Final consistancy check, verify previous info. */
 275     if (config.subversion != U34F)
 276         if (!config.dma_channel || !(config_2.tfr_port & 0x2)) {
 277 #if (ULTRASTOR_DEBUG & UD_DETECT)
 278             printk("US14F: detect: consistancy check failed\n");
 279 #endif
 280             return FALSE;
 281         }
 282 
 283     /* If we were TRULY paranoid, we could issue a host adapter inquiry
 284        command here and verify the data returned.  But frankly, I'm
 285        exhausted! */
 286 
 287     /* Finally!  Now I'm satisfied... */
 288 #if (ULTRASTOR_DEBUG & UD_DETECT)
 289     printk("US14F: detect: detect succeeded\n"
 290            "  Port address: %03X\n"
 291            "  BIOS segment: %05X\n"
 292            "  Interrupt: %u\n"
 293            "  DMA channel: %u\n"
 294            "  H/A SCSI ID: %u\n"
 295            "  Subversion: %u\n",
 296            PORT_ADDRESS, config.bios_segment, config.interrupt,
 297            config.dma_channel, config.ha_scsi_id, config.subversion);
 298 #endif
 299     host_number = hostnum;
 300     scsi_hosts[hostnum].this_id = config.ha_scsi_id;
 301     scsi_hosts[hostnum].unchecked_isa_dma = (config.subversion != U34F);
 302 
 303     if (request_irq(config.interrupt, ultrastor_interrupt)) {
 304         printk("Unable to allocate IRQ%u for UltraStor controller.\n",
 305                config.interrupt);
 306         return FALSE;
 307     }
 308     if (config.dma_channel && request_dma(config.dma_channel)) {
 309         printk("Unable to allocate DMA channel %u for UltraStor controller.\n",
 310                config.dma_channel);
 311         free_irq(config.interrupt);
 312         return FALSE;
 313     }
 314         scsi_hosts[hostnum].sg_tablesize = ULTRASTOR_14F_MAX_SG;
 315         printk("UltraStor: scatter/gather enabled.  Using %d SG lists.\n", ULTRASTOR_14F_MAX_SG);
 316 
 317     return TRUE;
 318 }
 319 
 320 const char *ultrastor_info(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 321 {
 322     static char buf[64];
 323 
 324     (void)sprintf(buf, "UltraStor %s SCSI @ Port %03X BIOS %05X IRQ%u DMA%u\n",
 325                   ((config.subversion < ARRAY_SIZE(subversion_names))
 326                    ? subversion_names[config.subversion] : "14F?"),
 327                   PORT_ADDRESS, (int)config.bios_segment, config.interrupt,
 328                   config.dma_channel);
 329     return buf;
 330 }
 331 
 332 static struct mscp mscp = {
 333     OP_SCSI, DTD_SCSI, 0, 1, 0          /* This stuff doesn't change */
 334 };
 335 
 336 static inline void build_sg_list(Scsi_Cmnd *SCpnt)
     /* [previous][next][first][last][top][bottom][index][help] */
 337 {
 338         ultrastor_sg_list *sglist;
 339         struct scatterlist *sl;
 340         long transfer_length = 0;
 341         int i;
 342 
 343         sl = (struct scatterlist *) SCpnt->request_buffer;
 344         SCpnt->host_scribble = (unsigned char *) scsi_malloc(512);
 345         if (SCpnt->host_scribble == NULL)
 346                 /* Not sure what to do here; just panic for now */
 347                 panic("US14F: Can't allocate DMA buffer for scatter-gather list!\n");
 348         /* Save ourselves some casts; can eliminate when we don't have to look at it anymore! */
 349         sglist = (ultrastor_sg_list *) SCpnt->host_scribble;
 350         for (i = 0; i < SCpnt->use_sg; i++) {
 351                 sglist[i].address = sl[i].address;
 352                 sglist[i].num_bytes = sl[i].length;
 353                 transfer_length += sl[i].length;
 354         }
 355         mscp.number_of_sg_list = (char) SCpnt->use_sg;
 356         mscp.transfer_data = *(Longword *)&sglist;
 357         /* ??? May not be necessary.  Docs are unclear as to whether transfer length field is */
 358         /* ignored or whether it should be set to the total number of bytes of the transfer.  */
 359         mscp.transfer_data_length = *(Longword *)&transfer_length;
 360 }
 361 
 362 int ultrastor_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
     /* [previous][next][first][last][top][bottom][index][help] */
 363 {
 364     unsigned char in_byte;
 365 
 366 #if (ULTRASTOR_DEBUG & UD_COMMAND)
 367     printk("US14F: queuecommand: called\n");
 368 #endif
 369 
 370         /* We want to be sure that a command queued while another command   */
 371         /* is running doesn't overwrite the mscp block until the running    */
 372         /* command is finished.  mscp_free is set in the interrupt handler. */
 373         /* I'm not sure if the upper level driver will send another command */
 374         /* with a command pending; this is just insurance.                  */
 375         while (1) {
 376                 cli();
 377                 if (mscp_free) {
 378                         mscp_free = FALSE;
 379                         sti();
 380                         break;
 381                 }
 382                 sti();
 383         }
 384     mscp.opcode = OP_SCSI;
 385     mscp.xdir = DTD_SCSI;
 386     mscp.dcn = FALSE;
 387     /* Tape drives don't work properly if the cache is used.  The SCSI
 388        READ command for a tape doesn't have a block offset, and the adapter
 389        incorrectly assumes that all reads from the tape read the same
 390        blocks.  Results will depend on read buffer size and other disk
 391        activity. 
 392 
 393        ???  Which other device types should never use the cache?   */
 394     mscp.ca = scsi_devices[SCpnt->index].type != TYPE_TAPE;
 395     mscp.target_id = SCpnt->target;
 396     mscp.ch_no = 0;
 397     mscp.lun = SCpnt->lun;
 398         if (SCpnt->use_sg) {
 399                 /* Set scatter/gather flag in SCSI command packet */
 400                 mscp.sg = TRUE;
 401                 build_sg_list(SCpnt);
 402         }
 403         else {
 404                 /* Unset scatter/gather flag in SCSI command packet */
 405                 mscp.sg = FALSE;
 406                 mscp.transfer_data = *(Longword *)&SCpnt->request_buffer;
 407                 mscp.transfer_data_length = *(Longword *)&SCpnt->request_bufflen;
 408                 SCpnt->host_scribble = NULL;
 409         }
 410     memset(&mscp.command_link, 0, sizeof(mscp.command_link));   /*???*/
 411     mscp.scsi_command_link_id = 0;      /*???*/
 412     mscp.length_of_sense_byte = 0;      /*???*/
 413     mscp.length_of_scsi_cdbs = COMMAND_SIZE(*(unsigned char *)SCpnt->cmnd);
 414     memcpy(mscp.scsi_cdbs, SCpnt->cmnd, mscp.length_of_scsi_cdbs);
 415     mscp.adapter_status = 0;
 416     mscp.target_status = 0;
 417     memset(&mscp.sense_data, 0, sizeof(mscp.sense_data));       /*???*/
 418 
 419     /* Find free OGM slot (OGMINT bit is 0) */
 420     do
 421         in_byte = inb_p(LCL_DOORBELL_INTR(PORT_ADDRESS));
 422     while (!aborted && (in_byte & 1));
 423     if (aborted) {
 424 #if (ULTRASTOR_DEBUG & (UD_COMMAND | UD_ABORT))
 425         printk("US14F: queuecommand: aborted\n");
 426 #endif
 427         /* ??? is this right? */
 428         return (aborted << 16);
 429     }
 430 
 431     /* Store pointer in OGM address bytes */
 432     outb_p(BYTE(&mscp, 0), OGM_DATA_PTR(PORT_ADDRESS + 0));
 433     outb_p(BYTE(&mscp, 1), OGM_DATA_PTR(PORT_ADDRESS + 1));
 434     outb_p(BYTE(&mscp, 2), OGM_DATA_PTR(PORT_ADDRESS + 2));
 435     outb_p(BYTE(&mscp, 3), OGM_DATA_PTR(PORT_ADDRESS + 3));
 436 
 437     /* Issue OGM interrupt */
 438     outb_p(0x1, LCL_DOORBELL_INTR(PORT_ADDRESS));
 439 
 440     ultrastor_done = done;
 441     SCint = SCpnt;
 442 
 443 #if (ULTRASTOR_DEBUG & UD_COMMAND)
 444     printk("US14F: queuecommand: returning\n");
 445 #endif
 446 
 447     return 0;
 448 }
 449 
 450 int ultrastor_abort(Scsi_Cmnd *SCpnt, int code)
     /* [previous][next][first][last][top][bottom][index][help] */
 451 {
 452 #if (ULTRASTOR_DEBUG & UD_ABORT)
 453     printk("US14F: abort: called\n");
 454 #endif
 455 
 456     aborted = (code ? code : DID_ABORT);
 457 
 458         /* Free DMA buffer used for scatter/gather list */
 459         if (SCpnt->host_scribble)
 460                 scsi_free(SCpnt->host_scribble, 512);
 461 
 462         /* Free up mscp block for next command */
 463         mscp_free = TRUE;
 464 
 465 #if (ULTRASTOR_DEBUG & UD_ABORT)
 466     printk("US14F: abort: returning\n");
 467 #endif
 468 
 469     return 0;
 470 }
 471 
 472 int ultrastor_reset(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 473 {
 474 #if 0
 475     unsigned char in_byte;
 476 #endif
 477 
 478 #if (ULTRASTOR_DEBUG & UD_RESET)
 479     printk("US14F: reset: called\n");
 480 #endif
 481 
 482         /* ??? SCSI bus reset causes problems on some systems. */
 483 #if 0
 484     /* Issue SCSI BUS reset */
 485     outb_p(0x20, LCL_DOORBELL_INTR(PORT_ADDRESS));
 486 
 487     /* Wait for completion... */
 488     do
 489         in_byte = inb_p(LCL_DOORBELL_INTR(PORT_ADDRESS));
 490     while (in_byte & 0x20);
 491 
 492     aborted = DID_RESET;
 493 #endif
 494 
 495 #if (ULTRASTOR_DEBUG & UD_RESET)
 496     printk("US14F: reset: returning\n");
 497 #endif
 498     return 0;
 499 }
 500 
 501 int ultrastor_biosparam(int size, int dev, int *ip)
     /* [previous][next][first][last][top][bottom][index][help] */
 502 {
 503     unsigned int s = config.heads * config.sectors;
 504 
 505     ip[0] = config.heads;
 506     ip[1] = config.sectors;
 507     ip[2] = (size + (s - 1)) / s;
 508 /*    if (ip[2] > 1024)
 509         ip[2] = 1024; */
 510     return 0;
 511 }
 512 
 513 static void ultrastor_interrupt(int cpl)
     /* [previous][next][first][last][top][bottom][index][help] */
 514 {
 515 #if (ULTRASTOR_DEBUG & UD_INTERRUPT)
 516     printk("US14F: interrupt: called: status = %08X\n",
 517            (mscp.adapter_status << 16) | mscp.target_status);
 518 #endif
 519 
 520     if (ultrastor_done == 0)
 521         panic("US14F: interrupt: unexpected interrupt");
 522     else {
 523         void (*done)(Scsi_Cmnd *);
 524         Scsi_Cmnd *SCtmp;
 525 
 526         /* Save ultrastor_done locally and zero before calling.  This is needed
 527            as once we call done, we may get another command queued before this
 528            interrupt service routine can return. */
 529         done = ultrastor_done;
 530         ultrastor_done = 0;
 531         SCtmp = SCint;
 532 
 533         /* Clean ICM slot (set ICMINT bit to 0) */
 534         outb_p(0x1, SYS_DOORBELL_INTR(PORT_ADDRESS));
 535 
 536         /* Let the higher levels know that we're done */
 537         /* ??? status is wrong here... */
 538         SCtmp->result = (mscp.adapter_status << 16) | mscp.target_status;
 539 
 540         /* Free temp space used for scatter-gather list */
 541         if (SCtmp->host_scribble)
 542                 scsi_free(SCtmp->host_scribble, 512);
 543 
 544         /* Free up mscp block for next command */
 545         mscp_free = TRUE;
 546 
 547         done(SCtmp);
 548     }
 549 
 550 #if (ULTRASTOR_DEBUG & UD_INTERRUPT)
 551     printk("US14F: interrupt: returning\n");
 552 #endif
 553 }

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