Linux驱动12 --- Linux2.6 开发方法&文件接口

发布于:2025-07-17 ⋅ 阅读:(20) ⋅ 点赞:(0)

目录

一、Linux2.6 字符设备开发

1.1 介绍

1.2 特点

1.3 相关 API

Linux2.6 的开发过程

添加头文件

申请设备号

释放设备号

Linux2.6 核心结构体初始化

Linux2.6 核心结构体添加

Linux2.6 核心结构体删除

1.4 通过指令生成设备文件

1.5 自动生成设备文件     

设备创建

类的创建

设备的销毁

类的销毁

二、文件接口

2.1 read 文件接口

read 的实现函数

2.2 write文件接口

write 的实现函数

2.3 ioctl

应用接口

内核接口

三、GPIO 子系统

设置 GPIO 为输入方向

获取 GPIO 当前的电平状态


一、Linux2.6 字符设备开发

1.1 介绍

        驱动开发方式之一

1.2 特点

        1、真正意义上的 32 位设备号

        2、设备号需要开发者申请/释放

        3、无法自动生成设备文件

        4、相对比较复杂

1.3 相关 API

        关键字:cdev

        头文件:#include <linux/cdev.h>

                      #include <linux/fs.h>

Linux2.6 的开发过程

添加头文件
申请设备号

        函数原型       

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

        函数参数

                dev:设备号存放的地址,是连续开辟的

                baseminor:申请的次设备号起始值

                                    次设备号是我们指定的,主设备号是系统分配的

                count:连续申请次设备号的个数

                       例如:第二个参数填 54,第三个参数填 2

                        那么会得到两个设备号,其中第一个次设备为 54,第二个次设备号为 55

                name:无所谓,尽可能有意义 ,申请设备号操作,这个 name 不要重复

        函数返回值

                成功返回 0,失败返回负数

释放设备号

        函数原型      

                void unregister_chrdev_region(dev_t dev, unsigned int count)

        函数参数

                dev:要释放设备号的起始值

                count:释放的个数

Linux2.6 核心结构体初始化

        函数原型

                void cdev_init(struct cdev *cdev, const struct file_operations *ops)

        函数参数

                cdev:Linux2.6 核心结构体

struct cdev
{

    const struct file_operations *ops; //文件接口核心结构体

    dev_t dev;                        //起始设备号
    
    unsigned int count;               //设备号连续的数量

    struct module *owner;             //固定填写

} 

                加粗的部分都是通过函数去填写的,我们只需要给 owner 赋值

Linux2.6 核心结构体添加

        函数原型

                int cdev_add(struct cdev *cdev, dev_t dev, unsigned int count)

        函数参数

                cdev:Linux2.6 核心结构体

                dev:起始设备号

                count:设备号的个数

        函数返回值

                成功返回 0,失败返回负数

Linux2.6 核心结构体删除

        函数原型

                void cdev_del(struct cdev *cdev)

        函数参数

                cdev:Linux2.6 核心结构体

1.4 通过指令生成设备文件

1.5 自动生成设备文件     

设备创建

        头文件:#include <linux/device.h>

        函数原型

                struct device *device_create(struct class *cls, struct device *parent, dev_t devt, void

*drvdata, const char *fmt, ...)

        函数参数

                cls:是一个类 --- 这个类只需要调用函数生成即可

                parent:设备的上一级,但是我们的设备是你我创建,没有填 NULL

                devt:设备使用的设备号

                drvdata:传递给内核的参数,不使用填 NULL

                fmt:可以填格式控制符,也可以直接填一个字符串作为文件的名字

        函数返回值

                无所谓,不用

类的创建

        函数原型               

                class_create(owner, name)

        函数参数

                struct module *owner:固定填写

                const char *name:无所谓,尽量有意义,不要重复

        函数返回值

                类的核心结构体,struct class

设备的销毁

        函数原型

                void device_destroy(struct class *cls, dev_t devt)

        函数参数

                cls:类

                devt:设备号

类的销毁

        函数原型       

                void class_destroy(struct class *cls)

        函数参数

                cls:类

二、文件接口

        对于系统层来说:read 函数可以获取一个数据,数据由内核产生,所以对于内核来说本质上是输出了一个数据

        对于系统层来说:wirt 函数可以写入一个数据,数据由内核接收,所以对于内核来说本质上是获取了一个数据

2.1 read 文件接口

        函数原型

                ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);

        函数参数

                struct file:用于多节点判断

                char __user *:应用层 read 函数存放数据空间的首地址

                size_t:获取内容的长度

                loff_t *:用在块设备,指代偏移量

read 的实现函数

        头文件:#include <linux/uaccess.h>

        函数原型

                static inline int copy_to_user(void __user volatile *to, const void *from, unsigned long n)

        函数参数

                to:数据到哪里去 --- 直接填写(*read)的第二个参数

                from:数据从哪里来 --- 内核定义一个变量,然后取地址即可

                n:数据的长度 --- 直接填写(*read)的第三个参数

        函数返回值

                必须承接,否则会报警告,而警告会被当成错误

2.2 write文件接口

        函数原型

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

        函数参数

                struct file:用于多节点判断

                const char __user *:应用层 write 函数存放数据空间的首地址

                size_t:获取内容的长度

                loff_t *:用在块设备,指代偏移量

         write 往内核中写数据,内核需要做接收

write 的实现函数

        函数原型

                static inline int copy_from_user(void *to, const void __user volatile *from, unsigned long n)

        函数参数

                to:数据到哪里去 --- 数据要到内核

                        内核就需要定义一个变量去承接 wirte 写入的数据

                from:数据从哪里来 --- (*write)第二个参数

                n:传入数据的长度

        函数返回值

                必须承接,否则会报错

2.3 ioctl

应用接口

        头文件

                #include <sys/ioctl.h>

        函数原型

                int ioctl(int fd, unsigned long request, ...)

        函数参数

                fd:文件描述符

                request:就是传递给内核的命令

                …:表示可变参数

                        可变参数具体是什么由第二个参数命令决定

        函数返回值

                成功返回 0,失败返回负数

内核接口

        函数原型

                long (*unlocked_ioctl) (struct file *file, unsigned int cmd, unsigned long n);

        函数参数

                struct file:用于多节点判断

                cmd:命令 --- 本质上是一个数字,但是数据传递无法直接传递数字

                          所以这个命令其实就是一个宏定义        

                          命令中,2 值已经被系统保留(我们没法用)

                n:数据长度

                ioctl 的应用主要在:标准文件接口

                        例如:

                                在 V4L2 摄像头开发中用于获取摄像头参数,以及设置摄像头参数,在屏幕开发中用于获取屏幕的参数

三、GPIO 子系统

设置 GPIO 为输入方向

        函数原型

                int gpio_direction_input(unsigned gpio)

        函数参数

                gpio:要设置的 GPIO 号

        函数返回值

                成功返回 0,失败返回负数

获取 GPIO 当前的电平状态

        函数原型

                int gpio_get_value(unsigned gpio)

        函数参数

                gpio:要获取电平状态的 GPIO 号

        函数返回值

                高电平,就返回 1,低电平返回 0     


网站公告

今日签到

点亮在社区的每一天
去签到