1. 程式人生 > >Linux裝置驅動--新增裝置節點

Linux裝置驅動--新增裝置節點

1 環境描述
    Host:Ubuntu14.04(64bit)
    Target:smdk2410
     Kernel:linux-2.6.39.4

2 前言

    我們在剛開始寫Linux裝置驅動程式的時候, 很多時候都是利用mknod命令手動建立裝置節點,實際上Linux核心為我們提供了一組函式,可以用來在模組載入的時候自動在/dev目錄下建立相應裝置節點,並在解除安裝模組時刪除該節點,當然前提條件是使用者空間移植了udev[2]。從linux 核心2.6的某個版本之後,devfs不復存在,udev成為devfs的替代。與devfs不同的是,udev是使用者空間的[2]。

    核心中定義struct class

結構體,該結構體的一個變數對應一個。核心同時提供了class_create(),可以用它來建立一個類,這個類存放於sysfs下面。一旦建立好了這個類,再呼叫device_create()/dev目錄下建立相應的裝置節點。這樣,載入模組的時候,使用者空間中的udev會自動響應device_create()函式,去/sysfs下尋找對應的類從而建立裝置節點[1]。

    在驅動用加入對udev的支援主要做的就是:在驅動初始化的程式碼裡呼叫class_create()為該裝置建立一個struct class,再為每個裝置呼叫device_create()建立對應的裝置[1],大致用法如下[2]:

struct class *myclass = class_create(THIS_MODULE, “my_device_driver”);
device_create(myclass, NULL, MKDEV(major_num, 0), NULL, “my_device”);

    這樣的module被載入時,udev daemon就會自動在/dev下建立my_device裝置檔案[2]。 

: 在2.6較早的核心中device_create()的曾用名為class_device_create()[1]。

3 FrameBuffer裝置節點

    建立FrameBuffer裝置節點的主要流程是,先定義一個struct class指標變數fb_class,然後在fbmem_init()中呼叫class_create()建立類並且賦值給fb_class,然後在do_register_framebuffer()中呼叫device_create()根據上述fb_class

建立裝置節點。

(1)fb_class

struct class *fb_class;
EXPORT_SYMBOL(fb_class);
/* 原始檔:drivers/video/fbmem.c */

(2)class_create()

static int __init fbmem_init(void)
{
	proc_create("fb", 0, NULL, &fb_proc_fops);

	if (register_chrdev(FB_MAJOR,"fb",&fb_fops))
		printk("unable to get major %d for fb devs\n", FB_MAJOR);

	fb_class = class_create(THIS_MODULE, "graphics");  /* 建立一個graphics類! */
	if (IS_ERR(fb_class)) {
		printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
		fb_class = NULL;
	}
	return 0;
}
/* 原始檔:drivers/video/fbmem.c */

(3)device_create()

    在do_register_framebuffer()函式中,就呼叫device_create()建立fbn裝置節點(n=0~31),如下面函式第25行所示:

static int do_register_framebuffer(struct fb_info *fb_info)
{
	int i;
	struct fb_event event;
	struct fb_videomode mode;

	if (fb_check_foreignness(fb_info))
		return -ENOSYS;

	do_remove_conflicting_framebuffers(fb_info->apertures, fb_info->fix.id,
					 fb_is_primary_device(fb_info));

	if (num_registered_fb == FB_MAX)
		return -ENXIO;

	num_registered_fb++;
	for (i = 0 ; i < FB_MAX; i++)
		if (!registered_fb[i])
			break;
	fb_info->node = i;
	atomic_set(&fb_info->count, 1);
	mutex_init(&fb_info->lock);
	mutex_init(&fb_info->mm_lock);

	fb_info->dev = device_create(fb_class, fb_info->device,           /* 建立裝置節點! */
				     MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
	if (IS_ERR(fb_info->dev)) {
		/* Not fatal */
		printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
		fb_info->dev = NULL;
	} else
		fb_init_device(fb_info);

	if (fb_info->pixmap.addr == NULL) {
		fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);
		if (fb_info->pixmap.addr) {
			fb_info->pixmap.size = FBPIXMAPSIZE;
			fb_info->pixmap.buf_align = 1;
			fb_info->pixmap.scan_align = 1;
			fb_info->pixmap.access_align = 32;
			fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
		}
	}	
	fb_info->pixmap.offset = 0;

	if (!fb_info->pixmap.blit_x)
		fb_info->pixmap.blit_x = ~(u32)0;

	if (!fb_info->pixmap.blit_y)
		fb_info->pixmap.blit_y = ~(u32)0;

	if (!fb_info->modelist.prev || !fb_info->modelist.next)
		INIT_LIST_HEAD(&fb_info->modelist);

	fb_var_to_videomode(&mode, &fb_info->var);
	fb_add_videomode(&mode, &fb_info->modelist);
	registered_fb[i] = fb_info;

	event.info = fb_info;
	if (!lock_fb_info(fb_info))
		return -ENODEV;
	fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
	unlock_fb_info(fb_info);
	return 0;
}
/* 原始檔:drivers/video/fbmem.c */

參考資料