Skip to content

Commit

Permalink
tester: add a mock for command-oriented i2c devices
Browse files Browse the repository at this point in the history
  • Loading branch information
kenbell authored and deadprogram committed May 12, 2021
1 parent bd2530a commit 6b58fdc
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 2 deletions.
3 changes: 3 additions & 0 deletions tester/device.go
Expand Up @@ -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
}
6 changes: 6 additions & 0 deletions tester/device16.go
Expand Up @@ -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
}
6 changes: 6 additions & 0 deletions tester/device8.go
Expand Up @@ -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) {
Expand Down
126 changes: 126 additions & 0 deletions 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
}
3 changes: 1 addition & 2 deletions tester/i2c.go
Expand Up @@ -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.
Expand Down

0 comments on commit 6b58fdc

Please sign in to comment.