/* * Copyright (c) 2000. M. Warner Losh. All Rights Reserved. * * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. M. Warner Losh */ /* * Simple driver for the I-Opener LED, but likely could be adapted * to any led driver. This is intended to be a thought excersize * as well as a useful sample driver. Since I don't have a hackable * iopener around to test it out on. * * The LED is located at 0x404c on the iopener. Likely we should find this * in the pci space, and then do stuff from tehre. However, it appears to * be controlled in some way by acpi, so I'm going to try to write this driver * to not interfere with that. * * the lower two bits of this register control the state of the LED. The left * led, with the mail ICON, is controlled by bit 0. The phone led is * controlled by bit 1. * * This is a bog simple ISA driver... Would make a useful example, imho. * * Since I'm lazy, I have only a write interface. The characters recieved * by the driver are masked and the results sent to these gpios. This * allows things like '1' to turn on the led and '0' to turn off the led. * There is a minor number for each led controlled. * * The read interface returns 1 character ('0' off '1' on) for the state * of the led. * * thanks to "roastbeef" who posted technical information about this to the * I-Opener BBS web site. */ #include #include #include #include #include #include #include #include #include #include #include #include #include struct led_softc { bus_space_tag_t bst; bus_space_handle_t bsh; dev_t dev0; dev_t dev1; u_int32_t open_mask; u_int32_t read_mask; struct resource *res; int rid; }; static devclass_t led_devclass; static d_open_t led_open; static d_close_t led_close; static d_read_t led_read; static d_write_t led_write; #define LED_IOADDR 0x404c #define NLED 2 #define MAJOR 199 static struct cdevsw led_cdevsw = { led_open, /* open */ led_close, /* close */ led_read, /* read */ led_write, /* write */ noioctl, /* ioctl */ nopoll, /* poll */ nommap, /* mmap */ nostrategy, /* strategy */ "led", /* name */ MAJOR, /* major */ nodump, /* dump */ nopsize, /* psize */ 0, /* flags */ -1 /* bmaj */ }; static int led_open(dev_t dev, int flags, int fmt, struct proc *p) { int unit = minor(dev) >> 16; int led = minor(dev) & 0xff; struct led_softc *sc; if (led >= NLED) return ENXIO; sc = devclass_get_softc(led_devclass, unit); if (sc == NULL) return ENXIO; if (sc->open_mask & (1 << led)) return EBUSY; sc->open_mask |= 1 << led; sc->read_mask |= 1 << led; return 0; } static int led_close(dev_t dev, int flags, int fmt, struct proc *p) { int unit = minor(dev) >> 16; int led = minor(dev) & 0xff; struct led_softc *sc; if (led >= NLED) return ENXIO; sc = devclass_get_softc(led_devclass, unit); if (sc == NULL) return ENXIO; sc->open_mask &= ~(1 << led); return 0; } static int led_read(dev_t dev, struct uio *uio, int flag) { int unit = minor(dev) >> 16; int led = minor(dev) & 0xff; struct led_softc *sc; int err; u_int8_t ch; if (led >= NLED) return ENXIO; sc = devclass_get_softc(led_devclass, unit); if (sc == NULL) return ENXIO; /* No error EOF condition */ if ((sc->read_mask & (1 << led)) == 0) return 0; ch = bus_space_read_1(sc->bst, sc->bsh, 0); if (ch & (1 << led)) ch = '1'; else ch = '0'; err = uiomove(&ch, 1, uio); sc->read_mask &= ~ (1 << led); return err; } static int led_write(dev_t dev, struct uio *uio, int flag) { int unit = minor(dev) >> 16; int led = minor(dev) & 0xff; struct led_softc *sc; int err; u_int8_t ch; u_int8_t old; if (led >= NLED) return ENXIO; sc = devclass_get_softc(led_devclass, unit); if (sc == NULL) return ENXIO; err = uiomove(&ch, 1, uio); if (err != 0) return (err); old = bus_space_read_1(sc->bst, sc->bsh, 0); if ((ch & 1) != 0) old |= (1 << led); else old &= ~(1 << led); bus_space_write_1(sc->bst, sc->bsh, 0, old); return err; } static void led_identify (driver_t *driver, device_t parent) { devclass_t dc; device_t child; dc = devclass_find("led"); if (devclass_get_device(dc, 0) == NULL) { child = BUS_ADD_CHILD(parent, 0, "led", -1); bus_set_resource(child, SYS_RES_IOPORT, 0, LED_IOADDR, 1); } } static int led_probe(device_t dev) { if (device_get_unit(dev) != 0) return (ENXIO); if (bus_get_resource_start(dev, SYS_RES_IOPORT, 0) == 0) return (ENXIO); return (0); } static int led_attach(device_t dev) { struct led_softc *sc; sc = (struct led_softc *) device_get_softc(dev); sc->rid = 0; sc->res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->rid, 0, ~0, 1, RF_ACTIVE); if (sc->res == NULL) return ENXIO; #ifdef INTR sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irqid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE); if (!sc->irq) return(ENXIO); bus_setup_intr(dev, sc->irq, INTR_TYPE_MISC, led_intr, sc, &sc->ih); #endif sc->bst = rman_get_bustag(sc->res); sc->bsh = rman_get_bushandle(sc->res); sc->dev0 = make_dev(&led_cdevsw, 0, UID_ROOT, GID_WHEEL, 0644, "led0"); sc->dev1 = make_dev(&led_cdevsw, 1, UID_ROOT, GID_WHEEL, 0644, "led1"); sc->open_mask = 0; sc->read_mask = 0; return 0; } static int led_detach(device_t dev) { struct led_softc *sc; sc = (struct led_softc *) device_get_softc(dev); bus_release_resource(dev, SYS_RES_IOPORT, sc->rid, sc->res); #ifdef INTR bus_teardown_intr(dev, sc->irq, sc->ih); bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); #endif return 0; } #ifdef INTR static void led_intr(void *p) { } #endif static device_method_t led_methods[] = { /* Device interface */ DEVMETHOD(device_identify, led_identify), DEVMETHOD(device_probe, led_probe), DEVMETHOD(device_attach, led_attach), DEVMETHOD(device_detach, led_detach), { 0, 0 } }; static driver_t led_driver = { "led", led_methods, sizeof(struct led_softc), }; DRIVER_MODULE(led, isa, led_driver, led_devclass, 0, 0);