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  }