Linux驱动开发——(linux内核字符设备硬件操作接口之ioctl)gpio(3)

简介: Linux驱动开发——(linux内核字符设备硬件操作接口之ioctl)gpio(3)

文章目录

linux内核字符设备硬件操作接口之ioctl

ioctl系统调用函数的使用

使用参考:

对应的底层驱动的ioctl接口

LED灯使用示例:

操作流程:


linux内核字符设备硬件操作接口之ioctl

ioctl系统调用函数的使用

  • 对于ioctl这个系统调用接口,Linux的创始人在2.0版本之前并没有进行添加,仅有write和read两个接口,但是后来发现当需要去控制文件的某些操作的时候,很显然这两个接口根本不够用。所以才有了这个万能控制接口ioctl,但是作为Linux的创始人Linus本人一直排斥该接口,因为这个ioctl接口的在内核中的使用相当于对应用层开设了一个能够直接交互的窗口,很影响内核整体的权限控制,不过由于目前还暂时没有更好可以替代的方法,所以还是继续保留了这个接口的使用。
  • 对应驱动开发来说,ioctl的接口使用真的很方便,能够帮助我们解决很多同设备或者驱动在应用层或者用户态下调用的问题。
  • ioctl函数原型:
  • int ioctl(int fd, int request, …)
  • 函数功能:
  • 1.向硬件设备发送控制命令
  • 2.还可以和硬件设备进行读或者写操作
  • 参数:
  • fd:文件描述符
  • request:给硬件设备发送的控制命令,此命令有驱动开发者自行定义,例如我们在LED驱动下可以定义:
  • #define LED_ON 0x100001
  • #define LED_OFF 0x100002
  • 返回值:执行成功返回0,执行失败返回-1。

使用参考:

  • 两个参数调用:
ioctl(fd, LED_ON);//仅仅向硬件设备发送开灯命令
ioctl(fd, LED_OFF);//仅仅向硬件设备发送关灯命令


  • 三个参数调用:
int index = 1;//定义初始化用户缓冲区
ioctl(fd, LED_ON, &index);//不仅仅向硬件设备发送  开灯命令,还向硬件设备写入数据1
也可以用来从设备中读取数据:


int state;
ioctl(fd, LED_ON, &state);//不仅仅向硬件设备发送开灯命令,还从硬件设备读取数据保存在state中
printf("state = %d\n", state);


对应的底层驱动的ioctl接口

struct file_operations {
    long (*unlocked_ioctl) (struct file *file, 
    unsigned int cmd, 
    unsigned long arg);
 };

  • 接口功能:

  • 1.向硬件设备发送控制命令
  • 2.还可以和硬件设备进行读或者写操作
  • 调用关系:应用ioctl->软中断->内核的sys_ioctl->驱动的unlocked_ioctl

  • 参数:

  • file:文件指针,和应用ioctl的fd是亲戚关系
  • cmd:保存应用发送来的控制命令,和应用ioctl的第二个参数相等也就是:cmd=LED_ON或者cmd=LED_OFF
  • arg:注意前提:如果应用程序要和硬件设备进行读或者写操作,arg保存的就是用户缓冲区的首地址(arg=&index或者arg=&state),但是驱动程序同样不能直接来访问arg(int kindex=*(int )arg或者(int *)arg = 1),太危险;如果要进行数据拷贝,同样 利用内核提供的内存拷贝函数:copy_from_user/copy_to_user注意arg的数据类型要进行强制转换!
  • 当然啦:如果应用ioctl传递的是两个参数(仅仅是发送控制命令),arg参数无需搭理!

LED灯使用示例:

对应LED灯控制来说,当使用了ioctl进行控制后,就没必要准备read和write使用,并且对open也不做定义了。


  • led_drv.c
#include 
#include 
#include 
#include 
#include 
#include 
#include  //copy_from_user声明
//声明描述LED硬件信息的数据结构
struct led_resource {
    int gpio; //GPIO编号
    char *name; //LED名称
};
//定义初始化LED的硬件信息对象
static struct led_resource led_info[] = {
    {
        .gpio = PAD_GPIO_C + 12,
        .name = "LED1"
    }
};
//调用关系:应用ioctl->软中断->内核sys_ioctl->驱动led_ioctl
//例子:int index=1;ioctl(fd, LED_ON, &index);//开第1个灯
//参数关系:
//      fd<---->file 亲戚关系
//      cmd=LED_ON或者cmd=LED_OFF
//      arg=(unsigned long)&index
#define LED_ON  0x100001 //开灯命令
#define LED_OFF 0x100002 //关灯命令
static long led_ioctl(struct file *file,
                        unsigned int cmd,
                        unsigned long arg)
{
    //1.分配内核缓冲区,保存用法发送过来的灯的编号
    int kindex;
    //2.拷贝用户缓冲区数据到内核缓冲区
    //kindex = *(int *)arg; //相当危险
    copy_from_user(&kindex, (int *)arg, sizeof(kindex));
    //3.解析用户发送过来的命令
    switch(cmd) {
        case LED_ON:
            gpio_set_value(led_info[kindex-1].gpio, 0);
            printk("%s:开第%d个灯", __func__, kindex);
            break;
        case LED_OFF:
            gpio_set_value(led_info[kindex-1].gpio, 1);
            printk("%s:关第%d个灯", __func__, kindex);
            break;
        default:
            printk("无效命令!\n");
            return -1;
    }
    return 0; //执行成功返回0,执行失败返回负值
}
//定义初始化LED的硬件操作接口对象
static struct file_operations led_fops = {
    .unlocked_ioctl = led_ioctl, //不仅仅向硬件设备发送控制命令,还能和设备进行读写操作
};
//定义设备号对象
static dev_t dev;
//定义字符设备对象
static struct cdev led_cdev;
static int led_init(void)
{
    int i;
    //1.申请设备号
    alloc_chrdev_region(&dev, 0, 1, "myled");
    //2.初始化字符设备对象,本质就是给字符设备添加操作接口
    cdev_init(&led_cdev, &led_fops);
    //3.向内核注册字符设备对象并且提供硬件操作接口
    cdev_add(&led_cdev, dev, 1);
    //4.申请GPIO资源配置为输出,输出1
    for(i = 0; i < ARRAY_SIZE(led_info); i++) {
        gpio_request(led_info[i].gpio, 
                        led_info[i].name);
        gpio_direction_output(led_info[i].gpio, 1);
    }
    return 0;
}
static void led_exit(void)
{
    int i;
    //1.输出1,释放GPIO资源
    for (i = 0; i < ARRAY_SIZE(led_info); i++) {
        gpio_set_value(led_info[i].gpio, 1);
        gpio_free(led_info[i].gpio);
    }
    //2.释放设备号
    unregister_chrdev_region(dev, 1);
    //3.卸载字符设备对象
    cdev_del(&led_cdev);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");


  • led_test.c
#include 
#include 
#include 
#include 
#include 
#define LED_ON  0x100001 //开灯命令
#define LED_OFF 0x100002 //关灯命令
int main(int argc, char *argv[])
{
    int fd;
    int index; //分配用户缓冲区,保存操作灯的编号
    if(argc != 3) {
        printf("用法:%s  <1|2|3|4>\n", argv[0]);
        return -1;
    }
    //打开设备
    fd = open("/dev/myled", O_RDWR);
    if (fd < 0) {
        printf("打开设备失败!\n");
        return -1;
    }
    //"1"->1
    index = strtoul(argv[2], NULL, 0);
    //应用ioctl->软中断->内核sys_ioctl->驱动led_ioctl
    if(!strcmp(argv[1], "on"))
        ioctl(fd, LED_ON, &index);
    else if(!strcmp(argv[1], "off"))
        ioctl(fd, LED_OFF, &index);
    //关闭设备
    close(fd);
    return 0;
}


  • Makefile
obj-m += led_drv.o
all:
  make -C /opt/kernel SUBDIRS=$(PWD) modules
clean:
  make -C /opt/kernel SUBDIRS=$(PWD) clean


操作流程:

在开发板上执行:


insmod led_drv.ko
cat /proc/devices
244 myled
# 创建设备文件
mknod /dev/myled c 244 0

20191227214140373.png

测试执行:

./led_test 
./led_test on 3
./led_test off 3

20191227214513589.png

相关文章
|
5天前
|
Linux 编译器 Android开发
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
在Linux环境下,本文指导如何交叉编译x265的so库以适应Android。首先,需安装cmake和下载android-ndk-r21e。接着,下载x265源码,修改crosscompile.cmake的编译器设置。配置x265源码,使用指定的NDK路径,并在配置界面修改相关选项。随后,修改编译规则,编译并安装x265,调整pc描述文件并更新PKG_CONFIG_PATH。最后,修改FFmpeg配置脚本启用x265支持,编译安装FFmpeg,将生成的so文件导入Android工程,调整gradle配置以确保顺利运行。
24 1
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
|
1月前
|
数据采集 Shell Linux
【Shell 命令集合 文档编辑】Linux 字符转换或删除 tr 命令使用指南
【Shell 命令集合 文档编辑】Linux 字符转换或删除 tr 命令使用指南
33 0
|
1月前
|
Linux C语言 C++
Linux Shell_cut命令(按列提取文本字符)
Linux Shell_cut命令(按列提取文本字符)
22 0
|
4天前
|
Linux Shell Android开发
自动化脚本之GPIO/LED相关适用于Android/Linux
自动化脚本之GPIO/LED相关适用于Android/Linux
13 0
|
18天前
|
存储 缓存 固态存储
Linux设备全览:从字符到块,揭秘每种设备的秘密
在Linux的世界里,设备是构成系统的基础,它们使得计算机能够与外界互动。Linux设备可以大致分为几种类型,每种类型都有其独特的特性和用途。🌌让我们一起探索这些设备类型及其特性。
|
21天前
|
Linux API C语言
FFmpeg开发笔记(一)搭建Linux系统的开发环境
本文指导初学者如何在Linux上搭建FFmpeg开发环境。首先,由于FFmpeg依赖第三方库,可以免去编译源码的复杂过程,直接安装预编译的FFmpeg动态库。推荐网站<https://github.com/BtbN/FFmpeg-Builds/releases>提供适用于不同系统的FFmpeg包。但在安装前,需确保系统有不低于2.22版本的glibc库。详细步骤包括下载glibc-2.23源码,配置、编译和安装。接着,下载Linux版FFmpeg安装包,解压至/usr/local/ffmpeg,并设置环境变量。最后编写和编译简单的C或C++测试程序验证FFmpeg环境是否正确配置。
37 8
FFmpeg开发笔记(一)搭建Linux系统的开发环境
|
1月前
|
存储 缓存 Linux
探秘Linux块设备驱动程序:成为内核开发大师的第一步
探秘Linux块设备驱动程序:成为内核开发大师的第一步
93 0
|
7月前
|
Linux C语言
Linux驱动 | procfs接口创建
Linux驱动 | procfs接口创建
|
7月前
|
Linux C语言
Linux驱动 | debugfs接口创建
Linux驱动 | debugfs接口创建