1. 應(yīng)用場(chǎng)景
比如我們的設(shè)備上有很多一樣的usb接口,這些usb接口都需要有驅(qū)動(dòng)才能工作,那么是每個(gè)usb都一套單獨(dú)的驅(qū)動(dòng)程序么?顯然不是的,這些usb接口屬于同一類(lèi)設(shè)備,用戶對(duì)他們的操作方法完全一致,只不過(guò)不是同一個(gè)設(shè)備,所以他們可以復(fù)用同一套驅(qū)動(dòng)代碼,在代碼中去判斷用戶要操作哪個(gè)設(shè)備,然后去open/read/write這個(gè)設(shè)備。
2. 如何區(qū)分不同的設(shè)備
前面說(shuō)過(guò),每個(gè)設(shè)備都有一個(gè)唯一的標(biāo)識(shí)符–設(shè)備號(hào),那么對(duì)于同一類(lèi)設(shè)備,它們的主設(shè)備號(hào)是一樣的,次設(shè)備號(hào)是不一樣的,用來(lái)區(qū)分它們,當(dāng)用戶想要操作哪個(gè)具體的設(shè)備,就會(huì)打開(kāi)這個(gè)設(shè)備對(duì)應(yīng)的設(shè)備文件(inode結(jié)構(gòu)體),并自動(dòng)在內(nèi)核中創(chuàng)建對(duì)應(yīng)的file結(jié)構(gòu)體,這個(gè)file結(jié)構(gòu)體中就保存了用戶操作的所有信息,最終會(huì)傳給我們的內(nèi)核驅(qū)動(dòng),驅(qū)動(dòng)再根據(jù)這個(gè)file結(jié)構(gòu)體和inode結(jié)構(gòu)體來(lái)判斷用戶具體要操作的哪個(gè)設(shè)備,然后去read/write這個(gè)具體的設(shè)備。
案例:
hanp@hanp:/dev/usb$ ls -l
crw------- 1 root root 180, 0 3月 11 17:29 hiddev0
crw-------?1?root?root?180,?1??3月?11?17:29?hiddev1
我的主機(jī)下面的兩個(gè)usb設(shè)備,他們共用了一套u(yù)sb驅(qū)動(dòng),但是他們的設(shè)備號(hào)是不一樣的(180,0)和(180,1),主設(shè)備號(hào)都是180表示都屬于同一類(lèi)設(shè)備(usb設(shè)備),次設(shè)備號(hào)分別是0和1,表示這是兩個(gè)不同的設(shè)備。
3. 代碼實(shí)現(xiàn)
int major = 255;
/* 兩個(gè)設(shè)備,所以有兩套結(jié)構(gòu)體 */
/* 設(shè)備0對(duì)應(yīng)的設(shè)備結(jié)構(gòu)體是hello_dev[0], 設(shè)備1對(duì)應(yīng)的設(shè)備結(jié)構(gòu)體是hello_dev[1] */
struct hello_device {
dev_t devno;
struct cdev cdev;
char data[128];
char name[16];
}hello_dev[NUM_OF_DEVICES];
struct class * hello_class;
const char DEVNAME[] = "hello_device";
int hello_open(struct inode * ip, struct file * fp)
{
printk("%s : %d
", __func__, __LINE__);
/* 獲取用戶打開(kāi)的設(shè)備對(duì)應(yīng)的設(shè)備結(jié)構(gòu)體 hello_dev[0] 或者 hello_dev[1] */
struct hello_device * dev = container_of(ip->i_cdev, struct hello_device, cdev);
/* open的時(shí)候,通過(guò)container_of能夠獲取到用戶要打開(kāi)的那個(gè)設(shè)備的設(shè)備結(jié)構(gòu)體,所有需要把這個(gè)結(jié)構(gòu)體通過(guò)file指針的
* private_data參數(shù)傳遞給read/write */
fp->private_data = dev;
/* 一般用來(lái)做初始化設(shè)備的操作 */
/* ... */
return 0;
}
int hello_close(struct inode * ip, struct file * fp)
{
printk("%s : %d
", __func__, __LINE__);
/* 一般用來(lái)做和open相反的操作,open申請(qǐng)資源,close釋放資源 */
/* ... */
return 0;
}
ssize_t hello_read(struct file * fp, char __user * buf, size_t count, loff_t * loff)
{
int ret;
/* 通過(guò)file指針,獲取到用戶要操作的設(shè)備對(duì)應(yīng)的設(shè)備結(jié)構(gòu)體 */
struct hello_device * dev = fp->private_data;
/* 將用戶需要的數(shù)據(jù)從內(nèi)核空間copy到用戶空間(buf) */
printk("%s : %d
", __func__, __LINE__);
if (count <=0 || count > 128)
count = 128;
if ((ret = copy_to_user(buf, dev->data, count)))
{
printk("copy_to_user err
");
return -1;
}
return count;
}
ssize_t hello_write(struct file * fp, const char __user * buf, size_t count, loff_t * loff)
{
int ret;
struct hello_device * dev = fp->private_data;
/* 將用戶需要的數(shù)據(jù)從內(nèi)核空間copy到用戶空間(buf) */
printk("%s : %d
", __func__, __LINE__);
if (count <=0 || count > 128)
count = 128;
if ((ret = copy_from_user(dev->data, buf, count)))
{
printk("copy_from_user err
");
return -1;
}
return count;
}
/* 2. 分配file_operations結(jié)構(gòu)體 */
struct file_operations hello_fops = {
.owner = THIS_MODULE,
.open = hello_open,
.release = hello_close,
.read = hello_read,
.write = hello_write
};
struct cdev cdev;
static int hello_init(void)
{
int i;
printk("%s : %d
", __func__, __LINE__);
/* 1. 生成并注冊(cè)兩個(gè)設(shè)備的設(shè)備號(hào) */
/* 3. 分配、設(shè)置、注冊(cè)兩套cdev結(jié)構(gòu)體 */
for (i = 0; i < NUM_OF_DEVICES; i++)
{
hello_dev[i].devno = MKDEV(major, i);
sprintf(hello_dev[i].name, "%s%d", DEVNAME, i);
register_chrdev_region(hello_dev[i].devno, 1, hello_dev[i].name);
hello_dev[i].cdev.owner = THIS_MODULE;
cdev_add(&hello_dev[i].cdev, hello_dev[i].devno, 1);
cdev_init(&hello_dev[i].cdev, &hello_fops);
/* 初始化兩個(gè)設(shè)備各自的存儲(chǔ)空間 */
sprintf(hello_dev[i].data, "Hi, I am hello device %d", i);
}
/* 在/sys/class目錄下創(chuàng)建hello類(lèi),并在這個(gè)類(lèi)下面創(chuàng)建hello_device0和hello_device1 */
hello_class = class_create(THIS_MODULE, DEVNAME);
for (i = 0; i < NUM_OF_DEVICES; i++)
{
device_create(hello_class, NULL, hello_dev[i].devno, NULL, "%s%d", DEVNAME, i);
printk("success!
");
}
return 0;
}
static void hello_exit(void)
{
int i;
printk("%s : %d
", __func__, __LINE__);
/* 釋放資源 */
for (i = 0; i < NUM_OF_DEVICES; i++)
{
device_destroy(hello_class, hello_dev[i].devno);
cdev_del(&hello_dev[i].cdev);
unregister_chrdev_region(hello_dev[i].devno, 1);
}
class_destroy(hello_class);
}
MODULE_LICENSE("GPL");
module_init(hello_init);
module_exit(hello_exit);
?
解釋?zhuān)?/span>
container_of:
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member)
?
功能:根據(jù)結(jié)構(gòu)體中某個(gè)成員的地址,從而獲取到整個(gè)結(jié)構(gòu)體的首地址
@ptr: 已知結(jié)構(gòu)體成員的地址
@type: 要獲取的結(jié)構(gòu)體的類(lèi)型
@member: 已知結(jié)構(gòu)體成員的名字
我們用到的實(shí)例解析:
struct hello_device * dev = container_of(ip->i_cdev, struct hello_device, cdev);
文章最后的圖解和上篇文章中我講到file結(jié)構(gòu)體和inode結(jié)構(gòu)體的關(guān)系,其中inode結(jié)構(gòu)體和文件系統(tǒng)下的文件是一一對(duì)應(yīng)的關(guān)系,里面保存了這個(gè)字符設(shè)備對(duì)應(yīng)的cdev結(jié)構(gòu)體:
struct cdev * i_cdev,而這個(gè)cdev結(jié)構(gòu)體又包含在設(shè)備結(jié)構(gòu)體hello_device中,其中hello_dev[0]中包含的是設(shè)備0的cdev,hello_dev[1]中包含的是設(shè)備1的cdev,那么
container_of函數(shù)就可以根據(jù)這個(gè)cdev來(lái)判斷用戶打開(kāi)的是hello_dev[0]還是hello_dev[1]并獲取到地址。
?
編譯安裝驅(qū)動(dòng):
sudo insmod hello.ko
hanp@hanp:/dev$ ls hello_device*
hello_device0??hello_device1
hanp@hanp:/dev$ cat /proc/devices | grep hello
255 hello_device0
255?hello_device1
可以看到在/proc/devices下注冊(cè)了兩個(gè)設(shè)備hello_device0和hello_device1,這兩個(gè)設(shè)備的主設(shè)備一樣都是255,但是次設(shè)備號(hào)不一樣(cat /dev/hello_deviceX可以查看次設(shè)備號(hào))。
4. 寫(xiě)應(yīng)用程序進(jìn)行測(cè)試 app.c
int main(char argc, char * argv[])
{
int fd;
int ret;
char buf[64];
if (argc != 2)
{
printf("Usage: %s
" , argv[0]);
return -1;
}
fd = open(argv[1], O_RDWR);
if (fd < 0)
{
perror("fail to open file");
return -1;
}
/* read data */
ret = read(fd, buf, sizeof(buf));
if (ret < 0)
{
printf("read err!");
return -1;
}
printf("buf = %s
", buf);
/* write data */
strcpy(buf, "write data from app!");
ret = write(fd, buf, sizeof(buf));
if (ret < 0)
{
printf("write err!");
return -1;
}
read(fd, buf, sizeof(buf));
printf("buf = %s
", buf);
close(fd);
return 0;
}
測(cè)試:
$ gcc app.c
$ sudo ./a.out /dev/hello_device0
buf = Hi, I am hello device 0
buf = write data from app!
$ sudo ./a.out /dev/hello_device1
buf = Hi, I am hello device 1
buf?=?write?data?from?app!
編輯:黃飛
評(píng)論
查看更多