summaryrefslogtreecommitdiffstats
path: root/sys/dev/ppbus/lpt.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/ppbus/lpt.c')
-rw-r--r--sys/dev/ppbus/lpt.c287
1 files changed, 157 insertions, 130 deletions
diff --git a/sys/dev/ppbus/lpt.c b/sys/dev/ppbus/lpt.c
index 3659252..e4288fc 100644
--- a/sys/dev/ppbus/lpt.c
+++ b/sys/dev/ppbus/lpt.c
@@ -105,9 +105,9 @@ static int volatile lptflag = 1;
#define BUFSTATSIZE 32
struct lpt_data {
- device_t dev;
- struct cdev *cdev;
- struct cdev *cdev_bypass;
+ device_t sc_dev;
+ struct cdev *sc_cdev;
+ struct cdev *sc_cdev_bypass;
short sc_state;
/* default case: negative prime, negative ack, handshake strobe,
prime once */
@@ -130,9 +130,10 @@ struct lpt_data {
#define LP_ENABLE_IRQ 0x04 /* enable IRQ on open */
#define LP_ENABLE_EXT 0x10 /* we shall use advanced mode when possible */
u_char sc_backoff ; /* time to call lptout() again */
+ struct callout sc_timer;
- struct resource *intr_resource; /* interrupt resource */
- void *intr_cookie; /* interrupt registration cookie */
+ struct resource *sc_intr_resource; /* interrupt resource */
+ void *sc_intr_cookie; /* interrupt cookie */
};
#define LPT_NAME "lpt" /* our official name */
@@ -144,8 +145,7 @@ static int lpt_detect(device_t dev);
#define DEVTOSOFTC(dev) \
((struct lpt_data *)device_get_softc(dev))
-static void lptintr(device_t dev);
-static void lpt_intr(void *arg); /* without spls */
+static void lptintr(void *arg);
static devclass_t lpt_devclass;
@@ -183,7 +183,6 @@ static d_ioctl_t lptioctl;
static struct cdevsw lpt_cdevsw = {
.d_version = D_VERSION,
- .d_flags = D_NEEDGIANT,
.d_open = lptopen,
.d_close = lptclose,
.d_read = lptread,
@@ -199,13 +198,17 @@ lpt_request_ppbus(device_t dev, int how)
struct lpt_data *sc = DEVTOSOFTC(dev);
int error;
+ /*
+ * We might already have the bus for a write(2) after an interrupted
+ * write(2) call.
+ */
+ ppb_assert_locked(ppbus);
if (sc->sc_state & HAVEBUS)
return (0);
- /* we have the bus only if the request succeded */
- if ((error = ppb_request_bus(ppbus, dev, how)) == 0)
+ error = ppb_request_bus(ppbus, dev, how);
+ if (error == 0)
sc->sc_state |= HAVEBUS;
-
return (error);
}
@@ -216,9 +219,12 @@ lpt_release_ppbus(device_t dev)
struct lpt_data *sc = DEVTOSOFTC(dev);
int error = 0;
- if ((error = ppb_release_bus(ppbus, dev)) == 0)
- sc->sc_state &= ~HAVEBUS;
-
+ ppb_assert_locked(ppbus);
+ if (sc->sc_state & HAVEBUS) {
+ error = ppb_release_bus(ppbus, dev);
+ if (error == 0)
+ sc->sc_state &= ~HAVEBUS;
+ }
return (error);
}
@@ -306,24 +312,25 @@ lpt_detect(device_t dev)
status = 1; /* assume success */
+ ppb_lock(ppbus);
if ((error = lpt_request_ppbus(dev, PPB_DONTWAIT))) {
- printf(LPT_NAME ": cannot alloc ppbus (%d)!\n", error);
- status = 0;
- goto end_probe;
+ ppb_unlock(ppbus);
+ device_printf(dev, "cannot alloc ppbus (%d)!\n", error);
+ return (0);
}
for (i = 0; i < 18 && status; i++)
if (!lpt_port_test(ppbus, testbyte[i], 0xff)) {
status = 0;
- goto end_probe;
+ break;
}
-end_probe:
/* write 0's to control and data ports */
ppb_wdtr(ppbus, 0);
ppb_wctr(ppbus, 0);
lpt_release_ppbus(dev);
+ ppb_unlock(ppbus);
return (status);
}
@@ -363,21 +370,33 @@ lpt_attach(device_t dev)
int error;
sc->sc_primed = 0; /* not primed yet */
+ ppb_init_callout(ppbus, &sc->sc_timer, 0);
+ ppb_lock(ppbus);
if ((error = lpt_request_ppbus(dev, PPB_DONTWAIT))) {
- printf(LPT_NAME ": cannot alloc ppbus (%d)!\n", error);
+ ppb_unlock(ppbus);
+ device_printf(dev, "cannot alloc ppbus (%d)!\n", error);
return (0);
}
ppb_wctr(ppbus, LPC_NINIT);
-
- /* check if we can use interrupt, should be done by ppc stuff */
- lprintf(("oldirq %x\n", sc->sc_irq));
+ ppb_unlock(ppbus);
+ lpt_release_ppbus(dev);
/* declare our interrupt handler */
- sc->intr_resource = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ sc->sc_intr_resource = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
RF_SHAREABLE);
- if (sc->intr_resource) {
+ if (sc->sc_intr_resource) {
+ error = bus_setup_intr(dev, sc->sc_intr_resource,
+ INTR_TYPE_TTY | INTR_MPSAFE, NULL, lptintr, sc,
+ &sc->sc_intr_cookie);
+ if (error) {
+ bus_release_resource(dev, SYS_RES_IRQ, rid,
+ sc->sc_intr_resource);
+ device_printf(dev,
+ "Unable to register interrupt handler\n");
+ return (error);
+ }
sc->sc_irq = LP_HAS_IRQ | LP_USE_IRQ | LP_ENABLE_IRQ;
device_printf(dev, "Interrupt-driven port\n");
} else {
@@ -386,17 +405,17 @@ lpt_attach(device_t dev)
}
lprintf(("irq %x\n", sc->sc_irq));
- lpt_release_ppbus(dev);
-
- sc->dev = dev;
- sc->cdev = make_dev(&lpt_cdevsw, unit,
+ sc->sc_inbuf = malloc(BUFSIZE, M_DEVBUF, M_WAITOK);
+ sc->sc_statbuf = malloc(BUFSTATSIZE, M_DEVBUF, M_WAITOK);
+ sc->sc_dev = dev;
+ sc->sc_cdev = make_dev(&lpt_cdevsw, unit,
UID_ROOT, GID_WHEEL, 0600, LPT_NAME "%d", unit);
- sc->cdev->si_drv1 = sc;
- sc->cdev->si_drv2 = 0;
- sc->cdev_bypass = make_dev(&lpt_cdevsw, unit,
+ sc->sc_cdev->si_drv1 = sc;
+ sc->sc_cdev->si_drv2 = 0;
+ sc->sc_cdev_bypass = make_dev(&lpt_cdevsw, unit,
UID_ROOT, GID_WHEEL, 0600, LPT_NAME "%d.ctl", unit);
- sc->cdev_bypass->si_drv1 = sc;
- sc->cdev_bypass->si_drv2 = (void *)LP_BYPASS;
+ sc->sc_cdev_bypass->si_drv1 = sc;
+ sc->sc_cdev_bypass->si_drv2 = (void *)LP_BYPASS;
return (0);
}
@@ -404,15 +423,21 @@ static int
lpt_detach(device_t dev)
{
struct lpt_data *sc = DEVTOSOFTC(dev);
+ device_t ppbus = device_get_parent(dev);
- destroy_dev(sc->cdev);
- destroy_dev(sc->cdev_bypass);
+ destroy_dev(sc->sc_cdev);
+ destroy_dev(sc->sc_cdev_bypass);
+ ppb_lock(ppbus);
lpt_release_ppbus(dev);
- if (sc->intr_resource != 0) {
- BUS_TEARDOWN_INTR(device_get_parent(dev), dev,
- sc->intr_resource, sc->intr_cookie);
- bus_release_resource(dev, SYS_RES_IRQ, 0, sc->intr_resource);
+ ppb_unlock(ppbus);
+ callout_drain(&sc->sc_timer);
+ if (sc->sc_intr_resource != NULL) {
+ bus_teardown_intr(dev, sc->sc_intr_resource,
+ sc->sc_intr_cookie);
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_intr_resource);
}
+ free(sc->sc_inbuf, M_DEVBUF);
+ free(sc->sc_statbuf, M_DEVBUF);
return (0);
}
@@ -420,18 +445,17 @@ lpt_detach(device_t dev)
static void
lptout(void *arg)
{
- device_t dev = (device_t)arg;
- struct lpt_data *sc = DEVTOSOFTC(dev);
-#ifdef LPT_DEBUG
+ struct lpt_data *sc = arg;
+ device_t dev = sc->sc_dev;
device_t ppbus = device_get_parent(dev);
-#endif
+ ppb_assert_locked(ppbus);
lprintf(("T %x ", ppb_rstr(ppbus)));
if (sc->sc_state & OPEN) {
sc->sc_backoff++;
if (sc->sc_backoff > hz/LPTOUTMAX)
sc->sc_backoff = sc->sc_backoff > hz/LPTOUTMAX;
- timeout(lptout, (caddr_t)dev, sc->sc_backoff);
+ callout_reset(&sc->sc_timer, sc->sc_backoff, lptout, sc);
} else
sc->sc_state &= ~TOUT;
@@ -442,7 +466,7 @@ lptout(void *arg)
* Avoid possible hangs due to missed interrupts
*/
if (sc->sc_xfercnt) {
- lptintr(dev);
+ lptintr(sc);
} else {
sc->sc_state &= ~OBUSY;
wakeup(dev);
@@ -458,17 +482,19 @@ lptout(void *arg)
static int
lptopen(struct cdev *dev, int flags, int fmt, struct thread *td)
{
- int s;
int trys, err;
struct lpt_data *sc = dev->si_drv1;
- device_t lptdev = sc->dev;
+ device_t lptdev = sc->sc_dev;
device_t ppbus = device_get_parent(lptdev);
if (!sc)
return (ENXIO);
+ ppb_lock(ppbus);
if (sc->sc_state) {
- lprintf((LPT_NAME ": still open %x\n", sc->sc_state));
+ lprintf(("%s: still open %x\n", device_get_nameunit(lptdev),
+ sc->sc_state));
+ ppb_unlock(ppbus);
return(EBUSY);
} else
sc->sc_state |= LPTINIT;
@@ -478,6 +504,7 @@ lptopen(struct cdev *dev, int flags, int fmt, struct thread *td)
/* Check for open with BYPASS flag set. */
if (sc->sc_flags & LP_BYPASS) {
sc->sc_state = OPEN;
+ ppb_unlock(ppbus);
return(0);
}
@@ -485,11 +512,12 @@ lptopen(struct cdev *dev, int flags, int fmt, struct thread *td)
if ((err = lpt_request_ppbus(lptdev, PPB_WAIT|PPB_INTR)) != 0) {
/* give it a chance to try later */
sc->sc_state = 0;
+ ppb_unlock(ppbus);
return (err);
}
- s = spltty();
- lprintf((LPT_NAME " flags 0x%x\n", sc->sc_flags));
+ lprintf(("%s flags 0x%x\n", device_get_nameunit(lptdev),
+ sc->sc_flags));
/* set IRQ status according to ENABLE_IRQ flag
*/
@@ -514,21 +542,21 @@ lptopen(struct cdev *dev, int flags, int fmt, struct thread *td)
do {
/* ran out of waiting for the printer */
if (trys++ >= LPINITRDY*4) {
- splx(s);
sc->sc_state = 0;
lprintf(("status %x\n", ppb_rstr(ppbus)));
lpt_release_ppbus(lptdev);
+ ppb_unlock(ppbus);
return (EBUSY);
}
/* wait 1/4 second, give up if we get a signal */
- if (tsleep(lptdev, LPPRI|PCATCH, "lptinit", hz/4) !=
- EWOULDBLOCK) {
+ if (ppb_sleep(ppbus, lptdev, LPPRI | PCATCH, "lptinit",
+ hz / 4) != EWOULDBLOCK) {
sc->sc_state = 0;
- splx(s);
lpt_release_ppbus(lptdev);
+ ppb_unlock(ppbus);
return (EBUSY);
}
@@ -548,22 +576,20 @@ lptopen(struct cdev *dev, int flags, int fmt, struct thread *td)
ppb_wctr(ppbus, sc->sc_control);
sc->sc_state = OPEN;
- sc->sc_inbuf = malloc(BUFSIZE, M_DEVBUF, M_WAITOK);
- sc->sc_statbuf = malloc(BUFSTATSIZE, M_DEVBUF, M_WAITOK);
sc->sc_xfercnt = 0;
- splx(s);
-
- /* release the ppbus */
- lpt_release_ppbus(lptdev);
/* only use timeout if using interrupt */
lprintf(("irq %x\n", sc->sc_irq));
if (sc->sc_irq & LP_USE_IRQ) {
sc->sc_state |= TOUT;
- timeout(lptout, (caddr_t)lptdev,
- (sc->sc_backoff = hz/LPTOUTINITIAL));
+ sc->sc_backoff = hz / LPTOUTINITIAL;
+ callout_reset(&sc->sc_timer, sc->sc_backoff, lptout, sc);
}
+ /* release the ppbus */
+ lpt_release_ppbus(lptdev);
+ ppb_unlock(ppbus);
+
lprintf(("opened.\n"));
return(0);
}
@@ -578,17 +604,21 @@ static int
lptclose(struct cdev *dev, int flags, int fmt, struct thread *td)
{
struct lpt_data *sc = dev->si_drv1;
- device_t lptdev = sc->dev;
+ device_t lptdev = sc->sc_dev;
device_t ppbus = device_get_parent(lptdev);
int err;
- if (sc->sc_flags & LP_BYPASS)
+ ppb_lock(ppbus);
+ if (sc->sc_flags & LP_BYPASS) {
+ sc->sc_state = 0;
+ ppb_unlock(ppbus);
goto end_close;
+ }
- if ((err = lpt_request_ppbus(lptdev, PPB_WAIT|PPB_INTR)) != 0)
+ if ((err = lpt_request_ppbus(lptdev, PPB_WAIT|PPB_INTR)) != 0) {
+ ppb_unlock(ppbus);
return (err);
-
- sc->sc_state &= ~OPEN;
+ }
/* if the last write was interrupted, don't complete it */
if ((!(sc->sc_state & INTERRUPTED)) && (sc->sc_irq & LP_USE_IRQ))
@@ -596,22 +626,23 @@ lptclose(struct cdev *dev, int flags, int fmt, struct thread *td)
(LPS_SEL|LPS_OUT|LPS_NBSY|LPS_NERR)) !=
(LPS_SEL|LPS_NBSY|LPS_NERR) || sc->sc_xfercnt)
/* wait 1/4 second, give up if we get a signal */
- if (tsleep(lptdev, LPPRI|PCATCH,
- "lpclose", hz) != EWOULDBLOCK)
+ if (ppb_sleep(ppbus, lptdev, LPPRI | PCATCH, "lpclose",
+ hz) != EWOULDBLOCK)
break;
+ sc->sc_state &= ~OPEN;
+ callout_stop(&sc->sc_timer);
ppb_wctr(ppbus, LPC_NINIT);
- free(sc->sc_inbuf, M_DEVBUF);
- free(sc->sc_statbuf, M_DEVBUF);
+ sc->sc_state = 0;
+ sc->sc_xfercnt = 0;
-end_close:
- /* release the bus anyway
+ /*
* unregistration of interrupt forced by release
*/
lpt_release_ppbus(lptdev);
+ ppb_unlock(ppbus);
- sc->sc_state = 0;
- sc->sc_xfercnt = 0;
+end_close:
lprintf(("closed.\n"));
return(0);
}
@@ -625,13 +656,14 @@ end_close:
* This code is only used when we are polling the port
*/
static int
-lpt_pushbytes(device_t dev)
+lpt_pushbytes(struct lpt_data *sc)
{
- struct lpt_data *sc = DEVTOSOFTC(dev);
+ device_t dev = sc->sc_dev;
device_t ppbus = device_get_parent(dev);
int spin, err, tic;
char ch;
+ ppb_assert_locked(ppbus);
lprintf(("p"));
/* loop for every character .. */
while (sc->sc_xfercnt > 0) {
@@ -660,7 +692,7 @@ lpt_pushbytes(device_t dev)
*/
if (tic > MAX_SLEEP)
tic = MAX_SLEEP;
- err = tsleep(dev, LPPRI,
+ err = ppb_sleep(ppbus, dev, LPPRI,
LPT_NAME "poll", tic);
if (err != EWOULDBLOCK) {
return (err);
@@ -686,7 +718,7 @@ static int
lptread(struct cdev *dev, struct uio *uio, int ioflag)
{
struct lpt_data *sc = dev->si_drv1;
- device_t lptdev = sc->dev;
+ device_t lptdev = sc->sc_dev;
device_t ppbus = device_get_parent(lptdev);
int error = 0, len;
@@ -695,8 +727,11 @@ lptread(struct cdev *dev, struct uio *uio, int ioflag)
return (EPERM);
}
- if ((error = ppb_1284_negociate(ppbus, PPB_NIBBLE, 0)))
+ ppb_lock(ppbus);
+ if ((error = ppb_1284_negociate(ppbus, PPB_NIBBLE, 0))) {
+ ppb_unlock(ppbus);
return (error);
+ }
/* read data in an other buffer, read/write may be simultaneous */
len = 0;
@@ -710,12 +745,16 @@ lptread(struct cdev *dev, struct uio *uio, int ioflag)
if (!len)
goto error; /* no more data */
- if ((error = uiomove(sc->sc_statbuf, len, uio)))
+ ppb_unlock(ppbus);
+ error = uiomove(sc->sc_statbuf, len, uio);
+ ppb_lock(ppbus);
+ if (error)
goto error;
}
error:
ppb_1284_terminate(ppbus);
+ ppb_unlock(ppbus);
return (error);
}
@@ -732,36 +771,30 @@ lptwrite(struct cdev *dev, struct uio *uio, int ioflag)
register unsigned n;
int err;
struct lpt_data *sc = dev->si_drv1;
- device_t lptdev = sc->dev;
+ device_t lptdev = sc->sc_dev;
device_t ppbus = device_get_parent(lptdev);
if (sc->sc_flags & LP_BYPASS) {
/* we can't do writes in bypass mode */
- return(EPERM);
+ return (EPERM);
}
/* request the ppbus only if we don't have it already */
- /* XXX interrupt registration?! */
- if ((err = lpt_request_ppbus(lptdev, PPB_WAIT|PPB_INTR)) != 0)
+ ppb_lock(ppbus);
+ if ((err = lpt_request_ppbus(lptdev, PPB_WAIT|PPB_INTR)) != 0) {
+ ppb_unlock(ppbus);
return (err);
-
- /* if interrupts are working, register the handler */
- if (sc->sc_irq & LP_USE_IRQ) {
- /* register our interrupt handler */
- err = bus_setup_intr(lptdev, sc->intr_resource,
- INTR_TYPE_TTY, NULL, lpt_intr, lptdev,
- &sc->intr_cookie);
- if (err) {
- device_printf(lptdev, "handler registration failed, polled mode.\n");
- sc->sc_irq &= ~LP_USE_IRQ;
- }
}
sc->sc_state &= ~INTERRUPTED;
while ((n = min(BUFSIZE, uio->uio_resid)) != 0) {
sc->sc_cp = sc->sc_inbuf;
- uiomove(sc->sc_cp, n, uio);
- sc->sc_xfercnt = n ;
+ ppb_unlock(ppbus);
+ err = uiomove(sc->sc_cp, n, uio);
+ ppb_lock(ppbus);
+ if (err)
+ break;
+ sc->sc_xfercnt = n;
if (sc->sc_irq & LP_ENABLE_EXT) {
/* try any extended mode */
@@ -775,15 +808,17 @@ lptwrite(struct cdev *dev, struct uio *uio, int ioflag)
break;
case EINTR:
sc->sc_state |= INTERRUPTED;
- return(err);
+ ppb_unlock(ppbus);
+ return (err);
case EINVAL:
/* advanced mode not avail */
log(LOG_NOTICE,
"%s: advanced mode not avail, polling\n",
- device_get_nameunit(sc->dev));
+ device_get_nameunit(sc->sc_dev));
break;
default:
- return(err);
+ ppb_unlock(ppbus);
+ return (err);
}
} else while ((sc->sc_xfercnt > 0)&&(sc->sc_irq & LP_USE_IRQ)) {
lprintf(("i"));
@@ -791,13 +826,14 @@ lptwrite(struct cdev *dev, struct uio *uio, int ioflag)
/* give it one */
if ((sc->sc_state & OBUSY) == 0){
lprintf(("\nC %d. ", sc->sc_xfercnt));
- lptintr(lptdev);
+ lptintr(sc);
}
lprintf(("W "));
if (sc->sc_state & OBUSY)
- if ((err = tsleep(lptdev,
+ if ((err = ppb_sleep(ppbus, lptdev,
LPPRI|PCATCH, LPT_NAME "write", 0))) {
sc->sc_state |= INTERRUPTED;
+ ppb_unlock(ppbus);
return(err);
}
}
@@ -806,38 +842,37 @@ lptwrite(struct cdev *dev, struct uio *uio, int ioflag)
if (!(sc->sc_irq & LP_USE_IRQ) && (sc->sc_xfercnt)) {
lprintf(("p"));
- err = lpt_pushbytes(lptdev);
+ err = lpt_pushbytes(sc);
- if (err)
- return(err);
+ if (err) {
+ ppb_unlock(ppbus);
+ return (err);
+ }
}
}
/* we have not been interrupted, release the ppbus */
lpt_release_ppbus(lptdev);
+ ppb_unlock(ppbus);
- return(0);
+ return (err);
}
/*
- * lpt_intr -- handle printer interrupts which occur when the printer is
+ * lptintr -- handle printer interrupts which occur when the printer is
* ready to accept another char.
*
* do checking for interrupted write call.
*/
static void
-lpt_intr(void *arg)
+lptintr(void *arg)
{
- device_t lptdev = (device_t)arg;
+ struct lpt_data *sc = arg;
+ device_t lptdev = sc->sc_dev;
device_t ppbus = device_get_parent(lptdev);
- struct lpt_data *sc = DEVTOSOFTC(lptdev);
int sts = 0;
int i;
- /* we must own the bus to use it */
- if ((sc->sc_state & HAVEBUS) == 0)
- return;
-
/*
* Is printer online and ready for output?
*
@@ -883,27 +918,18 @@ lpt_intr(void *arg)
lprintf(("sts %x ", sts));
}
-static void
-lptintr(device_t dev)
-{
- /* call the interrupt at required spl level */
- int s = spltty();
-
- lpt_intr(dev);
-
- splx(s);
- return;
-}
-
static int
lptioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td)
{
int error = 0;
struct lpt_data *sc = dev->si_drv1;
+ device_t ppbus;
u_char old_sc_irq; /* old printer IRQ status */
switch (cmd) {
case LPT_IRQ :
+ ppbus = device_get_parent(sc->sc_dev);
+ ppb_lock(ppbus);
if (sc->sc_irq & LP_HAS_IRQ) {
/*
* NOTE:
@@ -915,7 +941,7 @@ lptioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *t
* this gets syslog'd.
*/
old_sc_irq = sc->sc_irq;
- switch(*(int*)data) {
+ switch (*(int*)data) {
case 0:
sc->sc_irq &= (~LP_ENABLE_IRQ);
break;
@@ -939,13 +965,14 @@ lptioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *t
if (old_sc_irq != sc->sc_irq )
log(LOG_NOTICE, "%s: switched to %s %s mode\n",
- device_get_nameunit(sc->dev),
+ device_get_nameunit(sc->sc_dev),
(sc->sc_irq & LP_ENABLE_IRQ)?
"interrupt-driven":"polled",
(sc->sc_irq & LP_ENABLE_EXT)?
"extended":"standard");
} else /* polled port */
error = EOPNOTSUPP;
+ ppb_unlock(ppbus);
break;
default:
error = ENODEV;
OpenPOWER on IntegriCloud