Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1573558
  • 博文数量: 204
  • 博客积分: 2215
  • 博客等级: 大尉
  • 技术积分: 4426
  • 用 户 组: 普通用户
  • 注册时间: 2011-06-06 08:03
个人简介

气质,源于心灵的自信!

文章存档

2018年(1)

2017年(1)

2016年(1)

2015年(18)

2014年(20)

2013年(30)

2012年(119)

2011年(14)

分类: 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的亮度

阅读(2124) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~