(八)linux驅動之ioctl的使用

這篇文章給大家講解一下ioctl的簡單使用,關于ioctl更詳細的教程后面有機會單獨寫出來

(一)什么是ioctl

ioctl是設備驅動程序中對設備的I/O通道進行管理的函數。所謂對I/O通道進行管理,就是對設備的一些特性進行控制,例如串口的傳輸波特率、馬達的轉速、網卡速率等等。它的調用方式如下:

//在應用調用
int ioctl(int fd, int cmd, …);

其中fd是用戶程序打開設備時使用open函數返回的文件標示符,cmd是用戶程序對設備的控制命令,至于后面的省略號,那是一些補充參數,一般最多一個,這個參數的有無和cmd的意義相關。

(二)ioctl的作用

雖然在文件操作結構體"struct file_operations"中有很多對應的設備操作函數,但是有些命令是實在找不到對應的操作函數。如CD-ROM的驅動,想要一個彈出光驅的操作,這種操作并不是所有的字符設備都需要的,所以文件操作結構體也不會有對應的函數操作。出于這樣的原因,ioctl就有它的用處了————一些沒辦法歸類的函數就統一放在ioctl這個函數操作中,通過指定的命令來實現對應的操作。所以,ioctl函數里面都實現了多個的對硬件的操作,通過應用層傳入的命令來調用相應的操作。

(3)ioctl的優點

控制I/O設備 ,提供了一種獲得設備信息和向設備發送控制參數的手段。用于向設備發控制和配置命令 ,read/write函數處理的是數據,ioctl是控制命令
這樣用的可以分工明確:數據是數據,命令是命令

(四)ioctl相關接口介紹

(1)系統層

   #include <sys/ioctl.h>int ioctl(int fd, unsigned long request, ...);int fd:設備對應的打開的文件描述符
unsigned long request:請求碼
若有第三個參數,必須為無類型指針

請求碼cmd是 unsigned int 是 32 位。下圖給出了每位的含義

圖一
系統中給定了一些請求碼,下面列出一小部分

    // <include/asm-i386/socket.h>地址          宏名稱       采用對應宏名字的時候所傳遞的第三個參數值0x00008901   FIOSETOWN    const int *0x00008902   SIOCSPGRP    const int *0x00008903   FIOGETOWN    int *0x00008904   SIOCGPGRP    int *0x00008905   SIOCATMAR    int *0x00008906   SIOCGSTAMP   timeval *

除了系統給定的,內核中也提供了創建請求碼的宏:

/* 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 中魔數,用來代表一個驅動 8~15位。
nr  : cmd 中的序列號,0-7位
size: cmd 中的數據尺寸,16-29位

內核中提供了相應的解碼請求碼的宏:

/* used to decode ioctl numbers.. */
獲取請求碼是從內核獲取數據或者寫入數據到內核-----判定讀寫功能
#define _IOC_DIR(nr)	(((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)獲取請求碼對應的操作設備類型
#define _IOC_TYPE(nr)	(((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
獲取具體的請求碼的功能值(傳遞的是1還是0,或者其他數據)
#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 已經在內核中消失。其對應系統調用接口不變。

(五)實例代碼

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);//設置每個引腳為輸出狀態GPM4CON |=(0X1111<<0);//給指定的數據寄存器初始化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結合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 的值可能為負數,也可能為大于4的數,大于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))
#endif

Makefile

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.在字符驅動模型的file_operation結構體里.unlocked_ioctl指向的函數中編寫與ioctl相關的控制函數
(1)判斷設備類型 _IOC_TYPE(nr)
(2)判斷讀寫方向 _IOC_DIR(nr)
如果是讀的話將數據拷貝到用戶空間
3.在應用程序中調用ioctr(fd,請求碼,參數)來控制設備
這里的請求碼可以是我們第一步合成的


本文章僅供學習交流用禁止用作商業用途,文中內容來水枂編輯,如需轉載請告知,謝謝合作

微信公眾號:zhjj0729

微博:文藝to青年

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/531413.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/531413.shtml
英文地址,請注明出處:http://en.pswp.cn/news/531413.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

(九)linux中斷編程

目錄&#xff08;一&#xff09;linux中斷的介紹&#xff08;二&#xff09;內核中斷的操作過程&#xff08;三&#xff09;實例代碼&#xff08;一&#xff09;linux中斷的介紹 linux內核中的中斷通過中斷子系統來管理。linux系統中有專門的中斷子系統&#xff0c;原理很復雜…

網絡爬蟲(1)

參考&#xff1a;http://www.cnblogs.com/dongkuo/p/4851735.html算法分析我們現在從需求中提取關鍵詞來逐步分析問題。 首先是“種子節點”。它就是一個或多個在爬蟲程序運行前手動給出的URL&#xff08;網址&#xff09;&#xff0c;爬蟲正是下載并解析這些種子URL指向的頁面…

(十)Linux之等待隊列

&#xff08;一&#xff09;阻塞和非阻塞 阻塞&#xff1a;執行設備操作時&#xff0c;若不能獲得資源&#xff0c;則掛起進程進入休眠直到滿足可操作的條件后再操作。 非阻塞&#xff1a;進程在不能進行設備操作時&#xff0c;并不掛起&#xff0c;它要么放棄&#xff0c;要么…

(十一)linux之poll輪詢

目錄&#xff08;一&#xff09;poll輪詢的作用&#xff08;二&#xff09;poll輪詢相關的接口&#xff08;三&#xff09;poll使用流程&#xff08;四&#xff09;實例代碼&#xff08;一&#xff09;poll輪詢的作用 以阻塞的方式打開文件&#xff0c;那么對多個文件讀寫時&a…

校驗碼(海明校驗,CRC冗余校驗,奇偶校驗)

循環冗余校驗碼 CRC碼利用生成多項式為k個數據位產生r個校驗位進行編碼,其編碼長度為nkr所以又稱 (n,k)碼. CRC碼廣泛應用于數據通信領域和磁介質存儲系統中. CRC理論非常復雜,一般書就給個例題,講講方法.現在簡單介紹下它的原理: 在k位信息碼后接r位校驗碼,對于一個給定的(n,k…

(十二)linux內核定時器

目錄&#xff08;一&#xff09;內核定時器介紹&#xff08;二&#xff09;內核定時器相關接口&#xff08;三&#xff09;使用步驟&#xff08;四&#xff09;實例代碼&#xff08;一&#xff09;內核定時器介紹 內核定時器并不是用來簡單的定時操作&#xff0c;而是在定時時…

java Proxy(代理機制)

我們知道Spring主要有兩大思想&#xff0c;一個是IoC&#xff0c;另一個就是AOP&#xff0c;對于IoC&#xff0c;依賴注入就不用多說了&#xff0c;而對于Spring的核心AOP來說&#xff0c;我們不但要知道怎么通過AOP來滿足的我們的功能&#xff0c;我們更需要學習的是其底層是怎…

(十三)linux中斷底半部分處理機制

這篇文章介紹一下linux中斷的底半部分的tasklet和workquene兩種處理機制&#xff0c;其中tasklet中不能有延時函數&#xff0c;workquene的處理函數可以加入延時操作 目錄&#xff08;一&#xff09;tasklet小任務處理機制&#xff08;1&#xff09;tasklet相關函數接口&#x…

Codeforces Round #326 (Div. 2) B. Pasha and Phone C. Duff and Weight Lifting

B. Pasha and PhonePasha has recently bought a new phone jPager and started adding his friends phone numbers there. Each phone number consists of exactly n digits. Also Pasha has a number k and two sequences of length n?/?k (n is divisible by k) a1,?a2,?…

vmware中裝的ubuntu上不了網

本文章針對橋接方式進行講解&#xff0c;如果需要另外兩種連接方式請參考文末給出的鏈接 &#xff08;一&#xff09;問題 主機和虛擬機可以相互ping通&#xff0c;但是卻不能ping網址 &#xff08;二&#xff09;解決辦法 vmware為我們提供了三種網絡工作模式&#xff0c;…

document.getElementById()與 $()區別

document.getElementById()返回的是DOM對象&#xff0c;而$()返回的是jQuery對象 什么是jQuery對象&#xff1f; ---就是通過jQuery包裝DOM對象后產生的對象。jQuery對象是jQuery獨有的&#xff0c;其可以使用jQuery里的方法。 比如&#xff1a; $("#test").html() 意…

關于gedit的編碼問題

今天由于gedit的編碼格式導致LCD顯示屏的問題&#xff0c;開始沒有想到后來才發現&#xff0c;在這記錄一下 #include <stdio.h> #include <unistd.h> #include <stdio.h> #include <fcntl.h> #include <linux/fb.h> #include <sys/mman.h>…

c語言表白程序代碼

雙十一要到了&#xff0c;好激動啊&#xff01;&#xff01;&#xff01; 是時候準備出手了&#xff01; 花了一天的時間寫的表白代碼。 表示自己弱弱的..... 看了網上好多都是js寫的&#xff0c;感覺碉堡了&#xff01;js用的不熟&#xff0c;前端不好&#xff0c;java&#x…

tiny4412移植tslib庫

1、將tslib-1.4.tar.gz拷貝到虛擬機某個路徑進行解壓 2、進入解壓路徑tslib 3、執行#./autogen.sh 如果提示&#xff1a;./autogen.sh: 4: ./autogen.sh: autoreconf: not found 原因&#xff1a;沒有安裝automake工具, 解決辦法:需要安裝此工具&#xff1a; apt-get instal…

移植QT到tiny4412開發板

目錄&#xff08;一&#xff09; 環境準備&#xff08;二&#xff09; Qt源代碼下載&#xff08;三&#xff09; 移植tslib庫&#xff08;四&#xff09;操作流程1.解壓qt源碼包2.配置編譯環境3.生成Makefile4.編譯安裝5.安裝一些庫用來支持 qt6. 添加以下內容到開發板目錄下的…

c++面試常用知識(sizeof計算類的大小,虛擬繼承,重載,隱藏,覆蓋)

一. sizeof計算結構體 注&#xff1a;本機機器字長為64位 1.最普通的類和普通的繼承 #include<iostream> using namespace std;class Parent{ public:void fun(){cout<<"Parent fun"<<endl;} }; class Child : public Parent{ public:void fun(){…

嵌入式面試題(一)

目錄1 關鍵字volatile有什么含義&#xff1f;并給出三個不同的例子2. c和c中的struct有什么不同&#xff1f;3.進程和線程區別4.ARM流水線5.使用斷言6 .嵌入式系統的定義7 局部變量能否和全局變量重名&#xff1f;8 如何引用一個已經定義過的全局變量&#xff1f;9、全局變量可…

能ping通ip但無法ping通域名和localhost //ping: bad address 'www.baidu.com'

錯誤描述&#xff1a; ~ # ping localhost ping: bad address localhost原因&#xff0c;在/etc目錄下缺少hosts文件&#xff0c;將linux中的/etc hosts文件拷入即可 ~ # ping localhost PING localhost (127.0.0.1): 56 data bytes 64 bytes from 127.0.0.1: seq0 ttl64 tim…

eclipse導入web項目之后項目中出現小紅叉解決辦法

項目中有小紅叉我遇到的最常見的情況&#xff1a; 1、項目代碼本身有問題。&#xff08;這個就不說了&#xff0c;解決錯誤就OK&#xff09; 2、項目中的jar包丟失。&#xff08;有時候eclipse打開時會出現jar包丟失的情況&#xff0c;關閉eclipse重新打開或者重新引入jar包就O…

arm開發板通過網線連接筆記本電腦上外網

需要工具&#xff1a;arm開發板&#xff0c;網線&#xff0c;一臺雙網卡的win7筆記本電腦&#xff08;筆記本電腦一般都是雙網卡&#xff09; 一、筆記本電腦需要先連上外網&#xff0c;可以連上家里的WIFI&#xff0c;或者手機開熱點&#xff08;本人未測試過連接手機的熱點&…