Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Example Analysis of character device driver of Linux Kernel device driver

2025-01-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >

Share

Shulou(Shulou.com)06/01 Report--

Editor to share with you the Linux kernel device driver of the character device driver example analysis, I hope you will learn something after reading this article, let's discuss it!

/ * character device driver * /

(1) introduction of character device driver

Character devices refer to those devices accessed by byte stream, and the drivers for character devices are called character device drivers.

This type of driver is suitable for most simple hardware devices. For example, a parallel port printer, we access it by creating a device file (such as / dev/printer) under / dev.

The user application uses the standard open function to open dev/printer, then writes data to the file with write, and reads data from it with read.

Call process:

Write (): user space-- >

Sys_write (): VFS->

Fcompresopop- > write: how to write a specific device

The so-called driver is to provide the final write function to talk to the printer directly by accessing the registers of the printer hardware.

(2) Primary equipment number and secondary equipment number

a. Introduction of equipment number

Access to character devices is done through device files within the file system. These files are located in / dev. Use "ls-l" to view.

The device is identified by the device number. The equipment number is divided into two parts, the main equipment number and the secondary equipment number.

Usually, the main setting number indicates the corresponding driver of the device, and linux allows multiple drivers to share one main setting number.

The secondary device number is used to determine the device referred to in the device file.

In the kernel, the device number is saved with the dev_t type.

The 2.4 kernel uses 16-bit device numbers (8-bit master and 8-bit slaves), while 2.6 uses 32-bit, 12-bit master and 20-bit slaves.

Access the macro that should be defined in the device number in the driver.

Get the device number:

MAJOR (dev_t dev)

MINOR (dev_t dev)

MKDEV (int major, int minor)

b. Assign and release device number

Before setting up a character device, the driver needs to obtain the device number.

Assign:

# include int register_chrdev_region (dev_t first, unsigned int count, char * name); / / first: the starting value of the device number range to be assigned (the secondary device number is always 0) / / count: the requested consecutive number range / / name: the device name associated with the number (see / proc/devices)

You can also require the kernel to allocate dynamically:

Int alloc_chrdev_region (dev_t * dev, unsigned int firstminor, unsigned int count, char * name); / / firstminor: usually 0//*dev: stores the device number returned by the kernel

Release:

Void unregister_chrdev_region (dev_t first, unsigned int count); / / called in the cleanup function of the module

The device number that the kernel has assigned can be found in Documentation/devices.txt.

c. Create a device file

When the device driver module has applied for the primary and secondary number from the system and has been loaded into the kernel through insmod, we can access the device by creating a device file under / dev.

Creation of character devices: $> mknod / dev/mychar c major minor

We often use the method of dynamically assigning the primary and secondary device number in the driver, so that it will not conflict with the existing device number in the system.

During dynamic allocation, the device files under / dev also need to be dynamically established through analysis / proc/devices.

See char_load and char_unload scripts.

(3) the basic data structure of character equipment.

The three basic data structures most closely related to character device drivers are file, file_oepeations and inode.

A.file_operations data structure

The structure contains several function pointers. These functions are the functions that actually deal with the hardware.

Functions such as open,write called in user space will eventually call the function pointed to by the pointer in it. Each open file is associated with a set of functions.

See and drive the book p54

2.6 initialization of kernel structure:

Struct file_operations my_fops = {.owner = THIS_MODULE,.llseek = my_llseek,.read = my_read,.write = my_write,.ioctl = my_ioctl,.open = my_open,.release = my_release,}

2.4 initialization of kernel structure:

Struct file_operations my_fops = {owner: THIS_MODULE,llseek: my_llseek,...}

B.file structure

File is a kernel structure that actually corresponds to the file descriptor fd returned after the user's open file.

The file structure represents an open file, and each open file in the system has a corresponding file structure in kernel space.

It is created by the kernel during open and passed to all functions operating on the file until the final close function, which releases the structure after all instances of the file are closed.

After the user-space process fork a new process, the old and new processes will share the open file descriptor FD. This operation will not create new file structures in kernel space, but will only increase the count of created file structures.

See

Mode_t fallow mode; indicates whether the file is readable or writable through FMODE_READ and FMODE_WRITE.

Loff_t frankpos; current read and write location, loff_t is 64-bit

Unsigned int files; file flags, such as O_RDONLY, O_NONBLOCK, O_SYNC. Flags are defined in the

Struct file_operations * fallow; file related operations. The kernel assigns a value to this pointer when it executes open. You can assign different f_op according to the secondary device number in the open method of the driver

Void * private; usually assigns the architecture that represents the hardware device to private.

The structure of the directory entry (dentry) corresponding to the struct dentry * fallow entry; file. The index node can be accessed at filp- > fancidentry-> d_inode.

The rest of the file has little to do with the driver.

C.inode structure

The kernel uses an inode structure to represent an actual file, which can be a normal file or a device file.

There is only one inode structure per file, while there can be multiple file structures corresponding to the file descriptor (multiple open calls). These file all point to the same inode.

Inode is defined in

Dev_t iDev; for the inode structure that represents the device file, the i_rdev contains the real device number

Struct cdev * i_cdev cdev is the internal structure of the kernel that represents the character device. When inode represents a character device, i_cdev points to struct cdev in the kernel.

Other structures have little to do with device drivers.

Get the device number from inode with the following macro:

Unsigned int iminor (struct inode * inode)

Unsigned int imajor (struct inode * inode)

(4) Registration of character devices

The struct cdev structure is used inside the kernel to represent a character device.

Our drivers need to register their cdev with the kernel. See

a. Cdev is usually added to the structure of the device

Struct scull_dev {... struct cdev cdev; / * character device structure * /}

b. Initialization

Void cdev_init (struct cdev * cdev, struct file_operations * fops)

c. Set the content in cdev

Dev- > cdev.owner = THIS_MODULE

Dev- > cdev.ops = & scull_fops

d. Add a set cdev to the kernel

Int cdev_add (struct cdev * dev, dev_t num, unsigned int count); / / num: the first number corresponding to the device / / count: the number of device numbers associated with the device, which is usually taken as 1 cdev_add / once the device is returned, the kernel thinks the device is ready for use, so the hardware initialization of the device should be completed before the call.

(5) Old-fashioned registration function

The old registration functions in 2.4 still exist in a large number of driver functions, but the new code should not use this code.

Registration:

Int register_chrdev (unsigned int major, const char * name, struct file_operations * fops); / / register 0x255 as the secondary number for the given major number, and establish a corresponding default cdev structure for each device

Log out:

Int unregister_chrdev (unsigned int major, const char * name)

(6) open and release

A.open

The initialization of the device is completed in the open method of the driver. After the open is completed, the hardware can be used, and the user program can access the device through write. The work of open is as follows:

* check the device for specific errors

* if the device is opened for the first time, initialize it (it is possible to call open multiple times)

* update the f_op pointer if necessary

* assign and fill in the data placed in filp- > private_data

Open prototype

Int (* open) (struct inode * inode, struct file * filp); / / get the dev pointer in open through inode and assign it to file- > private_data//struct scull_dev * dev;//dev = contain_of (inode- > i_cdev, struct scull_dev, cdev); / / filp- > private_data = dev / / (if dev is statically assigned, dev can be accessed directly in methods such as open or write, but if dev is dynamically allocated during module_init, its pointer can only be obtained through the method above)

B.release

Not every close call causes a call to the release method. Only when the file counter returns to 00:00 will release be called, thus releasing the dev structure)

(7) read and write

The job of read and write is to copy data from user space to the kernel, or copy kernel data to user space. Its prototype is:

Ssize_t read (struct file * filp, char _ user * buff, size_t count, loff_t * offp); ssize_t write (struct file * filp, const char _ user * buff, size_t count, loff_t * offp); / / buff: buffer pointer in user space / / offp: the location of the user's access operation in the file / / in read and write, offp should be updated after copying the data, and the actual number of bytes of the copy will be returned.

(8) Exchange data with user space

The _ _ user * buff in read and write are pointers to user space. The kernel cannot directly refer to the contents (that is, it cannot directly value buff). You need to copy the data through the functions provided by the kernel. The reasons are:

a. Under different architectures, user space pointers may not be valid when running in kernel mode.

b. The memory in user space is paged, and when the system call is executed, the memory pointed to by buff may not be in RAM at all (swapped to disk)

c. This may be an invalid or malicious pointer (such as pointing to kernel space)

For the function of exchanging data between kernel and user space, see

Such as:

1. Unsigned long copy_to_user (

Void _ _ user * to

Const void * from

Unsigned long count)

/ / copy data to user space

2. Unsigned long copy_from_user (

Void * to

Const void _ _ user * from

Unsigned long count)

/ / get data from user space

3. Int put_user (datum, ptr)

/ / copy data to user space. The number of bytes is determined by sizeof (* ptr)

/ / the return value is 0 success and a negative error.

4. Int get_user (local, ptr)

/ / get data from user space. The number of bytes is determined by sizeof (* ptr)

/ / both the return value and local are data obtained from user space

Any function that accesses user space must be sleepable, and these functions need to be reentrant.

If the return value of a function such as copy_to_user is not equal to 0, read or write should return-EFAULT to user space

The primary device number is used to indicate the device driver, and the secondary device number indicates the device that uses the driver.

In the kernel dev_t represents the device number, which consists of the primary device number and the secondary device number

# include # define MAJOR (dev) ((unsigned int) ((dev) > MINORBITS)) / / obtain the primary device number # define MINOR (dev) ((unsigned int) ((dev) & MINORMASK)) / / obtain the secondary device number # define MKDEV (ma,mi) (ma) dsiginodee-> i_rdev), which can obtain the device number of the device file.

/ / the error code is in /

/ struct file_operations /

Inode represents the node object of the file opened by the application, and file represents the file descriptor obtained by opening the file.

0 is returned for success, and error code for failure

Int (* open) (struct inode *, struct file *)

Buf points to the buffer in the user's process, and len represents the size of the buf (passed in by the user when calling read)

Off represents the operation offset of the fl file descriptor and returns the number of bytes of data actually given to the user.

Ssize_t (* read) (struct file * fl, char _ _ user * buf, size_t len, loff_t * off)

The user process drives the data

Ssize_t (* write) (struct file *, const char _ _ user *, size_t, loff_t *)

To refers to the buffer of the user process, from refers to the buffer of the data in the driver, n how many bytes, the return value is 0

Extern inline long copy_to_user (void _ user * to, const void * from, long n)

To means driven. From users... N how many bytes,.

Static inline unsigned long _ _ must_check copy_to_user (void _ user * to, constvoid * from, unsigned long n) {if (access_ok (VERIFY_WRITE, to, n)) n = _ _ copy_to_user (to, from, n); return n; / / return the number of bytes left uncopied} extern inline long copy_from_user (void * to, constvoid _ user * from, long n)

If the data interacting with the user process is 8 bytes, use put_user (XQuery p) / / x as the value and p as the address.

If you get 1, 2, 4 bytes from the user process, you can use get_user (XMagne p).

/ apply for memory dynamically and clear zero. Size is the size of the application (no more than 128K), / / flags is marked (usually GFP_KERNEL). Return address for success, NULL// GFP_ATOMIC for failure, use the system's memory emergency pool void * kmalloc (size_t size, gfp_t flags); / / clear void * kzalloc (size_t size, gfp_t flags) after application; / / clear void kfree (const void * objp) for applied memory; / / reclaim kmalloc/kzalloc void * vmalloc (unsigned long size); / / apply for large memory space void vfree (const void * addr) / / reclaim the memory of vmalloc / / the memory requested by kmalloc is physical address contiguous, vmalloc is not necessarily continuous / container_of (ptr, type, member) type includes the structure of member member, / / ptr is the address of member member of type type structure. / / this macro obtains the first address of structure variable # define container_of (ptr, type) based on the address of structure member. Member) ({\ const typeof (type *) 0)-> member) * _ _ mptr = (ptr) \ (type *) ((char *) _ mptr-offsetof (type,member));}) # define offsetof (TYPE, MEMBER) ((size_t) & (TYPE *) 0)-> MEMBER) 15 typedef struct led_dev_t {16 dev_t mydevid; 17 unsigned int * rLEDCON; 18 unsigned int * rLEDDAT; 19 struct cdev mycdev; 20} LED_DEV; LED_DEV myled / / ind- > i_cdev is the address pointing to the myled.mycdev member / / the structure variable myled header address can be obtained by container_of (ind- > i_cdev, LED_DEV, mycdev)

/ automatically create device files /

# include

1.

Struct class * cl; cl = class_create (owner, name); / / owner refers to the module to which the name class name belongs. You can view / sys/class/ class name void class_destroy (struct class * class) after it is created; / / it is used to destroy the created class.

two。 Create a device file

Struct device * device_create (struct class * cls, struct device * parent, dev_t devt, void * drvdata, const char * fmt,...) _ attribute__ ((format (printf, 5,6)); device_create (the class to which it belongs, NULL, device number, NULL, "mydev%d", 88); / / generate the device file void device_destroy (struct class * cls, dev_t devt) named mydev88 under / dev/ directory / / used to destroy the created device file / int register_chrdev (unsigned int major, const char * name, const struct file_operations * fops); / / register the device number and create the driver object void unregister_chrdev (unsigned int major, const char * name) / / Unregister the device number and delete the driver object static inline int register_chrdev (unsigned int major, const char * name, const struct file_operations * fops) {return _ register_chrdev (major, 0256, name, fops);} int _ register_chrdev (unsigned int major, unsigned int baseminor, unsigned int count, const char * name, const struct file_operations * fops) {struct char_device_struct * cd;struct cdev * cdev;int err =-ENOMEM Cd = _ register_chrdev_region (major, baseminor, count, name); if (IS_ERR (cd)) return PTR_ERR (cd); cdev = cdev_alloc (); if (! cdev) goto out2;cdev- > owner = fops- > owner;cdev- > ops = fops;kobject_set_name (& cdev- > kobj, "% s", name); err = cdev_add (cdev, MKDEV (cd- > major, baseminor), count); if (err) goto out;cd- > cdev = cdev? 0: cdev;return major > cdev;return major Out:kobject_put (& cdev- > kobj); out2:kfree (_ _ unregister_chrdev_region (cd- > major, baseminor, count); return err;} after reading this article, I believe you have some understanding of "sample analysis of character device drivers for Linux kernel device drivers". If you want to know more about it, welcome to follow the industry information channel, thank you for reading!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Servers

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report