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") + } +}