栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

Linux字符设备驱动

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Linux字符设备驱动

字符设备驱动

linux中一切皆文件,驱动程序加载后会在/dev目录下生成一个相应的文件,应用程序通过对这个名为/dev/xxx的文件进行操作即可。

应用程序运行在用户空间,linux驱动运行在内核空间。用户空间不能直接对linux内核操作,必须使用系统调用的方法来实现从用户空间陷入到内核空间,这样才能实现对底层驱动的操作。open,close write和read函数都是由C库提供的,在linux系统中,系统调用作为c库的一部分。当我们调用open函数的时候流程如下

应用程序  		 |	C库           	|   内核				|	具体驱动
open()		-> |  C库中的open()->	 | open()系统调用	 ->	|	驱动open()

file_operations

每一个系统调用,在驱动中都有一个对应的驱动函数,这个函数的结合体就是linux内核驱动操作函数的集合,内容如下

struct file_operations {
	struct module *owner;
	loff_t (*llseek) (struct file *, loff_t, int);
	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
	ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
	ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
	int (*iterate) (struct file *, struct dir_context *);
	unsigned int (*poll) (struct file *, struct poll_table_struct *);
	long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
	int (*mmap) (struct file *, struct vm_area_struct *);
	int (*mremap)(struct file *, struct vm_area_struct *);
	int (*open) (struct inode *, struct file *);
	int (*flush) (struct file *, fl_owner_t id);
	int (*release) (struct inode *, struct file *);
	int (*fsync) (struct file *, loff_t, loff_t, int datasync);
	int (*aio_fsync) (struct kiocb *, int datasync);
	int (*fasync) (int, struct file *, int);
	int (*lock) (struct file *, int, struct file_lock *);
	ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
	unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
	int (*check_flags)(int);
	int (*flock) (struct file *, int, struct file_lock *);
	ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
	ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
	int (*setlease)(struct file *, long, struct file_lock **, void **);
	long (*fallocate)(struct file *file, int mode, loff_t offset,
			  loff_t len);
	void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
	unsigned (*mmap_capabilities)(struct file *);
#endif
};
字符设备注册和注销

注册函数

static inline int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)

参数:major:主设备号

​ name:设备名字

​ fops:与设备绑定的file_operation结构体

返回值 :负数:注册失败

注销函数

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

参数:major:主设备号

​ name:设备名

返回值:NULL

输入命令 “cat /proc/devices”可以查看已经被使用的设备号。

Linux设备号

linux每个设备都有一个自己的设备号,设备号由主设备号和次设备号组成,主设备号表示某一个具体的驱动,次设备号表示使用这个驱动的各个设备。linux使用dev_t的数据类型表示设备号,定义在include/linux/types.h

typedef __u32 __kernel_dev_t;

dev_t本质是一个unsigned int类型,其中高12位为主设备号,低20位为次设备号。linux的主设备号范围为0-4095.在include/linux/kdev_t.h中提供了几个关于设备号的宏。

#define MINORBITS	20											//次设备号位数	
#define MINORMASK	((1U << MINORBITS) - 1)						//次设备号掩码

#define MAJOR(dev)	((unsigned int) ((dev) >> MINORBITS))		//获取主设备号
#define MINOR(dev)	((unsigned int) ((dev) & MINORMASK))		//获取次设备号
#define MKDEV(ma,mi)	(((ma) << MINORBITS) | (mi))			//合成设备号
设备号分配

注册函数

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)

参数:dev:保存申请的设备号。

​ baseminor:次设备号起始地址

​ count:要申请的设备号数量

​ name: 设备名字

返回值:负数:分配失败

卸载函数

void unregister_chrdev_region(dev_t from, unsigned count)

参数:from:要释放的设备号

​ count:从from开始,要释放的设备号个数

字符设备驱动举例
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define DEV_NAME "led"
#define DEV_CNT (1)

struct chr_device
{
    dev_t devno;           //设备号
    struct cdev cdev;      //字符设备
    struct class *class;   //类
    struct device *device; //设备
    struct platform_device *dev;
};

ssize_t para_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    printk(KERN_EMERG "%s %s line is %d rn", __FILE__, __FUNCTION__, __LINE__);
    return sprintf(buf, "I am what I amn");
}

ssize_t para_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
    printk(KERN_EMERG "%s %s line is %d rn", __FILE__, __FUNCTION__, __LINE__);
    printk(KERN_EMERG "%sn", buf);
    return count;
}

static DEVICE_ATTR(para, 0664, para_show, para_store);

static const struct of_device_id chrdev_match_table[] = {
    {.compatible = "rk3399,testnode"},
    {},
};

static int chr_dev_open(struct inode *inode, struct file *filp)
{
    printk(KERN_EMERG "chrdev open!!!n");
    return 0;
}

static ssize_t chr_dev_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
    printk(KERN_EMERG "chrdev write!!!n");

    return cnt;
}

static ssize_t chr_dev_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    printk(KERN_EMERG "chrdev write!!!n");
    return 0;
}

static int chrdev_release(struct inode *inode, struct file *filp)
{
    printk(KERN_EMERG "chrdev release!!!n");
    return 0;
}

static struct file_operations chr_dev_fops =
{
        .owner = THIS_MODULE,
        .open = chr_dev_open,
        .read = chr_dev_read,
        .write = chr_dev_write,
        .release = chrdev_release,
};


int chrdev_probe(struct platform_device *dev)
{
    int ret = 0;
    struct chr_device *chr_dev;
    
    printk(KERN_EMERG "chrdev probe !n");

    chr_dev = (struct chr_device *)kzalloc(sizeof(struct chr_device), GFP_KERNEL);

    if (chr_dev == NULL)
        return -1;

    ret = alloc_chrdev_region(&chr_dev->devno, 0, DEV_CNT, DEV_NAME);
    if (ret < 0)
    {
        return -1;
    }

    chr_dev->cdev.owner = THIS_MODULE;
    cdev_init(&chr_dev->cdev, &chr_dev_fops);
    ret = cdev_add(&chr_dev->cdev, chr_dev->devno, DEV_CNT);
    if (ret < 0)
    {
        printk(KERN_EMERG "fail to add cdevn");
        goto add_err;
    }

    chr_dev->class = class_create(THIS_MODULE, DEV_NAME);

    chr_dev->device = device_create(chr_dev->class, NULL, chr_dev->devno, NULL, DEV_NAME);
    if (chr_dev->device == NULL)
    {
        printk(KERN_EMERG "fail to add  chr_dev->devicen");
        goto add_err;
    }


    chr_dev->dev = dev;
    device_create_file(chr_dev->device, &dev_attr_para);
    dev_set_drvdata(&dev->dev, chr_dev);

    return 0;

add_err:
    unregister_chrdev_region(chr_dev->devno, DEV_CNT);
    return -1;
}

int chrdev_remove(struct platform_device *dev)
{
    struct chr_device *chr_dev = dev_get_drvdata(&dev->dev);
    device_destroy(chr_dev->class, chr_dev->devno);
    class_destroy(chr_dev->class);
    cdev_del(&chr_dev->cdev);
    unregister_chrdev_region(chr_dev->devno, DEV_CNT);
    printk(KERN_EMERG "chrdev remove !n");
    return 0;
}

static struct platform_driver chrdev_driver = {
    .probe = chrdev_probe,
    .remove = chrdev_remove,
    .driver = {
        .name = "test node",
        .owner = THIS_MODULE,
        .of_match_table = chrdev_match_table,
    },
};

static int __init chrdev_init(void)
{
    return platform_driver_register(&chrdev_driver);
}

static void __exit chrdev_exit(void)
{
    platform_driver_unregister(&chrdev_driver);
    return;
}

module_init(chrdev_init)
module_exit(chrdev_exit);

MODULE_LICENSE("GPL");

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/973919.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号