12.LINUX驱动之好用的ioctl

342 阅读2分钟

unlock_ioctl函数

它是驱动程序中fops结构体的一个与应用层通讯的函数指针之一。使用Ioctl可以向驱动程序发送控制信号,而不必向之前我们写的程序一样通过read,write函数进行读写指针中的命令。

在驱动fops结构体的函数指针定义为。 filp是对应的设备文件,cmd 是应用程序发送过来的命令信息,arg 是应用程序发送过来的参数

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

其中cmd,通过查看别人定义的cmd,如下。__IOR是读IO ; __IOW是写IO;__IORW是写IO; 类型有很多。在ARM架构下cmd是14位的数据,由四个字段组成---幻数,序数,传输方向,数据大小。

#define RTC_IRQP_READ   _IOR('p', 0x0b, unsigned long)   /* Read IRQ rate   */
#define RTC_IRQP_SET    _IOW('p', 0x0c, unsigned long)   /* Set IRQ rate    */
#define RTC_EPOCH_READ  _IOR('p', 0x0d, unsigned long)   /* Read epoch      */
#define RTC_EPOCH_SET   _IOW('p', 0x0e, unsigned long)   /* Set epoch       */

在应用层中接口为

int ioctl(int d, int request, ...);

一、驱动程序

在驱动程序中定义IO的命令,在驱动中也要定义同样的指令进行传输。

本次实验测试三个指令:

打开定时器,关闭定时器,以及修改定时器的值。

因此在定时器处理函数中要求不可以设置绕回函数。

#define TIMER_CLOSE         _IO(0xEF, 1)
#define TIMER_OPEN          _IO(0xEF, 2)
#define TIMER_SET_PERIOD    _IOW(0xEF , 3 , int)
/*
    Timer中断处理函数
*/
static void timer_handler(unsigned long dummy){
    static int i = 0;
    struct timer_dev *dev = (struct timer_dev*)dummy;
​
    printk("i = %d \r\n" , i++);
}
​
long timer_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){
    int value = 0;
    int ret = 0;
    struct timer_dev *dev = (struct timer_dev *)filp->private_data;
​
    switch(cmd){
        case TIMER_CLOSE:
            del_timer(&dev->timerdev);
            break;
        case TIMER_OPEN:
            mod_timer(&dev->timerdev , jiffies + msecs_to_jiffies(dev->timeperiod));
            break;
        case TIMER_SET_PERIOD:
            ret = copy_from_user(&value , (int *)arg , sizeof(int));
            if(ret < 0){
                return -EFAULT;
            }
            dev->timeperiod = value;
            mod_timer(&dev->timerdev , jiffies + msecs_to_jiffies(dev->timeperiod));
            break;
    }
    
    return 0;
}
static const struct file_operations timer_fops = {
    .owner      = THIS_MODULE,
    .open       = timer_open,
    .release    = timer_release,
    .read       = timer_read,
    .write      = timer_write,
    .unlocked_ioctl = timer_ioctl,
};
​
​
    /*  5.初始化定时器  */
    init_timer(&timer.timerdev);
​
    timer.timeperiod = 1000;
    timer.timerdev.expires = jiffies + msecs_to_jiffies(timer.timeperiod);   //定时1s
    timer.timerdev.function = timer_handler;
    timer.timerdev.data = (unsigned long)&timer;
    add_timer(&timer.timerdev);     //添加到系统

二、应用测试程序

#define TIMER_CLOSE         _IO(0xEF, 1)
#define TIMER_OPEN          _IO(0xEF, 2)
#define TIMER_SET_PERIOD    _IOW(0xEF , 3 , int)int main(int argc, char *argv[])
{
    int timer_fd = 0 ,led_fd = 0, ret = 0 ;
    //int tmp = atoi(argv[1]);
    char read_buff[1] , write_buff[1];
    unsigned int cmd;
    unsigned int arg;
    unsigned char str[100];
    
    timer_fd = open("/dev/timer" , O_RDWR);
    if(timer_fd < 0){
        printf("打开KEY设备结点失败\n");
        return -1;
    }
​
    while(1){
        printf("Input Cmd!\r\n");
        ret = scanf("%d" ,&cmd );
        if(ret != 1){
            fgets(str , sizeof(str) , stdin);
        }
​
        if(cmd == 1)
            ioctl(timer_fd , TIMER_CLOSE , &arg);
        else if (cmd == 2)
            ioctl(timer_fd , TIMER_OPEN , &arg);
        else if (cmd == 3){
            printf("Please set timer period\r\n");
            ret = scanf("%d" , &arg);
            if(ret != 1){
                fgets(str , sizeof(str) , stdin);
            }
            ioctl(timer_fd , TIMER_SET_PERIOD , &arg);
        }
    
    }
    close(timer_fd);
    return 0;
}
​

\