How to write to `/dev/ttyUSB?` in kernel space? - linux-kernel

i want to send and receive data from/to an arduino connected to a /dev/ttyUSB port (serial) from a kernel module. i have seen enough ways how to do it in userspace, but that's not my question.
it would really help to be able to access a tty by major and minor numbers (188, 0 for /dev/ttyUSB0) as i dont want to use file io in a kernel module
related but didnt answer my question:
How to write to a tty from kernel space with only major and minor device numbers available? (1 answer but uses userspace)
Access /dev/ttyACM0 from kernel (no answers yet)
How can I write to TTY from a kernel module? (1 answer but how to get the struct tty_struct from a serial port?)
Read and write to Arduino serial using a kernel module (1 answer but uses file io and alternative is too vague)
https://unix.stackexchange.com/questions/585573/how-does-serial-driver-get-attached-to-a-tty-port (no answers yet)

SappyInsane on linuxquestions had the same problem and gave me his solution which worked
https://www.linuxquestions.org/questions/linux-kernel-70/reading-from-arduino-serial-in-a-kernel-module-4175704822/

Related

What are allowed and not allowed to do in a linux Device Driver?

I have a general question about linux device driver. More often I get confused which actions are allowed or not allowed to perform in a linux device driver?
Is there any rules or kind of lookup list to follow?
for instance with the following examples, which are not allowable?
msleep(1000);
al = kmallock(sizeof(val));
printk(KERN_ALERT "faild to print\n";
ret = adc_get_val()*0.001;
In linux device driver programming it depends in which context you are. There are two contexts that need to be distinguished:
process context
IRQ context.
Sleeping can only be done while in process context or you schedule the work for later execution (there are several mechanism available to do that). This is a complex topic that cannot be described in a paragraph.
Allocating memory can sleep, it depends with which parameters/flags kmalloc is invoked.
print can always be called (once the kernel has been invoked), otherwise use early_printk.
I don't know what the function add_get_val does. It is not part of the linux kernel. And as has already been commented, float values cannot be easily used in the kernel.

Where to find device-tree?

Coming form this question yesterday, I decided to port this library to my board. I was aware that I needed to change something, so I compiled the library, call it on a small program and see what happens. The 1st problem is here:
// Check for GPIO and peripheral addresses from device tree.
// Adapted from code in the RPi.GPIO library at:
// http://sourceforge.net/p/raspberry-gpio-python/
FILE *fp = fopen("/proc/device-tree/soc/ranges", "rb");
if (fp == NULL) {
return MMIO_ERROR_OFFSET;
}
This lib is aimed for Rpi, os the structure of the system on my board is not the same. So I was wondering if somebody could tell me where I could find this file or how it looks like so I can find it by my self in order to proceed the job.
Thanks.
You don't necessarily want that "file" (or more precisely /proc node).
The code this is found in is setting up to do direct memory mapped I/O using what appears to be a pi-specific gpio-flavored version of the /dev/mem type of device driver for exposing hardware special function registers to userspace.
To port this to your board, you would need to first determine if there is a /dev/mem or similar capability in your kernel which you can activate. Then you would need to determine the appropriate I/O registers for GPIO pins. The pi-specific code is reading the Device Tree to figure this out, but there are other ways, for example you can manually read the programmer's manual of the SoC on which you are running.
Another approach you can consider is adding some small microcontroller (or yes, barebones ***duino) to the system, and using that to collect information from various sensors and peripherals. This can then be forwarded to the SoC over a UART link, or queried out via I2C or similar - add a small amount of cost and some degree of bottleneck, but also means that the software on the SoC then becomes very portable - to a different comparable chip, or perhaps even to run on a desktop PC during development.

Device Drivers to Read and Write on a Virtual memory on linux

I am working with a SoC Cyclone V board. I want to exchange data between the HPS and FPGA. They share a common RAM, whose address can be seen on Qsys. I would like to Read and write data in this shared Memory, but dont want to use devmem2 every time i do it. I understand that a driver would be much safer. I was thinking of writing a char driver as it is one of the easy drivers to write for the basic read and write operations.
Is there a way to specify the address to be used by the char driver when we build and insert it?
If not, what driver can be written for the this function (to be able to read and write float values on a specific range of virtual address)?
I have found that user io device drivers or block drivers could be good options. But I am new to this area of development and don't know if these are the only options, or are they any more.
I could really use some help in deciding which driver is appropriate, it would be better if it was a char driver where the address can be specified.
Thank you.

From Kernel Space to User Space: Inner-workings of Interrupts

I have been trying to understand how do h/w interrupts end up in some user space code, through the kernel.
My research led me to understand that:
1- An external device needs attention from CPU
2- It signals the CPU by raising an interrupt (h/w trance to cpu or bus)
3- The CPU asserts, saves current context, looks up address of ISR in the
interrupt descriptor table (vector)
4- CPU switches to kernel (privileged) mode and executes the ISR.
Question #1: How did the kernel store ISR address in interrupt vector table? It might probably be done by sending the CPU some piece of assembly described in the CPUs user manual? The more detail on this subject the better please.
In user space how can a programmer write a piece of code that listens to a h/w device notifications?
This is what I understand so far.
5- The kernel driver for that specific device has now the message from the device and is now executing the ISR.
Question #3:If the programmer in user space wanted to poll the device, I would assume this would be done through a system call (or at least this is what I understood so far). How is this done? How can a driver tell the kernel to be called upon a specific systemcall so that it can execute the request from the user? And then what happens, how does the driver gives back the requested data to user space?
I might be completely off track here, any guidance would be appreciated.
I am not looking for specific details answers, I am only trying to understand the general picture.
Question #1: How did the kernel store ISR address in interrupt vector table?
Driver calls request_irq kernel function (defined in include/linux/interrupt.h and in kernel/irq/manage.c), and Linux kernel will register it in right way according to current CPU/arch rules.
It might probably be done by sending the CPU some piece of assembly described in the CPUs user manual?
In x86 Linux kernel stores ISR in Interrupt Descriptor Table (IDT), it format is described by vendor (Intel - volume 3) and also in many resources like http://en.wikipedia.org/wiki/Interrupt_descriptor_table and http://wiki.osdev.org/IDT and http://phrack.org/issues/59/4.html and http://en.wikibooks.org/wiki/X86_Assembly/Advanced_Interrupts.
Pointer to IDT table is registered in special CPU register (IDTR) with special assembler commands: LIDT and SIDT.
If the programmer in user space wanted to poll the device, I would assume this would be done through a system call (or at least this is what I understood so far). How is this done? How can a driver tell the kernel to be called upon a specific systemcall so that it can execute the request from the user? And then what happens, how does the driver gives back the requested data to user space?
Driver usually registers some device special file in /dev; pointers to several driver functions are registered for this file as "File Operations". User-space program opens this file (syscall open), and kernels calls device's special code for open; then program calls poll or read syscall on this fd, kernel will call *poll or *read of driver's file operations (http://www.makelinux.net/ldd3/chp-3-sect-7.shtml). Driver may put caller to sleep (wait_event*) and irq handler will wake it up (wake_up* - http://www.makelinux.net/ldd3/chp-6-sect-2 ).
You can read more about linux driver creation in book LINUX DEVICE DRIVERS (2005) by Jonathan Corbet, Alessandro Rubini, and Greg Kroah-Hartman: https://lwn.net/Kernel/LDD3/
Chapter 3: Char Drivers https://lwn.net/images/pdf/LDD3/ch03.pdf
Chapter 10: Interrupt Handling https://lwn.net/images/pdf/LDD3/ch10.pdf

Kernel does NOT recognize NAND bad blocks marked by u-boot

While in u-boot of my ARM based board (DM368) I mark some kernel partition block manually as bad. U-boot says that it was marked and, for example, while writing/reading kernel image I see it skipping this bad block.
But when I try to write the same partition from within Linux (loaded via NFS) I see that Linux nandwrite command USES this bad block! I checked this in several ways - Linux ignores bad block mark for 100%. But everywhere in the internet it is said that BBT is one for both u-boot and Linux.
So, where is the catch?
OK, the answer is found.
For some unclear reason Texas Instruments, manufacturer of the board DM365EVM which I use for development, provides the kernel with different BBT structure. They defined BBT offset as 2, while all the world, including the provided u-boot, defines this offset as 8.
I wish them a good health for many years.

Resources