📄 ppa.c
字号:
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--; udelay(1); } 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);#if defined(CONFIG_PARPORT) || defined(CONFIG_PARPORT_MODULE) if (ppa_pb_claim(host_no)) while (ppa_hosts[host_no].p_busy) schedule(); /* We can safe schedule here */#endif 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--; if (!retv) ppa_reset_pulse(ppb); 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); ppa_pb_release(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 (time_after(jiffies, start_jiffies + 1)) return 0; if ((cmd->SCp.this_residual <= 0)) { ppa_fail(host_no, DID_ERROR); return -1; /* ERROR_RETURN */ } /* On some hardware we have SCSI disconnected (6th bit low) * for about 100usecs. It is too expensive to wait a * tick on every loop so we busy wait for no more than * 500usecs to give the drive a chance first. We do not * change things for "normal" hardware since generally * the 6th bit is always high. * This makes the CPU load higher on some hardware * but otherwise we can not get more then 50K/secs * on this problem hardware. */ if ((r & 0xc0) != 0xc0) { /* Wait for reconnection should be no more than * jiffy/2 = 5ms = 5000 loops */ unsigned long k = ppa_hosts[host_no].recon_tmo; for (; k && ((r = (r_str(ppb) & 0xf0)) & 0xc0) != 0xc0; k--) udelay(1); if(!k) return 0; } /* 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 */}/* deprecated synchronous interface */int ppa_command(Scsi_Cmnd * cmd){ static int first_pass = 1; int host_no = cmd->host->unique_id; if (first_pass) { printk("ppa: using non-queuing interface\n"); first_pass = 0; } if (ppa_hosts[host_no].cur_cmd) { printk("PPA: bug in ppa_command\n"); return 0; } ppa_hosts[host_no].failed = 0; ppa_hosts[host_no].jstart = jiffies; ppa_hosts[host_no].cur_cmd = cmd; cmd->result = DID_ERROR << 16; /* default return code */ cmd->SCp.phase = 0; ppa_pb_claim(host_no); while (ppa_engine(&ppa_hosts[host_no], cmd)) schedule(); if (cmd->SCp.phase) /* Only disconnect if we have connected */ ppa_disconnect(cmd->host->unique_id); ppa_pb_release(host_no); ppa_hosts[host_no].cur_cmd = 0; return cmd->result;}/* * 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; unsigned long flags; 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); if (cmd->SCp.phase > 0) ppa_pb_release(cmd->host->unique_id); tmp->cur_cmd = 0; spin_lock_irqsave(&io_request_lock, flags); cmd->scsi_done(cmd); spin_unlock_irqrestore(&io_request_lock, flags); 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); unsigned char l = 0, h = 0; int retv; /* First check for any errors that may of occurred * Here we check for internal errors */ if (tmp->failed) return 0; switch (cmd->SCp.phase) { case 0: /* Phase 0 - Waiting for parport */ if ((jiffies - tmp->jstart) > HZ) { /* * We waited more than a second * for parport to call us */ ppa_fail(host_no, DID_BUS_BUSY); return 0; } return 1; /* wait until ppa_wakeup claims parport */ case 1: /* Phase 1 - Connected */ { /* Perform a sanity check for cable unplugged */ int retv = 2; /* Failed */ ppa_connect(host_no, CONNECT_EPP_MAYBE); w_ctr(ppb, 0xe); if ((r_str(ppb) & 0x08) == 0x08) retv--; w_ctr(ppb, 0xc); if ((r_str(ppb) & 0x08) == 0x00) retv--; if (retv) { if ((jiffies - tmp->jstart) > (1 * HZ)) { printk("ppa: Parallel port cable is unplugged!!\n"); ppa_fail(host_no, DID_BUS_BUSY); return 0; } else { ppa_disconnect(host_no); return 1; /* Try again in a jiffy */ } } cmd->SCp.phase++; } case 2: /* Phase 2 - We are now talking to the scsi bus */ if (!ppa_select(host_no, cmd->target)) { ppa_fail(host_no, DID_NO_CONNECT); return 0; } cmd->SCp.phase++; case 3: /* Phase 3 - Ready to accept a command */ w_ctr(ppb, 0x0c); if (!(r_str(ppb) & 0x80)) return 1; if (!ppa_send_command(cmd)) return 0; cmd->SCp.phase++; case 4: /* Phase 4 - Setup scatter/gather buffers */ if (cmd->use_sg) { /* if many buffers are available, start filling the first */ cmd->SCp.buffer = (struct scatterlist *) cmd->request_buffer; cmd->SCp.this_residual = cmd->SCp.buffer->length; cmd->SCp.ptr = cmd->SCp.buffer->address; } else { /* else fill the only available buffer */ cmd->SCp.buffer = NULL; cmd->SCp.this_residual = cmd->request_bufflen; cmd->SCp.ptr = cmd->request_buffer; } cmd->SCp.buffers_residual = cmd->use_sg; cmd->SCp.phase++; case 5: /* Phase 5 - Data transfer stage */ w_ctr(ppb, 0x0c); if (!(r_str(ppb) & 0x80)) return 1; retv = ppa_completion(cmd); if (retv == -1) return 0; if (retv == 0) return 1; cmd->SCp.phase++; case 6: /* Phase 6 - Read status/message */ cmd->result = DID_OK << 16; /* Check for data overrun */ if (ppa_wait(host_no) != (unsigned char) 0xf0) { ppa_fail(host_no, DID_ERROR); return 0; } if (ppa_in(host_no, &l, 1)) { /* read status byte */ /* Check for optional message byte */ if (ppa_wait(host_no) == (unsigned char) 0xf0) ppa_in(host_no, &h, 1); cmd->result = (DID_OK << 16) + (h << 8) + (l & STATUS_MASK); } return 0; /* Finished */ break; default: printk("ppa: Invalid scsi phase\n"); } return 0;}int ppa_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)){ int host_no = cmd->host->unique_id; if (ppa_hosts[host_no].cur_cmd) { printk("PPA: bug in ppa_queuecommand\n"); return 0; } ppa_hosts[host_no].failed = 0; ppa_hosts[host_no].jstart = jiffies; ppa_hosts[host_no].cur_cmd = cmd; cmd->scsi_done = done; cmd->result = DID_ERROR << 16; /* default return code */ cmd->SCp.phase = 0; /* bus free */ ppa_pb_claim(host_no); ppa_hosts[host_no].ppa_tq.data = ppa_hosts + host_no; ppa_hosts[host_no].ppa_tq.sync = 0; queue_task(&ppa_hosts[host_no].ppa_tq, &tq_immediate); mark_bh(IMMEDIATE_BH); return 0;}/* * Apparently the the disk->capacity attribute is off by 1 sector * for all disk drives. We add the one here, but it should really * be done in sd.c. Even if it gets fixed there, this will still * work. */int ppa_biosparam(Disk * disk, kdev_t dev, int ip[]){ ip[0] = 0x40; ip[1] = 0x20; ip[2] = (disk->capacity + 1) / (ip[0] * ip[1]); if (ip[2] > 1024) { ip[0] = 0xff; ip[1] = 0x3f; ip[2] = (disk->capacity + 1) / (ip[0] * ip[1]); if (ip[2] > 1023) ip[2] = 1023; } return 0;}int ppa_abort(Scsi_Cmnd * cmd){ int host_no = cmd->host->unique_id; /* * There is no method for aborting commands since Iomega * have tied the SCSI_MESSAGE line high in the interface */ switch (cmd->SCp.phase) { case 0: /* Do not have access to parport */ case 1: /* Have not connected to interface */ ppa_hosts[host_no].cur_cmd = NULL; /* Forget the problem */ return SUCCESS; break; default: /* SCSI command sent, can not abort */ return FAILED; break; }}static void ppa_reset_pulse(unsigned int base){ w_dtr(base, 0x40); w_ctr(base, 0x8); udelay(30); w_ctr(base, 0xc);}int ppa_reset(Scsi_Cmnd * cmd){ int host_no = cmd->host->unique_id; if (cmd->SCp.phase) ppa_disconnect(host_no); ppa_hosts[host_no].cur_cmd = NULL; /* Forget the problem */ ppa_connect(host_no, CONNECT_NORMAL); ppa_reset_pulse(PPA_BASE(host_no)); udelay(1000); /* device settle delay */ ppa_disconnect(host_no); udelay(1000); /* device settle delay */ return SUCCESS;}static int device_check(int host_no){ /* This routine looks for a device and then attempts to use EPP to send a command. If all goes as planned then EPP is available. */ static char cmd[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; int loop, old_mode, status, k, ppb = PPA_BASE(host_no); unsigned char l; old_mode = ppa_hosts[host_no].mode; for (loop = 0; loop < 8; loop++) { /* Attempt to use EPP for Test Unit Ready */ if ((ppb & 0x0007) == 0x0000) ppa_hosts[host_no].mode = PPA_EPP_32; second_pass: ppa_connect(host_no, CONNECT_EPP_MAYBE); /* Select SCSI device */ if (!ppa_select(host_no, loop)) { ppa_disconnect(host_no); continue; } printk("ppa: Found device at ID %i, Attempting to use %s\n", loop, PPA_MODE_STRING[ppa_hosts[host_no].mode]); /* Send SCSI command */ status = 1; w_ctr(ppb, 0x0c); for (l = 0; (l < 6) && (status); l++) status = ppa_out(host_no, cmd, 1); if (!status) { ppa_disconnect(host_no); ppa_connect(host_no, CONNECT_EPP_MAYBE); w_dtr(ppb, 0x40); w_ctr(ppb, 0x08); udelay(30); w_ctr(ppb, 0x0c); udelay(1000); ppa_disconnect(host_no); udelay(1000); if (ppa_hosts[host_no].mode == PPA_EPP_32) { ppa_hosts[host_no].mode = old_mode; goto second_pass; } printk("ppa: Unable to establish communication, aborting driver load.\n"); return 1; } w_ctr(ppb, 0x0c); k = 1000000; /* 1 Second */ do { l = r_str(ppb); k--; udelay(1); } while (!(l & 0x80) && (k)); l &= 0xf0; if (l != 0xf0) { ppa_disconnect(host_no); ppa_connect(host_no, CONNECT_EPP_MAYBE); ppa_reset_pulse(ppb); udelay(1000); ppa_disconnect(host_no); udelay(1000); if (ppa_hosts[host_no].mode == PPA_EPP_32) { ppa_hosts[host_no].mode = old_mode; goto second_pass; } printk("ppa: Unable to establish communication, aborting driver load.\n"); return 1; } ppa_disconnect(host_no); printk("ppa: Communication established with ID %i using %s\n", loop, PPA_MODE_STRING[ppa_hosts[host_no].mode]); ppa_connect(host_no, CONNECT_EPP_MAYBE); ppa_reset_pulse(ppb); udelay(1000); ppa_disconnect(host_no); udelay(1000); return 0; } printk("ppa: No devices found, aborting driver load.\n"); return 1;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -