From a54e7dc7ff61b3773fbb41666d56047880f9a64b Mon Sep 17 00:00:00 2001 From: SjB Date: Tue, 8 Apr 2014 23:09:21 -0400 Subject: [PATCH] Initial attempt to implemented GPIO Interrupts Basicly imported Dave Cheney's code from http://github.com/davecheney/gpio --- gpio.go | 37 ++++++++++++++++++ gpiodriver.go | 79 ++++++++++++++++++++++++++++++++++++-- host/generic/digitalpin.go | 42 +++++++++++++++++++- 3 files changed, 154 insertions(+), 4 deletions(-) diff --git a/gpio.go b/gpio.go index cc09dcd..8e363fe 100644 --- a/gpio.go +++ b/gpio.go @@ -23,6 +23,16 @@ const ( High ) +type Edge string +type IRQEvent func(pin DigitalPin) + +const ( + EdgeNone Edge = "none" + EdgeRising Edge = "rising" + EdgeFalling Edge = "falling" + EdgeBoth Edge = "both" +) + // DigitalPin implements access to a digital IO capable GPIO pin. type DigitalPin interface { // N returns the logical GPIO number. @@ -50,6 +60,11 @@ type DigitalPin interface { // PullDown pulls the pin down. PullDown() error + Watch(edge Edge, callback IRQEvent) error + StopWatching() error + + Signal() + // Close releases the resources associated with the pin. Close() error } @@ -103,6 +118,12 @@ type PWMPin interface { // GPIODriver implements a generic GPIO driver. type GPIODriver interface { + // Register a pin as an interrupt pin + RegisterInterrupt(fd int, p DigitalPin) error + + // Unregister the file handler from the Interrupt handler + UnregisterInterrupt(fd int) error + // Unregister unregisters the pin from the driver. Should be called when the pin is closed. Unregister(string) error @@ -209,6 +230,22 @@ func PullDown(key interface{}) error { return pin.PullDown() } +func Watch(key interface{}, edge Edge, callback IRQEvent) error { + pin, err := NewDigitalPin(key) + if err != nil { + return err + } + return pin.Watch(edge, callback) +} + +func StopWatching(key interface{}) error { + pin, err := NewDigitalPin(key) + if err != nil { + return err + } + return pin.StopWatching() +} + // NewAnalogPin returns a AnalogPin interface which allows control over // the analog GPIO pin. func NewAnalogPin(key interface{}) (AnalogPin, error) { diff --git a/gpiodriver.go b/gpiodriver.go index 07fceff..3b3f3cf 100644 --- a/gpiodriver.go +++ b/gpiodriver.go @@ -5,6 +5,11 @@ package embd import ( "errors" "fmt" + "syscall" +) + +const ( + MaxGPIOInterrupt = 64 ) type pin interface { @@ -22,20 +27,88 @@ type gpioDriver struct { apf analogPinFactory ppf pwmPinFactory - initializedPins map[string]pin + watchEventCallbacks map[int]DigitalPin + initializedPins map[string]pin } // NewGPIODriver returns a GPIODriver interface which allows control // over the GPIO subsystem. func NewGPIODriver(pinMap PinMap, dpf digitalPinFactory, apf analogPinFactory, ppf pwmPinFactory) GPIODriver { - return &gpioDriver{ + driver := &gpioDriver{ pinMap: pinMap, dpf: dpf, apf: apf, ppf: ppf, - initializedPins: map[string]pin{}, + watchEventCallbacks: map[int]DigitalPin{}, + initializedPins: map[string]pin{}, } + return driver +} + +var epollFD int + +func (io *gpioDriver) initializeEpoll() { + var err error + epollFD, err = syscall.EpollCreate1(0) + if err != nil { + panic(fmt.Sprintf("Unable to create epoll FD: ", err.Error())) + } + + go func() { + var epollEvents [MaxGPIOInterrupt]syscall.EpollEvent + + for { + numEvents, err := syscall.EpollWait(epollFD, epollEvents[:], -1) + if err != nil { + panic(fmt.Sprintf("EpollWait error: %s", err.Error())) + } + for i := 0; i < numEvents; i++ { + if eventPin, exists := io.watchEventCallbacks[int(epollEvents[i].Fd)]; exists { + eventPin.Signal() + } + } + } + }() +} + +func (io *gpioDriver) RegisterInterrupt(fd int, p DigitalPin) error { + + if epollFD == 0 { + io.initializeEpoll() + } + + var event syscall.EpollEvent + event.Events = syscall.EPOLLIN | (syscall.EPOLLET & 0xffffffff) | syscall.EPOLLPRI + + io.watchEventCallbacks[fd] = p + + if err := syscall.SetNonblock(fd, true); err != nil { + return err + } + + event.Fd = int32(fd) + + if err := syscall.EpollCtl(epollFD, syscall.EPOLL_CTL_ADD, fd, &event); err != nil { + return err + } + + return nil +} + +func (io *gpioDriver) UnregisterInterrupt(fd int) error { + + if err := syscall.EpollCtl(epollFD, syscall.EPOLL_CTL_DEL, fd, nil); err != nil { + return err + } + + if err := syscall.SetNonblock(fd, false); err != nil { + return err + } + + delete(io.watchEventCallbacks, fd) + + return nil } func (io *gpioDriver) Unregister(id string) error { diff --git a/host/generic/digitalpin.go b/host/generic/digitalpin.go index fabdeb5..3abd05f 100644 --- a/host/generic/digitalpin.go +++ b/host/generic/digitalpin.go @@ -28,10 +28,19 @@ type digitalPin struct { readBuf []byte initialized bool + + epollInitialTrigger bool + callback embd.IRQEvent } func NewDigitalPin(pd *embd.PinDesc, drv embd.GPIODriver) embd.DigitalPin { - return &digitalPin{id: pd.ID, n: pd.DigitalLogical, drv: drv, readBuf: make([]byte, 1)} + return &digitalPin{ + id: pd.ID, + n: pd.DigitalLogical, + drv: drv, + epollInitialTrigger: true, + readBuf: make([]byte, 1), + } } func (p *digitalPin) N() int { @@ -102,6 +111,37 @@ func (p *digitalPin) activeLowFile() (*os.File, error) { return p.openFile(path.Join(p.basePath(), "active_low")) } +func (p *digitalPin) setEdge(edge embd.Edge) error { + file, err := p.openFile(path.Join(p.basePath(), "edge")) + if err != nil { + return err + } + defer file.Close() + + _, err = file.Write([]byte(edge)) + return err +} + +func (p *digitalPin) Watch(edge embd.Edge, callback embd.IRQEvent) error { + if err := p.setEdge(edge); err != nil { + return err + } + p.callback = callback + return p.drv.RegisterInterrupt(int(p.val.Fd()), p) +} + +func (p *digitalPin) StopWatching() error { + return p.drv.UnregisterInterrupt(int(p.val.Fd())) +} + +func (p *digitalPin) Signal() { + if p.epollInitialTrigger { + p.epollInitialTrigger = false + } else { + p.callback(embd.DigitalPin(p)) + } +} + func (p *digitalPin) SetDirection(dir embd.Direction) error { if err := p.init(); err != nil { return err