linux字符设备驱动介绍

简介: linux字符设备驱动介绍

基本介绍


字符设备:指向以字节为单位进行读写的设备,不能随机读取设备中指向数据。字符设备是面向流的设备,例如:鼠标,键盘,串口,LED等。


字符设备驱动,用户空间和应用程序三者之间的关系:字符设备是3大类设备(字符设备,块设备,网络设备)中较简单的一类设备。如图所示,驱动中完成主要工作如下:


  • 添加和删除设备:使用struct cdev结构体来抽象一个字符设备;


  • 申请和释放设备号:通过dev_t类型的设备号(主设备号,次设备号)确定字符设备的唯一性;


  • 抽象文件操作结构体:填充文件操作结构体,实现基本的读写控制操作,为系统调用系统VFS接口,应用程序只需要在用户空间,进行识读操作即可。



驱动模型



基本流程


  • 申请设备号


  • 分配cdev


  • 创建class和device(也可以不要这一步)


  • files_operation具体实现


alloc_chrdev_region()
...
cdev_alloc()
...
cdev_init()
...
cdev_add()
...
class_create()
...
device_create()


相关数据结构介绍


cdev介绍


  • 数据结构
//<include/linux/cdev.h>
struct cdev {
  struct kobject kobj;        /*内嵌内核对象*/
  struct module *owner;   /*字符设备所在内核模块所有者对象指针,一般时THIS_MODULE*/
  const struct file_operations *ops;/*字符设备读写操作集*/
  struct list_head list;
  dev_t dev;                  /*字符设备设备号*/
  unsigned int count;     /*同一主设备号的次设备号个数*/
    ...
};


  • 相关操作
/*
 * 动态申请cdev设备对象
 */
struct cdev *cdev_alloc(void); 
/*
 * 初始化cdev成员,并建立cdev和file_operation之间的关联
 * strcut cdev - 被初始化的cdev对象
 * fops - 字符设备操作方法集
 */
 void cdev_init(struct cdev *p, const struct file_opration *fops);
/*
 * 注册cdev设备对象
 * strcut cdev - 被初始化的cdev对象
 * dev_t dev - 设备的第一个设备号
 * unsigned - 这个设备连续的次设备数量
 */
 int cdev_add(struct cdev *, dev_t, unsigned);
/*
 * 将cdev对象从系统中注销
 * strcut cdev - 被初始化的cdev对象
 */
 void cdev_del(struct cdev *));


设备号介绍


一个字符设备或块设备都有一个主设备号和一个次设备号。那种设备,用来区分同类型的设备。


  • 数据结构


/*
 * 在32位系统中dev_t是4字节,高12位表示主设备号,低20位表示次设备号
 */
 typedef u_long dev_t; 



MAJOR:从设备号中提取主设备号;


MINOR:从设备号中提取次设备号;


MKDEV:将主,次设备号拼凑为设备号


  • 相关操作


/*
 * 静态申请设备号
 * dev_t - 要申请设备号(起始)
 * unsigned - 要申请设备号数量
 * const char * - 设备名
 */
int register_chrdev_region(dev_t , unsigned , const char *);


/*
 * 动态申请设备号
 * dev_t - 要申请设备号(起始)
 * unsigned - 起始次设备号
 * unsigned - 要分配设备号数量
 * const char * - 设备名
 */
int alloc_chrdev_region(dev_t , unsigned ,unsigned,  const char *);


/*
 * 释放设备号
 * dev_t - 要释放设备号(起始)
 * unsigned - 要释放设备号数量
 */
void unregister_chrdev_region(dev_t , unsigned );


file_operation


struct file_operations {
  struct module *owner;
  ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
  ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);unsigned long, loff_t);
  long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
  int (*open) (struct inode *, struct file *);
  int (*release) (struct inode *, struct file *);
    ...
};


owner:模块拥有者,一般为THIS_MODULE


read:从设备读


write:从设备写


open:打开设备


release:关闭设备


ioctl:其他控制


简单使用


驱动代码


hello_world.c

#include <linux/vmalloc.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/major.h>
#include <linux/device.h>
#define DRIVER_NAME                      "hello_world"
typedef struct _device_info {
        int major;
        dev_t dev;
        struct cdev *cdev;
        struct device *devices;
        struct class *class;
} device_info_t;
static device_info_t *context_device = NULL;


基本操作


ssize_t hello_world_read(struct file *file, char __user *buf, size_t size, loff_t *off)                                   
{
        printk("read data to hello world\n");
        return 0;
}
ssize_t hello_world_write(struct file *file, char __user *buf, size_t size, loff_t *off)
{
        printk("write data to hello world\n");
        return 0;
}
int hello_world_open(struct inode *inode, struct file *file)
{
        printk("open hello world driver successful!\n");
        return 0;
}
int hello_world_release(struct inode *inode, struct file *file)
{
        printk(" close hello world \n");
        return 0;
}


构建和销毁字符设备


/* fops*/
static struct file_operations hello_world_fops = {
        .owner = THIS_MODULE,                
        .read = hello_world_read,
        .write = hello_world_write,
        .mmap = NULL,   /* reserved for future use */
        .open = hello_world_open,
        .release = hello_world_release,
};
/* 创建字符设备 */
static inline int create_hello_world_device(void)
{
        int ret;
        device_info_t *device = context_device;
        ret = alloc_chrdev_region(&device->dev, 0, 1, DRIVER_NAME);
        if (ret) {
                printk("ERROR: alloc_chrdev_region failed!\n");
                goto err_chrdev;
        }
        device->major = MAJOR(device->dev);
        device->cdev = cdev_alloc();
        if (!device->cdev) {
                printk("ERROR: cdev_alloc failed!\n");
                goto err_cdev;
        }
        device->cdev->ops   = &hello_world_fops;
        device->cdev->owner = THIS_MODULE;
        cdev_init(device->cdev, &hello_world_fops);
        ret = cdev_add(device->cdev, device->dev, 1);
        if (ret) {     
                printk("ERROR: cdev_add failed!\n");
                goto err_cdev_add;
        }
         device->class = class_create(THIS_MODULE, DRIVER_NAME);                                                                                                            
        if (!device->class) {
                printk("ERROR: class_create failed!\n");
                goto err_devclass;
        }
        device->devices = device_create(device->class, NULL, device->dev, NULL, DRIVER_NAME);
        if (!device->devices) {
                printk("ERROR: device_create failed!\n");
                goto error_dev;
        }
        return 0;
error_dev:
        class_destroy(device->class);
err_devclass:
        cdev_del(device->cdev);
err_cdev_add:
        unregister_chrdev(device->major, DRIVER_NAME);
err_cdev:
        unregister_chrdev_region(device->dev, 1);
err_chrdev:
        return -1;
}
 /* 注销字符设备*/
static inline int destory_hello_world_device(void)
{         
        if (context_device) {
                device_info_t *device = context_device;
                device_destroy(device->class, device->dev);
                class_destroy(device->class);
                cdev_del(device->cdev);
                unregister_chrdev(device->major, DRIVER_NAME);
                unregister_chrdev_region(device->dev, 1);
        }
        return 0;
}


驱动加载卸载


static int __init hello_world_init(void)                  
{
        int ret;
        /* alloc context_device */
        context_device = vzalloc(sizeof(device_info_t));
        if (!context_device) {
                printk("ERROR: alloc memory for context_device failed!\n");
                goto error_context;
        }
        ret = create_hello_world_device();
        if (ret) {
                printk("ERROR: create hello_world device error\n");
                goto error_create;
        }
        printk("hello world driver Register ok!\n");
        return 0;
error_context:
error_create:
        return -1;
}
static void __exit hello_world_exit(void)
{
        destory_hello_world_device();
}
module_init(hello_world_init);               
module_exit(hello_world_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("hello world driver test");


Makefile


CROSS_COMPILE ?= your compile 
KERNEL_DIR = you kernel dir 
KDIR := ${KERNEL_DIR} 
MODULE_NAME := hello_world_drv 
all: modules 
.PHONY: modules clean 
$(MODULE_NAME)-objs += hello_world.o 
obj-m += $(MODULE_NAME).o 
modules:
     @$(MAKE) -C $(KDIR) M=$(shell pwd) $@ 
clean: 
     @rm -rf *.o *~ .depend .*.cmd *.mod.c .tmp_versions *.ko *.symvers modules.order


测试


  • insmod hello_world_drv.ko



  • ls / dev / hello world



  • 应用程序测试:应用层通过open函数打开/ dev / helle_world,然后通过调用read / write函数即可,这里暂不过多介绍。


总结


本文主要介绍了,字符设备驱动的编写框架,具体细节实现临时暂时没有填充,从而让读者熟悉字符设备驱动的编写。

相关文章
|
2月前
|
Linux 开发工具 Perl
在Linux中,有一个文件,如何删除包含“www“字样的字符?
在Linux中,如果你想删除一个文件中包含特定字样(如“www”)的所有字符或行,你可以使用多种文本处理工具来实现。以下是一些常见的方法:
44 5
|
3月前
|
Linux 开发工具 Perl
Linux命令替换目录下所有文件里有"\n"的字符为""如何操作?
【10月更文挑战第20天】Linux命令替换目录下所有文件里有"\n"的字符为""如何操作?
53 4
|
5月前
|
NoSQL Unix Linux
Linux 设备驱动程序(一)(上)
Linux 设备驱动程序(一)
172 62
|
5月前
|
Java Linux API
Linux设备驱动开发详解2
Linux设备驱动开发详解
63 6
|
5月前
|
消息中间件 算法 Unix
Linux设备驱动开发详解1
Linux设备驱动开发详解
66 5
|
5月前
|
存储 缓存 Unix
Linux 设备驱动程序(三)(上)
Linux 设备驱动程序(三)
59 3
|
5月前
|
缓存 安全 Linux
Linux 设备驱动程序(一)((下)
Linux 设备驱动程序(一)
51 3
|
5月前
|
安全 数据管理 Linux
Linux 设备驱动程序(一)(中)
Linux 设备驱动程序(一)
40 2
|
5月前
|
Ubuntu NoSQL Linux
Linux内核和驱动
Linux内核和驱动
43 2
|
5月前
|
Linux
Linux 设备驱动程序(四)
Linux 设备驱动程序(四)
38 1