ioctl

  • 除了讀取和寫入設(shè)備之外萨西,大部分驅(qū)動程序還需要另外一種能力芙代,就是通過設(shè)備驅(qū)動程序執(zhí)行各種類型的硬件控制之拨。
  • 除簡單數(shù)據(jù)傳輸之外茉继,大部分設(shè)備可以執(zhí)行其他一些操作,比如蚀乔,用戶空間經(jīng)常會請求設(shè)備鎖門烁竭、彈出介質(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方法前肠虽,需要選擇對應(yīng)不同命令的編號幔戏。
  • 為了防止對錯誤的設(shè)備使用正確的命令,命令號必須在系統(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 是復(fù)位命令,復(fù)位驅(qū)動按鍵值為0
    TINY4412_BTN_IOGET 是獲取按鍵值命令
    TINY4412_BTN_IOSET 是設(shè)置按鍵值命令

  • 使用ioctl參數(shù)
    • ioctl的第三個參數(shù)表示與命令對應(yīng)的參數(shù),通常這個參數(shù)是一個用戶空間的指針想暗;
    • 當(dāng)參數(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;
    /*復(fù)位*/
    ioctl(fd,TINY4412_BTN_IORESET);
    /*復(fù)位后慈参,獲取并打印*/
    ioctl(fd,TINY4412_BTN_IOGET,&val);
    sleep(1);
    printf("app: reset and get val %d\n",val);  

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

    /*設(shè)置值后,再獲取打印值*/
    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)系作者
  • 序言:七十年代末刮萌,一起剝皮案震驚了整個濱河市驮配,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌着茸,老刑警劉巖壮锻,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異涮阔,居然都是意外死亡躯保,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進店門澎语,熙熙樓的掌柜王于貴愁眉苦臉地迎上來途事,“玉大人,你說我怎么就攤上這事擅羞∈洌” “怎么了?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵减俏,是天一觀的道長召烂。 經(jīng)常有香客問我,道長娃承,這世上最難降的妖魔是什么奏夫? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮历筝,結(jié)果婚禮上酗昼,老公的妹妹穿的比我還像新娘。我一直安慰自己梳猪,他們只是感情好麻削,可當(dāng)我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般呛哟。 火紅的嫁衣襯著肌膚如雪叠荠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天扫责,我揣著相機與錄音榛鼎,去河邊找鬼。 笑死鳖孤,一個胖子當(dāng)著我的面吹牛者娱,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播淌铐,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蔫缸!你這毒婦竟也來了腿准?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤拾碌,失蹤者是張志新(化名)和其女友劉穎吐葱,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體校翔,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡弟跑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了防症。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片孟辑。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蔫敲,靈堂內(nèi)的尸體忽然破棺而出饲嗽,到底是詐尸還是另有隱情,我是刑警寧澤奈嘿,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布貌虾,位于F島的核電站,受9級特大地震影響裙犹,放射性物質(zhì)發(fā)生泄漏尽狠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一叶圃、第九天 我趴在偏房一處隱蔽的房頂上張望袄膏。 院中可真熱鬧,春花似錦掺冠、人聲如沸哩陕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽悍及。三九已至闽瓢,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間心赶,已是汗流浹背扣讼。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留缨叫,地道東北人椭符。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像耻姥,于是被迫代替她去往敵國和親销钝。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,955評論 2 355

推薦閱讀更多精彩內(nèi)容