Return to BSD News archive
#! rnews 19424 bsd Path: euryale.cc.adfa.oz.au!newshost.carno.net.au!harbinger.cc.monash.edu.au!news.mira.net.au!news.netspace.net.au!news.mel.connect.com.au!munnari.OZ.AU!news.ecn.uoknor.edu!feed1.news.erols.com!howland.erols.net!newsxfer3.itd.umich.edu!su-news-hub1.bbnplanet.com!news.bbnplanet.com!cpk-news-hub1.bbnplanet.com!mindspring!psinntp!psinntp!pubxfer.news.psi.net!usenet From: Luoqi Chen <luoqi@watermarkgroup.com> Newsgroups: comp.unix.bsd.freebsd.misc Subject: Re: Parallel Port ZIP Drive Date: Wed, 22 Jan 1997 19:50:13 -0500 Organization: The Watermark Group Lines: 748 Message-ID: <32E6B5BC.2278@watermarkgroup.com> References: <5c5bhn$3kv@news.fe.up.pt> NNTP-Posting-Host: 38.246.139.33 Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="------------48026FF63BDE" X-Mailer: Mozilla 3.01Gold (Macintosh; I; PPC) To: Jorge Goncalves <mec204@crazy.fe.up.pt> Xref: euryale.cc.adfa.oz.au comp.unix.bsd.freebsd.misc:34037 This is a multi-part message in MIME format. --------------48026FF63BDE Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit Jorge Goncalves wrote: > > Is there support for this kind of device in FreeBSD? > > Thanks. > > -- > > Jorge Goncalves > mec204@crazy.fe.up.pt Sometimes ago there was a posting about this. I didn't save the article, but I downloaded the driver source code it mentioned. I haven't tried it myself, but it has a very detailed description at the top. -lq ---------------------- ppa3.c ------------------ --------------48026FF63BDE Content-Type: text/plain; charset=us-ascii; x-mac-type="54455854"; x-mac-creator="74747874"; name="ppa3.c" Content-Transfer-Encoding: 7bit Content-Description: SimpleText Document Content-Disposition: inline; filename="ppa3.c" /* * /sys/i386/isa/ppa3.c * * FreeBSD R2.1.0 driver for PPA3 adapter * embedded in the IOMEGA ZIP 100 drive. * ------------------------------------------------------------------- * * Release: 0.10 * Release: 0.11 * - *int32 becomes *int32_t according to * Subject: Installing parallel port IOMega ZIP drive? * Author: Nathan Melhorn at TELEBIT_CHELMSFORD_NFS * Date: 96.07.10 18:48 * form the freebsd-questions mailing-list * * Release: 0.20 * - Interrupt emulation with timeout(). * * THIS SOFTWARE IS DISTRIBUTED WITH NO WARRANTY. * ------------------------------------------------------------------- * * The Iomega Zip 100 drive is an external drive with removable 100Mb * disks. It is connected either to the parallel port (with an * embedded SCSI-2 adapter) or to a SCSI-2 adapter. An IDE version * is also available. * * | See http://www.iomega.com for details. * * This driver has been developed and tested for FreeBSD R2.1.0 with: * - an AMD486-DX120 processor * - no other SCSI hardware * * Add the following lines in your kernel configuration files: * ------------------------------------------------------------------- * * in /sys/i386/conf/files.i386, * * i386/isa/ppa3.c optional ppa device-driver * ------------------------------------------------------------------- * * in /sys/i386/conf/MACHINE, * * controller scbus0 #base SCSI code * device sd0 #SCSI disks * * controller isa0 * device ppa0 at isa? port 0x278 bio * ------------------------------------------------------------------- * * | The SCSI_DELAY may not be a good option. The driver was developed * | without it. * * | The base address of your parallel port (here 0x278) may be different. * | Printer services are not supported by ppa3.c ; your parallel port * | should not be shared with the lpt driver. Thus you have to disable * | the printer driver either in your MACHINE config file or at boot * | with the -c option (see boot(8) manpage). If you have more than one * | printer port, configure the base addresses as needed. * * (see /sys/i386/conf/LINT and config(8) manpage for details) * * You may have to increase timeouts, PPA_MAX_RETRY or PPA_SPEED_LOW. * * At startup, the size of the disk is probed and the driver * should report 196608 512 bytes blocks. * If the size reported is 0 the drive won't be usable, you have to * | reboot to get the right size. Actually, the scsi user interface * | should allow to re-probe the drive... * Toplevel scsi driver will report "using ficticious geometry..." * because it can't sense the drive geometry. This is not a problem. * * | The probe may take a while... the target of the drive is 6 on the * | ZIP scsi bus. Thus 6 devices are probed before the good one. * * Use fdisk to be sure the DOS partition of the disk is the 4th one. * Then, mount -t msdos /dev/sd0s4 /mnt, for exemple. * * About benchs... * | Transfer rate is around 15kB/s. * Writing directly do /dev/sd0s4 without the msdos filesystem * may be faster. * * | Try the parameter list below includes. With good profiling * | you can certainly get more than 15Kb/s with a Pentium or higher. * * | #define PPA_INTR_TMO 10 * | This is the timeout duration for each SCSI request. Increase it * | to get more CPU time and a worse transfer rate. * * | #define PPA_SPEED_LOW 3 * | This is the delay after each port output. * * | #define PPA_OPENNINGS 2 * | The size of the device queue. Since all requests are timedout() * | the maximum size depends on the timer queue. * * | #define PPA_SELECT_TMO 5000 * | #define PPA_SPIN_TMO 100000 * | #define PPA_MAX_RETRY 10 * | These parameters are timeouts, you should increase them if * | you have errors. * * This driver is a port of the Linux driver, * see http://www.torque.net/zip.html for details. * ------------------------------------------------------------------- * * Send bug report, suggestions to Nicolas.Souchu@prism.uvsq.fr, * see http://www.prism.uvsq.fr/~son/ppa3.html for more info. * ------------------------------------------------------------------- */ #include <sys/types.h> #ifdef KERNEL #include <sys/param.h> #include <sys/systm.h> #include <sys/errno.h> #include <sys/ioctl.h> #include <sys/malloc.h> #include <sys/buf.h> #include <sys/proc.h> #include <sys/user.h> #include <syslog.h> #include <machine/clock.h> #include <i386/isa/isa_device.h> #include <machine/clock.h> #include <machine/cpu.h> /* XXX for bootverbose: a funny place */ #endif /* KERNEL */ #include <scsi/scsi_all.h> #include <scsi/scsi_disk.h> #include <scsi/scsiconf.h> #include <sys/devconf.h> #ifdef KERNEL #include <sys/kernel.h> #endif /*KERNEL */ #include "ppa.h" /* ppa.h is generated by 'config' */ #if NPPA > 0 /* -------------------------------------------------------------------- * HERE ARE THINGS YOU MAY HAVE/WANT TO CHANGE */ #define PPA_INTR_TMO 10 /* timeout() duration */ #define PPA_SPEED_LOW 3 #define PPA_OPENNINGS 2 /* ppa queue size */ #define PPA_SELECT_TMO 5000 #define PPA_SPIN_TMO 100000 #define PPA_MAX_RETRY 10 /* * DO NOT MODIFY ANYTHING UNDER THIS LINE * -------------------------------------------------------------------- */ #define barrier() __asm__("": : :"memory") #define PPA_INITIATOR 0x7 #define PPA_SPEED_HIGH 1 #define PPA_SECTOR_SIZE 512 #define PPA_BUFFER_SIZE 0x12000 /* 64k should be enough, but to be sure... */ #define PPA_NIBBLE 0x1 struct ppa_data { short ppa_base; struct scsi_xfer * ppa_xfer; unsigned char ppa_buffer[PPA_BUFFER_SIZE]; unsigned int ppa_stat; unsigned int ppa_count; unsigned int ppa_flags; unsigned char ppa_port_delay; int flags; struct scsi_link sc_link; /* prototype for subdevs */ }; #define ppaio_outb(ppa,port,byte) { \ outb ((ppa)->ppa_base + port, byte); \ DELAY ((ppa)->ppa_port_delay); \ } #define ppaio_inb(ppa,port) ((char) inb((ppa)->ppa_base + port)) extern void ppaio_do_reset __P((struct ppa_data *)); extern int ppaio_init __P((struct ppa_data *)); extern int ppaio_do_scsi __P((struct ppa_data *, int, int, char *, int, char *, int, int *, int *)); struct ppa_data * ppadata[NPPA]; int ppaprobe(); int ppaattach(); int32_t ppa_scsi_cmd(); void ppaminphys(); u_int32_t ppa_adapter_info(); #ifdef KERNEL struct scsi_adapter ppa_switch = { ppa_scsi_cmd, ppaminphys, 0, 0, ppa_adapter_info, "ppa", { 0, 0 } }; /* * The below structure is so we have a default dev struct * for out link struct. */ struct scsi_device ppa_dev = { NULL, /* Use default error handler */ NULL, /* have a queue, served by this */ NULL, /* have no async handler */ NULL, /* Use default 'done' routine */ "ppa", 0, { 0, 0 } }; struct isa_driver ppadriver = { ppaprobe, ppaattach, "ppa" }; static struct kern_devconf kdc_ppa[NPPA] = { { 0, 0, 0, /* filled in by dev_attach */ "ppa", 0, { MDDT_ISA, 0, "bio" }, isa_generic_externalize, 0, 0, ISA_EXTERNALLEN, &kdc_isa0, /* parent */ 0, /* parentdata */ DC_UNCONFIGURED, /* always start out here */ "Iomega PPA3 Parallel Port SCSI host adapter", DC_CLS_MISC /* SCSI host adapters aren't special */ } }; static inline void ppa_registerdev (struct isa_device * id) { if (id->id_unit) kdc_ppa[id->id_unit] = kdc_ppa[0]; kdc_ppa[id->id_unit].kdc_unit = id->id_unit; kdc_ppa[id->id_unit].kdc_parentdata = id; dev_attach(&kdc_ppa[id->id_unit]); } #endif /* KERNEL */ static int ppaunit = 0; u_int32_t ppa_adapter_info (int unit) { return 1; } static int ppa_init (int unit) { struct ppa_data * ppa = ppadata[unit]; int rs; ppaio_do_reset (ppa); if ((rs = ppaio_init (ppa))) { printf ("ppa%d: ppa_init() failed (%d)\n", unit, rs); return 1; } return 0; } int ppaprobe (struct isa_device * dev) { int unit = ppaunit; struct ppa_data * ppa; int rv = -1; /* * find unit and check we have that many defined */ if (unit >= NPPA) { printf("ppa%d: unit number too high\n", unit); return 0; } dev->id_unit = unit; /* * Allocate a storage area for us */ if (ppadata[unit]) { printf("ppa%d: memory already allocated\n", unit); return 0; } ppa = malloc (sizeof(struct ppa_data), M_TEMP, M_NOWAIT); if (!ppa) { printf("ppa%d: cannot malloc!\n", unit); return 0; } bzero(ppa, sizeof(struct ppa_data)); ppadata[unit] = ppa; ppa->ppa_base = dev->id_iobase; ppa->ppa_port_delay = PPA_SPEED_LOW; #ifndef DEV_LKM ppa_registerdev(dev); #endif if ((rv = ppa_init(unit)) != 0) { #ifdef DEBUG_PPA3 log (LOG_DEBUG, "ppaprobe(): ppa_init() error (%d)\n", rv); #endif ppadata[unit] = NULL; free(ppa, M_TEMP); return 0; } ppaunit ++; return 4; } /* * Attach all the sub-devices we can find. */ int ppaattach (struct isa_device * dev) { int unit = dev->id_unit; struct ppa_data * ppa = ppadata[unit]; struct scsibus_data * scbus; /* * fill in the prototype scsi_link. */ ppa->sc_link.adapter_unit = unit; ppa->sc_link.adapter_targ = PPA_INITIATOR; ppa->sc_link.adapter = &ppa_switch; ppa->sc_link.device = &ppa_dev; ppa->sc_link.flags = ppa->flags; ppa->sc_link.opennings = PPA_OPENNINGS; /* * Prepare the scsibus_data area for the upperlevel * scsi code. */ scbus = scsi_alloc_bus(); if(!scbus) return 0; scbus->adapter_link = &ppa->sc_link; /* * Ask the adapter what subunits are present. */ kdc_ppa[unit].kdc_state = DC_BUSY; /* host adapters are always busy */ scsi_attachdevs(scbus); return 1; } void ppaminphys(struct buf * bp) { if (bp->b_bcount > PPA_BUFFER_SIZE) bp->b_bcount = PPA_BUFFER_SIZE; return; } /* * Must be called at splbio() level. */ static void ppaintr (struct ppa_data * ppa, struct scsi_xfer * xs) { int timeout, retry; #ifdef DEBUG_PPA3 log (LOG_DEBUG, "Entering ppaintr()...\n"); #endif if (xs->datalen && !(xs->flags & SCSI_DATA_IN)) bcopy (xs->data, ppa->ppa_buffer, xs->datalen); retry = 0; do { timeout = ppaio_do_scsi (ppa, PPA_INITIATOR, xs->sc_link->target, (char *) xs->cmd, xs->cmdlen, ppa->ppa_buffer, xs->datalen, &ppa->ppa_stat, &ppa->ppa_count); } while ((timeout || ppa->ppa_count < xs->datalen) && retry++ < PPA_MAX_RETRY); #ifdef DEBUG_PPA3 log (LOG_DEBUG, "ppa_do_scsi = %d, status = 0x%x, count = %d\n", timeout, ppa->ppa_stat, ppa->ppa_count); #endif if (timeout || ppa->ppa_count < xs->datalen) { xs->error = XS_TIMEOUT; goto error; } if (ppa->ppa_stat) { xs->error = XS_SENSE; goto error; } if (xs->datalen && (xs->flags & SCSI_DATA_IN)) bcopy (ppa->ppa_buffer, xs->data, xs->datalen); xs->resid = 0; xs->error = XS_NOERROR; error: xs->flags |= ITSDONE; scsi_done (xs); return; } static void ppa_softint (void * arg) { struct scsi_xfer * xs = (struct scsi_xfer *) arg; int s; s = splbio(); ppaintr (ppadata[xs->sc_link->adapter_unit], xs); splx(s); return; } /* * Must be called at splbio() level. */ #define PPA_SCHEDINTR(arg) timeout(ppa_softint, (void *) arg, PPA_INTR_TMO); int32_t ppa_scsi_cmd (struct scsi_xfer * xs) { int s; if (xs->flags & SCSI_DATA_UIO) { log (LOG_INFO, "UIO not supported by ppa_driver !\n"); xs->error = XS_DRIVER_STUFFUP; return COMPLETE; } #ifdef DEBUG_PPA3 { int i; log (LOG_DEBUG, "ppa_scsi_cmd(): xs->flags = 0x%x, "\ "xs->data = 0x%x, xs->datalen = %d\n", xs->flags, xs->data, xs->datalen); for (i=0; i<xs->cmdlen; i++) log (LOG_DEBUG, "%2x", ((u_char *) xs->cmd)[i]); log (LOG_DEBUG, "\n"); } #endif if (xs->flags & SCSI_NOMASK) { ppaintr (ppadata[xs->sc_link->adapter_unit], xs); return COMPLETE; } s = splbio (); PPA_SCHEDINTR(xs); splx (s); return SUCCESSFULLY_QUEUED; } static void ppaio_d_pulse (struct ppa_data * ppa, char b) { ppaio_outb (ppa, 0, b); ppaio_outb (ppa, 2, 0xc); ppaio_outb (ppa, 2, 0xe); ppaio_outb (ppa, 2, 0xc); ppaio_outb (ppa, 2, 0x4); ppaio_outb (ppa, 2, 0xc); } static void ppaio_disconnect (struct ppa_data * ppa) { ppaio_d_pulse(ppa, 0); ppaio_d_pulse(ppa, 0x3c); ppaio_d_pulse(ppa, 0x20); ppaio_d_pulse(ppa, 0xf); } static void ppaio_c_pulse (struct ppa_data * ppa, char b) { ppaio_outb (ppa, 0, b); ppaio_outb (ppa, 2, 0x4); ppaio_outb (ppa, 2, 0x6); ppaio_outb (ppa, 2, 0x4); ppaio_outb (ppa, 2, 0xc); } static void ppaio_connect (struct ppa_data * ppa) { ppaio_c_pulse (ppa, 0); ppaio_c_pulse (ppa, 0x3c); ppaio_c_pulse (ppa, 0x20); ppaio_c_pulse (ppa, 0x8f); } void ppaio_do_reset (struct ppa_data * ppa) { ppaio_outb (ppa, 2, 0); /* This is really just a guess */ DELAY (100); } /* 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. * * Grant R. Guenther */ int ppaio_init (struct ppa_data * ppa) { char r, s; ppaio_outb (ppa, 0, 0xaa); if (ppaio_inb (ppa, 0) != (char) 0xaa) return 1; ppaio_disconnect (ppa); ppaio_connect (ppa); ppaio_outb (ppa, 2, 0x6); if ((ppaio_inb (ppa, 1) & 0xf0) != 0xf0) return 2; ppaio_outb (ppa, 2, 0x4); if ((ppaio_inb (ppa, 1) & 0xf0) != 0x80) return 3; ppaio_disconnect (ppa); s = ppaio_inb (ppa, 2); ppaio_outb (ppa, 2, 0xec); ppaio_outb (ppa, 0, 0x55); r = ppaio_inb (ppa, 0); if (r != (char) 0xff) { ppa->ppa_flags |= PPA_NIBBLE; if (r != (char) 0x55) return 4; ppaio_outb (ppa, 0, 0xaa); if (ppaio_inb (ppa, 0) != (char) 0xaa) return 5; } ppaio_outb (ppa, 2, s); ppaio_connect (ppa); ppaio_outb (ppa, 0, 0x40); ppaio_outb (ppa, 2, 0x8); ppaio_outb (ppa, 2, 0xc); ppaio_disconnect (ppa); return 0; } char ppaio_select (struct ppa_data * ppa, int initiator, int target) { char r; int k; r = ppaio_inb (ppa, 1); ppaio_outb (ppa, 0, (1<<target)); ppaio_outb (ppa, 2, 0xe); ppaio_outb (ppa, 2, 0xc); ppaio_outb (ppa, 0, (1<<initiator)); ppaio_outb (ppa, 2, 0x8); k = 0; while (!((r = ppaio_inb (ppa, 1)) & 0xf0) && (k++ < PPA_SELECT_TMO)) barrier(); return r; } /* 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, but we call the scheduler to allow other * processes to run while we are waiting. (The LP driver does this * in polling mode.) * * Grant R. Guenther */ char ppaio_wait (struct ppa_data * ppa) { int k; char r; k = 0; while (!((r = ppaio_inb (ppa, 1)) & 0x80) && (k++ < PPA_SPIN_TMO)) barrier (); if (k < PPA_SPIN_TMO) return (r & 0xf0); ppaio_disconnect (ppa); return 0; /* command timed out */ } int ppaio_do_scsi (struct ppa_data * ppa, int host, int target, char * command, int clen, char * buffer, int blen, int * result, int * count) { char r, l, h; int dir, cnt, fast; int k, error = 0; ppaio_connect(ppa); r = ppaio_select(ppa,host,target); if (r == 0) { ppaio_disconnect (ppa); return 1; /* select timeout */ } ppaio_outb(ppa,2,0xc); if (!ppaio_wait(ppa)) return 2; /* not ready for command */ for (k=0;k<clen;k++) { /* send the command */ ppaio_outb(ppa,0,command[k]); ppaio_outb(ppa,2,0xe); ppaio_outb(ppa,2,0xc); if (!( r = ppaio_wait(ppa))) return 3; } /* * Completion ... */ fast = ((command[0] == READ_COMMAND) || (command[0] == READ_BIG) || (command[0] == WRITE_COMMAND) || (command[0] == WRITE_BIG)); cnt = 0; dir = 0; dir = (r == (char) 0xc0); ppa->ppa_port_delay = PPA_SPEED_HIGH; while (r != (char) 0xf0) { if (dir) do { ppaio_outb(ppa,0,buffer[cnt]); ppaio_outb(ppa,2,0xe); ppaio_outb(ppa,2,0xc); cnt++; } while (fast && (cnt % PPA_SECTOR_SIZE)); else { if (ppa->ppa_flags & PPA_NIBBLE) do { ppaio_outb(ppa,2,0x4); h = ppaio_inb(ppa,1); ppaio_outb(ppa,2,0x6); l = ppaio_inb(ppa,1); buffer[cnt] = ((l >> 4) & 0x0f) + (h & 0xf0); cnt ++; } while (fast && (cnt % PPA_SECTOR_SIZE)); else do { ppaio_outb (ppa, 2, 0x25); buffer[cnt] = ppaio_inb (ppa, 0); ppaio_outb (ppa, 2, 0x27); cnt++; } while (fast && (cnt % PPA_SECTOR_SIZE)); if ((ppa->ppa_flags & PPA_NIBBLE) == 0) { ppaio_outb (ppa, 2, 0x5); ppaio_outb (ppa, 2, 0x4); } ppaio_outb(ppa,2,0xc); } if (!(r = ppaio_wait(ppa))) { error = 6; goto error; } } ppa->ppa_port_delay = PPA_SPEED_LOW; *count = cnt; ppaio_outb(ppa,2,0x4); /* now read status byte */ h = ppaio_inb(ppa,1); ppaio_outb(ppa,2,0x6); l = ppaio_inb(ppa,1); ppaio_outb(ppa,2,0xc); *result = (l >> 4) + (h & 0xf0); ppaio_outb(ppa,2,0xe); ppaio_outb(ppa,2,0xc); ppaio_disconnect(ppa); return 0; error: ppa->ppa_port_delay = PPA_SPEED_LOW; ppaio_disconnect (ppa); return error; } #endif --------------48026FF63BDE--