ultrastor.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,205 行 · 第 1/3 页

C
1,205
字号
    /* Issue OGM interrupt */    if (config.slot) {	/* Write OGM command register on 24F */	outb(1, config.ogm_address - 1);	outb(0x2, LCL_DOORBELL_INTR(config.doorbell_address));    } else {	outb(0x1, LCL_DOORBELL_INTR(config.doorbell_address));    }#if (ULTRASTOR_DEBUG & UD_COMMAND)    printk("USx4F: queuecommand: returning\n");#endif    return 0;}/* This code must deal with 2 cases:   1. The command has not been written to the OGM.  In this case, set   the abort flag and return.   2. The command has been written to the OGM and is stuck somewhere in   the adapter.   2a.  On a 24F, ask the adapter to abort the command.  It will interrupt   when it does.   2b.  Call the command's done procedure. */static int ultrastor_abort(Scsi_Cmnd *SCpnt){#if ULTRASTOR_DEBUG & UD_ABORT    char out[108];    unsigned char icm_status = 0, ogm_status = 0;    unsigned int icm_addr = 0, ogm_addr = 0;#endif    unsigned int mscp_index;    unsigned char old_aborted;    unsigned long flags;    void (*done)(Scsi_Cmnd *);    struct Scsi_Host *host = SCpnt->device->host;    if(config.slot)       return FAILED;  /* Do not attempt an abort for the 24f */          /* Simple consistency checking */    if(!SCpnt->host_scribble)      return FAILED;    mscp_index = ((struct mscp *)SCpnt->host_scribble) - config.mscp;    if (mscp_index >= ULTRASTOR_MAX_CMDS)	panic("Ux4F aborting invalid MSCP");#if ULTRASTOR_DEBUG & UD_ABORT    if (config.slot)      {	int port0 = (config.slot << 12) | 0xc80;	int i;	unsigned long flags;		spin_lock_irqsave(host->host_lock, flags);	strcpy(out, "OGM %d:%x ICM %d:%x ports:  ");	for (i = 0; i < 16; i++)	  {	    unsigned char p = inb(port0 + i);	    out[28 + i * 3] = "0123456789abcdef"[p >> 4];	    out[29 + i * 3] = "0123456789abcdef"[p & 15];	    out[30 + i * 3] = ' ';	  }	out[28 + i * 3] = '\n';	out[29 + i * 3] = 0;	ogm_status = inb(port0 + 22);	ogm_addr = (unsigned int)isa_bus_to_virt(inl(port0 + 23));	icm_status = inb(port0 + 27);	icm_addr = (unsigned int)isa_bus_to_virt(inl(port0 + 28));	spin_lock_irqsave(host->host_lock, flags);      }    /* First check to see if an interrupt is pending.  I suspect the SiS       chipset loses interrupts.  (I also suspect is mangles data, but       one bug at a time... */    if (config.slot ? inb(config.icm_address - 1) == 2 :	(inb(SYS_DOORBELL_INTR(config.doorbell_address)) & 1))      {	printk("Ux4F: abort while completed command pending\n");		spin_lock_irqsave(host->host_lock, flags);	/* FIXME: Ewww... need to think about passing host around properly */	ultrastor_interrupt(0, NULL, NULL);	spin_unlock_irqrestore(host->host_lock, flags);	return SUCCESS;      }#endif    old_aborted = xchgb(DID_ABORT, &config.aborted[mscp_index]);    /* aborted == 0xff is the signal that queuecommand has not yet sent       the command.  It will notice the new abort flag and fail.  */    if (old_aborted == 0xff)	return SUCCESS;    /* On 24F, send an abort MSCP request.  The adapter will interrupt       and the interrupt handler will call done.  */    if (config.slot && inb(config.ogm_address - 1) == 0)      {	unsigned long flags;	spin_lock_irqsave(host->host_lock, flags);	outl(isa_virt_to_bus(&config.mscp[mscp_index]), config.ogm_address);	udelay(8);	outb(0x80, config.ogm_address - 1);	outb(0x2, LCL_DOORBELL_INTR(config.doorbell_address));#if ULTRASTOR_DEBUG & UD_ABORT	log_ultrastor_abort(&config, mscp_index);	printk(out, ogm_status, ogm_addr, icm_status, icm_addr);#endif	spin_unlock_irqrestore(host->host_lock, flags);	/* FIXME: add a wait for the abort to complete */	return SUCCESS;      }#if ULTRASTOR_DEBUG & UD_ABORT    log_ultrastor_abort(&config, mscp_index);#endif    /* Can't request a graceful abort.  Either this is not a 24F or       the OGM is busy.  Don't free the command -- the adapter might       still be using it.  Setting SCint = 0 causes the interrupt       handler to ignore the command.  */    /* FIXME - devices that implement soft resets will still be running       the command after a bus reset.  We would probably rather leave       the command in the queue.  The upper level code will automatically       leave the command in the active state instead of requeueing it. ERY */#if ULTRASTOR_DEBUG & UD_ABORT    if (config.mscp[mscp_index].SCint != SCpnt)	printk("abort: command mismatch, %p != %p\n",	       config.mscp[mscp_index].SCint, SCpnt);#endif    if (config.mscp[mscp_index].SCint == 0)	return SCSI_ABORT_NOT_RUNNING;    if (config.mscp[mscp_index].SCint != SCpnt) panic("Bad abort");    config.mscp[mscp_index].SCint = NULL;    done = config.mscp[mscp_index].done;    config.mscp[mscp_index].done = NULL;    SCpnt->result = DID_ABORT << 16;        /* Take the host lock to guard against scsi layer re-entry */    spin_lock_irqsave(host->host_lock, flags);    done(SCpnt);    spin_unlock_irqrestore(host->host_lock, flags);    /* Need to set a timeout here in case command never completes.  */    return SUCCESS;}static int ultrastor_host_reset(Scsi_Cmnd * SCpnt){    unsigned long flags;    int i;    struct Scsi_Host *host = SCpnt->device->host;    #if (ULTRASTOR_DEBUG & UD_RESET)    printk("US14F: reset: called\n");#endif    if(config.slot)    	return FAILED;    spin_lock_irqsave(host->host_lock, flags);    /* Reset the adapter and SCSI bus.  The SCSI bus reset can be       inhibited by clearing ultrastor_bus_reset before probe.  */    outb(0xc0, LCL_DOORBELL_INTR(config.doorbell_address));    if (config.slot)      {	outb(0, config.ogm_address - 1);	outb(0, config.icm_address - 1);      }#if ULTRASTOR_MAX_CMDS == 1    if (config.mscp_busy && config.mscp->done && config.mscp->SCint)      {	config.mscp->SCint->result = DID_RESET << 16;	config.mscp->done(config.mscp->SCint);      }    config.mscp->SCint = 0;#else    for (i = 0; i < ULTRASTOR_MAX_CMDS; i++)      {	if (! (config.mscp_free & (1 << i)) &&	    config.mscp[i].done && config.mscp[i].SCint)	  {	    config.mscp[i].SCint->result = DID_RESET << 16;	    config.mscp[i].done(config.mscp[i].SCint);	    config.mscp[i].done = NULL;	  }	config.mscp[i].SCint = NULL;      }#endif    /* FIXME - if the device implements soft resets, then the command       will still be running.  ERY                Even bigger deal with new_eh!      */    memset((unsigned char *)config.aborted, 0, sizeof config.aborted);#if ULTRASTOR_MAX_CMDS == 1    config.mscp_busy = 0;#else    config.mscp_free = ~0;#endif    spin_unlock_irqrestore(host->host_lock, flags);    return SCSI_RESET_SUCCESS;}int ultrastor_biosparam(struct scsi_device *sdev, struct block_device *bdev,		sector_t capacity, int * dkinfo){    int size = capacity;    unsigned int s = config.heads * config.sectors;    dkinfo[0] = config.heads;    dkinfo[1] = config.sectors;    dkinfo[2] = size / s;	/* Ignore partial cylinders */#if 0    if (dkinfo[2] > 1024)	dkinfo[2] = 1024;#endif    return 0;}static void ultrastor_interrupt(int irq, void *dev_id, struct pt_regs *regs){    unsigned int status;#if ULTRASTOR_MAX_CMDS > 1    unsigned int mscp_index;#endif    struct mscp *mscp;    void (*done)(Scsi_Cmnd *);    Scsi_Cmnd *SCtmp;#if ULTRASTOR_MAX_CMDS == 1    mscp = &config.mscp[0];#else    mscp = (struct mscp *)isa_bus_to_virt(inl(config.icm_address));    mscp_index = mscp - config.mscp;    if (mscp_index >= ULTRASTOR_MAX_CMDS) {	printk("Ux4F interrupt: bad MSCP address %x\n", (unsigned int) mscp);	/* A command has been lost.  Reset and report an error	   for all commands.  */	ultrastor_host_reset(dev_id);	return;    }#endif    /* Clean ICM slot (set ICMINT bit to 0) */    if (config.slot) {	unsigned char icm_status = inb(config.icm_address - 1);#if ULTRASTOR_DEBUG & (UD_INTERRUPT|UD_ERROR|UD_ABORT)	if (icm_status != 1 && icm_status != 2)	    printk("US24F: ICM status %x for MSCP %d (%x)\n", icm_status,		   mscp_index, (unsigned int) mscp);#endif	/* The manual says clear interrupt then write 0 to ICM status.	   This seems backwards, but I'll do it anyway.  --jfc */	outb(2, SYS_DOORBELL_INTR(config.doorbell_address));	outb(0, config.icm_address - 1);	if (icm_status == 4) {	    printk("UltraStor abort command failed\n");	    return;	}	if (icm_status == 3) {	    void (*done)(Scsi_Cmnd *) = mscp->done;	    if (done) {		mscp->done = NULL;		mscp->SCint->result = DID_ABORT << 16;		done(mscp->SCint);	    }	    return;	}    } else {	outb(1, SYS_DOORBELL_INTR(config.doorbell_address));    }    SCtmp = mscp->SCint;    mscp->SCint = NULL;    if (SCtmp == 0)      {#if ULTRASTOR_DEBUG & (UD_ABORT|UD_INTERRUPT)	printk("MSCP %d (%x): no command\n", mscp_index, (unsigned int) mscp);#endif	#if ULTRASTOR_MAX_CMDS == 1	config.mscp_busy = FALSE;#else	set_bit(mscp_index, &config.mscp_free);#endif	config.aborted[mscp_index] = 0;	return;      }    /* Save done locally and zero before calling.  This is needed as       once we call done, we may get another command queued before this       interrupt service routine can return. */    done = mscp->done;    mscp->done = NULL;    /* Let the higher levels know that we're done */    switch (mscp->adapter_status)      {      case 0:	status = DID_OK << 16;	break;      case 0x01:	/* invalid command */      case 0x02:	/* invalid parameters */      case 0x03:	/* invalid data list */      default:	status = DID_ERROR << 16;	break;      case 0x84:	/* SCSI bus abort */	status = DID_ABORT << 16;	break;      case 0x91:	status = DID_TIME_OUT << 16;	break;      }    SCtmp->result = status | mscp->target_status;    SCtmp->host_scribble = NULL;    /* Free up mscp block for next command */#if ULTRASTOR_MAX_CMDS == 1    config.mscp_busy = FALSE;#else    set_bit(mscp_index, &config.mscp_free);#endif#if ULTRASTOR_DEBUG & (UD_ABORT|UD_INTERRUPT)    if (config.aborted[mscp_index])	printk("Ux4 interrupt: MSCP %d (%x) aborted = %d\n",	       mscp_index, (unsigned int) mscp, config.aborted[mscp_index]);#endif    config.aborted[mscp_index] = 0;    if (done)	done(SCtmp);    else	printk("US14F: interrupt: unexpected interrupt\n");    if (config.slot ? inb(config.icm_address - 1) :       (inb(SYS_DOORBELL_INTR(config.doorbell_address)) & 1))#if (ULTRASTOR_DEBUG & UD_MULTI_CMD)      printk("Ux4F: multiple commands completed\n");#else      ;#endif#if (ULTRASTOR_DEBUG & UD_INTERRUPT)    printk("USx4F: interrupt: returning\n");#endif}static irqreturn_t do_ultrastor_interrupt(int irq, void *dev_id,						struct pt_regs *regs){    unsigned long flags;    struct Scsi_Host *dev = dev_id;        spin_lock_irqsave(dev->host_lock, flags);    ultrastor_interrupt(irq, dev_id, regs);    spin_unlock_irqrestore(dev->host_lock, flags);    return IRQ_HANDLED;}MODULE_LICENSE("GPL");static Scsi_Host_Template driver_template = {	.name              = "UltraStor 14F/24F/34F",	.detect            = ultrastor_detect,	.release	   = ultrastor_release,	.info              = ultrastor_info,	.queuecommand      = ultrastor_queuecommand,	.eh_abort_handler  = ultrastor_abort,	.eh_host_reset_handler  = ultrastor_host_reset,		.bios_param        = ultrastor_biosparam,	.can_queue         = ULTRASTOR_MAX_CMDS,	.sg_tablesize      = ULTRASTOR_14F_MAX_SG,	.cmd_per_lun       = ULTRASTOR_MAX_CMDS_PER_LUN,	.unchecked_isa_dma = 1,	.use_clustering    = ENABLE_CLUSTERING,};#include "scsi_module.c"

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?