天天看點

106 class:裝置管理

class:裝置的大管家

  • 硬體裝置分類管理
  • 與 udev 協作,自動建立裝置檔案
106 class:裝置管理

上圖中:

classes_init()

在系統啟動時在 sysfs根目錄

/sys

下建立

/class

class_create()

/sys/class

目錄下建立子目錄。

/sys/class/xxx

代表某一類裝置。

device_create()

進一步建立

/sys/class/xxx/yyy

, 不隻建立了

yyy

目錄項,也為

yyy

建立了屬性檔案,此屬性檔案記錄了這個硬體裝置的的裝置号。

kobject_uevent()

發送驅動添加的消息給使用者空間 udev守護程序。udev守護程序收到這個消息後根據

/sys/class/xxx/yyy

的裝置号屬性檔案,并調用

mknod()

函數在

/dev

目錄下建立同名檔案

/dev/yyy

建立一個class

class_create宏

include/linux/device.h

此宏本質上調用了另外一個函數。

#define class_create(owner, name)		\
({										\
	static struct lock_class_key __key;	\
	__class_create(owner, name, &__key);\
})
           
  • owner:一般設定為

    THIS_MODULE

  • name:

    kobject

    對象的名字
/* 注意上面的 class_create 宏在調用此函數時已經制定了第三個參數 */
struct class *__class_create(struct module *owner, const char *name,
			     struct lock_class_key *key)
           
  • struct class裡面"繼承"了kobject對象(間接繼承)。

在class下添加kobject對象

include/linux/device.h

device_create()函數
struct device *device_create(struct class *class, 
								struct device *parent,
			    				dev_t devt, 
			    				void *drvdata, 
			    				const char *fmt, ...) 
/* 這個可變參數也是用來設定 kobject 的名字,此函數也要建立新的kobject */
           
  • class:前一步調用 class_create 宏 新建構的class。
  • parent:struct device 也繼承了一各kobject,在此設定新kobject對象的上一層節點,一般為NULL。
  • dev_t:傳入屬性檔案記錄的裝置号。
  • drvdata:私有資料,一般為NULL。
  • fmt:變參參數,一般用來設定kobject對象的名字。

驅動源碼

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>

#include <linux/fs.h>
#include <linux/uaccess.h>
#include <asm/io.h>
#include <linux/device.h>

#define DEV_MAJOR		0		/* 動态申請主裝置号 */
#define DEV_NAME		"red_led" 	/*led裝置名字 */

/* GPIO虛拟位址指針 */
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;

static int led_open(struct inode *inode, struct file *filp)
{
	return 0;
}

static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
	return -EFAULT;
}

static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{

	unsigned char databuf[10];

	if(cnt >10)
		cnt =10;
		
    /*從使用者空間拷貝資料到核心空間*/
    if(copy_from_user(databuf, buf, cnt)){
		return -EIO;
	}
    	
	if(!memcmp(databuf,"on",2)) {	
		iowrite32(0 << 3, GPIO1_DR);	
	} else if(!memcmp(databuf,"off",3)) {
		iowrite32(1 << 3, GPIO1_DR);
	}
	/*寫成功後,傳回寫入的字數*/
	return cnt;
}

static int led_release(struct inode *inode, struct file *filp)
{
	return 0;
}

/* 自定義led的file_operations 接口*/
static struct file_operations led_fops = {
	.owner = THIS_MODULE,
	.open = led_open,
	.read = led_read,
	.write = led_write,
	.release = 	led_release,
};

int major = 0;
struct class *class_led;
static int __init led_init(void)
{
	
	/* GPIO相關寄存器映射 */
  	IMX6U_CCM_CCGR1 = ioremap(0x20c406c, 4);
	SW_MUX_GPIO1_IO03 = ioremap(0x20e006c, 4);
  	SW_PAD_GPIO1_IO03 = ioremap(0x20e02f8, 4);
	GPIO1_GDIR = ioremap(0x0209c004, 4);
	GPIO1_DR = ioremap(0x0209c000, 4);


	/* 使能GPIO1時鐘 */
	iowrite32(0xffffffff, IMX6U_CCM_CCGR1);

	/* 設定GPIO1_IO04複用為普通GPIO*/
	iowrite32(5, SW_MUX_GPIO1_IO03);
	
    /*設定GPIO屬性*/
	iowrite32(0x10B0, SW_PAD_GPIO1_IO03);

	/* 設定GPIO1_IO03為輸出功能 */
	iowrite32(1 << 3, GPIO1_GDIR);

	/* LED輸出高電平 */
	iowrite32(1<< 3, GPIO1_DR);

	/* 注冊字元裝置驅動,此函數傳回的是主裝置号 */
	major = register_chrdev(DEV_MAJOR, DEV_NAME, &led_fops);
    printk(KERN_ALERT "led major:%d\n",major);

	/* 建立/sys/class/xxx目錄項 */
	class_led = class_create(THIS_MODULE, "xxx");

	/* 建立/sys/class/xxx/my_led目錄項,并在此目錄下生成dev屬性檔案,
	 * 此屬性檔案記錄傳進去的裝置号。
	 * 此函數内部還會調用 kobject_uevent 函數
	 */
	device_create(class_led, NULL, MKDEV(major, 0), NULL,"my_led");

	return 0;
}

static void __exit led_exit(void)
{
	/* 取消映射 */
	iounmap(IMX6U_CCM_CCGR1);
	iounmap(SW_MUX_GPIO1_IO03);
	iounmap(SW_PAD_GPIO1_IO03);
	iounmap(GPIO1_DR);
	iounmap(GPIO1_GDIR);

	/* 登出字元裝置驅動 */
	unregister_chrdev(major, DEV_NAME);

	/*銷毀/sys/class/xxx/my_led目錄項*/
	device_destroy(class_led, MKDEV(major, 0));

	/*銷毀/sys/class/xxx目錄項*/
	class_destroy(class_led);
}


module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("embedfire ");
MODULE_DESCRIPTION("led_module");
MODULE_ALIAS("led_module");
           

測試

/lib/modules/4.1.15 # ls /dev/my_led 
/dev/my_led
/lib/modules/4.1.15 # echo on > /dev/my_led 
/lib/modules/4.1.15 # echo off > /dev/my_led 
/lib/modules/4.1.15 # 
/lib/modules/4.1.15 # ls /sys/class/xxx/
my_led
/lib/modules/4.1.15 # ls /sys/class/xxx/my_led/
dev        power      subsystem  uevent
/lib/modules/4.1.15 # cat /sys/class/xxx/my_led/dev 
249:0
/lib/modules/4.1.15 # 
           

如此利用 class 機制 自動建立裝置檔案。