linux驱动开发-1 我的第一个驱动程序

本文详细介绍了Linux内核中的驱动工作原理,包括file_operations结构体的作用,如何构建和使用它,以及驱动模块的加载、卸载机制。同时涵盖了设备号的组成和分配方式,以及如何编译和测试字符设备驱动程序。
摘要由CSDN通过智能技术生成

个人笔记

1、系统整体工作原理

(1)应用层->API->设备驱动->硬件
(2)API:open、read、write、close等
(3)驱动源码中提供真正的open、read、write、close等函数实体

2、 file_operations 结构体

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, unsignedlong);
 long (*compat_ioctl) (struct file *, unsigned int, unsignedlong);
 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
 };

file_operations 的结构体是 Linux 内核驱动操作函数集合,在做linux驱动时,要构建此结构体,要在其中构造好函数与放在该结构体之外的函数做好联系才能够调用函数。

static struct file_operations chardevbase_fops={
    .owner = THIS_MODULE,
    .open = chrdevbase_open,
    .release = chrdevbase_release,
    .read = chrdevbase_read,
    .write = chrdevbase_write,
};

当然并不是要全部都要写,只写好需要调用的函数就可以了。

3、驱动模块的加载与卸载

Linux 驱动有两种运行方式,第一种就是将驱动编译进 Linux 内核中,这样当 Linux 内核启
动的时候就会自动运行驱动程序。第二种就是将驱动编译成模块(Linux 下模块扩展名为.ko),在
Linux 内核启动以后使用“insmod”命令加载驱动模块。平时自己调试的时候用第二种很方便,利用nfs把程序发送到板子上再运行即可。

模块有加载和卸载两种操作,我们在编写驱动的时候需要注册这两种操作函数,模块的加载和
卸载注册函数如下:
module_init(xxx_init); //注册模块加载函数
module_exit(xxx_exit); //注册模块卸载函数

static int __init chrdevbase_init(void)
{
    int ret=0;
    printk("hello\r\n");
    ret = register_chrdev(CHRDEVBASE_MAJOR,CHRDEVBASE_NAME,&chardevbase_fops);
    if(ret < 0){
        printk("error\r\n");
    }
    return 0;
}

static void __exit chrdevbase_exit(void)
{
    printk("over\r\n");
    unregister_chrdev(CHRDEVBASE_MAJOR,CHRDEVBASE_NAME);
}

//模块注册
module_init(chrdevbase_init);

//模块卸载
module_exit(chrdevbase_exit);

驱动编译完成以后扩展名为.ko,使用modprobe命令可以加载驱动模块是最简单的模块加载命令,此命令用于加载指定的.ko 模块
modprobe drv.ko

驱动模块的卸载使用命令“rmmod”即可,比如要卸载 drv.ko,使用如下命令即可:
rmmod drv.ko
也可以使用“modprobe -r”命令卸载驱动,比如要卸载 drv.ko,命令如下:
modprobe -r drv.ko
使用 modprobe 命令可以卸载掉驱动模块所依赖的其他模块,前提是这些依赖模块已经没
有被其他模块所使用,否则就不能使用 modprobe 来卸载驱动模块。所以对于模块的卸载,还是
推荐使用 rmmod 命令

对于字符设备驱动而言,当驱动模块加载成功以后需要注册字符设备,同样,卸载驱动模
块的时候也需要注销掉字符设备。字符设备的注册和注销函数原型如下所示:

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

般字符设备的注册在驱动模块的入口函数 xxx_init 中进行,字符设备的注销在驱动模块
的出口函数 xxx_exit 中进行。

4、设备号

设备号的组成:

设备号是由一个 32 位的数据类型。这 32 位的数据构成了主设备号和次设备号两部分,其中高 12 位为主设备号,低 20 位为次设备号。因此 Linux系统中主设备号范围为 0~4095。

设备号的分配:

分为动态和静态两种分配方式,静态是开发者自己查看设备号后选择没有被分配的设备来分配。动态是由系统自动来分配的。

5、编译驱动程序和测试 app

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[])
{
    int ret = 0;
    int fd = 0;
    char *filename;
    char readbuf[100];
    char writebuf[100];
    static char usrdata[]={"user data\r\n"};

    if(argc != 3){
        printf("error usage\r\n");
        return -1;
    }
    filename = argv[1];
    fd = open(filename,O_RDWR);
    if(fd < 0){
        printf("error\r\n");
        return -1;
    }

    if(atol(argv[2]) == 1){
        ret = read(fd,readbuf,50);
        if(ret < 0){
            printf("open error: %s\r\n",filename);
        }
        else{
            printf("read data:%s\r\n",readbuf);
        }
    }
    if(atol(argv[2]) == 2){
        memcpy(writebuf,usrdata,sizeof(usrdata));
        ret = write(fd,writebuf,50);
        if(ret < 0){
            printf("write error %s\r\n",filename);
        }
        else{

        }       
    }
    ret = close(fd);
    if(ret < 0){
        printf("close error %s\r\n",filename);
    }

    return 0;
}

输入./chrdevbaseApp /dev/chrdevbase 2即可执行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值