Linux driver Reading Notes

Source: Internet
Author: User
Linux driver Reading Notes-Linux general technology-Linux programming and kernel information. The following is a detailed description. In LINUX, the entry points provided by the device driver are described to the system by a structure, which is defined:
# Include
Struct file_operations {
Int (* lseek) (struct inode * inode, struct file * filp,
Off_t off, int pos );
Int (* read) (struct inode * inode, struct file * filp,
Char * buf, int count );
Int (* write) (struct inode * inode, struct file * filp,
Char * buf, int count );
Int (* readdir) (struct inode * inode, struct file * filp,
Struct dirent * dirent, int count );
Int (* select) (struct inode * inode, struct file * filp,
Int sel_type, select_table * wait );
Int (* ioctl) (struct inode * inode, struct file * filp,
Unsigned int cmd, unsigned int arg );
Int (* mmap) (void );
Int (* open) (struct inode * inode, struct file * filp );
Void (* release) (struct inode * inode, struct file * filp );
Int (* fsync) (struct inode * inode, struct file * filp );
};
Here, struct inode provides information about the special device file/dev/driver (assuming this device is named driver), which is defined:
# Include
Struct inode {
Dev_t I _dev;
Unsigned long I _ino;/* Inode number */
Umode_t I _mode;/* Mode of the file */
Nlink_t I _nlink;
Uid_t I _uid;
Gid_t I _gid;
Dev_t I _rdev;/* Device major and minor numbers */
Off_t I _size;
Time_t I _atime;
Time_t I _mtime;
Time_t I _ctime;
Unsigned long I _blksize;
Unsigned long I _blocks;
Struct inode_operations * I _op;
Struct super_block * I _sb;
Struct wait_queue * I _wait;
Struct file_lock * I _flock;
Struct vm_area_struct * I _mmap;
Struct inode * I _next, * I _prev;
Struct inode * I _hash_next, * I _hash_prev;
Struct inode * I _bound_to, * I _bound_by;
Unsigned short I _count;
Unsigned short I _flags;/* Mount flags (see fs. h )*/
Unsigned char I _lock;
Unsigned char I _dirt;
Unsigned char I _pipe;
Unsigned char I _mount;
Unsigned char I _seek;
Unsigned char I _update;
Union {
Struct pipe_inode_info pipe_ I;
Struct minix_inode_info minix_ I;
Struct ext_inode_info ext_ I;
Struct msdos_inode_info msdos_ I;
Struct iso_inode_info isofs_ I;
Struct nfs_inode_info nfs_ I;
} U;
};
Struct file is mainly used for device drivers corresponding to the file system. Of course, other device drivers can also use it. It provides information about the opened file, defined as: # include
Struct file {
Mode_t f_mode;
Dev_t f_rdev;/* needed for/dev/tty */
Off_t f_pos;/* Curr. posn in file */
Unsigned short f_flags;/* The flags arg passed to open */
Unsigned short f_count;/* Number of opens on this file */
Unsigned short f_reada;
Struct inode * f_inode;/* pointer to the inode struct */
Struct file_operations * f_op;/* pointer to the fops struct */
};
In the file_operations structure, it indicates the entry point location provided by the device driver, which is
(1) lseek: the position of the pointer to move a file. Obviously, it can only be used for devices with random access.
(2) read: Perform read operations. The buf parameter is the buffer that stores the read results, and the count parameter is the length of the data to be read. If the returned value is negative, the read operation is incorrect. Otherwise, the actual number of bytes read is returned. For the bytes type, both the number of bytes read and the actual number of bytes returned must be a multiple of inode-> I _blksize.
(3) write, which is similar to read.
(4) readdir: Get the next directory entry point, which is used only by the device driver related to the file system.
(5) select the device. If the driver does not provide a select entry, the select Operation considers the device to be ready for any I/O operations.
(6) read and write operations other than ioctl. The cmd parameter is a custom command.
(7) mmap is used to map the device content to the address space, which is generally used only by the block device driver.
(8) open the device to prepare for I/O operations. If the return value is 0, the operation is successful. If the return value is negative, the operation fails. If the driver does not provide an open entry, the open is considered successful as long as the/dev/driver file exists.
(9) release, that is, the close operation.
The entry point provided by the device driver registers with the system when the device driver is initialized so that the system can call it as appropriate. In LINUX, you can call register_chrdev to register the drivers for devices. Register_chrdev is defined:
# Include
# Include
Int register_chrdev (unsigned int major, const char * name, struct file_operations * fops );
Among them, major is the master device number applied to the system for the device driver. If it is 0, the system dynamically allocates a master device number for the driver. Name is the device name. Fops is the preceding description of each call's entry point. If this function returns 0, the operation is successful. -EINVAL indicates that the requested primary device number is invalid. Generally, the primary device number is greater than the maximum device number allowed by the system. Return-EBUSY indicates that the requested master device number is being used by other device drivers. If the master device number is successfully allocated dynamically, this function returns the assigned master device number. If register_chrdev is successful, the device name will appear in the/proc/devices file.
The initialization part is generally responsible for applying system resources to the device driver, including memory, interrupt, clock, I/O port, etc. These resources can also be applied in the open subroutine or elsewhere. When these resources are not used, they should be released to facilitate resource sharing. In UNIX systems, interrupt processing is a core part of the system. Therefore, if data exchange between a device and the system is interrupted, the driver of the device must be a part of the core of the system. The device driver calls the request_irq function to request an interruption and uses free_irq to release the interruption. They are defined:
# Include
Int request_irq (unsigned int irq,
Void (* handler) (int irq, void dev_id, struct pt_regs * regs ),
Unsigned long flags,
Const char * device,
Void * dev_id );
Void free_irq (unsigned int irq, void * dev_id );
The irq parameter indicates the hardware interrupt number to be applied. Handler is the Interrupt Processing subroutine registered with the system. It is called by the system when the interrupt is generated. The irq parameter in the call is the interrupt number, and dev_id indicates the device ID of the system notified upon application, the regs is the register content when the interrupt occurs. The device name appears in the/proc/interrupts file. Flag is an option during application. It determines some features of the interrupt handler. The most important thing is that the interrupt handler is a fast handler (SA_INTERRUPT is set in the flag) or a slow processing program (no SA_INTERRUPT is set). When a fast processing program is running, all interruptions are blocked. When a slow processing program is running, except for the interruption being processed, other interruptions are not blocked.
In LINUX, interrupts can be shared by different interrupt handlers. This requires each interrupt handler to set SA_SHIRQ in flags when applying for an interrupt, these handlers are differentiated by dev_id. If the interrupt is exclusive to a handler, dev_id can be NULL. Request_irq: 0 indicates success,-INVAL indicates irq> 15 or handler = NULL, and-EBUSY indicates that the interrupt is occupied and cannot be shared. As part of the system's core, the device driver does not call malloc and free when applying for and releasing memory, but instead calls kmalloc and kfree. They are defined:
# Include
Void * kmalloc (unsigned int len, int priority );
Void kfree (void * obj );
The len parameter is the number of bytes to be applied, and obj is the memory pointer to be released. Priority is the priority of memory allocation operations, that is, how to operate when there is not enough idle memory, generally use GFP_KERNEL. Unlike interrupt and memory, using an unapplied I/O port will not cause CPU exceptions, and will not cause errors such as "segmentation fault. Any process can access any I/O port. At this time, the system cannot guarantee that the operation on the I/O port will not conflict, or even cause the system to crash. Therefore, before using the I/O port, You should also check whether other programs are using this I/O port. If not, mark this port as in use, release it after use. The following functions are required:
Int check_region (unsigned int from, unsigned int extent );
Void request_region (unsigned int from, unsigned int extent, const char * name );
Void release_region (unsigned int from, unsigned int extent );
When calling these functions, the parameters are as follows: from indicates the starting address of the applied I/O port; extent indicates the number of ports to be applied from; name indicates the device name, it will appear in the/proc/ioports file. Check_region returns 0, indicating that the I/O port is idle; otherwise, it is in use.
After applying for the I/O port, you can use the following functions to access the I/O port:
# Include
Inline unsigned int inb (unsigned short port );
Inline unsigned int inb_p (unsigned short port );
Inline void outb (char value, unsigned short port );
Inline void outb_p (char value, unsigned short port );
Inb_p and outb_p insert a certain delay to adapt to some slow I/O Ports. In device drivers, the timing mechanism is generally used. In LINUX, the clock is taken over by the system, and the device driver can apply for a clock from the system. Clock-related system calls include:
# Include
# Include
Void add_timer (struct timer_list * timer );
Int del_timer (struct timer_list * timer );
Inline void init_timer (struct timer_list * timer );
Struct timer_list is defined:
Struct timer_list {
Struct timer_list * next;
Struct timer_list * prev;
Unsigned long expires;
Unsigned long data;
Void (* function) (unsigned long d );
};
Expires indicates the time when the function is to be executed. At the core of the system, a global variable JIFFIES indicates the current time. Generally, when add_timer is called, jiffies = JIFFIES + num indicates that the function is executed after the minimum interval of num systems. The minimum system interval is related to the hardware platform used. In the core, the constant HZ is defined to indicate the number of minimum time intervals in one second, and num * HZ is used to indicate num seconds. The system calls the function at the scheduled time and deletes the subroutine from the scheduled queue. Therefore, if you want to execute the function at a certain interval, you must call add_timer again in the function. Function parameter d is the data item in timer. The following system functions may be used in the device driver:
# Include
# Define cli () _ asm _ volatile _ ("cli "::)
# Define sti () _ asm _ volatile _ ("sti "::)
These two functions enable and disable interrupt.
# Include
Void memcpy_fromfs (void * to, const void * from, unsigned long n );
Void memcpy_tofs (void * to, const void * from, unsigned long n );
When a user program calls read and write, because the running status of the process changes from user State to core state, the address space also changes to the core address space. The buf parameter in read and write points to the private address space of the user program, so it cannot be accessed directly. You must use the above two system functions to access the private address space of the user program. Memcpy_fromfs is replicated from the user program address space to the core address space, while memcpy_tofs is vice versa. The to parameter is the destination pointer for replication, from is the source pointer, and n is the number of bytes to be copied. In the device driver, you can call printk to print some debugging information. Its usage is similar to that of printf. The information printed by printk is not only displayed on the screen, but also recorded in the file syslog.
Implementation in LINUX
In LINUX, in addition to directly modifying the source code of the system core and adding the device driver to the core, you can also use the device driver as a module that can be loaded, the system administrator dynamically loads it to make it a core part. The system administrator can also dynamically uninstall the loaded modules.
In LINUX, the module can be written in c language and compiled into the target file using gcc (no link exists as a *. o file). Therefore, the-c parameter must be added to the gcc command line. During compilation, you should add the following parameter to the gcc command line:-d1_kernel _-DMODULE. Since gcc only allows one input file without link, all parts of a module must be implemented in one file. Compiled module *. o is stored in/lib/modules/xxxx/misc (xxxx indicates the core version, for example,/lib/modules/2.0.30/misc when the core version is 2.0.30 ), use depmod-a to make this module a load-able module. The module is loaded with the insmod command, the rmmod command is used to uninstall the module, and the lsmod command can be used to view the status of all loaded modules.
When writing a module program, you must provide two functions. One is int init_module (void), which is automatically called by insmod when loading this module and is responsible for device driver initialization. If the init_module returns 0, the initialization is successful. If the return value is negative, the initialization fails. Another function is voidcleanup_module (void). It is called when the module is detached and is responsible for clearing the device driver.
After successfully registering a device driver with the system (after successful register_chrdev call), you can use the mknod command to map the device to a special file. When other programs use this device, you only need to perform operations on this special file.
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.