Using new driver API

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.

4 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.

2 Likes

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

I tried to run driver recently created by @korli, but I can’t manage to make it loaded.

I use Haiku hrev54028 and I created i2c_drivers.hpkg package with following contents:

add-ons/kernel/bus_managers/i2c
add-ons/kernel/busses/i2c/pch_i2c

There are no i2c records in syslog and listimage. Reboot and ls -R /dev don’t help.

Did you add your own ACPI HID to pch_i2c_acpi.cpp?

Not yet. But isn’t it at least load and write something in syslog?

No, unless you enabled trace logs.

I added my device ID, enable tracing and add zero size check for MMIO to prevent “map_backing_store(): called with size=0 for area ‘PCHI2C memory mapped registers’!” KDL. Now something is working:

> i2c /dev/bus/i2c/1/bus_raw
Scanning I2C bus: /dev/bus/i2c/1/bus_raw
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00: 00 -- -- -- -- -- -- -- -- 09 -- -- -- -- -- -- 
10: 10 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 

OK. It looks like it didn’t find TCS1. Is it the same on Linux?

I am not familiar with Linux kernel internals and I don’t know how to check it.

I2C6 avalible at /dev/bus/i2c/1/bus_raw that contains i2c HID touchscreen and pen sensor seems working. This tablet also have LED light controlled by i2c.