Linux Kernel - GPIO_REQUEST and Sharing GPIOs - c

I am writing a linux kernel driver and am running into a strange issue which perhaps stems from my lack of understanding. The program I am writing is a kernel module that is attempting to do some communication over a GPIO. The communication protocol is simple (toggles high or low depending on what I am passing).
SITUATION There is an existing driver that is using a GPIO that I am interested in. It also uses it for communications, but only reads and does not write. My driver only does writing. The communication protocol we both use is the same. My module calls GPIO_REQUEST after the existing driver has set itself up (I do not have access to the source of the other driver).
Any attempt to get the gpio via GPIO_REQUEST will result in the driver resetting itself. I have confirmed this behaviour on the osciliscope.
QUESTION How can two drivers share a GPIO? I am assuming that my GPIO_REQUEST call somehow takes away control of the GPIO from the other driver and that calling GPIO_FREE does not fix the situation (causing the reset).
I would ideally like to "borrow" the gpio, set the direction to output, write my data, and then return it back to the other driver.
Note: Willing to modify kernel source if need be.

Related

I2C-Bus Implementation on Linux (Raspberry Pi)

I have recently written numerous functions in C for microfluidic pumps which are controlled via I2C on a Raspberry Pi. They work perfectly. I made use of the O_RDWR to write and read from "/dev/i2c-1".
However, I am still wondering how exactly these file changes are implemented on a low level. How is the code, which generates the correct bit sequences to communicate with these pumps, transferred to the PINs? What tells the kernel to change the states of the 2 I2C-PINs accordingly to code?
Thank you!
In most cases, software is not directly responsible for generating each individual logic level change. Instead, it is a combination of a hardware based I2C controller, that is managed from software by a driver.
In the case of your RPi, you are most likely using this driver:
https://elixir.bootlin.com/linux/v5.19/source/drivers/i2c/busses/i2c-bcm2835.c
The hardware block that this driver is controlling is described in section 3 of this reference manual:
https://www.raspberrypi.org/app/uploads/2012/02/BCM2835-ARM-Peripherals.pdf
(A side note: the kernel does know how to generate all logic changes in software as well, for cases where no dedicated hardware is available. If you want to know more about that, have a look at the i2c-gpio driver)

What does dev_net_set do in Linux?

I am writing a simple net device driver based on the loopback driver and want to register my net_device structure. This and that page on writing a net device say to just call register_netdev. But they're writing fancy drivers with PCI express and other complicated things.
So, if I just want something like the loopback driver, I should presumably base my code on loopback.c. My question is, what does the first line of this code in loopback_net_init do:
dev_net_set(dev, net);
err = register_netdev(dev);
Apparently net is determined by this code in net_namespace.c:
register_pernet_device(ops) ...
__register_pernet_operations(list, ops)
for_each_net(net) ...
What is this looping for? What might go wrong if I skip the dev_net_set call? Why are others not using it?
AFAIK, net is a structure that will allow the kernel to interact with the device. You need it to register the device and remove it in the module cleanup function. Please review the code under linux/net/8021q/ for examples.
AFAIK, looping happens at the level of sockets (layer 5-7), whereas net_dev is used as the kernel component that immediately interacts with the driver, when you actually want to use a say, ethernet card, or SLIP,PLIP for transmitting frames (layer 2-0). Loopback happens at the level of the network subsystem of the kernel, and lies way above the drivers which interact with the hardware. So I don't see why you would need a driver to use the loopback feature. However, there is also a provision for registering a dummy device with net_dev, though I don't know if that is what you are looking for.
That said, if your intention is to simply use some driver that simulates an actual physical device without one and say, reflects the packets that it recieves, that is possible too. Basically till the net_dev layer, the kernel does all the protocol stuff (TCP/IP), and finally passes off the packet to some handle that the device driver registers with the net_dev or something similar. Similarly on receiving stuff, the device triggers an interrupt, the driver does a DMA operation, and the kernel takes over from there. Hence instead of the code for doing the DMA operation, you can make a module that simply pass over a static packet, that is compatible with ethernet/TCP/IP . In a vast majority of cases, all these aspects (the network and other subsystems) are agnostic to the underlying bus details, i.e. it shouldn't matter whether the ethernet card is connected to PCI or ISA but there can be exceptions. Thus, IMHO, you are trying to do something that should only be attempted after having a thorough understanding of the network subsystem, and a good enough understanding of the kernel as a whole. Till then you will be shooting in the dark. Sometimes you may hit, but often-times you will miss.
http://man7.org/linux/man-pages/man8/ip-netns.8.html
A network namespace is logically another copy of the network stack,
with its own routes, firewall rules, and network devices.
So for_each_net is looping over these namespaces and creating a copy of all "per net" network devices in each one.
Use ip netns list to determine whether you are using network namespaces. Often they are not used, so drivers do not necessarily need to use dev_net_set.

PCIe bus latency when using ioctl vs read?

I've got a hardware client1 who's line of data acquisition cards I've written a Linux PCI kernel driver for.
The card can only communicate 1-4 bytes at a time depending on how the user specifies to utilize it, given this, I utilize ioctl for some of the functionality, but also make use of the file_operations structure to treat the card as a basic character device to give the user of the card the ability to just use read or write if they just want simple 1-byte communication with the card.
After discussing the driver with the client, one of their developers is in the understanding that treating the card as a character device by using open/read/write will introduce latency on the PCI bus, versus using open/ioctl.
Given that the driver makes no distinction how it's opened and the ioctl and read/write functions call the same code, is there any validity to this concern?
If so, how would I test the bus latency from my driver code? Are there kernel functions I can call to test this?
Lastly, wouldn't my test of the bus only be valid for my specific setup (kernel settings, platform, memory timing, CPU, etc.)?
1: they only have 2 other developers, neither of which have ever used Linux
I suspect the client's developer is slightly confused. He's thinking that the distinction between using read or write versus ioctl corresponds to the type of operation performed on the bus. If you explain to him that this is just a software API difference and either option performs the exact same operation on the bus, that should satisfy them.

SPI kernel module, how to istantiate the driver?

I've been tasked to import the spi driver into an existing platform running Openwrt.
After "successfully" build the full Openwrt: packages and the kernel matching the one running into the platform, including the spidev kernel module I run into some trouble in make this module work.
**insmod** of the driver terminates without error, and I see that in the **/sys/class** the creation of the directory **spidev**, but it is empty.
Looking at the code of the spidev kernel module, the function **probe** has caught my eye. My feel is that this function is what actually allocates the minor device number an make the device available to be used. But it's not clear to me who, or what should call it.
Another doubt I have is about the architecture. The spi depends on the underlaying architecture, which in my case is the MT7620 soc which is a mipsel architecture have a specific spi code. In my understanding this SOC specific code is wrapped by the spidev kernel module and the link between these two entities should be
status = spi_register_driver(&spidev_spi_driver);
in the
static int __init spidev_init(void)
function.
Again, I'm far to be sure of what I'm writing and I'm here asking for directions.
First, you may want to read the Linux Device Driver - Chapter 14.
Generally speaking, in the Linux kernel you have devices and drivers on a bus (subsystem). You have to register them using the functions register_device() and register_driver() (the exact name depends on the subsystem). When a device or a driver get registered the subsystem will try to match devices with drivers. If they match, the subsystem calls the driver's function probe(). So, the execution of the probe() function can be triggered by the driver registration or the device registration. This also means that the device exists before the execution of probe()
Back to your specific case: SPI. To register a device instance you need to use spi_alloc_device and spi_add_device, or spi_new_device. Where to put this code? This depends on your need. Typically, the SPI devices are declared in some architecture file or device-tree description. Probably you can also do it in a module (not sure).
The spidev is a Linux device driver that exports the SPI raw interface to the user-space. This means that once you register an SPI device instance the driver spidev take control of it. Actually, the spidev does nothing: it waits for an user-space program to read/write data on the SPI bus. So, you will end up writing the driver for your device in userspace.

Linux device driver for a RS232 device in embedded system

I have recently started learning to write Linux device drivers for a specific project that I am working on. Previously most of the work I have done has been with devices running no OS so Linux drivers and development is somewhat new to me.
For the project I am working on I have an embedded system running a Linux based operating system. I have an external device with is controlled via RS232 that I need to write a driver for.
Questions:
1) Is there a way to access serial ports from withing kernel space (and possibly use serial.h, serial_core.h, etc.), how is this usually done, any good examples?
2) From what I found it seems like it would be much easier to access the serial ports in user space by just opening dev/ttyS* and writing to it. When writing a driver for a device like this (RS232 device) is it preferred to do it in user space or is there a way to write a kernel module? How does one decide to write a driver as a kernel module over user space or vise versa?
Are drivers only for generic devices such as UART/serial and then above that is userspace or should this driver be written as a kernel module? I appreciate the help, I have been unable to find much information to answer my questions.
There are a few times when a module that communicates over a serial port may be in the kernel. The pppd (point to point protocol daemon) is one example as Linux has some kernel code devoted to that since it is a high traffic use of serial and it also needs to turn around and put the IP packets into kernel space.
Most other uses would work better from user space since you have a good API that already takes care of a lot of the errors that can happen. This also lessens the chance that your errors will result in massive system failure.
Doing things like this from user space does result in some latency. Reads and writes are buffered, and it's often difficult to tell where in the write operations the hardware actually is, and canceling an already succeeded write call isn't really doable from user space, even if the hardware hasn't yet received the bytes.
I would suggest attempting to do it from user space first and then move to OS driver if necessary. Even if it is necessary to move this into an OS level driver, you'll likely be able to get some progress made from user space.

Resources