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

驱动

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

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);
    }
}
相关推荐
让子弹飞022 分钟前
36.2Linux单总线驱动DS18B20实验(详细讲解代码)_csdn
linux·ubuntu·驱动的分离和分层
Yana.nice18 分钟前
yum list 和 repoquery的区别
linux
码出钞能力1 小时前
更换libc.so导致linux变砖,通过LD_PRELOAD挽救
linux·服务器
小马学嵌入式~1 小时前
嵌入式 SQLite 数据库开发笔记
linux·c语言·数据库·笔记·sql·学习·sqlite
小猪咪piggy2 小时前
【JavaEE】(24) Linux 基础使用和程序部署
linux·运维·服务器
Haven-2 小时前
Linux常见命令
linux·基本指令
IT 小阿姨(数据库)2 小时前
PgSQL中pg_stat_user_tables 和 pg_stat_user_objects参数详解
linux·运维·数据库·sql·postgresql·oracle
MChine慕青2 小时前
顺序表与单链表:核心原理与实战应用
linux·c语言·开发语言·数据结构·c++·算法·链表
虎头金猫3 小时前
如何在Linux上使用Docker在本地部署开源PDF工具Stirling PDF:StirlingPDF+cpolar让专业操作像在线文档一样简单
linux·运维·ubuntu·docker·pdf·开源·centos
努力学习的小廉4 小时前
深入了解linux系统—— 线程同步
linux·服务器·数据库·算法