ioctl函数-控制设备的I/O通道

本文详细介绍了ioctl函数在Linux系统中的工作原理,包括用户层如何通过ioctl与驱动程序交互,以及内核层中ioctl函数的实现,涉及请求码设计、宏定义和实际操作示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ioctl函数

注:用户程序所作的只是通过命令码告诉驱动程序它想做什么,至于怎么解释这些命令和怎么实现这些命令,这都是驱动程序要做的事情。驱动程序提供了对ioctl的支持,用户就可以在用户程序中使用ioctl函数控制设备的I/O通道  //  GREE_ON  BLUE_ON

功能:input output 的控制

user(应用层):

#include <sys/ioctl.h>
int ioctl(int fd, int request, ...);(RED_ON)
(让点灯的代码变得简洁)
	参数:
		@fd     : 打开文件产生的文件描述符
		@request: 请求码(读写|第三个参数传递的字节的个数)
				 :在sys/ioctl.h中有这个请求码的定义方式。
		@...    :可写、可不写,如果要写,写一个内存的地址

--------------------------------------------------------

Kernel(内核层):

在驱动程序中实现的ioctl函数体内,实际上是有一个switch{case}结构,每一个case对应一个命令码,做出一些相应的操作。怎么实现这些操作,这是每一个程序员自己的事情

fops:

long (*unlocked_ioctl) (struct file *file, unsigned int request, unsigned long args);

对于使用ioctl函数时,主要的就是请求码的设计,请求码主要在sys/ioctl.h文件里面进行了设计。

表示我本次是读还是写的字节的大小;再往下看当调用_IOC的时候怎样把四个域组合在一起的。

一个一个看,鼠标放在_IOC_DIRSHIFT,进行跳转,出现下面的同学

#define _IO(type,nr)

_IOC(_IOC_NONE,(type),(nr),0)

#define _IOR(type,nr,size)

_IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))

#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))

#define _IOWR(type,nr,size)       _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))

#define RDE_LED  _IO(type,nr)

这些宏是帮助你完成请求码的封装的。#define _IOC(dir,type,nr,size) \

(((dir)  << _IOC_DIRSHIFT) | \

((type) << _IOC_TYPESHIFT) | \

((nr)   << _IOC_NRSHIFT) | \

((size) << _IOC_SIZESHIFT))

dir << 30 | size<<16 | type << 8 | nr << 

 2           14         8          8

方向        大小       类型        序号

(方向:00 01 10 11读写相关,)

         (大小:sizeof(变量名))

        (类型:组合成一个唯一的不重合的整数,一般传一个字符)

        (序号:表示同类型中的第几个,当开灯的时候写0,那关的时候就不写0)。

#define RLED_ON  _IOWR('a',0,int)//亮灯

#define RLED_OFF  _IOWR('a',1,int) //灭灯

内核中已经使用的命令码的域在如下文档中已经声明了。

vi kernel-3.4.39/Documentation/ioctl$ vi ioctl-number.txt 

(2^32次方 = 4G的数字,所以可以使用,内核的想法是:每一个数字代表一个,功能和数字一一对应,但是不一样的驱动使用的时候相同也是可以的)

ioctl控制

头函数

#ifndef __HEAD_H__
#define __HEAD_H__
#include <asm-generic/ioctl.h>
#define RED_ON _IO('A',0)
#define RED_OFF _IO('A',1)

#define GREE_ON _IO('A',2)
#define GREE_OFF _IO('A',3)

#define BLUE_ON _IO('A',4)
#define BLUE_OFF _IO('A',5)
#endif

应用层

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
 
#include <stdio.h>
#include "head.h"
int main(int argc,char *argv[])
{
    int fd = open("/dev/led",O_RDWR);
    if(fd < 0)
    {
        printf("open %s failed\n","led");
        return 2;
    }
    while(1)
    {
        ioctl(fd,RED_ON);
        sleep(1);
        ioctl(fd,RED_OFF);
        sleep(1);
        ioctl(fd,GREE_ON);
        sleep(1);
        ioctl(fd,GREE_OFF);
        sleep(1);
        ioctl(fd,BLUE_ON);
        sleep(1);
        ioctl(fd,BLUE_OFF);
        sleep(1);
    }
    
    close(fd);
    return 0;
}

内核层

#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <asm/io.h>
#include <linux/device.h>
#include "head.h"
#define NAME "chrdev_dev"

//定义宏保存物理地址基地址
#define RED_BASE 0xc001a000
#define GRE_BASE 0xc001e000
#define BLU_BASE 0xc001b000

//定义指针保存映射后的虚拟地址首地址
unsigned int *red_addr=NULL;
unsigned int *gre_addr=NULL;
unsigned int *blu_addr=NULL;
struct class *cls=NULL;
struct device *dev=NULL;

int major = 0;
char kbuf[32];
char kbuf_r[32]="welcome to hqyj!";

//open  read  write  close
int myopen(struct inode *node, struct file *file_t)
{
    printk("%s %s %d\n", __FILE__, __func__, __LINE__);
    return 0;
}
long myioctl(struct file *file_t,unsigned int request,unsigned long data)
{
    printk("%s %s %d\n",__FILE__, __func__, __LINE__);
    switch(request)
    {
        case RED_ON:
        *red_addr |= (1<<28);
        break;
        case RED_OFF:
        *red_addr &= (~(1<<28));
        break;
        case GREE_ON:
        *gre_addr |= (1<<13);
        break;
        case GREE_OFF:
        *gre_addr &= (~(1<<13));
        break;
        case BLUE_ON:
        *blu_addr |= (1<<12);
        break;
        case BLUE_OFF:
        *blu_addr &= (~(1<<12));
        break;

    }
}
int myclose(struct inode *node, struct file *file_t)
{
    printk("%s %s %d\n", __FILE__, __func__, __LINE__);
    return 0;
}

struct file_operations fops = {
    .open = myopen,
    .unlocked_ioctl = myioctl,
    .release = myclose,
};

static int __init chrdev_init(void)
{
    printk("%s %s %d\n", __FILE__, __func__, __LINE__);
    //注册字符设备驱动
    major=register_chrdev(major, NAME, &fops);
    if(major < 0)
    {
        printk("register_chrdev err.\n");
        return -EINVAL ;
    }
    
    //建立虚拟地址和物理地址之间的映射关系-控制红灯
    red_addr=(unsigned int *)ioremap(RED_BASE,40);
    if(red_addr == NULL)
    {
         printk("ioremap red err.\n");
         return -EINVAL ;
    }
    //初始化红灯
    *(red_addr+9) &= (~(3<<24));//选择GPIOA28功能
      //*(red_addr+9) =*(red_addr+9)& (~(3<<24));  
    *(red_addr+1) |= (1<<28); //选择输出使能
    *red_addr &= (~(1<<28)); //红灯关闭
//////////////////////////////////////////////////////////////
    gre_addr=(unsigned int *)ioremap(GRE_BASE,40);
    if(gre_addr == NULL)
    {
         printk("ioremap gred err.\n");
         return -EINVAL ;
    }
    //初始化绿灯
    *(gre_addr+8) &= (~(3<<26));//选择GPIOA28功能 
    *(gre_addr+1) |= (1<<13); //选择输出使能
    *gre_addr &= (~(1<<13)); //红灯关闭
///////////////////////////////////////////////////////////
    blu_addr=(unsigned int *)ioremap(BLU_BASE,40);
    if(blu_addr == NULL)
    {
         printk("ioremap blu err.\n");
         return -EINVAL ;
    }
    //初始化蓝灯
    *(blu_addr+8) &= (~(0<<24));//选择GPIOA28功能 
    *(blu_addr+8) |= (1<<25);//选择GPIOA28功能 
    *(blu_addr+1) |= (1<<12); //选择输出使能
    *blu_addr &= (~(1<<12)); //红灯关闭


    // 自动创建设备节点
    // 1.提交目录信息
    cls = class_create(THIS_MODULE,NAME);
    if (IS_ERR(cls))
    {
        printk("class creat err\n");
        return -EINVAL ;
    }
    // 提交文件信息
    dev=device_create(cls,NULL,MKDEV(major,0),NULL,"led");
    if (IS_ERR(dev))
    {
        printk("device creat err\n");
        return -EINVAL ;
    }

    return 0;
}
static void __exit chrdev_exit(void)
{
    printk("%s %s %d\n", __FILE__, __func__, __LINE__);

    device_destroy(cls,MKDEV(major,0));
    class_destroy(cls);
    
    //取消映射
    iounmap(red_addr);
    iounmap(gre_addr);
    iounmap(blu_addr);
    //注销字符设备驱动
    unregister_chrdev(major,NAME);
}

module_init(chrdev_init);
module_exit(chrdev_exit);
MODULE_LICENSE("GPL");

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

满山的猴子我的腚最红

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值