tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/tester/devicecmd.go (about) 1 package tester 2 3 // Cmd represents a command sent via I2C to a device. 4 // 5 // A command matches when (Command & Mask) == (Data & Mask). If 6 // a command is recognized, Response bytes is returned. 7 type Cmd struct { 8 Command []byte 9 Mask []byte 10 Response []byte 11 Invocations int 12 } 13 14 // I2CDeviceCmd represents a mock I2C device that does not 15 // have 'registers', but has a command/response model. 16 // 17 // Commands and canned responses are pre-loaded into the 18 // Commands member. For each command the mock receives it 19 // will lookup the command and return the corresponding 20 // canned response. 21 type I2CDeviceCmd struct { 22 c Failer 23 24 // addr is the i2c device address. 25 addr uint8 26 27 // Commands are the commands the device recognizes and responds to. 28 Commands map[uint8]*Cmd 29 30 // Command response that is pending (used with a command is split over) 31 // two transactions 32 pendingResponse []byte 33 34 // If Err is non-nil, it will be returned as the error from the 35 // I2C methods. 36 Err error 37 } 38 39 // NewI2CDeviceCmd returns a new mock I2C device. 40 func NewI2CDeviceCmd(c Failer, addr uint8) *I2CDeviceCmd { 41 return &I2CDeviceCmd{ 42 c: c, 43 addr: addr, 44 } 45 } 46 47 // Addr returns the Device address. 48 func (d *I2CDeviceCmd) Addr() uint8 { 49 return d.addr 50 } 51 52 // ReadRegister implements I2C.ReadRegister. 53 func (d *I2CDeviceCmd) readRegister(r uint8, buf []byte) error { 54 if d.Err != nil { 55 return d.Err 56 } 57 58 return nil 59 } 60 61 // WriteRegister implements I2C.WriteRegister. 62 func (d *I2CDeviceCmd) writeRegister(r uint8, buf []byte) error { 63 if d.Err != nil { 64 return d.Err 65 } 66 67 return nil 68 } 69 70 // Tx implements I2C.Tx. 71 func (d *I2CDeviceCmd) Tx(w, r []byte) error { 72 if d.Err != nil { 73 return d.Err 74 } 75 76 if len(w) == 0 && len(d.pendingResponse) != 0 { 77 return d.respond(r) 78 } 79 80 cmd := d.FindCommand(w) 81 if cmd == nil { 82 d.c.Fatalf("command [%#x] not identified", w) 83 return nil 84 } 85 86 cmd.Invocations++ 87 d.pendingResponse = cmd.Response 88 return d.respond(r) 89 } 90 91 func (d *I2CDeviceCmd) FindCommand(command []byte) *Cmd { 92 for _, c := range d.Commands { 93 if len(c.Command) > len(command) { 94 continue 95 } 96 97 match := true 98 for i := 0; i < len(c.Command); i++ { 99 mask := c.Mask[i] 100 if (c.Command[i] & mask) != (command[i] & mask) { 101 match = false 102 break 103 } 104 } 105 106 if match { 107 return c 108 } 109 } 110 111 return nil 112 } 113 114 func (d *I2CDeviceCmd) respond(r []byte) error { 115 if len(r) > len(d.pendingResponse) { 116 d.c.Fatalf("read too large (expected: <= %#x, got: %#x)", 117 len(d.pendingResponse), len(r)) 118 } 119 120 if len(r) > 0 { 121 copy(r, d.pendingResponse[:len(r)]) 122 d.pendingResponse = nil 123 } 124 125 return nil 126 }