Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Linux Kernel Programming Part 2 - Char Device Drivers and Kernel Synchronization

You're reading from   Linux Kernel Programming Part 2 - Char Device Drivers and Kernel Synchronization Create user-kernel interfaces, work with peripheral I/O, and handle hardware interrupts

Arrow left icon
Product type Paperback
Published in Mar 2021
Publisher Packt
ISBN-13 9781801079518
Length 452 pages
Edition 1st Edition
Tools
Arrow right icon
Author (1):
Arrow left icon
Kaiwan N. Billimoria Kaiwan N. Billimoria
Author Profile Icon Kaiwan N. Billimoria
Kaiwan N. Billimoria
Arrow right icon
View More author details
Toc

Table of Contents (11) Chapters Close

Preface 1. Section 1: Character Device Driver Basics
2. Writing a Simple misc Character Device Driver FREE CHAPTER 3. User-Kernel Communication Pathways 4. Working with Hardware I/O Memory 5. Handling Hardware Interrupts 6. Working with Kernel Timers, Threads, and Workqueues 7. Section 2: Delving Deeper
8. Kernel Synchronization - Part 1 9. Kernel Synchronization - Part 2 10. Other Books You May Enjoy

Writing the misc driver code – part 2

Armed with this knowledge, look again at the init code of ch1/miscdrv/miscdrv.c. You will see that, just as described in the previous section, we have initialized the fops member of the miscdev struct to a file_operations structure, thus setting up the functionality of the driver. The relevant code snippet (from our driver) is as follows:

static const struct file_operations llkd_misc_fops = {
.open = open_miscdrv,
.read = read_miscdrv,
.write = write_miscdrv,
.release = close_miscdrv,
};

static struct miscdevice llkd_miscdev = {
[ ... ]
.fops = &llkd_misc_fops, /* connect to this driver's 'functionality' */
};

So, now you can see it: when a user space process (or thread) that has opened our device file invokes, say, a read(2) system call, the kernel VFS layer will follow the pointers (generically, filp->f_op->foo()) and invoke the function, read_miscdrv(), in effect handing over control to the device driver! How exactly the read method is written is covered in the next section.

Continuing with the init code of our simple misc driver:

    [ ... ] 
/* Retrieve the device pointer for this device */
dev = llkd_miscdev.this_device;
pr_info("LLKD misc driver (major # 10) registered, minor# = %d,"
" dev node is /dev/%s\n", llkd_miscdev.minor, llkd_miscdev.name);
dev_info(dev, "sample dev_info(): minor# = %d\n", llkd_miscdev.minor);
return 0; /* success */
}

Our driver retrieves a pointer to the device structure – it's something required by every driver. Within the misc kernel framework, it's available within the this_device member of our miscdevice structure.

Next, pr_info() shows the minor number dynamically obtained. The dev_info() helper routine is more interesting: as a driver author, you are expected to use these dev_xxx() helpers when emitting printk; it will also prefix useful information about the device. The only difference in syntax between the dev_xxx() and pr_xxx() helpers is that the first parameter to the former is the pointer to the device structure.

Okay, let's get our hands dirty! We build the driver and insmod it into kernel space (we use our lkm helper script to do so):

Figure 1.4 – Screenshot of building and loading our miscdrv.ko skeleton misc driver on an x86_64 Ubuntu VM

(By the way, as you can see in Figure 1.4, I tried out this misc driver on a more recent distro: Ubuntu 20.04.1 LTS running the 5.4.0-58-generic kernel.) Notice the two prints toward the bottom of Figure 1.4; the first is emitted via the pr_info() (prefixed with the pr_fmt() macro content, as explained in the companion guide Linux Kernel Programming - Chapter 4, Writing Your First Kernel Module - LKMs Part 1 section Standardizing printk output via the pr_fmt macro). The second print is emitted via the dev_info() helper routine – it's prefixed with the words misc llkd_miscdrv, indicating that it originated from the kernel's misc framework, and specifically from the llkd_miscdrv device! (The dev_xxx() routines are versatile; depending on the bus they're on, they will display various details. This is useful for debugging and logging purposes. We repeat: you're recommended to use the dev_*() routines when writing drivers.) You can also see that the /dev/llkd_miscdrv device node is indeed created, with the expected type (character) and major and minor pair (10 and 56 here).

You have been reading a chapter from
Linux Kernel Programming Part 2 - Char Device Drivers and Kernel Synchronization
Published in: Mar 2021
Publisher: Packt
ISBN-13: 9781801079518
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image