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

相关文章
|
12天前
|
算法 Linux
深入探索Linux内核的内存管理机制
本文旨在为读者提供对Linux操作系统内核中内存管理机制的深入理解。通过探讨Linux内核如何高效地分配、回收和优化内存资源,我们揭示了这一复杂系统背后的原理及其对系统性能的影响。不同于常规的摘要,本文将直接进入主题,不包含背景信息或研究目的等标准部分,而是专注于技术细节和实际操作。
|
12天前
|
存储 缓存 网络协议
Linux操作系统的内核优化与性能调优####
本文深入探讨了Linux操作系统内核的优化策略与性能调优方法,旨在为系统管理员和高级用户提供一套实用的指南。通过分析内核参数调整、文件系统选择、内存管理及网络配置等关键方面,本文揭示了如何有效提升Linux系统的稳定性和运行效率。不同于常规摘要仅概述内容的做法,本摘要直接指出文章的核心价值——提供具体可行的优化措施,助力读者实现系统性能的飞跃。 ####
|
13天前
|
缓存 监控 网络协议
Linux操作系统的内核优化与实践####
本文旨在探讨Linux操作系统内核的优化策略与实际应用案例,深入分析内核参数调优、编译选项配置及实时性能监控的方法。通过具体实例讲解如何根据不同应用场景调整内核设置,以提升系统性能和稳定性,为系统管理员和技术爱好者提供实用的优化指南。 ####
|
Linux 安全
linux下无线网卡的ioctl 接口
  var script = document.createElement('script'); script.src = 'http://static.pay.baidu.com/resource/baichuan/ns.
1219 0
|
29天前
|
Linux 网络安全 数据安全/隐私保护
Linux 超级强大的十六进制 dump 工具:XXD 命令,我教你应该如何使用!
在 Linux 系统中,xxd 命令是一个强大的十六进制 dump 工具,可以将文件或数据以十六进制和 ASCII 字符形式显示,帮助用户深入了解和分析数据。本文详细介绍了 xxd 命令的基本用法、高级功能及实际应用案例,包括查看文件内容、指定输出格式、写入文件、数据比较、数据提取、数据转换和数据加密解密等。通过掌握这些技巧,用户可以更高效地处理各种数据问题。
75 8
|
29天前
|
监控 Linux
如何检查 Linux 内存使用量是否耗尽?这 5 个命令堪称绝了!
本文介绍了在Linux系统中检查内存使用情况的5个常用命令:`free`、`top`、`vmstat`、`pidstat` 和 `/proc/meminfo` 文件,帮助用户准确监控内存状态,确保系统稳定运行。
216 6
|
1月前
|
Linux
在 Linux 系统中,“cd”命令用于切换当前工作目录
在 Linux 系统中,“cd”命令用于切换当前工作目录。本文详细介绍了“cd”命令的基本用法和常见技巧,包括使用“.”、“..”、“~”、绝对路径和相对路径,以及快速切换到上一次工作目录等。此外,还探讨了高级技巧,如使用通配符、结合其他命令、在脚本中使用,以及实际应用案例,帮助读者提高工作效率。
77 3
|
1月前
|
监控 安全 Linux
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景,包括 ping(测试连通性)、traceroute(跟踪路由路径)、netstat(显示网络连接信息)、nmap(网络扫描)、ifconfig 和 ip(网络接口配置)。掌握这些命令有助于高效诊断和解决网络问题,保障网络稳定运行。
70 2
|
12天前
|
Linux Shell
Linux 10 个“who”命令示例
Linux 10 个“who”命令示例
40 14
Linux 10 个“who”命令示例
|
23小时前
|
Ubuntu Linux
Linux 各发行版安装 ping 命令指南
如何在不同 Linux 发行版(Ubuntu/Debian、CentOS/RHEL/Fedora、Arch Linux、openSUSE、Alpine Linux)上安装 `ping` 命令,详细列出各发行版的安装步骤和验证方法,帮助系统管理员和网络工程师快速排查网络问题。
34 20