-
Notifications
You must be signed in to change notification settings - Fork 1
Home
Welcome to the LLKDD wiki!
For further information on Linux device drivers/Kernel programming, Hardware, C, git see also the References page.
As the Linux Kernel evolves at a fast pace, some improvements are introduced making previous code obsolete/deprecated. It means, new code should use the new interfaces/methods/APIs. So far these one are listed here, as well as other topics that may be not clear at a first glance.
First of first of all, please use the Linux Kernel version 3.7.x
or newer. The project code is developed based on the newest possible stable version. If an older version is used, the code may not compile. E.g.: the driver kbdlogger
uses the function input_get_new_minor()
, which was included in the version 3.7.0
.
They were replaced by the new mutex interface, in order to implement concurrency management, when a process/thread can be put to sleep. See: [Documentation/mutex-design.txt](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/Documentation/mutex design.txt?id=455c6fdbd219161bd09b1165f11699d6d73de11c)
This character device (registration) interface was deprecated in favor of cdev interface. This interface is used to register a new char device into the Kernel.
Many devices are accessed through the file system, specially char (character) devices. They can be found under the /dev directory. The kernel needs to know how to access the device. Not only does the kernel need to be told what kind of device is being accessed but also any special information, such as the partition number if it is a hard disk or if it is mouse for example. This is accomplished by the major number and minor number of that device.
The major number is the association between the device driver and the device. All devices controlled by the same device driver have a common major number. The minor number is used to distinguish the different devices on the same controller or class/type and exposes to the kernel some device characteristics/resources, like partition in a hard disk. The example bellow (ls -l /dev/sd*
) lists the first hard disk partitions:
brw-rw---- 1 root disk 8, 0 Jun 23 08:30 /dev/sda
brw-rw---- 1 root disk 8, 1 Jun 23 08:30 /dev/sda1
brw-rw---- 1 root disk 8, 2 Jun 23 08:30 /dev/sda2
The major number remains the same number, whereas the minor changes. The 0
is refers the and 1, 2
refer the first and second partition respectively.
There is a lot of debate and regarding the use of goto
statement, if it is a good programming practice or not.
In fact, the goto
is nothing more than a jmp
(jump not equal), jne
(jump not equal), je
(jump equal) instruction in assembly/machine language. Every high-level programming language is translated (interpreted or compiled) to machine/assembly language in order to be executed by the target processor. Therefore it uses the instructions described bellow.
C code:
#include<stdio.h>
int main(void)
{
for (int i = 0; i < 100; i++) {
printf("loop iteration number %d\n", i);
}
return 0;
}
ARMv7 Assembly code:
main:
push {r4, lr}
movs r4, #0
.L2:
movs r0, #1
mov r2, r4
ldr r1, .L5
adds r4, r4, r0
bl __printf_chk
cmp r4, #100
bne .L2
movs r0, #0
pop {r4, pc}
.L5:
.word .LC0
x86_64 code:
.LC0:
.string "loop iteration number %d\n"
main:
pushq %rbx
xorl %ebx, %ebx
.L2:
movl %ebx, %esi
xorl %eax, %eax
movl $.LC0, %edi
addl $1, %ebx
call printf
cmpl $100, %ebx
jne .L2
xorl %eax, %eax
popq %rbx
ret
One can see the in the assembly code the use of conditional jumping instructions. On the other hand, nobody is advocating an indiscriminate use of goto
, but in certain well-constrained situations its use is well suited, and even elegant.
For example, in Kernel-space every CPU cycle and cache hit matters (every user wants a fast and responsive system, doesn't she/he ?). The use of goto
provides a fast further code execution, whenever a condition inside a loop or conditional statement is matched. The code bellow illustrates a practical example in the Linux kernel real time scheduler in SMP systems (see the code here):
#ifdef CONFIG_SMP
/*
* We ran out of runtime, see if we can borrow some from our neighbours.
*/
static int do_balance_runtime(struct rt_rq *rt_rq)
{
struct rt_bandwidth *rt_b = sched_rt_bandwidth(rt_rq);
struct root_domain *rd = cpu_rq(smp_processor_id())->rd;
int i, weight, more = 0;
u64 rt_period;
weight = cpumask_weight(rd->span);
raw_spin_lock(&rt_b->rt_runtime_lock);
rt_period = ktime_to_ns(rt_b->rt_period);
for_each_cpu(i, rd->span) {
struct rt_rq *iter = sched_rt_period_rt_rq(rt_b, i);
s64 diff;
if (iter == rt_rq)
continue;
raw_spin_lock(&iter->rt_runtime_lock);
/*
* Either all rqs have inf runtime and there's nothing to steal
* or __disable_runtime() below sets a specific rq to inf to
* indicate its been disabled and disalow stealing.
*/
if (iter->rt_runtime == RUNTIME_INF)
goto next;
/*
* From runqueues with spare time, take 1/n part of their
* spare time, but no more than our period.
*/
diff = iter->rt_runtime - iter->rt_time;
if (diff > 0) {
diff = div_u64((u64)diff, weight);
if (rt_rq->rt_runtime + diff > rt_period)
diff = rt_period - rt_rq->rt_runtime;
iter->rt_runtime -= diff;
rt_rq->rt_runtime += diff;
more = 1;
if (rt_rq->rt_runtime == rt_period) {
raw_spin_unlock(&iter->rt_runtime_lock);
break;
}
}
next:
raw_spin_unlock(&iter->rt_runtime_lock);
}
raw_spin_unlock(&rt_b->rt_runtime_lock);
return more;
}
This function is used to find spare CPU time in other CPUs in a SMP system and borrow it to a CPU that needs it. If it doesn't, the current CPU is skipped and the spinlock is unlocked and it can iterate loop quickly.
See also:coding style, chapter 7
See also:Donald Knuth (1974), "Structured Programming with go to Statements"
The Linux Kernel developers make an extensive use of the preprocessor. Here are listed some relevant macros normally found on device drivers:
__init
: this definition tells the kernel that the given function is used only at initialization time.
__initdata
: does the same as __init
, but regarding the data used in the initialization time.
__exit
: does the same as __init
, but regarding the termination time, when the driver is unloaded from kernel memory.
__user
: tells the kernel this pointer is used on user space and cannot be trusted. One practical is, this pointer cannot lie in the moment it is accessed in main memory, but in swap partition on disk, and generate a page fault, followed by the process to be put to sleep. Pointers that communicated with user space must always be dealt with caution.
When returning an error, it is a good practice to return the appropriate error code to the caller function. Use the already defined error codes on include/uapi/asm-generic/errno-base.h and
include/uapi/asm-generic/errno.h. In the Linux kernel, the error code is always a negative value, e.g. -ENOMEM
,-EIO
, -ENODEV
.
Already answered by Linus Torvalds and other Kernel Hackers here.
Right bellow one can find documentation about the device drivers written in this repository.
1.1 helloworld
1.2 one
1.3 intn
1.4 intn2
1.5 Keyboard logger
TODO: Write this section.
Common problems that may appear during development.