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
This commit is contained in:
Karan Misra 2014-04-06 05:30:54 +05:30
parent 6f90af377d
commit 57328c979d
7 changed files with 164 additions and 22 deletions

23
bbb.go
View File

@ -157,15 +157,18 @@ func bbbEnsureFeatureDisabled(id string) error {
} }
type bbbAnalogPin struct { type bbbAnalogPin struct {
n int id string
n int
drv GPIODriver
val *os.File val *os.File
initialized bool initialized bool
} }
func newBBBAnalogPin(n int) AnalogPin { func newBBBAnalogPin(pd *PinDesc, drv GPIODriver) AnalogPin {
return &bbbAnalogPin{n: n} return &bbbAnalogPin{id: pd.ID, n: pd.AnalogLogical, drv: drv}
} }
func (p *bbbAnalogPin) N() int { func (p *bbbAnalogPin) N() int {
@ -227,6 +230,10 @@ func (p *bbbAnalogPin) Read() (int, error) {
} }
func (p *bbbAnalogPin) Close() error { func (p *bbbAnalogPin) Close() error {
if err := p.drv.Unregister(p.id); err != nil {
return err
}
if !p.initialized { if !p.initialized {
return nil return nil
} }
@ -257,6 +264,8 @@ const (
type bbbPWMPin struct { type bbbPWMPin struct {
n string n string
drv GPIODriver
period int period int
polarity Polarity polarity Polarity
@ -267,8 +276,8 @@ type bbbPWMPin struct {
initialized bool initialized bool
} }
func newBBBPWMPin(n string) PWMPin { func newBBBPWMPin(pd *PinDesc, drv GPIODriver) PWMPin {
return &bbbPWMPin{n: n} return &bbbPWMPin{n: pd.ID, drv: drv}
} }
func (p *bbbPWMPin) N() string { func (p *bbbPWMPin) N() string {
@ -463,6 +472,10 @@ func (p *bbbPWMPin) reset() error {
} }
func (p *bbbPWMPin) Close() error { func (p *bbbPWMPin) Close() error {
if err := p.drv.Unregister(p.n); err != nil {
return err
}
if !p.initialized { if !p.initialized {
return nil return nil
} }

41
bbb_test.go Normal file
View File

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

View File

@ -14,7 +14,10 @@ import (
) )
type digitalPin struct { type digitalPin struct {
n int id string
n int
drv GPIODriver
dir *os.File dir *os.File
val *os.File val *os.File
@ -25,8 +28,8 @@ type digitalPin struct {
initialized bool initialized bool
} }
func newDigitalPin(n int) DigitalPin { func newDigitalPin(pd *PinDesc, drv GPIODriver) DigitalPin {
return &digitalPin{n: n, readBuf: make([]byte, 1)} return &digitalPin{id: pd.ID, n: pd.DigitalLogical, drv: drv, readBuf: make([]byte, 1)}
} }
func (p *digitalPin) N() int { func (p *digitalPin) N() int {
@ -223,6 +226,10 @@ func (p *digitalPin) PullDown() error {
} }
func (p *digitalPin) Close() error { func (p *digitalPin) Close() error {
if err := p.drv.Unregister(p.id); err != nil {
return err
}
if !p.initialized { if !p.initialized {
return nil return nil
} }

22
digitalpin_test.go Normal file
View File

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

View File

@ -103,6 +103,9 @@ type PWMPin interface {
// GPIODriver implements a generic GPIO driver. // GPIODriver implements a generic GPIO driver.
type GPIODriver interface { 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 returns a pin capable of doing digital IO.
DigitalPin(key interface{}) (DigitalPin, error) DigitalPin(key interface{}) (DigitalPin, error)

View File

@ -11,9 +11,9 @@ type pin interface {
Close() error Close() error
} }
type digitalPinFactory func(n int) DigitalPin type digitalPinFactory func(pd *PinDesc, drv GPIODriver) DigitalPin
type analogPinFactory func(n int) AnalogPin type analogPinFactory func(pd *PinDesc, drv GPIODriver) AnalogPin
type pwmPinFactory func(n string) PWMPin type pwmPinFactory func(pd *PinDesc, drv GPIODriver) PWMPin
type gpioDriver struct { type gpioDriver struct {
pinMap PinMap 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) { func (io *gpioDriver) DigitalPin(key interface{}) (DigitalPin, error) {
if io.dpf == nil { if io.dpf == nil {
return nil, errors.New("gpio: digital io not supported on this host") 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) 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 io.initializedPins[pd.ID] = p
return p, nil 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) 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 io.initializedPins[pd.ID] = p
return p, nil 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) 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 io.initializedPins[pd.ID] = p
return p, nil return p, nil

View File

@ -6,7 +6,10 @@ import (
) )
type fakeDigitalPin struct { type fakeDigitalPin struct {
n int id string
n int
drv GPIODriver
} }
func (p *fakeDigitalPin) N() int { func (p *fakeDigitalPin) N() int {
@ -41,12 +44,12 @@ func (*fakeDigitalPin) PullDown() error {
return nil return nil
} }
func (*fakeDigitalPin) Close() error { func (p *fakeDigitalPin) Close() error {
return nil return p.drv.Unregister(p.id)
} }
func newFakeDigitalPin(n int) DigitalPin { func newFakeDigitalPin(pd *PinDesc, drv GPIODriver) DigitalPin {
return &fakeDigitalPin{n} return &fakeDigitalPin{id: pd.ID, n: pd.DigitalLogical, drv: drv}
} }
func TestGpioDriverDigitalPin(t *testing.T) { func TestGpioDriverDigitalPin(t *testing.T) {
@ -73,7 +76,10 @@ func TestGpioDriverDigitalPin(t *testing.T) {
} }
type fakeAnalogPin struct { type fakeAnalogPin struct {
n int id string
n int
drv GPIODriver
} }
func (p *fakeAnalogPin) N() int { func (p *fakeAnalogPin) N() int {
@ -92,8 +98,8 @@ func (*fakeAnalogPin) Close() error {
return nil return nil
} }
func newFakeAnalogPin(n int) AnalogPin { func newFakeAnalogPin(pd *PinDesc, drv GPIODriver) AnalogPin {
return &fakeAnalogPin{n} return &fakeAnalogPin{id: pd.ID, n: pd.AnalogLogical, drv: drv}
} }
func TestGpioDriverAnalogPin(t *testing.T) { 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) 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")
}
}