`
sony-soft
  • 浏览: 1019715 次
文章分类
社区版块
存档分类
最新评论

设备节点的动态建立 class

 
阅读更多

我们在刚开始写Linux设备驱动程序的时候,很多时候都是利用mknod命令手动创建设备节点,实际上Linux内核为我们提供了一组函数,可以用来在模块加载的时候自动在/dev目录下创建相应设备节点,并在卸载模块时删除该节点,当然前提条件是用户空间移植了udev。

内核中定义了struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用device_create(…)函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。

注意,在2.6较早的内核版本中,device_create(…)函数名称不同,是class_device_create(…),所以在新的内核中编译以前的模块程序有时会报错,就是因为函数名称不同,而且里面的参数设置也有一些变化。

struct class和device_create(…) 以及device_create(…)都定义在/include/linux/device.h中,使用的时候一定要包含这个头文件,否则编译器会报错。

例子:创建/sys/class/leds 目录

在/sys/class/leds 目录keyboard-backlight子目录创建led_brightness文件

/*
* LED Class Core
*
* Copyright (C) 2005 John Lenz <lenz@cs.wisc.edu>
* Copyright (C) 2005-2007 Richard Purdie <rpurdie@openedhand.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/device.h>
#include <linux/sysdev.h>
#include <linux/timer.h>
#include <linux/err.h>
#include <linux/ctype.h>
#include <linux/leds.h>
#include "leds.h"

static struct class *leds_class;

static void led_update_brightness(struct led_classdev *led_cdev)
{
if (led_cdev->brightness_get)
led_cdev->brightness = led_cdev->brightness_get(led_cdev);
}

static ssize_t led_brightness_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);

/* no lock needed for this */
led_update_brightness(led_cdev);

return sprintf(buf, "%u/n", led_cdev->brightness);
}

static ssize_t led_brightness_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
ssize_t ret = -EINVAL;
char *after;
unsigned long state = simple_strtoul(buf, &after, 10);
size_t count = after - buf;

if (*after && isspace(*after))
count++;

if (count == size) {
ret = count;

if (state == LED_OFF)
led_trigger_remove(led_cdev);
led_set_brightness(led_cdev, state);
}

return ret;
}

static DEVICE_ATTR(brightness, 0644, led_brightness_show, led_brightness_store);
#ifdef CONFIG_LEDS_TRIGGERS
static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store);
#endif

/**
* led_classdev_suspend - suspend an led_classdev.
* @led_cdev: the led_classdev to suspend.
*/
void led_classdev_suspend(struct led_classdev *led_cdev)
{
led_cdev->flags |= LED_SUSPENDED;
led_cdev->brightness_set(led_cdev, 0);
}
EXPORT_SYMBOL_GPL(led_classdev_suspend);

/**
* led_classdev_resume - resume an led_classdev.
* @led_cdev: the led_classdev to resume.
*/
void led_classdev_resume(struct led_classdev *led_cdev)
{
led_cdev->brightness_set(led_cdev, led_cdev->brightness);
led_cdev->flags &= ~LED_SUSPENDED;
}
EXPORT_SYMBOL_GPL(led_classdev_resume);

static int led_suspend(struct device *dev, pm_message_t state)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);

if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
led_classdev_suspend(led_cdev);

return 0;
}

static int led_resume(struct device *dev)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);

if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
led_classdev_resume(led_cdev);

return 0;
}

/**
* led_classdev_register - register a new object of led_classdev class.
* @parent: The device to register.
* @led_cdev: the led_classdev structure for this device.
*/
int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
{
int rc;

led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,
"%s", led_cdev->name);//在/sys/class/leds目录创建keyboard-backlight子目录

if (IS_ERR(led_cdev->dev))
return PTR_ERR(led_cdev->dev);

/* register the attributes */
rc = device_create_file(led_cdev->dev, &dev_attr_brightness);//在/sys/class/leds/keyboard-backlight子目录创建led_brightness文件
if (rc)
goto err_out;

#ifdef CONFIG_LEDS_TRIGGERS
init_rwsem(&led_cdev->trigger_lock);
#endif
/* add to the list of leds */
down_write(&leds_list_lock);
list_add_tail(&led_cdev->node, &leds_list);
up_write(&leds_list_lock);

led_update_brightness(led_cdev);

#ifdef CONFIG_LEDS_TRIGGERS
rc = device_create_file(led_cdev->dev, &dev_attr_trigger);
if (rc)
goto err_out_led_list;

led_trigger_set_default(led_cdev);
#endif

printk(KERN_INFO "Registered led device: %s/n",
led_cdev->name);

return 0;

#ifdef CONFIG_LEDS_TRIGGERS
err_out_led_list:
device_remove_file(led_cdev->dev, &dev_attr_brightness);
list_del(&led_cdev->node);
#endif
err_out:
device_unregister(led_cdev->dev);
return rc;
}
EXPORT_SYMBOL_GPL(led_classdev_register);

/**
* led_classdev_unregister - unregisters a object of led_properties class.
* @led_cdev: the led device to unregister
*
* Unregisters a previously registered via led_classdev_register object.
*/
void led_classdev_unregister(struct led_classdev *led_cdev)
{
device_remove_file(led_cdev->dev, &dev_attr_brightness);
#ifdef CONFIG_LEDS_TRIGGERS
device_remove_file(led_cdev->dev, &dev_attr_trigger);
down_write(&led_cdev->trigger_lock);
if (led_cdev->trigger)
led_trigger_set(led_cdev, NULL);
up_write(&led_cdev->trigger_lock);
#endif

device_unregister(led_cdev->dev);

down_write(&leds_list_lock);
list_del(&led_cdev->node);
up_write(&leds_list_lock);
}
EXPORT_SYMBOL_GPL(led_classdev_unregister);

static int __init leds_init(void)
{
leds_class = class_create(THIS_MODULE, "leds");
if (IS_ERR(leds_class))
return PTR_ERR(leds_class);
leds_class->suspend = led_suspend;
leds_class->resume = led_resume;
return 0;
}

static void __exit leds_exit(void)
{
class_destroy(leds_class);
}

subsys_initcall(leds_init);
module_exit(leds_exit);

MODULE_AUTHOR("John Lenz, Richard Purdie");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("LED Class Interface");

在/sys/class/leds 目录创建lcd-backlight子目录

static struct led_classdev sapphire_backlight_led = {
.name= "lcd-backlight",
.brightness = SAPPHIRE_DEFAULT_BACKLIGHT_BRIGHTNESS,
.brightness_set = sapphire_brightness_set,
};

static int sapphire_backlight_probe(struct platform_device *pdev)
{
led_classdev_register(&pdev->dev, &sapphire_backlight_led);
return 0;
}

在/sys/class/leds 目录创建keyboard-backlight子目录

/*
* leds-msm-pmic.c - MSM PMIC LEDs driver.
*
* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, you can find it at http://www.fsf.org.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>

#include <mach/pmic.h>

#define MAX_KEYPAD_BL_LEVEL16

static void msm_keypad_bl_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
int ret;

ret = pmic_set_led_intensity(LED_KEYPAD, value / MAX_KEYPAD_BL_LEVEL);
if (ret)
dev_err(led_cdev->dev, "can't set keypad backlight/n");
}

static struct led_classdev msm_kp_bl_led = {
.name= "keyboard-backlight",
.brightness_set= msm_keypad_bl_led_set,
.brightness= LED_OFF,
};

static int msm_pmic_led_probe(struct platform_device *pdev)
{
int rc;

rc = led_classdev_register(&pdev->dev, &msm_kp_bl_led);
if (rc) {
dev_err(&pdev->dev, "unable to register led class driver/n");
return rc;
}
msm_keypad_bl_led_set(&msm_kp_bl_led, LED_OFF);
return rc;
}

static int __devexit msm_pmic_led_remove(struct platform_device *pdev)
{
led_classdev_unregister(&msm_kp_bl_led);

return 0;
}

#ifdef CONFIG_PM
static int msm_pmic_led_suspend(struct platform_device *dev,
pm_message_t state)
{
led_classdev_suspend(&msm_kp_bl_led);

return 0;
}

static int msm_pmic_led_resume(struct platform_device *dev)
{
led_classdev_resume(&msm_kp_bl_led);

return 0;
}
#else
#define msm_pmic_led_suspend NULL
#define msm_pmic_led_resume NULL
#endif

static struct platform_driver msm_pmic_led_driver = {
.probe= msm_pmic_led_probe,
.remove= __devexit_p(msm_pmic_led_remove),
.suspend= msm_pmic_led_suspend,
.resume= msm_pmic_led_resume,
.driver= {
.name= "pmic-leds",
.owner= THIS_MODULE,
},
};

static int __init msm_pmic_led_init(void)
{
return platform_driver_register(&msm_pmic_led_driver);
}
module_init(msm_pmic_led_init);

static void __exit msm_pmic_led_exit(void)
{
platform_driver_unregister(&msm_pmic_led_driver);
}
module_exit(msm_pmic_led_exit);

MODULE_DESCRIPTION("MSM PMIC LEDs driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:pmic-leds");

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics