tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

RFC: gpio attachment for ppbus



Hi,

occasionally I need to control custom devices like a PIC programmer or a
JTAG adapter that connect to the parallel port. Up to now I accessed the
parallel port directly, bypassing the lpt driver. This is, of course,
not The Right Thing to do.

I would like to be able to control the pins of the parallel port
individually using the existing GPIO subsystem, so that I don't need to
depend on special I/O privileges and unportable code in my user space
programs. This would also allow the use of any other GPIO device to
control custom hardware without changing the software. The ppbus
framework (which is currently not enabled by default in any kernel)
makes such an gpio attachment very simple.

The attached patch does two things. First, it extends the generic gpio
code to support open and close functions. This is needed to request and
release the ppbus when opening/closing the attached gpio device. The
existing gpio drivers don't need this, so I made those functions
optional. Second, ppbus is extended to attach a gpiobus which supports
17 pins of the parallel port (the remaining pins are hardwired to GND).
There are 12 output pins and 5 input pins, and it is not possible to
change their direction or set any other characteristics. A few pins are
inverted by the hardware, the driver knows about them and adjusts the
values read or written accordingly. The ppbus(4) and gpio(4) manpages
are updated, too.

The patch is against a quite current -current. I tested this on i386
using a simple PIC programming device and a custom programming software.
I expect this code to work on other platforms, too, but I have no way to
test.

Please comment. If possible, I would like to see this in 5.0 :)


Hans


-- 
%SYSTEM-F-ANARCHISM, The operating system has been overthrown
Index: share/man/man4/gpio.4
===================================================================
RCS file: /cvsroot/src/share/man/man4/gpio.4,v
retrieving revision 1.6
diff -u -p -r1.6 gpio.4
--- share/man/man4/gpio.4       9 Jan 2008 16:15:41 -0000       1.6
+++ share/man/man4/gpio.4       28 Apr 2008 20:03:58 -0000
@@ -26,6 +26,7 @@
 .Cd "gpio* at gcscpcib?"
 .Cd "gpio* at gscpcib?"
 .Cd "gpio* at nsclpcsio?"
+.Cd "gpio* at ppbus?"
 .Pp
 .In sys/types.h
 .In sys/gpio.h
Index: share/man/man4/ppbus.4
===================================================================
RCS file: /cvsroot/src/share/man/man4/ppbus.4,v
retrieving revision 1.12
diff -u -p -r1.12 ppbus.4
--- share/man/man4/ppbus.4      20 Aug 2007 16:07:05 -0000      1.12
+++ share/man/man4/ppbus.4      28 Apr 2008 20:03:58 -0000
@@ -38,6 +38,7 @@
 .Cd "options PPBUS_DEBUG"
 .Cd "options DEBUG_1284"
 .Pp
+.Cd "gpio* at ppbus?"
 .Cd "lpt* at ppbus?"
 .Cd "plip* at ppbus?"
 .Cd "pps* at ppbus?"
@@ -59,6 +60,10 @@ architecture-independent macros or funct
 .It
 mechanism to allow various devices to share the same parallel port
 .It
+a
+.Xr gpio 4
+interface to access the individual pins
+.It
 a user interface named
 .Xr ppi 4
 that allows parallel port access from outside the kernel without
@@ -395,8 +400,18 @@ level for efficiency.
 .\" various I/O sequences to initialize, select and allocate the
 .\" peripheral
 .\" .El
+.Ss GPIO interface
+Pins 1 through 17 of the parallel port can be controlled through the
+.Xr gpio 4
+interface, pins 18 through 25 are hardwired to ground. Pins 10 through
+13 and pin 15 are input pins, the others are output pins. Some of the
+pins are inverted by the hardware, the values read or written are
+adjusted accordingly. Note that the
+.Xr gpio 4
+interface starts at 0 when numbering pins.
 .Sh SEE ALSO
 .Xr atppc 4 ,
+.Xr gpio 4,
 .Xr lpt 4 ,
 .Xr plip 4 ,
 .Xr ppi 4 ,
Index: sys/dev/gpio/gpio.c
===================================================================
RCS file: /cvsroot/src/sys/dev/gpio/gpio.c,v
retrieving revision 1.15
diff -u -p -r1.15 gpio.c
--- sys/dev/gpio/gpio.c 29 Feb 2008 06:38:59 -0000      1.15
+++ sys/dev/gpio/gpio.c 28 Apr 2008 20:03:58 -0000
@@ -275,6 +275,7 @@ gpioopen(dev_t dev, int flag, int mode,
     struct lwp *l)
 {
        struct gpio_softc *sc;
+       int ret;
 
        sc = (struct gpio_softc *)device_lookup(&gpio_cd, minor(dev));
        if (sc == NULL)
@@ -282,6 +283,10 @@ gpioopen(dev_t dev, int flag, int mode,
 
        if (sc->sc_opened)
                return (EBUSY);
+
+       if ((ret = gpiobus_open(sc->sc_gc, &sc->sc_dev)))
+               return ret;
+
        sc->sc_opened = 1;
 
        return (0);
@@ -294,6 +299,7 @@ gpioclose(dev_t dev, int flag, int mode,
        struct gpio_softc *sc;
 
        sc = (struct gpio_softc *)device_lookup(&gpio_cd, minor(dev));
+       gpiobus_close(sc->sc_gc, &sc->sc_dev);
        sc->sc_opened = 0;
 
        return (0);
Index: sys/dev/gpio/gpiovar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/gpio/gpiovar.h,v
retrieving revision 1.4
diff -u -p -r1.4 gpiovar.h
--- sys/dev/gpio/gpiovar.h      20 Feb 2006 03:18:36 -0000      1.4
+++ sys/dev/gpio/gpiovar.h      28 Apr 2008 20:03:58 -0000
@@ -24,6 +24,8 @@
 typedef struct gpio_chipset_tag {
        void    *gp_cookie;
 
+       int     (*gp_gc_open)(void *, device_t);
+       void    (*gp_gc_close)(void *, device_t);
        int     (*gp_pin_read)(void *, int);
        void    (*gp_pin_write)(void *, int, int);
        void    (*gp_pin_ctl)(void *, int, int);
@@ -48,6 +50,10 @@ struct gpiobus_attach_args {
 int gpiobus_print(void *, const char *);
 
 /* GPIO framework private methods */
+#define gpiobus_open(gc, dev) \
+    ((gc)->gp_gc_open ? ((gc)->gp_gc_open((gc)->gp_cookie, dev)) : 0)
+#define gpiobus_close(gc, dev) \
+    ((gc)->gp_gc_close ? ((gc)->gp_gc_close((gc)->gp_cookie, dev)) : 0)
 #define gpiobus_pin_read(gc, pin) \
     ((gc)->gp_pin_read((gc)->gp_cookie, (pin)))
 #define gpiobus_pin_write(gc, pin, value) \
Index: sys/dev/ppbus/files.ppbus
===================================================================
RCS file: /cvsroot/src/sys/dev/ppbus/files.ppbus,v
retrieving revision 1.7
diff -u -p -r1.7 files.ppbus
--- sys/dev/ppbus/files.ppbus   11 Dec 2005 12:23:28 -0000      1.7
+++ sys/dev/ppbus/files.ppbus   28 Apr 2008 20:03:59 -0000
@@ -10,12 +10,13 @@ defflag     opt_ppbus_1284.h        DONTPROBE_1284 
 define parport { }
 
 # ppbus related files
-device ppbus { }
+device ppbus { }: gpiobus
 attach ppbus at parport
 file   dev/ppbus/ppbus_conf.c  ppbus           needs-flag
 file   dev/ppbus/ppbus_base.c  ppbus
 file   dev/ppbus/ppbus_msq.c   ppbus
 file   dev/ppbus/ppbus_1284.c  ppbus
+file   dev/ppbus/ppbus_gpio.c  ppbus & gpio
 
 # lpt driver
 defflag        opt_ppbus_lpt.h         LPT_DEBUG LPT_VERBOSE
Index: sys/dev/ppbus/ppbus_conf.c
===================================================================
RCS file: /cvsroot/src/sys/dev/ppbus/ppbus_conf.c,v
retrieving revision 1.15
diff -u -p -r1.15 ppbus_conf.c
--- sys/dev/ppbus/ppbus_conf.c  18 Apr 2008 14:56:40 -0000      1.15
+++ sys/dev/ppbus/ppbus_conf.c  28 Apr 2008 20:03:59 -0000
@@ -35,6 +35,8 @@ __KERNEL_RCSID(0, "$NetBSD: ppbus_conf.c
 #include "opt_ppbus.h"
 #include "opt_ppbus_1284.h"
 
+#include "gpio.h"
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
@@ -169,6 +171,9 @@ ppbus_attach(device_t parent, device_t s
        SLIST_INIT(&(ppbus->sc_childlist_head));
        config_search_ia(ppbus_search_children, self, "ppbus", &args);
 
+#if NGPIO > 0
+       gpio_ppbus_attach(ppbus);
+#endif
        return;
 }
 
Index: sys/dev/ppbus/ppbus_conf.h
===================================================================
RCS file: /cvsroot/src/sys/dev/ppbus/ppbus_conf.h,v
retrieving revision 1.10
diff -u -p -r1.10 ppbus_conf.h
--- sys/dev/ppbus/ppbus_conf.h  15 Apr 2008 15:02:29 -0000      1.10
+++ sys/dev/ppbus/ppbus_conf.h  28 Apr 2008 20:03:59 -0000
@@ -31,6 +31,8 @@
 #ifndef __PPBUS_CONF_H
 #define __PPBUS_CONF_H
 
+#include "gpio.h"
+
 #include <sys/device.h>
 #include <sys/mutex.h>
 #include <sys/queue.h>
@@ -40,6 +42,10 @@
 #include <dev/ppbus/ppbus_msq.h>
 #include <dev/ppbus/ppbus_device.h>
 
+#if NGPIO > 0
+#include <dev/gpio/gpiovar.h>
+#define PPBUS_NPINS 17
+#endif
 
 /* Function pointer types used for interface */
 typedef u_char (*PARPORT_IO_T)(device_t, int, u_char *, int, u_char);
@@ -130,6 +136,15 @@ struct ppbus_softc {
        PARPORT_DMA_FREE_T ppbus_dma_free;
        PARPORT_ADD_HANDLER_T ppbus_add_handler;
        PARPORT_REMOVE_HANDLER_T ppbus_remove_handler;
+
+#if NGPIO > 0
+       struct gpio_chipset_tag sc_gpio_gc;
+       gpio_pin_t sc_gpio_pins[PPBUS_NPINS];
+#endif
 };
 
+#if NGPIO > 0
+void gpio_ppbus_attach(struct ppbus_softc *);
+#endif
+
 #endif /* __PPBUS_CONF_H */
Index: sys/dev/ppbus/ppbus_gpio.c
===================================================================
RCS file: sys/dev/ppbus/ppbus_gpio.c
diff -N sys/dev/ppbus/ppbus_gpio.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ sys/dev/ppbus/ppbus_gpio.c  28 Apr 2008 20:03:59 -0000
@@ -0,0 +1,161 @@
+/* $NetBSD$ */
+
+/*
+ *  Copyright (c) 2008, Hans Rosenfeld 
<rosenfeld%grumpf.hope-2000.org@localhost>
+ *  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. Neither the name of Hans Rosenfeld nor the names of his
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 AUTHOR OR CONTRIBUTORS 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.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+
+#include <dev/gpio/gpiovar.h>
+
+#include <dev/ppbus/ppbus_conf.h>
+#include <dev/ppbus/ppbus_base.h>
+#include <dev/ppbus/ppbus_io.h>
+
+static int  gpio_ppbus_open(void *, device_t);
+static void gpio_ppbus_close(void *, device_t);
+static int  gpio_ppbus_pin_read(void *, int);
+static void gpio_ppbus_pin_write(void *, int, int);
+static void gpio_ppbus_pin_ctl(void *, int, int);
+
+
+#define PORT(r, b, i) { PPBUS_R##r##TR, PPBUS_W##r##TR, b, i}
+static struct {
+       u_char rreg;
+       u_char wreg;
+       u_char bit;
+       u_char inv;
+} ppbus_port[PPBUS_NPINS] = {  /* parallel port wiring: */
+       PORT(C, 0, 1),          /*     1: /C0  Output    */
+       PORT(D, 0, 0),          /*     2:  D0  Output    */
+       PORT(D, 1, 0),          /*     3:  D1  Output    */
+       PORT(D, 2, 0),          /*     4:  D2  Output    */
+       PORT(D, 3, 0),          /*     5:  D3  Output    */
+       PORT(D, 4, 0),          /*     6:  D4  Output    */
+       PORT(D, 5, 0),          /*     7:  D5  Output    */
+       PORT(D, 6, 0),          /*     8:  D6  Output    */
+       PORT(D, 7, 0),          /*     9:  D7  Output    */
+       PORT(S, 6, 0),          /*    10:  S6  Input     */
+       PORT(S, 7, 1),          /*    11: /S7  Input     */
+       PORT(S, 5, 0),          /*    12:  S5  Input     */
+       PORT(S, 4, 0),          /*    13:  S4  Input     */
+       PORT(C, 1, 1),          /*    14: /C1  Output    */
+       PORT(S, 3, 0),          /*    15:  S3  Input     */
+       PORT(C, 2, 0),          /*    16:  C2  Output    */
+       PORT(C, 3, 1),          /*    17: /C3  Output    */
+};                             /* 18-25: GND            */
+
+void
+gpio_ppbus_attach(struct ppbus_softc *sc)
+{
+       struct gpiobus_attach_args gba;
+       gpio_pin_t *pin;
+       int i;
+
+       for (pin = &sc->sc_gpio_pins[0], i = 0; i < PPBUS_NPINS; pin++, i++) {
+               pin->pin_num = i;
+               
+               if (((i >= 9) && (i <= 12)) || (i == 14)) {
+                       pin->pin_caps = GPIO_PIN_INPUT;
+                       pin->pin_flags = GPIO_PIN_INPUT;
+                       pin->pin_state = gpio_ppbus_pin_read(sc, i);
+               } else {
+                       pin->pin_caps = GPIO_PIN_OUTPUT;
+                       pin->pin_flags = GPIO_PIN_OUTPUT;
+                       pin->pin_state = GPIO_PIN_LOW;
+                       gpio_ppbus_pin_write(sc, i, pin->pin_state);
+               }
+
+               gpio_ppbus_pin_ctl(sc, i, pin->pin_flags);
+       }
+
+       sc->sc_gpio_gc.gp_cookie = sc;
+       sc->sc_gpio_gc.gp_gc_open = gpio_ppbus_open;
+       sc->sc_gpio_gc.gp_gc_close = gpio_ppbus_close;
+       sc->sc_gpio_gc.gp_pin_read = gpio_ppbus_pin_read;
+       sc->sc_gpio_gc.gp_pin_write = gpio_ppbus_pin_write;
+       sc->sc_gpio_gc.gp_pin_ctl = gpio_ppbus_pin_ctl;
+
+       gba.gba_gc = &sc->sc_gpio_gc;
+       gba.gba_pins = sc->sc_gpio_pins;
+       gba.gba_npins = PPBUS_NPINS;
+
+       config_found_ia(sc->sc_dev, "gpiobus", &gba, gpiobus_print);
+}
+
+static int
+gpio_ppbus_open(void *arg, device_t dev)
+{
+       struct ppbus_softc *sc = arg;
+
+       return ppbus_request_bus(sc->sc_dev, dev, PPBUS_WAIT|PPBUS_INTR, (hz));
+}
+
+static void
+gpio_ppbus_close(void *arg, device_t dev)
+{
+       struct ppbus_softc *sc = arg;
+
+       (void) ppbus_release_bus(sc->sc_dev, dev, PPBUS_WAIT|PPBUS_INTR, (hz));
+}
+
+static int
+gpio_ppbus_pin_read(void *arg, int pin)
+{
+       struct ppbus_softc *sc = arg;
+       u_char port = ppbus_io(sc->sc_dev, ppbus_port[pin].rreg, NULL, 0, 0);
+
+       return ((port >> ppbus_port[pin].bit) & 1) ^ ppbus_port[pin].inv;
+}
+
+static void
+gpio_ppbus_pin_write(void *arg, int pin, int value)
+{
+       struct ppbus_softc *sc = arg;
+       u_char port = ppbus_io(sc->sc_dev, ppbus_port[pin].rreg, NULL, 0, 0);
+
+       value ^= ppbus_port[pin].inv;
+       value <<= ppbus_port[pin].bit;
+       port &= ~(1 << ppbus_port[pin].bit);
+       port |= value;
+
+       ppbus_io(sc->sc_dev, ppbus_port[pin].wreg, NULL, 0, port);
+}
+
+static void
+gpio_ppbus_pin_ctl(void *arg, int pin, int flags)
+{
+       /* can't change parallel port pin configuration */
+}


Home | Main Index | Thread Index | Old Index