字符设备驱动开发与杂项开发

驱动

驱动,即操作系统用来操作硬件的逻辑代码

Linux下的驱动

  • 特点

底层驱动要配合应用层才能完成对底层硬件的获取/操作

  • 流程

向内核插入驱动xxx.ko -> 生成一个设备文件 /dev/xxx -> 执行应用层代码的可执行程序main--->实现对硬件设备的操作

设备文件

  • 本质

内核驱动需要生成设备文件作为上层应用与底层的桥梁。底层需要一直编写接口,以便于上层的操作。

在Linux下,一切皆文件。==所有设备在Linux下都是设备文件。

  • 特点

驱动设备文件是由内核生成的特殊文件,存放在/dev目录下,在Ubuntu下呈黄色字体,对其操作时需要使用非缓冲区操作(open、close、read、write)。每个设备文件都有唯一的设备号(uint32_t的数字),其中高12bit为主设备号,低20bit为次设备号。

设备文件分为3类:

  1. 块设备(block):硬件、内存条、Flash
  2. 网络设备:WiFi、蓝牙、网卡
  3. 字符设备(char):除以上设备外都属于字符设备。

字符设备有三种开发方法:杂项开发、经典开发、Linux2.6开发

杂项驱动设备文件

  • 特点

杂项类单独占用一个主设备号(10),且最多允许255个设备。在注册时会自动占用空闲设备号。

  • 注册杂项设备接口函数

函数原型:

cpp 复制代码
int misc_register(struct miscdevice *misc);

函数功能:注册一个杂项设备,并生成一个对应的设备文件

函数头文件:#include "linux/miscdevice.h"

函数参数:misc:注册结构体

cpp 复制代码
struct miscdevice  {
 int minor; //次设备号,填入255可以由系统自动分配次设备号。杂项设备的主设备号为10
 const char *name; //设备文件名
 const struct file_operations *fops; //当前设备文件在底层的接口函数结构体
 struct list_head list; //余下参数可以不填写
 struct device *parent;
 struct device *this_device;
 const struct attribute_group **groups;
 const char *nodename;
 umode_t mode;
};

fops参数中至少需要填入三个参数:

owner :填入THIS_MODULE

open :int (*open) (struct inode *, struct file *);// 自主编写本设备文件的打开操作函数

release: int (*release) (struct inode *, struct file *);//自主编写本设备文件的关闭操作函数

函数返回值:成功返回0,失败返回负数。

  • 注销杂项设备接口函数

函数原型:

cpp 复制代码
void misc_deregister(struct miscdevice *misc);

函数参数:已经注册的设备文件结构体。

杂项开发驱动蜂鸣器与led灯

  • 驱动开发程序
cpp 复制代码
#include "linux/module.h"
#include "linux/kernel.h"
#include "linux/miscdevice.h"
#include "linux/gpio.h"
struct miscdevice mymisc0, mymisc1, mymisc2;
struct file_operations ops0, ops1, ops2;
//我自己在内核层写了一个 open
/**
 * 当我的上层的生成的设备文件->/dev/test06beep被上层人员打开了 open
 * 内核层就会调用该函数一次!
 */
int my_beep_open(struct inode * i,struct file *f_delown)
{
    printk("蜂鸣器被打开\r\n");
    gpio_set_value(36,1);
    return 0;
}
int my_beep_close(struct inode * i,struct file * f_delown){
    printk("蜂鸣器被关闭\r\n");
    gpio_set_value(36,0);
    return 0;
}
//加载函数->驱动入口
static int __init test_init(void)
{
    printk("hello  入口函数!\r\n");
    gpio_request(36,"mybeep");
    gpio_direction_output(36,0);
    ops0.owner = THIS_MODULE;
    ops0.open = my_beep_open;
    ops0.release = my_beep_close;
    mymisc0.minor = 255;
    mymisc0.name  = "test06beep";
    mymisc0.fops = &ops0;
    misc_register(&mymisc0);
    return 0;
}         
//卸载函数->驱动的出口
static  void __exit test_exit(void)
{
    printk("hello  出口函数!\r\n");
    gpio_free(36);
    misc_deregister(&mymisc0);
}
//内核层的声明
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");//必须遵循开源协议
cpp 复制代码
#include "linux/module.h"
#include "linux/kernel.h"
#include "linux/miscdevice.h"
#include "linux/gpio.h"
struct miscdevice mymisc1;
struct file_operations ops1;
int my_led1_open(struct inode * i,struct file *f_delown)
{
    printk("led1被打开\r\n");
    gpio_set_value(24,0);
    return 0;
}
int my_led1_close(struct inode * i,struct file * f_delown){
    printk("led1被关闭\r\n");
    gpio_set_value(24,1);
    return 0;
}
//加载函数->驱动入口
static int __init test_init(void)
{
    printk("hello  入口函数!\r\n");
    gpio_request(24,"myled1");
    gpio_direction_output(24,1);
    ops1.owner = THIS_MODULE;
    ops1.open = my_led1_open;
    ops1.release = my_led1_close;
    mymisc1.minor = 255;
    mymisc1.name  = "test06led1";
    mymisc1.fops = &ops1;
    misc_register(&mymisc1);
    return 0;
}         
//卸载函数->驱动的出口
static  void __exit test_exit(void)
{
    printk("hello  出口函数!\r\n");
    gpio_free(24);
    misc_deregister(&mymisc1);
}
//内核层的声明
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");//必须遵循开源协议
cpp 复制代码
#include "linux/module.h"
#include "linux/kernel.h"
#include "linux/miscdevice.h"
#include "linux/gpio.h"
struct miscdevice mymisc2;
struct file_operations ops2;
int my_led2_open(struct inode * i,struct file *f_delown)
{
    printk("led2被打开\r\n");
    gpio_set_value(61,0);
    return 0;
}
int my_led2_close(struct inode * i,struct file * f_delown){
    printk("led2被关闭\r\n");
    gpio_set_value(61,1);
    return 0;
}
//加载函数->驱动入口
static int __init test_init(void)
{
    printk("hello  入口函数!\r\n");
    gpio_request(61,"myled2");
    gpio_direction_output(61,1);
    ops2.owner = THIS_MODULE;
    ops2.open = my_led2_open;
    ops2.release = my_led2_close;
    mymisc2.minor = 255;
    mymisc2.name  = "test06led2";
    mymisc2.fops = &ops2;
    misc_register(&mymisc2);
    return 0;
}         
//卸载函数->驱动的出口
static  void __exit test_exit(void)
{
    printk("hello  出口函数!\r\n");
    gpio_free(61);
    misc_deregister(&mymisc2);
}
//内核层的声明
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");

这里的所有设备文件都单独驱动,不建议一个驱动里写好多个设备。

  • 应用层程序
cpp 复制代码
#include "stdio.h"
#include "unistd.h"
#include "fcntl.h"
#include "sys/types.h"
#include "sys/stat.h"
int main(){
    char buf[10]={0};
    int fd0 =0 ,fd1 = 0,fd2 = 0;
    while(1){
        fd0 = open("/dev/test06beep",O_RDONLY);
        sleep(1);
        close(fd0);
        fd1 = open("/dev/test06led1",O_RDONLY);
        sleep(1);
        close(fd1);
        fd2 = open("/dev/test06led2",O_RDONLY);
        sleep(1);
        close(fd2);
    }
}
相关推荐
Johny_Zhao11 小时前
OpenClaw安装部署教程
linux·人工智能·ai·云计算·系统运维·openclaw
chlk1232 天前
Linux文件权限完全图解:读懂 ls -l 和 chmod 755 背后的秘密
linux·操作系统
舒一笑2 天前
Ubuntu系统安装CodeX出现问题
linux·后端
改一下配置文件2 天前
Ubuntu24.04安装NVIDIA驱动完整指南(含Secure Boot解决方案)
linux
深紫色的三北六号2 天前
Linux 服务器磁盘扩容与目录迁移:rsync + bind mount 实现服务无感迁移(无需修改配置)
linux·扩容·服务迁移
SudosuBash3 天前
[CS:APP 3e] 关于对 第 12 章 读/写者的一点思考和题解 (作业 12.19,12.20,12.21)
linux·并发·操作系统(os)
哈基咪怎么可能是AI3 天前
为什么我就想要「线性历史 + Signed Commits」GitHub 却把我当猴耍 🤬🎙️
linux·github
十日十行4 天前
Linux和window共享文件夹
linux
木心月转码ing4 天前
WSL+Cpp开发环境配置
linux
崔小汤呀5 天前
最全的docker安装笔记,包含CentOS和Ubuntu
linux·后端