首页 > 其他分享 >驱动

驱动

时间:2023-04-16 14:14:16浏览次数:40  
标签:static 驱动 pin4 open class 设备

linux应用编程其实都是通过调用c库,c库给上层应用提供支配内核(进程、网络等)干活的接口

 

驱动:

  在内核中,有个驱动链表管理类所有的设备驱动,驱动插入链表的顺序由设备号检索;

  设备号分为主设备号和次设备号,主设备号被系统用来确定要调用哪个驱动程序设备类型:如USB设备,硬盘设备),主设备号相同的设备是同类设备,使用同一个驱动程序,次设备号被驱动程序用来确定具体哪一个设备。主设备号用来区分不同种类的设备,而次设备号用来区分同一类型的多个设备。

 

驱动的开发大致分为两个步骤:

  1. 添加驱动:编写驱动程序,添加到内核驱动链表

    如何编写驱动程序?按照驱动框架,分为如下几步:

      a. 设备名

      b. 设备号  

      c. 向驱动框架中添加驱动函数(即操作相关的寄存器)

  2. 调用驱动:调用open、reead、write函数,操作硬件

    内核是如何知道调用哪个驱动呢?

      因为open时由设备名,通过设备名就能知道设备号,然后在驱动链表中就能找到对应的驱动

所以驱动的开发大致流程如下:

  当向内核添加驱动后,用户在用户空间调用open,会触发系统调用sys_call,sys_call会调用到sys_open,

  然后sys_open会在驱动链表里面通过设备名找到设备号,从而确定对应的驱动,然后再调用驱动函数里的open,在驱动函数里的open就操作硬件寄存器

 

驱动框架:

#include <linux/fs.h>         //file_operations声明
#include <linux/module.h>    //module_init  module_exit声明
#include <linux/init.h>      //__init  __exit 宏定义声明
#include <linux/device.h>     //class  devise声明
#include <linux/uaccess.h>   //copy_from_user 的头文件
#include <linux/types.h>     //设备号  dev_t 类型声明
#include <asm/io.h>          //ioremap iounmap的头文件
 
static struct class *pin4_class;  
static struct device *pin4_class_dev;
 
static dev_t devno;                //设备号
static int major =231;             //主设备号
static int minor =0;               //次设备号
static char *module_name="pin4";   //模块名
 
//pin4_read函数
//static int pin4_read(struct file *file,char __user *buf,size_t count, loff_t *ppos)
//{
//    printk("pin4_write\n");
//    return 0;
//}
 
//pin4_open函数
static int pin4_open(struct inode *inode,struct file *file)
{
    printk("pin4_open\n");  //内核的打印函数,和printf类似
   
    return 0;
}
 
//pin4_write函数
static ssize_t pin4_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos)
{
    printk("pin4_write\n");
    return 0;
}
 
static struct file_operations pin4_fops = {
 
    .owner = THIS_MODULE,
    .open  = pin4_open,
    .write = pin4_write,
};
 
int __init pin4_drv_init(void)   //驱动的真正入口
{
 
    int ret;
    devno = MKDEV(major,minor);  //创建设备号
    ret   = register_chrdev(major, module_name,&pin4_fops);  //注册驱动  告诉内核,把这个驱动加入到内核驱动的链表中
 
    pin4_class=class_create(THIS_MODULE,"myfirstdemo");  //由代码在/dev下自动生成设备
    pin4_class_dev =device_create(pin4_class,NULL,devno,NULL,module_name);  //创建设备文件
 
 
    return 0;
}
 
void __exit pin4_drv_exit(void)
{
 
    device_destroy(pin4_class,devno);
    class_destroy(pin4_class);
    unregister_chrdev(major, module_name);  //卸载驱动
 
}
 
module_init(pin4_drv_init);  //入口:内核加载驱动的时候(insmod),这个宏会被调用,而真正的驱动入口是它调用的函数
module_exit(pin4_drv_exit);
MODULE_LICENSE("GPL v2");

这样通过驱动框架编写好一个驱动程序,前面提到驱动可以直接编译进内核,也可以通过模块的方式加载进内核,这里我们以模块的方式。

  1. 将这个驱动代码放到内核源码下的linux-rpi-4.14.y/drivers/char,然后仿照该目录下的makefile,添加将这个驱动代码编译成模块的指令,如

 

   2. 然后再执行编译驱动的命令(将其编译成模块):

    ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make -j4 modules

  3. 然后在树莓派中装驱动

    sudo insmod pin4driver.ko

  4. 查看添加的驱动文件是否成功/查看内核中挂载的驱动

    ls -l /dev/pin4   或  lsmod

  5. 修改/dev/pin4权限

    sudo chmod 666 /dev/pin4

demsg:查看内核打印信息,pintk是内核的输出函数。

删除驱动:sudo rmmod pin4drive

GPIO驱动;

#include <linux/fs.h>         //file_operations声明
#include <linux/module.h>    //module_init  module_exit声明
#include <linux/init.h>      //__init  __exit 宏定义声明
#include <linux/device.h>     //class  devise声明
#include <linux/uaccess.h>   //copy_from_user 的头文件
#include <linux/types.h>     //设备号  dev_t 类型声明
#include <asm/io.h>          //ioremap iounmap的头文件
 
static struct class *pin4_class;  
static struct device *pin4_class_dev;
 
static dev_t devno;                //设备号
static int major =231;             //主设备号
static int minor =0;               //次设备号
static char *module_name="pin4";   //模块名
 
volatile unsigned int* GPFSEL0 = NULL;
volatile unsigned int* GPSET0  = NULL;
volatile unsigned int* GPCLR0  = NULL;
 
//pin4_open函数
static int pin4_open(struct inode *inode,struct file *file)
{
    printk("pin4_open\n");  //内核的打印函数,和printf类似
    //open的时候配置pin4为输出引脚
    *GPFSEL0 &= ~(0x6 << 12);
    *GPFSEL0 |= (0x1 << 12);
    
    return 0;
}
 
//pin4_write函数
static ssize_t pin4_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos)
{
    int userCmd;//上层写的是整型数1,底层就要对应起来用int.如果是字符则用char
 
    printk("pin4_write\n");
    //获取上层write的值
    copy_from_user(&userCmd,buf,count);//用户空间向内核空间传输数据
    
    //根据值来执行操作
    if(userCmd == 1){
        printk("set 1\n");
        *GPSET0 |= 0x1 << 4;
    }else if(userCmd == 0){
        printk("set 0\n");
        *GPCLR0 |= 0x1 << 4;
    }else{
        printk("cmd error\n");
    }
    
    return 0;
}
 
static struct file_operations pin4_fops = {
 
    .owner = THIS_MODULE,
    .open  = pin4_open,
    .write = pin4_write,
};
 
int __init pin4_drv_init(void)   //驱动的真正入口
{
 
    int ret;
    printk("insmod driver pin4 success\n");
    devno = MKDEV(major,minor);  //创建设备号
    ret   = register_chrdev(major, module_name,&pin4_fops);  //注册驱动  告诉内核,把这个驱动加入到内核驱动的链表中
 
    pin4_class=class_create(THIS_MODULE,"myfirstdemo");  //由代码在/dev下自动生成设备
    pin4_class_dev =device_create(pin4_class,NULL,devno,NULL,module_name);  //创建设备文件
 
    GPFSEL0 = (volatile unsigned int *)ioremap(0x3f200000,4);//0x3f200000是物理地址,代码操作的是虚拟地址,所以要将物理地址映射成虚拟地址
    GPSET0  = (volatile unsigned int *)ioremap(0x3f20001C,4);
    GPCLR0  = (volatile unsigned int *)ioremap(0x3f200028,4);
     
     return 0;
}
 
void __exit pin4_drv_exit(void)//可以发现和init刚好是相反的执行顺序。
{
    iounmap(GPFSEL0);
    iounmap(GPSET0);
    iounmap(GPCLR0);
    
    device_destroy(pin4_class,devno);
    class_destroy(pin4_class);
    unregister_chrdev(major, module_name);  //卸载驱动
 
}
 
module_init(pin4_drv_init);  //入口:内核加载驱动的时候,这个宏会被调用,而真正的驱动入口是它调用的函数
module_exit(pin4_drv_exit);
MODULE_LICENSE("GPL v2");

 

标签:static,驱动,pin4,open,class,设备
From: https://www.cnblogs.com/zj-studyrecoding/p/17323198.html

相关文章

  • 如何建立目标驱动的软件职业生涯
    不要讨厌你的日常工作大多数开发人员都感到无聊。每天的工作都是永无止境的任务、工单和会议。他们很少觉得与他们正在构建的产品有联系。如果这是你,那不是你的错!大多数软件产品都没有吸引力。另一个约会应用程序或送餐网站。FinTech或AdTech产品只会让富有的公司变得更富......
  • 领域驱动设计-软件核心复杂性应对之道:第二章
    第二章语言的交流和使用2.1模式​ 由于语言上存在鸿沟,领域专家们只能模糊地描述他们想要的东西。开发人员虽然努力去理解一个自己不熟悉的领域,但也只能形成模糊的认识。有少数的团队成员会学着同时说这两种语言,但由于这样的人太少了,信息流会遭遇瓶颈问题,而且他们的翻译也不准......
  • 驱动模块加载三要素例子
    #include<linux/kernel.h>#include<linux/module.h>#include<linux/cdev.h>#include<linux/fs.h>#defineHELLO_MAJOR250#defineHELLO_MINOR0#defineDEV_NUM1#defineDEV_NAME"hello_world&qu......
  • 解决帆软Report连接数据库是出现未下载驱动问题
    1.在帆软Report连接数据库的时候一直出现问题,还有就是版本11的不太好用,建议使用版本10的下载驱动在官网都是英文看不懂,直接在csdn上搜即可,然后需要把下载下来的驱动放在这个目录下,这样帆软连接数据库的问题解决 ......
  • 信创操作系统--麒麟Kylin桌面版(项目一 操作系统安装教程3:麒麟系统驱动安装)
    安装驱动1.1安装显卡驱动1.1.1AMD显卡驱动安装在麒麟操作系统中,其内核已集成AMD显卡的开源驱动,该开源驱动体验良好,能满足日常办公的使用。若要在麒麟操作系统中使用图形密集型程序(如玩游戏、绘制CAD、视频剪辑等),建议安装mesa-vulkan-drivers驱动程序包。在终端中执行以下命令,安装......
  • dell OptiPlex 7080 安装centos7没有网卡驱动解决
    dellOptiPlex7080安装centos7安装完没有网卡驱动通过lspci|grep-ieth执行查看网卡型号,命令又没有。需要yum下载命令没有网,只能通过配置本地光盘源挂载光驱mount/dev/sr1/media/cd/etc/yum.repos.d/&&mkdirbak&&mv*bak/cat>media.repo<<EOF[media......
  • 跨网摆渡系统如何实现数据安全交换,从而驱动业务流转?
    在这个数据驱动的时代,一次数据泄露就可能影响到数亿甚至数十亿人。数字化转型进一步推动了数据的移动,而随着攻击者加速利用日常生活中的数据依赖性,数据泄露也正随之扩大。一旦边界的防线被攻破或绕过,攻击者就可以在数据中心内部横向移动,而中心内部基本没有安全控制的手段可以阻止......
  • 聊一聊对领域驱动设计中“领域”这个词语的理解与分析方法
    1.什么是领域百度百科对领域的解释:领域具体指一种特定的范围或区域领域一般指的是业务的问题域,领域是有边界的,边界内,规定了我们要做什么,要做的范围,软件项目从开始到交付的过程中,所有涵盖的业务,每个业务模块或者方向都有自己的业务范围和问题比如做家装行业的公司进行软件系......
  • 规划高速公路上完全可再生动力充电站:数据驱动的鲁棒优化方法
    规划高速公路上完全可再生动力充电站:数据驱动的鲁棒优化方法本文提出了一种全面的两级方法,用于在公路网络上采用和大化独立电动电动机充电站。在第一阶段,从提供交通需求和电池数据的MonteCarlo仿真获得单个车辆需要充电服务的位置;提出了一种整数编程模型,以确定来自潜在候选者......
  • 42V刹车灯IC 踏板车灯 低压LED降压恒流IC驱动
    AH1180是一款外围电路简单,连续工作模式非隔离式恒流LED驱动控制芯片。采用了PWM工作模式,工作频率为160KHz,AH1180通过对MODE端口进行控制实现二功能切换。高压LED灯串线性恒流驱动,耐压可以达到100V;芯片内置各种保护机制,比如线性温度保护和电压的过压保护等功能,当芯片温度......