/dev/io или управление lptом (linux driver example)
Ключевые слова: linux, driver, example, (найти похожие документы)
_ RU.UNIX.BSD (2:5077/15.22) _____________________________________ RU.UNIX.BSD _
From : vadik likholetov 2:5030/266 Wed 30 Dec 98 17:06
Subj : /dev/io или управление lptом
________________________________________________________________________________
кто-то там просил -- возьмите осмысленный драйвер, с которого можно
начать ;-)
/*
* Power controller (c) 1998, vadik likholetov vadik@sensi.org Hardware by
* Alexandr Priomov, alexandr@sensi.org
*/
#include "pwc.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/uio.h>
#include <i386/isa/isa.h>
#include <i386/isa/isa_device.h>
#include <i386/isa/lptreg.h>
static struct pwc_softc {
int sc_port;
short sc_state;
int hw_state;
} pwc_sc[NPWC];
#define TASK_SIZE 12
#define DEFAULT 0 /* default state for device */
#define OPEN (1<<0) /* device is open */
#define OBUSY (1<<1) /* doing output */
#define OPENDING (1<<2) /* pending output */
static int pwc_probe(struct isa_device * dvp);
static int pwc_attach(struct isa_device * isdp);
struct isa_driver pwcdriver = {
pwc_probe, pwc_attach, "pwc"
};
static d_open_t pwc_open;
static d_close_t pwc_close;
static d_write_t pwc_write;
#define CDEV_MAJOR 220
static struct cdevsw pwc_cdevsw =
{pwc_open, pwc_close, noread, pwc_write,
noioctl, nullstop, nullreset, nodevtotty,
seltrue, nommap, nostrat, "pwc", NULL, -1};
int
pwc_probe(struct isa_device * dvp)
{
/* TODO -- test hardware for presence */
return 1;
}
int
pwc_attach(struct isa_device * isdp)
{
struct pwc_softc *sc;
int unit = isdp->id_unit;
sc = pwc_sc + unit;
sc->sc_port = isdp->id_iobase;
sc->sc_state = DEFAULT;
outb(sc->sc_port, 0);
outb(sc->sc_port + lpt_control, 0);
return 1;
}
static int
pwc_open(dev_t dev, int flags, int fmt, struct proc * p)
{
struct pwc_softc *sc;
u_int unit = minor(dev);
sc = pwc_sc + unit;
if ((unit >= NPWC) || (sc->sc_port == 0))
return ENXIO;
if (sc->sc_state != DEFAULT)
return EBUSY;
return 0;
}
static int
pwc_close(dev_t dev, int flags, int fmt, struct proc * p)
{
struct pwc_softc *sc = pwc_sc + minor(dev);
sc->sc_state &= ~OPEN;
while (sc->sc_state & OBUSY)
if (tsleep((caddr_t) sc, PZERO | PCATCH, "pwcclose", hz) != EWOULDBLOCK)
break;
sc->sc_state = DEFAULT;
return 0;
}
static int
pwc_write(dev_t dev, struct uio * uio, int ioflag)
{
struct pwc_softc *sc = pwc_sc + minor(dev);
char buffer[TASK_SIZE];
int port = sc->sc_port;
int s;
int ret;
int i, j, d;
if (uio->uio_resid != TASK_SIZE)
return EOPNOTSUPP;
s = spltty();
if (sc->sc_state & OBUSY)
return EBUSY;
sc->sc_state |= OBUSY;
splx(s);
/* output goes there */
uiomove(buffer, TASK_SIZE, uio);
if (!(inb(port + lpt_status) & ~LPS_NACK)) {
printf("pwc%d: no power on device\n", minor(dev));
ret = EIO;
goto gout;
}
#define NTRIES 10
for (i = 0; i < TASK_SIZE; i++) {
for (j = 0; j < NTRIES; j++)
if ((inb(port + lpt_status) & LPS_NBSY))
break;
else
DELAY(1);
if (j == NTRIES) {
printf("pwc%d: timeout(1) waiting for ~BSY (%x)\n",
minor(dev),inb(port+lpt_status));
ret = EIO;
goto gout;
}
d = buffer[i] == '1' ? 1 : 0;
if (i > 3 && i < 8)
i ^= 1;
outb(port + lpt_data, d);
outb(port + lpt_control, LPC_STB);
DELAY(1); /* ??? */
outb(port + lpt_control, 0);
}
for (j = 0; j < NTRIES; j++)
if (inb(port + lpt_status) & LPS_NBSY)
break;
else
DELAY(1);
if (j == NTRIES) {
printf("pwc%d: timeout(2) waiting for ~BSY\n", minor(dev));
ret = EIO;
goto gout;
}
ret = 0;
gout:
s = spltty();
sc->sc_state &= ~OBUSY;
#if 0
if (sc->sc_state & OPENDING)
#endif
wakeup((caddr_t) sc);
splx(s);
return ret;
}
static pwc_devsw_installed = 0;
static void
pwc_drvinit(void *unused)
{
dev_t dev;
if (!pwc_devsw_installed) {
dev = makedev(CDEV_MAJOR, 0);
cdevsw_add(&dev, &pwc_cdevsw, NULL);
pwc_devsw_installed = 1;
}
}
SYSINIT(pwcdev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE + CDEV_MAJOR, pwc_drvinit,
NULL)
--
vadik likholetov
--- ifmail v.2.12.os.sensi
* Origin: /kernel: last message repeated 2 times (2:5030/266@fidonet)