📜 ⬆️ ⬇️

The process of porting Linux device drivers

Hello, hablochiteli!

Introduction


Sometimes it happens that there is a need to upgrade to a newer version of the Linux kernel and, accordingly, to migrate existing device drivers.


')
The transfer process can take from several minutes to a longer period of time. It depends not only on the complexity of the driver, but also on which version of the kernel you are going to upgrade to (API has a tendency to change - hence all the problems climb), and also on the quality of the code implementation, it is easier to rewrite than to migrate but we will not talk about it.

Unfortunately, I can not attach the source code of the driver, but we will consider all the problems that I and you may encounter during the migration process. Next, we will consider an example of porting a simple driver with kernel version 2.6.25 to 4.12.5, which is located in drivers / serial / name_uart.c. Also the next resource 2.6.25 and 4.12.5 will help us a lot, where you can see the structure of the kernel, as well as the source codes.

Formulation of the problem


The formulation of the problem is extremely primitive and simple - it is required to transfer the above mentioned driver from kernel version 2.6.25 to 4.12.5.

Implementation


First of all, if you are not familiar with the driver that you have to transfer, then I would recommend at least superficially examine it. This can greatly simplify the task. After that, you can proceed to the transfer.

Step one

Our driver has the following path in the 2.6.25 kernel: drivers / serial / name_uart.c
Now we need to find a suitable directory where you can put it. And here we meet the first problem - there is no such directory drivers / serial / in the kernel 4.12.5.

It is solved very simply: we take and put our driver in drivers / tty / serial / name_uart.c

Step two

After this, in order for Linux to be able to include our driver in the assembly, we need to add it to two files.

First file: drivers / tty / serial / Makefile
We add the following line to it:

obj-$(CONFIG_SERIAL_NAME) += name_uart.o 

Second file: drivers / tty / serial / Kconfig
In it we write the following:

 config SERIAL_NAME tristate "SERIAL_NAME UART driver" help Write description here 

Step Three

Once we have completed the first two steps, you can proceed to the assembly.

From the root directory, run make menuconfig and include our driver in the build.
When the command is executed, the graphical interface opens and there is no problem to find our driver in it (you can do the search in the following way: click / and write the full or part of the name, then enter).

There is another way - you can simply edit the .config file and include your driver in the build.

Then you can try to build the kernel with our included driver.

Step Four

After we tried to build the kernel, most likely we had errors that need to be resolved in order to perform a successful kernel build afterwards and be sure to check our driver for operability.

The error I encountered was an outdated interface for working with the proc system, as well as a remote macro.

For example, the call to irequest_irq in kernel 2.6.25 is successful, BUT

 irequest_irq(64 + ISC_DMA, dma_interrupt_handler, IRQF_DISABLED, "DMA", NULL); 

in kernel 4.12.5, the IRQF_DISABLED macro was removed, and therefore the driver was not going to.
Solution - take and substitute 0 instead of IRQF_DISABLED.

 irequest_irq(64 + ISC_DMA, dma_interrupt_handler, 0, "DMA", NULL); 

The next error was that in the kernel version 2.6.25, interaction with the proc system occurred using create_proc_entry, the implementation of which you will no longer find in 4.12.5.

Therefore, it was necessary to rewrite the implementation a bit, and the result was the following:

 /****************************************************************************** * /proc interface ******************************************************************************/ static int xr16_get_status(struct seq_file *m, void *v) { struct uart_name_uart *pp; unsigned long f; u64 tmp; int i, xmax = ARRAY_SIZE(pp->in_irq); seq_printf(m, "UART NAME ports stat:\n"); for (i = 0; i < UART_NAME_MAXPORTS; i++) { pp = &uart_name_16_ports[i]; if (!pp->used) continue; local_irq_save(f); tmp = ktime_to_us(ktime_sub(ktime_get(), pp->ktm_last)); do_div(tmp, USEC_PER_SEC); /* *   . */ local_irq_restore(f); } return 0; } static int xr16_proc_open(struct inode *inode, struct file *file) { return single_open(file, uart_name_get_status, NULL); } static const struct file_operations uart_name_proc_fops = { .owner = THIS_MODULE, .open = uart_name_proc_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, } 

If we summarize all the above, the driver transfer is divided into the following steps:

1. copy the source code of the previous version of the driver to the new version of the Linux kernel;
2. add the description in Kconfig and in the Makefile;
3. pick up our driver with the help of the busybox into the kernel assembly (or by directly editing the .config file);
4. try to eliminate all errors during the build, until the kernel is assembled.

So, we looked at the basics of how to port the device driver to a newer version of the Linux kernel, as well as problems that you may encounter.

In the next article we will write our first full-fledged own driver according to a given technical task for one of the new versions of the Linux kernel, and also we will test it not only using standard Linux utilities, but we will also write our own test for it.

Please, if you find inaccuracies, or you have something to add - write in the LAN or in the comments.

Thank!

Source: https://habr.com/ru/post/350828/


All Articles