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

相关文章
|
1天前
|
Linux 数据库
Linux内核中的锁机制:保障并发操作的数据一致性####
【10月更文挑战第29天】 在多线程编程中,确保数据一致性和防止竞争条件是至关重要的。本文将深入探讨Linux操作系统中实现的几种关键锁机制,包括自旋锁、互斥锁和读写锁等。通过分析这些锁的设计原理和使用场景,帮助读者理解如何在实际应用中选择合适的锁机制以优化系统性能和稳定性。 ####
14 6
|
1天前
|
监控 网络协议 算法
Linux内核优化:提升系统性能与稳定性的策略####
本文深入探讨了Linux操作系统内核的优化策略,旨在通过一系列技术手段和最佳实践,显著提升系统的性能、响应速度及稳定性。文章首先概述了Linux内核的核心组件及其在系统中的作用,随后详细阐述了内存管理、进程调度、文件系统优化、网络栈调整及并发控制等关键领域的优化方法。通过实际案例分析,展示了这些优化措施如何有效减少延迟、提高吞吐量,并增强系统的整体健壮性。最终,文章强调了持续监控、定期更新及合理配置对于维持Linux系统长期高效运行的重要性。 ####
|
20小时前
|
缓存 算法 Linux
深入理解Linux内核调度器:公平性与性能的平衡####
真知灼见 本文将带你深入了解Linux操作系统的核心组件之一——完全公平调度器(CFS),通过剖析其设计原理、工作机制以及在实际系统中的应用效果,揭示它是如何在众多进程间实现资源分配的公平性与高效性的。不同于传统的摘要概述,本文旨在通过直观且富有洞察力的视角,让读者仿佛亲身体验到CFS在复杂系统环境中游刃有余地进行任务调度的过程。 ####
16 6
|
Shell Linux Perl
在Linux命令行中进行大小写字符转换
在Linux命令行中进行大小写字符转换
在Linux命令行中进行大小写字符转换
|
Shell Linux Perl
Linux命令行:对内容进行大小写字符转换 ​​​​
Linux命令行:对内容进行大小写字符转换 ​​​​
Linux命令行:对内容进行大小写字符转换 ​​​​
|
18天前
|
运维 安全 Linux
Linux中传输文件文件夹的10个scp命令
【10月更文挑战第18天】本文详细介绍了10种利用scp命令在Linux系统中进行文件传输的方法,涵盖基础文件传输、使用密钥认证、复制整个目录、从远程主机复制文件、同时传输多个文件和目录、保持文件权限、跨多台远程主机传输、指定端口及显示传输进度等场景,旨在帮助用户在不同情况下高效安全地完成文件传输任务。
127 5
|
18天前
|
Linux
Linux系统之expr命令的基本使用
【10月更文挑战第18天】Linux系统之expr命令的基本使用
61 4
|
5天前
|
缓存 监控 Linux
|
8天前
|
Linux Shell 数据安全/隐私保护
|
9天前
|
域名解析 网络协议 安全
下一篇
无影云桌面