高級(jí)字符設(shè)備驅(qū)動(dòng)在簡單字符驅(qū)動(dòng)的基礎(chǔ)上添加ioctl方法、阻塞非阻塞讀寫、poll方法、和自動(dòng)創(chuàng)建設(shè)備文件的功能。
一、重要知識(shí)點(diǎn)
1.ioctl
ioctl命令:使用4個(gè)字段定義一個(gè)ioctl命令,包括
type: 幻數(shù),一般使用一個(gè)字符定義,在內(nèi)核中唯一。
number: 序數(shù)。
direction: 數(shù)據(jù)傳輸方向,當(dāng)不涉及數(shù)據(jù)傳輸時(shí),此字段無效。
size: 所涉及用戶數(shù)據(jù)的大小,當(dāng)不涉及數(shù)據(jù)傳輸時(shí),此字段無效。
_IOC_NONE
_IOC_READ
_IOC_WRITE
“方向”字段的可能值。“讀”和“寫”是不同的位,可以用“OR”在一起指定讀寫。
_IOC(dir, type, size)
_IO(type,nr)
_IOR(type, nr, size)
_IOW(type, nr, size)
用于生產(chǎn)ioctl命令的宏
_IOC_DIR(cmd)
_IOC_TYPE(cmd)
_IOC_NR(cmd)
_IOC_SIZE(cmd)
用于解碼ioctl命令的宏
intaccess_ok(int type, const void *addr, unsigned long size)
這個(gè)函數(shù)驗(yàn)證指向用戶空間的指針是否可用,如果允許訪問,access_ok返回非0值。
int put_user(datum, ptr)
int get_user(local, ptr)
int __put_user(datum, ptr)
int __get_user(local, ptr)
用于向(或從)用戶空間保存(或獲?。﹩蝹€(gè)數(shù)據(jù)項(xiàng)的宏。傳送的字節(jié)數(shù)目由sizeof(*ptr)決定。前兩個(gè)要先調(diào)用access_ok,后兩個(gè)(__put_user和__get_user)則假設(shè)access_ok已經(jīng)被調(diào)用過了。
2.阻塞型I/O
typedef struct {/*…..*/} wait_queue_head_t
void init_waitqueue_head(wait_queue_head_t*queue)
DECLARE_WAIT_QUEUE_HEAD(queue)
預(yù)先定義的Linux內(nèi)核等待隊(duì)列類型。wait_queue_head_t類型必須顯示地初始化,初始化方法可以在運(yùn)行時(shí)調(diào)用init_waitqueue_head,或在編譯時(shí)DECLARE_WAIT_QUEUE_HEAD。
void wait_event((wait_queue_head_t q, intcondition)
int wait_event_interruptible(wait_queue_head_tq, int condition)
int wait_event_timeout(wait_queue_head_t q,int condition, int time)
int wait_event_interruptible_timeout(wait_queue_head_tq, int condition, int time)
使進(jìn)程在指定的隊(duì)列上休眠,直到給定的condition值為真。
void wake_up(struct wait_queue **q)
void wake_up_interruptible(structwait_queue **q)
這些函數(shù)喚醒休眠在隊(duì)列q上的進(jìn)程。_interruptible形式的函數(shù)只能喚醒可中斷的進(jìn)程。在實(shí)踐中約定做法是在使用wait_event時(shí)用wake_up,而在使用wait_event_interruptible時(shí)使用wake_up_interruptible。
3.poll方法
poll方法分兩步處理,第一步調(diào)用poll_wait指定等待隊(duì)列,第二步返回是否可操作的掩碼。
POLLIN表示設(shè)備可讀的掩碼,POLLRDORM表示數(shù)據(jù)可讀的掩碼。POLLOUT表示設(shè)備可寫的掩碼,POLLWRNORM表示數(shù)據(jù)可讀的掩碼。一般同時(shí)返回POLLIN和POLLRDORM或者POLLOUT和POLLWRNORM。
4.select系統(tǒng)調(diào)用
原型為intselect(int mafdp1, fd_set *restrict readfds, fd_set *restrict writefds, fd_set*restrict exceptfds, struct timeval *restrict tvptr)
返回值:就緒的描述符數(shù),若超時(shí)則返回0,若出錯(cuò)則返回-1
void FD_ISSET(int fd, fd_set *fdset)
void FD_CLR(int fd, fd_set *fdset)
void FD_SET(int fd, fd_set *fdset)
void FD_ZERO(fd_set *fdset)
調(diào)用FD_ZERO將一個(gè)指定的fd_set變量的所有位設(shè)置為0。調(diào)用FD_SET設(shè)置一個(gè)fd_set變量指定位。調(diào)用FD_CLR則將一指定位清除。最后,調(diào)用FD_ISSET測(cè)試一指定位是否設(shè)置。
5.自動(dòng)創(chuàng)建設(shè)備文件
struct class *class_create(struct module*owner, const char *name)
struct device *device_create(struct class*class, struct device *parent, dev_t devt, const char *fmt, ...)??????????
通過這兩個(gè)函數(shù)可以專門用來創(chuàng)建一個(gè)字符設(shè)備文件節(jié)點(diǎn),class_create 第一個(gè)參數(shù)指定所有者,第二參數(shù)指定類得名字。class_device_create第一個(gè)參數(shù)指定第一個(gè)參數(shù)指定所要?jiǎng)?chuàng)建的設(shè)備所從屬的類,第二個(gè)參數(shù)是這個(gè)設(shè)備的父設(shè)備,如果沒有就指定為NULL,第三個(gè)參數(shù)是設(shè)備號(hào),第四個(gè)參數(shù)是設(shè)備名稱。
二、驅(qū)動(dòng)代碼
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#define?MEMDEV_MAJOR?251??
#define?MEMDEV_NUM?2??
#define?MEMDEV_SIZE?1024??
//定義設(shè)備IOCTL命令??
#define?MEMDEV_IOC_MAGIC?'k'??
#define?MEMDEV_IOC_NR?2??
#define?MEMDEV_IOC_PRINT_IO(MEMDEV_IOC_MAGIC,?0)??
#define?MEMDEV_IOC_RD_IOR(MEMDEV_IOC_MAGIC,?1,?int)??
#define?MEMDEV_IOC_WT_IOW(MEMDEV_IOC_MAGIC,?2,?char)??
struct?mem_dev??
{??
unsignedint?size;??
char*data;??
structsemaphore?sem;??
wait_queue_head_t??inque;??
};??
static?int?mem_major?=?MEMDEV_MAJOR;??
struct?cdev?mem_cdev;??
struct?mem_dev?*mem_devp;??
bool?havedata?=?false;??
static?int?mem_open(struct?inode?*inode,struct?file?*filp)??
{??
structmem_dev?*dev;??
unsignedint?num;??
printk("mem_open.\n");??
num=?MINOR(inode->i_rdev);//獲得次設(shè)備號(hào)??
if(num>?(MEMDEV_NUM?-1))??????????//檢查次設(shè)備號(hào)有效性??
return-ENODEV;??
dev=?&mem_devp[num];??
filp->private_data=?dev;?//將設(shè)備結(jié)構(gòu)保存為私有數(shù)據(jù)??
return0;??
}??
static?int?mem_release(struct?inode?*inode,struct?file?*filp)??
{??
printk("mem_release.\n");??
return0;??
}??
static?ssize_t?mem_read(struct?file?*filp,char?__user?*buf,?size_t?size,?loff_t?*ppos)??
{??
intret?=?0;??
structmem_dev?*dev;??
unsignedlong?p;??
unsignedlong?count;??
printk("mem_read.\n");??
dev=?filp->private_data;//獲得設(shè)備結(jié)構(gòu)??
count=?size;??
p=?*ppos;??
//檢查偏移量和數(shù)據(jù)大小的有效性??
if(p>?MEMDEV_SIZE)??
return0;??
if(count>?(MEMDEV_SIZE-p))??
count=?MEMDEV_SIZE?-?p;??
if(down_interruptible(&dev->sem))//鎖定互斥信號(hào)量??
return-ERESTARTSYS;??
while(!havedata)??
{??
up(&dev->sem);??
if(filp->f_flags&?O_NONBLOCK)??
return-EAGAIN;??
printk("readyto?go?sleep");??
if(wait_event_interruptible(dev->inque,havedata))//等待數(shù)據(jù)??
return-ERESTARTSYS;??
if(down_interruptible(&dev->sem))??
return-ERESTARTSYS;??
}??
//讀取數(shù)據(jù)到用戶空間??
if(copy_to_user(buf,dev->data+p,?count)){??
ret=?-EFAULT;??
printk("copyfrom?user?failed\n");??
}??
else{??
*ppos+=?count;??
ret=?count;??
printk("read%ld?bytes?from?dev\n",?count);??
havedata=?false;//數(shù)據(jù)已經(jīng)讀出??
}??
up(&dev->sem);//解鎖互斥信號(hào)量??
returnret;??
}??
static?ssize_t?mem_write(struct?file?*filp,const?char?__user?*buf,?size_t?size,?loff_t?*ppos)//注意:第二個(gè)參數(shù)和read方法不同??
{??
intret?=?0;??
structmem_dev?*dev;??
unsignedlong?p;??
unsignedlong?count;??
printk("mem_write.\n");??
dev=?filp->private_data;??
count=?size;??
p=?*ppos;??
if(p>?MEMDEV_SIZE)??
return0;??
if(count>?(MEMDEV_SIZE-p))??
count=?MEMDEV_SIZE?-?p;??
if(down_interruptible(&dev->sem))//鎖定互斥信號(hào)量??
return-ERESTARTSYS;??
if(copy_from_user(dev->data+p,buf,?count)){??
ret=?-EFAULT;??
printk("copyfrom?user?failed\n");??
}??
else{??
*ppos+=?count;??
ret=?count;??
printk("write%ld?bytes?to?dev\n",?count);??
havedata=?true;??
wake_up_interruptible(&dev->inque);//喚醒等待數(shù)據(jù)的隊(duì)列??
}??
up(&dev->sem);//解鎖互斥信號(hào)量??
returnret;??
}??
static?loff_t?mem_llseek(struct?file?*filp,loff_t?offset,?int?whence)??
{??
intnewpos;??
printk("mem_llseek.\n");??
switch(whence)??
{??
case0://從文件頭開始??
newpos=?offset;??
break;??
case1://從文件當(dāng)前位置開始??
newpos=?filp->f_pos?+?offset;??
break;??
case2://從文件末尾開始??
newpos=?MEMDEV_SIZE?-?1?+?offset;??
break;??
default:??
return-EINVAL;??
}??
if((newpos<0)||?(newpos>(MEMDEV_SIZE?-?1)))??
return-EINVAL;??
filp->f_pos=?newpos;??
returnnewpos;??
}??
static?int?mem_ioctl(struct?inode?*inode,struct?file?*filp,?unsigned?int?cmd,?unsigned?long?arg)??
{??
interr?=?0,?ret?=?0;??
intioarg?=?0;??
charrdarg?=?'0';??
//參數(shù)檢查??
if(_IOC_TYPE(cmd)!=?MEMDEV_IOC_MAGIC)//參數(shù)類型檢查??
return-ENOTTY;??
if(_IOC_NR(cmd)>?MEMDEV_IOC_NR)//參數(shù)命令號(hào)檢查??
return-ENOTTY;??
//用戶空間指針有效性檢查??
if(_IOC_DIR(cmd)&?_IOC_READ)??
err=?!access_ok(VERIFY_WRITE,?(void?__user?*)arg,?_IOC_SIZE(cmd));??
elseif(_IOC_DIR(cmd)?&?_IOC_WRITE)??
err=?!access_ok(VERIFY_WRITE,?(void?__user?*)arg,?_IOC_SIZE(cmd));??
if(err)??
return-ENOTTY;??
//根據(jù)命令執(zhí)行操作??
switch(cmd)??
{??
case?MEMDEV_IOC_PRINT:??
printk("memdevioctl?print?excuting...\n");??
break;??
caseMEMDEV_IOC_RD:??
ioarg=?1024;??
ret??=?__put_user(ioarg,?(int?*)arg);//用戶空間向內(nèi)核空間獲得數(shù)據(jù)??
printk("memdevioctl?read?excuting...?\n");??
break;??
caseMEMDEV_IOC_WT:??
ret=?__get_user(rdarg,?(char?*)arg);//用戶空間向內(nèi)核空間傳輸數(shù)據(jù)??
printk("memdevioctl?write?excuting...?arg:%c\n",?rdarg);??
break;??
default:??
return-ENOTTY;??
}??
returnret;??
}??
static?unsigned?int?mem_poll(struct?file*filp,?poll_table?*wait)??
{??
structmem_dev?*dev;??
unsignedint?mask?=?0;??
dev=?filp->private_data;??
if(down_interruptible(&dev->sem))//鎖定互斥信號(hào)量??
return-ERESTARTSYS;??
poll_wait(filp,&dev->inque,?wait);??
if(havedata)??
mask|=?POLLIN?|?POLLRDNORM;//返回可讀掩碼??
up(&dev->sem);//釋放信號(hào)量??
returnmask;??
}??
static?const?struct?file_operationsmem_fops?=?{??
.owner=?THIS_MODULE,??
.open=?mem_open,??
.write=?mem_write,??
.read=?mem_read,??
.release=?mem_release,??
.llseek=?mem_llseek,??
.ioctl=?mem_ioctl,??
.poll=?mem_poll,??
};??
static?int?__init?memdev_init(void)??
{??
intresult;??
interr;??
inti;??
structclass?*memdev_class;??
//申請(qǐng)?jiān)O(shè)備號(hào)??
dev_tdevno?=?MKDEV(mem_major,?0);??
if(mem_major)??
result=?register_chrdev_region(devno,?MEMDEV_NUM,?"memdev");//注意靜態(tài)申請(qǐng)的dev_t參數(shù)和動(dòng)態(tài)dev_t參數(shù)的區(qū)別??
else{??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????//靜態(tài)直接傳變量,動(dòng)態(tài)傳變量指針??
result=?alloc_chrdev_region(&devno,?0,?MEMDEV_NUM,?"memdev");??
mem_major=?MAJOR(devno);??
}??
if(result0){??
printk("can'tget?major?devno:%d\n",?mem_major);??
returnresult;??
}??
//注冊(cè)設(shè)備驅(qū)動(dòng)??
cdev_init(&mem_cdev,&mem_fops);??
mem_cdev.owner=?THIS_MODULE;??
err=?cdev_add(&mem_cdev,?MKDEV(mem_major,?0),?MEMDEV_NUM);//如果有N個(gè)設(shè)備就要添加N個(gè)設(shè)備號(hào)??
if(err)??
printk("add?cdev?faild,err?is%d\n",?err);??
//分配設(shè)備內(nèi)存??
mem_devp=?kmalloc(MEMDEV_NUM*(sizeof(struct?mem_dev)),?GFP_KERNEL);??
if(!mem_devp){??
result?=?-?ENOMEM;??
goto?fail_malloc;??
}??
memset(mem_devp,0,?MEMDEV_NUM*(sizeof(struct?mem_dev)));??
for(i=0;i
mem_devp[i].size=?MEMDEV_SIZE;??
mem_devp[i].data=?kmalloc(MEMDEV_SIZE,?GFP_KERNEL);??
memset(mem_devp[i].data,0,?MEMDEV_SIZE);??
init_MUTEX(&mem_devp[i].sem);//初始化互斥鎖??
//初始化等待隊(duì)列??
init_waitqueue_head(&mem_devp[i].inque);??
}??
//自動(dòng)創(chuàng)建設(shè)備文件??
memdev_class=?class_create(THIS_MODULE,?"memdev_driver");??
device_create(memdev_class,NULL,?MKDEV(mem_major,?0),?NULL,?"memdev0");??
returnresult;??
fail_malloc:??
unregister_chrdev_region(MKDEV(mem_major,0),?MEMDEV_NUM);??
returnresult;??
}??
static?void?memdev_exit(void)??
{??
cdev_del(&mem_cdev);??
unregister_chrdev_region(MKDEV(mem_major,0),?MEMDEV_NUM);//注意釋放的設(shè)備號(hào)個(gè)數(shù)一定要和申請(qǐng)的設(shè)備號(hào)個(gè)數(shù)保存一致??
//否則會(huì)導(dǎo)致設(shè)備號(hào)資源流失??
printk("memdev_exit\n");??
}??
module_init(memdev_init);??
module_exit(memdev_exit);??
MODULE_AUTHOR("Y-Kee");??
MODULE_LICENSE("GPL");??
?
評(píng)論