5.3 LED字符设备驱动
1. 简单的LED设备驱动
1.1 静态映射操作LED
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h>
#include <linux/string.h>#define LED_MODULE_NAME "static_led_module"#define reg_GPJ0CON *((volatile unsigned int *) S5PV210_GPJ0CON)
#define reg_GPJ0DAT *((volatile unsigned int *) S5PV210_GPJ0DAT)int s_led_module_major;// 主映射表:arch/arm/plat-s5p/include/plat/Map-s5p.h
// 虚拟地址基地址定义:arch/arm/plat-samsung/include/plat/map-base.h 0xFD000000是虚拟映射地址的基地址
// GPIO相关的主映射表:arch/arm/mach-s5pv210/include/mach/regs-gpio.h
// GPIO具体寄存器定义:arch/arm/mach-s5pv210/include/mach/gpio-bank.hstatic int static_led_module_read(struct file* file, char __user* buf, size_t count, loff_t* ppos)
{return 0;
}static int static_led_module_write(struct file* file, const char __user* ubuf, size_t count, loff_t* ppos)
{return 0;
}static int static_led_module_open(struct inode* inode, struct file* file)
{reg_GPJ0CON = 0x11111111;reg_GPJ0DAT = ((0 << 3) | (0 << 4) | (0 << 5));return 0;
}static int static_led_module_release(struct inode* inode, struct file* file)
{reg_GPJ0DAT = ((1 << 3) | (1 << 4) | (1 << 5));return 0;
}static const struct file_operations static_led_fops =
{.owner = THIS_MODULE,.read = static_led_module_read,.write = static_led_module_write,.open = static_led_module_open,.release = static_led_module_release,
};static int __init static_led_init(void)
{s_led_module_major = register_chrdev(0, LED_MODULE_NAME, &static_led_fops);if (s_led_module_major < 0){printk(KERN_ERR "static_led_init register_chrdev failed\n");return -EINVAL;}return 0;
}static void __exit static_led_exit(void)
{unregister_chrdev(s_led_module_major, LED_MODULE_NAME);
}module_init(static_led_init);
module_exit(static_led_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("xy_L");
MODULE_DESCRIPTION("static_led_module");
MODULE_ALIAS("static_led_test");
1.2 动态映射操作LED
//向内核申请需要映射的内存资源
#define request_mem_region(start,n,name) __request_region(&iomem_resource, (start), (n), (name), 0) //检查物理地址的合法性,建立页表(包括访问权限),完成物理地址到虚拟地址的转换
void * ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags);//释放内存资源
#define release_mem_region(start,n) __release_region(&iomem_resource, (start), (n))//取消ioremap的映射
void iounmap(void * addr);//需要先iounmap再进行release_mem_region
案例:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h>
#include <linux/string.h>
#include <linux/cdev.h>
#include <linux/device.h>#define LED_MODULE_NAME "dynamic_led_module"#define GPJ0CON 0xE0200240 //GPJ0CON物理地址
#define GPJ0DAT 0xE0200244 //GPJ0DAT物理地址unsigned int *pGPJ0CON;
unsigned int *pGPJ0DAT;struct cdev s_cdev; /* 字符设备 */
dev_t s_devNo; /* 设备号 */static struct class* s_pmodule_class;static int dynamic_led_module_read(struct file* file, char __user* buf, size_t count, loff_t* ppos)
{char kBuf[1024] = "module_test_read";int ret = copy_to_user(ubuf, kBuf, count);if (ret == 0)printk(KERN_DEBUG, "copy_to_user success");return 0;
}static int dynamic_led_module_write(struct file* file, const char __user* ubuf, size_t count, loff_t* ppos)
{char kBuf[1024];int ret = copy_from_user(kBuf, ubuf, count);if (ret == 0)printk(KERN_DEBUG, "copy_from_user success");*pGPJ0CON = 0x11111111; if (kBuf[0] = '1')*pGPJ0DAT = (0 << 3);else if (kBuf[0] = '0')*pGPJ0DAT = (1 << 3);return 0;
}static int dynamic_led_module_open(struct inode* inode, struct file* file)
{//***************** 动态映射操作寄存器 *************///动态映射GPJ0CONif (!request_mem_region(GPJ0CON, 4, "GPJ0CON"))return -EINVAL;pGPJ0CON = ioremap(GPJ0CON, 4);//动态映射GPJ0CON if (!request_mem_region(GPJ0DAT, 4, "GPJ0DAT"))return -EINVAL;pGPJ0DAT = ioremap(GPJ0DAT, 4);return 0;
}static int dynamic_led_module_release(struct inode* inode, struct file* file)
{// 解除映射iounmap(pGPJ0CON);release_mem_region(GPJ0CON, 4);iounmap(pGPJ0DAT);release_mem_region(GPJ0DAT, 4);return 0;
}static const struct file_operations dynamic_led_fops =
{.owner = THIS_MODULE,.read = dynamic_led_module_read,.write = dynamic_led_module_write,.open = dynamic_led_module_open,.release = dynamic_led_module_release,
};static int __init dynamic_led_init(void)
{int ret;ret = alloc_chrdev_region(&s_devNo, 0, 1, LED_MODULE_NAME);if (ret){printk(KERN_ERR "alloc_chrdev_region failed\n");return -EINVAL;}// 字符设备号和file_opertations绑定cdev_init(&s_cdev, &dynamic_led_fops );// 注册字符设备驱动ret = cdev_add(&s_cdev, s_devNo, 1);if (ret){printk(KERN_ERR "cdev_add failed\n");return -EINVAL;}// 创建设备类文件s_pmodule_class = class_create(THIS_MODULE, "led_module_test");if (IS_ERR(s_pmodule_class))return -EINVAL;device_create(s_pmodule_class, NULL, s_devNo, NULL, "led_module");return 0;
}static void __exit static_led_exit(void)
{// 销毁设备文件device_destroy(s_pmodule_class, s_devNo);// 销毁设备类class_destroy(s_pmodule_class);// 注销字符设备驱动cdev_del(&s_cdev);// 注销字符设备号unregister_chrdev_region(s_devNo, 1);
}module_init(dynamic_led_init);
module_exit(dynamic_led_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("xy_L");
MODULE_DESCRIPTION("dynamic_led_module");
MODULE_ALIAS("dynamic_led_test");
2. LED驱动框架
2.1 LED驱动框架分析
(1)Linux的LED驱动框架源代码在kernel/drivers/leds目录中。
(2)leds-class.c和leds-core.c就是内核封装的led驱动框架。
leds-class.c:
/** 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 (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 ssize_t led_max_brightness_show(struct device *dev,struct device_attribute *attr, char *buf)
{struct led_classdev *led_cdev = dev_get_drvdata(dev);return sprintf(buf, "%u\n", led_cdev->max_brightness);
}static struct device_attribute led_class_attrs[] = {__ATTR(brightness, 0644, led_brightness_show, led_brightness_store),__ATTR(max_brightness, 0444, led_max_brightness_show, NULL),
#ifdef CONFIG_LEDS_TRIGGERS__ATTR(trigger, 0644, led_trigger_show, led_trigger_store),
#endif__ATTR_NULL,
};/*** 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)
{led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,"%s", led_cdev->name);if (IS_ERR(led_cdev->dev))return PTR_ERR(led_cdev->dev);#ifdef CONFIG_LEDS_TRIGGERSinit_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);if (!led_cdev->max_brightness)led_cdev->max_brightness = LED_FULL;led_update_brightness(led_cdev);#ifdef CONFIG_LEDS_TRIGGERSled_trigger_set_default(led_cdev);
#endifprintk(KERN_DEBUG "Registered led device: %s\n",led_cdev->name);return 0;
}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)
{
#ifdef CONFIG_LEDS_TRIGGERSdown_write(&led_cdev->trigger_lock);if (led_cdev->trigger)led_trigger_set(led_cdev, NULL);up_write(&led_cdev->trigger_lock);
#endifdevice_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;leds_class->dev_attrs = led_class_attrs;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");
案例:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/leds.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <mach/gpio.h>
#include <linux/errno.h>#define GPIO_LED1 S5PV210_GPJ0(3)
#define GPIO_LED2 S5PV210_GPJ0(4)
#define GPIO_LED3 S5PV210_GPJ0(5)static struct led_classdev s_led1_classdev;
static struct led_classdev s_led2_classdev;
static struct led_classdev s_led3_classdev;
/*
* led驱动框架
* kernel/driver/leds目录就是led驱动框架的源代码
* led-class.c
* led-core.c
*/ /*
* gpiolib
* struct s3c_gpio_chip
* s5pv210_gpio_4bit
*/static void led1_set(struct led_classdev* led_cdev, enum led_brightness value)
{if (value == LED_OFF){gpio_set_value(GPIO_LED1, 1);}else{gpio_set_value(GPIO_LED1, 0);}
}static void led2_set(struct led_classdev* led_cdev, enum led_brightness value)
{if (value == LED_OFF){gpio_set_value(GPIO_LED2, 1);}else{gpio_set_value(GPIO_LED2, 0);}
}static void led3_set(struct led_classdev* led_cdev, enum led_brightness value)
{if (value == LED_OFF){gpio_set_value(GPIO_LED3, 1);}else{gpio_set_value(GPIO_LED3, 1);}
}static int __init led_driver_framework_init(void)
{int ret = 0;if (gpio_request(GPIO_LED1, "led1_gpj0.3")){printk(KERN_ERR "gpio_request failed\n");}else{gpio_direction_output(GPIO_LED1, 1);}if (gpio_request(GPIO_LED2, "led2_gpj0.4")){printk(KERN_ERR "gpio_request failed\n");}else{gpio_direction_output(GPIO_LED2, 1);}if (gpio_request(GPIO_LED3, "led3_gpj0.5")){printk(KERN_ERR "gpio_request failed\n");}else{gpio_direction_output(GPIO_LED3, 1);}s_led1_classdev.name = "led1";s_led1_classdev.brightness = 0;s_led1_classdev.brightness_set = led1_set;ret = led_classdev_register(NULL, &s_led1_classdev);if (ret < 0){printk(KERN_ERR "led1 led_classdev_register failed\n");}s_led2_classdev.name = "led2";s_led2_classdev.brightness = 0;s_led2_classdev.brightness_set = led2_set;ret = led_classdev_register(NULL, &s_led2_classdev);if (ret < 0){printk(KERN_ERR "led3 led_classdev_register failed\n");}s_led3_classdev.name = "led3";s_led3_classdev.brightness = 0;s_led3_classdev.brightness_set = led3_set;ret = led_classdev_register(NULL, &s_led3_classdev);if (ret < 0){printk(KERN_ERR "led3 led_classdev_register failed\n");}return ret;
}static void __exit led_driver_framework_exit(void)
{led_classdev_unregister(&s_led1_classdev);led_classdev_unregister(&s_led2_classdev);led_classdev_unregister(&s_led3_classdev);gpio_free(GPIO_LED1);gpio_free(GPIO_LED2);gpio_free(GPIO_LED3);
}module_init(led_driver_framework_init);
module_exit(led_driver_framework_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("xy_L");
MODULE_DESCRIPTION("led_driver_framework");
MODULE_ALIAS("led_driver_framework_test");