Re: [TECH TOPIC] Rust for Linux

Miguel Ojeda
2021-07-19T14:43:23Z

On Mon, Jul 19, 2021 at 3:53 PM Linus Walleij <linus.walleij@linaro.org> wrote:
>
> fn unmask(data: &Ref<DeviceData>, irq_data: &IrqData) {
> let mask = bit(irq_data.hwirq() % u64::from(PL061_GPIO_NR));
> let _guard = data.lock();
> if let Some(pl061) = data.resources() {
> let gpioie = pl061.base.readb(GPIOIE) | mask;
> let _ = pl061.base.try_writeb(gpioie, GPIOIE);
> }
> }

Wedson will answer thoroughly your other questions, but a quick note
on handling `{read,write}{r,w,l,q}` (since you asked in the past and
you put here the example :)

`pl061.base.readb(GPIOIE)` like above and similar calls are safe --
the function knows the address is correct because it controls the
computation of the address, rather than leaving users to compute the
address and then call a generic `{read,write}{r,w,l,q}` e.g. via
`readb(foo->base + GPIOIE)`. This is key.

`regmap` is able to check things similarly, but it does it at runtime
-- here the check happens at compile-time.

In fact, we can take this further. For instance, take a look at this
function in another GPIO driver that uses `regmap`:

static int ts4900_gpio_get_direction(struct gpio_chip *chip,
unsigned int offset)
{
struct ts4900_gpio_priv *priv = gpiochip_get_data(chip);
unsigned int reg;

regmap_read(priv->regmap, offset, &reg);

if (reg & TS4900_GPIO_OE)
return GPIO_LINE_DIRECTION_OUT;

return GPIO_LINE_DIRECTION_IN;
}

The entirety of this function and similar ones can be automatically
generated, including the masking and conversion into another type,
things like shifting, unit conversion, bit banging, etc. so that you
could call:

ts4900.map.get_direction()

Of course, this can be automated in independent tools/scripts that
e.g. generate C code from a hardware description. However, we could
also do the codegen in Rust itself, and provide a custom syntax to
describe the register map easily.

For instance, the driver developer could write something like this
right in the Rust source code:

memory_map!(
0x0042 simple_value 2 value(u16)
0x0044 some_flags 2 flags(..., FLAG1, FLAG2)
0x0046 custom_type 1 value(MyCustomType)
0x0047 across_bits 2 [
0..2 other_flags flags(FOO, BAR),
2..4 _ unused,
4..31 temp value(Temperature),
]
);

And then simply call:

driver.map.read_temp()

and it would automatically perform the shifting, masking, unit
conversions, etc. to get the raw sensor data into a value in proper
Kelvin.

Cheers,
Miguel