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

驱动

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

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);
    }
}
相关推荐
若云止水14 分钟前
Ubuntu 下 nginx-1.24.0 源码分析 - cycle->modules[i]->ctx
linux·nginx·ubuntu
亦世凡华、15 分钟前
快速部署:在虚拟机上安装 CentOS 7 的详细步骤
linux·运维·经验分享·centos·安装教程
Elastic 中国社区官方博客41 分钟前
使用 Elastic-Agent 或 Beats 将 Journald 中的 syslog 和 auth 日志导入 Elastic Stack
大数据·linux·服务器·elasticsearch·搜索引擎·信息可视化·debian
星图辛某人1 小时前
《Linux命令行和shell脚本编程大全》第四章阅读笔记
linux·运维·笔记
gallonyin1 小时前
免root运行python保活守护进程supervisor
linux·开发语言·python
孤寂大仙v3 小时前
【Linux笔记】理解文件系统(上)
linux·运维·笔记
钢板兽4 小时前
Java后端高频面经——JVM、Linux、Git、Docker
java·linux·jvm·git·后端·docker·面试
byxdaz4 小时前
NVIDIA显卡驱动、CUDA、cuDNN 和 TensorRT 版本匹配指南
linux·人工智能·深度学习