Share a device between two drivers

To share a device between two drivers in Linux, you can use the binding and unbinding operations which are supported in a modern Linux kernel.

In order to bind / unbind a device to / from a driver, simply write the bus ID of the device to the bind / unbind file of that driver. See Manual driver binding and unbinding for more details.

For example, uart4 of imx6qdl has the following device node:

uart4: serial@21f0000 {
	compatible = "fsl,imx6q-uart", "fsl,imx21-uart";
	reg = <0x021f0000 0x4000>;
	interrupts = <0 29 IRQ_TYPE_LEVEL_HIGH>;
	clocks = <&clks IMX6QDL_CLK_UART_IPG>,
	clock-names = "ipg", "per";
	dmas = <&sdma 31 4 0>, <&sdma 32 4 0>;
	dma-names = "rx", "tx";
	status = "disabled";

The bus ID of this device will be 21f0000.serial.

And the driver that matches the compatible property is imx-uart.

The sysfs path of imx-uart will be /sys/bus/platform/drivers/imx-uart.

So, in order to unbind uart4 from imx-uart:

echo "21f0000.serial" > /sys/bus/platform/drivers/imx-uart/unbind

And to bind them again:

echo "21f0000.serial" > /sys/bus/platform/drivers/imx-uart/bind

Now if you want to use uart4 for other purpose, e.g. as a port connecting to a printer speaking proprietary protocol, you should add a new string to compatible property to be compatible with both drivers:

&uart4 {
	compatible = "fsl,imx6q-uart-raw", "fsl,imx6q-uart", "fsl,imx21-uart";

And your proprietary driver should have fsl,imx6q-uart-raw as compatible of struct of_device_id.

Then binding and unbinding operations might be (assuming driver name is imx-uart-raw):

echo "21f0000.serial" > /sys/bus/platform/drivers/imx-uart-raw/bind
echo "21f0000.serial" > /sys/bus/platform/drivers/imx-uart-raw/unbind

Besides, please note the following things:

  • Make sure each driver supports that device, not only the compatible property of that device in the device tree should list each driver it can be bound to, but also each driver’s source code should work properly with that device.
  • Make sure the probe() and remove() callback functions of each driver works, especially remove() should tear down all the resources constructed in probe(). Likewise, bind() and unbind() callbacks of struct component_ops if any.
  • A device couldn’t be bound to more than one driver at any time. You can check the driver symlink in the device directory before binding.
  • The default binding doesn’t rely on the order of compatible property, but the linked order of driver binaries, which is basically the order of the driver in the Makefile.
  • If you do the binding dynamically from an application and use udev to change the permissions of the device, make sure udev rules triggered before the application tries to open / read / write that device to avoid failure.

Have fun!

comments powered by Disqus