- 除了讀取和寫入設(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