设备节点的动态建立 class

来源:互联网 发布:ubuntu命令行复制粘贴 编辑:程序博客网 时间:2024/04/30 18:43

我们在刚开始写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_LEVEL 16

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");

原创粉丝点击