學(xué)號(hào):19021211263
一、register_chrdev的缺點(diǎn)
之前注冊(cè)字符設(shè)備用的如下函數(shù)注冊(cè)字符設(shè)備驅(qū)動(dòng):
register_chrdev(unsigned int major, const char *name,const struct file_operations *fops);
但其實(shí)這個(gè)函數(shù)是linux版本2.4之前的注冊(cè)方式,它的原理是:
(1)確定一個(gè)主設(shè)備號(hào)
(2)構(gòu)造一個(gè)file_operations結(jié)構(gòu)體, 然后放在chrdevs數(shù)組中
(3)注冊(cè):register_chrdev
然后當(dāng)讀寫字符設(shè)備的時(shí)候,就會(huì)根據(jù)主設(shè)備號(hào)從chrdevs數(shù)組中取出相應(yīng)的結(jié)構(gòu)體,并調(diào)用相應(yīng)的處理函數(shù)。
它會(huì)有個(gè)很大的缺點(diǎn):
每注冊(cè)個(gè)字符設(shè)備,還會(huì)連續(xù)注冊(cè)0~255個(gè)次設(shè)備號(hào),使它們綁定在同一個(gè)file_operations操作方法結(jié)構(gòu)體上,在大多數(shù)情況下,都只用極少的次設(shè)備號(hào),所以會(huì)浪費(fèi)很多資源。
最多只能有255個(gè)設(shè)備
改進(jìn)后的優(yōu)點(diǎn):
- 通過一定區(qū)間長度的次設(shè)備號(hào)將file_operations操作方法結(jié)構(gòu)體的數(shù)量限制住
- 通過主設(shè)備號(hào)和次設(shè)備號(hào)來查找設(shè)備,其中次設(shè)備號(hào)20位,主設(shè)備號(hào)12位,理論上最大支持4G個(gè)驅(qū)動(dòng)程序
二、全新字符驅(qū)動(dòng)設(shè)備的注冊(cè)方法
在2.4版本后,內(nèi)核里就加入了以下幾個(gè)函數(shù)也可以來實(shí)現(xiàn)注冊(cè)字符設(shè)備:
1、register_chrdev_region
/*指定設(shè)備編號(hào)來靜態(tài)注冊(cè)一個(gè)字符設(shè)備*/
int register_chrdev_region(dev_t from, unsigned count, const char *name);
from: 注冊(cè)的指定起始設(shè)備編號(hào),比如:MKDEV(100, 0),表示起始主設(shè)備號(hào)100, 起始次設(shè)備號(hào)為0
count:需要連續(xù)注冊(cè)的次設(shè)備編號(hào)個(gè)數(shù),比如: 起始次設(shè)備號(hào)為0,count=100,表示0~99的次設(shè)備號(hào)都要綁定在同一個(gè)file_operations操作方法結(jié)構(gòu)體上
name:字符設(shè)備名稱
當(dāng)返回值小于0,表示注冊(cè)失敗
2、alloc_chrdev_region
/*動(dòng)態(tài)分配一個(gè)字符設(shè)備,注冊(cè)成功并將分配到的主次設(shè)備號(hào)放入*dev里*/
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name);
dev: 存放起始設(shè)備編號(hào)的指針,當(dāng)注冊(cè)成功, *dev就會(huì)等于分配到的起始設(shè)備編號(hào),可以通過MAJOR()和MINNOR()函數(shù)來提取主次設(shè)備號(hào)
baseminor:次設(shè)備號(hào)基地址,也就是起始次設(shè)備號(hào)
count:需要連續(xù)注冊(cè)的次設(shè)備編號(hào)個(gè)數(shù),比如: 起始次設(shè)備號(hào)(baseminor)為0,baseminor=2,表示0~1的此設(shè)備號(hào)都要綁定在同一個(gè)file_operations操作方法結(jié)構(gòu)體上
name:字符設(shè)備名稱
當(dāng)返回值小于0,表示注冊(cè)失敗
3、cdev_init
/*初始化cdev結(jié)構(gòu)體,并將file_operations結(jié)構(gòu)體放入cdev-> ops 里*/
void cdev_init(struct cdev *cdev, const struct file_operations *fops);
其中cdev結(jié)構(gòu)體的成員,如下所示:
struct cdev {
struct kobject kobj; // 內(nèi)嵌的kobject對(duì)象
struct module *owner; //所屬模塊
const struct file_operations *ops;//操作方法結(jié)構(gòu)體
struct list_head list; //與 cdev 對(duì)應(yīng)的字符設(shè)備文件的 inode->i_devices 的鏈表頭
dev_t dev; //起始設(shè)備編號(hào),可以通過MAJOR(),MINOR()來提取主次設(shè)備號(hào)
unsigned int count; //連續(xù)注冊(cè)的次設(shè)備號(hào)個(gè)數(shù)
};
其中可以通過MAJOR(cdev->dev),MINOR(cdev->dev)來提取主次設(shè)備號(hào),亦可以通過MKDEV(major,minor)來將主設(shè)備號(hào)major和次設(shè)備號(hào)minor轉(zhuǎn)換成dev_t類型變量dev
4、cdev_add
/*將cdev結(jié)構(gòu)體添加到系統(tǒng)中,并將dev(注冊(cè)好的設(shè)備編號(hào))放入cdev-> dev里, count(次設(shè)備編號(hào)個(gè)數(shù))放入cdev->count里*/
int cdev_add(struct cdev *p, dev_t dev, unsigned count);
5、cdev_del
/*將系統(tǒng)中的cdev結(jié)構(gòu)體刪除掉*/
void cdev_del(struct cdev *p);
6、unregister_chrdev_region
/*注銷字符設(shè)備*/
void unregister_chrdev_region(dev_t from, unsigned count);
from: 注銷的指定起始設(shè)備編號(hào),比如:MKDEV(100, 0),表示起始主設(shè)備號(hào)100, 起始次設(shè)備號(hào)為0
count:需要連續(xù)注銷的次設(shè)備編號(hào)個(gè)數(shù),比如: 起始次設(shè)備號(hào)為0,baseminor=100,表示注銷掉0~99的次設(shè)備號(hào)
三、編寫全新的字符驅(qū)動(dòng)設(shè)備
通過調(diào)用上面的函數(shù),構(gòu)造兩個(gè)不同的file_operations操作結(jié)構(gòu)體,
次設(shè)備號(hào)0~1對(duì)應(yīng)第一個(gè)file_operations,
次設(shè)備號(hào)2~3對(duì)應(yīng)第二個(gè)file_operations,
然后在/dev/下,通過次設(shè)備號(hào)(0~4)創(chuàng)建5個(gè)設(shè)備節(jié)點(diǎn), 利用應(yīng)用程序打開這5個(gè)文件,看有什么現(xiàn)象
驅(qū)動(dòng)代碼
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/list.h>
#include <linux/cdev.h>
static int hello_fops1_open(struct inode *inode, struct file *file)
{
printk("open_hello1!!!\n");
return 0;
}
static int hello_fops2_open (struct inode *inode, struct file *file)
{
printk("open_hello2!!!\n");
return 0;
}
/* 操作結(jié)構(gòu)體1 */
static struct file_operations hello1_fops={
.owner=THIS_MODULE,
.open =hello_fops1_open,
};
/* 操作結(jié)構(gòu)體2 */
static struct file_operations hello2_fops={
.owner=THIS_MODULE,
.open =hello_fops2_open,
};
static int major; //主設(shè)備
static struct cdev hello1_cdev;//保存 hello1_fops操作結(jié)構(gòu)體的字符設(shè)備
static struct cdev hello2_cdev;//保存 hello2_fops操作結(jié)構(gòu)體的字符設(shè)備
static struct class *cls;
static int chrdev_ragion_init(void)
{
dev_t devid;
alloc_chrdev_region(&devid, 0, 4,"hello");//動(dòng)態(tài)分配字符設(shè)備: (major,0)、(major,1)、(major,2)、(major,3)
major=MAJOR(devid);
cdev_init(&hello1_cdev, &hello1_fops);
cdev_add(&hello1_cdev, MKDEV(major,0), 2);//(major,0) (major,1)
cdev_init(&hello2_cdev, &hello2_fops);
cdev_add(&hello2_cdev,MKDEV(major,2), 2);//(major,2) (major,3)
cls=class_create(THIS_MODULE, "hello");
/*創(chuàng)建字符設(shè)備節(jié)點(diǎn)*/
class_device_create(cls,0, MKDEV(major,0), 0, "hello0");//對(duì)應(yīng)hello_fops1操作結(jié)構(gòu)體
class_device_create(cls,0, MKDEV(major,1), 0, "hello1");//對(duì)應(yīng)hello_fops1操作結(jié)構(gòu)體
class_device_create(cls,0, MKDEV(major,2), 0, "hello2");//對(duì)應(yīng)hello_fops2操作結(jié)構(gòu)體
class_device_create(cls,0, MKDEV(major,3), 0, "hello3");//對(duì)應(yīng)hello_fops2操作結(jié)構(gòu)體
class_device_create(cls,0, MKDEV(major,4), 0, "hello4");//對(duì)應(yīng)空
return 0;
}
void chrdev_ragion_exit(void)
{
class_device_destroy(cls, MKDEV(major,4));
class_device_destroy(cls, MKDEV(major,3));
class_device_destroy(cls, MKDEV(major,2));
class_device_destroy(cls, MKDEV(major,1));
class_device_destroy(cls, MKDEV(major,0));
class_destroy(cls);
cdev_del(&hello1_cdev);
cdev_del(&hello2_cdev);
unregister_chrdev_region(MKDEV(major,0), 4);//注銷(major,0)~(major,3)
}
module_init(chrdev_ragion_init);
module_exit(chrdev_ragion_exit);
MODULE_LICENSE("GPL");
測試代碼
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//打印使用幫助信息
void print_useg(char arg[])
{
printf("useg: \n");
printf("%s [dev]\n",arg);
}
int main(int argc,char **argv)
{
int fd;
if(argc!=2)
{
print_useg(argv[0]);
return -1;
}
fd=open(argv[1],O_RDWR);
if(fd<0)
printf("can't open %s \n",argv[1]);
else
printf("can open %s \n",argv[1]);
return 0;
}
運(yùn)行測試
如下所示,掛載驅(qū)動(dòng)后,通過 ls /dev/hello* -l ,看到創(chuàng)建了5個(gè)字符設(shè)備節(jié)點(diǎn)
# ls /dev/hello* -l
crw-rw---- 1 0 0 252, 0 Jan 1 00: 06/dev/hello0
crw-rw---- 1 0 0 252, 1 Jan 1 00: 06/dev/hello1
crw-rw---- 1 0 0 252, 2 Jan 1 00: 06/dev/hello2
crw-rw---- 1 0 0 252, 3 Jan 1 00: 06/dev/hello3
crw-rw---- 1 0 0 252, 4 Jan 1 00: 06/dev/hello4
接下來開始測試驅(qū)動(dòng),如下圖所示,
打開/dev/hello0時(shí),調(diào)用的是驅(qū)動(dòng)代碼的操作結(jié)構(gòu)體hello1_fops里的.open(),
打開/dev/hello2時(shí),調(diào)用的是驅(qū)動(dòng)代碼的操作結(jié)構(gòu)體hello2_fops里的.open(),
打開/dev/hello4時(shí),打開無效,因?yàn)樵隍?qū)動(dòng)代碼里沒有分配次設(shè)備號(hào)4的操作結(jié)構(gòu)體,
# ./20th_chrdev_region_test /dev/hello0
open_hello1!!!
can open /dev/hello0
#
#
# ./20th_chrdev_region_test /dev/hello2
open_hello2!!!
can open /dev/hello2
#
#
# ./20th_chrdev_region_test /dev/hello4
can't open /dev/hello4
總結(jié)
使用register_chrdev_region()等函數(shù)來注冊(cè)字符設(shè)備,里面可以存放多個(gè)不同的file_oprations操作結(jié)構(gòu)體,實(shí)現(xiàn)各種不同的功能
更多有趣內(nèi)容歡迎訪問我的個(gè)人博客。