From 6b58fdc95a25fe10eab084438e7fa8f6f3113e1a Mon Sep 17 00:00:00 2001 From: Kenneth Bell Date: Tue, 11 May 2021 22:18:09 -0700 Subject: [PATCH] tester: add a mock for command-oriented i2c devices --- tester/device.go | 3 ++ tester/device16.go | 6 +++ tester/device8.go | 6 +++ tester/devicecmd.go | 126 ++++++++++++++++++++++++++++++++++++++++++++ tester/i2c.go | 3 +- 5 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 tester/devicecmd.go diff --git a/tester/device.go b/tester/device.go index 9a9648d19..9933ef87b 100644 --- a/tester/device.go +++ b/tester/device.go @@ -10,6 +10,9 @@ type I2CDevice interface { // WriteRegister implements I2C.WriteRegister. WriteRegister(r uint8, buf []byte) error + // Tx implements I2C.Tx + Tx(w, r []byte) error + // Addr returns the Device address. Addr() uint8 } diff --git a/tester/device16.go b/tester/device16.go index deb4ce27b..4c6c81c8a 100644 --- a/tester/device16.go +++ b/tester/device16.go @@ -72,3 +72,9 @@ func (d *I2CDevice16) WriteRegister(r uint8, buf []byte) error { return nil } + +// Tx implements I2C.Tx. +func (bus *I2CDevice16) Tx(w, r []byte) error { + // TODO: implement this + return nil +} diff --git a/tester/device8.go b/tester/device8.go index 2c589335b..114cf535a 100644 --- a/tester/device8.go +++ b/tester/device8.go @@ -53,6 +53,12 @@ func (d *I2CDevice8) WriteRegister(r uint8, buf []byte) error { return nil } +// Tx implements I2C.Tx. +func (bus *I2CDevice8) Tx(w, r []byte) error { + // TODO: implement this + return nil +} + // assertRegisterRange asserts that reading or writing the given // register and subsequent registers is in range of the available registers. func (d *I2CDevice8) assertRegisterRange(r uint8, buf []byte) { diff --git a/tester/devicecmd.go b/tester/devicecmd.go new file mode 100644 index 000000000..decb0423e --- /dev/null +++ b/tester/devicecmd.go @@ -0,0 +1,126 @@ +package tester + +// Cmd represents a command sent via I2C to a device. +// +// A command matches when (Command & Mask) == (Data & Mask). If +// a command is recognized, Response bytes is returned. +type Cmd struct { + Command []byte + Mask []byte + Response []byte + Invocations int +} + +// I2CDeviceCmd represents a mock I2C device that does not +// have 'registers', but has a command/response model. +// +// Commands and canned responses are pre-loaded into the +// Commands member. For each command the mock receives it +// will lookup the command and return the corresponding +// canned response. +type I2CDeviceCmd struct { + c Failer + + // addr is the i2c device address. + addr uint8 + + // Commands are the commands the device recognizes and responds to. + Commands map[uint8]*Cmd + + // Command response that is pending (used with a command is split over) + // two transactions + pendingResponse []byte + + // If Err is non-nil, it will be returned as the error from the + // I2C methods. + Err error +} + +// NewI2CDeviceCmd returns a new mock I2C device. +func NewI2CDeviceCmd(c Failer, addr uint8) *I2CDeviceCmd { + return &I2CDeviceCmd{ + c: c, + addr: addr, + } +} + +// Addr returns the Device address. +func (d *I2CDeviceCmd) Addr() uint8 { + return d.addr +} + +// ReadRegister implements I2C.ReadRegister. +func (d *I2CDeviceCmd) ReadRegister(r uint8, buf []byte) error { + if d.Err != nil { + return d.Err + } + + return nil +} + +// WriteRegister implements I2C.WriteRegister. +func (d *I2CDeviceCmd) WriteRegister(r uint8, buf []byte) error { + if d.Err != nil { + return d.Err + } + + return nil +} + +// Tx implements I2C.Tx. +func (d *I2CDeviceCmd) Tx(w, r []byte) error { + if d.Err != nil { + return d.Err + } + + if len(w) == 0 && len(d.pendingResponse) != 0 { + return d.respond(r) + } + + cmd := d.FindCommand(w) + if cmd == nil { + d.c.Fatalf("command [%#x] not identified", w) + return nil + } + + cmd.Invocations++ + d.pendingResponse = cmd.Response + return d.respond(r) +} + +func (d *I2CDeviceCmd) FindCommand(command []byte) *Cmd { + for _, c := range d.Commands { + if len(c.Command) > len(command) { + continue + } + + match := true + for i := 0; i < len(c.Command); i++ { + mask := c.Mask[i] + if (c.Command[i] & mask) != (command[i] & mask) { + match = false + break + } + } + + if match { + return c + } + } + + return nil +} + +func (d *I2CDeviceCmd) respond(r []byte) error { + if len(r) > len(d.pendingResponse) { + d.c.Fatalf("read too large (expected: <= %#x, got: %#x)", + len(d.pendingResponse), len(r)) + } + + if len(r) > 0 { + copy(r, d.pendingResponse[:len(r)]) + d.pendingResponse = nil + } + + return nil +} diff --git a/tester/i2c.go b/tester/i2c.go index 1d86d140c..fb3628a8c 100644 --- a/tester/i2c.go +++ b/tester/i2c.go @@ -48,8 +48,7 @@ func (bus *I2CBus) WriteRegister(addr uint8, r uint8, buf []byte) error { // Tx implements I2C.Tx. func (bus *I2CBus) Tx(addr uint16, w, r []byte) error { - // TODO: implement this - return nil + return bus.FindDevice(uint8(addr)).Tx(w, r) } // FindDevice returns the device with the given address.