From 57328c979d6cdefc0e7dc598a56d3d43fefba8cf Mon Sep 17 00:00:00 2001 From: Karan Misra Date: Sun, 6 Apr 2014 05:30:54 +0530 Subject: [PATCH] gpio: implement pin caching this allows for the short version of the API to work as expected as consecutive calls to the same pin would now be internally working on the pin instance (all the 3 currently supported types). closing a pin busts the cache --- bbb.go | 23 ++++++++++++++++----- bbb_test.go | 41 +++++++++++++++++++++++++++++++++++++ digitalpin.go | 13 +++++++++--- digitalpin_test.go | 22 ++++++++++++++++++++ gpio.go | 3 +++ gpiodriver.go | 33 ++++++++++++++++++++++++------ gpiodriver_test.go | 51 ++++++++++++++++++++++++++++++++++++++-------- 7 files changed, 164 insertions(+), 22 deletions(-) create mode 100644 bbb_test.go create mode 100644 digitalpin_test.go diff --git a/bbb.go b/bbb.go index 8eec247..feeb4b4 100644 --- a/bbb.go +++ b/bbb.go @@ -157,15 +157,18 @@ func bbbEnsureFeatureDisabled(id string) error { } type bbbAnalogPin struct { - n int + id string + n int + + drv GPIODriver val *os.File initialized bool } -func newBBBAnalogPin(n int) AnalogPin { - return &bbbAnalogPin{n: n} +func newBBBAnalogPin(pd *PinDesc, drv GPIODriver) AnalogPin { + return &bbbAnalogPin{id: pd.ID, n: pd.AnalogLogical, drv: drv} } func (p *bbbAnalogPin) N() int { @@ -227,6 +230,10 @@ func (p *bbbAnalogPin) Read() (int, error) { } func (p *bbbAnalogPin) Close() error { + if err := p.drv.Unregister(p.id); err != nil { + return err + } + if !p.initialized { return nil } @@ -257,6 +264,8 @@ const ( type bbbPWMPin struct { n string + drv GPIODriver + period int polarity Polarity @@ -267,8 +276,8 @@ type bbbPWMPin struct { initialized bool } -func newBBBPWMPin(n string) PWMPin { - return &bbbPWMPin{n: n} +func newBBBPWMPin(pd *PinDesc, drv GPIODriver) PWMPin { + return &bbbPWMPin{n: pd.ID, drv: drv} } func (p *bbbPWMPin) N() string { @@ -463,6 +472,10 @@ func (p *bbbPWMPin) reset() error { } func (p *bbbPWMPin) Close() error { + if err := p.drv.Unregister(p.n); err != nil { + return err + } + if !p.initialized { return nil } diff --git a/bbb_test.go b/bbb_test.go new file mode 100644 index 0000000..534430d --- /dev/null +++ b/bbb_test.go @@ -0,0 +1,41 @@ +package embd + +import "testing" + +func TestBBBAnalogPinClose(t *testing.T) { + pinMap := PinMap{ + &PinDesc{ID: "P1_1", Aliases: []string{"1"}, Caps: CapAnalog}, + } + driver := newGPIODriver(pinMap, nil, newBBBAnalogPin, nil) + pin, err := driver.AnalogPin(1) + if err != nil { + t.Fatalf("Looking up analog pin 1: got %v", err) + } + pin.Close() + pin2, err := driver.AnalogPin(1) + if err != nil { + t.Fatalf("Looking up analog pin 1: got %v", err) + } + if pin == pin2 { + t.Fatal("Looking up closed analog pin 1: but got the old instance") + } +} + +func TestBBBPWMPinClose(t *testing.T) { + pinMap := PinMap{ + &PinDesc{ID: "P1_1", Aliases: []string{"1"}, Caps: CapPWM}, + } + driver := newGPIODriver(pinMap, nil, nil, newBBBPWMPin) + pin, err := driver.PWMPin(1) + if err != nil { + t.Fatalf("Looking up pwm pin 1: got %v", err) + } + pin.Close() + pin2, err := driver.PWMPin(1) + if err != nil { + t.Fatalf("Looking up pwm pin 1: got %v", err) + } + if pin == pin2 { + t.Fatal("Looking up closed pwm pin 1: but got the old instance") + } +} diff --git a/digitalpin.go b/digitalpin.go index bffd450..0515919 100644 --- a/digitalpin.go +++ b/digitalpin.go @@ -14,7 +14,10 @@ import ( ) type digitalPin struct { - n int + id string + n int + + drv GPIODriver dir *os.File val *os.File @@ -25,8 +28,8 @@ type digitalPin struct { initialized bool } -func newDigitalPin(n int) DigitalPin { - return &digitalPin{n: n, readBuf: make([]byte, 1)} +func newDigitalPin(pd *PinDesc, drv GPIODriver) DigitalPin { + return &digitalPin{id: pd.ID, n: pd.DigitalLogical, drv: drv, readBuf: make([]byte, 1)} } func (p *digitalPin) N() int { @@ -223,6 +226,10 @@ func (p *digitalPin) PullDown() error { } func (p *digitalPin) Close() error { + if err := p.drv.Unregister(p.id); err != nil { + return err + } + if !p.initialized { return nil } diff --git a/digitalpin_test.go b/digitalpin_test.go new file mode 100644 index 0000000..942e3d9 --- /dev/null +++ b/digitalpin_test.go @@ -0,0 +1,22 @@ +package embd + +import "testing" + +func TestDigitalPinClose(t *testing.T) { + pinMap := PinMap{ + &PinDesc{ID: "P1_1", Aliases: []string{"1"}, Caps: CapDigital}, + } + driver := newGPIODriver(pinMap, newDigitalPin, nil, nil) + pin, err := driver.DigitalPin(1) + if err != nil { + t.Fatalf("Looking up digital pin 1: got %v", err) + } + pin.Close() + pin2, err := driver.DigitalPin(1) + if err != nil { + t.Fatalf("Looking up digital pin 1: got %v", err) + } + if pin == pin2 { + t.Fatal("Looking up closed digital pin 1: but got the old instance") + } +} diff --git a/gpio.go b/gpio.go index c58a26a..cc09dcd 100644 --- a/gpio.go +++ b/gpio.go @@ -103,6 +103,9 @@ type PWMPin interface { // GPIODriver implements a generic GPIO driver. type GPIODriver interface { + // Unregister unregisters the pin from the driver. Should be called when the pin is closed. + Unregister(string) error + // DigitalPin returns a pin capable of doing digital IO. DigitalPin(key interface{}) (DigitalPin, error) diff --git a/gpiodriver.go b/gpiodriver.go index d36f9df..8d9b558 100644 --- a/gpiodriver.go +++ b/gpiodriver.go @@ -11,9 +11,9 @@ type pin interface { Close() error } -type digitalPinFactory func(n int) DigitalPin -type analogPinFactory func(n int) AnalogPin -type pwmPinFactory func(n string) PWMPin +type digitalPinFactory func(pd *PinDesc, drv GPIODriver) DigitalPin +type analogPinFactory func(pd *PinDesc, drv GPIODriver) AnalogPin +type pwmPinFactory func(pd *PinDesc, drv GPIODriver) PWMPin type gpioDriver struct { pinMap PinMap @@ -36,6 +36,15 @@ func newGPIODriver(pinMap PinMap, dpf digitalPinFactory, apf analogPinFactory, p } } +func (io *gpioDriver) Unregister(id string) error { + if _, ok := io.initializedPins[id]; !ok { + return fmt.Errorf("gpio: pin %v is not registered yet, cannot unregister", id) + } + + delete(io.initializedPins, id) + return nil +} + func (io *gpioDriver) DigitalPin(key interface{}) (DigitalPin, error) { if io.dpf == nil { return nil, errors.New("gpio: digital io not supported on this host") @@ -46,7 +55,11 @@ func (io *gpioDriver) DigitalPin(key interface{}) (DigitalPin, error) { return nil, fmt.Errorf("gpio: could not find pin matching %v", key) } - p := io.dpf(pd.DigitalLogical) + if p, ok := io.initializedPins[pd.ID]; ok { + return p.(DigitalPin), nil + } + + p := io.dpf(pd, io) io.initializedPins[pd.ID] = p return p, nil @@ -62,7 +75,11 @@ func (io *gpioDriver) AnalogPin(key interface{}) (AnalogPin, error) { return nil, fmt.Errorf("gpio: could not find pin matching %v", key) } - p := io.apf(pd.AnalogLogical) + if p, ok := io.initializedPins[pd.ID]; ok { + return p.(AnalogPin), nil + } + + p := io.apf(pd, io) io.initializedPins[pd.ID] = p return p, nil @@ -78,7 +95,11 @@ func (io *gpioDriver) PWMPin(key interface{}) (PWMPin, error) { return nil, fmt.Errorf("gpio: could not find pin matching %v", key) } - p := io.ppf(pd.ID) + if p, ok := io.initializedPins[pd.ID]; ok { + return p.(PWMPin), nil + } + + p := io.ppf(pd, io) io.initializedPins[pd.ID] = p return p, nil diff --git a/gpiodriver_test.go b/gpiodriver_test.go index cc15578..301545b 100644 --- a/gpiodriver_test.go +++ b/gpiodriver_test.go @@ -6,7 +6,10 @@ import ( ) type fakeDigitalPin struct { - n int + id string + n int + + drv GPIODriver } func (p *fakeDigitalPin) N() int { @@ -41,12 +44,12 @@ func (*fakeDigitalPin) PullDown() error { return nil } -func (*fakeDigitalPin) Close() error { - return nil +func (p *fakeDigitalPin) Close() error { + return p.drv.Unregister(p.id) } -func newFakeDigitalPin(n int) DigitalPin { - return &fakeDigitalPin{n} +func newFakeDigitalPin(pd *PinDesc, drv GPIODriver) DigitalPin { + return &fakeDigitalPin{id: pd.ID, n: pd.DigitalLogical, drv: drv} } func TestGpioDriverDigitalPin(t *testing.T) { @@ -73,7 +76,10 @@ func TestGpioDriverDigitalPin(t *testing.T) { } type fakeAnalogPin struct { - n int + id string + n int + + drv GPIODriver } func (p *fakeAnalogPin) N() int { @@ -92,8 +98,8 @@ func (*fakeAnalogPin) Close() error { return nil } -func newFakeAnalogPin(n int) AnalogPin { - return &fakeAnalogPin{n} +func newFakeAnalogPin(pd *PinDesc, drv GPIODriver) AnalogPin { + return &fakeAnalogPin{id: pd.ID, n: pd.AnalogLogical, drv: drv} } func TestGpioDriverAnalogPin(t *testing.T) { @@ -142,3 +148,32 @@ func TestGpioDriverUnavailablePinType(t *testing.T) { t.Fatalf("Looking up analog pin 1: got error %q, expected %q", err, expected) } } + +func TestGpioPinCaching(t *testing.T) { + pinMap := PinMap{ + &PinDesc{ID: "P1_1", Aliases: []string{"1"}, Caps: CapDigital}, + } + driver := newGPIODriver(pinMap, newFakeDigitalPin, nil, nil) + pin, err := driver.DigitalPin(1) + if err != nil { + t.Fatalf("Looking up digital pin 1: got %v", err) + } + // Lookup the same pin again + pin2, err := driver.DigitalPin(1) + if err != nil { + t.Fatalf("Looking up digital pin 1: got %v", err) + } + if pin != pin2 { + t.Fatalf("Looking up digital pin 1 for the second time: got %v, want %v", &pin2, &pin) + } + // Looking up a closed pin + pin.Close() + pin3, err := driver.DigitalPin(1) + if err != nil { + t.Fatalf("Looking up digital pin 1: got %v", err) + return + } + if pin == pin3 { + t.Fatal("Looking up a closed pin, but got the same old instance") + } +}