ISA driver for IRQ and PortIO
John Baldwin
jhb at freebsd.org
Thu Feb 28 17:09:51 UTC 2013
On Wednesday, February 27, 2013 5:16:41 pm star-one at tx.rr.com wrote:
> I need to write a simple device driver for the ISA bus that uses one IRQ and
a few reads and writes to I/O Ports 0x300 +.
>
>
> I don't know even where to start with FreeBSD. I've done drivers before in
other OS systems and have a book for Linux but FreeBSD seems MUCH differnt.
>
>
> Are there any examples I can get? I thought about modifying the serial
driver but I'm not een sure if that's a good idea.
>
>
> Any help would be great.
There is a FreeBSD Device Driver book. You will need to allocate 'struct
resource' objects for your IRQ and I/O port and then use the bus_space API
with your I/O port resource to do inb/outb operations. That is, you would
need something like this:
In /boot/device.hints:
hint.foo.0.at="isa0"
hint.foo.0.irq=X
hint.foo.0.port=0x300
Then a sketch of your driver would be:
#define NPORTS 4 /* How many I/O ports you need starting at 0x300 */
/* Sample only of names for the 4 ports via relative offsets to the start */
#define CONTROL_REG 0
#define DATA_REG 1
struct foo_softc {
device_t dev;
struct resource *io;
struct resource *irq;
void *intr_cookie;
};
/* Interrupt handler. */
static void
foo_int(void *arg)
{
struct foo_softc *sc;
sc = arg;
device_printf(sc->dev, "got an interrupt\n");
}
static int
foo_probe(device_t dev)
{
/* Ignore PNP devices. */
if (isa_get_logicalid(dev) != 0)
return (ENXIO);
/* Require IRQ and port hints. */
if (isa_get_port(dev) == -1 ||
isa_get_irq(dev) == -1)
return (ENXIO);
device_set_desc(dev, "My foo device");
return (BUS_PROBE_GENERIC);
}
static int
foo_attach(device_t dev)
{
struct foo_softc *sc;
int error, rid;
sc = device_get_softc(dev);
sc->dev = dev;
/* Allocate resources. */
rid = 0;
sc->io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0ul, ~0ul, NPORTS,
RF_ACTIVE);
if (sc->io == NULL) {
device_printf(dev, "Failed to allocate I/O ports\n");
error = ENXIO;
goto out;
rid = 0;
sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
if (sc->irq == NULL) {
device_printf(dev, "Failed to allocate IRQ\n");
error = ENXIO;
goto out;
}
/* Read a byte from port 0x300 */
device_printf(dev, "Current control value = %x\n",
bus_read_1(sc->io, CONTROL_REG));
/* Read a byte from port 0x301 */
device_printf(dev, "Current data value = %x\n",
bus_read_1(sc->io, DATA_REG));
/* Write a byte to the control reg at 0x300 */
bus_write_1(sc->io, CONTROL_REG, 0xff);
/* Setup interrupt handler. */
error = bus_setup_intr(dev, sc->irq, INTR_TYPE_MISC | INTR_MPSAFE, NULL,
foo_intr, sc, &sc->intr_cookie);
if (error != 0) {
device_printf(dev, "Failed to setup interrupt handler\n");
goto out;
}
return (0);
out:
if (sc->irq != NULL)
bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq);
if (sc->io != NULL)
bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->io);
return (error);
}
static int
foo_detach(device_t dev)
{
struct foo_softc *sc;
sc = device_get_softc(dev);
bus_teardown_intr(dev, sc->irq, sc->intr_cookie);
bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq);
bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->io);
return (0);
}
static device_method_t foo_methods[] = {
DEVMETHOD(device_probe, foo_probe),
DEVMETHOD(device_attach, foo_attach),
DEVMETHOD(device_detach, foo_detach),
DEVMETHOD_END
};
static driver_t foo_driver = {
"foo",
foo_methods,
sizeof(struct foo_softc);
};
static devclass_t foo_devclass;
DRIVER_MODULE(foo, isa, foo_driver, foo_devclass, NULL, NULL);
That should get you up and running, but to do something useful with the device
you'll probably want to create a cdev or some such.
--
John Baldwin
More information about the freebsd-drivers
mailing list