气质,源于心灵的自信!
全部博文(204)
分类: LINUX
2012-04-23 22:02:48
创建timed_output类
kernel/drivers/staging/android/Timed_output.c
在sys/class目录创建timed_output子目录和文件enable
timed_output_class = class_create(THIS_MODULE, "timed_output");创建timed_output子目录
ret = device_create_file(tdev->dev, &dev_attr_enable);在sys/class/timed_output子目录创建文件enable
static int create_timed_output_class(void)
{
if (!timed_output_class) {
timed_output_class = class_create(THIS_MODULE, "timed_output");
if (IS_ERR(timed_output_class))
return PTR_ERR(timed_output_class);
atomic_set(&device_count, 0);
}
return 0;
}
int timed_output_dev_register(struct timed_output_dev *tdev)
{
int ret;
if (!tdev || !tdev->name || !tdev->enable || !tdev->get_time)
return -EINVAL;
ret = create_timed_output_class();
if (ret < 0)
return ret;
tdev->index = atomic_inc_return(&device_count);
tdev->dev = device_create(timed_output_class, NULL,
MKDEV(0, tdev->index), NULL, tdev->name);
if (IS_ERR(tdev->dev))
return PTR_ERR(tdev->dev);
ret = device_create_file(tdev->dev, &dev_attr_enable);
if (ret < 0)
goto err_create_file;
dev_set_drvdata(tdev->dev, tdev);
tdev->state = 0;
return 0;
err_create_file:
device_destroy(timed_output_class, MKDEV(0, tdev->index));
printk(KERN_ERR "timed_output: Failed to register driver %s/n",
tdev->name);
return ret;
}
EXPORT_SYMBOL_GPL(timed_output_dev_register);
驱动注册马达的驱动,注册一个定时器用于控制震动时间(回调函数vibrator_timer_func),注册两个队列,一共给马达打开用,一共为马达震动关闭用。
static void pmic_vibrator_on(struct work_struct *work)
{
set_pmic_vibrator(1);
}
static void pmic_vibrator_off(struct work_struct *work)
{
set_pmic_vibrator(0);
}
static void timed_vibrator_on(struct timed_output_dev *sdev)
{
schedule_work(&work_vibrator_on);
}
static void timed_vibrator_off(struct timed_output_dev *sdev)
{
schedule_work(&work_vibrator_off);
}
static void vibrator_enable(struct timed_output_dev *dev, int value)
{
hrtimer_cancel(&vibe_timer);
if (value == 0)
timed_vibrator_off(dev);
else {
value = (value > 15000 ? 15000 : value);
timed_vibrator_on(dev);
hrtimer_start(&vibe_timer,
ktime_set(value / 1000, (value % 1000) * 1000000),
HRTIMER_MODE_REL);
}
}
static int vibrator_get_time(struct timed_output_dev *dev)
{
if (hrtimer_active(&vibe_timer)) {
ktime_t r = hrtimer_get_remaining(&vibe_timer);
return r.tv.sec * 1000 + r.tv.nsec / 1000000;
} else
return 0;
}
static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer)
{
timed_vibrator_off(NULL);
return HRTIMER_NORESTART;
}
static struct timed_output_dev pmic_vibrator = {
.name = "vibrator",
.get_time = vibrator_get_time,
.enable = vibrator_enable,
};
void __init pxa_init_pmic_vibrator(void)
{
INIT_WORK(&work_vibrator_on, pmic_vibrator_on);
INIT_WORK(&work_vibrator_off, pmic_vibrator_off);
hrtimer_init(&vibe_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
vibe_timer.function = vibrator_timer_func;
timed_output_dev_register(&pmic_vibrator);
}
当上层要设置马达震动时,往文件/sys/class/timed_output/vibrator/enable写入振动的时间长度,通过
static ssize_t enable_store(
struct device *dev, struct device_attribute *attr,
const char *buf, size_t size)
{
struct timed_output_dev *tdev = dev_get_drvdata(dev);
int value;
sscanf(buf, "%d", &value);
tdev->enable(tdev, value);
return size;
}
调用驱动的enable函数也就是vibrator_enable( .enable = vibrator_enable,)
vibrator_enable
|
|
v
timed_vibrator_on(dev);
|
|
v
schedule_work(&work_vibrator_on);
|
|
v
pmic_vibrator_on
|
|
v
set_pmic_vibrator(1); //给马达供电震动
|
|
v
hrtimer_start(&vibe_timer,
ktime_set(value / 1000, (value % 1000) * 1000000),
HRTIMER_MODE_REL);
最终是设置马达的硬件控制驱动管给马达供电,并且启动定时器,定时时间是上层给的参数。
定时时间到了就调用定时器的回调函数vibrator_timer_func
vibrator_timer_func
|
|
v
timed_vibrator_off(NULL);
|
|
v
schedule_work(&work_vibrator_off);
|
|
v
pmic_vibrator_off
|
|
v
set_pmic_vibrator(0); //断开马达的供电,马达停止震动
最终是设置马达的硬件控制驱动管断开马达供电,停止马达震动
led类驱动
4.1,驱动创建leds类,系统启动时执行leds_init在目录/sys/class/创建子目录leds
kernel/drivers/leds/Led-class.c
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;
}
4.2,led_classdev_register,调用这个函数就在目录/sys/class/leds创建子目录led_cdev->name和属性文件brightness
对brightness文件写就执行led_brightness_store,对brightness文件读就执行led_brightness_show,为下面的lcd,led注册做好准备
kernel/drivers/leds/Led-class.c
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);
/**
* 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);
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);
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);
4.3,lcd驱动调用led_classdev_register,在目录/sys/class/leds创建子目录lcd-backlight和属性文件brightness
kernel/drivers/video/msm/Msm_fb.c
static int lcd_backlight_registered;
static void msm_fb_set_bl_brightness(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct msm_fb_data_type *mfd = dev_get_drvdata(led_cdev->dev->parent);
int bl_lvl;
if (value > MAX_BACKLIGHT_BRIGHTNESS)
value = MAX_BACKLIGHT_BRIGHTNESS;
/* This maps android backlight level 0 to 255 into
driver backlight level 0 to bl_max with rounding */
bl_lvl = (2 * value * mfd->panel_info.bl_max + MAX_BACKLIGHT_BRIGHTNESS)
/(2 * MAX_BACKLIGHT_BRIGHTNESS);
if (!bl_lvl && value)
bl_lvl = 1;
msm_fb_set_backlight(mfd, bl_lvl, 1);
}
static struct led_classdev backlight_led = {
.name = "lcd-backlight",
.brightness = MAX_BACKLIGHT_BRIGHTNESS,
.brightness_set = msm_fb_set_bl_brightness,
};
if (!lcd_backlight_registered) {
if (led_classdev_register(&pdev->dev, &backlight_led))
printk(KERN_ERR "led_classdev_register failed/n");
else
lcd_backlight_registered = 1;
}
就在目录/sys/class/leds创建子目录 lcd-backlight和属性文件brightness
当按键或者来的或者改变lcd亮度时,上层对属性文件/sys/class/leds/lcd-backlight/brightness写入背光的亮度数值就
调用led_brightness_store
调用simple_strtoul(buf, &after, 10);将输入的字符串转换为10进制的数字
执行led_set_brightness
执行led_cdev->brightness_set(led_cdev, value
调用msm_fb_set_bl_brightness ,因为 .brightness_set = msm_fb_set_bl_brightness,
/* This maps android backlight level 0 to 255 into driver backlight level 0 to bl_max with rounding */
bl_lvl = (2 * value * mfd->panel_info.bl_max + MAX_BACKLIGHT_BRIGHTNESS) /(2 * MAX_BACKLIGHT_BRIGHTNESS);
将输入的0--255转换为IC的0--bl_max
调用 msm_fb_set_backlight(mfd, bl_lvl, 1);
最终改变LCD的背光驱动电路的设置,调节LCD的背光的亮度
4.4 键盘背光灯
上层对属性文件/sys/class/leds/keyboard-backlight/brightness写入背光的亮度数值
(kernel/drivers/leds/Leds-msm-pmic.c
#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:p
系统行动执行msm_pmic_led_init(void)
调用 platform_driver_register(&msm_pmic_led_driver);
调用msm_pmic_led_probe
调用 led_classdev_register(&pdev->dev, &msm_kp_bl_led);
就在目录/sys/class/leds创建子目录 keyboard-backlight和属性文件brightness
当按键时,上层对属性文件/sys/class/leds/keyboard-backlight/brightness写入背光的亮度数值就
调用led_brightness_store
调用simple_strtoul(buf, &after, 10);将输入的字符串转换为10进制的数字
执行led_set_brightness
执行led_cdev->brightness_set(led_cdev, value
调用msm_keypad_bl_led_set ,因为 .brightness_set = msm_keypad_bl_led_set,
调用 ret = pmic_set_led_intensity(LED_KEYPAD, value / MAX_KEYPAD_BL_LEVEL);
最终改变LED驱动电路的设置,调节LED的亮度