⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ppa.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* ppa.c   --  low level driver for the IOMEGA PPA3  * parallel port SCSI host adapter. *  * (The PPA3 is the embedded controller in the ZIP drive.) *  * (c) 1995,1996 Grant R. Guenther, grant@torque.net, * under the terms of the GNU Public License. *  * Current Maintainer: David Campbell (Perth, Western Australia, GMT+0800) *                     campbell@torque.net */#include <linux/config.h>/* The following #define is to avoid a clash with hosts.c */#define PPA_CODE 1#include <linux/blk.h>#include <asm/io.h>#include <linux/parport.h>#include "sd.h"#include "hosts.h"int ppa_release(struct Scsi_Host *);static void ppa_reset_pulse(unsigned int base);typedef struct {    struct pardevice *dev;	/* Parport device entry         */    int base;			/* Actual port address          */    int mode;			/* Transfer mode                */    int host;			/* Host number (for proc)       */    Scsi_Cmnd *cur_cmd;		/* Current queued command       */    struct tq_struct ppa_tq;	/* Polling interupt stuff       */    unsigned long jstart;	/* Jiffies at start             */    unsigned long recon_tmo;    /* How many usecs to wait for reconnection (6th bit) */    unsigned int failed:1;	/* Failure flag                 */    unsigned int p_busy:1;	/* Parport sharing busy flag    */} ppa_struct;#define PPA_EMPTY	\{	dev:		NULL,		\	base:		-1,		\	mode:		PPA_AUTODETECT,	\	host:		-1,		\	cur_cmd:	NULL,		\	ppa_tq:		{ routine: ppa_interrupt },	\	jstart:		0,		\	recon_tmo:      PPA_RECON_TMO,	\	failed:		0,		\	p_busy:		0		\}#include  "ppa.h"#define NO_HOSTS 4static ppa_struct ppa_hosts[NO_HOSTS] ={PPA_EMPTY, PPA_EMPTY, PPA_EMPTY, PPA_EMPTY};#define PPA_BASE(x)	ppa_hosts[(x)].basevoid ppa_wakeup(void *ref){    ppa_struct *ppa_dev = (ppa_struct *) ref;    if (!ppa_dev->p_busy)	return;    if (parport_claim(ppa_dev->dev)) {	printk("ppa: bug in ppa_wakeup\n");	return;    }    ppa_dev->p_busy = 0;    ppa_dev->base = ppa_dev->dev->port->base;    if (ppa_dev->cur_cmd)	ppa_dev->cur_cmd->SCp.phase++;    return;}int ppa_release(struct Scsi_Host *host){    int host_no = host->unique_id;    printk("Releasing ppa%i\n", host_no);    parport_unregister_device(ppa_hosts[host_no].dev);    return 0;}static int ppa_pb_claim(int host_no){    if (parport_claim(ppa_hosts[host_no].dev)) {	ppa_hosts[host_no].p_busy = 1;	return 1;    }    if (ppa_hosts[host_no].cur_cmd)	ppa_hosts[host_no].cur_cmd->SCp.phase++;    return 0;}#define ppa_pb_release(x) parport_release(ppa_hosts[(x)].dev)/*************************************************************************** *                   Parallel port probing routines                        * ***************************************************************************/static Scsi_Host_Template driver_template = PPA;#include  "scsi_module.c"/* * Start of Chipset kludges */int ppa_detect(Scsi_Host_Template * host){    struct Scsi_Host *hreg;    int ports;    int i, nhosts, try_again;    struct parport *pb;    /*     * unlock to allow the lowlevel parport driver to probe     * the irqs     */    spin_unlock_irq(&io_request_lock);    pb = parport_enumerate();    printk("ppa: Version %s\n", PPA_VERSION);    nhosts = 0;    try_again = 0;    if (!pb) {	printk("ppa: parport reports no devices.\n");	spin_lock_irq(&io_request_lock);	return 0;    }  retry_entry:    for (i = 0; pb; i++, pb = pb->next) {	int modes, ppb, ppb_hi;	ppa_hosts[i].dev =	    parport_register_device(pb, "ppa", NULL, ppa_wakeup,				    NULL, 0, (void *) &ppa_hosts[i]);	if (!ppa_hosts[i].dev)	    continue;	/* Claim the bus so it remembers what we do to the control	 * registers. [ CTR and ECP ]	 */	if (ppa_pb_claim(i)) {	    unsigned long now = jiffies;	    while (ppa_hosts[i].p_busy) {		schedule();	/* We are safe to schedule here */		if (time_after(jiffies, now + 3 * HZ)) {		    printk(KERN_ERR "ppa%d: failed to claim parport because a "		      "pardevice is owning the port for too longtime!\n",			   i);		    spin_lock_irq(&io_request_lock);		    return 0;		}	    }	}	ppb = PPA_BASE(i) = ppa_hosts[i].dev->port->base;	ppb_hi =  ppa_hosts[i].dev->port->base_hi;	w_ctr(ppb, 0x0c);	modes = ppa_hosts[i].dev->port->modes;	/* Mode detection works up the chain of speed	 * This avoids a nasty if-then-else-if-... tree	 */	ppa_hosts[i].mode = PPA_NIBBLE;	if (modes & PARPORT_MODE_TRISTATE)	    ppa_hosts[i].mode = PPA_PS2;	if (modes & PARPORT_MODE_ECP) {	    w_ecr(ppb_hi, 0x20);	    ppa_hosts[i].mode = PPA_PS2;	}	if ((modes & PARPORT_MODE_EPP) && (modes & PARPORT_MODE_ECP))	    w_ecr(ppb_hi, 0x80);	/* Done configuration */	ppa_pb_release(i);	if (ppa_init(i)) {	    parport_unregister_device(ppa_hosts[i].dev);	    continue;	}	/* now the glue ... */	switch (ppa_hosts[i].mode) {	case PPA_NIBBLE:	    ports = 3;	    break;	case PPA_PS2:	    ports = 3;	    break;	case PPA_EPP_8:	case PPA_EPP_16:	case PPA_EPP_32:	    ports = 8;	    break;	default:		/* Never gets here */	    continue;	}	host->can_queue = PPA_CAN_QUEUE;	host->sg_tablesize = ppa_sg;	hreg = scsi_register(host, 0);	if(hreg == NULL)		continue;	hreg->io_port = pb->base;	hreg->n_io_port = ports;	hreg->dma_channel = -1;	hreg->unique_id = i;	ppa_hosts[i].host = hreg->host_no;	nhosts++;    }    if (nhosts == 0) {	if (try_again == 1) {	    printk("WARNING - no ppa compatible devices found.\n");	    printk("  As of 31/Aug/1998 Iomega started shipping parallel\n");	    printk("  port ZIP drives with a different interface which is\n");	    printk("  supported by the imm (ZIP Plus) driver. If the\n");	    printk("  cable is marked with \"AutoDetect\", this is what has\n");	    printk("  happened.\n");	    return 0;	    spin_lock_irq(&io_request_lock);	}	try_again = 1;	goto retry_entry;    } else {	spin_lock_irq(&io_request_lock);	return 1;		/* return number of hosts detected */    }}/* This is to give the ppa driver a way to modify the timings (and other * parameters) by writing to the /proc/scsi/ppa/0 file. * Very simple method really... (To simple, no error checking :( ) * Reason: Kernel hackers HATE having to unload and reload modules for * testing... * Also gives a method to use a script to obtain optimum timings (TODO) */static inline int ppa_proc_write(int hostno, char *buffer, int length){    unsigned long x;    if ((length > 5) && (strncmp(buffer, "mode=", 5) == 0)) {	x = simple_strtoul(buffer + 5, NULL, 0);	ppa_hosts[hostno].mode = x;	return length;    }    if ((length > 10) && (strncmp(buffer, "recon_tmo=", 10) == 0)) {	x = simple_strtoul(buffer + 10, NULL, 0);	ppa_hosts[hostno].recon_tmo = x;        printk("ppa: recon_tmo set to %ld\n", x);	return length;    }    printk("ppa /proc: invalid variable\n");    return (-EINVAL);}int ppa_proc_info(char *buffer, char **start, off_t offset,		  int length, int hostno, int inout){    int i;    int len = 0;    for (i = 0; i < 4; i++)	if (ppa_hosts[i].host == hostno)	    break;    if (inout)	return ppa_proc_write(i, buffer, length);    len += sprintf(buffer + len, "Version : %s\n", PPA_VERSION);    len += sprintf(buffer + len, "Parport : %s\n", ppa_hosts[i].dev->port->name);    len += sprintf(buffer + len, "Mode    : %s\n", PPA_MODE_STRING[ppa_hosts[i].mode]);#if PPA_DEBUG > 0    len += sprintf(buffer + len, "recon_tmo : %lu\n", ppa_hosts[i].recon_tmo);#endif    /* Request for beyond end of buffer */    if (offset > length)	return 0;    *start = buffer + offset;    len -= offset;    if (len > length)	len = length;    return len;}static int device_check(int host_no);#if PPA_DEBUG > 0#define ppa_fail(x,y) printk("ppa: ppa_fail(%i) from %s at line %d\n",\	   y, __FUNCTION__, __LINE__); ppa_fail_func(x,y);static inline void ppa_fail_func(int host_no, int error_code)#elsestatic inline void ppa_fail(int host_no, int error_code)#endif{    /* If we fail a device then we trash status / message bytes */    if (ppa_hosts[host_no].cur_cmd) {	ppa_hosts[host_no].cur_cmd->result = error_code << 16;	ppa_hosts[host_no].failed = 1;    }}/* * Wait for the high bit to be set. *  * In principle, this could be tied to an interrupt, but the adapter * doesn't appear to be designed to support interrupts.  We spin on * the 0x80 ready bit.  */static unsigned char ppa_wait(int host_no){    int k;    unsigned short ppb = PPA_BASE(host_no);    unsigned char r;    k = PPA_SPIN_TMO;    /* Wait for bit 6 and 7 - PJC */    for (r = r_str (ppb); ((r & 0xc0)!=0xc0) && (k); k--) {	    udelay (1);	    r = r_str (ppb);    }    /*     * return some status information.     * Semantics: 0xc0 = ZIP wants more data     *            0xd0 = ZIP wants to send more data     *            0xe0 = ZIP is expecting SCSI command data     *            0xf0 = end of transfer, ZIP is sending status     */    if (k)	return (r & 0xf0);    /* Counter expired - Time out occurred */    ppa_fail(host_no, DID_TIME_OUT);    printk("ppa timeout in ppa_wait\n");    return 0;			/* command timed out */}/* * Clear EPP Timeout Bit  */static inline void epp_reset(unsigned short ppb){    int i;    i = r_str(ppb);    w_str(ppb, i);    w_str(ppb, i & 0xfe);}/*  * Wait for empty ECP fifo (if we are in ECP fifo mode only) */static inline void ecp_sync(unsigned short hostno){    int i, ppb_hi=ppa_hosts[hostno].dev->port->base_hi;    if (ppb_hi == 0) return;    if ((r_ecr(ppb_hi) & 0xe0) == 0x60) { /* mode 011 == ECP fifo mode */        for (i = 0; i < 100; i++) {            if (r_ecr(ppb_hi) & 0x01)                return;            udelay(5);        }        printk("ppa: ECP sync failed as data still present in FIFO.\n");    }}static int ppa_byte_out(unsigned short base, const char *buffer, int len){    int i;    for (i = len; i; i--) {	w_dtr(base, *buffer++);	w_ctr(base, 0xe);	w_ctr(base, 0xc);    }    return 1;			/* All went well - we hope! */}static int ppa_byte_in(unsigned short base, char *buffer, int len){    int i;    for (i = len; i; i--) {	*buffer++ = r_dtr(base);	w_ctr(base, 0x27);	w_ctr(base, 0x25);    }    return 1;			/* All went well - we hope! */}static int ppa_nibble_in(unsigned short base, char *buffer, int len){    for (; len; len--) {	unsigned char h;	w_ctr(base, 0x4);	h = r_str(base) & 0xf0;	w_ctr(base, 0x6);	*buffer++ = h | ((r_str(base) & 0xf0) >> 4);    }    return 1;			/* All went well - we hope! */}static int ppa_out(int host_no, char *buffer, int len){    int r;    unsigned short ppb = PPA_BASE(host_no);    r = ppa_wait(host_no);    if ((r & 0x50) != 0x40) {	ppa_fail(host_no, DID_ERROR);	return 0;    }    switch (ppa_hosts[host_no].mode) {    case PPA_NIBBLE:    case PPA_PS2:	/* 8 bit output, with a loop */	r = ppa_byte_out(ppb, buffer, len);	break;    case PPA_EPP_32:    case PPA_EPP_16:    case PPA_EPP_8:	epp_reset(ppb);	w_ctr(ppb, 0x4);#ifdef CONFIG_SCSI_IZIP_EPP16	if (!(((long) buffer | len) & 0x01))	    outsw(ppb + 4, buffer, len >> 1);#else	if (!(((long) buffer | len) & 0x03))	    outsl(ppb + 4, buffer, len >> 2);#endif	else	    outsb(ppb + 4, buffer, len);	w_ctr(ppb, 0xc);	r = !(r_str(ppb) & 0x01);	w_ctr(ppb, 0xc);	ecp_sync(host_no);	break;    default:	printk("PPA: bug in ppa_out()\n");	r = 0;    }    return r;}static int ppa_in(int host_no, char *buffer, int len){    int r;    unsigned short ppb = PPA_BASE(host_no);    r = ppa_wait(host_no);    if ((r & 0x50) != 0x50) {	ppa_fail(host_no, DID_ERROR);	return 0;    }    switch (ppa_hosts[host_no].mode) {    case PPA_NIBBLE:	/* 4 bit input, with a loop */	r = ppa_nibble_in(ppb, buffer, len);	w_ctr(ppb, 0xc);	break;    case PPA_PS2:	/* 8 bit input, with a loop */	w_ctr(ppb, 0x25);	r = ppa_byte_in(ppb, buffer, len);	w_ctr(ppb, 0x4);	w_ctr(ppb, 0xc);	break;    case PPA_EPP_32:    case PPA_EPP_16:    case PPA_EPP_8:	epp_reset(ppb);	w_ctr(ppb, 0x24);#ifdef CONFIG_SCSI_IZIP_EPP16	if (!(((long) buffer | len) & 0x01))	    insw(ppb + 4, buffer, len >> 1);#else	if (!(((long) buffer | len) & 0x03))	    insl(ppb + 4, buffer, len >> 2);#endif	else	    insb(ppb + 4, buffer, len);	w_ctr(ppb, 0x2c);	r = !(r_str(ppb) & 0x01);	w_ctr(ppb, 0x2c);	ecp_sync(host_no);	break;    default:	printk("PPA: bug in ppa_ins()\n");	r = 0;	break;    }    return r;}/* end of ppa_io.h */static inline void ppa_d_pulse(unsigned short ppb, unsigned char b){    w_dtr(ppb, b);    w_ctr(ppb, 0xc);    w_ctr(ppb, 0xe);    w_ctr(ppb, 0xc);    w_ctr(ppb, 0x4);    w_ctr(ppb, 0xc);}static void ppa_disconnect(int host_no){    unsigned short ppb = PPA_BASE(host_no);    ppa_d_pulse(ppb, 0);    ppa_d_pulse(ppb, 0x3c);    ppa_d_pulse(ppb, 0x20);    ppa_d_pulse(ppb, 0xf);}static inline void ppa_c_pulse(unsigned short ppb, unsigned char b){    w_dtr(ppb, b);    w_ctr(ppb, 0x4);    w_ctr(ppb, 0x6);    w_ctr(ppb, 0x4);    w_ctr(ppb, 0xc);}static inline void ppa_connect(int host_no, int flag){    unsigned short ppb = PPA_BASE(host_no);    ppa_c_pulse(ppb, 0);    ppa_c_pulse(ppb, 0x3c);    ppa_c_pulse(ppb, 0x20);    if ((flag == CONNECT_EPP_MAYBE) &&	IN_EPP_MODE(ppa_hosts[host_no].mode))	ppa_c_pulse(ppb, 0xcf);    else	ppa_c_pulse(ppb, 0x8f);}static int ppa_select(int host_no, int target){    int k;    unsigned short ppb = PPA_BASE(host_no);    /*     * Bit 6 (0x40) is the device selected bit.     * First we must wait till the current device goes off line...     */    k = PPA_SELECT_TMO;    do {	k--;	udelay(1);    } while ((r_str(ppb) & 0x40) && (k));    if (!k)	return 0;

⌨️ 快捷键说明

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