One 、Linux Equipment classification

Linux The system is for the convenience of management , Divide devices into three basic types :

  • Character device
  • Block device
  • Network devices

Character device :

character (char) A device can be like a byte stream ( Similar documents ) The same device that was accessed , This feature is implemented by the character device driver . Character device drivers usually have to implement at least open、close、read and write System call .

Character terminal (/dev/console) And serial port (/dev/ttyS0 And similar devices ) It's a two character device , They are a good illustration of “ flow ” This abstraction .

Character devices can be accessed through file nodes , such as /dev/tty1 and /dev/lp0 etc. . The only difference between these device files and ordinary files is that the access to ordinary files can be moved back and forth , Most character devices are data channels that can only be accessed sequentially . However , There are also character devices with data area characteristics , When you access them, you can move the access location back and forth . for example framebuffer It's such a device ,app It can be used mmap or lseek Access the entire image captured .

stay /dev perform ls -l , You can see a lot of created device nodes :

Character device file ( The type is c), Device files have no file size , Instead, two numbers : The main equipment, 5 + Secondary device number 1 .

Block device :

Similar to character devices , Block devices are also available through /dev Directory file system node to access . Block device ( For example, disk ) It can hold filesystem. In most of Unix In the system , Conduct I/O During operation, the block device can only transmit one or more complete blocks at a time , And each piece contains 512 byte ( or 2 Higher power byte of data ).

Linux It can make app Read and write block devices like character devices , Allow any number of bytes of data to be passed at one time . therefore , The only difference between a block device and a character device is the way the kernel manages data , That is the software interface between kernel and driver , And these differences are transparent to users . In kernel , Compared to character drivers , The block driver has a completely different interface .

Block device file ( The type is b):

Network devices :

Any network thing needs to be formed through a network interface , A network interface is a device that can exchange data with other hosts . An interface is usually a hardware device , But it could also be a pure software device , Like loop (loopback) Interface .

The network interface is driven by the network subsystem in the kernel , Responsible for sending and receiving packets . A lot of network connections ( Especially in use TCP Protocol connection ) It's stream oriented , But network devices are designed around the transmission and reception of data packets . Network drivers don't need to know about each connection , It just processes packets .

Because it's not a stream oriented device , So map the network interface to filesystem The nodes in the ( such as /dev/tty1) More difficult .

Unix The way to access network interfaces is still to assign them a unique name ( such as eth0), But the name is filesystem There is no corresponding node in . Communication between kernel and network device driver , Completely different from the communication between the kernel and the character and block drivers , The kernel calls a set of packet related functions socket, It's also called socket .

View network device usage commands ifconfig:

Two 、 How is the character device architecture implemented ?

stay Linux Everything in my world is a document , All hardware operations to the application layer will be abstracted as file operations . We know that if the application layer wants to access hardware devices , It must call the corresponding hardware driver .Linux There are so many drivers in the kernel , How can the application layer accurately call the driver of the underlying layer ?

Here we take the character device as an example , Take a look at how applications relate to the underlying drivers . Must know the basics :

  • 1. stay Linux File system , One for each file struct inode Structure to describe , This structure records all the information of this file , for example : file type , Access rights, etc .

  • 2. stay Linux Operating system , Each driver in the application layer /dev There will be a device file corresponding to it in the directory , And the file will have the corresponding primary device number and secondary device number .

  • 3. stay Linux Operating system , Each driver is assigned a master device number , The device number of the character device is saved in the struct cdev In the structure .

 struct cdev {struct kobject kobj;struct module *owner;const struct file_operations *ops;// Interface function set struct list_head list;// Kernel list dev_t dev;    // Device number unsigned int count;// The number of secondary equipment numbers };

  • 4. stay Linux Operating system , Every time you open a file ,Linux Operating system in VFS Each layer will be assigned a struct file Structure to describe the open file . This structure is used to maintain file open permissions 、 File pointer offset value 、 Private memory address and other information .

Be careful :

Often we think that struct inode Describes the static information of the file , That is, the information rarely changes . and struct file It describes dynamic information , That is, in the operation of the file ,struct file The information in it often changes . Typically struct file Inside the structure f_pos( Record the displacement of the current file ), Every time I read and write a normal file f_ops The value of will change .

The relationship between these structures is shown in the figure below :

From the picture above, we can know , If you want to access the underlying device , You have to open the corresponding device file . That is, in the process of opening ,Linux The kernel associates the application layer with the corresponding driver .

  • 1. When open Function to open a device file , According to the device file corresponding to struct inode Structure description information , You can know the type of device to be operated next ( Character device or block device ). There will also be a struct file Structure .

  • 2. according to struct inode The equipment number recorded in the structure , You can find the corresponding driver . Take the character device as an example . stay Linux Each character device in the operating system has a struct cdev Structure . This structure describes all the information about the character device , One of the most important is the operation function interface of character device .

  • 3. find struct cdev After structure ,Linux The kernel will struct cdev For the first time, the memory space of the structure is recorded in struct inode Structure of the i_cdev Among members . take struct cdev The address of the function operation interface recorded in the structure is recorded in struct file Structure of the f_op Among members .

  • 4. Task to complete ,VFS Layer returns a file descriptor to the application layer (fd). This fd Is and struct file Structure corresponding to . Next, the upper application can go through fd To find the strut file, And then in struct file Found the function interface to operate the character device .

3、 ... and 、 Character driven correlation function analysis

 * cdev_init() - initialize a cdev structure
 * @cdev: the structure to initialize
 * @fops: the file_operations for this device
 * Initializes @cdev, remembering @fops, making it ready to add to the
 * system with cdev_add().
 */void cdev_init(struct cdev *cdev, const struct file_operations *fops) function :
   initialization cdev Structure
Parameters :
  @cdev cdev Structure address
  @fops  The function interface address of the operating character device
Return value :

 * register_chrdev_region() - register a range of device numbers
 * @from: the first in the desired range of device numbers; must include
 *        the major number.
 * @count: the number of consecutive device numbers required
 * @name: the name of the device or driver.
 * Return value is zero on success, a negative error code on failure.
 */                                              int register_chrdev_region(dev_t from, unsigned count, const char *name) function :
   Register a scope () Your device number
Parameters :
  @from  Device number
  @count  Number of registered devices
  @name  Name of equipment
Return value :
   Successfully returns 0, Failure return error code ( negative )

 * cdev_add() - add a char device to the system
 * @p: the cdev structure for the device
 * @dev: the first device number for which this device is responsible
 * @count: the number of consecutive minor numbers corresponding to this
 *         device
 * cdev_add() adds the device represented by @p to the system, making it
 * live immediately.  A negative error code is returned on failure.
 */int cdev_add(struct cdev *p, dev_t dev, unsigned count) function :
   Add a character device to the operating system
Parameters :
  @p cdev Structure address
  @dev  Device number
  @count  The number of secondary equipment numbers
Return value :
   Successfully returns 0, Failure return error code ( negative )

 * cdev_del() - remove a cdev from the system
 * @p: the cdev structure to be removed
 * cdev_del() removes @p from the system, possibly freeing the structure
 * itself.
 */void cdev_del(struct cdev *p) function :
   Remove a character device from the system
Parameters :
  @p cdev Structure address
Return value :

static inline int register_chrdev(unsigned int major, const char *name,  const struct file_operations *fops) function :
   Register or assign device numbers , And register fops To cdev Structure ,
   If major>0, The function is to register the master device number ,
   If major=0, The function is to dynamically assign the master device number .
Parameters :
  @major :  The main equipment,
  @name :  Equipment name , perform  cat /proc/devices Display name
  @fops  :  Interface pointer to the file system
Return value
   If major>0    Successfully returns 0, Failure returns a negative error code
   If major=0   Master device number returned successfully , Failure returns a negative error code 

This function implements the function of cdev Encapsulation of initialization and registration of , So you don't need to do it yourself after calling this function cdev 了 .

The relative logout function is unregister_chrdev

static inline void unregister_chrdev(unsigned int major, const char *name)

Four 、 How to write character device driver

Refer to the above figure , Write character device driver steps as follows :

1. Implement module loading and unloading entry function

module_init (hello_init);module_exit (hello_exit);

2. Apply for master device number

Apply for master device number ( Kernel is used to distinguish and manage different character devices )

register_chrdev_region (devno, number_of_devices, "hello");

3. Create device nodes

Create device node file ( To provide users with an interface to operate to the file –open())
There are two ways to create device nodes : Create... Manually , Functions are created automatically .
Manually create :

mknod /dev/hello c 250 0

Automatically create device nodes

Besides using mknod Command to manually create a device node , It can also be used linux Of udev、mdev Mechanism , And ours ARM Transplanted on the development board busybox Yes mdev Mechanism , So use mdev Mechanism to automatically create device nodes .

stay etc/init.d/rcS There is a sentence in the document :

echo /sbin/mdev > /proc/sys/kernel/hotplug

This command is used to create device nodes automatically .

udev It's a tool that works in user space , It can dynamically update device files according to the status of hardware devices in the system , Including the creation of device files , Delete , Authority, etc . These files are usually defined in /dev Under the table of contents , But you can also specify... In the configuration file .udev There has to be... In the kernel sysfs and tmpfs Support ,sysfs by udev Provide equipment access and uevent passageway ,tmpfs by udev Device files provide storage space .

udev Running in user mode , Not in the kernel .udev The initialization script of creates the device node when the system starts , And when you plug in a new device —— Add driver module —— stay sysfs After registering new data on ,udev Will innovate new device nodes .

Be careful ,udev It is through modifying the device files generated by the kernel , Or add alias to achieve the purpose of custom device file . however ,udev It's a user mode program , It doesn't change kernel behavior . in other words , The kernel will still create sda,sdb And other equipment documents , and udev Different devices can be distinguished according to their unique information , And generate new device files ( Or links ).

for example :

If the driver module can export its device number as a kernel parameter , stay sysfs One of the files is called uevent The file records its value .
 Insert picture description here

It can be seen from the above figure ,uevent Contains the values of the primary and secondary device numbers and the device name .

stay Linux The application layer starts a udev Program , The first time this program runs , Can traverse /sys Catalog , Look for each subdirectory's uevent file , From these uevent File to get the information about creating the device node , And then call mknod The program is in /dev Create a device node under the directory . After that ,udev Start waiting for the kernel space event. This device model thing , Let's talk about it in detail later . It can be understood in this way , stay Linux Some function interfaces are provided in the kernel , Through these function interfaces , We can be in sysfs Export the value of our device number in the file system , After exporting the value , The kernel also reports to the application layer event. here udev I know there's work to do , It received this event after , Just read event Corresponding information , Next, start creating device nodes .

How to create a device class ?

First step : Through macro class_create() Create a class Object of type ;

/* This is a #define to keep the compiler from merging different
 * instances of the __key variable */#define class_create(owner, name)    \
({            \
  static struct lock_class_key __key;  \
  __class_create(owner, name, &__key);  \
}) Parameters :
  @owner  THIS_MODULE
  @name    Class name
Return value
   You can define a struct class Pointer variable for cls Accept return value , And then through IS_ERR(cls) Judge
   Failure or not , If successful, the macro returns 0, Failure returns to non 9 value ( Can pass PTR_ERR(cls) To obtain a
   The error code returned by the failure )

stay Linux The kernel , Sort the equipment out , The same kind of equipment can be placed in the same directory , The implication of this function is to create a class , for example :
 Insert picture description here

The second step : Export our device information to user space

 * device_create - creates a device and registers it with sysfs
 * @class: pointer to the struct class that this device should be registered to
 * @parent: pointer to the parent struct device of this new device, if any
 * @devt: the dev_t for the char device to be added
 * @drvdata: the data to be added to the device for callbacks
 * @fmt: string for the device's name
 * This function can be used by char device classes.  A struct device
 * will be created in sysfs, registered to the specified class.
 * A "dev" file will be created, showing the dev_t for the device, if
 * the dev_t is not 0,0.
 * If a pointer to a parent struct device is passed in, the newly created
 * struct device will be a child of that device in sysfs.
 * The pointer to the struct device will be returned from the call.
 * Any further sysfs files that might be required can be created using this
 * pointer.
 * Returns &struct device pointer on success, or ERR_PTR() on error.
 * Note: the struct class passed to this function must have previously
 * been created with a call to class_create().
 */struct device *device_create(struct class *class, struct device *parent,   dev_t devt, void *drvdata, const char *fmt, ...)

Automatically create device node usage instances :

static struct class *cls;static struct device *test_device;
  devno = MKDEV(major,minor);
  cls = class_create(THIS_MODULE,"helloclass");
  {unregister_chrdev(major,"hello");return result;
  test_device = device_create(cls,NULL,devno,NULL,"hellodevice");
  if(IS_ERR(test_device ))
  {class_destroy(cls);unregister_chrdev(major,"hello");return result;

4 Realization file_operations

static const struct file_operations fifo_operations = {.owner =   THIS_MODULE,.open =   dev_fifo_open,.read =   dev_fifo_read,.write =   dev_fifo_write,.unlocked_ioctl =   dev_fifo_unlocked_ioctl,};

open、release Corresponding to the application layer open()、close() function . The implementation is relatively simple ,

Go straight back to 0 that will do .
among read、write、unloched_ioctrl Function implementation needs to involve user space
And memory space .

stay Linux Operating system , User space and kernel space are independent of each other . In other words, kernel space can not directly access the memory address of user space , Similarly, user space can't directly access kernel space memory address .

If you want to achieve , Copy user space data to kernel space or kernel space data to user space , We have to rely on the interface provided by the kernel to complete .

1. read Interface implementation

User space –> Kernel space

Character device write The interface is defined as follows :

ssize_t (*write)(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos); Parameters :
  filp: Device files to be operated file Structure pointer
  buf: Pointer to the user space buffer to be written to the read data
  count: Number of bytes of data to be read
  f_pos: The location of the data file to be read , After the write is completed, it is repositioned according to the actual number of bytes written
return :
   The number of bytes actually written successfully , Failure returns a negative value 

If the operation is empty , Will make write The system call returns negative EINVAL Failure , Returns the number of bytes actually written .

To copy data from user space to kernel space, you need to use copy_from_user function , The function is defined in arch/arm/include/asm/uaccess.h in .

static inline int copy_from_user(void *to, const void __user volatile *from,unsigned long n) Parameters :
  to: Destination address ( Kernel space )
  from: source address ( User space )
  n: The number of bytes of data to be copied
return :
   Successfully returns 0, Failure returns the number of bytes of data that were not copied successfully 

You can also use get_user macro :

int get_user(data, ptr); Parameters :
  data: It can be bytes 、 Half word 、 word 、 Kernel variables of double word type
  ptr: User space memory pointer
return :
   Successfully returns 0, Failure returns to non 0

2. write Interface implementation

Kernel space –> User space

Character device read The interface is defined as follows :

ssize_t (*read)(struct file *filp, char __user *buf, size_t  count, lofft *f_pos); Parameters :
  filp:  Device files to be operated file Structure pointer
  buf:   Pointer to the user space buffer to be written to the read data
  count: Number of bytes of data to be read
  f_pos: The location of the data file to be read , After the read is completed, it is repositioned according to the actual number of bytes read
  __user : A macro is empty , It's mainly used to show the pointer variable to the programmer, which stores the address of the user space .
Return value :
   The number of bytes actually read successfully , Failure returns a negative value 

Be careful : If the operation is empty , Will make read The system call returns negative EINVAL Failure , Returns the number of bytes actually read .

User space reads data from kernel space using copy_to_user function :

 static inline int copy_to_user(void __user volatile *to, const void *from,unsigned long n) Parameters :
  to: Destination address ( User space )
  from: source address ( Kernel space )
  n: The number of bytes of data to be copied
return :
   Successfully returns 0, Failure returns the number of bytes of data that were not copied successfully 

 Insert picture description here

You can also use put_user macro :

int put_user(data, prt)
Parameters :
  data: It can be bytes 、 Half word 、 word 、 Kernel variables of double word type
  ptr: User space memory pointer
return :
   Successfully returns 0,  Failure returns to non 0

So we can achieve read、write Function , Examples are as follows :

ssize_t hello_read (struct file *filp, char *buff,   size_t count, loff_t *offp){
  ssize_t   result = 0;
  if (count   > 127) 
    count = 127;
  if   (copy_to_user (buff, data, count))
  {result =   -EFAULT;
  {printk   (KERN_INFO "wrote %d bytes\n", count);result =   count;
  return   result;}ssize_t hello_write (struct file *filp,const char *buf, size_t count, loff_t *f_pos){
  ssize_t ret   = 0;
  //printk   (KERN_INFO "Writing %d bytes\n", count);
  if (count   > 127) return -ENOMEM;
  if   (copy_from_user (data, buf, count)) {ret =   -EFAULT;
  else {data[count] = '\0';printk   (KERN_INFO"Received: %s\n", data);ret =   count;
  return ret;}

3. unlocked_ioctl Interface implementation

(1) Why to achieve xxx_ioctl ?

We have implemented the read / write interface in the driver , Through these interfaces, we can read and write the device . But many times our application layer engineers need to read and write data to the device , Also hope to be able to control the equipment . for example : For serial devices , In addition to the driver layer needs to provide read and write to the serial port , Also need to provide the baud rate of the serial port 、 Parity bit 、 The setting of the termination bit , This configuration information needs to transfer some basic data from the application layer , It's just that the data types are different .

adopt xxx_ioctl The function interface , It can provide the ability to control the equipment , Increase driver flexibility .

(2) How to achieve xxx_ioctl The function interface ?

increase xxx_ioctl The function interface , The application layer can go through ioctl system call , Operate according to different commands dev_fifo.

kernel 2.6.35 And previous versions struct file_operations Altogether 3 individual ioctl :ioctl,unlocked_ioctl and compat_ioctl Now only unlocked_ioctl and compat_ioctl 了

stay kernel 2.6.36 Has been completely removed from struct file_operations Medium ioctl A function pointer , In its place unlocked_ioctl .

· 2.6.36 The previous kernel

long (ioctl) (struct inode node ,struct file* filp, unsigned int cmd,unsigned long arg)

· 2.6.36 The kernel after that

long (*unlocked_ioctl) (struct file *filp, unsigned int cmd, unsigned long arg)

Parameters cmd: By applying functions ioctl The order passed down

Let's take a look at the application layer ioctl And the driver layer xxx_ioctl Corresponding relation :

<1> application layer ioctl Parametric analysis

int ioctl(int fd, int cmd, ...); Parameters :
@fd: Get the file descriptor when opening the device file  
@ cmd: The second parameter : Commands passed to the driver layer , When you need attention , The command of driver layer and application layer must be unified
@ The third parameter : "..." stay C In language , It's often understood as a variable parameter .
Return value
        success :0    Failure :-1, Simultaneous setting errno

Tips: :

 When we go through ioctl Call the driver layer xxx_ioctl When , There are three options :
1:  Don't pass data to xxx_ioctl 
2:  Transfer data to xxx_ioctl, Hopefully it will eventually write data to the device ( for example : Set the baud rate of the serial port )
3:  call xxxx_ioctl Want to get the hardware parameters of the device ( for example : Get the baud rate of the current serial device ) In these three cases , Sometimes you need to transfer data , Sometimes you don't need to transfer data . stay C In language , yes
Unable to implement function overload . What to do with that ? use "..." To cheat the compiler ,"..." The original meaning is to pass on
Passing multiple parameters . It means with a parameter or without a parameter .
Parameters can pass integer values , You can also pass the address of a block of memory , Kernel interface functions must be based on the actual situation
Extract the corresponding information .

<2> Driver layer xxx_ioctl Parametric analysis

long (*unlocked_ioctl) (struct file *file, unsigned int cmd, unsigned long arg); Parameters :
@file:   vfs Layer creates a structure for the process that opens the character device file , It is used to store the dynamic information of files  
@ cmd:  Commands passed in user space , You can do different things according to different orders
@ The third parameter :  Data in user space , The main data may be an address value ( User space passes an address ), It could also be a number , It may not be worth it
Return value
        success :0    Failure : Negative value with error code 

<3> How to determine the cmd Value .

This value is mainly used to distinguish the type of command , Although I just need to pass any integer value , But we try to follow the kernel specifications , Make the most of it 32bite Space , If there are no rules , How can it be square ?

Now I'll take a look , stay Linux This in the kernel cmd How it was designed !

The specific meaning is as follows :

Device type Type or magic number , Represents a class of equipment , Usually with a letter or 1 individual 8bit The number of
Serial number The number one command for this device
Fang towards The representation is from kernel space to user space , Or from user space to kernel space , Enter into : read-only , Just write , Reading and writing , other
Data size Indicates the size of the parameter that needs to be read and written

You can use a command from above 4 Component composition , Every part needs bite It's not exactly the same , Making a command requires writing different numbers in different bit fields ,Linux The system has encapsulated the macro for us , We just need to call the macro directly to design the command .

 Insert picture description here

adopt Linux The macro provided by the system , When we design commands , Just specify the device type 、 Command serial number , Data type three fields are OK .

Linux The system has been designed with a command , You can refer to Linux In the source Documentation/ioctl/ioctl-number.txt file , See which commands have been used .

<4> How to check the command ?

You can use macro _IOC_TYPE(nr) To determine the command passed down by the application type Whether it is right ;

You can use macro _IOC_DIR(nr) Get the command to read or write , And then through the macro access_ok(type,addr,size) To determine whether the memory address passed by the user layer is legal .

How to use it is as follows :

  if(_IOC_TYPE(cmd)!=DEV_FIFO_TYPE){pr_err("cmd   %u,bad magic 0x%x/0x%x.\n",cmd,_IOC_TYPE(cmd),DEV_FIFO_TYPE);return-ENOTTY;
  if(_IOC_DIR(cmd)&_IOC_READ)ret=!access_ok(VERIFY_WRITE,(void __user*)arg,_IOC_SIZE(cmd));
  else if( _IOC_DIR(cmd)&_IOC_WRITE )ret=!access_ok(VERIFY_READ,(void   __user*)arg,_IOC_SIZE(cmd));
  if(ret){pr_err("bad   access %ld.\n",ret);return-EFAULT;

5 register cdev

Well defined file_operations Structure , You can use the function cdev_init()、cdev_add() Registered character device driver .

Examples are as follows :

static struct cdev cdev;cdev_init(&cdev,&hello_ops);error = cdev_add(&cdev,devno,1);

Note that if a function is used register_chrdev(), You don't have to do this , Because this function has been implemented on cdev Encapsulation .

5、 ... and 、 example

Thousands and thousands of words , All in one picture , You can learn from the corresponding levels .
 Insert picture description here

6、 ... and 、 example

Okay , Now we can implement a complete framework of characters , Including opening 、 close 、 Reading and writing 、ioctrl、 Automatic creation of device nodes and other functions .

#include <linux/init.h>#include <linux/module.h>#include <linux/cdev.h>#include <linux/fs.h>#include <linux/device.h>#include <linux/slab.h>#include <asm/uaccess.h>#include "dev_fifo_head.h"// Specified master device number #define   MAJOR_NUM 250// Your own character device struct mycdev{int len;unsigned   char buffer[50];struct   cdev cdev;};MODULE_LICENSE("GPL");// Device number static dev_t   dev_num = {0};// overall situation gcdstruct mycdev *gcd;// Equipment class struct class *cls;// Get the data passed by the user , According to it to determine the number of registered devices static int ndevices = 1;module_param(ndevices, int, 0644);MODULE_PARM_DESC(ndevices, "The number of devices for register.\n");// Turn on the device static int dev_fifo_open(struct   inode *inode,   struct file *file){struct   mycdev *cd;  printk("dev_fifo_open   success!\n");  // use struct file File private data pointer to save struct mycdev Structure pointer cd   = container_of(inode->i_cdev,struct   mycdev,cdev);file->private_data =   cd;  return   0;}// Read device static ssize_t   dev_fifo_read(struct file *file, char   __user *ubuf,   size_t
size, loff_t *ppos){int n;int ret;char   *kbuf;struct   mycdev *mycd =   file->private_data;printk("read *ppos :   %lld\n",*ppos); if(*ppos == mycd->len)return   0;// Request large size  > buffer The number of bytes left    : Read the actual number of bytes if(size > mycd->len - *ppos)n = mycd->len - *ppos;elsen = size;printk("n =   %d\n",n);// Read data from the position of the last file location pointer kbuf   = mycd->buffer   + *ppos;// Copy data to user space ret   = copy_to_user(ubuf,kbuf, n);if(ret != 0)return   -EFAULT;// Update the value of the file location pointer *ppos += n;printk("dev_fifo_read   success!\n");return   n;}// Write device static ssize_t   dev_fifo_write(struct file *file, const char __user *ubuf,size_t size, loff_t *ppos){int n;int ret;char   *kbuf;struct   mycdev *mycd =   file->private_data;printk("write *ppos :   %lld\n",*ppos);// Has arrived buffer The tail is broken if(*ppos == sizeof(mycd->buffer))   return   -1;// Request large size  > buffer The number of bytes left ( Write as much data as you have space )if(size > sizeof(mycd->buffer) - *ppos)n = sizeof(mycd->buffer) - *ppos;elsen = size;// Write data from the position of the last file location pointer kbuf   = mycd->buffer   + *ppos;// Copy data into kernel space ret   = copy_from_user(kbuf, ubuf, n);if(ret != 0)return   -EFAULT;// Update the value of the file location pointer *ppos += n;// to update dev_fifo.lenmycd->len += n;printk("dev_fifo_write   success!\n");return   n;}//linux  Kernel in 2.6 in the future , It's abandoned ioctl Function pointer structure , In its place long   dev_fifo_unlocked_ioctl(struct file *file,   unsigned int cmd,unsigned   long arg){
  int ret = 0;
  struct mycdev *mycd   = file->private_data;
  if(_IOC_TYPE(cmd)!=DEV_FIFO_TYPE){pr_err("cmd   %u,bad magic 0x%x/0x%x.\n",cmd,_IOC_TYPE(cmd),DEV_FIFO_TYPE);return-ENOTTY;
  if(_IOC_DIR(cmd)&_IOC_READ)ret=!access_ok(VERIFY_WRITE,(void __user*)arg,_IOC_SIZE(cmd));
  else if( _IOC_DIR(cmd)&_IOC_WRITE )ret=!access_ok(VERIFY_READ,(void   __user*)arg,_IOC_SIZE(cmd));
  if(ret){pr_err("bad   access %ld.\n",ret);return-EFAULT;
  } switch(cmd){  case DEV_FIFO_CLEAN: printk("CMD:CLEAN\n");  memset(mycd->buffer, 0, sizeof(mycd->buffer)); break;  case DEV_FIFO_SETVALUE: printk("CMD:SETVALUE\n"); mycd->len = arg; break;  case DEV_FIFO_GETVALUE: printk("CMD:GETVALUE\n"); ret   = put_user(mycd->len, (int *)arg); break;  default: return   -EFAULT;}return   ret;}// Device operation function interface static const struct file_operations fifo_operations = {.owner =   THIS_MODULE,.open =   dev_fifo_open,.read =   dev_fifo_read,.write =   dev_fifo_write,.unlocked_ioctl =   dev_fifo_unlocked_ioctl,};// Module entry int __init dev_fifo_init(void){int i = 0;int n = 0;int ret;struct   device *device;
  gcd   = kzalloc(ndevices   * sizeof(struct   mycdev), GFP_KERNEL);if(!gcd){return   -ENOMEM;}// Device number  :  The main equipment, (12bit) |  Secondary device number (20bit)dev_num   = MKDEV(MAJOR_NUM, 0);// Static registration device number ret   = register_chrdev_region(dev_num,ndevices,"dev_fifo");if(ret < 0){// Static registration failed , Dynamic registration of device number  ret   =alloc_chrdev_region(&dev_num,0,ndevices,"dev_fifo");  if(ret < 0){printk("Fail to register_chrdev_region\n");goto   err_register_chrdev_region;  }}// Create device classes cls   = class_create(THIS_MODULE, "dev_fifo");if(IS_ERR(cls)){ret   = PTR_ERR(cls);goto   err_class_create;}printk("ndevices :   %d\n",ndevices);for(n = 0;n < ndevices;n   ++){  // Initialize character device   cdev_init(&gcd[n].cdev,&fifo_operations);  // Add devices to the operating system   ret   = cdev_add(&gcd[n].cdev,dev_num + n,1);  if (ret < 0)  { goto   err_cdev_add;  } // Export device information to user space (/sys/class/ Class name / Device name )  device   = device_create(cls,NULL,dev_num +n,NULL,"dev_fifo%d",n);  if(IS_ERR(device)){ ret   = PTR_ERR(device); printk("Fail to device_create\n"); goto   err_device_create;    
      }}printk("Register   dev_fito to system,ok!\n");return   0;err_device_create:// Remove the exported device information for(i = 0;i < n;i ++){   device_destroy(cls,dev_num + i);    }err_cdev_add:// Remove all that have been added for(i = 0;i < n;i ++){   cdev_del(&gcd[i].cdev);}err_class_create:unregister_chrdev_region(dev_num,   ndevices);err_register_chrdev_region:return   ret;}void __exit dev_fifo_exit(void){int i;// Delete sysfs Devices in the file system for(i = 0;i < ndevices;i   ++){device_destroy(cls,dev_num + i);    }// Delete the device class in the system class_destroy(cls);// Remove the added character device from the system for(i = 0;i < ndevices;i   ++){   cdev_del(&gcd[i].cdev);} // Release the requested device number unregister_chrdev_region(dev_num,   ndevices);return;}module_init(dev_fifo_init);module_exit(dev_fifo_exit);

Content of header file :



Makefile :

ifeq ($(KERNELRELEASE),)KERNEL_DIR ?=/lib/modules/$(shell uname -r)/build  
PWD :=$(shell pwd)modules:$(MAKE) -C $(KERNEL_DIR)   M=$(PWD) modules
.PHONY:modules clean
clean:$(MAKE) -C $(KERNEL_DIR)   M=$(PWD) cleanelseobj-m := dev_fifo.o  

Applications :

#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <string.h>#include <sys/stat.h>#include <fcntl.h>int main(int argc, const char *argv[]){int fd ;int n;char buf[1024] = "hello   word";
    fd = open("/dev/dev_fifo0",O_RDWR);if(fd < 0){perror("Fail   ot open");return   -1;}printf("open   successful ,fd = %d\n",fd);n = write(fd,buf,strlen(buf));if(n < 0){perror("Fail   to write");return   -1;}printf("write   %d bytes!\n",n);n = write(fd,buf,strlen(buf));if(n < 0){perror("Fail   to write");return   -1;}printf("write   %d bytes!\n",n);return 0;}

testing procedure :

(1) Load module

sudo insmod hello.ko

(2) Create device nodes

sudo mknod /dev/hello c 250 0

If the function of automatically creating device nodes is added to the code , Don't do this step .

(3) Test character devices

gcc test.c -o runsudo ./run