Linux字符设备驱动传统框架

【版权申明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) 

Linux字符设备驱动传统框架

1. 驱动常见类型解释

// 函数操作集
struct file_operations {
    struct module *owner;   //THIS_MODULE
    
    //读设备
    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    //写设备
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);

    //映射内核空间到用户空间
    int (*mmap) (struct file *, struct vm_area_struct *);

    //读写设备参数、读设备状态、控制设备
    long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);

    //打开设备
    int (*open) (struct inode *, struct file *);
    //关闭设备
    int (*release) (struct inode *, struct file *);

    //刷新设备
    int (*flush) (struct file *, fl_owner_t id);

    //文件定位
    loff_t (*llseek) (struct file *, loff_t, int);

    //异步通知
    int (*fasync) (int, struct file *, int);
    //POLL机制
    unsigned int (*poll) (struct file *, struct poll_table_struct *);

	......
};
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;	//设备数量
};

2. 驱动的注册

static int __init chrdev_init(void)
{
    ...
    /* 构造cdev设备对象 */
    struct cdev *cdev_alloc(void);

    /* 初始化cdev设备对象 */
    void cdev_init(struct cdev*, const struct file_opeartions*);

    /* 申请设备号,静态or动态*/
    /* 为字符设备静态申请第一个设备号 */
    int register_chrdev_region(dev_t from, unsigned count, const char* name);

    /* 为字符设备动态申请第一个设备号 */
    int alloc_chrdev_region(dev_t* dev, unsigned baseminor, unsigned count, const char* name);

    ma = MAJOR(dev)     //从dev_t数据中得到主设备号
    mi = MINOR(dev)     //从dev_t数据中得到次设备号
    MKDEV(ma,1) //将主设备号和次设备号组合成设备号,多用于批量创建/删除设备文件

    /* 注册字符设备对象cdev到内核 */
    int cdev_add(struct cdev* , dev_t, unsigned);
    ...
}

3. 驱动的注销

static void __exit chrdev_exit(void)
{
    ...
    /* cdev_del()、cdev_put()二选一 */
    /* 从内核注销cdev设备对象 */
    void cdev_del(struct cdev* );

    /* 从内核注销cdev设备对象 */
    void cdev_put(stuct cdev *);

    /* 回收设备号 */
    void unregister_chrdev_region(dev_t from, unsigned count);
    ...
}

4. 驱动的操作函数

ssize_t led_write(struct file *pfile, const char __user *userbuf, size_t size, loff_t *loff)
{
	// long copy_from_user(void *to, const void __user * from, unsigned long n)

	return 0;
}

ssize_t led_read(struct file *pfile, char __user *userbuf, size_t size, loff_t *loff)
{
	// long copy_to_user(void __user *to, const void *from, unsigned long n)

	return 0;
}

static struct file_operations led_fops = {
	.owner = THIS_MODULE,
	.open  = led_open,
	.read  = led_read, 
	.write = led_write
};

5. 创建驱动的设备文件, 使用udev

前置操作: (有了这些准备,只需要导出相应的设备信息到"/sys"就可以按照我们的要求自动创建设备文件) (二选一)

1.在编译内核时添加如下选项:

Device Drivers —>
   Generic Driver Options —>
      [ * ]Maintain a devtmpfs filesystem to mount at /dev
      [ * ]Automount devtmpfs at /dev,after the kernel mounted the rootfs

OR 2. 在根文件系统的启动脚本写入以下内容(/etc/init.d/SXXYYY)

mount -t sysfs none sysfs /sys
mdev -s//udev也行

class_create(owner,name);
struct device *device_create(struct class *class, struct device *parent,
			     dev_t devt, void *drvdata, const char *fmt, ...)

void class_destroy(struct class *cls);   
void device_destroy(struct class *cls, dev_t devt);

6. 下面给出完整代码, 无具体的功能

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>

#include <linux/cdev.h>
#include <linux/device.h>

static dev_t dev_num = 0;
static struct cdev *cdevice = NULL;

static struct class *sys_class = NULL;
static struct device *class_device = NULL;

int led_open(struct inode *pinode, struct file *pfile)
{

	return 0;
}

ssize_t led_write(struct file *pfile, const char __user *userbuf, size_t size, loff_t *loff)
{
	//long copy_from_user(void *to, const void __user * from, unsigned long n)

	return 0;
}

ssize_t led_read(struct file *pfile, char __user *userbuf, size_t size, loff_t *loff)
{
	// long copy_to_user(void __user *to, const void *from, unsigned long n)

	return 0;
}

static struct file_operations led_fops = {
	.owner = THIS_MODULE,
	.open  = led_open,
	.read  = led_read, 
	.write = led_write
};

static int __init led_init(void)
{
	cdevice = cdev_alloc();			// 构建cdev设备对象
	cdev_init(cdevice, &led_fops);	// 初始化cdev设备对象, 将它与操作函数集绑定起来

#if 0	// 申请设备号, 静态 or 动态.
	register_chrdev_region(dev_t from, unsigned count, const char * name)
#else
	alloc_chrdev_region(&dev_num, 0, 1, "yangbkLed");
#endif

	cdev_add(cdevice, dev_num, 1);

	sys_class = class_create(THIS_MODULE, "yangbkClass");			// 在/sys中新建设备类
	class_device = device_create(sys_class, NULL, dev_num, NULL, "yangbkDevice");	// 在sys_class指向的类中创建一组(个)设备文件
	
	return 0;
}

static void __exit led_exit(void)
{
	device_destroy(sys_class, dev_num);
	class_destroy(sys_class);


#if 0	// 向内核注销cdev设备对象
	cdev_put(cdevice);
#else
	cdev_del(cdevice);
#endif

	unregister_chrdev_region(dev_num, 1);	// 回收设备号.
}

module_init(led_init);
module_exit(led_exit);

MODULE_AUTHOR("yangbkGit");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("traditional driver model.");
MODULE_ALIAS("model");


发布了68 篇原创文章 · 获赞 22 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/MACMACip/article/details/104828526