2014-06-29 00:54:02 XGsilence 阅读数 1717

开始学习驱动的时候,是将驱动程序编译成模块然后用mknod命令手动建立设备节点以提供给应用程序调用。这对于刚开始调试驱动程序的时候常用的一种方法。但是,当有种需要必须在系统启动的时候就将驱动程序就绪,来供应用层程序调用。这时就不能再手动的建立设备节点了,而必须自动的创建设备节点(不需要人为的操作)。

★注册类

注册类的目的是为了使mdev可以在/dev/目录下建立设备节点。

首先要定义一个类,利用struct class结构体。这个结构体定义在头文件include/linux/device.h中

struct class {
	const char		* name;
	struct module		* owner;

	struct subsystem	subsys;
	struct list_head	children;
	struct list_head	devices;
	struct list_head	interfaces;
	struct semaphore	sem;	/* locks both the children and interfaces lists */

	struct kobject		*virtual_dir;

	struct class_attribute		* class_attrs;
	struct class_device_attribute	* class_dev_attrs;
	struct device_attribute		* dev_attrs;

	int	(*uevent)(struct class_device *dev, char **envp,
			   int num_envp, char *buffer, int buffer_size);
	int	(*dev_uevent)(struct device *dev, char **envp, int num_envp,
				char *buffer, int buffer_size);

	void	(*release)(struct class_device *dev);
	void	(*class_release)(struct class *class);
	void	(*dev_release)(struct device *dev);

	int	(*suspend)(struct device *, pm_message_t state);
	int	(*resume)(struct device *);
}


然后使用

完成对类的注册。其中第一个参数一般为:THIS_MODULE。第二个参数为:设备节点的名称

举个例子:

★创建设备节点

创建设备节点的函数:

<pre name="code" class="cpp">struct device *device_create(struct class *class, struct device *parent,dev_t devt, const char *fmt, ...)
{
	va_list args;
	struct device *dev = NULL;
	int retval = -ENODEV;

	if (class == NULL || IS_ERR(class))
		goto error;

	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	if (!dev) {
		retval = -ENOMEM;
		goto error;
	}

	dev->devt = devt;
	dev->class = class;
	dev->parent = parent;
	dev->release = device_create_release;

	va_start(args, fmt);
	vsnprintf(dev->bus_id, BUS_ID_SIZE, fmt, args);
	va_end(args);
	retval = device_register(dev);
	if (retval)
		goto error;

	return dev;

error:
	kfree(dev);
	return ERR_PTR(retval);
}



该函数的四个参数从左到右以此为:创建设备节点所属的类、该设备的父节点(若果没有就指定为NULL)、设备号、设备名称、次设备号。

★销毁类和设备节点

注意不要忘记了还要销毁类和销毁设备节点。

销毁类:参数为用struct class结构体定义的变量

void class_destroy(struct class *cls)
{
	if ((cls == NULL) || (IS_ERR(cls)))
		return;

	class_unregister(cls);
}
销毁设备节点:

void device_destroy(struct class *class, dev_t devt)
{
	struct device *dev = NULL;
	struct device *dev_tmp;

	down(&class->sem);
	list_for_each_entry(dev_tmp, &class->devices, node) {
		if (dev_tmp->devt == devt) {
			dev = dev_tmp;
			break;
		}
	}
	up(&class->sem);

	if (dev)
		device_unregister(dev);
}

★例子(自己写的延时驱动)

#include <linux/miscdevice.h>    
#include <linux/delay.h>    
#include <asm/irq.h>
#include <linux/kernel.h>    
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/unistd.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <asm/uaccess.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/device.h> 

#define NAME	"ralink_drive_delay"
#define RALINK_GPIO_DEVNAME "my_delay" 

#define delay_us 0     //most least is 10 US
#define delay_ms 1     //Ms

int delay_MAJOR = 109;
MODULE_LICENSE("Dual BSD/GPL");

static long Ralink_delay_ioctl(struct inode * inode, struct file * file, unsigned int cmd,unsigned long arg)
{
    
    switch(cmd)
    {
            case delay_us: 
                udelay(10 * arg);
                return 0;
            case delay_ms:
                udelay(1000);
                return 0;
            default:
                return -1;
    }
}

static struct file_operations My_delay_fops = 
{
    .owner = THIS_MODULE,
    .ioctl = Ralink_delay_ioctl,
};

static struct class *delay_class;

static int __init my_delay_init(void)
{
    int ret = 0;
    ret = register_chrdev(delay_MAJOR, RALINK_GPIO_DEVNAME,&My_delay_fops);
    if(ret < 0)
    {
        printk("unable to register character device\n");
        return ret;
    }
    if (delay_MAJOR == 0) 
    {
	    delay_MAJOR = ret;
	    printk(KERN_DEBUG NAME ": got dynamic major %d\n", ret);
    }
     
    //注册一个类,使mdev可以在"/dev/目录下建立设备节点"
    delay_class = class_create(THIS_MODULE, RALINK_GPIO_DEVNAME);
    if(IS_ERR(delay_class))
    {
        printk("failed in My_led class.\n");
        return -1;                       
    } 
    device_create(delay_class, NULL, MKDEV(delay_MAJOR, 0),RALINK_GPIO_DEVNAME 0);//
    //第一个参数是所要创建的设备所从属的类
    //第二个参数是这个设备的父节点,没有指定就是NULL 
    //第三个参数是设备号
    //第四个参数是设备名称
    //第五个参数是从设备号 
    补充(2016.6.2)
    最近在编写另一个驱动用到自动创建设备节点的时候,就是参考上面device_create函数,结果在insmod驱动模块的时候出现Oops的错误。查找了一下错误原因发现是因为device_create这个函数里面的参数搞错了,现在贴出Linux2.6.30.4中关于这个函数参数的介绍:
  device_create - creates a device and registers it with sysfs
 @class: pointer to the struct class that this device should be registered to
  @parent: pointer to the parent struct device of this new device, if any
 @devt: the dev_t for the char device to be added
 @drvdata: the data to be added to the device for callbacks//(如果没有设置为NULL) 
 @fmt: string for the device's name//第五个参数是设备名称
    printk("my_delay driver initialized\n");
    return 0;
}

void __exit my_delay_exit(void)
{
    unregister_chrdev(delay_MAJOR,RALINK_GPIO_DEVNAME);
    device_destroy(delay_class,MKDEV(delay_MAJOR,0));//注销设备节点 
    class_destroy(delay_class);//销毁类 
    printk("my_delay driver exited\n");
    
}

module_init(my_delay_init);
module_exit(my_delay_exit);







2012-11-26 15:05:07 fontlose 阅读数 5853

在有2.6系列版本中支持udev管理设备文件可以方便的创建设备节点,不必使用mknod来创建,本文使用最小编码来说明创建的几个方法。

//主要用到的四个方法在linux/device.h定义:

//创建类和释放类的函数  创建成后将创建/sys/class/name文件夹
extern struct class *class_create(struct module *owner, const char *name);
extern void class_destroy(struct class *cls);


//在低版本的内核提供class_device_create来创建设备节点 和 删除设备的方法 
extern struct class_device *class_device_create(struct class *cls,
						struct class_device *parent,
						dev_t devt,
						struct device *device,
						const char *fmt, ...)
					__attribute__((format(printf,5,6)));
extern void class_device_destroy(struct class *cls, dev_t devt);


//在高版本的内核提供device_create来创建设备节点 和 删除设备的方法 
extern struct device *device_create(struct class *cls, 
				    struct device *parent,
				    dev_t devt, void *drvdata,
				    const char *fmt, ...)
				    __attribute__((format(printf, 5, 6)));
extern void device_destroy(struct class *cls, dev_t devt);

//如果你不知道你的到底哪个函数,可以直接到内核头文件目录下找device.h,搜索一下定义的是哪个函数


#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/kdev_t.h>
#include <linux/err.h>
MODULE_AUTHOR("my name");
MODULE_LICENSE("Dual BSD/GPL");
static int major_i=66;
static int minor_i=0;
struct class* mclass;
static int node_init(void)
{
  /*class_create 成后在/sys/class创建noddev文件夹*/    
  mclass=class_create(THIS_MODULE,"noddev");
  if(IS_ERR(mclass))
  {
    printk(KERN_ALERT "fail to create class\n");
    return -1;
  }  
  /*class_device_create 在/dev下创建 noddev0设备*/
  device_create(mclass,NULL,MKDEV(major_i,minor_i),NULL,"noddev0");  
  /*这里最后一个参数可以用格式化参数 const char *fmt, ...*/
  device_create(mclass,NULL,MKDEV(major_i,minor_i+20),NULL,"noddev%d",minor_i+20);
  printk(KERN_ALERT "create node success:\n");
  printk(KERN_ALERT "  ls -l /dev/noddev*\n");
  printk(KERN_ALERT "  ls -l /sys/class/noddev\n");
  return 0;
}

static void node_exit(void)
{    
  /*删除创建的设备文件*/
  device_destroy(mclass,MKDEV(major_i,minor_i));
  device_destroy(mclass,MKDEV(major_i,minor_i+20)); 
  class_destroy(mclass); /*删除类*/
  printk(KERN_ALERT "goodbye\n");
}
module_init(node_init);
module_exit(node_exit);

加载结果

[root@localhost node]# insmod node.ko
[root@localhost node]# dmesg | tail -3
[23503.365316] create node success:
[23503.365319]   ls -l /dev/noddev*
[23503.365321]   ls -l /sys/class/noddev
[root@localhost node]# ls -l /dev/noddev*
crw------- 1 root root 66,  0 11月 26 15:02 /dev/noddev0
crw------- 1 root root 66, 20 11月 26 15:02 /dev/noddev20
[root@localhost node]# ls -l /sys/class/noddev
总用量 0
lrwxrwxrwx 1 root root 0 11月 26 15:02 noddev0 -> ../../devices/virtual/noddev/noddev0
lrwxrwxrwx 1 root root 0 11月 26 15:02 noddev20 -> ../../devices/virtual/noddev/noddev20

rmmod node后创建的文件自动被删除




2011-08-23 11:06:31 lionfire 阅读数 2386
 

忘记很早以前从哪里的找到的一个很简单的C代码,完成了字符设备注册,自动获取设备号(主/从),并且自动建立设备节点。

由于最近好几个人问我类似的问题,因此在这里贴出了,供大家参考。

感谢代码的原作者:zengxiaolong

以下代码在2..6.35-22内核下编译通过。

create_chrdev.c:

// create_chrdev.c
// ---------------------------------------------
#include <linux/types.h>     //dev_t
#include <linux/cdev.h>      //struct cdev
#include <linux/fs.h>        //alloc_chrdev_region()
#include <linux/device.h>    //class_create()

dev_t                  devid;
static struct cdev     *led_cdev;
static int             led_Major = 0;
static int             led_Minor = 0;
static struct class    *led_class;

static struct file_operations led_fops = {
    .owner        = THIS_MODULE,
};


static int __init hello_init(void)
{
    int err;

    //初始化cdev
    led_cdev        = cdev_alloc();
    cdev_init(led_cdev, &led_fops);
    led_cdev->owner = THIS_MODULE;

    //动态获取主设备号(dev_t devid中包含"主设备号"和"次设备号"信息)
    alloc_chrdev_region(&devid, 66, 1, "led");//从66开始分配1个设备号
    led_Major = MAJOR(devid);
    led_Minor = MINOR(devid);
    printk(KERN_INFO "I was assigned major number %d.\n", led_Major);
    printk(KERN_INFO "I was assigned minor number %d.\n", led_Minor);

    //注册字符设备 (1)
    err = cdev_add(led_cdev, devid, 1);
    if (err) {
        printk(KERN_NOTICE "Error %d adding device\n", err);
        return -1;
    }

   led_class = class_create(THIS_MODULE, "led_class1");//在/sys目录下建立了一个一个led_class1的目录
    if (IS_ERR(led_class)) {
        printk(KERN_INFO "create class error\n");
        return -1;
    }
    device_create(led_class,NULL,devid,NULL,"led" "%d", MINOR(devid));

   //在2.6早期的版本应该使用class_device_create,早于2.6.27? 2.6.29? 忘记了。这一步在/dev目录下建立了一个led66的设备文件

    return 0;
}


static void __exit hello_exit(void)
{
    unregister_chrdev_region(devid, 1);
    cdev_del(led_cdev);

    device_destroy(led_class, devid);//在2.6早期的版本应该使用class_device_destroy,早于2.6.27? 2.6.29? 忘记了。
    class_destroy(led_class);
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("zengxiaolong ");
MODULE_DESCRIPTION("A sample driver");
MODULE_SUPPORTED_DEVICE("testdevice");

 

Makefile(for PC):

obj-m := create_chrdev.o
KERNELDIR :=/lib/modules/$(shell uname -r)/build
default:
 make -C $(KERNELDIR) M=$(shell pwd) modules
install:
 insmod create_chrdev.ko
uninstall:
 rmmod create_chrdev
clean:
 make -C $(KERNELDIR) M=$(shell pwd) clean

Makefile(for ARM):

obj-m := create_chrdev.o
KERNELDIR := /usr/local/src/ARM2440/kernel-2.6.29 #it is your arm kernel folder
default:
 make -C $(KERNELDIR) M=$(shell pwd) modules
install:
 insmod create_chrdev.ko
uninstall:
 rmmod create_chrdev
clean:
 make -C $(KERNELDIR) M=$(shell pwd) clean

2015-08-18 17:47:13 gzzaigcn 阅读数 1778
1. 自动创建设备节点时mknod做了哪些事情?
mknod会在内部自动创建一个inode,代表设备文件节点的物理磁盘属性,该inode存在于devtmpfs或者tmpfs等ramfs文件系统中,inode会记录下建立时传入的设备文件节点char/block等类型以及相应的设备号等信息,通过父目录的ramfs_dir_inode_operations来决定,调用ramfs_mknod生成当前设备节点文件的inode,并初始化管理设备类型和设备号。一般更上层的话,dentry更多的是内核维护着,其中的d_inode即为实际的inode节点,是多对一的关系。
inode创建更多的依赖于每一级目录dentry,通过不断的lookup然后直到最好一级目录时,完成设备文件节点的inode创建以及初始化,一般使用ramfs的mknod接口完成,mknod来源于其父目录如/dev/,/dev/input/等,每级目录都是属于ramfs文件系统,调用如下
int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
{
     int error = may_create(dir, dentry);

     if (error)
          return error;

     if ((S_ISCHR(mode) || S_ISBLK(mode)) &&
         !ns_capable(inode_userns(dir), CAP_MKNOD))
          return -EPERM;

     if (!dir->i_op->mknod)
          return -EPERM;

     error = devcgroup_inode_mknod(mode, dev);
     if (error)
          return error;

     error = security_inode_mknod(dir, dentry, mode, dev);
     if (error)
          return error;

     error = dir->i_op->mknod(dir, dentry, mode, dev);//创建设备文件节点,调用的是ramfs的mknod
     if (!error)
          fsnotify_create(dir, dentry);
     return error;
}

这也进一步说明了,mknod的使命是将驱动设备号跟inode关联起来的。

一旦打开对应的设备节点路径时,内核就会去查找指定路径对应的dentry,然后找到inode。进而可以确定该inode该执行的i_op,一般如char设备类型,则执行chardev_open函数。进一步则以inode为核心,以devt主设备号查找该字符设备mamp表中的字符设备,然后将节点的i_cdev指向查找到的fops,进而去确定实际file对应的fops(由内核根据次设备号去分发决定).

在存储介质中,每个文件对应唯一的inode结点,但是,每个文件又可以有多个文件名。即可以通过不同的文件名访问同一个文件。这里多个文件名对应一个文件的关系在数据结构中表示就是dentry和inode的关系。
Inode中不存储文件的名字,它只存储节点号;而dentry则保存有名字和与其对应的节点号,所以就可以通过不同的dentry访问同一个inode。

2010-09-14 00:20:00 zhenwenxian 阅读数 1605

我们在刚开始写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");

驱动节点的建立

阅读数 533

没有更多推荐了,返回首页