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

📄 ppa.c

📁 GNU Mach 微内核源代码, 基于美国卡内基美隆大学的 Mach 研究项目
💻 C
📖 第 1 页 / 共 3 页
字号:
/* 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) *                     campbell@gear.torque.net *                     dcampbel@p01.as17.honeywell.com.au * * My unoffical company acronym list is 21 pages long: *      FLA:    Four letter acronym with built in facility for *              future expansion to five letters. */#include <linux/config.h>/* The following #define is to avoid a clash with hosts.c */#define PPA_CODE 1#ifndef HAVE_PC87332#define HAVE_PC87332    0#endif#define PPA_PROBE_SPP   0x0001#define PPA_PROBE_PS2   0x0002#define PPA_PROBE_ECR   0x0010#define PPA_PROBE_EPP17 0x0100#define PPA_PROBE_EPP19 0x0200int port_probe(unsigned short);#include <linux/blk.h>#include "sd.h"#include "hosts.h"typedef struct {    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 failed:1;		/* Failure flag                 */} ppa_struct;#define PPA_EMPTY \{-1,            /* base */      \PPA_AUTODETECT, /* mode */      \-1,             /* host */      \NULL,           /* cur_cmd */   \{0, 0, ppa_interrupt, NULL},    \0,              /* jstart */    \0               /* failed */    \}#include "ppa.h"#undef CONFIG_PARPORT#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)].baseint base[NO_HOSTS] ={0x03bc, 0x0378, 0x0278, 0x0000};#define parbus_base	base#define parbus_no	NO_HOSTSstatic inline int ppa_pb_claim(int host_no){    if (ppa_hosts[host_no].cur_cmd)	ppa_hosts[host_no].cur_cmd->SCp.phase++;    return 0;}/*************************************************************************** *                   Parallel port probing routines                        * ***************************************************************************/  #ifndef MODULE/* * Command line parameters (for built-in driver): * * Syntax:  ppa=base[,mode[,use_sg]] * * For example:  ppa=0x378   or   ppa=0x378,0,3 * */void ppa_setup(char *str, int *ints){    static int x = 0;    if (x == 0) {		/* Disable ALL known ports */	int i;	for (i = 0; i < NO_HOSTS; i++)	    parbus_base[i] = 0x0000;    }    switch (ints[0]) {    case 3:	ppa_sg = ints[3];    case 2:	ppa_hosts[x].mode = ints[2];	parbus_base[x] = ints[1];	break;    default:	printk("PPA: I only use between 2 to 3 parameters.\n");	break;    }    x++;  }#elseScsi_Host_Template driver_template = PPA;#include  "scsi_module.c"#endif  /* * Start of Chipset kludges */#if HAVE_PC87332 > 0#warning PC87332 Kludge code includedstatic inline int pc87332_port(int host_no){    /* A routine to detect and kludge pc87332 chipsets into the     * "optimum" mode for parallel port data transfer.     * This assumes EPP is better than ECP...     * (Which it is for disk drives but not printers and scanners)     */    int base = ppa_hosts[host_no].base;    /* This is where an pc87332 can hide */    unsigned short index_addr[4] =    {	0x0398, 0x026e, 0x015c, 0x002e    };    /* Bits 0&1 of FAR (Function Address Register) which specify where     * the LPT port will show up at.     */    unsigned short port_ref[4] =    {	0x378, 0x3bc, 0x278, 0xffff    };    unsigned char a;    int loop;    for (loop = 0; loop < 4; loop++) {	/* Clear the "wax" out of the pc87332, only needed after hard	 * reset.	 */	inb(index_addr[loop]);	inb(index_addr[loop]);	inb(index_addr[loop]);	inb(index_addr[loop]);	/* Anyone home ?? */	outb(0xff, index_addr[loop]);	a = inb(index_addr[loop]);	switch (a) {	case (0x0f):		/* PC87732 */	    break;	case (0x1f):		/* PC87306 */	    break;	case (0x7f):		/* PC87??? */	    break;	default:	    continue;	}			/* Is this pc87332 on the desired port */	outb(0x01, index_addr[loop]);	a = inb(index_addr[loop] + 1);	if (port_ref[a & 0x03] != base)	    continue;	/* Found a pc87332 */	printk("NatSemi PC87332 (or variant) at 0x%04x\n", base);	/* Try to enable EPP modes	 * with hardware data direction	 */	if (base != 0x3bc) {	    /* EPP 1.9 */	    outb(0x04, index_addr[loop]);	    a = inb(index_addr[loop] + 1);	    printk("Old reg1 = %02x\n", a);	    /* 0x01 for EPP 1.7, 0x03 for EPP 1.9, 0x0c for ECP */	    a = (a & 0xf0) | 0x03;	    outb(a, index_addr[loop] + 1);	    outb(a, index_addr[loop] + 1);	    /* Software data direction selection */	    outb(0x02, index_addr[loop]);	    a = inb(index_addr[loop] + 1);	    printk("Old reg2 = %02x\n", a);	    /* 0x80 for software, 0x00 for hardware */	    a = (a & 0x7f) | 0x80;	    outb(a, index_addr[loop] + 1);	    outb(a, index_addr[loop] + 1);	    ppa_hosts[host_no].mode = PPA_EPP_32;	} else {	    /* There is not enough address space for the 0x3bc port	     * to have EPP registers so we will kludge it into an	     * ECP	     * port to allow bi-directional byte mode...	     */	    /* ECP */	    outb(0x04, index_addr[loop]);	    a = inb(index_addr[loop] + 1);	    a = (a & 0xfb) | 0x06;	    outb(a, index_addr[loop] + 1);	    outb(a, index_addr[loop] + 1);	    ppa_hosts[host_no].mode = PPA_PS2;	}	outb(0x04, index_addr[loop]);	a = inb(index_addr[loop] + 1);	return ppa_hosts[host_no].mode;    }    return 0;  }#else#define pc87332_port(x)#endif				/* HAVE_PC87332 */  static inline int generic_port(int host_no){    /* Generic parallel port detection     * This will try to discover if the port is     * EPP, ECP, PS/2 or NIBBLE (In that order, approx....)     */    unsigned int save_ctr, save_ecr, r;    int ppb = PPA_BASE(host_no);    save_ctr = r_ctr(ppb);    save_ecr = r_ecr(ppb);    r = port_probe(ppb);    w_ecr(ppb, save_ecr);    w_ctr(ppb, save_ctr);    if (r & PPA_PROBE_SPP)	ppa_hosts[host_no].mode = PPA_NIBBLE;    if (r & PPA_PROBE_PS2) {	ppa_hosts[host_no].mode = PPA_PS2;	if (r & PPA_PROBE_ECR)	    w_ecr(ppb, 0x20);    }    if ((r & PPA_PROBE_EPP17) || (r & PPA_PROBE_EPP19)) {	/* ppa_hosts[host_no].mode = PPA_EPP_32; */	if (r & PPA_PROBE_ECR)	    w_ecr(ppb, 0x80);    }    return ppa_hosts[host_no].mode;}int ppa_detect(Scsi_Host_Template * host){    struct Scsi_Host *hreg;    int ports;    int i, nhosts;    unsigned short ppb;    printk("ppa: Version %s\n", PPA_VERSION);    nhosts = 0;    for (i = 0; i < parbus_no; i++) {	if (parbus_base[i] == 0x0000)	    continue;	ppb = ppa_hosts[i].base = parbus_base[i];	/* sanity checks */	if (check_region(parbus_base[i],			 (parbus_base[i] == 0x03bc) ? 3 : 8))	    continue;	pc87332_port(i);	if (!generic_port(i))	    continue;	if (ppa_init(i))	    continue;	/* now the glue ... */	switch (ppa_hosts[i].mode) {	case PPA_NIBBLE:	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;	}	request_region(ppa_hosts[i].base, ports, "ppa");	host->can_queue = PPA_CAN_QUEUE;	host->sg_tablesize = ppa_sg;	hreg = scsi_register(host, 0);	hreg->io_port = ppa_hosts[i].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)	return 0;    else	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_strncmp(const char *a, const char *b, int len){    int loop;    for (loop = 0; loop < len; loop++)	if (a[loop] != b[loop])	    return 1;    return 0;}static inline int ppa_proc_write(int hostno, char *buffer, int length){    unsigned long x;    if ((length > 5) && (ppa_strncmp(buffer, "mode=", 5) == 0)) {	x = simple_strtoul(buffer + 5, NULL, 0);	ppa_hosts[hostno].mode = 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, "Port    : 0x%04x\n", ppa_hosts[i].base);    len += sprintf(buffer + len, "Mode    : %s\n", PPA_MODE_STRING[ppa_hosts[i].mode]);    /* Request for beyond end of buffer */    if (offset > len)	return 0;    *start = buffer + offset;    len -= offset;    if (len > length)	len = length;    return len;}				/* end of ppa.c */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;    do {	r = r_str(ppb);	k--;	udelay(1);    }    while (!(r & 0x80) && (k));    /*     * 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 */}/* * output a string, in whatever mode is available, according to the * PPA protocol.  */static inline void epp_reset(unsigned short ppb){    int i;    i = r_str(ppb);    w_str(ppb, i);    w_str(ppb, i & 0xfe);}static inline void ecp_sync(unsigned short ppb){    int i;    if ((r_ecr(ppb) & 0xe0) != 0x80)	return;    for (i = 0; i < 100; i++) {	if (r_ecr(ppb) & 0x01)	    return;	udelay(5);    }    printk("ppa: ECP sync failed as data still present in FIFO.\n");}/* * Here is the asm code for the SPP/PS2 protocols for the i386. * This has been optimised for speed on 386/486 machines. There will * be very little improvement on the current 586+ machines as it is the * IO statements which will limit throughput. */#ifdef __i386__#define BYTE_OUT(reg) \	"	movb " #reg ",%%al\n" \	"	outb %%al,(%%dx)\n" \	"	addl $2,%%edx\n" \	"	movb $0x0e,%%al\n" \	"	outb %%al,(%%dx)\n" \	"	movb $0x0c,%%al\n" \	"	outb %%al,(%%dx)\n" \	"	subl $2,%%edx\n"static inline int ppa_byte_out(unsigned short base, char *buffer, unsigned 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! */}#define BYTE_IN(reg) \	"	inb (%%dx),%%al\n" \	"	movb %%al," #reg "\n" \	"	addl $2,%%edx\n" \	"	movb $0x27,%%al\n" \	"	outb %%al,(%%dx)\n" \	"	movb $0x25,%%al\n" \

⌨️ 快捷键说明

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