/********************
* Character device driver
********************/
(1) Character device driver Introduction
A character device is a device that is accessed by a byte stream and is called a character device driver for a character device.
This type of drive is suitable for most simple hardware devices. For example, a port printer, we access it by creating a device file (such as/dev/printer) under/dev.
The user application opens the Dev/printer with the standard open function, writes the data to the file with write, and reads the data from inside with read.
Call Process:
Write (): User space-->
Sys_write (): VFS-->
F_op->write: Writing methods for specific devices
The so-called driver, is to provide the final write function, by accessing the printer hardware registers directly and the printer dialog
(2) Main equipment number and minor equipment number
A. Introduction to the equipment number
Access to character devices is done through device files within the file system. These files are located in/dev. View with "Ls-l".
The device is identified by the device number. The equipment number is divided into two parts, the main equipment number and the minor equipment number.
Typically, the main device number identifies the device's corresponding driver, and Linux allows multiple drivers to share a single main device number;
The secondary device number is used to determine the device that the device file refers to.
In the kernel, use the dev_t type <linux/types.h> Save the device number.
The 2.4 kernel uses a 16-bit device number (8 main, 8 bits from), and 2.6 uses 32 bits, 12 main, 20 bits from.
Access to the device number in the drive should use the macros defined in <linux/kdev_t.h>.
Get device number:
MAJOR (dev_t Dev)
MINOR (dev_t Dev)
Mkdev (int major, int minor)
B. Allocation and release of device numbers
Before building a character device, the driver needs to get the device number first.
Distribution:
#include <linux/fs.h>
int register_chrdev_region (dev_t, unsigned int count, char *name);
First: The starting value of the range of device numbers to assign (the minor number is standing at 0)
Count: Range of consecutive numbers requested
Name: The device names associated with the number (see/proc/devices)
You can also require the kernel to be dynamically allocated:
int alloc_chrdev_region (dev_t *dev, unsigned int firstminor, unsigned int count, char *name);
Firstminor: usually 0
*dev: Storing the device number returned by the kernel
Release:
void Unregister_chrdev_region (dev_t, unsigned int count);
Called in the module's purge function
In Documentation/devices.txt, you can find the device number that the kernel has already allocated.
C. Establishment of equipment files
When the device driver module has applied to the system for the main device number and the minor number, and has been loaded into the kernel via insmod, we can access the device by creating a device file under/dev.
The creation of character devices:
$>mknod/dev/mychar C Major Minor
We often use dynamic allocation of primary and secondary devices in the drive, which will not conflict with the existing device number in the system.
When dynamically allocated, the device files under/dev need to be dynamically established through the analysis/proc/devices.
See Char_load and Char_unload scripts.
(3) Basic data structure of character device
The 3 basic data structures that are most closely related to character device drivers are:
File, File_oepeations and Inode
A.file_operations Data structure
The structure contains a number of function pointers. These functions are functions that actually interact with the hardware.
Functions such as the open,write called by the user space will eventually call the function that the pointer points to. Each open file is associated with a set of functions.
See the p54 of <linux/fs.h> and driver book
2.6 Initialization of the 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 the kernel structure:
struct File_operations my_fops = {
Owner:this_module,
Llseek:my_llseek,
...
}
B.file structure <linux/fs.h>
File is a kernel structure that actually corresponds to the file descriptor FD that is returned after the user open file.
The file structure represents an open document, and each open file in the system has a corresponding file structure in the kernel space.
It is created by the kernel on open and passed to all functions that operate on the file, until the last Close function releases the structure after all instances of the file have been closed.
After the user space process fork a new process, the old and new process shares the open file descriptor fd, which does not create a new file structure in kernel space, but only increases the count of the created file structure.
See <linux/fs.h>
mode_t F_mode;
Indicates whether the file is readable or writable through Fmode_read and Fmode_write.
loff_t F_pos;
Current read/write position, loff_t to 64-bit
unsigned int f_flags;
File flags, such as O_rdonly, O_nonblock, O_sync. Logos are defined in <linux/fcntl.h>
struct File_operations *f_op;
File-related operations. The kernel assigns a value to this pointer when it executes open. You can assign different f_op to the secondary device number in the driven open method.
void *private;
The structure that represents the hardware device is usually assigned to private.
struct Dentry *f_dentry;
The directory entry (DENTRY) structure that corresponds to the file. Index nodes can be accessed through Filp->f_dentry->d_inode.
The other contents and drivers in file are not related.
C.inode structure
The kernel uses the INODE structure to represent an actual file, which can be either a normal file or a device file.
Each file has only one inode structure, and the file structure corresponding to the filename can have multiple (open calls multiple times). These file all point to the same inode.
Inode definition in <linux/fs.h>
dev_t I_rdev;
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 that represents the kernel of a character device. When the inode represents a character device, I_cdev points to the struct cdev in the kernel.
Other structural and device-driven relationships are not significant.
Obtain the device number from the Inode with the following macro:
unsigned int iminor (struct inode *inode)
unsigned int imajor (struct inode *inode)
(4) Registration of character devices
A struct CDEV structure is used inside the kernel to represent a character device.
Our drivers are going to register their cdev in the kernel. See <linux/cdev.h>
A. Usually add cdev to the structure of the equipment
struct scull_dev{
...
struct Cdev Cdev; /* character Device structure * *
}
B. Initialization
void Cdev_init (struct cdev *cdev struct file_operations)
C. Setting the contents of the Cdev
Dev->cdev.owner = This_module;
Dev->cdev.ops = &scull_fops;
D. Add a set of 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: Number of device numbers associated with the device, often taking 1
Once the Cdev_add is returned, the kernel thinks the device is ready to use, so the hardware initialization of the device is completed before the call is made.
(5) Old-fashioned registration function
The old registration function in 2.4 still exists heavily in the driver function, but the new code should not use the code.
Registered:
int Register_chrdev (unsigned int major,
const Char *name,
struct file_operations *fops);
Registers the 0~255 as the secondary device number for the given main device number, and establishes a corresponding default Cdev structure for each device
Cancellation:
int Unregister_chrdev (unsigned int major,
const char *name);
(6) Open and release
A.open
In the drive open method to complete the initialization of the device, the open is complete, the hardware can be used, user programs can access the device through write, open work:
* Check the device for specific errors
* Initialize the device if it is first opened (it is possible to call open more than once)
* Update the F_OP pointer if necessary
* Allocate and fill in the data placed in the Filp->private_data
Open prototype;
Int (*open) (struct inode *inode, struct file *filp);
Get the dev pointer through the inode in open 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 allocated, dev can be accessed directly in methods such as open or write, but if Dev is dynamically allocated at module_init time, it can only be obtained by the method above)
B.release
Not every close call causes a call to the release method, which is invoked only when the counter of file is zero, releasing the dev structure.
(7) Read and write
The work 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 for user space
OFFP: The location of the user's access operation in the file
In read and write, after the data is copied, the OFFP should be updated and the actual number of bytes copied will be returned.
(8) Exchange data with user space
The __user *buff in read and write is a pointer to user space, and the kernel cannot directly reference its contents (that is, it cannot directly perform a value operation on buff) and needs to copy the data through the functions provided by the kernel.
The reasons are:
A. Under different architectures, the pointer to user space may be invalid when running in kernel mode.
B. User-space memory is paging, and the memory that buff points to may not be in RAM at all (swapped to disk) when the system call executes
C. This may be an invalid or malicious pointer (such as pointing to kernel space)
Functions of data exchange between kernel and user space see <asm/uaccess.h>
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);
Getting 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 successful and is a negative error.
4. int Get_user (local, PTR);
Get data from user space. The number of bytes is determined by sizeof (*PTR)
The return value and the local are all data obtained from user space
Any function that accesses a user's space must be restful, and these functions need to be reentrant.
Copy_to_user function If the return value is not equal to 0, read or write should return to the user space-efault
----------------------------------------------------------------------------------------------------------- ----------------------
The main device number is used to indicate the device driver, and the secondary device number indicates the device using the drive
The kernel dev_t represents the device number, which consists of the main unit number and the minor unit number.
#include <linux/kdev_t.h>
#define MAJOR (Dev) (unsigned int) (dev) >> minorbits)////Get the main unit number according to the device number
#define MINOR (Dev) (unsigned int) (dev) & minormask)//Get the secondary device number
#define MKDEV (Ma,mi) ((MA) << minorbits) | (MI)////////////////////*
#include <linux/fs.h>
Static: Request a specified device number, from the device number, count refers to the number of devices using the drive (the secondary device number), device name
int Register_chrdev_region (dev_t from, unsigned count, const char *name);
Name cannot exceed 64 bytes in length
Dynamic request device number, the kernel is not used by the core allocation of the main device number, allocated equipment exist dev, baseminor refers to the number of units starting from how many, count refers to the number of devices, name device name
int alloc_chrdev_region (dev_t *dev, unsigned baseminor, unsigned count,
const char *name)
Release device number, from refers to device number, count refers to number of devices
void Unregister_chrdev_region (dev_t from, unsigned count)
Cat/proc/devices to view device usage
Documentations/devices.txt of the kernel source can see the static distribution of the device number
The kernel uses struct Cdev to describe a character device driver
#include <linux/cdev.h>
struct Cdev {
struct Kobject kobj; Kernel for managing character device drivers
struct module *owner; Typically set to This_module to prevent drivers from unloading drive modules when in use
const struct File_operations *ops; How to Operate (VFS)
struct List_head list; Because multiple devices can use the same driver, use a linked list to record
dev_t Dev; Equipment number
unsigned int count; Number of devices
};
Character device driver//////////
1. Application Equipment Number
2. Define a Cdev device-driven object
struct Cdev Mycdev;
Defines a File_operations file action object
struct File_operations fops = {
. Owner = This_module,
. Read = Read function
....
};
3. Associating the FoPs object with the Mycdev
Cdev_init (&mycdev, &fops); Mycdev.ops = &fops;
Mycdev.owner = This_module;
4. Add the device driver to the kernel and specify the device number for the drive
Cdev_add (&mycdev, equipment number, number of secondary equipment number);
5. Uninstall the module, to remove the device driver from the core, and the device number back to register
Cdev_del (&mycdev);
To create a device file
mknod/dev/device filename C Primary equipment number of secondary equipment
The Inode node object describes a file/device file, including information such as permissions, device numbers, etc.
struct Inode {
...
dev_t I_rdev; Device number corresponding to the device file
struct Cdev *i_cdev; Address to the corresponding device-driven object
...
};
The file object descriptor, created when the file is opened, is destroyed when it is closed
struct File {
...
const struct File_operations *f_op; The address of the corresponding file action object
unsigned int f_flags; Flag for File Open
fmode_t F_mode; Permissions
loff_t F_pos; Offset of File descriptor
struct Fown_struct f_owner; Which process belongs to
unsigned int f_uid, f_gid;
void *private_data; For driver programmers to use
...
};
Member of the F_path.dentry->d_inode->i_rdev file can get the device number of the device files
Error code in <asm/errno.h>////
struct File_operations////
The inode represents the node object of the file that the application opens, and file represents the filename obtained by opening the file.
Successfully returned 0, failure returned error code
Int (*open) (struct inode *, struct file *);
BUF points to a buffer in the user's process, Len represents the size of the BUF (the user calls read newsletters in)
Off indicates the operation offset of the FL file descriptor, and the return value is the number of bytes of data that are actually given to the user.
ssize_t (*read) (struct file *fl, char __user *buf, size_t len, loff_t *off);
User process to drive data
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
////////////////
To refers to the buffer of the user process, the buffer in which the data is loaded from the drive, n how many bytes, and the return value is 0
extern inline long copy_to_user (void __user *to, const void *from, Long N)
To refers to the driver ... from user ... n how many bytes, ....
Static inline unsigned long __must_check copy_to_user (void __user *to, const
void *from, unsigned long N)
{
if (ACCESS_OK (Verify_write, to, N))
n = __copy_to_user (to, from, N);
return n; The return value is the number of bytes left not copied
}
///////
extern inline long copy_from_user (void *to, const void __user *from, long N)
If the data that interacts with the user process is 1,2,4,8 bytes, the available
Put_user (x,p)//x is the value, p is the address
If you get 1,2,4 bytes from a user process, you can
Get_user (X,P)
///////////
Dynamic application memory, and clear 0. Size is the size of the application (not exceeding 128K),
Flags are flags (often gfp_kernel). Successfully returned address, failed return null
Gfp_atomic, using the system's memory emergency pool
void *kmalloc (size_t size, gfp_t flags);//After application to clear zero memory
void *kzalloc (size_t size, gfp_t flags); The requested memory has been zeroed out
void Kfree (const void *OBJP); Reclaim Kmalloc/kzalloc Memory
void *vmalloc (unsigned long size); Request Large Memory space
void Vfree (const void *ADDR); Reclaim Vmalloc Memory
Kmalloc Application of memory is a continuous physical address, vmalloc is not necessarily continuous
The Container_of (PTR, type, member) type consists of the members of the structure body,
PTR is the address of member members of the type struct.
This macro obtains the first address of a struct variable based on the address of the member of the struct body
#define CONTAINER_OF (PTR, type, member) ({\
Const typeof ((type *) 0)->member) *__mptr = (PTR); \
(Type *) ((char *) __mptr-offsetof (Type,member));}
#define OFFSETOF (Type, member) ((size_t) & ((type *) 0)->member)
typedef struct LED_DEV_T {
dev_t Mydevid;
unsigned int *rledcon;
unsigned int *rleddat;
struct Cdev Mycdev;
}led_dev;
Led_dev myled;
Ind->i_cdev is the address that points to the Myled.mycdev member
The first address of the structural variable myled can be obtained by container_of (Ind->i_cdev, Led_dev, Mycdev);
Automatically create device files////
#include <linux/device.h>
1. struct class *cl;
CL = class_create (owner, name); Owner refers to which module, name Class name
You can view the/sys/class/class name when you create it
void Class_destroy (struct class *cls); Used to destroy the created class
2. Create a Device file
struct Device *device_create (struct class *cls, struct device,
dev_t devt, void *drvdata,
const char *FMT, ...)
__ATTRIBUTE__ (Format (printf, 5, 6));
Device_create (class, NULL, device number, NULL, "mydev%d", 88); produce a device file named mydev88 in the/dev/directory
void Device_destroy (struct class *cls, dev_t devt); Used to destroy the created device file
////////
int Register_chrdev (unsigned int major, const char *name,
const struct file_operations *fops); Registering a device number and creating a driver object
void Unregister_chrdev (unsigned int major, const char *name); Anti-registration device number and delete drive object
static inline int Register_chrdev (unsigned int major, const char *name,
const struct File_operations *fops)
{
Return __register_chrdev (major, 0, 256, 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;
Return major? 0:cd->major;
Out
Kobject_put (&cdev->kobj);
Out2:
Kfree (__unregister_chrdev_region (Cd->major, Baseminor, count));
return err;
}