Interfacing a linux device driver with dummy PCI device - c

I have a user space program that simulates a PCI device. I have downloaded the nvme linux device driver that interacts with the PCI device using the NVMe standard. I have to verify that my userspace program is compatible with the standard.
The nvme.c(the linux device driver) contains the nvme_probe() function that would be called when the device is plugged in. Since I do not have the device so I think I will incorporate the probe functionality in nvme_init() function.
Now I have studied quite a lot on the internet to understand how to emulate a PCI device, posts such as
Installing PCI driver without connection to device,
emulating a PCI device on linux
I do not get the idea how to return the populated struct pci_dev to the function call in the nvme_probe() ofpci_set_drvdata(pdev, dev);
And if you could suggest a tutorial, on how to manually populate the pci_dev struct with dummy device configuration and memory address of the userspace program function pointers to emulate interaction with the nvme driver.

I don't think it is possible to fake such thing with standard linux kernel.
Because in module_init() you are telling the kernel's PCI SUBSYSTEM to load the operation handlers (a.k.a - callbacks through function pointers) when a certain device is present in the system (via id_table).
so whenever you insmod your module, kernel's PCI SUBSYTEM then knows to load your driver whenever a device of matching vid/pid is plugged into the PCIe slot. The operation is like below -
Tell kernel to load {my_driver.ko} when this {vid/pid} pci device is
found in module_init or _init
After kernel knew, whenever a matching {vid/pid} device is connected to the system, it will call the .probe function callback of {my_driver.ko}
You may init the device (for real-device) or just return true to tell kernel that has correctly initialized the device.
You can also register new driver type from this probe function (for
read/write).
I am not sure about any magic VID/PID number which causes the PCI SUBSYTEM to always load the driver.
But you can actually load the PCI driver by using an actual PCI device.
Just remove appropriate driver for a real-PCI device. and use it's VID & PID as your driver's VID PID. Then the PCI SUBSYTEM will load your driver & you can also test your driver to simulate PCI device afterwards.
Hope this helps,
regards.

Related

How does Linux kernel discover PCI devices?

On the driver side, pci_register_driver() is called when a driver module is loaded, or at boot time if the module is built-in. (Whenever a device/driver is added, driver/device list is looped to find a match, I get that part.)
But where/when are pci devices discovered and registered with the bus? I imagine this is arch specific, and would involve BIOS on x86, such as - BIOS routine probe PCI devices and then put the results in a list some where in RAM, before loading the kernel, and each list entry contains information of a single pci device including vendorId/deviceId etc. Kernel then pick up the list and insert them into pci_bus_type.p.klist_devices at some point. But this is pure guess, can anyone give some hints?
Actually, BIOS need not be involved.
PCI standardizes a certain procedure for discovery of devices on the bus. This procedure can be triggered at any time (not exclusively on boot) by hotplug controller or even manually, via /sys/bus/pci/rescan (see pci_rescan_bus).
The scan will proceed recursively, traversing the bridges as discovered and reading the configuration space data off each device encountered (see PCI configuration space).
For each device found, if not yet active, the kernel will look for an instance of pci_driver object with a matching pci_device_id. Then it will call the probe method of that object (the rest is driver implementation specific).
If appropriate pci_driver instance is not found, kernel will emit an event to an user space daemon (udev or hotpug or whatever), which may load an appropriate module and create the necessary pci_driver object.

pci_find_capability return 0: "device does not support it"

I call pci_find_capability with PCI_CAP_ID_EXP and return code is zero, which means:
Tell if a device supports a given PCI capability. Returns the address of the requested capability structure within the device’s PCI configuration space or 0 in case the device does not support it
Why the device doesn't support?
int pcie_cap_addr = pci_find_capability(pdev, PCI_CAP_ID_EXP);
PCI_CAP_ID_EXP is defined as 0x10.
PCI_CAP_ID_EXP is defined as 0x10, which is the capability ID of the PCI Express Capability. The PCI Express Specification requires all PCIe devices to implement this capability. Thus any device that doesn't implement it isn't a PCIe device.
If you're using a system old enough to have built-in PCI slots, then it could be a PCI card in a PCI slot.
Otherwise, it could be a PCI device behind a PCIe-PCI bridge, or it could be a root complex integrated device (RCIEP) that doesn't use any new PCIe features so it identifies itself as a PCI device rather than a PCIe device.

Clarification regarding PCI device initialization

Wikipedia says:
To address a PCI device, it must be enabled by being mapped into the system's I/O port address space or memory-mapped address space. The system's firmware, device drivers or the operating system program the Base Address Registers (commonly called BARs) to inform the device of its address mapping by writing configuration commands to the PCI controller.
Does this mean that a PCI device gets initialized when an address is written to the BAR? I'm trying to initialize the Bochs VGA card on Qemu Aarch64 thru bare metal and that's why I'm asking. Thanks!
Writing to the BAR simply tells the device what address range it should respond to. (It doesn't even enable the device to respond to the address; for that you need to set MSE [memory space enable].) There are many steps typically needed to initialize a device. Some of the steps are common for different PCI devices and others are completely device specific.

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 USB device driver misconception

My question is going to be rather vague but I will try to explain as detailed as I can what I am trying to resolve.
Trying to learn Linux kernel USB stack I have started to think of making a simple USB driver for my Atmel evaluation board based on ARM M0+ MCU to run away from Windows tools (Visual Studio plugin).
I have spent few days learning kernel's USB API and come to conclusion of how to make this. My driver aims to make my board connected to PC through USB cable act like a simple USB flash drive. Making that I then can easily program it with a new version of firmware written by me.
I have found that I need to find out specific interface (I am talking about interface in terms of USB specification, not interface we used to use as a code abstraction) that holds an endpoint (pipe) responsible for interaction with flash memory. And then I can map it to character device and interact with it using standard I/O operations that are described in struct file_operations structure.
Simply using cat on /proc/* file descriptor that was created by USB Core subsystem I have investigated that interface responsible for interaction with flash memory holds bulk endpoint (likewise, this terms come from USB specification, CMIIAW) that act as a "descriptor". Linux kernel USB Core subsystem gives neat interfaces to talk to different kind of endpoints whether it control, interrupt, bulk or asynchronous endpoint.
Now I have come closer to my very question.
Also the main transfer unit in communication between two USB devices is abstraction called urb - you allocate it, you fill it, you send it to USB Core subsystem, you read it if it was IN type of urb and, finally, you free it. What is confusing for me and tightly related to my question is the next API include/linux/usb.h:
static inline void usb_fill_bulk_urb(struct urb *urb,
struct usb_device *dev,
unsigned int pipe,
void *transfer_buffer,
int buffer_length,
usb_complete_t complete_fn,
void *context)
Assume I have obtained an information from board's datasheet about where to write a program code. Let's say, we have 0x00100 - 0x10000 memory region. I will compile my code, obtain a binary and then using standard Linux tools or writing a simple user-space wrapper application I will use lseek to set file's offset to 0x00100 and write system call provided with a buffer (binary compiled previously) and it's length.
In kernel space, I will have to allocate urb in write system call handler, fill it with a buffer sent from user space and submit this urb to USB Core.
BUT I can not find a way how to specify an OFFSET set earlier by lseek. Do I miss something? Maybe I missed some concepts or, perhaps, I am watching in a wrong way?
When your embedded Linux device acts as a USB mass storage device, the flash as a peripheral on Linux device is unmounted, and the gadget driver is loaded. Linux then loses control to the flash, and now the PC connected to your Linux device fully controls the flash. This is because a flash as a USB device can only has one USb host.
The gadget driver works purely in kernel space. It does not receive or transmit data from/to user space. It calls vfs_read() and vfs_write() to access the files on the flash, with an field offset. The offset is got from the USB commands sent from your host - Windows PC.
There is no way to specify offset using USB subsystem's API. I misunderstood whole conception of USB as communication protocol, unwise me. You must first learn underlying protocol your device uses to communicate with others.
If your device acts as a USB HID device then learning specification of how to exchange data with USB HID device is the way to go. If there is something proprietary then you can do nothing but reverse engineer it (listening USB packets with a sniffer on system where a driver for your device exists).
As for my board it has embedded debugger that serves as a communication module besides being debugger itself. Specifically, my device is equipped with EDBG and here is a link on description of protocol it uses for communication.

Resources