Linux C programming practice (5 )?? Driver Design 1. introduction the device driver is the interface between the operating system kernel and the Machine hardware. it shields the hardware details for applications. Generally, the Linux device driver must complete the following functions: (1) initialize the device. (2) provide various...
C programming practices in Linux (V)
?? Driver Design
1. Introduction
The device driver is the interface between the operating system kernel and the hardware of the machine. it shields the hardware details from the application. Generally, the Linux device driver must complete the following functions:
(1) initialize the device;
(2) provide various equipment services;
(3) responsible for data exchange between the kernel and the device;
(4) detect and handle device errors during operation.
What's amazing is that the device drivers in Linux are organized into a set of functions to complete different tasks. these functions make Windows device operations look like files. In
In the application's view, a hardware device is just a device file, and an application can operate the hardware device like a common file. Chapter 1 of this series of articles has been read in file system programming
These functions are open (), close (), read (), write (), and so on.
Linux mainly divides devices into two categories: Character devices and block devices (of course, the drivers for network devices and USB and other devices are slightly different ). The difference between the two types of devices is:
When a read/write request is sent to a character device, the actual hardware I/O usually happens immediately after the device sends a read/write request, while the block device does not. it uses a system memory as a buffer, when a user process requests a device
If the request data can meet the requirements of the user, the request data will be returned. if not, the request function will be called for actual I/O operations. Block devices are mainly used for disks and other slow devices. Drive by character device
Therefore, this chapter describes how to write the driver for character devices.
2. driver module functions
The init function is used to initialize the controlled device and call the register_chrdev () function to register the character device. Assume that there is a character device "exampledev", its init
Function:
Void exampledev_init (void)
{
If (register_chrdev (MAJOR_NUM, "exampledev", & exampledev_fops ))
TRACE_TXT ("Device exampledev driver registered error ");
Else
TRACE_TXT ("Device exampledev driver registered successfully ");
... // Device initialization
}
The MAJOR_NUM parameter in the register_chrdev function is the main device number, "exampledev" is the device name, and exampledev_fops is The struct containing the entry point of the basic function, class
Type: file_operations. When exampledev_init is executed, it will call the kernel function register_chrdev to store the driver's basic entry point pointer to the character device in the kernel.
In the address table, the entry address is provided when the user process calls the device.
The file_operations struct is defined:
Struct file_operations
{
Int (* lseek )();
Int (* read )();
Int (* write )();
Int (* readdir )();
Int (* select )();
Int (* ioctl )();
Int (* mmap )();
Int (* open )();
Void (* release )();
Int (* fsync )();
Int (* fasync )();
Int (* check_media_change )();
Void (* revalidate )();
};
Most drivers only take advantage of some of them. for features not required in the driver, you only need to set the value of the corresponding position to NULL. For character devices
The main portals include open (), release (), read (), write (), and ioctl ().
When the open () function is used to call the open () system for special files on the device, the open () function of the driver is called:
Int open (struct inode * inode, struct file * file );
The inode parameter is the pointer to the inode (index node) structure of the special file of the device. the parameter file is the pointer to the file structure of the device. The main task of open () is to determine the hardware
In the ready state, verify the validity of the next device number (the next device number can be obtained using MINOR (inode-> I-rdev) control the number of processes using the device, and return the status code based on the execution status.
(0 indicates success, and negative indicates an error;
Release () function when the last user process that opens the device executes the close () system call, the kernel will call the release () function of the driver:
Void release (struct inode * inode, struct file * file );
The main task of the release function is to clear unfinished input/output operations, release resources, and reset the user-defined exclusive flag.
When you call the read () system for a special file on the device, the read () function of the driver is called:
Void read (struct inode * inode, struct file * file, char * buf, int count );
The parameter buf is a pointer to the user space buffer, which is provided by the user process. count is the number of bytes that the user process requires to read.
The read () function reads or copies count bytes from the hard device or kernel memory to the buffer specified by buf. When copying data, note that the driver runs in the kernel.
The buffer specified by buf is in the user's memory area and cannot be accessed and used directly in the kernel. Therefore, special replication functions must be used to complete replication.
Segment. h>:
Void put_user_byte (char data_byte, char * u_addr );
Void put_user_word (short data_word, short * u_addr );
Void put_user_long (long data_long, long * u_addr );
Void memcpy_tofs (void * u_addr, void * k_addr, unsigned long cnt );
The u_addr parameter is the user space address, k_addr is the kernel space address, and cnt is the number of bytes.
The write () function calls the write () function of the driver when a special file of the device is called by the write () system:
Void write (struct inode * inode, struct file * file, char * buf, int count );
The write () function is to copy the count bytes in the buffer specified by the buf parameter to the hardware or kernel memory. LIKE read (), replication also needs to be completed by special functions.
To:
Unsigned char_get_user_byte (char * u_addr );
Unsigned char_get_user_word (short * u_addr );
Unsigned char_get_user_long (long * u_addr );
Unsigned memcpy_fromfs (void * k_addr, void * u_addr, unsigned long cnt );
Ioctl () is a special control function that can be used to transmit control information to a device or obtain status information from a device. the function is prototype:
Int ioctl (struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg );
The cmd parameter is the code of the command to be executed by the device driver. it is customized by the user. The arg parameter provides parameters for the corresponding command. the type can be integer or pointer.
Similarly, in the driver, the definitions of these functions must also comply with naming rules. as agreed in this article, these functions of the driver of the device "exampledev" should be named respectively
Exampledev_open, exampledev _ release, exampledev_read, exampledev_write, and exampledev_ioctl.
The structure variable exampledev_fops is assigned as follows:
Struct file_operations exampledev_fops {
NULL,
Exampledev_read,
Exampledev_write,
NULL,
NULL,
Exampledev_ioctl,
NULL,
Exampledev_open,
Exampledev_release,
NULL,
NULL,
NULL,
NULL
};
3. memory allocation
Because the Linux driver runs in the kernel, you cannot use the user-level malloc/free function when the device driver needs to apply for/release the memory. Instead, you must use the kernel-level functions.
Kmalloc/kfree (). the prototype of the kmalloc () function is as follows:
Void kmalloc (size_t size, int priority );
The parameter size is the number of bytes for memory allocation. the parameter priority indicates the action that the user process uses if kmalloc () cannot be allocated immediately: GFP_KERNEL indicates waiting, that is, wait
The kmalloc () function allocates some memory to the swap zone to meet your memory needs. GFP_ATOMIC indicates no waiting. if the memory cannot be allocated immediately, 0 is returned. The return value of the function indicates
The starting address of the allocated memory. If an error occurs, 0 is returned.
The kfree () function is used to release the memory allocated by kmalloc (). kfree () is defined:
# Define kfree (n) kfree_s (n), 0)
The prototype of the kfree_s () function is:
Void kfree_s (void * ptr, int size );
The ptr parameter is the allocated memory pointer returned by kmalloc (). size is the number of bytes to release the memory. if it is 0, the kernel automatically determines the memory size.
4. interruption
Many devices involve interrupt operations. Therefore, the interrupt service program must be provided for hardware interrupt requests in the drivers of such devices. Similar to the basic entry point for registration, the driver
The kernel must also be requested to associate specific interrupt requests with the interrupt service program. In Linux, the request_irq () function is used to implement the request:
Int request_irq (unsigned int irq, void (* handler) int, unsigned long type, char * name );
The irq parameter is the request number to be interrupted, and the handler parameter is the pointer to the interrupted service program. the type parameter is used to determine whether the service is interrupted normally or quickly (normal or normal
After the return, the kernel can execute a scheduler to determine which process to run. a fast interrupt means that the interrupted program is executed immediately after the return of the interrupted service subprogram, and the type of normal interruption is set
The value is 0, and the value of fast interrupt type is SA_INTERRUPT). the parameter name is the name of the device driver.
5. instance
I recently designed a circuit board using Samsung S3C2410 ARM processor (ARM processor is widely used in mobile phones, PDAs, and other embedded systems). The board contains four user-programmable LEDs.
LEDs are connected to the programmable I/O ports (GPIO) of the ARM processor. The connection principle between the ARM central processor and the LED is given:
We have transplanted the Linux operating system on the ARM processor, and now we will write these LED drivers:
# Include
# Include
# Include
# Include
# Include
# Include
# Include
# Include
# Include
# Include
# Include
# Include
# Define DEVICE_NAME "leds"/* define the name of the led device */
# Define LED_MAJOR 231/* define the main device number of the led device */
Static unsigned long led_table [] =
{
/* Hardware resources of led devices in I/O mode */
GPIO_B10, GPIO_B8, GPIO_B5, GPIO_B6,
};
/* Use ioctl to control LEDs */
Static int leds_ioctl (struct inode * inode, struct file * file, unsigned int cmd,
Unsigned long arg)
{
Switch (cmd)
{
Case 0:
Case 1:
If (arg> 4)
{
Return-EINVAL;
}
Write_gpio_bit (led_table [arg],! Cmd );
Default:
Return-EINVAL;
}
}
Static struct file_operations leds_fops =
{
Owner: THIS_MODULE, ioctl: leds_ioctl,
};
Static devfs_handle_t devfs_handle;
Static int _ init leds_init (void)
{
Int ret;
Int I;
/* Register the device in the kernel */
Ret = register_chrdev (LED_MAJOR, DEVICE_NAME, & leds_fops );
If (ret <0)
{
Printk (DEVICE_NAME "can't register major number" n ");
Return ret;
}
Devfs_handle = devfs_register (NULL, DEVICE_NAME, DEVFS_FL_DEFAULT, LED_MAJOR,
0, S_IFCHR | S_IRUSR | S_IWUSR, & leds_fops, NULL );
/* Use macro to initialize the port. set_gpio_ctrl and write_gpio_bit are MACRO Definitions */
For (I = 0; I <8; I ++)
{
Set_gpio_ctrl (led_table [I] | GPIO_PULLUP_EN | GPIO_MODE_OUT );
Write_gpio_bit (led_table [I], 1 );
}
Printk (DEVICE_NAME "initialized" n ");
Return 0;
}
Static void _ exit leds_exit (void)
{
Devfs_unregister (devfs_handle );
Unregister_chrdev (LED_MAJOR, DEVICE_NAME );
}
Module_init (leds_init );
Module_exit (leds_exit );
Use the command to compile the led driver module:
# Arm-linux-gcc-dsf-kernel _-I/arm/kernel/include
-DKBUILD_BASENAME = leds-DMODULE-c-o leds. o leds. c
The above Command will generate the leds. o file, copy the file to the/lib Directory of the board, and use the following command to install the leds driver module:
# Insmod/lib/leds. o
The command to delete this module is:
# Rmmod leds
6. Summary
This chapter describes the entry functions of the Linux device driver and the memory application and interruption in the driver, and provides a driver instance for controlling the LED through the GPIO port of the ARM processor.