ppa.c
来自「linux 内核源代码」· C语言 代码 · 共 1,148 行 · 第 1/2 页
C
1,148 行
*/ if ((r & 0xc0) != 0xc0) { /* Wait for reconnection should be no more than * jiffy/2 = 5ms = 5000 loops */ unsigned long k = dev->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(dev, cmd->SCp.ptr, fast); else status = ppa_in(dev, cmd->SCp.ptr, fast); cmd->SCp.ptr += fast; cmd->SCp.this_residual -= fast; if (!status) { ppa_fail(dev, 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 = sg_virt(cmd->SCp.buffer); } } /* 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(struct work_struct *work){ ppa_struct *dev = container_of(work, ppa_struct, ppa_tq.work); struct scsi_cmnd *cmd = dev->cur_cmd; if (!cmd) { printk(KERN_ERR "PPA: bug in ppa_interrupt\n"); return; } if (ppa_engine(dev, cmd)) { schedule_delayed_work(&dev->ppa_tq, 1); 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(KERN_DEBUG "ppa: no device at SCSI ID %i\n", cmd->device->target); break; case DID_BUS_BUSY: printk(KERN_DEBUG "ppa: BUS BUSY - EPP timeout detected\n"); break; case DID_TIME_OUT: printk(KERN_DEBUG "ppa: unknown timeout\n"); break; case DID_ABORT: printk(KERN_DEBUG "ppa: told to abort\n"); break; case DID_PARITY: printk(KERN_DEBUG "ppa: parity error (???)\n"); break; case DID_ERROR: printk(KERN_DEBUG "ppa: internal driver error\n"); break; case DID_RESET: printk(KERN_DEBUG "ppa: told to reset device\n"); break; case DID_BAD_INTR: printk(KERN_WARNING "ppa: bad interrupt (???)\n"); break; default: printk(KERN_WARNING "ppa: bad return code (%02x)\n", (cmd->result >> 16) & 0xff); }#endif if (cmd->SCp.phase > 1) ppa_disconnect(dev); ppa_pb_dismiss(dev); dev->cur_cmd = NULL; cmd->scsi_done(cmd);}static int ppa_engine(ppa_struct *dev, struct scsi_cmnd *cmd){ unsigned short ppb = dev->base; unsigned char l = 0, h = 0; int retv; /* First check for any errors that may of occurred * Here we check for internal errors */ if (dev->failed) return 0; switch (cmd->SCp.phase) { case 0: /* Phase 0 - Waiting for parport */ if (time_after(jiffies, dev->jstart + HZ)) { /* * We waited more than a second * for parport to call us */ ppa_fail(dev, 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(dev, 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 (time_after(jiffies, dev->jstart + (1 * HZ))) { printk(KERN_ERR "ppa: Parallel port cable is unplugged.\n"); ppa_fail(dev, DID_BUS_BUSY); return 0; } else { ppa_disconnect(dev); 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(dev, scmd_id(cmd))) { ppa_fail(dev, 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 = sg_virt(cmd->SCp.buffer); } 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 - 1; 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(dev) != (unsigned char) 0xf0) { ppa_fail(dev, DID_ERROR); return 0; } if (ppa_in(dev, &l, 1)) { /* read status byte */ /* Check for optional message byte */ if (ppa_wait(dev) == (unsigned char) 0xf0) ppa_in(dev, &h, 1); cmd->result = (DID_OK << 16) + (h << 8) + (l & STATUS_MASK); } return 0; /* Finished */ break; default: printk(KERN_ERR "ppa: Invalid scsi phase\n"); } return 0;}static int ppa_queuecommand(struct scsi_cmnd *cmd, void (*done) (struct scsi_cmnd *)){ ppa_struct *dev = ppa_dev(cmd->device->host); if (dev->cur_cmd) { printk(KERN_ERR "PPA: bug in ppa_queuecommand\n"); return 0; } dev->failed = 0; dev->jstart = jiffies; dev->cur_cmd = cmd; cmd->scsi_done = done; cmd->result = DID_ERROR << 16; /* default return code */ cmd->SCp.phase = 0; /* bus free */ schedule_delayed_work(&dev->ppa_tq, 0); ppa_pb_claim(dev); return 0;}/* * Apparently 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. */static int ppa_biosparam(struct scsi_device *sdev, struct block_device *dev, sector_t capacity, int ip[]){ ip[0] = 0x40; ip[1] = 0x20; ip[2] = ((unsigned long) capacity + 1) / (ip[0] * ip[1]); if (ip[2] > 1024) { ip[0] = 0xff; ip[1] = 0x3f; ip[2] = ((unsigned long) capacity + 1) / (ip[0] * ip[1]); if (ip[2] > 1023) ip[2] = 1023; } return 0;}static int ppa_abort(struct scsi_cmnd *cmd){ ppa_struct *dev = ppa_dev(cmd->device->host); /* * 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 */ dev->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);}static int ppa_reset(struct scsi_cmnd *cmd){ ppa_struct *dev = ppa_dev(cmd->device->host); if (cmd->SCp.phase) ppa_disconnect(dev); dev->cur_cmd = NULL; /* Forget the problem */ ppa_connect(dev, CONNECT_NORMAL); ppa_reset_pulse(dev->base); mdelay(1); /* device settle delay */ ppa_disconnect(dev); mdelay(1); /* device settle delay */ return SUCCESS;}static int device_check(ppa_struct *dev){ /* 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 u8 cmd[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; int loop, old_mode, status, k, ppb = dev->base; unsigned char l; old_mode = dev->mode; for (loop = 0; loop < 8; loop++) { /* Attempt to use EPP for Test Unit Ready */ if ((ppb & 0x0007) == 0x0000) dev->mode = PPA_EPP_32;second_pass: ppa_connect(dev, CONNECT_EPP_MAYBE); /* Select SCSI device */ if (!ppa_select(dev, loop)) { ppa_disconnect(dev); continue; } printk(KERN_INFO "ppa: Found device at ID %i, Attempting to use %s\n", loop, PPA_MODE_STRING[dev->mode]); /* Send SCSI command */ status = 1; w_ctr(ppb, 0x0c); for (l = 0; (l < 6) && (status); l++) status = ppa_out(dev, cmd, 1); if (!status) { ppa_disconnect(dev); ppa_connect(dev, CONNECT_EPP_MAYBE); w_dtr(ppb, 0x40); w_ctr(ppb, 0x08); udelay(30); w_ctr(ppb, 0x0c); udelay(1000); ppa_disconnect(dev); udelay(1000); if (dev->mode == PPA_EPP_32) { dev->mode = old_mode; goto second_pass; } return -EIO; } 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(dev); ppa_connect(dev, CONNECT_EPP_MAYBE); ppa_reset_pulse(ppb); udelay(1000); ppa_disconnect(dev); udelay(1000); if (dev->mode == PPA_EPP_32) { dev->mode = old_mode; goto second_pass; } return -EIO; } ppa_disconnect(dev); printk(KERN_INFO "ppa: Communication established with ID %i using %s\n", loop, PPA_MODE_STRING[dev->mode]); ppa_connect(dev, CONNECT_EPP_MAYBE); ppa_reset_pulse(ppb); udelay(1000); ppa_disconnect(dev); udelay(1000); return 0; } return -ENODEV;}static int ppa_adjust_queue(struct scsi_device *device){ blk_queue_bounce_limit(device->request_queue, BLK_BOUNCE_HIGH); return 0;}static struct scsi_host_template ppa_template = { .module = THIS_MODULE, .proc_name = "ppa", .proc_info = ppa_proc_info, .name = "Iomega VPI0 (ppa) interface", .queuecommand = ppa_queuecommand, .eh_abort_handler = ppa_abort, .eh_bus_reset_handler = ppa_reset, .eh_host_reset_handler = ppa_reset, .bios_param = ppa_biosparam, .this_id = -1, .sg_tablesize = SG_ALL, .cmd_per_lun = 1, .use_clustering = ENABLE_CLUSTERING, .can_queue = 1, .slave_alloc = ppa_adjust_queue,};/*************************************************************************** * Parallel port probing routines * ***************************************************************************/static LIST_HEAD(ppa_hosts);static int __ppa_attach(struct parport *pb){ struct Scsi_Host *host; DECLARE_WAIT_QUEUE_HEAD_ONSTACK(waiting); DEFINE_WAIT(wait); ppa_struct *dev; int ports; int modes, ppb, ppb_hi; int err = -ENOMEM; dev = kzalloc(sizeof(ppa_struct), GFP_KERNEL); if (!dev) return -ENOMEM; dev->base = -1; dev->mode = PPA_AUTODETECT; dev->recon_tmo = PPA_RECON_TMO; init_waitqueue_head(&waiting); dev->dev = parport_register_device(pb, "ppa", NULL, ppa_wakeup, NULL, 0, dev); if (!dev->dev) goto out; /* Claim the bus so it remembers what we do to the control * registers. [ CTR and ECP ] */ err = -EBUSY; dev->waiting = &waiting; prepare_to_wait(&waiting, &wait, TASK_UNINTERRUPTIBLE); if (ppa_pb_claim(dev)) schedule_timeout(3 * HZ); if (dev->wanted) { printk(KERN_ERR "ppa%d: failed to claim parport because " "a pardevice is owning the port for too long " "time!\n", pb->number); ppa_pb_dismiss(dev); dev->waiting = NULL; finish_wait(&waiting, &wait); goto out1; } dev->waiting = NULL; finish_wait(&waiting, &wait); ppb = dev->base = dev->dev->port->base; ppb_hi = dev->dev->port->base_hi; w_ctr(ppb, 0x0c); modes = dev->dev->port->modes; /* Mode detection works up the chain of speed * This avoids a nasty if-then-else-if-... tree */ dev->mode = PPA_NIBBLE; if (modes & PARPORT_MODE_TRISTATE) dev->mode = PPA_PS2; if (modes & PARPORT_MODE_ECP) { w_ecr(ppb_hi, 0x20); dev->mode = PPA_PS2; } if ((modes & PARPORT_MODE_EPP) && (modes & PARPORT_MODE_ECP)) w_ecr(ppb_hi, 0x80); /* Done configuration */ err = ppa_init(dev); ppa_pb_release(dev); if (err) goto out1; /* now the glue ... */ if (dev->mode == PPA_NIBBLE || dev->mode == PPA_PS2) ports = 3; else ports = 8; INIT_DELAYED_WORK(&dev->ppa_tq, ppa_interrupt); err = -ENOMEM; host = scsi_host_alloc(&ppa_template, sizeof(ppa_struct *)); if (!host) goto out1; host->io_port = pb->base; host->n_io_port = ports; host->dma_channel = -1; host->unique_id = pb->number; *(ppa_struct **)&host->hostdata = dev; dev->host = host; list_add_tail(&dev->list, &ppa_hosts); err = scsi_add_host(host, NULL); if (err) goto out2; scsi_scan_host(host); return 0;out2: list_del_init(&dev->list); scsi_host_put(host);out1: parport_unregister_device(dev->dev);out: kfree(dev); return err;}static void ppa_attach(struct parport *pb){ __ppa_attach(pb);}static void ppa_detach(struct parport *pb){ ppa_struct *dev; list_for_each_entry(dev, &ppa_hosts, list) { if (dev->dev->port == pb) { list_del_init(&dev->list); scsi_remove_host(dev->host); scsi_host_put(dev->host); parport_unregister_device(dev->dev); kfree(dev); break; } }}static struct parport_driver ppa_driver = { .name = "ppa", .attach = ppa_attach, .detach = ppa_detach,};static int __init ppa_driver_init(void){ printk(KERN_INFO "ppa: Version %s\n", PPA_VERSION); return parport_register_driver(&ppa_driver);}static void __exit ppa_driver_exit(void){ parport_unregister_driver(&ppa_driver);}module_init(ppa_driver_init);module_exit(ppa_driver_exit);MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?