// SPDX-License-Identifier: GPL-2.0-only /* 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.) * * My unofficial company acronym list is 21 pages long: * FLA: Four letter acronym with built in facility for * future expansion to five letters.
*/
/* The following #define is to avoid a clash with hosts.c */ #define IMM_PROBE_SPP 0x0001 #define IMM_PROBE_PS2 0x0002 #define IMM_PROBE_ECR 0x0010 #define IMM_PROBE_EPP17 0x0100 #define IMM_PROBE_EPP19 0x0200
typedefstruct { 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 */ struct scsi_cmnd *cur_cmd; /* Current queued command */ struct delayed_work imm_tq; /* Polling interrupt stuff */ unsignedlong jstart; /* Jiffies at start */ unsigned failed:1; /* Failure flag */ unsigned dp:1; /* Data phase present */ unsigned rd:1; /* Read data in data phase */ unsigned wanted:1; /* Parport sharing busy flag */ unsignedint dev_no; /* Device number */
wait_queue_head_t *waiting; struct Scsi_Host *host; struct list_head list;
} imm_struct;
/* 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... (Too 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)
*/ staticint imm_write_info(struct Scsi_Host *host, char *buffer, int length)
{
imm_struct *dev = imm_dev(host);
#if IMM_DEBUG > 0 #define imm_fail(x,y) printk("imm: imm_fail(%i) from %s at line %d\n",\
y, __func__, __LINE__); imm_fail_func(x,y); staticinlinevoid
imm_fail_func(imm_struct *dev, int error_code) #else staticinlinevoid
imm_fail(imm_struct *dev, int error_code) #endif
{ /* If we fail a device then we trash status / message bytes */ if (dev->cur_cmd) {
dev->cur_cmd->result = error_code << 16;
dev->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.
*/ staticunsignedchar imm_wait(imm_struct *dev)
{ int k; unsignedshort ppb = dev->base; unsignedchar 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(dev, DID_TIME_OUT);
printk("imm timeout in imm_wait\n"); return 0; /* command timed out */
}
staticint 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
*/
unsignedshort base = tmp->base; unsignedchar a, mode;
switch (tmp->mode) { case IMM_NIBBLE:
mode = 0x00; break; case IMM_PS2:
mode = 0x01; break; default: return 0;
}
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)
*/ staticinlinevoid ecp_sync(imm_struct *dev)
{ int i, ppb_hi = dev->base_hi;
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");
}
}
staticint imm_byte_out(unsignedshort base, constchar *buffer, int len)
{ int i;
w_ctr(base, 0x4); /* apparently a sane mode */ for (i = len >> 1; i; i--) {
w_dtr(base, *buffer++);
w_ctr(base, 0x5); /* Drop STROBE low */
w_dtr(base, *buffer++);
w_ctr(base, 0x0); /* STROBE high + INIT low */
}
w_ctr(base, 0x4); /* apparently a sane mode */ return 1; /* All went well - we hope! */
}
staticint imm_nibble_in(unsignedshort base, char *buffer, int len)
{ unsignedchar l; int i;
/* * The following is based on documented timing signals
*/
w_ctr(base, 0x4); for (i = len; i; i--) {
w_ctr(base, 0x6);
l = (r_str(base) & 0xf0) >> 4;
w_ctr(base, 0x5);
*buffer++ = (r_str(base) & 0xf0) | l;
w_ctr(base, 0x4);
} return 1; /* All went well - we hope! */
}
staticint imm_byte_in(unsignedshort base, char *buffer, int len)
{ int i;
/* * The following is based on documented timing signals
*/
w_ctr(base, 0x4); for (i = len; i; i--) {
w_ctr(base, 0x26);
*buffer++ = r_dtr(base);
w_ctr(base, 0x25);
} return 1; /* All went well - we hope! */
}
staticint imm_out(imm_struct *dev, char *buffer, int len)
{ unsignedshort ppb = dev->base; int r = imm_wait(dev);
/* * Make sure that: * a) the SCSI bus is BUSY (device still listening) * b) the device is listening
*/ if ((r & 0x18) != 0x08) {
imm_fail(dev, DID_ERROR);
printk("IMM: returned SCSI status %2x\n", r); return 0;
} switch (dev->mode) { case IMM_EPP_32: case IMM_EPP_16: case IMM_EPP_8:
epp_reset(ppb);
w_ctr(ppb, 0x4); if (dev->mode == IMM_EPP_32 && !(((long) buffer | len) & 0x03))
outsl(ppb + 4, buffer, len >> 2); elseif (dev->mode == IMM_EPP_16 && !(((long) buffer | len) & 0x01))
outsw(ppb + 4, buffer, len >> 1); else
outsb(ppb + 4, buffer, len);
w_ctr(ppb, 0xc);
r = !(r_str(ppb) & 0x01);
w_ctr(ppb, 0xc);
ecp_sync(dev); break;
case IMM_NIBBLE: case IMM_PS2: /* 8 bit output, with a loop */
r = imm_byte_out(ppb, buffer, len); break;
default:
printk("IMM: bug in imm_out()\n");
r = 0;
} return r;
}
staticint imm_in(imm_struct *dev, char *buffer, int len)
{ unsignedshort ppb = dev->base; int r = imm_wait(dev);
/* * Make sure that: * a) the SCSI bus is BUSY (device still listening) * b) the device is sending data
*/ if ((r & 0x18) != 0x18) {
imm_fail(dev, DID_ERROR); return 0;
} switch (dev->mode) { case IMM_NIBBLE: /* 4 bit input, with a loop */
r = imm_nibble_in(ppb, buffer, len);
w_ctr(ppb, 0xc); break;
case IMM_PS2: /* 8 bit input, with a loop */
r = imm_byte_in(ppb, buffer, len);
w_ctr(ppb, 0xc); break;
case IMM_EPP_32: case IMM_EPP_16: case IMM_EPP_8:
epp_reset(ppb);
w_ctr(ppb, 0x24); if (dev->mode == IMM_EPP_32 && !(((long) buffer | len) & 0x03))
insw(ppb + 4, buffer, len >> 2); elseif (dev->mode == IMM_EPP_16 && !(((long) buffer | len) & 0x01))
insl(ppb + 4, buffer, len >> 1); else
insb(ppb + 4, buffer, len);
w_ctr(ppb, 0x2c);
r = !(r_str(ppb) & 0x01);
w_ctr(ppb, 0x2c);
ecp_sync(dev); break;
default:
printk("IMM: bug in imm_ins()\n");
r = 0; break;
} return r;
}
staticint imm_cpp(unsignedshort ppb, unsignedchar b)
{ /* * Comments on udelay values refer to the * Command Packet Protocol (CPP) timing diagram.
*/
staticint imm_select(imm_struct *dev, int target)
{ int k; unsignedshort ppb = dev->base;
/* * Firstly we want to make sure there is nothing * holding onto the SCSI bus.
*/
w_ctr(ppb, 0xc);
k = IMM_SELECT_TMO; do {
k--;
} while ((r_str(ppb) & 0x08) && (k));
if (!k) return 0;
/* * Now assert the SCSI ID (HOST and TARGET) on the data bus
*/
w_ctr(ppb, 0x4);
w_dtr(ppb, 0x80 | (1 << target));
udelay(1);
/* * Deassert SELIN first followed by STROBE
*/
w_ctr(ppb, 0xc);
w_ctr(ppb, 0xd);
/* * ACK should drop low while SELIN is deasserted. * FAULT should drop low when the SCSI device latches the bus.
*/
k = IMM_SELECT_TMO; do {
k--;
} while (!(r_str(ppb) & 0x08) && (k));
/* * Place the interface back into a sane state (status mode)
*/
w_ctr(ppb, 0xc); return (k) ? 1 : 0;
}
if (autodetect) { int 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;
}
if (imm_connect(dev, 0) != 1) return -EIO;
imm_reset_pulse(dev->base);
mdelay(1); /* Delay to allow devices to settle */
imm_disconnect(dev);
mdelay(1); /* Another delay to allow devices to settle */
/* NOTE: IMM uses byte pairs */ for (k = 0; k < cmd->cmd_len; k += 2) if (!imm_out(dev, &cmd->cmnd[k], 2)) 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.
*/ staticint imm_completion(struct scsi_cmnd *const cmd)
{ /* Return codes: * -1 Error * 0 Told to schedule * 1 Finished data transfer
*/ struct scsi_pointer *scsi_pointer = imm_scsi_pointer(cmd);
imm_struct *dev = imm_dev(cmd->device->host); unsignedshort ppb = dev->base; unsignedlong start_jiffies = jiffies;
unsignedchar 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 != (unsignedchar) 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 || scsi_pointer->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 && scsi_pointer->this_residual >=
IMM_BURST_SIZE ? IMM_BURST_SIZE : 2;
status = imm_out(dev, scsi_pointer->ptr, fast);
} else {
fast = bulk && scsi_pointer->this_residual >=
IMM_BURST_SIZE ? IMM_BURST_SIZE : 1;
status = imm_in(dev, scsi_pointer->ptr, fast);
}
if (!status) {
imm_fail(dev, DID_BUS_BUSY); return -1; /* ERROR_RETURN */
} if (scsi_pointer->buffer && !scsi_pointer->this_residual) { /* if scatter/gather, advance to the next segment */ if (scsi_pointer->buffers_residual--) {
scsi_pointer->buffer =
sg_next(scsi_pointer->buffer);
scsi_pointer->this_residual =
scsi_pointer->buffer->length;
scsi_pointer->ptr = sg_virt(scsi_pointer->buffer);
/* * Make sure that we transfer even number of bytes * otherwise it makes imm_byte_out() messy.
*/ if (scsi_pointer->this_residual & 0x01)
scsi_pointer->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.
*/ staticvoid imm_interrupt(struct work_struct *work)
{
imm_struct *dev = container_of(work, imm_struct, imm_tq.work); struct scsi_cmnd *cmd = dev->cur_cmd; struct Scsi_Host *host = cmd->device->host; unsignedlong flags;
if (imm_engine(dev, cmd)) {
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 (imm_scsi_pointer(cmd)->phase > 1)
imm_disconnect(dev);
staticint imm_engine(imm_struct *dev, struct scsi_cmnd *const cmd)
{ struct scsi_pointer *scsi_pointer = imm_scsi_pointer(cmd); unsignedshort ppb = dev->base; unsignedchar 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 (scsi_pointer->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 */
case 2: /* Phase 2 - We are now talking to the scsi bus */ if (!imm_select(dev, scmd_id(cmd))) {
imm_fail(dev, DID_NO_CONNECT); return 0;
}
scsi_pointer->phase++;
fallthrough;
case 3: /* Phase 3 - Ready to accept a command */
w_ctr(ppb, 0x0c); if (!(r_str(ppb) & 0x80)) return 1;
if (!imm_send_command(cmd)) return 0;
scsi_pointer->phase++;
fallthrough;
/* * 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.
*/ staticint imm_biosparam(struct scsi_device *sdev, struct block_device *dev,
sector_t capacity, int ip[])
{
ip[0] = 0x40;
ip[1] = 0x20;
ip[2] = ((unsignedlong) capacity + 1) / (ip[0] * ip[1]); if (ip[2] > 1024) {
ip[0] = 0xff;
ip[1] = 0x3f;
ip[2] = ((unsignedlong) capacity + 1) / (ip[0] * ip[1]);
} return 0;
}
staticint 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 (imm_scsi_pointer(cmd)->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; default: /* SCSI command sent, can not abort */ return FAILED;
}
}
staticint device_check(imm_struct *dev, bool autodetect)
{ /* 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. */
old_mode = dev->mode; for (loop = 0; loop < 8; loop++) { /* Attempt to use EPP for Test Unit Ready */ if (autodetect && (ppb & 0x0007) == 0x0000)
dev->mode = IMM_EPP_8;
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);
/*************************************************************************** * Parallel port probing routines *
***************************************************************************/
static LIST_HEAD(imm_hosts);
/* * Finds the first available device number that can be alloted to the * new imm device and returns the address of the previous node so that * we can add to the tail and have a list in the ascending order.
*/
dev->dev = parport_register_dev_model(pb, "imm", &imm_cb, dev->dev_no); 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);
dev->base = dev->dev->port->base;
dev->base_hi = dev->dev->port->base_hi;
w_ctr(dev->base, 0x0c);
/* 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;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.