├── LICENSE ├── README.md └── chapters ├── char-driver └── src │ ├── Makefile │ └── main.c ├── device-file └── src │ ├── Makefile │ └── main.c ├── first-driver ├── first-driver.md └── src │ ├── Makefile │ └── hello-world-module.c ├── introduction ├── imgs │ └── kernel-space-vs-user-space.png └── introduction.md ├── ioctl └── src │ ├── Makefile │ └── main.c ├── major-minor └── src │ ├── Makefile │ └── main.c └── module-parameter ├── module-parameter.md └── src ├── Makefile └── hello-world-module.c /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Renan Prata 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Linux Device Driver Tutorial # 2 | 3 | ## What is it? ## 4 | This tutorial discusses technical issues to develop your own linux device driver. The aim of this tutorial is to provide, easy and practical examples so that everybody can understand the concepts in a simple manner. The work is divided into chapters that starts in a simple way and gradually covers more issues related to the development of Linux kernel modules. 5 | 6 | ## Who is this tutorial for? ## 7 | People interested in understanding more about how the Linux kernel works and developing kernel modules. 8 | 9 | ## Requirements ## 10 | * A computer or Virtual Machine running a linux distro (we recommend [Ubuntu <3](https://www.ubuntu.com)); 11 | * [GCC](https://gcc.gnu.org); 12 | * A text editor. [vim](https://www.vim.org/), [emacs](https://www.gnu.org/s/emacs/), [sublime text](https://www.sublimetext.com); 13 | * Basic knowledge of linux (you need to know how to use linux command line); 14 | * Basic knowledge of C; 15 | 16 | ## Chapters ## 17 | 1. [Introduction](chapters/introduction/introduction.md) 18 | 2. [First Driver](chapters/first-driver/first-driver.md) 19 | 3. [Passing Arguments to Device Driver](chapters/module-parameter/module-parameter.md) 20 | 21 | ## License ## 22 | Linux Device Driver Tutorial may be modified and distributed under the terms of the MIT license. See the LICENSE file for details. 23 | 24 | -------------------------------------------------------------------------------- /chapters/char-driver/src/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += main.o 2 | 3 | KDIR = /lib/modules/$(shell uname -r)/build 4 | 5 | all: 6 | make -C $(KDIR) M=$(shell pwd) modules 7 | 8 | clean: 9 | make -C $(KDIR) M=$(shell pwd) clean -------------------------------------------------------------------------------- /chapters/char-driver/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define mem_size 1024 12 | 13 | dev_t dev = 0; 14 | static struct class *dev_class; 15 | static struct cdev main_cdev; 16 | 17 | uint8_t *kernel_buffer = NULL; 18 | 19 | static int __init main_init(void); 20 | static void __exit main_exit(void); 21 | static int main_open(struct inode *inode, struct file *file); 22 | static int main_release(struct inode *inode, struct file *file); 23 | static ssize_t main_read(struct file *filp, char __user *buf, size_t len,loff_t * off); 24 | static ssize_t main_write(struct file *filp, const char *buf, size_t len, loff_t * off); 25 | 26 | static struct file_operations fops = 27 | { 28 | .owner = THIS_MODULE, 29 | .read = main_read, 30 | .write = main_write, 31 | .open = main_open, 32 | .release = main_release, 33 | }; 34 | 35 | static int main_open(struct inode *inode, struct file *file) 36 | { 37 | printk(KERN_INFO "Driver Open Function Called...!!!\n"); 38 | 39 | if((kernel_buffer = kmalloc(mem_size , GFP_KERNEL)) == 0) 40 | { 41 | printk(KERN_INFO "Cannot allocate memory in kernel\n"); 42 | return -1; 43 | } 44 | return 0; 45 | } 46 | 47 | static int main_release(struct inode *inode, struct file *file) 48 | { 49 | printk(KERN_INFO "Driver Release Function Called...!!!\n"); 50 | if (kernel_buffer) 51 | { 52 | kfree(kernel_buffer); 53 | } 54 | return 0; 55 | } 56 | 57 | static ssize_t main_read(struct file *filp, char __user *buf, size_t len, loff_t *off) 58 | { 59 | printk(KERN_INFO "Driver Read Function Called...!!!\n"); 60 | copy_to_user(buf, kernel_buffer, mem_size); 61 | return 0; 62 | } 63 | static ssize_t main_write(struct file *filp, const char __user *buf, size_t len, loff_t *off) 64 | { 65 | copy_from_user(kernel_buffer, buf, len); 66 | printk(KERN_INFO "Driver Write Function Called []...!!!\n"); 67 | return len; 68 | } 69 | 70 | static int __init main_init(void) 71 | { 72 | if (alloc_chrdev_region(&dev, 0, 1, "KernelTest_Dev") < 0) 73 | { 74 | printk(KERN_INFO "Cannot allocate major number for device 1\n"); 75 | } 76 | 77 | printk(KERN_INFO "Major = %d / Minor = %d", MAJOR(dev), MINOR(dev)); 78 | 79 | cdev_init(&main_cdev, &fops); 80 | 81 | if((cdev_add(&main_cdev, dev, 1)) < 0){ 82 | printk(KERN_INFO "Cannot add the device to the system\n"); 83 | goto r_class; 84 | } 85 | 86 | 87 | if ((dev_class = class_create(THIS_MODULE, "test_class")) == NULL) { 88 | printk(KERN_INFO "Cannot create struct class for device\n"); 89 | goto r_class; 90 | } 91 | 92 | if (device_create(dev_class, NULL, dev, NULL, "test_dev") == NULL) { 93 | printk(KERN_INFO "Cannot create device test_dev\n"); 94 | goto r_device; 95 | } 96 | printk(KERN_INFO "[main-module] Kernel Module Inserted Successfully...\n"); 97 | return 0; 98 | 99 | r_device: 100 | class_destroy(dev_class); 101 | r_class: 102 | unregister_chrdev_region(dev, 1); 103 | return -1; 104 | } 105 | 106 | void __exit main_exit(void) 107 | { 108 | device_destroy(dev_class, dev); 109 | class_destroy(dev_class); 110 | cdev_del(&main_cdev); 111 | unregister_chrdev_region(dev, 1); 112 | printk(KERN_INFO "[main-module] Kernel Module Removed Successfully...\n"); 113 | } 114 | 115 | module_init(main_init); 116 | module_exit(main_exit); 117 | 118 | MODULE_LICENSE("GPL"); 119 | MODULE_AUTHOR("Renan Prata "); 120 | MODULE_DESCRIPTION("A simple hello world driver"); 121 | MODULE_VERSION("1:2"); -------------------------------------------------------------------------------- /chapters/device-file/src/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += main.o 2 | 3 | KDIR = /lib/modules/$(shell uname -r)/build 4 | 5 | all: 6 | make -C $(KDIR) M=$(shell pwd) modules 7 | 8 | clean: 9 | make -C $(KDIR) M=$(shell pwd) clean -------------------------------------------------------------------------------- /chapters/device-file/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | dev_t dev = 0; 9 | 10 | static struct class *dev_class; 11 | 12 | static int __init main_init(void) 13 | { 14 | if (alloc_chrdev_region(&dev, 0, 1, "KernelTest_Dev") < 0) 15 | { 16 | printk(KERN_INFO "Cannot allocate major number for device 1\n"); 17 | } 18 | printk(KERN_INFO "Major = %d / Minor = %d", MAJOR(dev), MINOR(dev)); 19 | 20 | if ((dev_class = class_create(THIS_MODULE, "test_class")) == NULL) { 21 | printk(KERN_INFO "Cannot create struct class for device\n"); 22 | goto r_class; 23 | } 24 | 25 | if (device_create(dev_class, NULL, dev, NULL, "test_dev") == NULL) { 26 | printk(KERN_INFO "Cannot create device test_dev\n"); 27 | goto r_device; 28 | } 29 | printk(KERN_INFO "[main-module] Kernel Module Inserted Successfully...\n"); 30 | return 0; 31 | 32 | r_device: 33 | class_destroy(dev_class); 34 | r_class: 35 | unregister_chrdev_region(dev, 1); 36 | return -1; 37 | } 38 | 39 | void __exit main_exit(void) 40 | { 41 | device_destroy(dev_class, dev); 42 | class_destroy(dev_class); 43 | unregister_chrdev_region(dev, 1); 44 | printk(KERN_INFO "[main-module] Kernel Module Removed Successfully...\n"); 45 | } 46 | 47 | module_init(main_init); 48 | module_exit(main_exit); 49 | 50 | MODULE_LICENSE("GPL"); 51 | MODULE_AUTHOR("Renan Prata "); 52 | MODULE_DESCRIPTION("A simple hello world driver"); 53 | MODULE_VERSION("1:2"); -------------------------------------------------------------------------------- /chapters/first-driver/first-driver.md: -------------------------------------------------------------------------------- 1 | # First Device Driver # 2 | 3 | Now we are going to see First Device Driver development. Before writing driver, we should give the module information. So First we will see about those module information. 4 | 5 | ## Module Information ## 6 | 7 | * License 8 | * Author 9 | * Module Description 10 | * Module Version 11 | 12 | These all informations are present in the linux/module.h as a macros. 13 | 14 | ### License ### 15 | 16 | The following license idents are currently accepted as indicating free software modules: 17 | 18 | * [GPL](https://opensource.org/licenses/gpl-license) 19 | 20 | * [LGPL](https://opensource.org/licenses/lgpl-license) 21 | 22 | * [BSD-2-Clause](https://opensource.org/licenses/BSD-3-Clause) 23 | 24 | * [MIT](https://opensource.org/licenses/MIT) 25 | 26 | * [Apache-2.0](https://opensource.org/licenses/Apache-2.0) 27 | 28 | * [MPL-2.0](https://opensource.org/licenses/MPL-2.0) 29 | 30 | This exists for several reasons: 31 | 32 | 1. modinfo can show license info for users wanting to vet their setup is free 33 | 2. The community can ignore bug reports including proprietary modules 34 | 3. Vendors can do likewise based on their own policies 35 | 36 | We can give the License for our driver (module) like below. For this you need to include the Linux/module.h header file. 37 | 38 | ```C 39 | MODULE_LICENSE("MIT"); 40 | ``` 41 | 42 | ### Author ### 43 | Using this Macro we can mention that who is wrote this driver or module. So modinfo can show author name for users wanting to know. We can give the Author name for our driver (module) like below. For this you need to include the Linux/module.h header file. 44 | 45 | ```C 46 | MODULE_AUTHOR("Renan Prata"); 47 | ``` 48 | 49 | *Note: Use “Name *email*” or just “Name”, for multiple authors use multiple MODULE_AUTHOR() statements/lines.* 50 | 51 | ### Module Description ### 52 | Using this Macro we can give the description of the module or driver. So modinfo can show module description for users wanting to know. We can give the description for our driver (module) like below. For this you need to include the linux/module.h header file. 53 | 54 | ```C 55 | MODULE_DESCRIPTION("A sample driver"); 56 | ``` 57 | 58 | ### Module Version ### 59 | Using this Macro we can give the version of the module or driver. So modinfo can show module version for users wanting to know. 60 | 61 | * Version of form [**epoch>**]**version**[-**extra-version**]. 62 | 63 | * **epoch**: A (small) unsigned integer which allows you to start versions anew. If not mentioned, it’s zero. eg. "2:1.0" is after "1:2.0". 64 | 65 | * **version**: The **version** may contain only alphanumerics and the character '.'. Ordered by numeric sort for numeric parts, ascii sort for ascii parts (as per RPM or DEB algorithm). 66 | 67 | * **extraversion**: Like **version**, but inserted for local customizations, eg "rh3" or "rusty1". 68 | 69 | ```C 70 | MODULE_VERSION("1:1.0"); 71 | ``` 72 | 73 | ## Simple Kernel Programming ## 74 | So as of now we know the very basic things that needed for writing driver. Now we will move into programming. In every programming language, how we will start to write the code? Any ideas? Well, in all programming there would be a starting point and ending point. If you take C Language, starting point would be the main function, Isn’t it? It will start from the starting of the main function and run through the functions which is calling from main function. Finally it exits at the main function closing point. But Here two separate functions used for that starting and ending. 75 | 76 | 1. Init function 77 | 2. Exit function 78 | 79 | Kernel modules require a different set of header files than user programs require.And keep in mind, Module code should not invoke user space Libraries or API’s or System calls. 80 | 81 | ### Init function ### 82 | This is the function which will executes first when the driver is loaded into the kernel. For example when we load the driver using insmod, this function will execute. Please see below to know the syntax of this function. 83 | 84 | ```C 85 | static int __init hello_world_init(void) /* Constructor */ 86 | { 87 | return 0; 88 | } 89 | module_init(hello_world_init); 90 | ``` 91 | 92 | ### Exit function ### 93 | This is the function which will executes last when the driver is unloaded from the kernel. For example when we unload the driver using rmmod, this function will execute. Please see below to know the syntax of this function. 94 | 95 | ```C 96 | void __exit hello_world_exit(void) 97 | { 98 | 99 | } 100 | module_exit(hello_world_exit); 101 | ``` 102 | 103 | This function should register itself by using module_exit() macro. 104 | 105 | ### Printk() ### 106 | In C programming how we will print the values or whatever? Correct. Using printf() function. printf() is a user space function. So we cant use this here. So they created one another function for kernel which is printk(). 107 | 108 | One of the differences is that printk lets you classify messages according to their severity by associating different loglevels, or priorities, with the messages. You usually indicate the loglevel with a macro. I will explain about the macros now. There are several macros used for printk. 109 | 110 | * KERN_EMERG - Used for emergency messages, usually those that precede a crash. 111 | 112 | * KERN_ALERT - Situation requiring immediate action. 113 | 114 | * KERN_CRIT - Critical conditions, often related to serious hardware or software failures. 115 | 116 | * KERN_ERR - Used to report error conditions; device drivers often use KERN_ERR to report hardware difficulties. 117 | 118 | * KERN_WARNING - Warnings about problematic situations that do not, in themselves, create serious problems with the system. 119 | 120 | * KERN_NOTICE - Situations that are normal, but still worthy of note. A number of security-related conditions are reported at this level. 121 | 122 | * KERN_INFO - Informational messages. Many drivers print information about the hardware they find at startup time at this level. 123 | 124 | * KERN_DEBUG - Used for debugging messages. 125 | 126 | ```C 127 | printk(KERN_INFO "Linux Device Driver Hello World"); 128 | ``` 129 | 130 | ### Difference between printf and printk ### 131 | * printk() is a kernel level function, which has the ability to print out to different loglevels. We can see the prints using dmesg command. 132 | * printf() will always print to a file descriptor – STD_OUT. We can see the prints in STD_OUT console. 133 | 134 | ## Simple Driver ## 135 | 136 | This is the complete code for our simple device driver (hello-world-module.c). You can access this code in subdirectory 'src'. Once we have the C code, it is time to compile it and create the module file hello-world-module.ko. Creating a Makefile for your module is straightforward. With the C code (hello-world-module.c) and Makefile ready, all we need to do is invoke make to build our first driver (hello-world-module.ko). 137 | 138 | In Terminal you need to enter make like below video. 139 | 140 | [![asciicast](https://asciinema.org/a/T4FG5THwcMFRy7SGIeafbPACi.png)](https://asciinema.org/a/T4FG5THwcMFRy7SGIeafbPACi) 141 | 142 | ### Loading, Unloading and Listing the Device driver ### 143 | To load a Kernel Module, use the insmod command with root privileges. For example our module file name is hello_world_module.ko: 144 | 145 | ```bash 146 | $ sudo insmod hello_world_module.ko 147 | ``` 148 | 149 | lsmod used to see the modules were inserted. In below image, i’ve shown the prints in init function. Use dmesg to see the kernel prints: 150 | ```bash 151 | $ lsmod hello_world_module.ko 152 | ``` 153 | 154 | To unload a Kernel Module, use the rmmod command with root privileges: 155 | ```bash 156 | $ sudo rmmod hello_world_module 157 | ``` 158 | 159 | In order to get information about a Module (author, supported options), we may use the modinfo command: 160 | ```bash 161 | $ modinfo hello_world_module.ko 162 | ``` 163 | 164 | Now we know where to start to write the Linux device driver. 165 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /chapters/first-driver/src/Makefile: -------------------------------------------------------------------------------- 1 | obj-m+=hello-world-module.o 2 | KDIR=/lib/modules/$(shell uname -r)/build 3 | 4 | all: 5 | make -C $(KDIR) M=$(shell pwd) modules 6 | clean: 7 | make -C $(KDIR) M=$(shell pwd) clean -------------------------------------------------------------------------------- /chapters/first-driver/src/hello-world-module.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static int __init hello_world_init(void) 6 | { 7 | printk(KERN_INFO "[hello-world-module] Kernel Module Inserted Successfully...\n"); 8 | return 0; 9 | } 10 | 11 | void __exit hello_world_exit(void) 12 | { 13 | printk(KERN_INFO "[hello-world-module] Kernel Module Removed Successfully...\n"); 14 | } 15 | 16 | module_init(hello_world_init); 17 | module_exit(hello_world_exit); 18 | 19 | MODULE_LICENSE("MIT"); 20 | MODULE_AUTHOR("Renan Prata "); 21 | MODULE_DESCRIPTION("A simple hello world driver"); 22 | MODULE_VERSION("1:1.0"); -------------------------------------------------------------------------------- /chapters/introduction/imgs/kernel-space-vs-user-space.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rprata/linux-device-driver-tutorial/13bd6276139f142b950086cc7a135a0fb7cc2eab/chapters/introduction/imgs/kernel-space-vs-user-space.png -------------------------------------------------------------------------------- /chapters/introduction/introduction.md: -------------------------------------------------------------------------------- 1 | # Introduction # 2 | 3 | Linux is a free open source operating system (OS) based on UNIX that was created in 1991 by Linus Torvalds. Users can modify and create variations of the source code, known as distributions, for computers and other devices. 4 | 5 | ## Linux Architecture ## 6 | 7 | Linux is primarily divided into User Space & Kernel Space. These two components interact through a System Call Interface – which are predefined and matured interface to Linux Kernel for User space applications. The below image will give you the basic understanding. 8 | 9 | ![Kernel Space vs User Space](./imgs/kernel-space-vs-user-space.png) 10 | 11 | ### Kernel Space ### 12 | Kernel space is where the kernel (i.e., the core of the operating system) executes (i.e., runs) and provides its services. 13 | 14 | ### User Space ### 15 | User Space is where the user applications are executed. 16 | 17 | ## Linux Kernel Modules ## 18 | 19 | * Kernel modules are pieces of code that can be loaded and unloaded into the kernel upon demand. They extend the functionality of the kernel without the need to reboot the system. 20 | * Custom codes can be added to Linux kernels via two methods. 21 | 22 | The basic way is to add the code to the kernel source tree and recompile the kernel. 23 | 24 | A more efficient way is to do this is by adding code to the kernel while it is running. This process is called loading the module, where module refers to the code that we want to add to the kernel. 25 | 26 | Since we are loading these codes at runtime and they are not part of the official Linux kernel, these are called loadable kernel module(LKM), which is different from the “base kernel”. Base kernel is located in /boot directory and is always loaded when we boot our machine whereas LKMs are loaded after the base kernel is already loaded. Nonetheless, these LKM are very much part of our kernel and they communicate with base kernel to complete their functions. 27 | 28 | LKMs can perform a variety of task, but basically they come under three main categories, 29 | 30 | * Device drivers 31 | 32 | * Filesystem drivers 33 | 34 | * System calls 35 | 36 | ## Device Drivers ## 37 | 38 | A device driver is designed for a specific piece of hardware. The kernel uses it to communicate with that piece of hardware without having to know any details of how the hardware works. 39 | 40 | ### Filesystem drivers ### 41 | A filesystem driver interprets the contents of a filesystem (which is typically the contents of a disk drive) as files and directories and such. There are lots of different ways of storing files and directories and such on disk drives, on network servers, and in other ways. For each way, you need a filesystem driver. For example, there’s a filesystem driver for the ext2 filesystem type used almost universally on Linux disk drives. There is one for the MS-DOS filesystem too, and one for NFS. 42 | 43 | ### System cals ### 44 | User space programs use system calls to get services from the kernel. For example, there are system calls to read a file, to create a new process, and to shut down the system. Most system calls are integral to the system and very standard, so are always built into the base kernel (no LKM option). 45 | 46 | But you can invent a system call of your own and install it as an LKM. Or you can decide you don’t like the way Linux does something and override an existing system call with an LKM of your own. 47 | 48 | ## Advantages of LKM 49 | One major advantage they have is that we don’t need to keep rebuilding the kernel every time we add a new device or if we upgrade an old device. This saves time and also helps in keeping our base kernel error free. 50 | 51 | LKMs are very flexible, in the sense that they can be loaded and unloaded with a single line of command. This helps in saving memory as we load the LKM only when we need them. 52 | 53 | ## Differences Between Kernel Modules and User Programs ## 54 | 55 | **Kernel modules have separate address space**. A module runs in kernel space. An application runs in user space. System software is protected from user programs. Kernel space and user space have their own memory address spaces. 56 | 57 | **Kernel modules have higher execution privilege**. Code that runs in kernel space has greater privilege than code that runs in user space. 58 | 59 | **Kernel modules do not execute sequentially**. A user program typically executes sequentially and performs a single task from beginning to end. A kernel module does not execute sequentially. A kernel module registers itself in order to serve future requests. 60 | 61 | **Kernel modules use different header files**. Kernel modules require a different set of header files than user programs require. 62 | 63 | ## Difference Between Kernel Drivers and Kernel Modules ## 64 | 65 | A kernel module is a bit of compiled code that can be inserted into the kernel at run-time, such as withinsmod or modprobe. 66 | 67 | A driver is a bit of code that runs in the kernel to talk to some hardware device. It “drives” the hardware. Most every bit of hardware in your computer has an associated driver. 68 | 69 | ## Types ## 70 | 71 | In the traditional classification, there are three kinds of device: 72 | 73 | * Character device 74 | * Block device 75 | * Network device 76 | 77 | In Linux everything is a file. I mean Linux treat everything as a File even hardware. 78 | 79 | 80 | ### Character Device ### 81 | A char file is a hardware file which reads/write data in character by character fashion. Some classic examples are keyboard, mouse, serial printer. If a user use a char file for writing data no other user can use same char file to write data which blocks access to other user. Character files uses synchronize Technic to write data. Of you observe char files are used for communication purpose and they can not be mounted. 82 | 83 | ### Block Device ### 84 | A block file is a hardware file which read/write data in blocks instead of character by character. This type of files are very much useful when we want to write/read data in bulk fashion. All our disks such are HDD, USB and CDROMs are block devices. This is the reason when we are formatting we consider block size. The write of data is done in asynchronous fashion and it is CPU intensive activity. These devices files are used to store data on real hardware and can be mounted so that we can access the data we written. 85 | 86 | ### Network Device ### 87 | A network device is, so far as Linux’s network subsystem is concerned, an entity that sends and receives packets of data. This is normally a physical device such as an ethernet card. Some network devices though are software only such as the loopback device which is used for sending data to yourself. 88 | 89 | **This is all about the basics of Linux and device driver. Now we will create our first driver.** 90 | 91 | 92 | -------------------------------------------------------------------------------- /chapters/ioctl/src/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += main.o 2 | 3 | KDIR = /lib/modules/$(shell uname -r)/build 4 | 5 | all: 6 | make -C $(KDIR) M=$(shell pwd) modules 7 | 8 | clean: 9 | make -C $(KDIR) M=$(shell pwd) clean -------------------------------------------------------------------------------- /chapters/ioctl/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define WR_VALUE _IOW('a','a',int32_t*) 13 | #define RD_VALUE _IOR('a','b',int32_t*) 14 | 15 | int32_t value = 0; 16 | 17 | dev_t dev = 0; 18 | static struct class *dev_class; 19 | static struct cdev main_cdev; 20 | 21 | uint8_t *kernel_buffer = NULL; 22 | 23 | static int __init main_init(void); 24 | static void __exit main_exit(void); 25 | static int main_open(struct inode *inode, struct file *file); 26 | static int main_release(struct inode *inode, struct file *file); 27 | static ssize_t main_read(struct file *filp, char __user *buf, size_t len,loff_t * off); 28 | static ssize_t main_write(struct file *filp, const char *buf, size_t len, loff_t * off); 29 | static long main_ioctl(struct file *file, unsigned int cmd, unsigned long arg); 30 | 31 | static struct file_operations fops = 32 | { 33 | .owner = THIS_MODULE, 34 | .read = main_read, 35 | .write = main_write, 36 | .open = main_open, 37 | .unlocked_ioctl = main_ioctl, 38 | .release = main_release, 39 | }; 40 | 41 | static int main_open(struct inode *inode, struct file *file) 42 | { 43 | printk(KERN_INFO "Driver Open Function Called...!!!\n"); 44 | 45 | return 0; 46 | } 47 | 48 | static int main_release(struct inode *inode, struct file *file) 49 | { 50 | printk(KERN_INFO "Driver Release Function Called...!!!\n"); 51 | return 0; 52 | } 53 | 54 | static long main_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 55 | { 56 | switch(cmd) { 57 | case WR_VALUE: 58 | copy_from_user(&value ,(int32_t*) arg, sizeof(value)); 59 | printk(KERN_INFO "Value = %d\n", value); 60 | break; 61 | case RD_VALUE: 62 | copy_to_user((int32_t*) arg, &value, sizeof(value)); 63 | break; 64 | } 65 | return 0; 66 | } 67 | 68 | static ssize_t main_read(struct file *filp, char __user *buf, size_t len, loff_t *off) 69 | { 70 | printk(KERN_INFO "Driver Read Function Called...!!!\n"); 71 | return 0; 72 | } 73 | 74 | static ssize_t main_write(struct file *filp, const char __user *buf, size_t len, loff_t *off) 75 | { 76 | printk(KERN_INFO "Driver Write Function Called []...!!!\n"); 77 | return len; 78 | } 79 | 80 | static int __init main_init(void) 81 | { 82 | if (alloc_chrdev_region(&dev, 0, 1, "KernelTest_Dev") < 0) 83 | { 84 | printk(KERN_INFO "Cannot allocate major number for device 1\n"); 85 | } 86 | 87 | printk(KERN_INFO "Major = %d / Minor = %d", MAJOR(dev), MINOR(dev)); 88 | 89 | cdev_init(&main_cdev, &fops); 90 | 91 | if((cdev_add(&main_cdev, dev, 1)) < 0){ 92 | printk(KERN_INFO "Cannot add the device to the system\n"); 93 | goto r_class; 94 | } 95 | 96 | 97 | if ((dev_class = class_create(THIS_MODULE, "test_class")) == NULL) { 98 | printk(KERN_INFO "Cannot create struct class for device\n"); 99 | goto r_class; 100 | } 101 | 102 | if (device_create(dev_class, NULL, dev, NULL, "test_dev") == NULL) { 103 | printk(KERN_INFO "Cannot create device test_dev\n"); 104 | goto r_device; 105 | } 106 | printk(KERN_INFO "[main-module] Kernel Module Inserted Successfully...\n"); 107 | return 0; 108 | 109 | r_device: 110 | class_destroy(dev_class); 111 | r_class: 112 | unregister_chrdev_region(dev, 1); 113 | return -1; 114 | } 115 | 116 | void __exit main_exit(void) 117 | { 118 | device_destroy(dev_class, dev); 119 | class_destroy(dev_class); 120 | cdev_del(&main_cdev); 121 | unregister_chrdev_region(dev, 1); 122 | printk(KERN_INFO "[main-module] Kernel Module Removed Successfully...\n"); 123 | } 124 | 125 | module_init(main_init); 126 | module_exit(main_exit); 127 | 128 | MODULE_LICENSE("GPL"); 129 | MODULE_AUTHOR("Renan Prata "); 130 | MODULE_DESCRIPTION("A simple hello world driver"); 131 | MODULE_VERSION("1:3"); -------------------------------------------------------------------------------- /chapters/major-minor/src/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += main.o 2 | 3 | KDIR = /lib/modules/$(shell uname -r)/build 4 | 5 | all: 6 | make -C $(KDIR) M=$(shell pwd) modules 7 | 8 | clean: 9 | make -C $(KDIR) M=$(shell pwd) clean -------------------------------------------------------------------------------- /chapters/major-minor/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | dev_t dev = 0; 9 | static int __init main_init(void) 10 | { 11 | if (alloc_chrdev_region(&dev, 0, 1, "KernelTest_Dev") < 0) 12 | { 13 | printk(KERN_INFO "Cannot allocate major number for device 1\n"); 14 | } 15 | printk(KERN_INFO "Major = %d / Minor = %d", MAJOR(dev), MINOR(dev)); 16 | printk(KERN_INFO "[main-module] Kernel Module Inserted Successfully...\n"); 17 | return 0; 18 | } 19 | 20 | void __exit main_exit(void) 21 | { 22 | unregister_chrdev_region(dev, 1); 23 | printk(KERN_INFO "[main-module] Kernel Module Removed Successfully...\n"); 24 | } 25 | 26 | module_init(main_init); 27 | module_exit(main_exit); 28 | 29 | MODULE_LICENSE("MIT"); 30 | MODULE_AUTHOR("Renan Prata "); 31 | MODULE_DESCRIPTION("A simple hello world driver"); 32 | MODULE_VERSION("1:1.0"); -------------------------------------------------------------------------------- /chapters/module-parameter/module-parameter.md: -------------------------------------------------------------------------------- 1 | # Passing Arguments to Device Driver # 2 | 3 | We can pass the arguments to any other functions in the same program. But Is it possible to pass any arguments to any program? I think Probably yes. Right? Well, we can. In C Programming we can pass the arguments to the program. For that we need to add argc and argv in the main function definition. I hope everyone knows that. Now come to our topic. Is it possible to pass the argument to the Device Driver? Fine. In this tutorial, we are going to see that topic. 4 | 5 | ## Types of Permissions ## 6 | 7 | * `S_IWUSR` 8 | * `S_IRUSR` 9 | * `S_IXUSR` 10 | * `S_IRGRP` 11 | * `S_IWGRP` 12 | * `S_IXGR` 13 | 14 | ``` 15 | In this S_I is a common header. 16 | R = read ,W =write,X= Execute. 17 | USR =user, GRP = Group 18 | Using OR ‘|’ (or operation) we can set multiple permissions at a time. 19 | ``` 20 | 21 | ## Module Parameters Macros ## 22 | 23 | * `module_param` 24 | * `module_param_array` 25 | * `module_param_cb` 26 | 27 | ### module_param ### 28 | 29 | This macro used to initialize the arguments. module_param takes three parameters: the name of the variable, its type, and a permissions mask to be used for an accompanying sysfs entry. The macro should be placed outside of any function and is typically found near the head of the source file. `module_param` macro, defined in `linux/moduleparam.h`. 30 | 31 | ``` 32 | module_param(devName, typeOfParam, perm) 33 | ``` 34 | 35 | module_param() macro creates the sub-directory under /sys/module. For example: 36 | 37 | ``` 38 | module_param(fakeDev, int, S_IWUSR|S_IRUSR) 39 | ``` 40 | 41 | This will create a sysfs entry `/sys/module//parameters/fakeDev`. 42 | Numerous types are supported for module parameters: 43 | 44 | * bool 45 | * invbool (type inverts the value, so that true values become false and vice versa) 46 | * charp (char *) 47 | * int 48 | * long 49 | * short 50 | * uint 51 | * ulong 52 | * ushort 53 | 54 | ### module_param_array ### 55 | 56 | This macro is used to send the array as an argument. Array parameters, where the values are supplied as a comma-separated list, are also supported by the module loader. To declare an array parameter, use: 57 | 58 | ``` 59 | module_param_array(devName, typeOfParam, num, perm) 60 | ``` 61 | 62 | ### module_param_cb ## 63 | 64 | This macro used to register the callback whenever the argument (parameter) got changed. I think you don’t understand. Let me explain it properly. 65 | 66 | For Example, I have created one parameter by using module_param and it will create a sysfs entry. When value of sysfs changed (you can change using `echo 1 > /sys/module//parameters/fakeDev`), we can get notification by callback. If you want to get a notification whenever the value got to change. we need to register our handler function to its file operation structure. 67 | 68 | ``` 69 | struct kernel_param_ops { 70 | int (*set)(const char *val, const struct kernel_param *kp); 71 | int (*get)(char *buffer, const struct kernel_param *kp); 72 | void (*free)(void *arg); 73 | } 74 | ``` 75 | 76 | ## When we will need this notification? ## 77 | 78 | I will tell you about the practical scenario. Whenever the value is set to 1, you have to write something into a hardware register. How can you do this if the change of value variable is not notified to you? Got it? I think you have understood. If you didn't understand, just see the explanation in the same directory. -------------------------------------------------------------------------------- /chapters/module-parameter/src/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += hello-world-module.o 2 | 3 | KDIR = /lib/modules/$(shell uname -r)/build 4 | 5 | all: 6 | make -C $(KDIR) M=$(shell pwd) modules 7 | 8 | clean: 9 | make -C $(KDIR) M=$(shell pwd) clean -------------------------------------------------------------------------------- /chapters/module-parameter/src/hello-world-module.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int value, arr_value[4]; 7 | char *name; 8 | int cb_value = 0; 9 | 10 | module_param(value, int, S_IRUSR|S_IWUSR); //integer value 11 | module_param(name, charp, S_IRUSR|S_IWUSR); //String 12 | module_param_array(arr_value, int, NULL, S_IRUSR|S_IWUSR); //Array of integers 13 | 14 | /*----------------------Module_param_cb()--------------------------------*/ 15 | int notify_param(const char *val, const struct kernel_param *kp) 16 | { 17 | int res = param_set_int(val, kp); // Use helper for write variable 18 | if(res==0) { 19 | printk(KERN_INFO "Call back function called...\n"); 20 | printk(KERN_INFO "New value of cb_value = %d\n", cb_value); 21 | return 0; 22 | } 23 | return -1; 24 | } 25 | 26 | const struct kernel_param_ops my_param_ops = 27 | { 28 | .set = ¬ify_param, // Use our setter ... 29 | .get = ¶m_get_int, // .. and standard getter 30 | }; 31 | 32 | module_param_cb(cb_value, &my_param_ops, &cb_value, S_IRUGO|S_IWUSR ); 33 | /*-------------------------------------------------------------------------*/ 34 | 35 | static int __init hello_world_init(void) 36 | { 37 | int i; 38 | printk(KERN_INFO "value = %d \n", value); 39 | printk(KERN_INFO "cb_value = %d \n", cb_value); 40 | printk(KERN_INFO "name = %s \n", name); 41 | for (i = 0; i < (sizeof arr_value / sizeof (int)); i++) { 42 | printk(KERN_INFO "Arr_value[%d] = %d\n", i, arr_value[i]); 43 | } 44 | printk(KERN_INFO "Kernel Module Inserted Successfully...\n"); 45 | return 0; 46 | } 47 | 48 | void __exit hello_world_exit(void) 49 | { 50 | printk(KERN_INFO "Kernel Module Removed Successfully...\n"); 51 | } 52 | 53 | module_init(hello_world_init); 54 | module_exit(hello_world_exit); 55 | 56 | MODULE_LICENSE("MIT"); 57 | MODULE_AUTHOR("Renan Prata