1.之前註冊字符設備用的如下函數註冊字符設備驅動:
register_chrdev(unsigned int major, const char *name,const struct file_operations *fops);
但其實這個函數是linux版本2.4之前的註冊方式,它的原理是:
(1)確定一個主設備號
(2)構造一個file_operations結構體, 然後放在chrdevs數組中
(3)註冊:register_chrdev
然後當讀寫字符設備的時候,就會根據主設備號從chrdevs數組中取出相應的結構體,並調用相應的處理函數。
它會有個很大的缺點:
每註冊個字符設備,還會連續註冊0~255個次設備號,使它們綁定在同一個file_operations操作方法結構體上,在大多數情況下,都只用極少的次設備號,所以會浪費很多資源.
2.所以在2.4版本後,內核裏就加入了以下幾個函數也可以來實現註冊字符設備:
分爲了靜態註冊(指定設備編號來註冊)、動態分配(不指定設備編號來註冊),以及有連續註冊的次設備編號範圍區間,避免了register_chrdev()浪費資源的缺點
2.1:
/*指定設備編號來靜態註冊一個字符設備*/
int register_chrdev_region(dev_t from, unsigned count, const char *name);
from: 註冊的指定起始設備編號,比如:MKDEV(100, 0),表示起始主設備號100, 起始次設備號爲0
count:需要連續註冊的次設備編號個數,比如: 起始次設備號爲0,count=100,表示0~99的次設備號都要綁定在同一個file_operations操作方法結構體上
*name:字符設備名稱
當返回值小於0,表示註冊失敗
2.2:
/*動態分配一個字符設備,註冊成功並將分配到的主次設備號放入*dev裏*/
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name);
dev: 存放起始設備編號的指針,當註冊成功, *dev就會等於分配到的起始設備編號,可以通過MAJOR()和MINNOR()函數來提取主次設備號
baseminor:次設備號基地址,也就是起始次設備號
count:需要連續註冊的次設備編號個數,比如: 起始次設備號(baseminor)爲0,baseminor=2,表示0~1的此設備號都要綁定在同一個file_operations操作方法結構體上
*name:字符設備名稱
當返回值小於0,表示註冊失敗
2.3:
/*初始化cdev結構體,並將file_operations結構體放入cdev-> ops 裏*/
void cdev_init(struct cdev *cdev, const struct file_operations *fops);
其中cdev結構體的成員,如下所示:
struct cdev {
struct kobject kobj; // 內嵌的kobject對象
struct module *owner; //所屬模塊
const struct file_operations *ops; //操作方法結構體
struct list_head list; //與 cdev 對應的字符設備文件的 inode->i_devices 的鏈表頭
dev_t dev; //起始設備編號,可以通過MAJOR(),MINOR()來提取主次設備號
unsigned int count; //連續註冊的次設備號個數
};
2.4:
/*將cdev結構體添加到系統中,並將dev(註冊好的設備編號)放入cdev-> dev裏, count(次設備編號個數)放入cdev->count裏*/
int cdev_add(struct cdev *p, dev_t dev, unsigned count);
2.5:
/*將系統中的cdev結構體刪除掉*/
void cdev_del(struct cdev *p);
2.6:
/*註銷字符設備*/
void unregister_chrdev_region(dev_t from, unsigned count);
from: 註銷的指定起始設備編號,比如:MKDEV(100, 0),表示起始主設備號100, 起始次設備號爲0
count:需要連續註銷的次設備編號個數,比如: 起始次設備號爲0,baseminor=100,表示註銷掉0~99的次設備號
3.接下來,我們便來寫一個字符設備驅動
裏面調用兩次上面的函數,構造兩個不同的file_operations操作結構體,
次設備號0~1對應第一個file_operations,
次設備號2~3對應第二個file_operations,
然後在/dev/下,通過次設備號(0~4)創建5個設備節點, 利用應用程序打開這5個文件,看有什麼現象
3.1 驅動代碼如下:
#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;
}
/* 操作結構體1 */
static struct file_operations hello1_fops={
.owner=THIS_MODULE,
.open =hello_fops1_open,
};
/* 操作結構體2 */
static struct file_operations hello2_fops={
.owner=THIS_MODULE,
.open =hello_fops2_open,
};
static int major; //主設備
static struct cdev hello1_cdev; //保存 hello1_fops操作結構體的字符設備
static struct cdev hello2_cdev; //保存 hello2_fops操作結構體的字符設備
static struct class *cls;
static int chrdev_ragion_init(void)
{
dev_t devid;
alloc_chrdev_region(&devid, 0, 4,"hello"); //動態分配字符設備: (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");
/*創建字符設備節點*/
class_device_create(cls,0, MKDEV(major,0), 0, "hello0"); //對應hello_fops1操作結構體
class_device_create(cls,0, MKDEV(major,1), 0, "hello1"); //對應hello_fops1操作結構體
class_device_create(cls,0, MKDEV(major,2), 0, "hello2"); //對應hello_fops2操作結構體
class_device_create(cls,0, MKDEV(major,3), 0, "hello3"); //對應hello_fops2操作結構體
class_device_create(cls,0, MKDEV(major,4), 0, "hello4"); //對應空
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");
3.2 測試代碼如下所示:
#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;
}