linux驱动开发-字符设备控制技术笔记3(代码片段)

hntea-hong hntea-hong     2023-02-28     143

关键词:

字符设备控制技术

  笔记要做的自己看起来舒服和有头绪,这不又折腾切换编辑器来从新排版,有强迫症啊!对于字符控制,很多时候编写上层应用程序时,使用ioctl系统调用来控制设备,原型如下:

/*
    fd: 要控制的设备文件描述符
    cmd: 发送给设备的控制命令
    …: 第3个参数是可选的参数,存在与否是依赖于控制命令(第2 个参数)。
*/
int ioctl(int fd,unsigned long cmd,...);

但是在Linux2.6.36之后的

/*
c) 参数说明

     fiel:打开的设备描述符

     cmd:传递的命令

     arg:命令的个数
*/
long (*unlocked_ioctl) (struct file *file, unsigned int cmd, unsigned long arg);

long (*compat_ioctl) (struct file *file, unsigned int cmd, unsigned long arg);

d) 注意:

  • 为了防止对错误设备使用正确的命令,命令号应在系统范围内是唯一的
  • 既每个命令号都应该由多个位段组成
  • 定义号码的方法使用四个位段

位段详解

a) 头文件:<linux/ioctl.h>

b) type(类型)

  • i. 幻数(magic number):选择系统中没有使用的号码,并在整个驱动程序中使用这个号码。

    ii. 8位宽度(_IOC_TYPEBITS)

    iii. 源码的/Document目录下magic-num.txt 记录了当前已经使用的幻数

c) number(号码)

i. 序数:8位宽度(_IOC_NRBITS)

d) direct(方向)

i. 如果该命令有数据传输。则它定义数据传输方向

  • _IOC_NONE:没有传输方向

    _IOC_READ:

    _IOC_WRITE:

    _IOC_READ|_IOC_WRITE:双向传输

e) size(尺寸)

  • i. 涉及数据的大小

    ii. 如果想要驱动可移植,则只能认为最大尺寸可达255字节

    iii. 如果驱动程序需要更大的尺度的数据传输,则可忽略这个字段

4.构造命令号码的宏

/*
 type:幻数
 nr:命令编号
*/
_IO(type,nr); //定义一个没有数据传输的命令号编号

/*
type:幻数
nr:命令编号
size:数据大小
*/
 _IOR(type,nr,size):定义一个读数据的命令编号;从设备读取参数

 _IOW(type,nr,size):定义一个写数据的命令编号;向设备写入参数
 _IOWR(type,nr,size):定义一个可读可写的命名编号
 _IOC_DIR(nr):获得命令编号中的数据传输方向
 _IOC_TYPE(nr):获取命令编号中的幻数
 _IOC_NR(nr):获取命令编号中的号码
 _IOC_SIZE(nr):获取命令编号中的尺寸

5.例:

a) 新建控制文件:led_ioctl.h;编写一下代码

 #define LED_MAGIC ‘L’
 #define LED_ON _IOW(LED_MAGIC,1,int)
 #define LED_OFF _IOW(LED_MAGIC,0,int)

b) 引用头文件“led_ioctl.h”

c) 驱动程序在相应的文件中可以使用这些数据

代码架构:

static int xxx_ioctl(struct file* filp,unsigned int cmd,unsignend long arg)

    if(_IOC_TYPE(cmd) != xxx_IOC_MAGIC) /*驱动的幻数,我们在头文件中定义的*/
        return -ENOTTY;
    

    if(_IOC_NR(cmd) >= xxx_MAXNR)
    return -ENOTTY;
    

    switch(cmd)
    
    case LED_ON:

    处理;

    break;

    case LED_OFF:

    处理;

    break;

    default:
        return -ENOTTY;
    break;
    

应用程序测试

代码架构:

#include<fcntl.h>

#include<sys/ioctl.h>

#include  “led_ioctl.h”

/*

参数说明:

arg:参数个数(注意,在终端输入执行命令时已近占据0号参数,我们自己的 参数从1开始)

argv:参数内容

*/

int main(int arg,unsigned char* argv)



    int fd=0;

    int ret=0;

    /*处理终端传递过来的参数*/

    atio();//将终端输入的字符串转换成int型。

    fd = open(“驱动设备文件”,打开模式);

    判断是否成功;

    /*更具需要调用读写,控制函数,这里调用ioctl()*/

    ret = ioctl(fd,cmd,...);

    close(fd);

LED 控制驱动程序设计

第一步:申明控制号,保存文件为 led_ioctl.h

#define LED_MAGIC 'L'

#define TURNON_LED _IOW(LED_MAGIC,1,int)

#define TURNOFF_LED _IOW(LED_MAGIC,0,int)

第二步:编写驱动

/**********************************************

作者:hntea

时间:2016/3/9

功能:led 字符设备驱动+ioctl

**********************************************/

#include <linux/init.h>

#include <linux/module.h>

#include <linux/fcntl.h>

#include <linux/kernel.h>

#include <linux/types.h>

#include <linux/cdev.h>

#include <linux/sched.h>

#include <linux/slab.h>

#include <linux/fs.h>

#include <linux/mm.h>

#include <linux/ioctl.h>

#include <asm/io.h>

#include <asm/uaccess.h>

#include "led_ioctl.h"

#define DEV_NAME "led"

#define MAJOR_NR 10

#define MINIR_NR 1

#define LED_ON 0x00000018

#define LED_OFF 0X00000000

#define CON_ADDR 0xE0200060

#define DAT_ADDR 0xE0200064

struct cdev led_dev; /*静态分配设备号*/

dev_t dev_nm; /*设备号*/


/*************************************************

函数名: led_hardinit 实现

函数参数:

函数功能:led硬件初始化

*************************************************/

static void led_hardinit(void)



    unsigned int  tmp = 0;

    unsigned int *led_config;



    /*找出物理地址对应的虚拟地址*/

    led_config = ioremap(CON_ADDR,4); /*该寄存器是32位数值*/

    /*读取当前寄存器状态*/

    #if 0

    tmp = ioread32(led_config);

    tmp &= (~(0x11 << 3));

    tmp |= (0x11 << 3);

    #endif

    tmp = 0x11000;

    /*寄存器设置写回*/

    iowrite32(tmp,led_config);



static void led_on(void)



    unsigned int *led_data;

    led_data = ioremap(DAT_ADDR,4);

    iowrite32(LED_ON,led_data);

    printk("revice cmd:ledon\\n");



static void led_off(void)



    unsigned int *led_data;

    led_data = ioremap(DAT_ADDR,4);

    iowrite32(LED_OFF,led_data);

    printk("revice cmd:ledoff\\n");





/*************************************************

函数名: file_operations 实现

函数参数:

函数功能:

*************************************************/

static int  led_open (struct inode *inode, struct file *fp)



int ret = 0;

/*led 打开时只需要初始化相关寄存器就够*/

led_hardinit();

return ret;





static int led_release (struct inode *inode, struct file *fp)



return 0;





static long led_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)



    /*这里可以使用命令解析宏解析幻数再判断,使代码逻辑更严谨*/

    /*命令解析*/

    switch(cmd)
    

        case TURNON_LED:

        led_on();

        break;

        case TURNOFF_LED:

        led_off();

        break;

        default:

        return -ENOTTY;

        break;

    

return 0;



/*************************************************

函数名: led_write 实现

函数参数:

函数功能:向led设备文件写如数据

*************************************************/

static ssize_t led_write(struct file *fp, const char __user *buf, size_t count, loff_t *f_pos)


    unsigned char count_tmp = count ; /*获取写入字节数*/

    unsigned char led_sta[2]; /*用来存放两个led的状态*/

    int i = 0;

    memset(led_sta,0,2); /*数据清零*/

    if( count > 2)
        count_tmp = 2;
    

    /*将数据从用户空间复制到内核空间*/

    if(copy_from_user(led_sta,buf,count_tmp))
    return -EFAULT;else
    
        /*控制两个led的状态*/
        for(i=0;i<2;i++)
        
            /*读取当前led寄存器状态,严谨就要处理,防止数据破话而使系统奔溃*/
            if( led_sta[i] == '1' )
                led_on();
            else
                led_off();
            
        

    

    return 0;



struct file_operations ledfp=

    .owner = THIS_MODULE,

    .open = led_open,

    .write = led_write,

    .unlocked_ioctl = led_ioctl,

    .release = led_release,

;

struct cdev cdev=

    .owner = THIS_MODULE,

    .ops = &ledfp,

;



static int ledInit(void)


    int ret = 0;

    /*1.分配设备号,0是以该数字作为主设备号分配的起始,2是次设备号(用来标识设备的个数)*/

    if((ret = alloc_chrdev_region(&dev_nm, 0, 1,DEV_NAME))<0)
    
        printk("device num alloc err!\\n");
    

    /*2.分配设备结构;静态分配一个全局变量*/

    printk("led major number is %d\\n",MAJOR(dev_nm));

    printk("led minor number is %d\\n",MINOR(dev_nm));

    /*3.初始化设备结构*/

     cdev_init(&cdev, &ledfp);

    /*4.注册设备*/

     if((ret = cdev_add(&cdev,dev_nm, 1)) < 0) /*最后一个参数说明设备数*/

     

      printk("cdev add err!\\n");

     

     /*5.创建设备文件,现在使用 手工创建mknod*/

    //device_create();

     printk("led.ko insmod success!\\n");

    return 0;





static void ledexit(void)

    /*2.注销设备号,注销设备*/
    cdev_del(&cdev);   /*注销设备*/
    unregister_chrdev_region(dev_nm, 1); /*释放设备号*/
    printk("led device rmmod success!\\n");






MODULE_AUTHOR("hntea");

MODULE_LICENSE("GPL");

module_init(ledInit);

module_exit(ledexit);

加载驱动:手动创建 /dev/led 节点文件

mknod /dev/led c 主设备号 次设备号

第三步:编译测试应用

#include<sys/fcntl.h>

#include<sys/types.h>

#include<sys/stat.h>

#include<sys/ioctl.h>

#include<stdio.h>

#include "led_ioctl.h"

#define DEVICE_FILE "/dev/led" 

int main(int arg,char **argv)



    int fd = 0 ;

    int cmd = 0;

    int ret = 0;

    /*解析传递进来的参数*/

    if(arg < 2)

    printf("Please input : <cmd>  <0/1>\\n");

    return -1;

    

    /*格式化*/

    cmd = atoi((argv[1]));

    /*设备操作*/

    if((fd  = open(DEVICE_FILE,O_RDWR))< 0)

    printf("File open err!\\n");

    

    switch(cmd)

    

        case 0:

        ret = ioctl(fd,TURNOFF_LED);

        break;

        case 1:

        ret = ioctl(fd,TURNON_LED);

        break;

        default:

        printf("Command er!\\n");

        return -1;

    

    close(fd);

linux驱动开发-混杂字符设备驱动模型笔记4(代码片段)

混杂字符设备驱动模型概念linux系统中,称以主设备号为“10”,但次设备号不同的字符设备为混杂设备所有的混杂设备形成一个链表对设备访问时,内核根据次设备号查找到相应的设备结构描述structmiscdeviceintminor;/*... 查看详情

linux驱动开发-混杂字符设备驱动模型笔记4(代码片段)

混杂字符设备驱动模型概念linux系统中,称以主设备号为“10”,但次设备号不同的字符设备为混杂设备所有的混杂设备形成一个链表对设备访问时,内核根据次设备号查找到相应的设备结构描述structmiscdeviceintminor;/*... 查看详情

linux驱动开发-字符设备驱动笔记2

字符设备驱动程序设计概念字符设备文件:应用程序通过字符设备文件而调用字符设备驱动的文件操作映射关系是一个文件描述符字符设备文件创建方法可以使用以下命令形式:cat/proc/devices //查看主设备号mknod/dev/xxxc100... 查看详情

linux驱动开发-01_字符设备驱动开发(代码片段)

文章目录字符设备驱动开发1.字符设备驱动简介2字符设备驱动开发步骤2.1驱动模块的加载和卸载2.2字符设备注册与注销2.3实现设备的具体操作函数2.4添加LICENSE和作者信息3Linux设备号3.1设备号的组成3.2设备号的分配4chrdevbase字符设... 查看详情

嵌入式开发(七):linux字符型设备驱动初步

...嵌牛导读】:本篇文章整理Linux知识点—Linux字符型设备驱动初步。【嵌牛鼻子】:Linux设备类型,结构体,驱动模块【嵌牛提问】:Linux设备有什么类型?关键函数有哪些?【嵌牛内容】–linux字符型设备驱动初步一、Linux字符设... 查看详情

linux驱动开发:字符设备驱动开发(代码片段)

文章目录Linux驱动开发:字符设备驱动开发一、字符设备简介二、驱动调用原理三、驱动加载与卸载四、字符设备注册与注销五、设备驱动编写流程六、添加驱动信息Linux驱动开发:字符设备驱动开发一、字符设备简介字... 查看详情

linux驱动开发:字符设备驱动开发(代码片段)

文章目录Linux驱动开发:字符设备驱动开发一、字符设备简介二、驱动调用原理三、驱动加载与卸载四、字符设备注册与注销五、设备驱动编写流程六、添加驱动信息Linux驱动开发:字符设备驱动开发一、字符设备简介字... 查看详情

学习笔记——《linux设备驱动程序(第三版)》linux设备模型:内核添加删除设备驱动程序(代码片段)

文章目录1.前言2.准备工作2.1.概念2.2.具体总线、设备、驱动结构体说明2.3.注册总线3.添加设备3.1.STEP1——发现设备并创建设备结构structXXX_dev3.2.STEP2——初始化设备结构3.3.STEP3——注册设备4.删除设备5.添加驱动程序6.删除驱动程... 查看详情

linux驱动程序开发实例的目录

参考技术A前言第1章Linux设备驱动程序模型11.1设备驱动程序基础11.1.1驱动程序的概念11.1.2驱动程序的加载方式21.1.3编写可加载模块31.1.4带参数的可加载模块51.1.5设备驱动程序的分类61.2字符设备驱动程序原理71.2.1file_operations结构71.... 查看详情

linux驱动开发:字符设备驱动开发实战(代码片段)

Linux驱动开发:字符设备驱动开发实战一、工程创建VSCode创建工程,设置C/C++配置,导入linuxkernel源码目录,方便vscode写代码自动补全,vscode配置"configurations":["name":"Linux","includ 查看详情

一张图掌握linux字符设备驱动架构!建议收藏(代码片段)

目录一.Linux中字符设备驱动简介二.字符设备驱动快速入门(超简单demo)1.demo2.代码编译3.加载驱动模块4.创建设备节点文件5.APP设备文件操作6.卸载驱动模块三.字符设备驱动开发流程介绍:1.驱动模块加载和卸载的入口... 查看详情

虚拟字符设备驱动开发(代码片段)

目录字符设备驱动简介内核驱动操作函数集合字符设备驱动开发步骤驱动模块的加载和卸载字符设备注册与注销实现设备的具体操作函数添加LICENSE和作者信息Linux设备号设备号的组成设备号的分配chrdevbase字符设备驱动开发实验... 查看详情

linux驱动开发:杂项字符设备(代码片段)

...miscdevice)也是在嵌入式系统中用得比较多的一种设备驱动。       在Linux内核的include\\linux目录下有Miscdevice.h文件,misc设备定义及其内核提供的相关函数在这里。       其实是因为这些字符设备不符合预先确定的... 查看详情

linux驱动开发:杂项字符设备(代码片段)

...miscdevice)也是在嵌入式系统中用得比较多的一种设备驱动。       在Linux内核的include\\linux目录下有Miscdevice.h文件,misc设备定义及其内核提供的相关函数在这里。       其实是因为这些字符设备不符合预先确定的... 查看详情

linux驱动入门led驱动(代码片段)

原文:https://dandelioncloud.cn/article/details/1454352899043295234Linux驱动入门系列Linux驱动入门(一)字符设备驱动基础Linux驱动入门(二)操作硬件Linux驱动入门(三)Led驱动Linux驱动入门(四)非阻塞方式实现按键驱动Linux驱动入门(五... 查看详情

linux驱动学习笔记字符设备驱动

今天对程序进行编译,结果出现make-C/lib/modules/3.3.2-6.fc16.x86_64/buildM=/home/jqzeng/workSpace/ldd3/ldd3-samples-1.0.0/scullLDDINC=/home/jqzeng/workSpace/ldd3/ldd3-samples-1.0.0/scull/../includemodulesmake[1]:Enteringdirectory`/usr/src/kernels/3.3.2-6.fc16.x86_64'... 查看详情

linux设备驱动spi详解3-控制器驱动(代码片段)

整个SPI驱动架构可以分为协议驱动、通用接口层和控制器驱动三大部分。其中,控制器驱动负责最底层的数据收发工作,为了完成数据的收发工作,控制器驱动需要完成以下这些功能:(1)申请必要的硬件资源,例如中断,DMA... 查看详情

linux驱动开发--设备驱动基础笔记1

设备驱动基础设备驱动的使用方式:动态加载直接编译进内核直接编译进内核的方法:(内核源码/Doucument/kbuild/…中有相应的介绍).config:顶层配置文件,可以手动修改,也可以通过makemenuconfig等命令配置... 查看详情