📄 ppa.c
字号:
" outb %%al,(%%dx)\n" \ " subl $2,%%edx\n"static inline 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! */}#define NIBBLE_IN(reg) \ " incl %%edx\n" \ " movb $0x04,%%al\n" \ " outb %%al,(%%dx)\n" \ " decl %%edx\n" \ " inb (%%dx),%%al\n" \ " andb $0xf0,%%al\n" \ " movb %%al," #reg "\n" \ " incl %%edx\n" \ " movb $0x06,%%al\n" \ " outb %%al,(%%dx)\n" \ " decl %%edx\n" \ " inb (%%dx),%%al\n" \ " shrb $4,%%al\n" \ " orb %%al," #reg "\n"static inline int ppa_nibble_in(unsigned short str_p, 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! */}#else /* Old style C routines */static inline int ppa_byte_out(unsigned short base, const char *buffer, int len){ unsigned short ctr_p = base + 2; int i; for (i = len; i; i--) { outb(*buffer++, base); outb(0xe, ctr_p); outb(0xc, ctr_p); } return 1; /* All went well - we hope! */}static inline int ppa_byte_in(unsigned short base, char *buffer, int len){ unsigned short ctr_p = base + 2; int i; for (i = len; i; i--) { *buffer++ = inb(base); outb(0x27, ctr_p); outb(0x25, ctr_p); } return 1; /* All went well - we hope! */}static inline int ppa_nibble_in(unsigned short str_p, char *buffer, int len){ unsigned short ctr_p = str_p + 1; unsigned char h, l; int i; for (i = len; i; i--) { outb(0x4, ctr_p); h = inb(str_p); outb(0x6, ctr_p); l = inb(str_p); *buffer++ = (h & 0xf0) | ((l & 0xf0) >> 4); } return 1; /* All went well - we hope! */ } #endif static inline int ppa_epp_out(unsigned short epp_p, unsigned short str_p, const char *buffer, int len){ int i; for (i = len; i; i--) { outb(*buffer++, epp_p);#ifdef CONFIG_SCSI_PPA_HAVE_PEDANTIC if (inb(str_p) & 0x01) return 0; #endif } return 1; } 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_PPA_HAVE_PEDANTIC r = ppa_epp_out(ppb + 4, ppb + 1, buffer, len);#else if (!(((long) buffer | len) & 0x03)) outsl(ppb + 4, buffer, len >> 2); else outsb(ppb + 4, buffer, len); w_ctr(ppb, 0xc); r = !(r_str(ppb) & 0x01);#endif w_ctr(ppb, 0xc); ecp_sync(ppb); break; default: printk("PPA: bug in ppa_out()\n"); r = 0; } return r;}static inline int ppa_epp_in(int epp_p, int str_p, char *buffer, int len){ int i; for (i = len; i; i--) { *buffer++ = inb(epp_p);#ifdef CONFIG_SCSI_PPA_HAVE_PEDANTIC if (inb(str_p) & 0x01) return 0;#endif } return 1; } 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 + 1, 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_PPA_HAVE_PEDANTIC r = ppa_epp_in(ppb + 4, ppb + 1, buffer, len); #else if (!(((long) buffer | len) & 0x03)) insl(ppb + 4, buffer, len >> 2); else insb(ppb + 4, buffer, len); w_ctr(ppb, 0x2c); r = !(r_str(ppb) & 0x01);#endif w_ctr(ppb, 0x2c); ecp_sync(ppb); 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--; } while ((r_str(ppb) & 0x40) && (k)); if (!k) return 0; w_dtr(ppb, (1 << target)); w_ctr(ppb, 0xe); w_ctr(ppb, 0xc); w_dtr(ppb, 0x80); /* This is NOT the initator */ w_ctr(ppb, 0x8); k = PPA_SELECT_TMO; do { k--; } while (!(r_str(ppb) & 0x40) && (k)); if (!k) return 0; return 1;}/* * This is based on a trace of what the Iomega DOS 'guest' driver does. * I've tried several different kinds of parallel ports with guest and * coded this to react in the same ways that it does. * * The return value from this function is just a hint about where the * handshaking failed. * */static int ppa_init(int host_no){ int retv; unsigned short ppb = PPA_BASE(host_no); ppa_disconnect(host_no); ppa_connect(host_no, CONNECT_NORMAL); retv = 2; /* Failed */ w_ctr(ppb, 0xe); if ((r_str(ppb) & 0x08) == 0x08) retv--; w_ctr(ppb, 0xc); if ((r_str(ppb) & 0x08) == 0x00) retv--; /* This is a SCSI BUS reset signal */ if (!retv) { w_dtr(ppb, 0x40); w_ctr(ppb, 0x08); udelay(30); w_ctr(ppb, 0x0c); udelay(1000); /* Allow devices to settle down */ } ppa_disconnect(host_no); udelay(1000); /* Another delay to allow devices to settle */ if (!retv) retv = device_check(host_no); return retv;}static inline int ppa_send_command(Scsi_Cmnd * cmd){ int host_no = cmd->host->unique_id; int k; w_ctr(PPA_BASE(host_no), 0x0c); for (k = 0; k < cmd->cmd_len; k++) if (!ppa_out(host_no, &cmd->cmnd[k], 1)) return 0; return 1;}/* * The bulk flag enables some optimisations in the data transfer loops, * it should be true for any command that transfers data in integral * numbers of sectors. * * The driver appears to remain stable if we speed up the parallel port * i/o in this function, but not elsewhere. */static int ppa_completion(Scsi_Cmnd * cmd){ /* Return codes: * -1 Error * 0 Told to schedule * 1 Finished data transfer */ int host_no = cmd->host->unique_id; unsigned short ppb = PPA_BASE(host_no); unsigned long start_jiffies = jiffies; unsigned char r, v; int fast, bulk, status; v = cmd->cmnd[0]; bulk = ((v == READ_6) || (v == READ_10) || (v == WRITE_6) || (v == WRITE_10)); /* * We only get here if the drive is ready to comunicate, * hence no need for a full ppa_wait. */ r = (r_str(ppb) & 0xf0); while (r != (unsigned char) 0xf0) { /* * If we have been running for more than a full timer tick * then take a rest. */ if (jiffies > start_jiffies + 1) return 0; if (((r & 0xc0) != 0xc0) || (cmd->SCp.this_residual <= 0)) { ppa_fail(host_no, DID_ERROR); return -1; /* ERROR_RETURN */ } /* determine if we should use burst I/O */ fast = (bulk && (cmd->SCp.this_residual >= PPA_BURST_SIZE)) ? PPA_BURST_SIZE : 1; if (r == (unsigned char) 0xc0) status = ppa_out(host_no, cmd->SCp.ptr, fast); else status = ppa_in(host_no, cmd->SCp.ptr, fast); cmd->SCp.ptr += fast; cmd->SCp.this_residual -= fast; if (!status) { ppa_fail(host_no, DID_BUS_BUSY); return -1; /* ERROR_RETURN */ } if (cmd->SCp.buffer && !cmd->SCp.this_residual) { /* if scatter/gather, advance to the next segment */ if (cmd->SCp.buffers_residual--) { cmd->SCp.buffer++; cmd->SCp.this_residual = cmd->SCp.buffer->length; cmd->SCp.ptr = cmd->SCp.buffer->address; } } /* Now check to see if the drive is ready to comunicate */ r = (r_str(ppb) & 0xf0); /* If not, drop back down to the scheduler and wait a timer tick */ if (!(r & 0x80)) return 0; } return 1; /* FINISH_RETURN */}/* * Since the PPA itself doesn't generate interrupts, we use * the scheduler's task queue to generate a stream of call-backs and * complete the request when the drive is ready. */static void ppa_interrupt(void *data){ ppa_struct *tmp = (ppa_struct *) data; Scsi_Cmnd *cmd = tmp->cur_cmd; if (!cmd) { printk("PPA: bug in ppa_interrupt\n"); return; } if (ppa_engine(tmp, cmd)) { tmp->ppa_tq.data = (void *) tmp; tmp->ppa_tq.sync = 0; queue_task(&tmp->ppa_tq, &tq_timer); return; } /* Command must of completed hence it is safe to let go... */#if PPA_DEBUG > 0 switch ((cmd->result >> 16) & 0xff) { case DID_OK: break; case DID_NO_CONNECT: printk("ppa: no device at SCSI ID %i\n", cmd->target); break; case DID_BUS_BUSY: printk("ppa: BUS BUSY - EPP timeout detected\n"); break; case DID_TIME_OUT: printk("ppa: unknown timeout\n"); break; case DID_ABORT: printk("ppa: told to abort\n"); break; case DID_PARITY: printk("ppa: parity error (???)\n"); break; case DID_ERROR: printk("ppa: internal driver error\n"); break; case DID_RESET: printk("ppa: told to reset device\n"); break; case DID_BAD_INTR: printk("ppa: bad interrupt (???)\n"); break; default: printk("ppa: bad return code (%02x)\n", (cmd->result >> 16) & 0xff); } #endif if (cmd->SCp.phase > 1) ppa_disconnect(cmd->host->unique_id); tmp->cur_cmd = 0; cmd->scsi_done(cmd); return;}static int ppa_engine(ppa_struct * tmp, Scsi_Cmnd * cmd){ int host_no = cmd->host->unique_id; unsigned short ppb = PPA_BASE(host_no);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -