Using new driver API

It seems that Haiku has 2 driver API. First are legacy one and use init_hardware, init_driver etc. module exports. Second are new one and using modules and module_dependencies module exports. How to use new API? I tried to copy acpi_button driver, rename to acpi_test and change path from power to misc. Then I tried to copy it to /boot/home/config/non-packaged/add-ons/kernel/misc and nothing happend. Then I tried to move it to bin folder and api_version missing error appear.

My goal is making tablet touchscreen and pen usable on Haiku. Touchscreen and pen are i2c hid devices and i2c controller enumerated in ACPI bus with _HID = 808622C1 (Intel® I2C Controller). This controller is supported in Linux: https://github.com/torvalds/linux/blob/v5.4/drivers/i2c/busses/i2c-designware-platdrv.c#L126. Touchscreen and pen are working in Linux.

I’d recommend testing with Krita, since it is well-suited for pen and touch input. It even has support for pressure sensitivity, should the hardware be capable of sending input signals along the z-axis.

The question here is about the driver APIs, not about testing them, so I’m not sure what your comment is about…

As you have already noticed, the new API has a number of pitfalls and needs to be rethought, which is why there has not been a mass effort to move all drivers to it yet :slight_smile:

IIRC, the new drivers are only enumerated when paths are requested. That is, when /dev/misc is open()ed, drivers that are “misc” are scanned. However, I’m not sure if “misc” is included in the “new driver” paths, so it may not be enumerated properly in this way (another pitfall of the new API.)

Probably reading over the new-driver module code in the kernel would be your best bet. Some of the other developers may know a bit more (the haiku-development mailing list is a better place for questions like this one.)

1 Like

It was a recommendation of what application to use for testing the pen and touchscreen drivers that they are developing.

As an aside, so does WonderBrush. And now, back to your regularly scheduled dev talk…

The new API is centered around the device_manager. The idea is to remove the initial full scan of all devices on boot, and instead, as waddlesplash mentionned, trigger scanning dynamically when the devfs is opened by applications.

However, that approach doesn’t work that well, because a driver may need another one, and the scanning doesn’t always happen in the right order. The device_manager attempts to catch most of these cases, which results in it hardcoding a lot of knowledge about things (for example, which kind of PCI devices might have children that are mass storage).

The general idea is documented at https://git.haiku-os.org/haiku/tree/docs/develop/kernel/device_manager_introduction.html

In your case, you would first need an I2C bus manager (exposing the generic I2C devices and offering read, write, etc methods on them allowing to send them commands and an I2C bus driver (implementing the i2c communication over whatever hardware does the I2C bus mastering on your machine). Then, you need drivers which will scan this I2C bus (with the help of the device manager) and publish input devices in /dev/input/i2c/…

You could look at the MMC bus and SD card driver for an example of how this is all put together for a somewhat similar combination of bus, bus manager, and device.

Hope that helps :slight_smile:

2 Likes

It looks like the touch pad on the Lenovo laptop that I’m trying to get working is also a I2C HID Device. If these devices would both benefit from some of the same shared effort, I’m interested in working with someone on this.

2 Likes

In the Haiku install, I am not seeing mmc as an installed driver. If it is installed, should I find it at /system/add-ons/kernel/bus_manager/?

In that directory I do not see the mmc. But I do find the header in /system/develop/headers/private/drivers

I also see files in the haiku source under src/add-ons/kernel/bus_managers/mmc

Where should I be looking for the “MMC bus and SD card driver” examples you mentioned?

The driver is not finished so it is not added to the haiku images currently. You found the bus manager, there is also a bus (add-ons/kernel/busses/mmc) and then the driver for sd cards (add-ons/kernel/drivers/disk/mmc).

Some of the work on these is still pending review on review.haiku-os.org (search for “status:open mmc”) including the jamfiles changes to add the drivers to the haiku filesystem image

1 Like

Maybe unintentionally a revealing mistake. Haiku doesn’t merely perform a scan “of all devices” but instead it loads each device driver and runs the probe methods like BeOS or other 1980s-style operating systems. On a typical modern OS having device drivers you don’t need right now just costs disk space - which is very cheap, but in Haiku every such driver makes your OS slower and use up RAM.

Axel’s “new” driver API (over a decade old) screws this up even worse by flipping the hierarchy upside down.

In a typical modern OS device discovery starts from some very low-level platform hardware and then rises, using driver software to build a hierarchy, so e.g. the OS finds a PCI bus, a PCI bus driver finds a USB controller, the USB controller driver finds a video chipset, the video chipset discovers a 1280x1024 video panel. For an end user the experience is they plug a monitor into the HDMI socket on a USB dock they plugged into their laptop and now they can drag windows onto that display, which makes sense.

Axel’s design starts the other end with the user-facing stuff. But that’s a confusion. Consider the same hardware as before. The graphical display is looking for video outputs, but where might they be? Could be anywhere! It ends up having to scan the entire system and load all the drivers in case any of them might be hiding a video output. When should it re-scan to look for anything plugged in? Constantly? In practice, of course, the setup I described simply doesn’t work in Haiku at all, and there are several reasons for that, but the terrible driver API design is one factor.

2 Likes

Thanks for replies.

I tried to change “misc” to “power” and move driver to /boot/home/config/non-packaged/add-ons/kernel/power and still no effect in syslog. ls /dev/power don’t list acpi_test device. Old API is working, but I don’t know is it possible to use ACPI module with old API. USB module provide register_driver function, but there are no similar function in ACPI module.

Target device is enumerated only in ACPI tree, it is not enumerated in PCI bus.

Relevant part of dump of ACPI table:

        Device (I2C6)
        {
            Name (_HID, "808622C1")  // _HID: Hardware ID
            Name (_CID, "808622C1")  // _CID: Compatible ID
            Name (_DDN, "Intel(R) I2C Controller #6 - 808622C6")  // _DDN: DOS Device Name
            Name (_UID, 0x06)  // _UID: Unique ID
            Name (_DEP, Package (0x01)  // _DEP: Dependencies
            {
                PEPD
            })
            Name (RBUF, ResourceTemplate ()
            {
                Memory32Fixed (ReadWrite,
                    0x00000000,         // Address Base
                    0x00001000,         // Address Length
                    _Y1A)
                Interrupt (ResourceConsumer, Level, ActiveLow, Exclusive, ,, )
                {
                    0x00000025,
                }
                FixedDMA (0x001A, 0x0002, Width32bit, )
                FixedDMA (0x001B, 0x0003, Width32bit, )
            })
            Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
            {
                CreateDWordField (RBUF, \_SB.PCI0.I2C6._Y1A._BAS, B0BA)  // _BAS: Base Address
                CreateDWordField (RBUF, \_SB.PCI0.I2C6._Y1A._LEN, B0LN)  // _LEN: Length
                B0BA = I60A /* \I60A */
                B0LN = I60L /* \I60L */
                Return (RBUF) /* \_SB_.PCI0.I2C6.RBUF */
            }

            Method (_STA, 0, NotSerialized)  // _STA: Status
            {
                If (((I60A == Zero) || (L26D == One)))
                {
                    Return (Zero)
                }

                Return (0x0F)
            }

            Method (_PS3, 0, NotSerialized)  // _PS3: Power State 3
            {
                PSAT |= 0x03
                PSAT |= Zero
            }

            Method (_PS0, 0, NotSerialized)  // _PS0: Power State 0
            {
                PSAT &= 0xFFFFFFFC
                PSAT |= Zero
            }

            OperationRegion (KEYS, SystemMemory, I61A, 0x0100)
            Field (KEYS, DWordAcc, NoLock, WriteAsZeros)
            {
                Offset (0x84), 
                PSAT,   32
            }

            Method (_PSC, 0, NotSerialized)  // _PSC: Power State Current
            {
                If ((IC6P == Zero))
                {
                    Return (Zero)
                }
                Else
                {
                    Return (0x03)
                }
            }

            Device (TCS0)
            {
                Name (_ADR, Zero)  // _ADR: Address
                Name (_HID, "ELAN9010")  // _HID: Hardware ID
                Name (_CID, "PNP0C50" /* HID Protocol Device (I2C bus) */)  // _CID: Compatible ID
                Name (_S0W, Zero)  // _S0W: S0 Device Wake State
                Name (AHRV, One)
                Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
                {
                    Name (WBUF, ResourceTemplate ()
                    {
                        I2cSerialBusV2 (0x0010, ControllerInitiated, 0x00061A80,
                            AddressingMode7Bit, "\\_SB.PCI0.I2C6",
                            0x00, ResourceConsumer, , Exclusive,
                            )
                        GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
                            "\\_SB.GPO1", 0x00, ResourceConsumer, ,
                            )
                            {   // Pin list
                                0x0014
                            }
                        GpioInt (Level, ActiveLow, Shared, PullDefault, 0x0000,
                            "\\_SB.GPO3", 0x00, ResourceConsumer, ,
                            )
                            {   // Pin list
                                0x004D
                            }
                    })
                    Return (WBUF) /* \_SB_.PCI0.I2C6.TCS0._CRS.WBUF */
                }

                Method (_DSM, 4, Serialized)  // _DSM: Device-Specific Method
                {
                    Debug = "Method _DSM begin"
                    If ((Arg0 == ToUUID ("3cdff6f7-4267-4555-ad05-b30a3d8938de") /* HID I2C Device */))
                    {
                        Switch (ToInteger (Arg2))
                        {
                            Case (Zero)
                            {
                                Switch (ToInteger (Arg1))
                                {
                                    Case (One)
                                    {
                                        Debug = "Method _DSM Function Query"
                                        Return (Buffer (One)
                                        {
                                             0x03                                             // .
                                        })
                                    }
                                    Default
                                    {
                                        Return (Buffer (One)
                                        {
                                             0x00                                             // .
                                        })
                                    }

                                }
                            }
                            Case (One)
                            {
                                Debug = "Method _DSM Function HID"
                                Return (One)
                            }
                            Default
                            {
                                Return (Zero)
                            }

                        }
                    }
                    Else
                    {
                        Return (Buffer (One)
                        {
                             0x00                                             // .
                        })
                    }
                }

                Method (_STA, 0, NotSerialized)  // _STA: Status
                {
                    If ((BDID == One))
                    {
                        Return (Zero)
                    }

                    If ((ITSA == 0x10))
                    {
                        Return (0x0F)
                    }

                    Return (Zero)
                }
            }

            Device (TCS1)
            {
                Name (_ADR, Zero)  // _ADR: Address
                Name (_HID, "NTRG0001")  // _HID: Hardware ID
                Name (_CID, "PNP0C50" /* HID Protocol Device (I2C bus) */)  // _CID: Compatible ID
                Name (_S0W, Zero)  // _S0W: S0 Device Wake State
                Method (_PS3, 0, Serialized)  // _PS3: Power State 3
                {
                    If ((^^^^GPO1.AVBL == One))
                    {
                        ^^^^GPO1.TCD3 = Zero
                    }

                    Sleep (0x78)
                }

                Method (_PS0, 0, Serialized)  // _PS0: Power State 0
                {
                    If ((^^^^GPO1.AVBL == One))
                    {
                        ^^^^GPO1.TCD3 = One
                    }

                    Sleep (0x78)
                }

                Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
                {
                    Name (BBUF, ResourceTemplate ()
                    {
                        I2cSerialBusV2 (0x0060, ControllerInitiated, 0x00061A80,
                            AddressingMode7Bit, "\\_SB.PCI0.I2C6",
                            0x00, ResourceConsumer, , Exclusive,
                            )
                        GpioInt (Level, ActiveLow, Shared, PullDefault, 0x0000,
                            "\\_SB.GPO3", 0x00, ResourceConsumer, ,
                            )
                            {   // Pin list
                                0x004D
                            }
                    })
                    Return (BBUF) /* \_SB_.PCI0.I2C6.TCS1._CRS.BBUF */
                }

                Method (_DSM, 4, Serialized)  // _DSM: Device-Specific Method
                {
                    Debug = "Method _DSM begin"
                    If ((Arg0 == ToUUID ("3cdff6f7-4267-4555-ad05-b30a3d8938de") /* HID I2C Device */))
                    {
                        Switch (ToInteger (Arg2))
                        {
                            Case (Zero)
                            {
                                Switch (ToInteger (Arg1))
                                {
                                    Case (One)
                                    {
                                        Debug = "Method _DSM Function Query"
                                        Return (Buffer (One)
                                        {
                                             0x03                                             // .
                                        })
                                    }
                                    Default
                                    {
                                        Return (Buffer (One)
                                        {
                                             0x00                                             // .
                                        })
                                    }

                                }
                            }
                            Case (One)
                            {
                                Debug = "Method _DSM Function HID"
                                Return (One)
                            }
                            Default
                            {
                                Return (One)
                            }

                        }
                    }
                    Else
                    {
                        Return (Buffer (One)
                        {
                             0x00                                             // .
                        })
                    }
                }

                Method (_STA, 0, NotSerialized)  // _STA: Status
                {
                    If ((BDID == One))
                    {
                        Return (Zero)
                    }

                    Return (Zero)
                }
            }

            Device (TCS2)
            {
                Name (_ADR, Zero)  // _ADR: Address
                Name (_HID, "WCOM0016")  // _HID: Hardware ID
                Name (_CID, "PNP0C50" /* HID Protocol Device (I2C bus) */)  // _CID: Compatible ID
                Name (_S0W, Zero)  // _S0W: S0 Device Wake State
                Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
                {
                    Name (RBUF, ResourceTemplate ()
                    {
                        I2cSerialBusV2 (0x0009, ControllerInitiated, 0x00061A80,
                            AddressingMode7Bit, "\\_SB.PCI0.I2C6",
                            0x00, ResourceConsumer, , Exclusive,
                            )
                        GpioInt (Level, ActiveLow, Shared, PullDefault, 0x0000,
                            "\\_SB.GPO2", 0x00, ResourceConsumer, ,
                            )
                            {   // Pin list
                                0x0013
                            }
                        GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
                            "\\_SB.GPO1", 0x00, ResourceConsumer, ,
                            )
                            {   // Pin list
                                0x0014
                            }
                    })
                    Return (RBUF) /* \_SB_.PCI0.I2C6.TCS2._CRS.RBUF */
                }

                Method (_DSM, 4, Serialized)  // _DSM: Device-Specific Method
                {
                    Debug = "Method _DSM begin"
                    If ((Arg0 == ToUUID ("3cdff6f7-4267-4555-ad05-b30a3d8938de") /* HID I2C Device */))
                    {
                        Switch (ToInteger (Arg2))
                        {
                            Case (Zero)
                            {
                                Switch (ToInteger (Arg1))
                                {
                                    Case (One)
                                    {
                                        Debug = "Method _DSM Function Query"
                                        Return (Buffer (One)
                                        {
                                             0x03                                             // .
                                        })
                                    }
                                    Default
                                    {
                                        Return (Buffer (One)
                                        {
                                             0x00                                             // .
                                        })
                                    }

                                }
                            }
                            Case (One)
                            {
                                Debug = "Method _DSM Function HID"
                                Return (One)
                            }
                            Default
                            {
                                Return (Zero)
                            }

                        }
                    }
                    Else
                    {
                        Return (Buffer (One)
                        {
                             0x00                                             // .
                        })
                    }

                    Return (Zero)
                }

                Method (_STA, 0, NotSerialized)  // _STA: Status
                {
                    If ((DGEF == One))
                    {
                        Return (0x0F)
                    }
                    Else
                    {
                        Return (Zero)
                    }
                }
            }
        }

TCS0 is touchscreen, TCS2 is pen sensor.

I already have pen testing application for Windows that use WM_POINTER API.

Pen operation is not big problem because it use HID protocol that already handled in Haiku (but hardcoded to USB). Most complex problem is getting I2C controller working. First, I need to find ACPI device with _HID = “808622C1” and get working MMIO ranges. Currently threre are no drivers in Haiku that use MMIO from ACPI table. I’m not sure that it actually working.

If you want to use new-style drivers, you need to add something to the device_manager to get it to work. Otherwise your driver will never get loaded.

See here: https://git.haiku-os.org/haiku/tree/src/system/kernel/device_manager/device_manager.cpp#n1545

You can see that we use very few new-style drivers, because of the limitations and problems tialaramex has given. The way this device manager was designed is very PCI/USB centric, assuming that it’s easy to know how to find a particular type of device by the use of things like device classes (for example, USB input devices would be of type HID, PCI mass storage ones would be one of a few types as you can see in that list, etc). But this does not work well for other device types.

So it’s quite likely that we will drop or at least significantly rework this, as mentionned again by tialaramex, to have a “device tree” based approach, where we start from some kind of “root node” and explore it, and each time we discover a device in the tree, match it up with a driver if possible.

Maybe, until that’s done it makes more sense to write drivers using the old style API instead, where things are a bit simpler: you’re just asked “do you handle any device on this machine?” and you can ask the ACPI module if there is something that you can handle.

In this case should I use device manager module to search/register device or use ACPI module directly? If second what happens when 2 drivers attempt to use same device?

In the old driver model there is no reservation system and multiple drivers can attempt to use the same hardware. I guess BeOS engineers would already be very happy that there could be one driver for each device, they didn’t think that there could ever be two?

I managed to find ACPI device and call get_current_resources, but returned MMIO range was 0x00000000 .. 0x00000fff. Why start address is zero? Does some initialization sequence required to set MMIO?

On Windows 10 MMIO range is 0x91927000 .. 0x91927fff.

Well, Haiku still boots pretty quickly, and drivers mostly work, so we have not yet run into anything that we need to fix in order to move forward. Certainly we should redesign this for all the reasons you mentioned, but it’s not really holding back the system right now.

1 Like

Attempt to use test driver on target machine failed because of https://dev.haiku-os.org/ticket/15702.

After adding additional compiler flags I managed to compile test driver on 86_64 and run it on target machine. MMIO range was successfully detected and value is same as in Windows 10. Now I can communicate to I2C controller (I hope).
Syslog:

KERN: driver "acpi_test" added
KERN: devfs: reload driver "acpi_test" (3, 2623034)
KERN: acpi_test: Found device: "\_SB_.PCI0.I2C1"
KERN: acpi_test: Found device: "\_SB_.PCI0.I2C2"
KERN: acpi_test: Found device: "\_SB_.PCI0.I2C3"
KERN: acpi_test: Found device: "\_SB_.PCI0.I2C5"
KERN: acpi_test: Found device: "\_SB_.PCI0.I2C6"
KERN: acpi_test: Found device: "\_SB_.PCI0.I2C7"
KERN: acpi_test: handle: 0xffffffff82b19ba8
KERN: acpi_test: HID: "808622C1"
KERN: acpi_test: _PS0 called
KERN: acpi_test: _STA: 1, 15
KERN: acpi_test: MMIO: 91927000, 4096
KERN: acpi_test: IRQ: 37
KERN: acpi_test: DMA: 26, 2, 2
KERN: acpi_test: DMA: 27, 3, 2
3 Likes