Skip to content

dotai315/C_Device_Driver_Learning

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

C_Device_Driver_Learning

All my studying about linux device driver with c languages. Reference this course: https://embetronicx.com/tutorials/linux/device-drivers/linux-device-driver-part-1-introduction/

Table Content


  1. Introduction
  2. First Driver
  3. Passing Arguments
  4. Major & Minor Number
  5. Creating Device File
  6. File Operations
  7. Real Device Driver
  8. IOCTL Tutorial
  9. Procfs Tutorial
  10. Waitqueue Tutorial
  11. SysFS Tutorial
  12. Interrupt Tutorial
  13. Interrupt Programming
  14. Workqueue (Static Method)
  15. Workqueue (Dynamic Method)
  16. Own Workqueue
  17. Linked List 1
  18. Linked List 2
  19. Kernenl Thread
  20. Tasklet (Static Method)
  21. Tasklet (Dynamic Method)
  22. Mutex Tutorial
  23. Spinlock Tutorial 1
  24. Spinlock Tutorial 2
  25. Sending Signals
  26. Kernel Timer Tutorial
  27. High Resolution Timer Tutorial
  28. Completion Tutorial
  29. EXPORT_SYMBOL
  30. Atomic Variables Tutorial
  31. Seqlock
  32. Misc Device Driver
  33. USB Device Driver
  34. GPIO Driver Basic
  35. GPIO Interrupt
  36. I2C Linux Device Driver
  37. Dummy I2C Bus Driver
  38. Real I2C Bus Driver
  39. I2C Bus Driver Using I2C-GPIO
  40. SSD1306 I2C Linux Device Driver
  41. Poll Linux
  42. Select Linux
  43. E-Poll Linux
  44. Softirq Linux
  45. Threaded IRQ in Linux
  46. SPI Protocol Driver
  47. BMP280 I2C Pressure Sensor Driver
  48. Reference

1. Introduction

What is Linux?

Linux is an open-source Unix-like operating system based on the Linux kernel, an operating system kernel first released on September 17, 1991, by Linus Torvalds. Linux is typically packaged as a Linux distribution.

Linux Architechture

At the top is the user, or application, space. This is where the user applications are executed. Below the user space is the kernel space. Here, the Linux kernel exists.


There is also the GNU C Library (glibc). This provides the system call interface that connects to the kernel and provides the mechanism to transition between the user-space application and the kernel. This is important because the kernel and user application occupy different protected address spaces. And while each user-space process occupies its own virtual address space, the kernel occupies a single address space.


The Linux kernel can be further divided into three gross levels. At the top is the system call interface, which implements the basic functions such as read and write. Below the system call interface is the kernel code, which can be more accurately defined as the architecture-independent kernel code. This code is common to all of the processor architectures supported by Linux. Below this is the architecture-dependent code, which forms what is more commonly called a BSP (Board Support Package). This code serves as the processor and platform-specific code for the given architecture.

Linux Kernel Module

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.


Custom Codes can be added to Linux kernels via two methods:

  • The basic way is to add the code to the kernel source tree and recompile the kernel
  • 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 the module refers to the code that we want to add to the kernel.

Since we are loading these codes at runtime and they are not part of the official Linux Kernel, these are called loadable kernel modules (LKM), which is different from the "base kernel". The base kernel is located in /boot directory and is always loaded when we boot our machine whereas LKMs ared loaded after the base kernel is alread loaded. Nonetheless, this LKM is very much part of out kernel and they communicate with base kernel to complete their functions.

LKMs can perform a variety of task, but basically, they come uner three main categories:

  • Device drivers
  • Filesystem drivers
  • System calls

Device Driver

A device driver is a particular form of software application that is designed to enable interaction with hardware devices. Without the required device driver, the corresponding hardware device fails to work.


A device driver usually communicates with the hardware by means of the communications subsystem or computer bus to which the hardware is connected. Device drivers are operating system-specific and hardware-dependent. A device driver acts as a translator between the hardware device and the programs or operating systems that use it.

Character Device

A char file is a hardware file that reads/writes data in character by character fashion. Some classic examples are keyboard, mouse, serial printer. If a user uses a char file for writing data no other user can use the same char file to write data that blocks access to another user. Character files use synchronize Technic to write data. Of you observe char files are used for communication purposes and they can not be mounted.

Block Device

A block file is a hardware file that reads/writes data in blocks instead of character by character. This type of file is very much useful when we want to write/read data in a 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 writing of data is done in an asynchronous fashion and it is CPU-intensive activity. These device files are used to store data on real hardware and can be mounted so that we can access the data we have written.

Network Device

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.

2. First Driver

Module Information

  • License
  • Author
  • Module Description
  • Module Version

These pieces of infomations are present in the linux/module.h as macros.

License

GPL, or the GNU General Public License, is an open-source license meant for software. If your software is licensed under the terms of the GPL, it is free. However, “free” here does not essentially mean freeware—it can also be paid software. Instead, “free” as per the GPL means freedom. As proponents of GPL proudly proclaim, free as in freedom, not free beer.

The following license idents are currently accepted as indicating free software modules.

"GPL" [GNU Public License v2 or later]

"GPL v2" [GNU Public License v2]

"GPL and additional rights" [GNU Public License v2 rights and more]

"Dual BSD/GPL" [GNU Public License v2 or BSD license choice]

"Dual MIT/GPL" [GNU Public License v2 or MIT license choice]

"Dual MPL/GPL" [GNU Public License v2 or Mozilla license choice]

"Proprietary" [Non free products]

There are dual-licensed components, but when running with Linux it is the GPL that is relevant so this is a non-issue. Similarly, LGPL linked with GPL is a GPL combined work.

This exists for several reasons

  1. modinfo can show license info for users wanting to vet their setup is free
  2. The community can ignore bug reports including proprietary modules
  3. Vendors can do likewise based on their own policies

Author

Using this Macro we can mention who is writing this Linux device driver or module. So modinfo can show the author’s name for users wanting to know. We can give the Author’s name for our driver (module) like below. For this, you need to include the linux/module.h header file.

MODULE_AUTHOR("Author");

Version

Using this Macro we can give the version of the module or driver. So, modinfo can show the module version for users wanting to know.

Version of form [epoch:]version[-extra-version].
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”.
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).
extraversion: Like version, but inserted for local customizations, eg “rh3” or “rusty1”.

Example

MODULE_VERSION("2:1.0");

Simple Kernel Module Programming

Init function

This is the function that will execute first when the Linux device 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.

static int __init hello_world_init(void) /* Constructor */
{
    return 0;
}
module_init(hello_world_init);

Exit function

This is the function that will execute last when the Linux device 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.

void __exit hello_world_exit(void)
{
}
module_exit(hello_world_exit);

Printk()

print a kernel message

KERN_EMERG: Used for emergency messages, usually those that precede a crash.

KERN_ALERT:A situation requiring immediate action.

KERN_CRIT: Critical conditions are often related to serious hardware or software failures.

KERN_ERR: Used to report error conditions; device drivers often use KERN_ERR to report hardware difficulties.

KERN_WARNING: Warnings about problematic situations that do not, in themselves, create serious problems with the system.

KERN_NOTICE: Situations that are normal, but still worthy of note. A number of security-related conditions are reported at this level.

KERN_INFO:Informational messages. Many drivers print information about the hardware they find at startup time at this level.

KERN_DEBUG: Used for debugging messages.

Example

printk(KERN_INFO "Welcome To EmbeTronicX");

In the newer Linux kernels, you can use the APIs below instead of this printk.

  1. pr_info – Print an info-level message. (ex. pr_info("test info message\n")).
  2. pr_cont – Continues a previous log message in the same line.
  3. pr_debug – Print a debug-level message conditionally.
  4. pr_err – Print an error-level message. (ex. pr_err(“test error message\n”)).
  5. pr_warn – Print a warning-level message.

3. Passing Arguments

Module Parameters Macros

  • module_param()
  • module_param_array()
  • module_param_cb()

There are several types of permissions:

  • S_IWUSR
  • S_IRUSR
  • S_IXUSR
  • S_IRGRP
  • S_IWGRP
  • S_IXGRP

module_param()

This macro is 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.

module_param(name, type, perm);

Numberous types are supported for module parameters:

  • bool
  • invbool
  • charp
  • int
  • long
  • short
  • uint
  • ulong
  • ushort

module_param_array()

This macro is used to send the array as an argument to the Linux device driver. Array parameters, where the values are supplied as a comma-separated list, are also supported by the module loader. To declare an array parameter:

module_param_array(name, type, num, perm);

name is the name of your array

type is the type of the array elements

num is an integer variable(optional) otherwise NULL

perm is the usual permissions value

module_param_cb()

This macro is used to register the callback. Whenever the argument (parameter) got changed, this callback function will be called.

4. Character Device Driver

Character Device Driver Major Number and Minor Number

One of the basic features of the Linux kernel is that it abstracts the handling of devices. All hardware devices look like regular files; they can be opened, closed, read, and written using the same, standard, system calls that are used to manipulate files. To Linux, everything is a file. To write to the hard disk, you write to a file. To read from the keyboard is to read from a file. To store backups on a tape device is to write to a file. Even to read from memory is to read from a file. If the file from which you are trying to read or to which you are trying to write is a “normal” file, the process is fairly easy to understand: the file is opened and you read or write data. So the device driver also likes the file. The driver will create a special file for every hardware device. We can communicate with the hardware using those special files (device files).

If you want to create a special file, we should know about the major number and minor number in the device driver. In this tutorial, we will learn the major and minor numbers.

Major and Minor Number

Major Number

Traditionally, the major number identifies the driver associated with the device. A major number can also be shared by multiple device drivers. See /proc/devices to find out how major numbers are assigned on a running Linux instance.

Minor Number

The major number is to identify the corresponding driver. Many devices may use the same major number. So we need to assign the number to each device that is using the same major number. So, this is a minor number. In other words, The device driver uses the minor number minor to distinguish individual physical or logical devices.

Allocating Major and Minor Number

Static Allocating

If you want to set a particular major number for your driver, you can use this method. This method will allocate that major number if it is available.

int register_chrdev_region(dev_t first, unsigned int count, char *name);

Here, first is the beginning device number of the range you would like to allocate.

count is the total number of contiguous device numbers you are requesting. Note that, if the count is large, the range you request could spill over to the next major number; but everything will still work properly as long as the number range you request is available.

name is the name of the device that should be associated with this number range; it will appear in /proc/devices and sysfs.

The return value from register_chrdev_region will be 0 if the allocation was successfully performed. In case of error, a negative error code will be returned, and you will not have access to the requested region.

The dev_t type (defined in linux/types.h) is used to hold device numbers—both the major and minor parts. dev_t is a 32-bit quantity with 12 bits set aside for the major number and 20 for the minor number.

If you want to create the dev_t structure variable for your major and minor number

MKDEV(int major, int minor);

If you want to get your major number and minor number from dev_t

MAJOR(dev_t dev);

MINOR(dev_t dev);

If we don’t want the fixed major and minor numbers please use this method. This method will allocate the major number dynamically to your driver which is available.

int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);

dev is an output-only parameter that will, on successful completion, hold the first number in your allocated range.

firstminor should be the requested first minor number to use; it is usually 0.

count is the total number of contiguous device numbers you are requesting.

name is the name of the device that should be associated with this number range; it will appear in /b>/proc/devices and sysfs

Unregister the Major and Minor Number

Regardless of how you allocate your device numbers, you should free them when they are no longer in use. Device numbers are freed with:

void unregister_chrdev_region(dev_t first, unsigned int count);

The usual place to call unregister_chrdev_region would be in your module’s cleanup function (Exit Function).

48. Reference

  1. https://embetronicx.com/tutorials/linux/device-drivers/
  2. https://developer.ibm.com/articles/l-linux-kernel/
  3. https://en.wikipedia.org/wiki/Linux

About

All my studying about linux device driver with c languages. Reference this course: https://embetronicx.com/tutorials/linux/device-drivers/linux-device-driver-part-1-introduction/

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published