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

驱动

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

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);
    }
}
相关推荐
wdxylb1 小时前
云原生俱乐部-shell知识点归纳(1)
linux·云原生
飞雪20072 小时前
Alibaba Cloud Linux 3 在 Apple M 芯片 Mac 的 VMware Fusion 上部署的完整密码重置教程(二)
linux·macos·阿里云·vmware·虚拟机·aliyun·alibaba cloud
路溪非溪2 小时前
关于Linux内核中头文件问题相关总结
linux
Lovyk5 小时前
Linux 正则表达式
linux·运维
Fireworkitte6 小时前
Ubuntu、CentOS、AlmaLinux 9.5的 rc.local实现 开机启动
linux·ubuntu·centos
sword devil9006 小时前
ubuntu常见问题汇总
linux·ubuntu
ac.char6 小时前
在CentOS系统中查询已删除但仍占用磁盘空间的文件
linux·运维·centos
淮北也生橘128 小时前
Linux的ALSA音频框架学习笔记
linux·笔记·学习
华强笔记11 小时前
Linux内存管理系统性总结
linux·运维·网络
十五年专注C++开发12 小时前
CMake进阶: CMake Modules---简化CMake配置的利器
linux·c++·windows·cmake·自动化构建