Objective: To use a piece of kernel memory as a character device through which the user can read and write the memory.
Summary:
1, equipment number
The main device number identifies the driver for the device, and the secondary device is used by the kernel to determine which device the device file refers to.
Obtain a direct pointer to the kernel device through the secondary device number, or use this device number as an index to the device's local array.
The device number is represented by dev_t (Linux/types.h 32 bits, where 12 digits represent the primary device number, and 20 bits represent the secondary device number).
From dev_t to obtain the main equipment number or the secondary equipment number: MAJOR (dev_t Dev); MINOR (dev_t Dev)
Get dev_t Type: Mkdev (int major, int minor) by known master number and minor number
Get one or more device numbers: int register_chrdev_region (dev_t first, unsigned int count, char *name);(static allocation, known for device numbers beforehand)
Dynamic allocation device number: int alloc_chrdev_region (dev_t *dev, unsigned int firstminor, unsigned int count, char *name); When the call succeeds, Dev saves the first number assigned.
Release device number: void Unregister_chrdev_region (dev_t, unsigned int count);
Next, the driver needs to connect the device number with the internal function.
Note: (The next step is to try to use the dynamic allocation device number )
Dynamic allocation device number disadvantage: The device node cannot be created beforehand (because the assigned device number is not guaranteed to always be consistent).
2, file operation File_operations:
These actions will be connected to the device number.
__user: For a document, indicating that the pointer is a user-space address.
Main members: Open, IOCTL, read, write, Llseek
3, struct file structure linux/fs.h document descriptor
Each time a file is opened, the kernel creates a corresponding file structure that is created on open () and passed to all functions that operate on the file (because the file structure contains a file_operations structure that contains all the functions that drive the action).
The kernel uses FILP as a pointer to execute the file structure.
Main members:
mode_t F_mode; loff_t F_pos; struct File_operations *f_pos; void Private_data;
4. Inode structure
There is only one inode for a single file, and there may be multiple file (due to fork,dup operations).
Main members:
dev_t I_rdev; For the inode structure that represents the device file, this field contains the true device number.
struct Cdev *i_cdev; The structure represents the internal structure of the kernel of a character device, and when the inode points to a character device file, the field contains a pointer to the struct CDEV structure.
Obtain the device number from the inode: Iminor (struct inode *inode); Imajor (Inode);
5, Character equipment registration/linux/cdev.h
The kernel uses the struct CDEV structure to represent a character device, so you need to allocate and register one or more of the structures before the kernel invokes the device operation.
There are two ways of registering:
New method:
1 defines the structure of the byte:
struct My_dev {
struct Cdev Cdev; If you define a pointer type here, you will need to request allocation of memory
}my_dev;
My_dev->cdev = Cdev_alloc (); This step is required if Cdev is a pointer
My_dev->cdev.ops = &my_fops;
My_dev->cdev.owner = This_module;
2) Again call Cdev_init (struct Cdev *cdev, struct file_operations);
3) Call Cdev_add (struct Cdev *dev, dev_t num, unsigned int count);
Every step above is to determine if the function call is wrong.
old approach (old interface):
Registration: int Register_chrdev (); removing: int Unregister_chrdev ()
6, each operation function realizes
1 Open int (*open) (struct inode *inode, struct file *filp)
Do the following: Pass in an inode, create a file structure
n Check for device specific errors (if not ready);
N Initialize if the device is first opened;
N update f_op pointer when necessary;
n Assign and fill in filp->private_data;
Note: The inode structure is an incoming parameter, corresponding to a specific device (which is why you should mknodde in/dev), and the Filp of the file structure is the parameter to be modified (outgoing), a file descriptor for the device, that is, an inode may have multiple file descriptors , and each descriptor needs to keep information about the inode, which is stored in the filp->private_data.
2 release int (*release) (struct inode *inode, struct file *filp)
Complete the work: Pass in an inode, release this file structure
N Releases the content that is allocated by open and stored in filp->private_data;
n Turn off the device on the last Close
Both DUP and fork create a new file structure (corresponding to the same inode) without calling open.
Note: Not every close call calls release, only the close call that really frees the device data structure calls. The number of counters that the kernel maintains its use for each file structure, whether fork or DUP, does not create new data structures (open only), they simply add counters to existing structures. Close will call release only if the file structure has a count of 0 o'clock.
3 Read ssize_t read (struct file *filp, char __user *buf, Count, loff_t *OFFP)
Complete work: Incoming file, write count byte data to user address buf, modify loff_t
Implemented by Copy_to_user ()
Return Value Description:
N equals count: The requested number of bytes was read successfully;
n The return value is positive but less than count: only part of the data is read;
N is 0: the end of the file has been reached;
N Negative: Error
4 Write ssize_t write (struct file *filp, char __user *buf, Count, OFFP);
Implemented by Copy_from_user ()
The return value is ibid.
1) Driver code Demo.h #ifndef _demo_h_ #define _demo_h_ #include <linux/ioctl.h>/*macros to help debuging*/#undef Pdebug # ifdef demo_debug #ifdef __kernel__ #define PDEBUG (FMT, args ...) printk (kern_debug "DEMO:" fmt,## args) #else #defin
E pdebug (FMT, args ...) fprintf (stderr, FMT, # # args) #endif #else #define PDEBUG (FMT, args ...) #endif #define DEMO_MAJOR 224 #define DEMO_MINOR 0 #define COMMAND1 1 #define COMMAND2 2 struct Demo_dev {struct CDEV
Cdev;
};
ssize_t demo_read (struct file *filp, char __user *buf, size_t count, loff_t *f_pos);
ssize_t demo_write (struct file *filp, const char __user *buf, size_t count, loff_t *f_pos);
loff_t demo_llseek (struct file *filp, loff_t off, int whence);
int Demo_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); #endif demo.c #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include < linux/errno.h> #include <linux/types.h> #include <linux/fcntl.h> #include <linux/cdev.h> #include <linux/version.h> #include <linux/vmalloc.h> #include <
linux/ctype.h> #include <linux/pagemap.h> #include "demo.h" Module_author ("Yangjin");
Module_license ("Dual BSD/GPL");
struct Demo_dev *demo_devices;
static unsigned char demo_inc = 0;//global variable, only one device can be opened at a time static U8 demo_buffer[256];
int Demo_open (struct inode *inode, struct file *filp) {struct Demo_dev;
if (Demo_inc > 0) Return-erestartsys;
demo_inc++;
dev = container_of (inode->i_cdev, struct demo_dev, Cdev);
Filp->private_data = Dev;
return 0;
int demo_release (struct inode *inode, struct file *filp) {demo_inc--;
return 0;
} ssize_t demo_read (struct file *filp, char __user *buf, size_t count, loff_t *f_pos) {int result; loff_t pos = *f_pos;
Pos:offset if (POS >= 256) {result = 0;
Goto out; if (Count > (256-pos))
Count = 256-pos;
pos + = count;
if (Copy_to_user (buf, Demo_buffer+*f_pos, Count)) {count =-efault;
Goto out;
} *f_pos = pos;
Out:return count; } ssize_t demo_write (struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) {ssize_t retval =-enomem
;
loff_t pos = *f_pos;
if (pos > 256) goto out;
if (Count > (256-pos)) count = 256-pos;
pos + = count;
if (Copy_from_user (Demo_buffer+*f_pos, buf, Count)) {retval =-efault;
Goto out;
} *f_pos = pos;
retval = count;
Out:return retval;
int Demo_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) {if (cmd = = COMMAND1) {
PRINTK ("IOCTL command 1 successfully\n");
return 0;
} if (cmd = = COMMAND2) {PRINTK ("IOCTL command 2 successfully\n");
return 0;
} PRINTK ("IOCTL error\n");
Return-efault;
} loff_t Demo_llseek (struct file *filp, loff_t off, int whence) {loff_t pos;
pos = filp->f_pos;
Switch (whence) {case 0: pos = off;
Break
Case 1:pos + = off;
Break
Case 2:default:return-einval; if (pos > 256) | |
(POS < 0))
Return-einval;
return filp->f_pos = pos; struct File_operations demo_fops = {. Owner = This_module,. Llseek = Demo_llseek,. Read = Demo_read,. Write = Demo_
Write,. ioctl = Demo_ioctl,. Open = Demo_open,. Release = Demo_release,};
void Demo_cleanup_module (void) {dev_t Devno = Mkdev (Demo_major, Demo_minor);
if (demo_devices) {Cdev_del (&demo_devices->cdev);
Kfree (demo_devices);
} unregister_chrdev_region (Devno, 1);
Init Module Process: 1 Registration device number Mkdev 2 registers the device driver, that is, initializing the CDEV structure (embedded in the demo_devices structure) int demo_init_module (void) {int result;
dev_t dev = 0;
dev = Mkdev (demo_major, Demo_minor);
result = Register_chrdev_region (dev, 1, "DEMO");
if (Result < 0) {PRINTK (kern_warning "Demo:can ' t get major%d\n", demo_major);
return result;
} demo_devices = Kmalloc (sizeof (struct demo_dev), gfp_kernel); if (!demo_devices){result =-enomem;
Goto fail;
memset (demo_devices, 0, sizeof (struct demo_dev));
Cdev_init (&demo_devices->cdev, &demo_fops);
Demo_devices->cdev.owner = This_module; Demo_devices->cdev.ops = &demo_fops;
Connect the created character device to the function operations in file_operations result = Cdev_add (&demo_devices->cdev, Dev, 1);
if (result) {PRINTK (kern_notice "error%d adding demo\n", result);
Goto fail;
return 0;
Fail:demo_cleanup_module ();
return result;
} module_init (Demo_init_module);
Module_exit (Demo_cleanup_module);
2) Load Drive Insmod Demo.ko, then use Lsmod or cat/proc/modules to see if the driver is installed;
3 Create device node: Mknod/dev/yangjin C 224 0; Note: The node device number here is the same as the registered device number in the driver.
4) and then write the application test code:
User Test Code:
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <linux/ rtc.h>
#include <linux/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
# Define COMMAND1 1
#define COMMAND2 2
int main ()
{
int fd;
int i;
Char data[256] = {0};
int retval;
FD = open ("/dev/yangjin", O_RDWR);
if (fd = = 1) {
perror ("Open error\n");
Exit ( -1);
}
printf ("Open/dev/yangjin successfully\n");
retval = IOCTL (FD, COMMAND1, 0);
if (retval = = 1) {
perror ("IOCTL error\n");
Exit ( -1);
}
printf ("IOCTL command 1 successfully\n");
retval = Write (FD, "Yangjin", 7);
if (retval = = 1) {
perror ("Write error\n");
Exit ( -1);
}
retval = Lseek (fd, 0, 0);
if (retval = = 1) {
perror ("Lseek error\n");
Exit ( -1);
}
retval = Read (fd, data, ten);
if (retval = = 1) {
perror ("read error\n");
Exit ( -1);
}
printf ("Read successfully:%s\n", data);
Close (FD);
return 0;
}