Restoring Keyboard IRQ - linux

I'm very new to the Linux Kernel Module development and trying to write a simple kernel module which can later be extended as the keyboard driver.
I tried following two approaches:
Interrupt Based Approach
I started writing the code after following the guide given here. But the only problem is that the machine freezes when I run rmmod because it is not able to restore the IRQ to the original keyboard driver.
Is there any way to save the device name & device id of the original keyboard driver before requesting the IRQ in init() and then restore everything back to normal once the exit() i.e. cleanup_module() is fired?
void cleanup_module() {
/* Something to restore everything back to normal */
free_irq(1, NULL);
}
Polling Approach
In this approach, I am continuously polling for the Key Pressed & Released by using a while loop and then copying the input back to the user.
while(!(inb(0x64) & 0x1) || (input = inb(0x60)) & 0x80);
The problem I'm facing here is that it never comes out of the while loop. I'm assuming that is because the original keyboard driver serves the request.
Is there any way to get the request forwarded from the original keyboard driver?
I appreciate any help/pointers on this.
Thanks!

I'm afraid that I do not see how this can work as long as the normal kernel keyboard driver is also controlling the keyboard, since both drivers will be attempting to control the device. The kernel i8042 driver (I assume that is the relevant one for you) registers its interrupt as shared, and if your driver managed to register for the same interrupt then it will also have registered its handler as shared, so that both got notified on interrupts and would race to access the device.
If you registered a shared handler, this might also explain the crash when you unload it: unregistering a shared interrupt handler only works when the second parameter contains a valid dev_id; so unregistering would fail when called with NULL as you did, but the handler code would still be unloaded from memory. This would leading to a crash on a future interrupt.
Regarding your polling approach, yes, since the normal driver is notified on interrupts it is rather likely to beat you to reading the keyboard.

Related

Linux keyboard delay

So I want to build a kernel module (I suppose) which would insert little delay after a keyboard key is pressed (let's say 500ms). I managed to do this in Windows through hooks, but it seems to be different in Linux. Note that I do not wish to use x11 methods, as I want it to also work from the linux console (even if there is no X server running). From what I was able to understand, it would require to build a kernel module and insert it dynamically into the kernel with insmod. I managed to build a key logger which would dump each pressed key to the kernel log, but inserting a delay would require sending the thread that handles the keyboard interrupt handler to sleep, which is a very bad idea, and also to rewrite the entire USB_KBD driver, because the current script calls the request_irq function with the IRQF_SHARED flag set, so I guess the original driver still does its job before executing my function.
I am currently requesting an interrupt handler like this
request_irq (1, (irq_handler_t) irq_handler, IRQF_SHARED, "keyboard_stats_irq", (void *)(irq_handler));
Any suggestions on how to handle this (any other way) ?
You can write a user space daemon that reads the input events from /dev/input/input* (whatever the keyboard device is), while grabbing the device to block the events to go through the rest of the system (ioctl(fd, EVIOCGRAB, 1)).
Then, the daemon can create a virtual input device, using /dev/uinput and write the input events there after some delay. Since the delay will be implemented in user-space, that will be quite easy.
Starting your daemon will be equivalent to hot-plugging a virtual keyboard, and modern X servers (less than 10 years?) are able to cope with hot-plugged input devices. And the vconsole driver works fine too with those.

Handle corrupted SPI data using STM32F1 MCU

I'm developing an application for a custom board with a STM32F1 MCU which needs to be able to recover from a unexpected data corruption.
The data flow is as follows:
The master device (a Linux machine) sends a request to the slave which parses the message and gets ready to send a reply. Then the master reads the reply. The exchange is fast (#18MHz) and implemented like this:
if (::ioctl(_fd, SPI_IOC_MESSAGE(2), &transaction) < 0) {
warn("message not sent");
return false;
}
The delay between these two messages is ~50us. The message length is fixed.
On the STM side I use a DMA-driven SPI driver that is implemented in the way I'm going to write below.
I'm using the SPI2 which is clocked off APB1#36MHz (HSE#24 MHz; AHB#72MHz; APB1#36MHz).
After the SPI is configured to read the message (fixed length!) by issuing a DMA request on RXNEIE (CR2->RXDMAEN). After the message is processed the answer is getting transmitted via DMA1 (CR2->TXDMAEN).
Everything works like a charm until I interfere somehow. The scenario I'm trying to recover from is unplugging SCLK line while transferring.
I'm struggling to recover from this. I'm going to lay out my thoughts because I'm not sure where the bug is.
The DMA is configured to handle fixed length messages. That's why when I interfere somehow, the DMA controller waits until the whole message is processed and the buffer gets shifted. Suppose, I got a one third of the message when the SCLK suddenly vanished. DMA will be waiting for the rest two thirds. The master continues to send requests. Hence after SCLK is back, 2/3 of the next message will be placed in the buffer. The DMA interrupt is issued but the remaining trail of the last message is lost. It's lost for sure, but I can detect that using an ERRIE flag to issue an interrupt on OVR flag that is going to be set.
I've tried to handle that interrupt but to no avail.
The interrupt handler I have now checks if BSY flag is set (the trail is getting process by SPI controller). If it's set I kill DMA (that already starts to handle the next message) and leave OVR flag. Once BSY is cleared I clear OVR and reset DMA for reception.
This doesn't help much.
Another option I might use is a dedicated timer that gets reset on rising edge on SCLK (AN3109 application note inspired solution). This way I could implement a DMA timeout. If I got only the part of a message I can generate an interrupt on timer overflow if SCLK is not with us for a long time. This solution has issues, though.
I know the description is vague but I've tried my best and hope somebody with a greater insight might help.
Install an interrupt handler on the CS line. On rising edge, abort everything and start over DMA if the transfer is not yet complete. Use the SSI bit in SPI_CR1, set on rising edge, clear on falling edge.

Linux Kernel - stoping a running kernel timer from user space

I am trying to blink an LED in my embedded device while Linux is booting. Basically the LED blink shows that Linux is in the process of booting.
To blinks the LED, I am doing the following things
Created a global timer (LED blink timer) in init/main.c
static struct timer_list pwr_led_timer;
Started the timer as soon as init_timers() function is finished in start_kernel()
setup_timer and mod_timer functions used.
When timer fires, in the timer handler, LED is toggled. And timer restarted.
When kernel finishes the whole boot process, I am switching off the LED and deleting the timer.
del_timer_sync(&pwr_led_timer);
Question:
I would like to stop the timer and switch of the LED from user space from my application instead of stopping at the kernel (that is point number 4). Is there a standard way to stop the kernel running timer from user space ?
since pwr_led_timer is global struct, can any IOCTL call be used to stop the timer from user space ? My Idea is do some IOCTL and get access to kernel. Since the pwr_led_timer is global, in IOCTL I can call the del_timer_sync() with pwr_led_timer. But not sure which device I should open to IOCTL (?)
Sorry, I am new to kernel/driver programming. I tried to search the net but could not get any clue.
Kindly let me know if anyone has any inputs. Thanks in advance.
Regards,
Emerson
Have you considered trying netlink? It's a powerful method for user-to-kernel (or process-to-process) communication which both simple and lightweight.

Linux Device Driver - How to unblock reading thread when closing file?

I am attempting to implement a character device driver for Linux and am having trouble. In short, data written to the device is buffered for reading. When no data is available, the call to read blocks via 'wait_event_interruptible'. Data received by the write handler calls 'wake_up_interruptible'. The release handler also calls 'wake_up_interruptible' to unblock the reader but sets a flag to indicate the driver is releasing.
From user space I have an executable that opens the driver via 'open' and then starts another thread. The main thread proceeds to call 'read'. As intended, no data is available for reading and the call blocks. The other thread sleeps for one second (providing sufficient time for the main thread to read and block), calls 'close' and then calls 'close' again. The first call returns '0' while the second returns '-1' (as expected). However, my driver's release handler is never called and I cannot understand how to unblock my reading thread without explicitly sending it a signal or writing some data to the device. My understanding is that when the last handle to the driver closes that its release handler is invoked. I am trying to implement what I believe is standard user space behavior- a thread blocked reading from a file will become unblocked and receive an end-of-file return value when asynchronously closed.
Do I have the correct understanding of read/close at the file level in user space? Do I have the correct device driver understanding? Am I missing something else? I looked through 'Linux Device Drivers 3rd Edition' and couldn't quite find an answer to this question. I have also searched Google but cannot seem to find the answer either. Any help you can provide is appreciated. My kernel version is 3.0.15.
Unfortunately the read syscall keeps a reference on the file itself and not the file descriptor. So closing the file descriptor will not abort the read.
In all cases you must be careful about races conditions between unblocking and closing, you don't want the thread (or another one) to re-enter the syscall between ;)

Is the driver using the Linux NAPI Interface?

Is there a way to confirm if a Linux Ethernet driver is using the NAPI interface?
I know this sounds like an obvious answer, but check the source to see if the NAPI API is being used.
For example:
In your driver's interrupt handler, does it use any of the calls below?
Does your driver implement a poll() method for the NAPI? If so, see if it uses
netif_receive_skb() instead of netif_rx().
If both of those questions lead to 'YES', the your driver is using NAPI.
Note: The following was copied from here
NAPI API
netif_rx_schedule(dev)
Called by an IRQ handler to schedule a poll for device
netif_rx_schedule_prep(dev)
puts the device in a state ready to be added to the CPU polling list if it is up and running. You can look at this as the first half of netif_rx_schedule(dev).
__netif_rx_schedule(dev)
Add device to the poll list for this CPU; assuming that netif_schedule_prep(dev) has already been called and returned 1
__netif_rx_schedule_prep(dev)
similar to netif_rx_schedule_prep(dev) but no check if device is up, usually not used
netif_rx_reschedule(dev, undo)
Called to reschedule polling for device specifically for some deficient hardware.
netif_rx_complete(dev)
Remove interface from the CPU poll list: it must be in the poll list on current cpu. This primitive is called by dev->poll(), when it completes its work. The device cannot be out of poll list at this call, if it is then clearly it is a BUG().
__netif_rx_complete(dev)
same as netif_rx_complete but called when local interrupts are already disabled.
Check out this wikipedia article and the external links in it for more detailed information.
I hope that helps.

Resources