ioctl

  • 除了讀取和寫入設備之外,大部分驅(qū)動程序還需要另外一種能力,就是通過設備驅(qū)動程序執(zhí)行各種類型的硬件控制。
  • 除簡單數(shù)據(jù)傳輸之外,大部分設備可以執(zhí)行其他一些操作,比如,用戶空間經(jīng)常會請求設備鎖門、彈出介質(zhì)、報告錯誤信息、改變波特率等等。
  • 這些操作通常通過ioctl方法來實現(xiàn)
  • 用戶空間,ioctl系統(tǒng)調(diào)用具有如下原型:
    int ioctl(int fd,int cmd, ...);
    參數(shù):fd 使要操作的文件描述符
    cmd:控制命令
    ...:可選參數(shù),具體形式依賴于第二個參數(shù)cmd,因為有些控制命令需要參數(shù),有些命令不需要參數(shù)。
  • 內(nèi)核驅(qū)動程序,ioctl方法原型如下:
    int (*ioctl)(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg);
    參數(shù)inode和filp:和open方法的參數(shù)一樣,表示文件節(jié)點和打開的file結(jié)構(gòu)
    參數(shù)cmd:由用戶空間不經(jīng)修改地傳遞進來
    參數(shù)arg:使用戶空間傳遞的整數(shù)值;如果用戶空間的調(diào)用程序沒有第三個參數(shù),則驅(qū)動的arg參數(shù)為未定義狀態(tài)。

ioctl參數(shù)傳遞

ioctl 命令

  • 在驅(qū)動中編寫ioctl方法前,需要選擇對應不同命令的編號。
  • 為了防止對錯誤的設備使用正確的命令,命令號必須在系統(tǒng)范圍內(nèi)唯一。
  • 為了方便程序員創(chuàng)建唯一的iotcl命令號,內(nèi)核規(guī)定每一個命令號被分為4個位段,各位段的含義在頭文件<asm-generic/ioctl.h>中說明,對于ARM來說,4個位段的含義分別是:


  • 在內(nèi)核頭文件<asm-generic/ioctl.h>中,提供了一些宏來構(gòu)造命令號:
_IO(type,nr):用于構(gòu)造無參數(shù)的命令號
_IOR(type,nr,datetype):用于構(gòu)造從驅(qū)動程序中讀取數(shù)據(jù)的命令號
_IOW(type,nr,datatype):用于構(gòu)造向驅(qū)動程序?qū)懭霐?shù)據(jù)的命令號
_IORW(type,nr,datatype):用于構(gòu)造雙向傳輸?shù)拿钐?
  • 解開位字段的宏:
_IOC_DIR(cmd):獲得傳輸方向位段的值
_IOC_TYPE(cmd):獲得類型的值
_IOC_NR(cmd);獲得編號的值
_IOC_SIZE(cmd):獲得大小的值
  • 以按鍵為例子,在tiny4412定義一組ioctl命令,如下所示:



    命令類型為‘f’
    TINY4412_BTN_IORESET 是復位命令,復位驅(qū)動按鍵值為0
    TINY4412_BTN_IOGET 是獲取按鍵值命令
    TINY4412_BTN_IOSET 是設置按鍵值命令

  • 使用ioctl參數(shù)
    • ioctl的第三個參數(shù)表示與命令對應的參數(shù),通常這個參數(shù)是一個用戶空間的指針;
    • 當參數(shù)是一個指向用戶空間的指針時,在使用這個指針前,必須確保指向的用戶空間是合法的。
    • 可以通過函數(shù)access_ok()來驗證這個地址是否合法;
      int access_ok(int type,const void *addr,unsigned long size);
    • 第一個參數(shù)type: VERIFY_READ--從指向的空間中讀取
      VERIFY_WRITE:寫入到用戶空間
    • addr:就是要驗證的地址
    • size:指針指向用戶內(nèi)存空間的字節(jié)數(shù)
    • 返回值:1表示成功,0表示失敗
  • 在2.6.36以后ioctl函數(shù)已經(jīng)不再存在了,而是用unlocked_ioctl和compat_ioctl兩個函數(shù)實現(xiàn)以前版本的ioctl函數(shù)。
    • unlocked_ioctl:不阻塞的ioctl
    • compat_ioctl:兼容性的ioctl(用戶空間為32位模式,而內(nèi)核運行在64位模式時)。

實戰(zhàn)代碼

btn_drv.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/types.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <mach/hardware.h>
#include <linux/gpio.h>

#include <mach/map.h>
#include <mach/gpio.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>

#include "btn.h"

struct btn_t {
    int gpio;
    int key;
    char *name;
};
static struct btn_t btns[] = {
    { EXYNOS4_GPX3(2), 1 ,"K1" },
    { EXYNOS4_GPX3(3), 2 ,"K2" },
    { EXYNOS4_GPX3(4), 3 ,"K3" },
    { EXYNOS4_GPX3(5), 4 ,"K4" },
};

static DECLARE_WAIT_QUEUE_HEAD(btn_waitq);
static volatile char key_value = 0;
static volatile int ev_press = 0;

static irqreturn_t btn_irq_handler(int irq, void *dev)
{
    struct btn_t * p = (struct btn_t *)dev;
    key_value = p->key;
    ev_press = 1;
    wake_up_interruptible(&btn_waitq);
    
    return IRQ_HANDLED;
}

static int btn_drv_open(struct inode *inode, struct file *file)
{
    return 0;
}

static int btn_drv_close(struct inode *inode, struct file *file)
{
    return 0;
}

static int btn_drv_read(struct file *filp, char __user *buff,size_t count, loff_t *offp)
{
    //休眠直到ev_press為真
    wait_event_interruptible(btn_waitq, ev_press);
    ev_press = 0;
    copy_to_user(buff,&key_value,sizeof(key_value));
    return sizeof(key_value);
}

static long btn_drv_ioctl (struct file *filp, unsigned int cmd, unsigned long arg)
{
    int err = 0;
    int retval = 0;
    if(_IOC_TYPE(cmd) != TINY4412_BTN_TYPE )
        return -EINVAL;
    if(_IOC_NR(cmd) >TINY4412_BTN_MAXNUM )
        return -EINVAL;
    if(_IOC_DIR(cmd) &_IOC_READ)
        {
            if(!access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)))
                return -EFAULT;
        }
    else if(_IOC_DIR(cmd)&_IOC_WRITE)
        {
            if(!access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)))
                return -EFAULT;
        }

    switch(cmd)
    {
        case TINY4412_BTN_IORESET:
            key_value = 0;
            printk("TINY4412 btn drv ioctl IORESET!\n ");
            break;
            
        case TINY4412_BTN_IOGET:
            key_value++;
            copy_to_user((void __user *)arg, &key_value, sizeof(char));
            printk("TINY4412 btn drv ioctl IOGET key_val++\n");
            break;
            
        case TINY4412_BTN_IOSET:
            copy_from_user(&key_value, (void __user *)arg, sizeof(char));
            printk("TINY4412 btn drv ioctl IOSET\n");
            break;
        default:
            printk("TINY4412 btn drv ioctl error cmd\n");
            return -EINVAL;
    }

    return retval;
}


static struct file_operations btn_fops = {
    .owner      = THIS_MODULE,
    .open       = btn_drv_open,
    .release    = btn_drv_close, 
    .read       = btn_drv_read,
    .unlocked_ioctl = btn_drv_ioctl,
};

static int major = 0;
static struct class *btn_class = NULL;

static int __init btn_drv_init(void)
{
    int irq;
    int i;
    for(i = 0;i<4;i++){
        irq = gpio_to_irq(btns[i].gpio);
        request_irq(irq, btn_irq_handler, IRQ_TYPE_EDGE_FALLING, 
                btns[i].name, &btns[i]);
    }
    
    major = register_chrdev(0, "btn",&btn_fops);
    btn_class = class_create(THIS_MODULE, "btn");
    device_create(btn_class, NULL, MKDEV(major, 0),NULL,"btn");
    return 0;
}

static void __exit btn_drv_exit(void)
{
    int irq;
    int i;
    for(i = 0;i<4;i++){
        irq = gpio_to_irq(btns[i].gpio);
        free_irq(irq, &btns[i]);
    }
    device_destroy(btn_class, MKDEV(major,0));
    class_destroy(btn_class);
    unregister_chrdev(major, "btn");
}

module_init(btn_drv_init);
module_exit(btn_drv_exit);
MODULE_LICENSE("GPL");

ioctl_test.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>

#include "btn.h"

int main(void)
{
    int fd;
    fd = open("/dev/btn",O_RDWR);
    if(fd<0)
    {
        perror("open");
        exit(-1);
    }
    char val;
    /*復位*/
    ioctl(fd,TINY4412_BTN_IORESET);
    /*復位后,獲取并打印*/
    ioctl(fd,TINY4412_BTN_IOGET,&val);
    sleep(1);
    printf("app: reset and get val %d\n",val);  

    /*設置值為10*/
    val = 10;
    ioctl(fd,TINY4412_BTN_IOSET,&val);

    /*設置值后,再獲取打印值*/
    ioctl(fd,TINY4412_BTN_IOGET,&val);
    sleep(1);   
    printf("app:set and get val %d\n",val);

    return 0;
}

btn.h

#ifndef __BTN_H
#define __BTN_H

#include <linux/ioctl.h>

#define TINY4412_BTN_TYPE 'f'

#define TINY4412_BTN_IORESET    _IO(TINY4412_BTN_TYPE,0)
#define TINY4412_BTN_IOGET  _IOR(TINY4412_BTN_TYPE, 1, char )
#define TINY4412_BTN_IOSET  _IOW(TINY4412_BTN_TYPE, 2, char )

#define TINY4412_BTN_MAXNUM     3

#endif
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容