(八)linux驱动之ioctl的使用
這篇文章給大家講解一下ioctl的簡單使用,關(guān)于ioctl更詳細(xì)的教程后面有機(jī)會單獨寫出來
(一)什么是ioctl
ioctl是設(shè)備驅(qū)動程序中對設(shè)備的I/O通道進(jìn)行管理的函數(shù)。所謂對I/O通道進(jìn)行管理,就是對設(shè)備的一些特性進(jìn)行控制,例如串口的傳輸波特率、馬達(dá)的轉(zhuǎn)速、網(wǎng)卡速率等等。它的調(diào)用方式如下:
//在應(yīng)用調(diào)用 int ioctl(int fd, int cmd, …);其中fd是用戶程序打開設(shè)備時使用open函數(shù)返回的文件標(biāo)示符,cmd是用戶程序?qū)υO(shè)備的控制命令,至于后面的省略號,那是一些補充參數(shù),一般最多一個,這個參數(shù)的有無和cmd的意義相關(guān)。
(二)ioctl的作用
雖然在文件操作結(jié)構(gòu)體"struct file_operations"中有很多對應(yīng)的設(shè)備操作函數(shù),但是有些命令是實在找不到對應(yīng)的操作函數(shù)。如CD-ROM的驅(qū)動,想要一個彈出光驅(qū)的操作,這種操作并不是所有的字符設(shè)備都需要的,所以文件操作結(jié)構(gòu)體也不會有對應(yīng)的函數(shù)操作。出于這樣的原因,ioctl就有它的用處了————一些沒辦法歸類的函數(shù)就統(tǒng)一放在ioctl這個函數(shù)操作中,通過指定的命令來實現(xiàn)對應(yīng)的操作。所以,ioctl函數(shù)里面都實現(xiàn)了多個的對硬件的操作,通過應(yīng)用層傳入的命令來調(diào)用相應(yīng)的操作。
(3)ioctl的優(yōu)點
控制I/O設(shè)備 ,提供了一種獲得設(shè)備信息和向設(shè)備發(fā)送控制參數(shù)的手段。用于向設(shè)備發(fā)控制和配置命令 ,read/write函數(shù)處理的是數(shù)據(jù),ioctl是控制命令
這樣用的可以分工明確:數(shù)據(jù)是數(shù)據(jù),命令是命令
(四)ioctl相關(guān)接口介紹
(1)系統(tǒng)層
#include <sys/ioctl.h>int ioctl(int fd, unsigned long request, ...);int fd:設(shè)備對應(yīng)的打開的文件描述符 unsigned long request:請求碼 若有第三個參數(shù),必須為無類型指針請求碼cmd是 unsigned int 是 32 位。下圖給出了每位的含義
系統(tǒng)中給定了一些請求碼,下面列出一小部分
除了系統(tǒng)給定的,內(nèi)核中也提供了創(chuàng)建請求碼的宏:
/* used to create numbers */ #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))) type: cmd 中魔數(shù),用來代表一個驅(qū)動 8~15位。 nr : cmd 中的序列號,0-7位 size: cmd 中的數(shù)據(jù)尺寸,16-29位內(nèi)核中提供了相應(yīng)的解碼請求碼的宏:
/* used to decode ioctl numbers.. */ 獲取請求碼是從內(nèi)核獲取數(shù)據(jù)或者寫入數(shù)據(jù)到內(nèi)核-----判定讀寫功能 #define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)獲取請求碼對應(yīng)的操作設(shè)備類型 #define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK) 獲取具體的請求碼的功能值(傳遞的是1還是0,或者其他數(shù)據(jù)) #define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK) 獲取請求碼的代銷 #define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)注意:
unlocked_ioctl 用來替換 ioctl 接口的, 在 Linux 3.0 以后 ioctl 已經(jīng)在內(nèi)核中消失。其對應(yīng)系統(tǒng)調(diào)用接口不變。
(五)實例代碼
cdev.c
#include <linux/kernel.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/slab.h> #include <linux/uaccess.h> #include <linux/io.h>int i=0; dev_t dev=0; char stat[4]={-1,-1,-1,-1}; #define CDEVCOUNT 5 #define CDEVNAME "cdevdevice" #define CDEVCLASS "myclass" #define INODENAME "mycdev" #define ADDRSIZE 8 unsigned int phy_dadr=0x110002E0;//映射的起始地址,為GPM4CON的偏移地址 unsigned int *virt_addr=NULL; //用來接收映射后首地址 struct cdev * cdev=NULL; struct class * cdevclass=NULL; #define GPM4CON (*(volatile unsigned int *)virt_addr) #define GPM4DAT (*(volatile unsigned int *)(virt_addr+1))int cdev_open (struct inode *node, struct file *file) {//先將配置寄存器清空GPM4CON &=~(0XFFFF<<0);//設(shè)置每個引腳為輸出狀態(tài)GPM4CON |=(0X1111<<0);//給指定的數(shù)據(jù)寄存器初始化GPM4DAT |=(0XF<<0);return 0; } ssize_t cdev_read (struct file *fp, char __user *buf, size_t size, loff_t *offset) {int i=0,ret=0;if(size<=0 || *offset>4 )return -EINVAL;if(size>4-*offset)size=4-*offset;for(i=*offset;i<*offset+size;i++){if(GPM4DAT&(1<<i)){stat[i]='0';}else if(!(GPM4DAT&(1<<i))){stat[i]='1';}}ret=copy_to_user(&buf[*offset],&stat[*offset],size);printk("%s\n",stat);printk("cdev_read is install\n");return 0; } ssize_t cdev_write (struct file *fp, const char __user * buf, size_t size, loff_t *offset) {int i=0;long ret=0;char str[4]={-1,-1,-1,-1};if(size<=0 || *offset>4 )return -EINVAL;if(size>4-*offset)size=4-*offset;ret=copy_from_user(&str[*offset],&buf[*offset],size);if(ret<0){return -1;}for(i=*offset;i<*offset+size;i++){if(str[i]=='0')GPM4DAT |=(1<<i);else if(str[i]=='1')GPM4DAT &=~(1<<i);}printk("cdev_write is install\n");return 0; } loff_t cdev_llseek (struct file *fp, loff_t off, int whence) {//獲取偏移量的操作需要whence和off結(jié)合loff_t newoff=0;switch(whence){case SEEK_SET:newoff=off;break;case SEEK_CUR:newoff=fp->f_pos+off;break;case SEEK_END:newoff=off+4;break;default: break;}//上述操作newoff 的值可能為負(fù)數(shù),也可能為大于4的數(shù),大于4和小于0沒有意義if(newoff<0){newoff=0;}if(newoff>4){newoff=4;}fp->f_pos=newoff;return fp->f_pos; } long cdev_ioctl (struct file *fp, unsigned int cmd, unsigned long tmp) {int i=0,ret=0;if(_IOC_TYPE(cmd)!='D')return -EINVAL;//提取cmd中定義的讀寫方向if(_IOC_DIR(cmd)==_IOC_WRITE){if(_IOC_NR(cmd)==1){GPM4DAT &=~(1<<tmp);}if(_IOC_NR(cmd)==0){GPM4DAT |= (1<<tmp);}}else if(_IOC_DIR(cmd)==_IOC_READ){for(i=0;i<4;i++){if(GPM4DAT&(1<<i)){stat[i]='0';}else if(!(GPM4DAT&(1<<i))){stat[i]='1';}}if((ret=copy_to_user((unsigned long *)tmp, stat, 4))<0)return -EFAULT;}return 0; }int cdev_release (struct inode *node, struct file *fp) {printk("cdev_release is install\n");return 0; } struct file_operations fop={.open=cdev_open,//.read=cdev_read,//.write=cdev_write,.release=cdev_release,//.llseek=cdev_llseek,.unlocked_ioctl=cdev_ioctl, }; static int __init cdev_module_init(void) {int ret=alloc_chrdev_region(&dev, 0, CDEVCOUNT, CDEVNAME);if(ret){return -1;}cdev=cdev_alloc();if (!cdev)goto out;cdev_init(cdev, &fop);if(cdev_add(cdev, dev, CDEVCOUNT)){goto out1;}printk("cdev_add success\n");cdevclass=class_create(THIS_MODULE,CDEVCLASS);if (IS_ERR(cdevclass)){goto out2;}printk("class_create success\n");for(i=0;i<5;i++)device_create(cdevclass,NULL, dev+i, NULL, "mycdev%d",i );virt_addr=ioremap(phy_dadr,ADDRSIZE);printk("device_create success\n");return 0; out:unregister_chrdev_region(dev,CDEVCOUNT);return -2; out1:unregister_chrdev_region(dev,CDEVCOUNT);kfree(cdev); out2:cdev_del(cdev);unregister_chrdev_region(dev,CDEVCOUNT);kfree(cdev);return PTR_ERR(cdevclass); } static void __exit cdev_module_cleanup(void) {iounmap(virt_addr);for(--i;i>=0;i--)device_destroy(cdevclass,dev+i);printk("device_destroy success\n");class_destroy(cdevclass);cdev_del(cdev);unregister_chrdev_region(dev,CDEVCOUNT);kfree(cdev);printk("kfree success\n"); } module_init(cdev_module_init); module_exit(cdev_module_cleanup); MODULE_LICENSE("GPL");cdev_app.c
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #include <sys/ioctl.h> #include "cmd.h" void display(int fd) {char buf[4]={0};int i=0;//read(fd,buf,4);ioctl(fd,LED_R,buf);printf("%s\n",buf);for(i=0;i<4;i++){printf("led%d is %s\n",i,(buf[i]-'0')?"on":"off");} } int main(int argc, char const* argv[]) {int i=0;int fd=open(argv[1],O_RDWR);if(fd==-1){perror("open");return -1;}for(i=0;i<4;i++){ioctl(fd,LED_ON,i);sleep(1);display(fd);ioctl(fd,LED_OFF,i);sleep(1);}printf("%s\n",stat);close(fd);return 0; }cmd.h
#ifndef __CMD_H_ #define __CMD_H_#define LED_OFF _IOW('D',0,sizeof(int)) #define LED_ON _IOW('D',1,sizeof(int)) #define LED_R _IOR('D',2,sizeof(int)) #endifMakefile
CFLAG=-C TARGET=cdev APP=cdev_app KERNEL=/driver/linux-3.5 obj-m +=$(TARGET).o all:make $(CFLAG) $(KERNEL) M=$(PWD)arm-linux-gcc -o $(APP) $(APP).c clean:make $(CFLAG) $(KERNEL) M=$(PWD) cleanrm $(APP)1.首先使用_IOW或_IOR宏來合成32位的請求碼,這個請求碼可以單獨定義在一個頭文件里
2.在字符驅(qū)動模型的file_operation結(jié)構(gòu)體里.unlocked_ioctl指向的函數(shù)中編寫與ioctl相關(guān)的控制函數(shù)
(1)判斷設(shè)備類型 _IOC_TYPE(nr)
(2)判斷讀寫方向 _IOC_DIR(nr)
如果是讀的話將數(shù)據(jù)拷貝到用戶空間
3.在應(yīng)用程序中調(diào)用ioctr(fd,請求碼,參數(shù))來控制設(shè)備
這里的請求碼可以是我們第一步合成的
本文章僅供學(xué)習(xí)交流用禁止用作商業(yè)用途,文中內(nèi)容來水枂編輯,如需轉(zhuǎn)載請告知,謝謝合作
微信公眾號:zhjj0729
微博:文藝to青年
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的(八)linux驱动之ioctl的使用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 艾滋病多久可以查出来(女生被进入的一瞬间
- 下一篇: fiddler发送post请求