class:裝置的大管家
- 硬體裝置分類管理
- 與 udev 協作,自動建立裝置檔案
上圖中:
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 機制 自動建立裝置檔案。