Skip to content

Disassociate struct device from devicetree nodes for application layer #57720

Open
@bjarki-andreasen

Description

@bjarki-andreasen

Introduction

This RFC looks into defining and classifying struct devices from within the device drivers by device class (which API they implement) and presenting them to the application by class agnostic from the devicetree.

Problem description

Consider the following devicetree snippet:

&i2c1 {
    status = "okay";
    clock-frequency = <I2C_BITRATE_STANDARD>;

    mcp7940n@25 {
        compatible = "microchip,mcp7940n";
        reg = <0x25>;
        status = "okay";

        int-gpios = <&gpioa 1>;

        rtc0: rtc {
            compatible = "microchip,mcp7940n-rtc";
            status = "okay";
        };

        bbram0: bbram {
            compatible = "microchip,mcp7940n-bbram";
            status = "okay";
        };
    };
};

The bbram and rtc nodes are endpoints, which don't have any capabilities useful to the devicetree. Yet, with the current devicetree model, we have to create a node for them here to make it available to the application.

Proposed change

The solution proposed in this RFC will allow us to create the child nodes within the device drivers, removing the need for these superfluous nodes in the devicetree, which will reduce the devicetree snippet to the following (just like the Linux devicetree :))

&i2c1 {
    status = "okay";
    clock-frequency = <I2C_BITRATE_STANDARD>;

    mcp7940n@25 {
        compatible = "microchip,mcp7940n";
        reg = <0x25>;
        status = "okay";
        int-gpios = <&gpioa 1>;
    };
};

This results in less boilerplate devicetree code, removes the need for additional devicetree compatibles for the child nodes, and removes the need for using devicetree aliases like bbram0, rtc0, ... from the application. To get the nodes from the application, we will do this instead:

BUILD_ASSERT(DEVICE_CLASS_HAS_ANY(rtc), "application requires an RTC");
BUILD_ASSERT(DEVICE_CLASS_HAS_ANY(bbram), "application requires BBRAM");

const struct device *rtc = DEVICE_CLASS_GET(rtc, 0);
const struct device *bbram DEVICE_CLASS_GET(bbram, 0);

This is possible using sections in elf files, which will find all struct device defined using DEVICE_DEFINE, and which API type the devices implements (their class). From this, we can create macros to find and iterate through struct devices by class agnostic from the devicetree.

Its essentially Linux SYSFS but static and using macros :)

Dependencies

Applications will now be encouraged to use the new class based macros to identify and use devices, instead of devicetree nodes. Subsystems like shells would also be built at the application level, or at their own level between drivers and the application to use the DEVICE_CLASS macros.

Devicetree nodes will still work as before, the drivers and kernel need to know the actual devicetree layout.

Concerns and Unresolved Questions

  • Is it practical?
  • How much does it break?
  • Is a device class based approach (like Linux sysfs) something we want on an embedded platform given its abstraction from the devicetree?
  • Can we, and should we, guarantee the enumeration of devices is consistent across builds? CAN0 will always be CAN0 for a particular board for example.
  • Can this be expanded to other subsystems like network devices? something like NET_IFACE_GET(ppp, 0);

Alternatives

Create runtime functions instead of macros to sort through all devices by types, this is slower than macros, and we can't check struct devices exist at compile time DEVICE_CLASS_HAS_ANY(...), but it's simple C code with no elf_parser magic, which is faster than looking through strings which we do for bindings now. It will also allow for users to create struct devices in the application, which would not be possible with the macro solution.

Metadata

Metadata

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions