1. 程式人生 > >linux設備驅動之misc驅動框架源碼分析(二)

linux設備驅動之misc驅動框架源碼分析(二)

linux驅動開發misc設備驅動

1、misc_open函數分析

該函數在driver/char/misc.c中,misc.c是驅動框架實現的,這裏面的misc_Open函數是misc驅動框架為應用層提供的一個打開misc設備的一個接口。


1、首先我們要知道在misc.c中的misc_init函數中,將misc這種類設備註冊成了字符設備驅動。代碼如下

static int __init misc_init(void)
{
	int err;

#ifdef CONFIG_PROC_FS
	proc_create("misc", 0, NULL, &misc_proc_fops);
#endif
	misc_class = class_create(THIS_MODULE, "misc");
	err = PTR_ERR(misc_class);
	if (IS_ERR(misc_class))
		goto fail_remove;

	err = -EIO;
	if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))    //misc類的設備註冊為字符設備驅動,因為使用的是register_chrdev函數。
		goto fail_printk;
	misc_class->devnode = misc_devnode;
	return 0;

fail_printk:
	printk("unable to get major %d for misc devices\n", MISC_MAJOR);
	class_destroy(misc_class);
fail_remove:
	remove_proc_entry("misc", NULL);
	return err;
}

上面代碼中的register_chrdev函數參數中可以看出,misc_fops為包含了操作該驅動的方法結構體。結構體內容如下

static const struct file_operations misc_fops = {
	.owner		= THIS_MODULE,
	.open		= misc_open,
};

因此可以知道,驅動框架中的misc_open函數為驅動框架為應用層提供的接口,接口函數是open函數。正常來說,驅動框架中是不會寫驅動的操作方法的,應該是留給我們驅動開發的人寫的。自己去寫這個file_operations結構體中的方法將其填充然後註冊。


2、misc_open函數的代碼分析,代碼與分析如下

static int misc_open(struct inode * inode, struct file * file)
{
	int minor = iminor(inode);    //從傳進來的參數中得到該設備的次設備號,inode是flash中文件的節點,file是打開的那一份的設備文件的路徑
	struct miscdevice *c;    
	int err = -ENODEV;
	const struct file_operations *old_fops, *new_fops = NULL; //定義兩個file_opreation結構體,這個結構體就不用說了,放的是接口函數,驅動
	                                                            //操作方法

	mutex_lock(&misc_mtx);
	
	list_for_each_entry(c, &misc_list, list) {    //遍歷內核misc雜散類設備的鏈表,如果鏈表中存在這個設備的次設備號,那麽就將內核鏈表中的
		if (c->minor == minor) {                //這個次設備號對應的設備的fops方法拿出來用,作為新的操作這個設備的方法。
			new_fops = fops_get(c->fops);		
			break;
		}
	}
		
	if (!new_fops) {    //如果在內核misc鏈表中沒有找到這個次設備號對應的fops,那麽就request_module一次,在去找一次,如果還找不到,則
		mutex_unlock(&misc_mtx);                                    //跳到fail位置。在註冊這個設備的時候,我們是會註冊這個設備的fops 
		                                                            //的。
		                                                            //應用層用open操作這個設備的時候,對應到驅動層,就是先找到次設備
		                  //號,因為註冊這個驅動的時候,fops已經提供了,同時,這個設備也註冊了,所以去內核鏈表中找這個次設備號,
		                  //找到次設備號後就去找fops方法,找到後就得到這個fops結構體,正常來說是一定能找到的。因為驅動註冊是你提供了
		                  //fops,設備創建註冊時,也註冊了對應的minor次設備號。
		request_module("char-major-%d-%d", MISC_MAJOR, minor);
		mutex_lock(&misc_mtx);

		list_for_each_entry(c, &misc_list, list) {
			if (c->minor == minor) {
				new_fops = fops_get(c->fops);
				break;
			}
		}
		if (!new_fops)
			goto fail;
	}

	err = 0;
	old_fops = file->f_op;    //將打開的文件的fop作為老的fop
	file->f_op = new_fops;    //將從misc內核鏈表中得到的fops作為新的ops給打開的。
	if (file->f_op->open) {    //如果得到了open的函數方法實例
		file->private_data = c;
		err=file->f_op->open(inode,file);    //然後調用open函數,這是真正的執行的open。上層用open操作misc設備時,最終執行的就是這個函
		                                     //數,這個被綁定到miscdevice結構體中的fops下的open,是寫驅動的人提供的,在x210-buzzer.c
		                                     //提供的fops中的open
		if (err) {
			fops_put(file->f_op);
			file->f_op = fops_get(old_fops);
		}
	}
	fops_put(old_fops);
fail:
	mutex_unlock(&misc_mtx);
	return err;
}

因此應用層使用open操作misc設備的時候,會映射到驅動中的/driver/char/misc.c文件中的misc_open函數執行file->f_op->open函數,這個函數映射的是

/drvier/char/buzzer/x210-buzzer.c中的

static struct file_operations dev_fops = {
    .owner   =   THIS_MODULE,
    .open    =   x210_pwm_open,
    .release =   x210_pwm_close, 
    .ioctl   =   x210_pwm_ioctl,
};

裏的open綁定的x210_pwm_open,x210_pwm_open的函數體是

static int x210_pwm_open(struct inode *inode, struct file *file)
{
	if (!down_trylock(&lock))
		return 0;
	else
		return -EBUSY;
	
}

2、misc在proc中的展現 proc不是很重要,因為proc文件系統現在用的越來越少,因為太亂,這裏主要是為了讓你知道這個代碼是做什麽的

cat /proc/misc文件時,會看到所有註冊到misc中的設備的次設備號和名字,這個文件中的內容是通過遍歷內核misc_list鏈表來顯示出來的。

這個/proc/misc文件的創建是在/drvier/char/misc.c文件中的misc_init函數中的proc_create時創建的,代碼如下

static int __init misc_init(void)
{
	int err;

#ifdef CONFIG_PROC_FS
	proc_create("misc", 0, NULL, &misc_proc_fops); //在/proc目錄下創建出misc文件。
	                                                //misc_proc_fops結構體就是操作這個/proc/misc文件時的方法。
#endif

misc_proc_fops結構體內容為

static const struct file_operations misc_proc_fops = {
	.owner	 = THIS_MODULE,
	.open    = misc_seq_open,
	.read    = seq_read,
	.llseek  = seq_lseek,
	.release = seq_release,
};

cat /proc/misc文件時對應的方法應該是

static const struct seq_operations misc_seq_ops = {
	.start = misc_seq_start,
	.next  = misc_seq_next,
	.stop  = misc_seq_stop,
	.show  = misc_seq_show,
};

中的misc_seq_show方法,內容如下

static int misc_seq_show(struct seq_file *seq, void *v)
{
	const struct miscdevice *p = list_entry(v, struct miscdevice, list);

	seq_printf(seq, "%3i %s\n", p->minor, p->name ? p->name : "");
	return 0;
}



3、內核互斥鎖

(1)何為互斥鎖?

和應用中的互斥鎖其實差不多。別人訪問時我不能訪問,訪問時別人訪問,需要上鎖和解鎖,不多說,

(2)內核中定義互斥鎖:DEFINE_MUTEX

(3)上鎖:mutex_lock和解鎖:mutex_unlock

(4)內核防止競爭狀態的手段:原子訪問、自旋鎖、互斥鎖、信號量

(5)原子訪問主要是用來計數(訪問過程不能被打斷)、自旋鎖後面說、互斥鎖和信號量很相似(其實就是計數值為1的信號量),互斥鎖的出現比信號量晚,實現上比信號量優秀,盡量使用互斥鎖。


原子操作:

比如你定義一個變量count,然後count++,實際上這個count++是可以被打斷的,因為count++轉換成可能是三句代碼,當執行了兩句突然被打斷時(可能是時間片時間到了),被打斷時又改了這個count的值,那麽這個時候這個count++的值肯定是不對的了,所以為了防止這種現象就有了原子操作,原子操作的計數不可以被打斷。


自旋鎖:自旋鎖跟原子操作是有本質區別的,在現在的驅動中用的越來越多。是在多核CPU的年代發明的,專門應對這種多處理器的CPU的,所以自旋鎖會用的越來越多,後面會詳細說。


信號量:和互斥鎖基本相似,信號量用來計數的,比如說一個設備只能被打開7次,每個人打開這個設備就會記一次數,當第八打開時,數為大於7了,就不能打開這個設備了。這就是用信號量來做的一個打開計數的手段。


互斥鎖:互斥鎖就是一種特殊的信號,他只能被打開一次。一個人打開了,數值可能就是1了,另一個人就不能打開了,當這個人解鎖的時候,這個值就為0了,另一個人就可以打開或訪問了。所以互斥鎖和信號量的區別就是次數的區別。能用互斥鎖的時候用互斥鎖不要信號量。照貓畫虎就行。如果驅動是你自己寫的,你自己要去選擇用互斥鎖和信號量的時候,這個時候你已經不會糾結了,因為你有了自己不去照別人的驅動寫驅動功力的時候,你就已經知道什麽時候用互斥鎖什麽時候用信號量了。


misc.c這個內核人寫的,寫驅動框架人寫的代碼已經分析的差不多了,這部分代碼只要理解就行,並不是驅動工程師需要去寫的,我們要寫的是驅動,而不是驅動框架代碼。

後面分析的蜂鳴器的驅動代碼,這部分代碼是驅動工程師需要去寫的。

本文出自 “whylinux” 博客,謝絕轉載!

linux設備驅動之misc驅動框架源碼分析(二)