📄 imm.c
字号:
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 imm_wait. */ w_ctr(ppb, 0x0c); r = (r_str(ppb) & 0xb8); /* * while (device is not ready to send status byte) * loop; */ while (r != (unsigned char) 0xb8) { /* * 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; /* * FAIL if: * a) Drive status is screwy (!ready && !present) * b) Drive is requesting/sending more data than expected */ if (((r & 0x88) != 0x88) || (cmd->SCp.this_residual <= 0)) { imm_fail(dev, DID_ERROR); return -1; /* ERROR_RETURN */ } /* determine if we should use burst I/O */ if (dev->rd == 0) { fast = (bulk && (cmd->SCp.this_residual >= IMM_BURST_SIZE)) ? IMM_BURST_SIZE : 2; status = imm_out(dev, cmd->SCp.ptr, fast); } else { fast = (bulk && (cmd->SCp.this_residual >= IMM_BURST_SIZE)) ? IMM_BURST_SIZE : 1; status = imm_in(dev, cmd->SCp.ptr, fast); } cmd->SCp.ptr += fast; cmd->SCp.this_residual -= fast; if (!status) { imm_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 = page_address(cmd->SCp.buffer->page) + cmd->SCp.buffer->offset; /* * Make sure that we transfer even number of bytes * otherwise it makes imm_byte_out() messy. */ if (cmd->SCp.this_residual & 0x01) cmd->SCp.this_residual++; } } /* Now check to see if the drive is ready to comunicate */ w_ctr(ppb, 0x0c); r = (r_str(ppb) & 0xb8); /* If not, drop back down to the scheduler and wait a timer tick */ if (!(r & 0x80)) return 0; } return 1; /* FINISH_RETURN */}/* * Since the IMM 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 imm_interrupt(void *data){ imm_struct *dev = (imm_struct *) data; struct scsi_cmnd *cmd = dev->cur_cmd; struct Scsi_Host *host = cmd->device->host; unsigned long flags; if (!cmd) { printk("IMM: bug in imm_interrupt\n"); return; } if (imm_engine(dev, cmd)) { INIT_WORK(&dev->imm_tq, imm_interrupt, (void *) dev); schedule_delayed_work(&dev->imm_tq, 1); return; } /* Command must of completed hence it is safe to let go... */#if IMM_DEBUG > 0 switch ((cmd->result >> 16) & 0xff) { case DID_OK: break; case DID_NO_CONNECT: printk("imm: no device at SCSI ID %i\n", cmd->device->id); break; case DID_BUS_BUSY: printk("imm: BUS BUSY - EPP timeout detected\n"); break; case DID_TIME_OUT: printk("imm: unknown timeout\n"); break; case DID_ABORT: printk("imm: told to abort\n"); break; case DID_PARITY: printk("imm: parity error (???)\n"); break; case DID_ERROR: printk("imm: internal driver error\n"); break; case DID_RESET: printk("imm: told to reset device\n"); break; case DID_BAD_INTR: printk("imm: bad interrupt (???)\n"); break; default: printk("imm: bad return code (%02x)\n", (cmd->result >> 16) & 0xff); }#endif if (cmd->SCp.phase > 1) imm_disconnect(dev); imm_pb_dismiss(dev); spin_lock_irqsave(host->host_lock, flags); dev->cur_cmd = NULL; cmd->scsi_done(cmd); spin_unlock_irqrestore(host->host_lock, flags); return;}static int imm_engine(imm_struct *dev, struct scsi_cmnd *cmd){ unsigned short ppb = dev->base; unsigned char l = 0, h = 0; int retv, x; /* First check for any errors that may have 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 */ imm_fail(dev, DID_BUS_BUSY); return 0; } return 1; /* wait until imm_wakeup claims parport */ /* Phase 1 - Connected */ case 1: imm_connect(dev, CONNECT_EPP_MAYBE); cmd->SCp.phase++; /* Phase 2 - We are now talking to the scsi bus */ case 2: if (!imm_select(dev, cmd->device->id)) { imm_fail(dev, DID_NO_CONNECT); return 0; } cmd->SCp.phase++; /* Phase 3 - Ready to accept a command */ case 3: w_ctr(ppb, 0x0c); if (!(r_str(ppb) & 0x80)) return 1; if (!imm_send_command(cmd)) return 0; cmd->SCp.phase++; /* Phase 4 - Setup scatter/gather buffers */ case 4: 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 = page_address(cmd->SCp.buffer->page) + cmd->SCp.buffer->offset; } 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++; if (cmd->SCp.this_residual & 0x01) cmd->SCp.this_residual++; /* Phase 5 - Pre-Data transfer stage */ case 5: /* Spin lock for BUSY */ w_ctr(ppb, 0x0c); if (!(r_str(ppb) & 0x80)) return 1; /* Require negotiation for read requests */ x = (r_str(ppb) & 0xb8); dev->rd = (x & 0x10) ? 1 : 0; dev->dp = (x & 0x20) ? 0 : 1; if ((dev->dp) && (dev->rd)) if (imm_negotiate(dev)) return 0; cmd->SCp.phase++; /* Phase 6 - Data transfer stage */ case 6: /* Spin lock for BUSY */ w_ctr(ppb, 0x0c); if (!(r_str(ppb) & 0x80)) return 1; if (dev->dp) { retv = imm_completion(cmd); if (retv == -1) return 0; if (retv == 0) return 1; } cmd->SCp.phase++; /* Phase 7 - Post data transfer stage */ case 7: if ((dev->dp) && (dev->rd)) { if ((dev->mode == IMM_NIBBLE) || (dev->mode == IMM_PS2)) { w_ctr(ppb, 0x4); w_ctr(ppb, 0xc); w_ctr(ppb, 0xe); w_ctr(ppb, 0x4); } } cmd->SCp.phase++; /* Phase 8 - Read status/message */ case 8: /* Check for data overrun */ if (imm_wait(dev) != (unsigned char) 0xb8) { imm_fail(dev, DID_ERROR); return 0; } if (imm_negotiate(dev)) return 0; if (imm_in(dev, &l, 1)) { /* read status byte */ /* Check for optional message byte */ if (imm_wait(dev) == (unsigned char) 0xb8) imm_in(dev, &h, 1); cmd->result = (DID_OK << 16) + (l & STATUS_MASK); } if ((dev->mode == IMM_NIBBLE) || (dev->mode == IMM_PS2)) { w_ctr(ppb, 0x4); w_ctr(ppb, 0xc); w_ctr(ppb, 0xe); w_ctr(ppb, 0x4); } return 0; /* Finished */ break; default: printk("imm: Invalid scsi phase\n"); } return 0;}static int imm_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)){ imm_struct *dev = imm_dev(cmd->device->host); if (dev->cur_cmd) { printk("IMM: bug in imm_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 */ INIT_WORK(&dev->imm_tq, imm_interrupt, dev); schedule_work(&dev->imm_tq); imm_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 imm_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]); } return 0;}static int imm_abort(struct scsi_cmnd *cmd){ imm_struct *dev = imm_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 imm_reset_pulse(unsigned int base){ w_ctr(base, 0x04); w_dtr(base, 0x40); udelay(1); w_ctr(base, 0x0c); w_ctr(base, 0x0d); udelay(50); w_ctr(base, 0x0c); w_ctr(base, 0x04);}static int imm_reset(struct scsi_cmnd *cmd){ imm_struct *dev = imm_dev(cmd->device->host); if (cmd->SCp.phase) imm_disconnect(dev); dev->cur_cmd = NULL; /* Forget the problem */ imm_connect(dev, CONNECT_NORMAL); imm_reset_pulse(dev->base); udelay(1000); /* device settle delay */ imm_disconnect(dev); udelay(1000); /* device settle delay */ return SUCCESS;}static int device_check(imm_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 char 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 = IMM_EPP_32; second_pass: imm_connect(dev, CONNECT_EPP_MAYBE); /* Select SCSI device */ if (!imm_select(dev, loop)) { imm_disconnect(dev); continue; } printk("imm: Found device at ID %i, Attempting to use %s\n", loop, IMM_MODE_STRING[dev->mode]); /* Send SCSI command */ status = 1; w_ctr(ppb, 0x0c); for (l = 0; (l < 3) && (status); l++) status = imm_out(dev, &cmd[l << 1], 2); if (!status) { imm_disconnect(dev); imm_connect(dev, CONNECT_EPP_MAYBE); imm_reset_pulse(dev->base); udelay(1000); imm_disconnect(dev); udelay(1000); if (dev->mode == IMM_EPP_32) { dev->mode = old_mode; goto second_pass; } printk("imm: Unable to establish communication\n"); return -EIO; } w_ctr(ppb, 0x0c); k = 1000000; /* 1 Second */ do { l = r_str(ppb); k--; udelay(1); } while (!(l & 0x80) && (k)); l &= 0xb8; if (l != 0xb8) { imm_disconnect(dev); imm_connect(dev, CONNECT_EPP_MAYBE); imm_reset_pulse(dev->base); udelay(1000); imm_disconnect(dev); udelay(1000); if (dev->mode == IMM_EPP_32) { dev->mode = old_mode; goto second_pass; } printk ("imm: Unable to establish communication\n"); return -EIO; } imm_disconnect(dev); printk ("imm: Communication established at 0x%x with ID %i using %s\n", ppb, loop, IMM_MODE_STRING[dev->mode]); imm_connect(dev, CONNECT_EPP_MAYBE); imm_reset_pulse(dev->base); udelay(1000); imm_disconnect(dev); udelay(1000); return 0; } printk("imm: No devices found\n"); return -ENODEV;}static int imm_adjust_queue(struct scsi_device *device){ blk_queue_bounce_limit(device->request_queue, BLK_BOUNCE_HIGH); return 0;}static struct scsi_host_template imm_template = { .module = THIS_MODULE, .proc_name = "imm", .proc_info = imm_proc_info, .name = "Iomega VPI2 (imm) interface", .queuecommand = imm_queuecommand, .eh_abort_handler = imm_abort, .eh_bus_reset_handler = imm_reset, .eh_host_reset_handler = imm_reset, .bios_param = imm_biosparam, .this_id = 7, .sg_tablesize = SG_ALL, .cmd_per_lun = 1, .use_clustering = ENABLE_CLUSTERING, .can_queue = 1, .slave_alloc = imm_adjust_queue,};/*************************************************************************** * Parallel port probing routines * ***************************************************************************/static LIST_HEAD(imm_hosts);static int __imm_attach(struct parport *pb){ struct Scsi_Host *host; imm_struct *dev; DECLARE_WAIT_QUEUE_HEAD(waiting); DEFINE_WAIT(wait); int ports; int modes, ppb; int err = -ENOMEM; init_waitqueue_head(&waiting); dev = kmalloc(sizeof(imm_struct), GFP_KERNEL); if (!dev) return -ENOMEM; memset(dev, 0, sizeof(imm_struct)); dev->base = -1; dev->mode = IMM_AUTODETECT; INIT_LIST_HEAD(&dev->list); dev->dev = parport_register_device(pb, "imm", NULL, imm_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 (imm_pb_claim(dev)) schedule_timeout(3 * HZ); if (dev->wanted) { printk(KERN_ERR "imm%d: failed to claim parport because " "a pardevice is owning the port for too long " "time!\n", pb->number); imm_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; dev->base_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 = IMM_NIBBLE; if (modes & PARPORT_MODE_TRISTATE) dev->mode = IMM_PS2; /* Done configuration */ err = imm_init(dev); imm_pb_release(dev); if (err) goto out1; /* now the glue ... */ if (dev->mode == IMM_NIBBLE || dev->mode == IMM_PS2) ports = 3; else ports = 8; INIT_WORK(&dev->imm_tq, imm_interrupt, dev); err = -ENOMEM; host = scsi_host_alloc(&imm_template, sizeof(imm_struct *)); if (!host) goto out1; host->io_port = pb->base; host->n_io_port = ports; host->dma_channel = -1; host->unique_id = pb->number; *(imm_struct **)&host->hostdata = dev; dev->host = host; list_add_tail(&dev->list, &imm_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 imm_attach(struct parport *pb){ __imm_attach(pb);}static void imm_detach(struct parport *pb){ imm_struct *dev; list_for_each_entry(dev, &imm_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 imm_driver = { .name = "imm", .attach = imm_attach, .detach = imm_detach,};static int __init imm_driver_init(void){ printk("imm: Version %s\n", IMM_VERSION); return parport_register_driver(&imm_driver);}static void __exit imm_driver_exit(void){ parport_unregister_driver(&imm_driver);}module_init(imm_driver_init);module_exit(imm_driver_exit);MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -