request_irq fails because there is no irq descriptor - linux-device-driver

I have a Linux system which I'm connecting to custom hardware. (To be specific, this is an SoC with an FPGA).
I am trying to write a kernel module that will respond to interrupts from my device. It contains a call to request_irq, but this is done via a call to uio_register_device. When I run insmod, it fails with "Invalid parameters".
After poking around the code for a while, I eventually added these lines in my module init function:
struct irq_desc *p;
p = irq_to_desc(89);
if (!p) {
printk(KERN_ALERT "No descriptor allocated for IRQ number 89!!\n");
}
p = irq_to_desc(121);
if (!p) {
printk(KERN_ALERT "No descriptor allocated for IRQ number 121!!\n");
}
Sure enough, I see these messages in dmesg.
What am I supposed to do in this case?

I managed to figure it out. For interrupts to work properly, you need to map a virtual IRQ number (i.e. the number you would use in request_interrupt) to a hardware IRQ number. Of course, hardware IRQ numbers are not unique; for example, two interrupt controllers could have a hardware IRQ number of 0. So what I really mean is that you have to associate the virtual IRQ number with the combination of an irq_domain and a hardware IRQ number.
There's a little more to it than that: irq_domains do not correspond 1-to-1 with interrupt controllers. A single controller could have more than one domain.
So, let's precisely define what we mean by "mapping" IRQs:
Each virtual IRQ has a struct irq_desc. Some of this struct's fields are mapped to point to the functions implemented by the interrupt controller driver. (I'm not sure what Linux does when it receives an interrupt before you do any mapping)
Each interrupt controller can provide some number of struct irq_domains. These essentially encode the mapping between hardware IRQ numbers to virtual IRQ numbers, and have an API for drivers to look up numbers.
The irq_domain struct also contains pointers to callbacks which are implemented by the interrupt controller driver. One of these callbacks is triggered when you create a new mapping; I think this is how the hardware is configured to generate particular virtual IRQs.
When you request an interrupt, your ISR will be added to the actions list in the irq_desc, provided the irq_desc has been properly initialized.
Anyway the method I found for getting the virtual IRQ number given a hardware IRQ number (and creating a new mapping if necessary) is as follows:
#include <linux/irqdomain.h> //For irq_domain struct
#include <linux/of.h> //For device tree struct types
#include <linux/irq.h> //Needed by other interrupt-related code
struct device_node *dn;
struct irq_domain *dom;
struct irq_fwspec dummy_fwspec = {
.param_count = 3,
.param = {0, 89, 4}
};
int virq;
//Find the Linux irq number
dn = of_find_node_by_name(NULL, "interrupt-controller");
if (!dn) {
printk(KERN_ERR "Could not find device node for \"interrupt-controller\"\n");
goto /*error_handler*/;
}
dom = irq_find_host(dn);
if (!dom) {
printk(KERN_ERR "Could not find irq domain\n");
goto /*error_handler*/;
}
dummy_fwspec.fwnode = dom->fwnode;
virq = irq_create_fwspec_mapping(&dummy_fwspec);
The irq_create_fwspec_mapping will either return an existing (mapped) virtual IRQ number, or map a new one. The struct irq_fwspec contains all the information that would be needed to perform the mapping. The fwnode field must point to the interrupt controller you are using; in my case, I found the correct value using the device tree API (there might be a better way, but I don't know what it is!)
This example is specifically for an MPSoC, and I happen to know it uses this driver (drivers/irqchip/irq-gic.c). I looked at the driver's source code to figure out what it wanted in the other parts of the irq_fwspec struct. Specifically, each controller driver will fill a struct irq_domain_ops, so CTRL-F for that to find the callback functions you need to satisfy.
In my specific example, I knew the gic_irq_domain_alloc would be called, and that it would also call gic_irq_domain_translate. This told me that the 2nd number was the hardware IRQ number, but that for some reason 32 would be added to it. Since I wanted hardware IRQ number 121, I plugged in 89 for the 2nd number. The first and third numbers are kind of a guess based on what I've seen other people doing.
If someone knows a simpler way to do this, let me know

Related

share information between function(BPF/XDP)

Objective: If process id/name = xxx then drop the packet
So, I am bit confused. So far I know you can't extract process information from XDP but bpf trace allows you to trace it.
Here's my probable solution, use bpf hash maps to share information between two function. If process name == xx then XDP_DROP. (This maybe wrong, but something I was trying)
But I am confused how to use BPF_HASHMAPS, I read the documentation on bcc yet..
Example: From this hello function I can trace events
struct data_t {
u32 pid;
u64 ts;
char comm[TASK_COMM_LEN];
};
BPF_PERF_OUTPUT(events);
int hello(struct pt_regs *ctx) {
struct data_t data = {};
data.pid = bpf_get_current_pid_tgid();
data.ts = bpf_ktime_get_ns();
bpf_get_current_comm(&data.comm, sizeof(data.comm));
events.perf_submit(ctx, &data, sizeof(data));
return 0;
}
XDP function to drop packer
int udpfilter(struct xdp_md *ctx) {
bpf_trace_printk("got a packet\n");
//u32 cpu = bpf_get_smp_processor_id();
//bpf_trace_printk("%s looking\n",cpu);
//u32 pid = bpf_get_current_pid_tgid();
return XDP_DROP;
}
Now how do I fetch pid value and use it in XDP function, plus does the solution even makes any sense. Thanks for the help, really appreciated.
So, as you know eBPF programs can be loaded into the kernel at different locations. XDP programs are loaded just after the network driver and just before the network stack. At this point the kernel doesn't know for which process a packet might be since it will figure all of that out in the network stack.
The hello program you are showing is an example of a kprobe(kernel probe). It attaches to whatever kernel function you specify, but it is a tracing tool, can't make changes.
Also, some helper functions like bpf_get_current_pid_tgid are program type dependent. bpf_get_current_pid_tgid only works in kprobes, uprobes, tracepoint programs (perf programs), the may actually also work in socket and cGroup programs, the issue is that there is not a very clear list or overview of which work where, these are two good but non-comprehensive links:
https://blogs.oracle.com/linux/post/bpf-in-depth-bpf-helper-functions
https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md#program-types
In the end it comes down to logic. The kernel can only give you access to data and actions it has access to itself. So if you want to do network related things based on process ID's you might need to use an eBPF program attached at a location where such info is available(keep in mind that this is obviously also slower).
So depending on what exactly you want to do you have a few options:
Attach an eBPF program to a network socket(BPF_PROG_TYPE_SOCKET_FILTER) so you can filter packets on the socket level. This does require the program that creates the socket to attach the program to it.
Use a cGroup and BPF_PROG_TYPE_CGROUP_SKB program to block packets. Since you attach the program to the cGroup, this doesn't require cooperation from the program.
Use an TC program(BPF_PROG_TYPE_SCHED_ACT), on this level a packet is already parsed, but you still need to match it to a process
Use an XDP program(BPF_PROG_TYPE_XDP) can still be used, this does require you to parse all layers of the network packet(Ethernet, VLAN, IP, UDP/TCP), and then manually extract the protocol, Destination IP, and Destination port. Just like in the TC program you then need to match it to an pid using a lookup table.
When going the XDP or TC route you need to create this lookup table. As far as I know you can't access the table of the kernel via helper functions. A few approaches are:
parsing the output of netstat -lpn(protocol, destination ip, destination port and PID) and setting the data in a map to be used by a program
Getting the same data but directly from /sys or /proc(I don't know where the data is stored exactly)
Recording which PIDs have which sockets during creation(using a second program(kprobe/tracepoint)) and setting this data in a map shared by both the XDP/TC program and the trace program. (not quite sure how to share maps between programs in BCC, but it is certainly possible when using libbpf)

How can I determine interrupt source on an stm32?

I recently ended up in the Default_Handler in my stm32 project and couldn't figure out what was casing it:
.section .text.Default_Handler,"ax",%progbits
Default_Handler:
Infinite_Loop:
b Infinite_Loop <--- here!
By default, a lot of interrupts are mapped to the default handler and the only way I could figure out what the actual interrupt reason was, would be to write handlers for all the interrupts (60+) and pause the code in the debugger. Bah!
I didn't find a good answer googling, so I thought I document the solution for others (or most likely myself in 6 months...)
So, it turns out there are some registers in the NVIC (interrupt controller) that we could use:
The above is from the STM32CubeIDE debugger. NVIC_IABRX contains a bitmask of the currently active interrupts and I can see that NVIC_IABR1 has a non-zero bit (it's 0x1000).
Each IABR reg is 32 bits wide, so with some simple bit counting I conclude that the interrupt source is 32+12 = 44. Now I need to look at the datasheet for my mcu (an stm32wb55) so see what that corresponds to:
Aha, so it's the IPCC that's causing the interrupt! To double check, I added a handler for this specific interrupt
void IPCC_C1_RX_IRQHandler(void)
{
}
And it got called!
Note: I initially just had a look at interrupt vector in the startup_stm32xxx.s file and counted from the start of that but that didn't work out since the first few interrupts are not included in the NVIC_IABRX registers.

stm32 NVIC_EnableIRQ() bare metal equivalent?

I'm using the blue pill, and trying to figure out interrupts. I have an interrupt handler:
void __attribute__ ((interrupt ("TIM4_IRQHandler"))) myhandler()
{
puts("hi");
TIM4->EGR |= TIM_EGR_UG; // send an update even to reset timer and apply settings
TIM4->SR &= ~0x01; // clear UIF
TIM4->DIER |= 0x01; // UIE
}
I set up the timer:
RCC_APB1ENR |= RCC_APB1ENR_TIM4EN;
TIM4->PSC=7999;
TIM4->ARR=1000;
TIM4->EGR |= TIM_EGR_UG; // send an update even to reset timer and apply settings
TIM4->EGR |= (TIM_EGR_TG | TIM_EGR_UG);
TIM4->DIER |= 0x01; // UIE enable interrupt
TIM4->CR1 |= TIM_CR1_CEN;
My timer doesn't seem to activate. I don't think I've actually enabled it though. Have I??
I see in lots of example code commands like:
NVIC_EnableIRQ(USART1_IRQn);
What is actually going in NVIC_EnableIRQ()?
I've googled around, but I can't find actual bare-metal code that's doing something similar to mine.
I seem to be missing a crucial step.
Update 2020-09-23 Thanks to the respondents to this question. The trick is to set the bit for the interrupt number in an NVIC_ISER register. As I pointed out below, this doesn't seem to be mentioned in the STM32F101xx reference manual, so I probably would never have been able to figure this out on my own; not that I have any real skill in reading datasheets.
Anyway, oh joy, I managed to get interrupts working! You can see the code here: https://github.com/blippy/rpi/tree/master/stm32/bare/04-timer-interrupt
Even if you go bare metal, you might still want to use the CMSIS header files that provide declarations and inline version of very basic ARM Cortex elements such NVIC_EnableIRQ.
You can find NVIC_EnableIRQ at https://github.com/ARM-software/CMSIS_5/blob/develop/CMSIS/Core/Include/core_cm3.h#L1508
It's defined as:
#define NVIC_EnableIRQ __NVIC_EnableIRQ
__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn)
{
if ((int32_t)(IRQn) >= 0)
{
__COMPILER_BARRIER();
NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL));
__COMPILER_BARRIER();
}
}
If you want to, you can ignore __COMPILER_BARRIER(). Previous versions didn't use it.
The definition is applicable to Cortex M-3 chips. It's different for other Cortex versions.
With the libraries is still considered bare metal. Without operating system, but anyway, good that you have a desire to learn at this level. Someone has to write the libraries for others.
I was going to do a full example here, (it really takes very little code to do this), but will take from my code for this board that uses timer1.
You obviously need the ARM documentation (technical reference manual for the cortex-m3 and the architectural reference manual for armv7-m) and the data sheet and reference manual for this st part (no need for programmers manual from either company).
You have provided next to no information related to making the part work. You should never dive right into a interrupt, they are advanced topics and you should poll your way as far as possible before finally enabling the interrupt into the core.
I prefer to get a uart working then use that to watch the timer registers when the roll over, count, etc. Then see/confirm the status register fired, learn/confirm how to clear it (sometimes it is just a clear on read).
Then enable it into the NVIC and by polling see the NVIC sees it, and that you can clear it.
You didn't show your vector table this is key to getting your interrupt handler working. Much less the core booting.
08000000 <_start>:
8000000: 20005000
8000004: 080000b9
8000008: 080000bf
800000c: 080000bf
...
80000a0: 080000bf
80000a4: 080000d1
80000a8: 080000bf
...
080000b8 <reset>:
80000b8: f000 f818 bl 80000ec <notmain>
80000bc: e7ff b.n 80000be <hang>
...
080000be <hang>:
80000be: e7fe b.n 80000be <hang>
...
080000d0 <tim1_handler>:
The first word loads the stack pointer, the rest are vectors, the address to the handler orred with one (I'll let you look that up).
In this case the st reference manual shows that interrupt 25 is TIM1_UP at address 0x000000A4. Which mirrors to 0x080000A4, and that is where the handler is in my binary, if yours is not then two things, one you can use VTOR to find an aligned space, sometimes sram or some other flash space that you build for this and point there, but your vector table handler must have the proper pointer or your interrupt handler won't run.
volatile unsigned int counter;
void tim1_handler ( void )
{
counter++;
PUT32(TIM1_SR,0);
}
volatile isn't necessarily the right way to share a variable between interrupt handler and foreground task, it happens to work for me with this compiler/code, you can do the research and even better, examine the compiler output (disassemble the binary) to confirm this isn't a problem.
ra=GET32(RCC_APB2ENR);
ra|=1<<11; //TIM1
PUT32(RCC_APB2ENR,ra);
...
counter=0;
PUT32(TIM1_CR1,0x00001);
PUT32(TIM1_DIER,0x00001);
PUT32(NVIC_ISER0,0x02000000);
for(rc=0;rc<10;)
{
if(counter>=1221)
{
counter=0;
toggle_led();
rc++;
}
}
PUT32(TIM1_CR1,0x00000);
PUT32(TIM1_DIER,0x00000);
A minimal init and runtime for tim1.
Notice that the NVIC_ISER0 is bit 25 that is set to enable interrupt 25 through.
Well before trying this code, I polled the timer status register to see how it works, compare with docs, clear the interrupt per the docs. Then with that knowledge confirmed with the NVIC_ICPR0,1,2 registers that it was interrupt 25. As well as there being no other gates between the peripheral and the NVIC as some chips from some vendors may have.
Then released it through to the core with NVIC_ISER0.
If you don't take these baby steps and perhaps you have already, it only makes the task much worse and take longer (yes, sometimes you get lucky).
TIM4 looks to be interrupt 30, offset/address 0x000000B8, in the vector table. NVIC_ISER0 (0xE000E100) covers the first 32 interrupts so 30 would be in that register. If you disassemble the code you are generating with the library then we can see what is going on, and or look it up in the library source code (as someone already did for you).
And then of course your timer 4 code needs to properly init the timer and cause the interrupt to fire, which I didn't check.
There are examples, you need to just keep looking.
The minimum is
vector in the table
set the bit in the interrupt set enable register
enable the interrupt to leave the peripheral
fire the interrupt
Not necessarily in that order.

Is there a HAL library ISR function that automatically triggers when a byte is received into the Rx buffer of SPIx on STM32L4xx?

I am wondering if there is a user-definable, built-in ISR function in the HAL library that triggers as soon as a byte is received in the SPIx Rx buffer on STM32L4xx MCU? For instance, as a startup test, I would like to send one byte (0xBC) from a Master STM32L452 nucleo board via SPI2 to a Slave STM32L452 nucleo board. Once the Slave board receives the byte, it flashes LED2, and transmits a different byte (0xCD) back to the Master. Once the Master receives the byte, it flashes LED2 as confirmation. I have initialized both boards as Master/Slave, enabled DMA and global interrupts, 8 bits per transfer using MXcube. I can achieve what I want using the HAL_SPI_Transmit_DMA() and HAL_SPI_Receive_DMA() functions and delays written into the while(1) portion of my main routine (as below). However, I would like to achieve the same using an ISR function that automatically executes once a byte is received into the SPI Rx Buffer.
Master Code:
uint8_t spiDataReceive = 0;
uint8_t spiDataTransmit = 0xBC;
while(1) {
if(!HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13)) {
//Transmit byte 0xBC to Slave and Receive Response
HAL_SPI_Transmit_DMA(&hspi2, &spiDataTransmit, 1);
HAL_Delay(20);
HAL_SPI_Receive_DMA(&hspi2, &spiDataReceive, 1);
if(spiDataReceive == 0xCD) {
flashLED2();
spiDataReceive = 0x00;
}
}
}
Slave Code:
uint8_t spiDataReceive = 0;
uint8_t spiDataTransmit = 0xCD;
while(1) {
HAL_SPI_Receive_DMA(&hspi2, &spiDataReceive, 1);
HAL_Delay(10);
if(spiDataReceive == 0xBC) {
HAL_SPI_Transmit_DMA(&hspi2, &spiDataTransmit, 1);
flashLED2();
spiDataReceive = 0x00;
}
}
No library is needed. You need to set RNEIE bit in the SPI CR register and enable in the NVIC the interrupt. 2 lines of code. No libraries needed.
The only needed resource is the Reference Manual from the STM website.
Yes, the HAL provides user callbacks. In order to use those, you have to activate the corresponding interrupt in NVIC and have the HAL handler called by the interrupt vector table (please have a look at stm32l4xx_it.c, too).
But before you do so, you should consider the following questions:
If you feel confused or frustrated by the complexity of ST HAL libraries, read the Reference Manual and follow the advice of P__J__ (see other answer).
If you feel confused or frustrated by the complexity of the hardware interface, follow the present answer.
Both HAL_SPI_Transmit_DMA() and HAL_SPI_Transmit_IT() support a variable number of transfer bytes.
If all you are going to need is that one-byte transfer, HAL functions may be an overkill.
Their advantage is that you can run some C library functions without dealing with HW register access in C (if that is quite new to you, coming from the arduino ecosystem). And of course, to transfer more than a single byte through the same interface when you extend your application.
You should decide whether you want to get an interrupt from the DMA you have tied to the UART, or if you want to avoid the DMA and get the interrupt from the UART itself. From my point of view, you should really not trigger an ISR by the same interrupt event which is used to start a DMA transfer to fetch the data!
In the same way as you find a description of the HW registers in the
Reference Manual and
Data Sheet of the controller, you find documentation on the HAL (layering concept, usage requirements etc.) in the
User manual of STM32L4/L4+ HAL and low-layer drivers
(see sections 70 and 102, resp., and chapter 3).
Of course, this interface aims mostly for abstraction and portability whereas directly addressing the HW interface usually allows much better efficiency in terms of latency/CPU load, and ROM/RAM usage. The "Low-Level" library drivers aim for a certain compromise, but if you are new to this whole topic and unsure what to start with, you should either start from the HW register interface, or from the portable HAL library API.
If the specification documents (HW or Lib description) are too abstract for you and you prefer some hands-on information source, you may want to first have a look at STM32Cube firmware examples for STM32CubeL4.
These also include SPI data exchange use cases (SPI_FullDuplex_ComIT for example) that are available for NUCLEO-L4532RE (and others) and described in application note AN4726 (page 16).
In addition to the interrupt selection/handling, you should check two more aspects of your program:
If you get an interrupt from the hardware, there is no reason for the HAL_Delay() calls.
Keep in mind that on SPI, you can only "return" data from slave to master while the master is transferring data (which may be zero data).
Otherwise, the "transmit" call on the slave side will only put data into the TX register, and the SPI peripheral will wait infinitely for the SCK trigger from the master...

Char device driver using interrupt - linux

I have a question about a char driver.
A char driver using GPIO pins to communicate with a hardware device, including interrupt interfacing.
The driver's "release ()" method is missing.
What order should function elements put in?
A. Delete cdev and unregister device
B. Free GPIO Resources
C. freeing IRQ resource
D. Unregistrer major / minor number
In which order in the "release()" method?
Thanks
As per my understanding correct order looks like C, B, A and D :-). Explanation: Need to free the IRQ since gpio pin (used as an interrupt pin), IRQ number is got from passing this gpio pin to gpio_to_irq and after this only you can go ahead in freeing up the gpio stuff. After that deletion of cdev come into picture to which file operations, device node info(dev_t, 32bit unsigned integer. In which 12 bit is used for major no and remaining 20 bit is used for minor no) and minor number info (minor no start value and how many minor no's asked for) are associated. At-last go ahead and unregister the driver.
Actually, some of these things may be done in the release() function, and some of these things must be done in the module_exit() function. It all depends on what you do where.
First, some terminology: module_init() is called when the module is loaded with insmod. The opposite function is module_exit() which is called when the module is unloaded with rmmod. open() is called when a user process tries to open a device file with the open() system call, and the opposite function release() is called when the process that opened the device file (as well as all processes that were branched from that original process) call the close() system call on the file descriptor.
The module_exit() function is the opposite of the module_init() function. Assuming you are using the CDev API, in the module init function you must register a major / minor numbers (D) first with alloc_chrdev_region() or register_chrdev_region() before adding the cdev to the system with cdev_init() and then cdev_add().
It stands to reason that when module_exit() is called, you should undo what you did in the reverse order; i.e. remove the cdev first with cdev_del() and then unregister the major/minor number with unregister_chrdev_region().
At some point in the module_init() function you may request the GPIO resources with request_mem_region() & ioremap(), and then the IRQ resource with request_irq(). On the other hand you may request the GPIO resources and the IRQ resource in the open() function instead.
If you request these resources in the module_init() function, then you should release these resources in the module_exit() function. However, if you do it in open() then you should keep track of how many processes have the device file open, and when all of them have released the device file, release the resources in the release() function.
Again, whatever order you requested the resources in, in general you should release the resources in the opposite order. I will say however, that almost always it is incorrect to release the memory resources (in your case the GPIO resources) before releasing the IRQ resource, since the IRQ will most likely want to talk to the hardware, either in the top half or the bottom half handler.
In summary, the order depends on how you implemented the driver to request the resources in the first place, however, if you implement your drivers like I do, then in general, perform C then B in release(), and perform A then D in module_exit().