📄 imm.c
字号:
/* imm.c -- low level driver for the IOMEGA MatchMaker * parallel port SCSI host adapter. * * (The IMM is the embedded controller in the ZIP Plus drive.) * * Current Maintainer: David Campbell (Perth, Western Australia) * campbell@torque.net * * 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 IMM_CODE 1#define IMM_PROBE_SPP 0x0001#define IMM_PROBE_PS2 0x0002#define IMM_PROBE_ECR 0x0010#define IMM_PROBE_EPP17 0x0100#define IMM_PROBE_EPP19 0x0200void imm_reset_pulse(unsigned int base);static int device_check(int host_no);#include <linux/blk.h>#include <asm/io.h>#include <linux/parport.h>#include "sd.h"#include "hosts.h"typedef struct { struct pardevice *dev; /* Parport device entry */ int base; /* Actual port address */ int base_hi; /* Hi Base address for ECP-ISA chipset */ int mode; /* Transfer mode */ int host; /* Host number (for proc) */ Scsi_Cmnd *cur_cmd; /* Current queued command */ struct tq_struct imm_tq; /* Polling interupt stuff */ unsigned long jstart; /* Jiffies at start */ unsigned failed:1; /* Failure flag */ unsigned dp:1; /* Data phase present */ unsigned rd:1; /* Read data in data phase */ unsigned p_busy:1; /* Parport sharing busy flag */} imm_struct;#define IMM_EMPTY \{ dev: NULL, \ base: -1, \ base_hi: 0, \ mode: IMM_AUTODETECT, \ host: -1, \ cur_cmd: NULL, \ imm_tq: { routine: imm_interrupt }, \ jstart: 0, \ failed: 0, \ dp: 0, \ rd: 0, \ p_busy: 0 \}#include "imm.h"#define NO_HOSTS 4static imm_struct imm_hosts[NO_HOSTS] ={IMM_EMPTY, IMM_EMPTY, IMM_EMPTY, IMM_EMPTY};#define IMM_BASE(x) imm_hosts[(x)].base#define IMM_BASE_HI(x) imm_hosts[(x)].base_hiint parbus_base[NO_HOSTS] ={0x03bc, 0x0378, 0x0278, 0x0000};void imm_wakeup(void *ref){ imm_struct *imm_dev = (imm_struct *) ref; if (!imm_dev->p_busy) return; if (parport_claim(imm_dev->dev)) { printk("imm: bug in imm_wakeup\n"); return; } imm_dev->p_busy = 0; imm_dev->base = imm_dev->dev->port->base; if (imm_dev->cur_cmd) imm_dev->cur_cmd->SCp.phase++; return;}int imm_release(struct Scsi_Host *host){ int host_no = host->unique_id; printk("Releasing imm%i\n", host_no); parport_unregister_device(imm_hosts[host_no].dev); return 0;}static int imm_pb_claim(int host_no){ if (parport_claim(imm_hosts[host_no].dev)) { imm_hosts[host_no].p_busy = 1; return 1; } if (imm_hosts[host_no].cur_cmd) imm_hosts[host_no].cur_cmd->SCp.phase++; return 0;}#define imm_pb_release(x) parport_release(imm_hosts[(x)].dev)/*************************************************************************** * Parallel port probing routines * ***************************************************************************/static Scsi_Host_Template driver_template = IMM;#include "scsi_module.c"int imm_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("imm: Version %s\n", IMM_VERSION); nhosts = 0; try_again = 0; if (!pb) { printk("imm: 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; imm_hosts[i].dev = parport_register_device(pb, "imm", NULL, imm_wakeup, NULL, 0, (void *) &imm_hosts[i]); if (!imm_hosts[i].dev) continue; /* Claim the bus so it remembers what we do to the control * registers. [ CTR and ECP ] */ if (imm_pb_claim(i)) { unsigned long now = jiffies; while (imm_hosts[i].p_busy) { schedule(); /* We are safe to schedule here */ if (time_after(jiffies, now + 3 * HZ)) { printk(KERN_ERR "imm%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 = IMM_BASE(i) = imm_hosts[i].dev->port->base; IMM_BASE_HI(i) = imm_hosts[i].dev->port->base_hi; w_ctr(ppb, 0x0c); modes = imm_hosts[i].dev->port->modes; /* Mode detection works up the chain of speed * This avoids a nasty if-then-else-if-... tree */ imm_hosts[i].mode = IMM_NIBBLE; if (modes & PARPORT_MODE_TRISTATE) imm_hosts[i].mode = IMM_PS2; /* Done configuration */ imm_pb_release(i); if (imm_init(i)) { parport_unregister_device(imm_hosts[i].dev); continue; } /* now the glue ... */ switch (imm_hosts[i].mode) { case IMM_NIBBLE: ports = 3; break; case IMM_PS2: ports = 3; break; case IMM_EPP_8: case IMM_EPP_16: case IMM_EPP_32: ports = 8; break; default: /* Never gets here */ continue; } host->can_queue = IMM_CAN_QUEUE; host->sg_tablesize = imm_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; imm_hosts[i].host = hreg->host_no; nhosts++; } if (nhosts == 0) { if (try_again == 1) { spin_lock_irq(&io_request_lock); return 0; } 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 imm driver a way to modify the timings (and other * parameters) by writing to the /proc/scsi/imm/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 imm_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); imm_hosts[hostno].mode = x; return length; } printk("imm /proc: invalid variable\n"); return (-EINVAL);}int imm_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 (imm_hosts[i].host == hostno) break; if (inout) return imm_proc_write(i, buffer, length); len += sprintf(buffer + len, "Version : %s\n", IMM_VERSION); len += sprintf(buffer + len, "Parport : %s\n", imm_hosts[i].dev->port->name); len += sprintf(buffer + len, "Mode : %s\n", IMM_MODE_STRING[imm_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;}#if IMM_DEBUG > 0#define imm_fail(x,y) printk("imm: imm_fail(%i) from %s at line %d\n",\ y, __FUNCTION__, __LINE__); imm_fail_func(x,y);static inline void imm_fail_func(int host_no, int error_code)#elsestatic inline void imm_fail(int host_no, int error_code)#endif{ /* If we fail a device then we trash status / message bytes */ if (imm_hosts[host_no].cur_cmd) { imm_hosts[host_no].cur_cmd->result = error_code << 16; imm_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 imm_wait(int host_no){ int k; unsigned short ppb = IMM_BASE(host_no); unsigned char r; w_ctr(ppb, 0x0c); k = IMM_SPIN_TMO; do { r = r_str(ppb); k--; udelay(1); } while (!(r & 0x80) && (k)); /* * STR register (LPT base+1) to SCSI mapping: * * STR imm imm * =================================== * 0x80 S_REQ S_REQ * 0x40 !S_BSY (????) * 0x20 !S_CD !S_CD * 0x10 !S_IO !S_IO * 0x08 (????) !S_BSY * * imm imm meaning * ================================== * 0xf0 0xb8 Bit mask * 0xc0 0x88 ZIP wants more data * 0xd0 0x98 ZIP wants to send more data * 0xe0 0xa8 ZIP is expecting SCSI command data * 0xf0 0xb8 end of transfer, ZIP is sending status */ w_ctr(ppb, 0x04); if (k) return (r & 0xb8); /* Counter expired - Time out occurred */ imm_fail(host_no, DID_TIME_OUT); printk("imm timeout in imm_wait\n"); return 0; /* command timed out */}static int imm_negotiate(imm_struct * tmp){ /* * The following is supposedly the IEEE 1284-1994 negotiate * sequence. I have yet to obtain a copy of the above standard * so this is a bit of a guess... * * A fair chunk of this is based on the Linux parport implementation * of IEEE 1284. * * Return 0 if data available * 1 if no data available */ unsigned short base = tmp->base; unsigned char a, mode; switch (tmp->mode) { case IMM_NIBBLE: mode = 0x00; break; case IMM_PS2: mode = 0x01; break; default: return 0; } w_ctr(base, 0x04); udelay(5); w_dtr(base, mode); udelay(100); w_ctr(base, 0x06); udelay(5); a = (r_str(base) & 0x20) ? 0 : 1; udelay(5); w_ctr(base, 0x07); udelay(5); w_ctr(base, 0x06); if (a) { printk("IMM: IEEE1284 negotiate indicates no data available.\n"); imm_fail(tmp->host, DID_ERROR); } return a;}/* * 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=IMM_BASE_HI(hostno); 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("imm: ECP sync failed as data still present in FIFO.\n"); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -