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

驱动

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

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);
    }
}
相关推荐
FoldWinCard4 小时前
D5 Linux 网络及端口命令
linux·运维·服务器
零涂毕业设计6 小时前
毕业设计模块开发-OLED显示屏(IIC协议0.96寸)STM32 ESP32 FPGA Linux驱动免费分享
linux·stm32·单片机·嵌入式·esp32·fpga·oled
tedcloud1236 小时前
OmniRoute怎么部署?开源AI模型路由平台Linux部署教程
linux·服务器·人工智能·开源·音视频
霸道流氓气质6 小时前
KMS 密钥管理服务(Key Management Service)原理与实践
linux·服务器·数据库
SakitamaX6 小时前
LVS(Linux Virtual Server)概念详解
linux·运维·lvs
小张同学a.6 小时前
Linux系统管理
linux·运维
阿龙的工作记录7 小时前
深入理解Linux设备驱动模型
linux·嵌入式硬件·工作记录
代码村新手7 小时前
Linux的基本指令
linux·运维·服务器
hkj88088 小时前
Ubuntu 切换java版本
java·linux·ubuntu
撩得Android一次心动9 小时前
Linux编程笔记3【个人用】
linux·笔记