目录

驱动字符设备开发之Linux2.6开发

驱动字符设备开发之Linux2.6开发


1.Linux2.6开发特点:

  1. 标准化。可以清晰明了地了解开发代码的操作。
  2. 设备范围广泛。不限制设备号,开发时需要自己申请设备号。
  3. 注册完毕后不会生成设备文件。需要用指令生成或内核其他接口生成。

2.Linux2.6开发流程

驱动注册->分配cdev设备号(静态分配register_chrdev_region/动态分配alloc_chrdev_region)->初始化cdev(cdev_init)->注册cdev(cdev_add)->初始化硬件->设备操作函数的实现(open/read/write/close)->驱动注销(cdev_del/unregister_chrdev_region)

3.Linux2.6开发的相关接口函数

·alloc_chrdev_region()申请设备号函数

函数功能:向内核申请一个可以使用的设备号(连续申请)

函数头文件:<linux/fs.h>

函数原型:

int alloc_chrdev_region(
    dev_t * devnum, 
    unsigned base_minor, 
    unsigned count, 
    const char * labelname
);

函数参数:

devnum:设备号。需要提供一个这样类型的空间。函数申请设备号成功,就会把申请得到的首设备号存入该空间。

base_minor:起始次设备号。跟系统指定准备从哪个次设备号开始使用。

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

label_name:给它们的名字

函数返回值:成功返回0

·unregister_chrdev_region()释放设备号函数

函数功能:释放申请得到的设备号

函数头文件:<linux/fs.h>

函数原型:

void unregister_chrdev_region(
    dev_t devnum, 
    unsigned count
);

函数参数:

devnum:申请得到的首设备号

count:连续释放的数量

函数返回值:无

·初始化cdev结构体

函数功能:初始化一个cdev结构体

函数头文件:<linux/cdev.h>

函数原型:

void cdev_init(
    struct cdev * cdevp, 
    const struct file_operations * fops
);

函数参数:

cdevp:给函数一个cdev结构体空间。

fops:提供一个ops结构体,并至少填入其中的owner、open、release。

函数返回值:无

·注册cdev设备

函数功能:向内核注册一个cdev结构体。可以一次注册多个设备。

函数原型:

int cdev_add(
    struct cdev * cdevp, 
    dev_t dev_num, 
    unsigned count
);

函数参数:

cdevp:填入经cdev_init初始化完毕的结构体

dev_num:经过alloc_chrdev_region申请的设备号

count:要注册的设备数量

函数返回值:成功返回0

·删除注册的cdev结构体

函数功能:从内核取消一个cdev的结构体的注册

函数原型:

void cdev_del(struct cdev * cdevp);

函数参数:cdevp:传入cdev结构体,即可自动取消全部设备的注册

函数返回值:无

4.生成设备文件的指令和接口

1.手动创建设备文件:

mknod  /dev/xxx c 主设备号 次设备号

2.利用函数生成:生成类结构体->利用类结构体生成设备文件

·生成类结构体函数
struct class * class_create(owner , name);

其中,owner固定填写,name可任意命名。

·摧毁类结构体函数
void class_destroy(struct class *cls);
·生成设备文件的函数
struct device *device_create(
    struct class *cls, //类结构体
    struct device *parent,//父设备
    dev_t devt, //设备号
    void *drvdata, //设备的私有数据
    const char *fmt, ...//格式化设备名
); 
·销毁设备文件的函数
void device_destroy(
    struct class *cls, 
    dev_t devt
);

5.基于Linux2.6开发驱动led灯闪烁

驱动开发程序

此处led1的引脚为gpio的第24个引脚,低电平亮

#include "linux/module.h"
#include "linux/kernel.h"
#include "linux/miscdevice.h"
#include "linux/gpio.h"
#include "linux/cdev.h"
#include "linux/fs.h"
#include "linux/device.h" 
dev_t devnum;
struct cdev cdevp;
struct file_operations ops;
struct class * cls;
int led1_open (struct inode * a, struct file * b){
    gpio_set_value(24,0);
    return 0;
}
int led1_close (struct inode * c, struct file * d){
    gpio_set_value(24,1);
    return 0;
}
	
//加载函数->驱动入口
static int __init test_init(void)
{
    gpio_request(24,"myled1");
    gpio_direction_output(24,1);
    //1.先申请一个设备号
    alloc_chrdev_region(&devnum,78,1,"led1_dev");
    printk("主设备号 == %d\r\n",devnum >> 20);
	printk("次设备号 == %d\r\n",devnum &0xFFFFF);

    //2.初始化cdev结构体
    ops.owner = THIS_MODULE;
    ops.open = led1_open;
    ops.release = led1_close;
    cdev_init(&cdevp,&ops);
    //3.注册cdev到内核
    cdev_add(&cdevp,devnum,1);
    cls = class_create(THIS_MODULE,"led1_devclass");
    device_create(cls,NULL,devnum,NULL,"LED1_test07");
    return 0;
}         
//卸载函数->驱动的出口
static  void __exit test_exit(void)
{
    printk("hello  出口函数!\r\n");
    device_destroy(cls, devnum);
    class_destroy(cls);
    //注销cdev设备
    cdev_del(&cdevp);
    //释放申请的设备号
    unregister_chrdev_region(devnum,1);
    //释放gpio引脚
    gpio_free(24);
}
//内核层的声明
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");//必须遵循开源协议

应用层程序–控制led1闪烁

#include "stdio.h"
#include "unistd.h"
#include "fcntl.h"
#include "sys/types.h"
#include "sys/stat.h"
int main(){
    int fd1 = 0;
    while(1){ 
        fd1 = open("/dev/LED1_test07",O_RDONLY);
        sleep(1);
        close(fd1);
        sleep(1);
    }
}

开发板下执行注册、注销和执行程序操作

https://i-blog.csdnimg.cn/direct/2219696a0c87461ba0de2c65ff0e6085.png

其中打印了字符设备的主设备号和次设备号,可以通过查看日志信息指令获取。

https://i-blog.csdnimg.cn/direct/74cdc752db864eceaca882bc148194d2.png