9300becc32
via ioctl; drop the traditional device minor mapping for those in favour of setting via lptctl(8) introduce notion of 'control' device (minor bit 0x100 set); ATM this device always skips the priming, which allows device open even with disconnected printer this also changes the default for interrupt use - polling is used by default now
749 lines
19 KiB
C
749 lines
19 KiB
C
/* $NetBSD: lpt.c,v 1.12 2004/02/03 21:15:03 jdolecek Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 1990 William F. Jolitz, TeleMuse
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This software is a component of "386BSD" developed by
|
|
* William F. Jolitz, TeleMuse.
|
|
* 4. Neither the name of the developer nor the name "386BSD"
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS A COMPONENT OF 386BSD DEVELOPED BY WILLIAM F. JOLITZ
|
|
* AND IS INTENDED FOR RESEARCH AND EDUCATIONAL PURPOSES ONLY. THIS
|
|
* SOFTWARE SHOULD NOT BE CONSIDERED TO BE A COMMERCIAL PRODUCT.
|
|
* THE DEVELOPER URGES THAT USERS WHO REQUIRE A COMMERCIAL PRODUCT
|
|
* NOT MAKE USE OF THIS WORK.
|
|
*
|
|
* FOR USERS WHO WISH TO UNDERSTAND THE 386BSD SYSTEM DEVELOPED
|
|
* BY WILLIAM F. JOLITZ, WE RECOMMEND THE USER STUDY WRITTEN
|
|
* REFERENCES SUCH AS THE "PORTING UNIX TO THE 386" SERIES
|
|
* (BEGINNING JANUARY 1991 "DR. DOBBS JOURNAL", USA AND BEGINNING
|
|
* JUNE 1991 "UNIX MAGAZIN", GERMANY) BY WILLIAM F. JOLITZ AND
|
|
* LYNNE GREER JOLITZ, AS WELL AS OTHER BOOKS ON UNIX AND THE
|
|
* ON-LINE 386BSD USER MANUAL BEFORE USE. A BOOK DISCUSSING THE INTERNALS
|
|
* OF 386BSD ENTITLED "386BSD FROM THE INSIDE OUT" WILL BE AVAILABLE LATE 1992.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE DEVELOPER BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* from: unknown origin, 386BSD 0.1
|
|
* From Id: lpt.c,v 1.55.2.1 1996/11/12 09:08:38 phk Exp
|
|
* From Id: nlpt.c,v 1.14 1999/02/08 13:55:43 des Exp
|
|
* FreeBSD: src/sys/dev/ppbus/lpt.c,v 1.15.2.3 2000/07/07 00:30:40 obrien Exp
|
|
*/
|
|
|
|
/*
|
|
* Device Driver for AT parallel printer port
|
|
* Written by William Jolitz 12/18/90
|
|
*/
|
|
|
|
/*
|
|
* Updated for ppbus by Nicolas Souchu
|
|
* [Mon Jul 28 1997]
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: lpt.c,v 1.12 2004/02/03 21:15:03 jdolecek Exp $");
|
|
|
|
#include "opt_ppbus_lpt.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/file.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/syslog.h>
|
|
|
|
#include <machine/bus.h>
|
|
|
|
#include <dev/ppbus/ppbus_1284.h>
|
|
#include <dev/ppbus/ppbus_base.h>
|
|
#include <dev/ppbus/ppbus_io.h>
|
|
#include <dev/ppbus/ppbus_msq.h>
|
|
#include <dev/ppbus/ppbus_var.h>
|
|
|
|
#include <dev/ppbus/lptvar.h>
|
|
#include <dev/ppbus/lptreg.h>
|
|
#include <dev/ppbus/lptio.h>
|
|
|
|
/* Autoconf functions */
|
|
static int lpt_probe(struct device *, struct cfdata *, void *);
|
|
static void lpt_attach(struct device *, struct device *, void *);
|
|
static int lpt_detach(struct device *, int);
|
|
|
|
/* Autoconf structure */
|
|
CFATTACH_DECL(lpt_ppbus, sizeof(struct lpt_softc), lpt_probe, lpt_attach,
|
|
lpt_detach, NULL);
|
|
|
|
extern struct cfdriver lpt_cd;
|
|
|
|
dev_type_open(lptopen);
|
|
dev_type_close(lptclose);
|
|
dev_type_read(lptread);
|
|
dev_type_write(lptwrite);
|
|
dev_type_ioctl(lptioctl);
|
|
|
|
const struct cdevsw lpt_cdevsw = {
|
|
lptopen, lptclose, lptread, lptwrite, lptioctl,
|
|
nostop, notty, nopoll, nommap, nokqfilter
|
|
};
|
|
|
|
|
|
/* Function prototypes */
|
|
static int lpt_detect(struct device *);
|
|
static int lpt_request_ppbus(struct lpt_softc *, int);
|
|
static int lpt_release_ppbus(struct lpt_softc *, int);
|
|
static int lpt_logstatus(const struct device * const, const unsigned char);
|
|
|
|
/*
|
|
* lpt_probe()
|
|
*/
|
|
static int
|
|
lpt_probe(struct device * parent, struct cfdata * match, void * aux)
|
|
{
|
|
/* Test ppbus's capability */
|
|
return lpt_detect(parent);
|
|
}
|
|
|
|
static void
|
|
lpt_attach(struct device * parent, struct device * self, void * aux)
|
|
{
|
|
struct lpt_softc * sc = (struct lpt_softc *) self;
|
|
struct ppbus_device_softc * ppbdev = &(sc->ppbus_dev);
|
|
struct ppbus_attach_args * args = aux;
|
|
char buf[64];
|
|
int error;
|
|
|
|
error = lpt_request_ppbus(sc, 0);
|
|
if(error) {
|
|
printf("%s(%s): error (%d) requesting bus(%s). Device not "
|
|
"properly attached.\n", __func__, self->dv_xname,
|
|
error, parent->dv_xname);
|
|
return;
|
|
}
|
|
|
|
/* Record capabilities */
|
|
ppbdev->capabilities = args->capabilities;
|
|
|
|
/* Allocate memory buffers */
|
|
if(ppbdev->capabilities & PPBUS_HAS_DMA) {
|
|
if(ppbus_dma_malloc(parent, &(sc->sc_inbuf),
|
|
&(sc->sc_in_baddr), BUFSIZE)) {
|
|
|
|
printf(" : cannot allocate input DMA buffer. Device "
|
|
"not properly attached!\n");
|
|
return;
|
|
}
|
|
if(ppbus_dma_malloc(parent, &(sc->sc_outbuf),
|
|
&(sc->sc_out_baddr), BUFSIZE)) {
|
|
|
|
ppbus_dma_free(parent, &(sc->sc_inbuf),
|
|
&(sc->sc_in_baddr), BUFSIZE);
|
|
printf(" : cannot allocate output DMA buffer. Device "
|
|
"not properly attached!\n");
|
|
return;
|
|
}
|
|
} else {
|
|
sc->sc_inbuf = malloc(BUFSIZE, M_DEVBUF, M_WAITOK);
|
|
sc->sc_outbuf = malloc(BUFSIZE, M_DEVBUF, M_WAITOK);
|
|
}
|
|
|
|
/* Print out mode */
|
|
ppbdev->ctx.mode = ppbus_get_mode(parent);
|
|
bitmask_snprintf(ppbdev->ctx.mode, "\20\1COMPATIBLE\2NIBBLE"
|
|
"\3PS2\4EPP\5ECP\6FAST_CENTR", buf, sizeof(buf));
|
|
printf(": port mode = %s\n", buf);
|
|
|
|
/* Initialize the device on open by default */
|
|
sc->sc_flags = LPT_PRIME;
|
|
|
|
lpt_release_ppbus(sc, 0);
|
|
}
|
|
|
|
static int
|
|
lpt_detach(struct device * self, int flags)
|
|
{
|
|
struct lpt_softc * lpt = (struct lpt_softc *) self;
|
|
struct ppbus_device_softc * ppbdev = (struct ppbus_device_softc *) lpt;
|
|
int err;
|
|
|
|
if(lpt->sc_state & HAVEBUS) {
|
|
err = lpt_release_ppbus(lpt, 0);
|
|
if(err) {
|
|
printf("%s error (%d) while releasing bus",
|
|
self->dv_xname, err);
|
|
if(flags & DETACH_FORCE) {
|
|
printf(", continuing (DETACH_FORCE)!\n");
|
|
}
|
|
else {
|
|
printf(", terminating!\n");
|
|
return 0;
|
|
}
|
|
}
|
|
lpt->sc_state &= ~HAVEBUS;
|
|
}
|
|
|
|
ppbdev->ctx.valid = 0;
|
|
|
|
/* Free memory buffers */
|
|
if(ppbdev->capabilities & PPBUS_HAS_DMA) {
|
|
ppbus_dma_free(self->dv_parent, &(lpt->sc_inbuf),
|
|
&(lpt->sc_in_baddr), BUFSIZE);
|
|
ppbus_dma_free(self->dv_parent, &(lpt->sc_outbuf),
|
|
&(lpt->sc_out_baddr), BUFSIZE);
|
|
} else {
|
|
free(lpt->sc_inbuf, M_DEVBUF);
|
|
free(lpt->sc_outbuf, M_DEVBUF);
|
|
}
|
|
|
|
if(!(flags & DETACH_QUIET)) {
|
|
printf("%s detached", self->dv_xname);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Grab bus for lpt device */
|
|
static int
|
|
lpt_request_ppbus(struct lpt_softc * lpt, int how)
|
|
{
|
|
struct device * dev = (struct device *) lpt;
|
|
int error;
|
|
|
|
error = ppbus_request_bus(dev->dv_parent, dev, how, (hz));
|
|
if (!(error)) {
|
|
lpt->sc_state |= HAVEBUS;
|
|
}
|
|
else {
|
|
LPT_DPRINTF(("%s(%s): error %d requesting bus.\n", __func__,
|
|
dev->dv_xname, error));
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
/* Release ppbus to enable other devices to use it. */
|
|
static int
|
|
lpt_release_ppbus(struct lpt_softc * lpt, int how)
|
|
{
|
|
struct device * dev = (struct device *) lpt;
|
|
int error;
|
|
|
|
if(lpt->sc_state & HAVEBUS) {
|
|
error = ppbus_release_bus(dev->dv_parent, dev, how, (hz));
|
|
if(!(error))
|
|
lpt->sc_state &= ~HAVEBUS;
|
|
else
|
|
LPT_DPRINTF(("%s(%s): error releasing bus.\n", __func__,
|
|
dev->dv_xname));
|
|
}
|
|
else {
|
|
error = EINVAL;
|
|
LPT_DPRINTF(("%s(%s): device does not own bus.\n", __func__,
|
|
dev->dv_xname));
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
/*
|
|
* Probe simplified by replacing multiple loops with a hardcoded
|
|
* test pattern - 1999/02/08 des@freebsd.org
|
|
*
|
|
* New lpt port probe Geoff Rehmet - Rhodes University - 14/2/94
|
|
* Based partially on Rod Grimes' printer probe
|
|
*
|
|
* Logic:
|
|
* 1) If no port address was given, use the bios detected ports
|
|
* and autodetect what ports the printers are on.
|
|
* 2) Otherwise, probe the data port at the address given,
|
|
* using the method in Rod Grimes' port probe.
|
|
* (Much code ripped off directly from Rod's probe.)
|
|
*
|
|
* Comments from Rod's probe:
|
|
* Logic:
|
|
* 1) You should be able to write to and read back the same value
|
|
* to the data port. Do an alternating zeros, alternating ones,
|
|
* walking zero, and walking one test to check for stuck bits.
|
|
*
|
|
* 2) You should be able to write to and read back the same value
|
|
* to the control port lower 5 bits, the upper 3 bits are reserved
|
|
* per the IBM PC technical reference manauls and different boards
|
|
* do different things with them. Do an alternating zeros, alternating
|
|
* ones, walking zero, and walking one test to check for stuck bits.
|
|
*
|
|
* Some printers drag the strobe line down when the are powered off
|
|
* so this bit has been masked out of the control port test.
|
|
*
|
|
* XXX Some printers may not like a fast pulse on init or strobe, I
|
|
* don't know at this point, if that becomes a problem these bits
|
|
* should be turned off in the mask byte for the control port test.
|
|
*
|
|
* We are finally left with a mask of 0x14, due to some printers
|
|
* being adamant about holding other bits high ........
|
|
*
|
|
* Before probing the control port, we write a 0 to the data port -
|
|
* If not, some printers chuck out garbage when the strobe line
|
|
* gets toggled.
|
|
*
|
|
* 3) Set the data and control ports to a value of 0
|
|
*
|
|
* This probe routine has been tested on Epson Lx-800, HP LJ3P,
|
|
* Epson FX-1170 and C.Itoh 8510RM
|
|
* printers.
|
|
* Quick exit on fail added.
|
|
*/
|
|
static int
|
|
lpt_detect(struct device * dev)
|
|
{
|
|
static const u_char testbyte[18] = {
|
|
0x55, /* alternating zeros */
|
|
0xaa, /* alternating ones */
|
|
0xfe, 0xfd, 0xfb, 0xf7,
|
|
0xef, 0xdf, 0xbf, 0x7f, /* walking zero */
|
|
0x01, 0x02, 0x04, 0x08,
|
|
0x10, 0x20, 0x40, 0x80 /* walking one */
|
|
};
|
|
int i, status;
|
|
u_char dtr, ctr, str, var;
|
|
|
|
/* Save register contents */
|
|
dtr = ppbus_rdtr(dev);
|
|
ctr = ppbus_rctr(dev);
|
|
str = ppbus_rstr(dev);
|
|
|
|
status = 1; /* assume success */
|
|
|
|
/* Test data port */
|
|
for(i = 0; i < 18; i++) {
|
|
ppbus_wdtr(dev, testbyte[i]);
|
|
if((var = ppbus_rdtr(dev)) != testbyte[i]) {
|
|
status = 0;
|
|
LPT_DPRINTF(("%s(%s): byte value %x cannot be written "
|
|
"and read from data port (got %x instead).\n",
|
|
__func__, dev->dv_xname, testbyte[i], var));
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
/* Test control port */
|
|
ppbus_wdtr(dev, 0);
|
|
for(i = 0; i < 18; i++) {
|
|
ppbus_wctr(dev, (testbyte[i] & 0x14));
|
|
if(((var = ppbus_rctr(dev)) & 0x14) != (testbyte[i] & 0x14)) {
|
|
status = 0;
|
|
LPT_DPRINTF(("%s(%s): byte value %x (unmasked value "
|
|
"%x) cannot be written and read from control "
|
|
"port (got %x instead).\n", __func__,
|
|
dev->dv_xname, (testbyte[i] & 0x14),
|
|
testbyte[i], (var & 0x14)));
|
|
break;
|
|
}
|
|
}
|
|
|
|
end:
|
|
/* Restore contents of registers */
|
|
ppbus_wdtr(dev, dtr);
|
|
ppbus_wctr(dev, ctr);
|
|
ppbus_wstr(dev, str);
|
|
|
|
return status;
|
|
}
|
|
|
|
/* Log status of status register for printer port */
|
|
static int
|
|
lpt_logstatus(const struct device * const dev, const unsigned char status)
|
|
{
|
|
int err;
|
|
|
|
err = EIO;
|
|
if(!(status & LPS_SEL)) {
|
|
log(LOG_ERR, "%s: offline.", dev->dv_xname);
|
|
}
|
|
else if(!(status & LPS_NBSY)) {
|
|
log(LOG_ERR, "%s: busy.", dev->dv_xname);
|
|
}
|
|
else if(status & LPS_OUT) {
|
|
log(LOG_ERR, "%s: out of paper.", dev->dv_xname);
|
|
err = EAGAIN;
|
|
}
|
|
else if(!(status & LPS_NERR)) {
|
|
log(LOG_ERR, "%s: output error.", dev->dv_xname);
|
|
}
|
|
else {
|
|
log(LOG_ERR, "%s: no error indication.", dev->dv_xname);
|
|
err = 0;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* lptopen -- reset the printer, then wait until it's selected and not busy.
|
|
*/
|
|
int
|
|
lptopen(dev_t dev_id, int flags, int fmt, struct proc *p)
|
|
{
|
|
int trys, err;
|
|
u_int8_t status;
|
|
struct device * dev;
|
|
struct lpt_softc * lpt;
|
|
struct ppbus_device_softc * ppbus_dev;
|
|
struct device * ppbus;
|
|
|
|
dev = device_lookup(&lpt_cd, LPTUNIT(dev_id));
|
|
if(!dev) {
|
|
LPT_DPRINTF(("%s(): device not configured.\n", __func__));
|
|
return ENXIO;
|
|
}
|
|
|
|
lpt = (struct lpt_softc *) dev;
|
|
|
|
ppbus = dev->dv_parent;
|
|
ppbus_dev = &(lpt->ppbus_dev);
|
|
|
|
/* Request the ppbus */
|
|
err = lpt_request_ppbus(lpt, PPBUS_WAIT|PPBUS_INTR);
|
|
if(err) {
|
|
LPT_DPRINTF(("%s(%s): error (%d) while requesting bus.\n",
|
|
__func__, dev->dv_xname, err));
|
|
return (err);
|
|
}
|
|
|
|
/* Update bus mode */
|
|
ppbus_dev->ctx.mode = ppbus_get_mode(ppbus);
|
|
|
|
/* init printer */
|
|
if ((lpt->sc_flags & LPT_PRIME) && !LPTCTL(dev_id)) {
|
|
LPT_VPRINTF(("%s(%s): initializing printer.\n", __func__,
|
|
dev->dv_xname));
|
|
lpt->sc_state |= LPTINIT;
|
|
ppbus_wctr(ppbus, LPC_SEL | LPC_NINIT);
|
|
|
|
/* wait till ready (printer running diagnostics) */
|
|
for(trys = 0, status = ppbus_rstr(ppbus); (status & RDY_MASK)
|
|
!= LP_READY; trys += LPT_STEP, status =
|
|
ppbus_rstr(ppbus)) {
|
|
|
|
/* Time up waiting for the printer */
|
|
if(trys >= LPT_TIMEOUT)
|
|
break;
|
|
/* wait LPT_STEP ticks, give up if we get a signal */
|
|
else {
|
|
err = tsleep((caddr_t)lpt, LPPRI|PCATCH,
|
|
"lptinit", LPT_STEP);
|
|
if((err) && (err != EWOULDBLOCK)) {
|
|
lpt->sc_state &= ~LPTINIT;
|
|
LPT_DPRINTF(("%s(%s): interrupted "
|
|
"during initialization.\n", __func__,
|
|
dev->dv_xname));
|
|
lpt_release_ppbus(lpt, PPBUS_WAIT);
|
|
return (err);
|
|
}
|
|
}
|
|
}
|
|
|
|
lpt->sc_state &= ~LPTINIT;
|
|
if(trys >= LPT_TIMEOUT) {
|
|
LPT_DPRINTF(("%s(%s): timed out while initializing "
|
|
"printer. [status %x]\n", __func__,
|
|
dev->dv_xname, status));
|
|
err = lpt_logstatus(dev, status);
|
|
lpt_release_ppbus(lpt, PPBUS_WAIT);
|
|
return (err);
|
|
}
|
|
else
|
|
LPT_VPRINTF(("%s(%s): printer ready.\n", __func__,
|
|
dev->dv_xname));
|
|
}
|
|
|
|
/* Set autolinefeed if requested */
|
|
if (lpt->sc_flags & LPT_AUTOLF)
|
|
ppbus_wctr(ppbus, LPC_AUTOL);
|
|
else
|
|
ppbus_wctr(ppbus, 0);
|
|
|
|
/* ready now */
|
|
lpt->sc_state |= OPEN;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* lptclose -- close the device, free the local line buffer.
|
|
*
|
|
* Check for interrupted write call added.
|
|
*/
|
|
int
|
|
lptclose(dev_t dev_id, int flags, int fmt, struct proc *p)
|
|
{
|
|
struct device * dev = device_lookup(&lpt_cd, LPTUNIT(dev_id));
|
|
struct lpt_softc * sc = (struct lpt_softc *) dev;
|
|
int err;
|
|
|
|
err = lpt_release_ppbus(sc, PPBUS_WAIT|PPBUS_INTR);
|
|
if(err) {
|
|
LPT_DPRINTF(("%s(%s): error (%d) while releasing ppbus.\n",
|
|
__func__, dev->dv_xname, err));
|
|
}
|
|
|
|
sc->sc_state = 0;
|
|
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* lptread --retrieve printer status in IEEE1284 NIBBLE mode
|
|
*/
|
|
int
|
|
lptread(dev_t dev_id, struct uio *uio, int ioflag)
|
|
{
|
|
size_t len = 0;
|
|
int error = 0;
|
|
struct device * dev = device_lookup(&lpt_cd, LPTUNIT(dev_id));
|
|
struct lpt_softc * sc = (struct lpt_softc *) dev;
|
|
|
|
if(!(sc->sc_state & HAVEBUS)) {
|
|
LPT_DPRINTF(("%s(%s): attempt to read using device which does "
|
|
"not own the bus(%s).\n", __func__, dev->dv_xname,
|
|
dev->dv_parent->dv_xname));
|
|
return (ENODEV);
|
|
}
|
|
|
|
sc->sc_state &= ~INTERRUPTED;
|
|
while (uio->uio_resid) {
|
|
error = ppbus_read(dev->dv_parent, sc->sc_outbuf,
|
|
min(BUFSIZE, uio->uio_resid), 0, &len);
|
|
|
|
/* If error or no more data, stop */
|
|
if (error) {
|
|
if (error != EWOULDBLOCK)
|
|
sc->sc_state |= INTERRUPTED;
|
|
break;
|
|
}
|
|
if (len == 0)
|
|
break;
|
|
|
|
if ((error = uiomove(sc->sc_outbuf, len, uio)))
|
|
break;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* lptwrite --copy a line from user space to a local buffer, then call
|
|
* putc to get the chars moved to the output queue.
|
|
*
|
|
* Flagging of interrupted write added.
|
|
*/
|
|
int
|
|
lptwrite(dev_t dev_id, struct uio * uio, int ioflag)
|
|
{
|
|
int error=0;
|
|
size_t n, cnt;
|
|
struct device * dev = device_lookup(&lpt_cd, LPTUNIT(dev_id));
|
|
struct lpt_softc * sc = (struct lpt_softc *) dev;
|
|
|
|
/* Check state and flags */
|
|
if(!(sc->sc_state & HAVEBUS)) {
|
|
LPT_DPRINTF(("%s(%s): attempt to write using device which does "
|
|
"not own the bus(%s).\n", __func__, dev->dv_xname,
|
|
dev->dv_parent->dv_xname));
|
|
return EINVAL;
|
|
}
|
|
|
|
LPT_VPRINTF(("%s(%s): writing %d bytes\n", __func__,
|
|
dev->dv_xname, uio->uio_resid));
|
|
|
|
/* Write the data */
|
|
sc->sc_state &= ~INTERRUPTED;
|
|
while (uio->uio_resid) {
|
|
n = MIN(BUFSIZE, uio->uio_resid);
|
|
error = uiomove(sc->sc_inbuf, n, uio);
|
|
if (error)
|
|
break;
|
|
|
|
error = ppbus_write(dev->dv_parent, sc->sc_inbuf, n, ioflag,
|
|
&cnt);
|
|
if (error) {
|
|
if (error != EWOULDBLOCK)
|
|
sc->sc_state |= INTERRUPTED;
|
|
break;
|
|
}
|
|
}
|
|
|
|
LPT_VPRINTF(("%s(%s): transfer finished, error %d.\n", __func__,
|
|
dev->dv_xname, error));
|
|
|
|
return error;
|
|
}
|
|
|
|
/* Printer ioctl */
|
|
int
|
|
lptioctl(dev_t dev_id, u_long cmd, caddr_t data, int flags, struct proc *p)
|
|
{
|
|
struct device *dev = device_lookup(&lpt_cd, LPTUNIT(dev_id));
|
|
struct lpt_softc *sc = (struct lpt_softc *) dev;
|
|
int val, fl;
|
|
int error=0;
|
|
|
|
if(!(sc->sc_state & HAVEBUS)) {
|
|
LPT_DPRINTF(("%s(%s): attempt to perform ioctl on device which "
|
|
"does not own the bus(%s).\n", __func__, dev->dv_xname,
|
|
dev->dv_parent->dv_xname));
|
|
return EBUSY;
|
|
}
|
|
|
|
switch (cmd) {
|
|
case LPTGMODE:
|
|
switch (ppbus_get_mode(dev->dv_parent)) {
|
|
case PPBUS_COMPATIBLE:
|
|
val = mode_standard;
|
|
break;
|
|
case PPBUS_NIBBLE:
|
|
val = mode_nibble;
|
|
break;
|
|
case PPBUS_PS2:
|
|
val = mode_ps2;
|
|
break;
|
|
case PPBUS_FAST:
|
|
val = mode_fast;
|
|
break;
|
|
case PPBUS_EPP:
|
|
val = mode_epp;
|
|
break;
|
|
case PPBUS_ECP:
|
|
val = mode_ecp;
|
|
break;
|
|
default:
|
|
error = EINVAL;
|
|
val = mode_unknown;
|
|
break;
|
|
}
|
|
*(int *)data = val;
|
|
break;
|
|
|
|
case LPTSMODE:
|
|
switch (*(int *)data) {
|
|
case mode_standard:
|
|
val = PPBUS_COMPATIBLE;
|
|
break;
|
|
case mode_nibble:
|
|
val = PPBUS_NIBBLE;
|
|
break;
|
|
case mode_ps2:
|
|
val = PPBUS_PS2;
|
|
break;
|
|
case mode_fast:
|
|
val = PPBUS_FAST;
|
|
break;
|
|
case mode_epp:
|
|
val = PPBUS_EPP;
|
|
break;
|
|
case mode_ecp:
|
|
val = PPBUS_ECP;
|
|
break;
|
|
default:
|
|
error = EINVAL;
|
|
val = mode_unknown;
|
|
break;
|
|
}
|
|
|
|
if (!error)
|
|
error = ppbus_set_mode(dev->dv_parent, val, 0);
|
|
|
|
break;
|
|
|
|
case LPTGFLAGS:
|
|
fl = 0;
|
|
|
|
/* DMA */
|
|
error = ppbus_read_ivar(dev->dv_parent, PPBUS_IVAR_DMA, &val);
|
|
if (error)
|
|
break;
|
|
if (val)
|
|
fl |= LPT_DMA;
|
|
|
|
/* IEEE mode negotiation */
|
|
error = ppbus_read_ivar(dev->dv_parent, PPBUS_IVAR_IEEE, &val);
|
|
if (error)
|
|
break;
|
|
if (val)
|
|
fl |= LPT_IEEE;
|
|
|
|
/* interrupts */
|
|
error = ppbus_read_ivar(dev->dv_parent, PPBUS_IVAR_INTR, &val);
|
|
if (error)
|
|
break;
|
|
if (val)
|
|
fl |= LPT_INTR;
|
|
|
|
/* lpt-only flags */
|
|
fl |= sc->sc_flags;
|
|
|
|
*(int *)data = fl;
|
|
break;
|
|
|
|
case LPTSFLAGS:
|
|
fl = *(int *)data;
|
|
|
|
/* DMA */
|
|
val = (fl & LPT_DMA);
|
|
error = ppbus_write_ivar(dev->dv_parent, PPBUS_IVAR_DMA, &val);
|
|
if (error)
|
|
break;
|
|
|
|
/* IEEE mode negotiation */
|
|
val = (fl & LPT_IEEE);
|
|
error = ppbus_write_ivar(dev->dv_parent, PPBUS_IVAR_IEEE, &val);
|
|
if (error)
|
|
break;
|
|
|
|
/* interrupts */
|
|
val = (fl & LPT_INTR);
|
|
error = ppbus_write_ivar(dev->dv_parent, PPBUS_IVAR_INTR, &val);
|
|
if (error)
|
|
break;
|
|
|
|
/* lpt-only flags */
|
|
sc->sc_flags = fl & (LPT_PRIME|LPT_AUTOLF);
|
|
|
|
break;
|
|
|
|
default:
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|