This is intended to give a brief overview of how the servo code works, enabling developers to quickly make additions, and improve the servo framework.
.xml files that outline what controls a servod instance will expose. These can be found inside data/. Example.
The drivers used to execute controls. Example.
<control> <name>ppvar_vbat_ma</name> <doc>milliamps being consumed (discharging/positive) or supplied (charging/negative) to the battery</doc> <params cmd="get" subtype="milliamps" interface="10" drv="ec"> </params> </control>
A control - like ppvar_vbat_ma - is defined in a configuration file and executes code on invocation through dut-control or an RPC proxy. `
Params are a dictionary of parameters that controls are defined with, and are used to execute the control. The params list is passed to the drv on initialization for a specific control.
The interface describes what interface the drv should use to execute a control. Some important interfaces are: 8 == AP console, and 10 == EC console.
In the servod world a
<map> is used to map a human readable name to a numerial value. E.g. the “onoff” map defines on = 1 and off = 0.
The servo framework works by having a servod instance (a server to process requests) running and executing controls with the help of physical servo devices (v2, v4 etc).
The servod instance is invoked with a couple of implicit configuration files (like common.xml) and some explicit configuration files (like when invoking sudo servod -b lulu -c lulu_r2.xml). These configuration files define the controls this servod instance can handle, and configure how to execute them.
The following graphic shows how a call to dut-control ec_board works.
The dut-control control issues a request to the servo server, asking it to get the control ‘ec_board’.
The server initializes and keeps around an ec drv instance to execute the ec_board control.
Note: This is crucial, because it means that one can share state between two invocations of ec_board - since they use the same drv instance to execute them - but not as easily between two invocations of different controls, since they will use different drv instances to execute.
The server then dispatches an attempt to retrieve the information by calling .get() on the drv.
The return value then gets propagated all the way back up until finally dut-control prints out the response on the terminal.
A configuration file is an xml file that has
<include> elements as top level tags.
These elements define what you would use for dut-control. They are for instance pwr_button, ec_board, etc.
<control> elements can have the following subtags:
<name> Defines the control’s name. required.
<doc> A docstring to display.
<alias> Alias is another name to use for this control.
<params> Params used to instantiate a driver.
This allows for generic drivers that get the information they need passed through by the params dictionary. required. See more below
<remap> Remap makes the control at
<remap> an alias for the control. See FAQ for details.
Note: two params may be defined if the params for the set version of the control is different from the get version of the control. In that case, the params are required to have a ‘cmd’ attribute each, one defined as ‘get’ the other defined as ‘set’ to distinguish between them.
These elements indicate a config file to source before this config file. They only have one subtag,
<name> with the config file name.
These elements string to numerial value mappings that can be used when setting a control. When the user calls pwr_button:press, there is a config file loaded into the servod instance that defines a map to map press to a numerial value. The pwr_button control uses that map in its params.
Maps use a
<name> tag to indicate their name, and one
<params> tag to configure the transformations.
Drv (drivers) are classes used to perform the actions listed out in the configuration file. These drivers handle low level communication, and execution of the control.
HwDriver is the source of all drivers, and needs to be inherited from when building out a new driver. It contains the logic for calling the
_Set_|control_name| of a derived class when a control with a subtype is defined.
Another important driver is ptyDriver that the EC, AP, and Cr50 console controls use.
As mentioned above, it’s crucial to note that drivers get instantiated with a set of params for each control.
Interfaces are what get passed to a driver to use for control execution. These can either be real interfaces, or the servod instance, if interface=“servo” is defined in the params.
Inside servo_interfaces.py one can see all the interfaces defined for each servo type (v2, v4, ccd, etc). The interface index in the list is what the interface attribute in the params dictionary specifies.
The driver needs to know what interface it is expecting in order to meaningfully execute a control.
In general, params can be any parameters that the config file writer decides to add that are needed for a driver. However, there are a couple special parameters that one should be aware of:
As a parameter map tells servod what map to use for input on this control.
Either set or get. On controls with different params for get and set method this needs to be defined to associate the right params dictionar to the right method.
fmt function to execute on output values. Currently only supports hex.
If a driver has more than one method it exposes, then subtype defines what method should be called to execute a given control. The method called on the driver instance then is drv.
Input on set methods will be cast to |input_type|. Currently float, int, and str are supported.
Index of the interface to use for this control. “servo” if the interface is intended to be the servod instance.