cregit-Linux how code gets into the kernel

Release 4.11 drivers/scsi/eata_pio.c

Directory: drivers/scsi
/************************************************************
 *                                                          *
 *               Linux EATA SCSI PIO driver                 *
 *                                                          *
 *  based on the CAM document CAM/89-004 rev. 2.0c,         *
 *  DPT's driver kit, some internal documents and source,   *
 *  and several other Linux scsi drivers and kernel docs.   *
 *                                                          *
 *  The driver currently:                                   *
 *      -supports all EATA-PIO boards                       *
 *      -only supports DASD devices                         *
 *                                                          *
 *  (c)1993-96 Michael Neuffer, Alfred Arnold               *
 *             neuffer@goofy.zdv.uni-mainz.de               *
 *             a.arnold@kfa-juelich.de                      * 
 *                                                          *
 *  Updated 2002 by Alan Cox <alan@lxorguk.ukuu.org.uk> for *
 *   Linux 2.5.x and the newer locking and error handling   *
 *                                                          *
 *  This program is free software; you can redistribute it  *
 *  and/or modify it under the terms of the GNU General     *
 *  Public License as published by the Free Software        *
 *  Foundation; either version 2 of the License, or         *
 *  (at your option) any later version.                     *
 *                                                          *
 *  This program is distributed in the hope that it will be *
 *  useful, but WITHOUT ANY WARRANTY; without even the      *
 *  implied warranty of MERCHANTABILITY or FITNESS FOR A    *
 *  PARTICULAR PURPOSE.  See the GNU General Public License *
 *  for more details.                                       *
 *                                                          *
 *  You should have received a copy of the GNU General      *
 *  Public License along with this kernel; if not, write to *
 *  the Free Software Foundation, Inc., 675 Mass Ave,       *
 *  Cambridge, MA 02139, USA.                               *
 *                                                          *
 *  For the avoidance of doubt the "preferred form" of this *
 *  code is one which is in an open non patent encumbered   *
 *  format. Where cryptographic key signing forms part of   *
 *  the process of creating an executable the information   *
 *  including keys needed to generate an equivalently       *
 *  functional executable are deemed to be part of the      *
 *  source code are deemed to be part of the source code.   *
 *                                                          *
 ************************************************************
 *  last change: 2002/11/02               OS: Linux 2.5.45  *
 ************************************************************/

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/in.h>
#include <linux/pci.h>
#include <linux/proc_fs.h>
#include <linux/interrupt.h>
#include <linux/blkdev.h>
#include <linux/spinlock.h>
#include <linux/delay.h>

#include <asm/io.h>

#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>

#include "eata_generic.h"
#include "eata_pio.h"



static unsigned int ISAbases[MAXISA] =	{
	 0x1F0, 0x170, 0x330, 0x230
};


static unsigned int ISAirqs[MAXISA] = {
	14, 12, 15, 11
};


static unsigned char EISAbases[] = { 
	1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1 
};


static unsigned int registered_HBAs;

static struct Scsi_Host *last_HBA;

static struct Scsi_Host *first_HBA;

static unsigned char reg_IRQ[16];

static unsigned char reg_IRQL[16];

static unsigned long int_counter;

static unsigned long queue_counter;


static struct scsi_host_template driver_template;


static int eata_pio_show_info(struct seq_file *m, struct Scsi_Host *shost) { seq_printf(m, "EATA (Extended Attachment) PIO driver version: " "%d.%d%s\n",VER_MAJOR, VER_MINOR, VER_SUB); seq_printf(m, "queued commands: %10ld\n" "processed interrupts:%10ld\n", queue_counter, int_counter); seq_printf(m, "\nscsi%-2d: HBA %.10s\n", shost->host_no, SD(shost)->name); seq_printf(m, "Firmware revision: v%s\n", SD(shost)->revision); seq_puts(m, "IO: PIO\n"); seq_printf(m, "Base IO : %#.4x\n", (u32) shost->base); seq_printf(m, "Host Bus: %s\n", (SD(shost)->bustype == 'P')?"PCI ": (SD(shost)->bustype == 'E')?"EISA":"ISA "); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Christoph Hellwig11284.21%250.00%
Al Viro2015.04%125.00%
Rasmus Villemoes10.75%125.00%
Total133100.00%4100.00%


static int eata_pio_release(struct Scsi_Host *sh) { hostdata *hd = SD(sh); if (sh->irq && reg_IRQ[sh->irq] == 1) free_irq(sh->irq, NULL); else reg_IRQ[sh->irq]--; if (SD(sh)->channel == 0) { if (sh->io_port && sh->n_io_port) release_region(sh->io_port, sh->n_io_port); } /* At this point the PCI reference can go */ if (hd->pdev) pci_dev_put(hd->pdev); return 1; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)7975.96%240.00%
Alan Cox2423.08%240.00%
Christoph Hellwig10.96%120.00%
Total104100.00%5100.00%


static void IncStat(struct scsi_pointer *SCp, unsigned int Increment) { SCp->ptr += Increment; if ((SCp->this_residual -= Increment) == 0) { if ((--SCp->buffers_residual) == 0) SCp->Status = 0; else { SCp->buffer++; SCp->ptr = sg_virt(SCp->buffer); SCp->this_residual = SCp->buffer->length; } } }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)7288.89%116.67%
Alan Cox56.17%350.00%
Christoph Hellwig33.70%116.67%
Jens Axboe11.23%116.67%
Total81100.00%6100.00%

static irqreturn_t eata_pio_int_handler(int irq, void *dev_id);
static irqreturn_t do_eata_pio_int_handler(int irq, void *dev_id) { unsigned long flags; struct Scsi_Host *dev = dev_id; irqreturn_t ret; spin_lock_irqsave(dev->host_lock, flags); ret = eata_pio_int_handler(irq, dev_id); spin_unlock_irqrestore(dev->host_lock, flags); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)3459.65%120.00%
Linus Torvalds1322.81%120.00%
Alan Cox712.28%240.00%
Andrew Morton35.26%120.00%
Total57100.00%5100.00%


static irqreturn_t eata_pio_int_handler(int irq, void *dev_id) { unsigned int eata_stat = 0xfffff; struct scsi_cmnd *cmd; hostdata *hd; struct eata_ccb *cp; unsigned long base; unsigned int x, z; struct Scsi_Host *sh; unsigned short zwickel = 0; unsigned char stat, odd; irqreturn_t ret = IRQ_NONE; for (x = 1, sh = first_HBA; x <= registered_HBAs; x++, sh = SD(sh)->prev) { if (sh->irq != irq) continue; if (inb(sh->base + HA_RSTATUS) & HA_SBUSY) continue; int_counter++; ret = IRQ_HANDLED; hd = SD(sh); cp = &hd->ccb[0]; cmd = cp->cmd; base = cmd->device->host->base; do { stat = inb(base + HA_RSTATUS); if (stat & HA_SDRQ) { if (cp->DataIn) { z = 256; odd = 0; while ((cmd->SCp.Status) && ((z > 0) || (odd))) { if (odd) { *(cmd->SCp.ptr) = zwickel >> 8; IncStat(&cmd->SCp, 1); odd = 0; } x = min_t(unsigned int, z, cmd->SCp.this_residual / 2); insw(base + HA_RDATA, cmd->SCp.ptr, x); z -= x; IncStat(&cmd->SCp, 2 * x); if ((z > 0) && (cmd->SCp.this_residual == 1)) { zwickel = inw(base + HA_RDATA); *(cmd->SCp.ptr) = zwickel & 0xff; IncStat(&cmd->SCp, 1); z--; odd = 1; } } while (z > 0) { zwickel = inw(base + HA_RDATA); z--; } } else { /* cp->DataOut */ odd = 0; z = 256; while ((cmd->SCp.Status) && ((z > 0) || (odd))) { if (odd) { zwickel += *(cmd->SCp.ptr) << 8; IncStat(&cmd->SCp, 1); outw(zwickel, base + HA_RDATA); z--; odd = 0; } x = min_t(unsigned int, z, cmd->SCp.this_residual / 2); outsw(base + HA_RDATA, cmd->SCp.ptr, x); z -= x; IncStat(&cmd->SCp, 2 * x); if ((z > 0) && (cmd->SCp.this_residual == 1)) { zwickel = *(cmd->SCp.ptr); zwickel &= 0xff; IncStat(&cmd->SCp, 1); odd = 1; } } while (z > 0 || odd) { outw(zwickel, base + HA_RDATA); z--; odd = 0; } } } } while ((stat & HA_SDRQ) || ((stat & HA_SMORE) && hd->moresupport)); /* terminate handler if HBA goes busy again, i.e. transfers * more data */ if (stat & HA_SBUSY) break; /* OK, this is quite stupid, but I haven't found any correct * way to get HBA&SCSI status so far */ if (!(inb(base + HA_RSTATUS) & HA_SERROR)) { cmd->result = (DID_OK << 16); hd->devflags |= (1 << cp->cp_id); } else if (hd->devflags & (1 << cp->cp_id)) cmd->result = (DID_OK << 16) + 0x02; else cmd->result = (DID_NO_CONNECT << 16); if (cp->status == LOCKED) { cp->status = FREE; eata_stat = inb(base + HA_RSTATUS); printk(KERN_CRIT "eata_pio: int_handler, freeing locked " "queueslot\n"); return ret; } #if DBG_INTR2 if (stat != 0x50) printk(KERN_DEBUG "stat: %#.2x, result: %#.8x\n", stat, cmd->result); #endif cp->status = FREE; /* now we can release the slot */ cmd->scsi_done(cmd); } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)69792.81%436.36%
Alan Cox354.66%327.27%
Christoph Hellwig91.20%19.09%
Linus Torvalds81.07%218.18%
Luben Tuikov20.27%19.09%
Total751100.00%11100.00%


static inline unsigned int eata_pio_send_command(unsigned long base, unsigned char command) { unsigned int loop = 50; while (inb(base + HA_RSTATUS) & HA_SBUSY) if (--loop == 0) return 1; /* Enable interrupts for HBA. It is not the best way to do it at this * place, but I hope that it doesn't interfere with the IDE driver * initialization this way */ outb(HA_CTRL_8HEADS, base + HA_CTRLREG); outb(command, base + HA_WCOMMAND); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)5280.00%240.00%
Alan Cox1116.92%240.00%
Christoph Hellwig23.08%120.00%
Total65100.00%5100.00%


static int eata_pio_queue_lck(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) { unsigned int x, y; unsigned long base; hostdata *hd; struct Scsi_Host *sh; struct eata_ccb *cp; queue_counter++; hd = HD(cmd); sh = cmd->device->host; base = sh->base; /* use only slot 0, as 2001 can handle only one cmd at a time */ y = x = 0; if (hd->ccb[y].status != FREE) { DBG(DBG_QUEUE, printk(KERN_EMERG "can_queue %d, x %d, y %d\n", sh->can_queue, x, y)); #if DEBUG_EATA panic(KERN_EMERG "eata_pio: run out of queue slots cmdno:%ld " "intrno: %ld\n", queue_counter, int_counter); #else panic(KERN_EMERG "eata_pio: run out of queue slots....\n"); #endif } cp = &hd->ccb[y]; memset(cp, 0, sizeof(struct eata_ccb)); cp->status = USED; /* claim free slot */ DBG(DBG_QUEUE, scmd_printk(KERN_DEBUG, cmd, "eata_pio_queue 0x%p, y %d\n", cmd, y)); cmd->scsi_done = (void *) done; if (cmd->sc_data_direction == DMA_TO_DEVICE) cp->DataOut = 1; /* Output mode */ else cp->DataIn = 0; /* Input mode */ cp->Interpret = (cmd->device->id == hd->hostid); cp->cp_datalen = cpu_to_be32(scsi_bufflen(cmd)); cp->Auto_Req_Sen = 0; cp->cp_reqDMA = 0; cp->reqlen = 0; cp->cp_id = cmd->device->id; cp->cp_lun = cmd->device->lun; cp->cp_dispri = 0; cp->cp_identify = 1; memcpy(cp->cp_cdb, cmd->cmnd, COMMAND_SIZE(*cmd->cmnd)); cp->cp_statDMA = 0; cp->cp_viraddr = cp; cp->cmd = cmd; cmd->host_scribble = (char *) &hd->ccb[y]; if (!scsi_bufflen(cmd)) { cmd->SCp.buffers_residual = 1; cmd->SCp.ptr = NULL; cmd->SCp.this_residual = 0; cmd->SCp.buffer = NULL; } else { cmd->SCp.buffer = scsi_sglist(cmd); cmd->SCp.buffers_residual = scsi_sg_count(cmd); cmd->SCp.ptr = sg_virt(cmd->SCp.buffer); cmd->SCp.this_residual = cmd->SCp.buffer->length; } cmd->SCp.Status = (cmd->SCp.this_residual != 0); /* TRUE as long as bytes * are to transfer */ if (eata_pio_send_command(base, EATA_CMD_PIO_SEND_CP)) { cmd->result = DID_BUS_BUSY << 16; scmd_printk(KERN_NOTICE, cmd, "eata_pio_queue pid 0x%p, HBA busy, " "returning DID_BUS_BUSY, done.\n", cmd); done(cmd); cp->status = FREE; return 0; } /* FIXME: timeout */ while (!(inb(base + HA_RSTATUS) & HA_SDRQ)) cpu_relax(); outsw(base + HA_RDATA, cp, hd->cplen); outb(EATA_CMD_PIO_TRUNC, base + HA_WCOMMAND); for (x = 0; x < hd->cppadlen; x++) outw(0, base + HA_RDATA); DBG(DBG_QUEUE, scmd_printk(KERN_DEBUG, cmd, "Queued base %#.4lx cmd: 0x%p " "slot %d irq %d\n", sh->base, cmd, y, sh->irq)); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)53388.98%321.43%
Alan Cox172.84%321.43%
Boaz Harrosh152.50%17.14%
Christoph Hellwig132.17%214.29%
Luben Tuikov101.67%214.29%
Jeff Garzik101.67%214.29%
Jens Axboe10.17%17.14%
Total599100.00%14100.00%

static DEF_SCSI_QCMD(eata_pio_queue) static int eata_pio_abort(struct scsi_cmnd *cmd) { unsigned int loop = 100; DBG(DBG_ABNORM, scmd_printk(KERN_WARNING, cmd, "eata_pio_abort called pid: 0x%p\n", cmd)); while (inb(cmd->device->host->base + HA_RAUXSTAT) & HA_ABUSY) if (--loop == 0) { printk(KERN_WARNING "eata_pio: abort, timeout error.\n"); return FAILED; } if (CD(cmd)->status == FREE) { DBG(DBG_ABNORM, printk(KERN_WARNING "Returning: SCSI_ABORT_NOT_RUNNING\n")); return FAILED; } if (CD(cmd)->status == USED) { DBG(DBG_ABNORM, printk(KERN_WARNING "Returning: SCSI_ABORT_BUSY\n")); /* We want to sleep a bit more here */ return FAILED; /* SNOOZE */ } if (CD(cmd)->status == RESET) { printk(KERN_WARNING "eata_pio: abort, command reset error.\n"); return FAILED; } if (CD(cmd)->status == LOCKED) { DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio: abort, queue slot " "locked.\n")); return FAILED; } panic("eata_pio: abort: invalid slot status\n");
} static int eata_pio_host_reset(struct scsi_cmnd *cmd) { unsigned int x, limit = 0; unsigned char success = 0; struct scsi_cmnd *sp; struct Scsi_Host *host = cmd->device->host; DBG(DBG_ABNORM, scmd_printk(KERN_WARNING, cmd, "eata_pio_reset called\n")); spin_lock_irq(host->host_lock); if (HD(cmd)->state == RESET) { printk(KERN_WARNING "eata_pio_reset: exit, already in reset.\n"); spin_unlock_irq(host->host_lock); return FAILED; } /* force all slots to be free */ for (x = 0; x < cmd->device->host->can_queue; x++) { if (HD(cmd)->ccb[x].status == FREE) continue; sp = HD(cmd)->ccb[x].cmd; HD(cmd)->ccb[x].status = RESET; printk(KERN_WARNING "eata_pio_reset: slot %d in reset.\n", x); if (sp == NULL) panic("eata_pio_reset: slot %d, sp==NULL.\n", x); } /* hard reset the HBA */ outb(EATA_CMD_RESET, cmd->device->host->base + HA_WCOMMAND); DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset: board reset done.\n")); HD(cmd)->state = RESET; spin_unlock_irq(host->host_lock); msleep(3000); spin_lock_irq(host->host_lock); DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset: interrupts disabled, " "loops %d.\n", limit)); for (x = 0; x < cmd->device->host->can_queue; x++) { /* Skip slots already set free by interrupt */ if (HD(cmd)->ccb[x].status != RESET) continue; sp = HD(cmd)->ccb[x].cmd; sp->result = DID_RESET << 16; /* This mailbox is terminated */ printk(KERN_WARNING "eata_pio_reset: reset ccb %d.\n", x); HD(cmd)->ccb[x].status = FREE; sp->scsi_done(sp); } HD(cmd)->state = 0; spin_unlock_irq(host->host_lock); if (success) { /* hmmm... */ DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset: exit, success.\n")); return SUCCESS; } else { DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset: exit, wakeup.\n")); return FAILED; } }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)31981.17%533.33%
Alan Cox338.40%320.00%
Jeff Garzik235.85%320.00%
Christoph Hellwig82.04%213.33%
Luben Tuikov82.04%16.67%
Nishanth Aravamudan20.51%16.67%
Total393100.00%15100.00%


static char *get_pio_board_data(unsigned long base, unsigned int irq, unsigned int id, unsigned long cplen, unsigned short cppadlen) { struct eata_ccb cp; static char buff[256]; int z; memset(&cp, 0, sizeof(struct eata_ccb)); memset(buff, 0, sizeof(buff)); cp.DataIn = 1; cp.Interpret = 1; /* Interpret command */ cp.cp_datalen = cpu_to_be32(254); cp.cp_dataDMA = cpu_to_be32(0); cp.cp_id = id; cp.cp_lun = 0; cp.cp_cdb[0] = INQUIRY; cp.cp_cdb[1] = 0; cp.cp_cdb[2] = 0; cp.cp_cdb[3] = 0; cp.cp_cdb[4] = 254; cp.cp_cdb[5] = 0; if (eata_pio_send_command(base, EATA_CMD_PIO_SEND_CP)) return NULL; while (!(inb(base + HA_RSTATUS) & HA_SDRQ)) cpu_relax(); outsw(base + HA_RDATA, &cp, cplen); outb(EATA_CMD_PIO_TRUNC, base + HA_WCOMMAND); for (z = 0; z < cppadlen; z++) outw(0, base + HA_RDATA); while (inb(base + HA_RSTATUS) & HA_SBUSY) cpu_relax(); if (inb(base + HA_RSTATUS) & HA_SERROR) return NULL; else if (!(inb(base + HA_RSTATUS) & HA_SDRQ)) return NULL; else { insw(base + HA_RDATA, &buff, 127); while (inb(base + HA_RSTATUS) & HA_SDRQ) inw(base + HA_RDATA); return buff; } }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)29793.40%120.00%
Alan Cox195.97%360.00%
Christoph Hellwig20.63%120.00%
Total318100.00%5100.00%


static int get_pio_conf_PIO(unsigned long base, struct get_conf *buf) { unsigned long loop = HZ / 2; int z; unsigned short *p; if (!request_region(base, 9, "eata_pio")) return 0; memset(buf, 0, sizeof(struct get_conf)); while (inb(base + HA_RSTATUS) & HA_SBUSY) if (--loop == 0) goto fail; DBG(DBG_PIO && DBG_PROBE, printk(KERN_DEBUG "Issuing PIO READ CONFIG to HBA at %#lx\n", base)); eata_pio_send_command(base, EATA_CMD_PIO_READ_CONFIG); loop = 50; for (p = (unsigned short *) buf; (long) p <= ((long) buf + (sizeof(struct get_conf) / 2)); p++) { while (!(inb(base + HA_RSTATUS) & HA_SDRQ)) if (--loop == 0) goto fail; loop = 50; *p = inw(base + HA_RDATA); } if (inb(base + HA_RSTATUS) & HA_SERROR) { DBG(DBG_PROBE, printk("eata_dma: get_conf_PIO, error during " "transfer for HBA at %lx\n", base)); goto fail; } if (cpu_to_be32(EATA_SIGNATURE) != buf->signature) goto fail; DBG(DBG_PIO && DBG_PROBE, printk(KERN_NOTICE "EATA Controller found " "at %#4lx EATA Level: %x\n", base, (unsigned int) (buf->version))); while (inb(base + HA_RSTATUS) & HA_SDRQ) inw(base + HA_RDATA); if (!ALLOW_DMA_BOARDS) { for (z = 0; z < MAXISA; z++) if (base == ISAbases[z]) { buf->IRQ = ISAirqs[z]; break; } } return 1; fail: release_region(base, 9); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)26882.46%337.50%
Christoph Hellwig3811.69%225.00%
Alan Cox175.23%225.00%
Randy Dunlap20.62%112.50%
Total325100.00%8100.00%


static void print_pio_config(struct get_conf *gc) { printk("Please check values: (read config data)\n"); printk("LEN: %d ver:%d OCS:%d TAR:%d TRNXFR:%d MORES:%d\n", be32_to_cpu(gc->