Hardware enablement

Linux 3.4 is close to be released and this version includes a device driver I was working on my free time a few months ago for the Cypress TrueTouch Gen3 family of touchscreen micro-controllers.

So, now these devices found on many embedded devices such as the HP Touchpad, Sony Xperia phone and the Nook Color e-reader are fully supported on Linux and the driver is using the stateful multi-touch protocol type B (identifiable contacts and slots) taking full advantage of the hardware finger tracking capabilities.

Many thanks to Kevin McNeely from Cypress that provided me the last version of Cypress’s Android Gingerbread driver and for answering all my questions about the device operation and hardware registers. First I thought I could just forward port this driver and post it for upstream inclusion. But after posting Cypress’s driver, I had so many change requests from the kernel hackers that I had to basically rewrite the driver (the most important issue was that it used input MT protocol type A instead of B).

The Linux multi-touch and input maintainers, Henrik Rydberg and Dmitry Torokhov helped me a *lot* reviewing my patches, explaining me the correct approach to report the contact slots to the input MT layer and pointing me out lots of issues with the driver. I wish all the Linux subsystem maintainers were as constructive and willing to help as Henrik and Dmitry.

I had fun working with the driver and now the Linux installation on my Nook Color is more close to functional. My next step is to work on the LCD panel driver that’s still not supported. A touch-screen without a panel is not very useful besides running evtest on a console to see how the input evens are reported to user-space 🙂

Finally, if you are a hardware vendor and want your device supported on Linux or have an out-of-tree driver that needs to get merged on the mainline kernel, please drop an email to sales@collabora.co.uk, we will be more than pleased to work with you to make that happen.

Netlink notifier chain

At Collabora we are working on improving the D-Bus IPC system. We are trying different approaches and one of them is implementing the D-Bus routing logic and match rules handling as a Generic Netlink service. Rodrigo wrote a great blog entry on the design of this Netlink solution.

A problem we found is that AF_NETLINK is not connection oriented. Applications don’t establish a connection before transmitting, they just open a socket and start sending messages. So, if an application close its socket, the other peers won’t notice. This also applies to the kernel code since in Generic Netlink, the in kernel service is just another user of the Netlink channel.

The problem is that D-bus is connection oriented, we have to keep track of each connection to the bus and remove it when the socket associate with the connection is closed.

Fortunately, the kernel has a mechanism known as notifier chains that allows kernel code to listen for certain events by registering to a specific notifier chain.

Netlink defines a notifier netlink_chain to notify when a socket is released (NETLINK_URELEASE event). So, we can use that information to remove the D-Bus connection associate with a socket when it is released.

The notifier API is very simple, we only have to define a notifier_block data structure and assign a function handler to the notifier_call function pointer. Once the notifier_block data structure is registered, the notifier_call function handler is called every time the events occurs.

I knew about notifier chains before but haven’t used until today so I thought it was something interesting to share. Here is a small example of how we can use the Netlink notifier chain:

static int dbus_netlink_notify(struct notifier_block *nb, unsigned long state,
                               void *_notify)
{
        struct netlink_notify *notify = _notify;

        if (state == NETLINK_URELEASE && notify->protocol == NETLINK_GENERIC)
                dbus_bus_del_connection(notify->pid);

        return NOTIFY_DONE;
}

static struct notifier_block dbus_netlink_notifier = {
	.notifier_call = dbus_netlink_notify,
};

static int __init dbus_init(void)
{
	int rc;

	rc = genl_register_family_with_ops(&dbus_family, dbus_ops,
					   ARRAY_SIZE(dbus_ops));
	if (rc)
                return rc;

	rc = netlink_register_notifier(&dbus_netlink_notifier);
	if (rc) {
                genl_unregister_family(&dbus_family);
                return rc;
	}

	return 0;
}