Linux character Device driver Framework detailed introduction _linux

Source: Internet
Author: User

Linux character device driver framework

Character device is one of the three major Linux devices (the other two are block equipment, network equipment), character equipment is the form of byte-stream communication of I/O devices, the vast majority of devices are character devices, common character devices including mice, keyboards, monitors, serial port, etc., when we execute ls-l/dev, Can see a large number of device files, C is a character device, B is a block device, network equipment does not have the corresponding device files. To write a character device driver for an external module, you need to write code as a character device in addition to the code needed to write a module.

Drive model

Linux everything is a file, then as a device file, its operating method interface encapsulated in struct file_operations, when we write a driver, we must implement the corresponding interface, so as to make this driver available, Linux kernel in a large number of "registration + callback" mechanism for the writing of the driver, the so-called registration callback, simple understanding is that when we open a device file, is actually through the VFS to find the corresponding inode, and perform the previous creation of this device file registered in the Inode in the open function, and other functions, so, in order to let us write the driver can be normal operation of the application, the first thing to do is to implement the appropriate methods, and then create the appropriate device files.

#include <linux/cdev.h>//for struct cdev #include <linux/fs.h>//for struct file #include <asm-generic/u access.h>//for copy_to_user #include <linux/errno.h>//for error number/* Prepare operation Method Set *///* struct FILE_OP  erations {struct module *owner;
  This_module//Read equipment ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);

  Write device ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);

  Mapping kernel space to user space int (*mmap) (struct file *, struct vm_area_struct *);

  Reading and writing device parameters, read device status, control device Long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
  Open device Int (*open) (struct inode *, struct file *);

  Turn off device int (*release) (struct inode *, struct file *);

  Refresh device int (*flush) (struct file *, fl_owner_t ID);

  File positioning loff_t (*llseek) (struct file *, loff_t, int);
  asynchronous notification int (*fasync) (int, struct file *, int);
  Poll mechanism unsigned int (*poll) (struct file *, struct poll_table_struct *);
。。。
};

*/ssize_t myread (struct file *filep, char __user * user_buf, size_t size, loff_t* offset) {return 0;}


struct file FoPs = {. Owner = This_module,. Read = Myread, ...};        /* Character Device Object type * * struct Cdev {//public struct module *owner; Module owner (this_module), for module Count const struct file_operations *ops; Action Method Set (Division of labor: Open, close, read/write 、...             ) dev_t Dev;         Device number (first) unsigned int count;

Number of equipment//private ...};

  static int __init chrdev_init (void) {.../* constructs Cdev device Object * * struct Cdev *cdev_alloc (void);

  /* Initialize Cdev device object/void Cdev_init (struct cdev*, const struct file_opeartions*);

  /* For the character device statically request the device number */int register_chedev_region (dev_t from, unsigned count, const char* name);

  /* For character devices dynamically request the main device number/int alloc_chedev_region (dev_t* dev, unsigned baseminor, unsigned count, const char* name); Mkdev (MA,MI)///combination of the main equipment number and the secondary equipment number MAJOR (dev)/from the dev_t data from the main device number MINOR (dev)/from the dev_t data received the secondary device number/* Register character device object Cdev to the kernel * /int Cdev_add (struct cdev*, dev_t, Unsigned);

...
}

  static void __exit chrdev_exit (void) {.../* unregister Cdev device object from kernel/void Cdev_del (struct cdev*);

  /* Unregister Cdev device object from kernel/void Cdev_put (stuct cdev *);
  /* Recovery equipment number */void Unregister_chrdev_region (dev_t from, unsigned count);

 ...
}

Implement Read,write

Linux has its own process space under each process, even if the kernel data is mapped to the user process, the PID of the data will automatically be converted to the PID of the user process, because of the existence of this mechanism, we can not directly copy the data from the kernel space and user space, but need the special copy data function/ Macro:

Long Copy_from_user (void *to, const void __user * FROM, unsigned long n)

long copy_to_user (void __user *to, const void *from, unsigned long N)

These two functions can copy the data from the kernel space to the user process space of the user process that callback the function, and with these two functions, the read,write in the kernel can realize the data copy of kernel space and user space.

ssize_t myread (struct file *filep, char __user * user_buf, size_t size, loff_t* offset)
{
  long ret = 0;
  size = size > max_kbuf? Max_kbuf:size;
  if (Copy_to_user (User_buf, kbuf,size)
    return-eagain;
  }
  return 0;
}

Implement IOCTL

IOCTL is a system call interface designed by Linux specifically for user Layer control devices, this interface has great flexibility, our device is intended to let users through which commands to implement what functions, can be implemented through it, ioctl in the action method set corresponding function pointer is long (* UNLOCKED_IOCTL) (struct file *, unsigned int, unsigned long), where the commands and parameters are entirely specified by the driver, Linux recommends defining the IOCTL () command in the way shown in the diagram

Device type serial number direction data size
8bit 8bit 2bit 13/14bit

Here, the device Type field is a magic number, which can be the number between 0~0xff, the kernel "ioctl-number.txt" gives a recommended and already used magic number (but has been a long time no one to maintain), the new device driver to define the magic number to avoid its conflict. The command code is in the direction field of 2bit, which indicates the direction of data transmission, and the possible values are: _ioc_none,_ioc_read,_ioc_write and _ioc_read|_ioc_write. The data field of the command code represents the size of the user data involved, and the width of the member depends on the architecture, usually 13 or 14 bits. The kernel also defines the _io (), _ior (), _iow (), _IOWR () 4 macros to assist in generating the command for this format. The functions of these macros are to generate the command code based on the incoming type (device Type field), the NR (Serial Number field) and the size (data length field) and the direction field shift combination of the Macro name bank. Some I/O control commands are predefined in the kernel, and if a device driver contains the same command code as a predefined command, these commands are handled by the kernel as predefined commands instead of being driven by the device, as follows: 4

    1. Fioclex: That is, file ioctl close on exec sets a special flag for the file, notifying the kernel that when the exec () system takes place, it automatically closes the open file when it occurs
    2. Fionclex: That is, file ioctl not close on Exec, clear flags set by Fioclex
    3. Fioqsize: Gets the size of a file or directory, and returns a Enotty error when used for device files
    4. Fionbio: File ioctl non-blocking I/o This call modifies the O_NONBLOCK flag in Flip->f_flags

We can include the driver design commands in a header file, record the user program and the driver's command convention, here is a simple example

//mycmd.h ... #include <asm/ioctl.h> #define CMDT ' A ' #define Karg_size
  karg{int kval;
Char Kbuf[karg_size];
}; #define Cmd_off _io (cmdt,0) #define CMD_ON _io (cmdt,1) #define Cmd_r _ior (cmdt,2,struct karg) #define CMD_W _iow (cmdt,3, 
struct Karg) ... 
CHRDEV.C
Static long demo_ioctl (struct file *filp, unsigned int cmd, unsigned long arg)
{
  static struct Karg Karg = {
    . kval = 0,
    . Kbuf = {0},
  };
  struct Karg *usr_arg;

  Switch (CMD) {case
  cmd_on:/
    * Turn on the light * * Break
    ;
  Case Cmd_off:
    * Turn off the light/break
    ;
  Case Cmd_r:
    if (_ioc_size (CMD)!= sizeof (Karg)) {
      return-einval;
    }
    Usr_arg = (struct Karg *) arg;
    if (Copy_to_user (Usr_arg, &karg, sizeof (Karg))) {
      return-eagain;
    }
    break;
  Case CMD_W:   
    if (_ioc_size (CMD)!= sizeof (Karg)) {
      return-einval;
    }
    Usr_arg = (struct Karg *) arg;
    if (Copy_from_user (&karg, Usr_arg, sizeof (Karg))) {
      return-eagain;
    }
    break;
  Default:
    ;
  };
  return 0;
}

To create a device file

Inserted device module, we can use the cat/proc/devices command to view the current system registered devices, but we have not created the appropriate device files, users can not access the device through the file. The inode for the device file should be the device number containing the device, the operation method set pointer, and so on, so that we can find the appropriate inode through the device file to access the device. There are two ways to create a device file, manually creating or automatically creating, and manually creating a device file is created using the command of the MKNOD/DEV/XXX device type master number, so you first need to use the cat/proc/ Devices view the main device number of the device and through the source to find the device's secondary device number, it should be noted that, in theory, the device file can be placed in any file folder, but put to "/dev" to comply with the Linux device management mechanism, This DEVTMPFS is a file system specifically designed to manage device files. When a device file is created, it is bound to the device specified at the time it was created, even if the device has been uninstalled, and if you want to remove the device file, you just need to delete the normal file as RM. Theoretically, the module name (lsmod), the device name (/proc/devices), the device filename (/dev) have nothing to do with it, but in principle it is recommended that the three be unified and manageable.

In addition to the use of the bad manual to create the device node, we can also use the device in the source code of the appropriate measures to make the device once it is loaded automatically create device files, the automatic creation of device files requires us to compile the kernel time or the root file system when the corresponding configuration:

Device Drivers--->
    Generic Driver Options--->
      [*]maintain a DEVTMPFS filesystem to mount At/dev
      [*] A Utomount DEVTMPFS At/dev,after The kernel mounted the Rootfs
OR

To make a boot script write to the root file system

Mount-t Sysfs None Sysfs/sys
mdev-s//udev is ok

With these preparations, you only need to export the appropriate device information to "/sys" to automatically create the device files according to our requirements. The kernel provides us with the relevant APIs

Class_create (owner,name);
struct Device *device_create_vargs (struct class *cls, struct device *parent,dev_t devt, void *drvdata,const char *fmt, Va_ List Vargs);

void Class_destroy (struct class *cls);  
void Device_destroy (struct class *cls, dev_t devt);

With these functions, we can complete the following code in the Xxx_init () and Xxx_exit () of the device to automate the creation of the delete device file

 * * Export device class information in/sys
  /cls = Class_create (this_module,dev_name);

  /* Create a set of (unit) device files in the class that the CLS points to (
  i= minor;i< (minor+cnt); i++) {
    DEVP = device_create (Cls,null,mkdev, i), NULL, "%s%d", dev_name,i);
   

/* Delete a set of (unit) device files in the class that the CLS points to (
  i= minor;i< (minor+cnt); i++) {Device_destroy (Cls,mkdev
    (major,i));

  * * Delete device class information in/sys
  /Class_destroy (CLS);       Be sure to uninstall device first and then uninstall class

With all this work done, a simple character device driver is built and is now ready to write a user program to test ^ ^

Thank you for reading, I hope to help you, thank you for your support for this site!

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.