Linux device driver with optional multiple platform driver interfaces - linux

Suppose I have a chip that has multiple options for interfacing with it, for example, the chip may have an I2C interface and a SPI interface and I can choose one of these interfaces and wire the device accordingly. How would I write a linux device driver for such device?
A chip with this behavior is ssd1307, which already has a driver implementation in the mainline linux kernel (https://github.com/torvalds/linux/blob/master/drivers/video/fbdev/ssd1307fb.c), implemented only for its I2C interface (the chip also has a 3/4 Wire SPI Interface). As you can see in the code, the probe function relies heavily on the fact that it is an I2C interface, so in order to use the SPI interface I would need to write an entirely new driver, repeating a lot of common code, or insert a lot of #ifdefs, which don't seem to be good approaches to me.

For chips providing several interface types, standard practice is to provide core driver and interface-specific frontends for it.
Several examples could be found e.g. in sound/soc/codecs/: pcm179x.c / pcm179x-i2c.c / pcm179x-spi.c and others.
This is not (yet?) done for ssd1307.

Related

How do I write a network device driver using SPI?

I have implemented a device driver for the NRF24L01+ transceiver in userspace using rust. The userspace driver makes use of the kernel spi interface driver. Writing the driver as kernel module seems incredibly hard, as the documentation for linux/netdevice.h as found in linux device drivers seems outdated (or I'm just not smart enough to understand the intricate details).
A new project from the TU Munich proposes the use of vfio. From my understanding this type of driver implementation uses the iommu to manage isolation to protected memory areas for the devices. "Project Ixy" uses the network device as block device, hence it can be mapped. SPI is different as insofar it is a streaming protocol.
My question is, if it is possible to integrate the user space spi network device driver into the linux network stack, e.g. having all protocols etc handled by the network stack. Is it possible to use a similar approach as Project Ixy, like having a small component in kernel space, which is isolated for security, that builds a "bridge" to userland?
I think it is possible in two ways:
using TAP interface
writing your own "bridging" interface like TAP between user space and kernel space
If ethernet-like interface is enough for you - then use TAP. I mean TAP provides functionality where physical layer is moved to user space. In your case it could work like this: data received by SPI can be pushed to TAP interface to linux network stack. The data received from TAP interface (from Linux network stack) you can push via SPI. Is that what you wanted?
If ethernet-like interace (as TAP is) is not enough for you - you can write your own interface in kernel space basing on TAP sources.

How do 'clients' fit in with 'platform devices' and 'platform drivers'

Reading about the driver-model platform, my understanding is that a platform driver is a static piece of kernel code that controls a platform device, which is a device which generally is always present in the system, as opposed to a driver composed of a kernel module, which can be loaded or removed at will and controls devices which might not always be connected. This answer seems to support that statement.
If all this is true, I expect it's what I'm looking for. I am trying to write a kernel module which, among other things, needs to communicate with an external device over I2C. While the external device isn't always part of the system, the I2C bus IS, so it ought to be controlled by a platform driver. So rather than writing code in my module to directly control the I2C, I ought to be using the platform driver. I found what I believe to be the proper platform driver for the I2C bus on my platform here, and it does use < linux/platform_device.h >, so that seems to be consistent.
However, in addition to this, when looking up I2C in general, I have seen reference to an I2C "client". From the name alone, this sounds like what I am trying to write now, a kernel module that uses the platform driver to communicate over I2C. However, in the documentation for the driver-model platform, nothing about a "client" is mentioned, so I don't know if this is correct or if it is some completely separate different model.
So how does a "client" kernel module fit into the driver-platform model for interfacing with hardware in Linux? Is my module supposed to instantiate a struct of the i2c_driver, or is that something done by the platform driver, and the module is supposed to create an interface to that?

What can be removed from the Linux i2c-dev driver to serve as a base for a new driver meant for only one device?

I'm trying to write a Linux character device driver for a device that just happens to communicate over I2C. The device is an Atmel microcontroller with code that provides an I2C address. It already works using the typical i2c-dev method on the Linux-side.
So now I want to replicate i2c-dev as a new driver that works specifically with this particular device, so that I can add some of my own device-specific abstraction code on top. But I'd like to trim out all the unnecessary code from i2c-dev that currently makes it generic. What can be removed in this situation?
What can be removed in this situation?
You're actually asking an XY question.
You would be better off looking at and adapting an existing I2C device driver that is already similar in required functionality, rather than hacking a special case driver for userspace access.
So now I want to replicate i2c-dev as a new driver that works specifically with this particular device, so that I can add some of my own device-specific abstraction code on top
So then you actually need to write a "Client driver" as described below (from Linux Documentation/i2c/summary):
When we talk about I2C, we use the following terms:
Bus -> Algorithm
Adapter
Device -> Driver
Client
An Algorithm driver contains general code that can be used for a whole class
of I2C adapters. Each specific adapter driver either depends on one algorithm
driver, or includes its own implementation.
A Driver driver (yes, this sounds ridiculous, sorry) contains the general
code to access some type of device. Each detected device gets its own
data in the Client structure. Usually, Driver and Client are more closely
integrated than Algorithm and Adapter.
Details are in Documentation/i2c/writing-clients.
To find a driver of similar functionality, scan the list of I2C client drivers. Note that these I2C drivers are located in the Linux source tree by their functionality (e.g. drivers/rtc/ or drivers/hwmon/) and not their interface (i.e. I2C).

How should I structure a linux driver that uses several chips in one device?

I have a hardware device that consists of 3 separate chips on an I2C bus. I would like to group them together and expose them to userland as one logical device. The user would see the logical device represented by a single directory somewhere in /sys as well as the nodes you'd expect from the I2C chips under /sys/class/i2c-adapter/i2c-?/*.
One of the chips is an MCP23017 which as far as I can tell already has a driver (drivers/gpio/gpio-mcp23s08.c) and I'd like to reuse it. Another of the chips is a PCA9685 and I would like to contribute a driver for this chip that uses the PWM system in include/linux/pwm.h. The third chip is an MCU running custom firmware.
How should I structure the set of drivers? One idea I had is to register a platform driver to present the logical device, and use I2C drivers from within that. Is this a good way to go? Are there better ways?
The logical device is a motor driver board and IR receiver. I have a simple diagram of its structure.
I'm looking to create two interfaces. The first similar to /sys/class/gpio where motors can be 'exported' and then accessed via reading and writing attributes. This would be useful for shell script access and quick debugging of the mechanical parts of the system attached to the motors. The second a character device node in /dev where data can be read or written in binary format, more useful for application control.
it seems not usual design, are you sure you have access to I2C bus of all chips ?
I think you should be able to talk only to MCU, and MCU should manage other devices.
Otherwise, why MCU is there ?
However, I cannot see your diagram, perhaps link is wrong.

How do user-space programs interact with a USB 802.11 Wi-Fi driver on Linux?

Allow me to qualify this question by stating that I am new to driver development. I am trying to understand the driver source code for a USB Wi-Fi card that uses a RealTek 8187L chip. Based on a good answer to my previous question, I established that the relevant driver source code that I needed to inspect is in drivers/net/wireless/rtl818x/rtl8187/dev.c (inside the Linux kernel source).
Doing some reading, it seems as though a USB driver instantiates a usb_driver struct that it registers with the kernel, which describes (among other things) the devices the driver supports (.id_table), the function to execute when a supported device is connected (.probe) and optionally, a set of file operations (.fops), for interaction with user-space. The usb_driver struct associated with the 8187L driver does not include a .fops:
static struct usb_driver rtl8187_driver = {
.name = KBUILD_MODNAME,
.id_table = rtl8187_table,
.probe = rtl8187_probe,
.disconnect = __devexit_p(rtl8187_disconnect),
.disable_hub_initiated_lpm = 1,
};
module_usb_driver(rtl8187_driver);
Hence, I am curious as to how user-space programs are interacting with this driver to send and receive data.
On an old Linux Journal post (2001), there is the following excerpt:
The fops and minor variables are optional. Most USB drivers hook into another kernel
subsystem, such as the SCSI, network or TTY subsystem. These types of drivers register
themselves with the other kernel subsystem, and any user-space interactions are provided
through that interface. But for drivers that do not have a matching kernel subsystem,
such as MP3 players or scanners, a method of interacting with user space is needed. The
USB subsystem provides a way to register a minor device number and a set of
file_operations [fops] function pointers that enable this user-space interaction.
So it sounds like the 8187L driver is probably one that "hooks into another kernel subsystem". So I suppose my question is, for such a driver, which doesn't supply a .fops function pointer, how is the interaction taking place with this other kernel subsystem? Ultimately I want to be able to find the point(s) in the driver code that programs are actually interacting with to send and receive data so I can continue to analyse how the code works.
The driver for an individual wireless chipset is at a very low level. There are many layers between it and userspace. The next layer above rtl8187 is mac80211. In drivers/net/wireless/rtl818x/rtl8187/dev.c observe the call to ieee80211_register_hw. That registration call provides the link between the mac80211 layer and the rtl8187 device.
Next look at the implementation of ieee80211_register_hw, found in net/mac80211/main.c. That calls ieee80211_if_add, found in net/mac80211/iface.c, which calls register_netdevice. And that puts the device in the kernel's main list of network interfaces, making it available for things like ifconfig and route.
Most userspace programs don't interact directly with a network interface, they just send packets to an IP address and the kernel uses the routing table to choose an outgoing interface.
The RTL8187 driver registers itself with the IEEE 802.11 wireless networking subsystem, by calling ieee80211_alloc_hw() and ieee80211_register_hw() in its probe function.

Resources