編輯:關於Android編程
從原理圖中我們發現,button-backlight是由兩路ISINK控制的,ISNIK是一種類似於PWM的控制器,它可以發出類似PWM的信號,可以通過寄存器的配置調整其占空比等參數,進入調節輸出電流,從而控制led的亮度。
二、按鍵燈的驅動實現#define USE_PINCTRL #ifdef USE_PINCTRL static const struct of_device_id leds_of_ids[] = { {.compatible = "mediatek,leds-mt65xx",}, {} }; #endif static struct platform_driver mt65xx_leds_driver = { .driver = { .name = "leds-mt65xx", .owner = THIS_MODULE, #ifdef USE_PINCTRL .of_match_table = leds_of_ids, //和dts中定義一致 #endif }, .probe = mt65xx_leds_probe, .remove = mt65xx_leds_remove, /* .suspend = mt65xx_leds_suspend, */ .shutdown = mt65xx_leds_shutdown, };//驅動模塊的加載
static int __init mt65xx_leds_init(void) { ret = platform_driver_register(&mt65xx_leds_driver); .... return ret; }//dts中定義leds 的相關節點如:red,green,blue,keyboard-backlight,button-backlight等(後面會用到)
led0:led@0 { compatible = "mediatek,red"; led_mode = <0>; data = <1>; pwm_config = <0 0 0 0 0>; }; led1:led@1 { compatible = "mediatek,green"; led_mode = <0>; data = <1>; pwm_config = <0 0 0 0 0>; }; led2:led@2 { compatible = "mediatek,blue"; led_mode = <0>; data = <1>; pwm_config = <0 0 0 0 0>; }; led4:led@4 { compatible = "mediatek,keyboard-backlight"; led_mode = <0>; data = <1>; pwm_config = <0 0 0 0 0>; }; led5:led@5 { compatible = "mediatek,button-backlight"; //這裡著重分析按鍵燈button-backlight led_mode = <3>; data = <1>; pwm_config = <0 0 0 0 0>; }; led6:led@6 { compatible = "mediatek,lcd-backlight"; led_mode = <5>; data = <1>; pwm_config = <0 0 0 0 0>; };//dts中定義和platform_device相關的節點信息
file:kernel-3.18/arch/arm/boot/dts/mt6735m.dtsi lightsystem: leds { compatible = "mediatek,leds-mt65xx"; //這裡的定義和上面platform_driver中定義的一致 };上述dts中定義按鍵燈leds節點配置,內核起來後會解析dts生成相關的設備,並與驅動中的driver匹配,如果匹配成功就執行下面的probe
static int mt65xx_leds_probe(struct platform_device *pdev) { int i; int ret;/* rc; */ //進入probe後,會從dts中獲取led節點的mode和data struct cust_mt65xx_led *cust_led_list = mt_get_cust_led_list(); LEDS_DRV_DEBUG("%s\n", __func__); get_div_array(); //MT65XX_LED_TYPE_TOTAL為改通用(common)驅動所支持燈的個數 for (i = 0; i < MT65XX_LED_TYPE_TOTAL; i++) { //觀察上面dts中mode如果mode為0(MT65XX_LED_MODE_NONE),則遍歷下個元素 if (cust_led_list[i].mode == MT65XX_LED_MODE_NONE) { g_leds_data[i] = NULL; continue; } ... //將dts中配置的mode,name ,data 保存起來後面會用到 //通過觀察上面的button-backlight 的配置,得出其mode為3,data為1,name為button-backlight g_leds_data[i]->cust.mode = cust_led_list[i].mode; g_leds_data[i]->cust.data = cust_led_list[i].data; g_leds_data[i]->cust.name = cust_led_list[i].name; g_leds_data[i]->cdev.name = cust_led_list[i].name; g_leds_data[i]->cust.config_data = cust_led_list[i].config_data; /* bei add */ g_leds_data[i]->cdev.brightness_set = mt65xx_led_set; //設置led亮度的函數 //創建sys目錄下的brightness等屬性節點,提供給用戶空間調用 ret = led_classdev_register(&pdev->dev, &g_leds_data[i]->cdev); ... return 0; ... }//後面會用到的一些結構的定義
enum mt65xx_led_type { MT65XX_LED_TYPE_RED = 0, MT65XX_LED_TYPE_GREEN, MT65XX_LED_TYPE_BLUE, MT65XX_LED_TYPE_JOGBALL, MT65XX_LED_TYPE_KEYBOARD, MT65XX_LED_TYPE_BUTTON, MT65XX_LED_TYPE_LCD, MT65XX_LED_TYPE_TOTAL, }; /** * led customization data structure * name : must the same as lights HAL * mode : control mode * data : * PWM: pwm number * GPIO: gpio id * PMIC: enum mt65xx_led_pmic * CUST: custom set brightness function pointer * config_data: pwm config data */ struct cust_mt65xx_led { char *name; enum mt65xx_led_mode mode; long data; struct PWM_config config_data; }; /** * led device node structure with mtk extentions * cdev: common led device structure * cust: customization data from device tree * work: workqueue for specialfied led device * level: brightness level * delay_on: on time if led is blinking * delay_off: off time if led is blinking */ struct mt65xx_led_data { struct led_classdev cdev; struct cust_mt65xx_led cust; struct work_struct work; int level; int delay_on; int delay_off; }; file:kernel-3.18/include/linux/leds.h enum led_brightness { LED_OFF = 0, LED_HALF = 127, LED_FULL = 255, }; struct led_classdev { const char *name; enum led_brightness brightness; enum led_brightness max_brightness; int flags; ... /* Set LED brightness level */ /* Must not sleep, use a workqueue if needed */ void (*brightness_set)(struct led_classdev *led_cdev, enum led_brightness brightness); /* Get LED brightness level */ enum led_brightness (*brightness_get)(struct led_classdev *led_cdev); ..... struct device *dev; const struct attribute_group **groups; struct list_head node; /* LED Device list */ ... };file:kernel-3.18/drivers/misc/mediatek/leds/mt6735/leds.c
char *leds_name[MT65XX_LED_TYPE_TOTAL] = { "red", "green", "blue", "jogball-backlight", "keyboard-backlight", "button-backlight", "lcd-backlight", }3.從dts中獲取各種led的配置信息
struct cust_mt65xx_led *mt_get_cust_led_list(void) { struct cust_mt65xx_led *cust_led_list = get_cust_led_dtsi(); return cust_led_list; } struct cust_mt65xx_led *get_cust_led_dtsi(void) { struct device_node *led_node = NULL; ... //MT65XX_LED_TYPE_TOTAL 為led數組長度,即可以支持led的個數 for (i = 0; i < MT65XX_LED_TYPE_TOTAL; i++) { char node_name[32] = "mediatek,"; pled_dtsi[i].name = leds_name[i]; //使用"mediatek,button-backlight"尋找dtsi中定義的節點 led_node = of_find_compatible_node(NULL, NULL, strcat(node_name, leds_name[i])); if (!led_node) { LEDS_DEBUG("Cannot find LED node from dts\n"); pled_dtsi[i].mode = 0; pled_dtsi[i].data = -1; } else { isSupportDTS = true; //讀取led_mode值 ret = of_property_read_u32(led_node, "led_mode", &mode); if (!ret) { pled_dtsi[i].mode = mode; LEDS_DEBUG ("The %s's led mode is : %d\n", pled_dtsi[i].name, pled_dtsi[i].mode); } //讀取led的data值 ret = of_property_read_u32(led_node, "data", &data); if (!ret) { pled_dtsi[i].data = data; LEDS_DEBUG ("The %s's led data is : %ld\n", pled_dtsi[i].name, pled_dtsi[i].data); } ... return pled_dtsi; }4. 創建相關的設備節點
** * 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_with_groups(leds_class, parent, 0, led_cdev, led_cdev->groups, "%s", led_cdev->name); ... return 0; }//device_create_with_groups的實現
/** * device_create_with_groups - 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 * @groups: NULL-terminated list of attribute groups to be created * @fmt: string for the device's name * * This function can be used by char device classes. A struct device * will be created in sysfs, registered to the specified class. * Additional attributes specified in the groups parameter will also * be created automatically. * * A "dev" file will be created, showing the dev_t for the device, if * the dev_t is not 0,0. * If a pointer to a parent struct device is passed in, the newly created * struct device will be a child of that device in sysfs. * The pointer to the struct device will be returned from the call. * Any further sysfs files that might be required can be created using this * pointer. * * Returns &struct device pointer on success, or ERR_PTR() on error. * * Note: the struct class passed to this function must have previously * been created with a call to class_create(). */ struct device *device_create_with_groups(struct class *class, struct device *parent, dev_t devt, void *drvdata, const struct attribute_group **groups, const char *fmt, ...) { va_list vargs; struct device *dev; va_start(vargs, fmt); dev = device_create_groups_vargs(class, parent, devt, drvdata, groups, fmt, vargs); va_end(vargs); return dev; }//device_create_groups_vargs 的實現
static struct device * device_create_groups_vargs(struct class *class, struct device *parent, dev_t devt, void *drvdata, const struct attribute_group **groups, 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; } device_initialize(dev); dev->devt = devt; dev->class = class; dev->parent = parent; dev->groups = groups; dev->release = device_create_release; dev_set_drvdata(dev, drvdata); retval = kobject_set_name_vargs(&dev->kobj, fmt, args); if (retval) goto error; retval = device_add(dev); if (retval) goto error; return dev; error: put_device(dev); return ERR_PTR(retval); }//device_add的實現
int device_add(struct device *dev) { ... /* first, register with generic layer. */ /* we require the name to be set before, and pass NULL */ error = kobject_add(&dev->kobj, dev->kobj.parent, NULL); if (error) goto Error; ... error = device_add_attrs(dev); ... }device_add_attrs的實現這裡將會調用device_add_groups,class->dev_groups 作為參數呗傳入,此時節點/sys/class/leds/xxx/brightness已經被
static int device_add_attrs(struct device *dev) { struct class *class = dev->class; const struct device_type *type = dev->type; int error; //這裡class->dev_groups先前已經在led-class.c的leds_init中被賦值 leds_class->dev_groups = led_groups; if (class) { error = device_add_groups(dev, class->dev_groups); if (error) return error; } ... return 0; }5、屬性節點的讀寫方法定義
static int __init leds_init(void) { leds_class = class_create(THIS_MODULE, "leds"); //創建class對象 ... leds_class->dev_groups = led_groups; //傳入brightness節點參數,led屬性節點組賦值給leds_class ... return 0; }再看led_groups的定義如下:
static const struct attribute_group *led_groups[] = { &led_group, #ifdef CONFIG_LEDS_TRIGGERS &led_trigger_group, #endif NULL, }; static const struct attribute_group led_group = { .attrs = led_class_attrs, }; static struct attribute *led_class_attrs[] = { &dev_attr_brightness.attr, &dev_attr_max_brightness.attr, NULL, };當用戶空間讀取屬性節點時候,會直接輸入當前亮度值
static ssize_t 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 brightness_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct led_classdev *led_cdev = dev_get_drvdata(dev); unsigned long state; ssize_t ret = -EINVAL; ret = kstrtoul(buf, 10, &state); //將傳入的字符串轉換為十進制 if (ret) return ret; if (state == LED_OFF) led_trigger_remove(led_cdev); __led_set_brightness(led_cdev, state); //設置燈的亮度(亮滅) return size; }定義brightness屬性的變量
static DEVICE_ATTR_RW(brightness);6.button-backlight 亮燈的實現
static inline void __led_set_brightness(struct led_classdev *led_cdev, enum led_brightness value) { if (value > led_cdev->max_brightness) value = led_cdev->max_brightness; //對傳入的值作越界處理 led_cdev->brightness = value; if (!(led_cdev->flags & LED_SUSPENDED)) led_cdev->brightness_set(led_cdev, value);//執行亮燈操作 }這個函數最終會調用led_cdev->brightness_set,而 led_cdev->brightness_set在leds_drv.c 中已經被賦值過如下:
g_leds_data[i]->cdev.brightness_set = mt65xx_led_set;
static void mt65xx_led_set(struct led_classdev *led_cdev, enum led_brightness level) { struct mt65xx_led_data *led_data = container_of(led_cdev, struct mt65xx_led_data, cdev); ... mt_mt65xx_led_set(led_cdev, level); ... }file:kernel-3.18/drivers/misc/mediatek/leds/mt6735/leds.c
void mt_mt65xx_led_set(struct led_classdev *led_cdev, enum led_brightness level) { struct mt65xx_led_data *led_data = container_of(led_cdev, struct mt65xx_led_data, cdev); /* unsigned long flags; */ /* spin_lock_irqsave(&leds_lock, flags); */ ... /* do something only when level is changed */ if (led_data->level != level) { led_data->level = level; if (strcmp(led_data->cust.name, "lcd-backlight") != 0) { LEDS_DEBUG("Set NLED directly %d at time %lu\n", led_data->level, jiffies); schedule_work(&led_data->work); } else { if (level != 0 && level * CONFIG_LIGHTNESS_MAPPING_VALUE < 255) { level = 1; } else { level = (level * CONFIG_LIGHTNESS_MAPPING_VALUE) / 255; } LEDS_DEBUG ("Set Backlight directly %d at time %lu, mapping level is %d\n", led_data->level, jiffies, level); if (MT65XX_LED_MODE_CUST_BLS_PWM == led_data->cust.mode) { mt_mt65xx_led_set_cust(&led_data->cust, ((((1 << MT_LED_INTERNAL_LEVEL_BIT_CNT) - 1) * level + 127) / 255)); } else { //最終調用mt_mt65xx_led_set_cust mt_mt65xx_led_set_cust(&led_data->cust, level); } } } }mt_mt65xx_led_set_cust的實現
int mt_mt65xx_led_set_cust(struct cust_mt65xx_led *cust, int level) { struct nled_setting led_tmp_setting = { 0, 0, 0 }; int tmp_level = level; static bool button_flag; unsigned int BacklightLevelSupport = Cust_GetBacklightLevelSupport_byPWM(); switch (cust->mode) { case MT65XX_LED_MODE_PWM: if (strcmp(cust->name, "lcd-backlight") == 0) { bl_brightness_hal = level; if (level == 0) { mt_pwm_disable(cust->data, cust->config_data.pmic_pad); } else { if (BacklightLevelSupport == BACKLIGHT_LEVEL_PWM_256_SUPPORT) level = brightness_mapping(tmp_level); else level = brightness_mapto64(tmp_level); mt_backlight_set_pwm(cust->data, level, bl_div_hal, &cust->config_data); } bl_duty_hal = level; } else { if (level == 0) { led_tmp_setting.nled_mode = NLED_OFF; mt_led_set_pwm(cust->data, &led_tmp_setting); mt_pwm_disable(cust->data, cust->config_data.pmic_pad); } else { led_tmp_setting.nled_mode = NLED_ON; mt_led_set_pwm(cust->data, &led_tmp_setting); } } return 1; case MT65XX_LED_MODE_GPIO: LEDS_DEBUG("brightness_set_cust:go GPIO mode!!!!!\n"); return ((cust_set_brightness) (cust->data)) (level); //這裡的MT65XX_LED_MODE_PMIC對應button-backlight的配置的mode=3 case MT65XX_LED_MODE_PMIC: /* for button baclight used SINK channel, when set button ISINK, don't do disable other ISINK channel */ //使用button-backlight的調用如下: if ((strcmp(cust->name, "button-backlight") == 0)) { if (button_flag == false) { switch (cust->data) { case MT65XX_LED_PMIC_NLED_ISINK0: button_flag_isink0 = 1; break; case MT65XX_LED_PMIC_NLED_ISINK1: button_flag_isink1 = 1; break; case MT65XX_LED_PMIC_NLED_ISINK2: button_flag_isink2 = 1; break; case MT65XX_LED_PMIC_NLED_ISINK3: button_flag_isink3 = 1; break; default: break; } button_flag = true; } } return mt_brightness_set_pmic(cust->data, level, bl_div_hal); case MT65XX_LED_MODE_CUST_LCM: if (strcmp(cust->name, "lcd-backlight") == 0) bl_brightness_hal = level; LEDS_DEBUG("brightness_set_cust:backlight control by LCM\n"); /* warning for this API revork */ return ((cust_brightness_set) (cust->data)) (level, bl_div_hal); case MT65XX_LED_MODE_CUST_BLS_PWM: if (strcmp(cust->name, "lcd-backlight") == 0) bl_brightness_hal = level; return ((cust_set_brightness) (cust->data)) (level); case MT65XX_LED_MODE_NONE: default: break; } return -1; }也就是說當用戶對屬性節點 /sys/class/leds/button-backlight/brightness寫入時最終調用mt_brightness_set_pmic函數,
int mt_brightness_set_pmic(enum mt65xx_led_pmic pmic_type, u32 level, u32 div) { static bool first_time = true; LEDS_DEBUG("PMIC#%d:%d\n", pmic_type, level); mutex_lock(&leds_pmic_mutex); if (pmic_type == MT65XX_LED_PMIC_NLED_ISINK0) { if ((button_flag_isink0 == 0) && (first_time == true)) { /* button flag ==0, means this ISINK is not for button backlight */ if (button_flag_isink1 == 0) pmic_set_register_value(PMIC_ISINK_CH1_EN, NLED_OFF); /* sw workround for sync leds status */ if (button_flag_isink2 == 0) pmic_set_register_value(PMIC_ISINK_CH2_EN, NLED_OFF); if (button_flag_isink3 == 0) pmic_set_register_value(PMIC_ISINK_CH3_EN, NLED_OFF); first_time = false; } pmic_set_register_value(PMIC_RG_DRV_32K_CK_PDN, 0x0); /* Disable power down */ pmic_set_register_value(PMIC_RG_DRV_ISINK0_CK_PDN, 0); pmic_set_register_value(PMIC_RG_DRV_ISINK0_CK_CKSEL, 0); pmic_set_register_value(PMIC_ISINK_CH0_MODE, ISINK_PWM_MODE); pmic_set_register_value(PMIC_ISINK_CH0_STEP, ISINK_3); /* 16mA */ pmic_set_register_value(PMIC_ISINK_DIM0_DUTY, 15); pmic_set_register_value(PMIC_ISINK_DIM0_FSEL, ISINK_1KHZ); /* 1KHz */ if (level) pmic_set_register_value(PMIC_ISINK_CH0_EN, NLED_ON); else pmic_set_register_value(PMIC_ISINK_CH0_EN, NLED_OFF); mutex_unlock(&leds_pmic_mutex); return 0; } else if (pmic_type == MT65XX_LED_PMIC_NLED_ISINK1) { if ((button_flag_isink1 == 0) && (first_time == true)) { /* button flag ==0, means this ISINK is not for button backlight */ if (button_flag_isink0 == 0) pmic_set_register_value(PMIC_ISINK_CH0_EN, NLED_OFF); /* sw workround for sync leds status */ if (button_flag_isink2 == 0) pmic_set_register_value(PMIC_ISINK_CH2_EN, NLED_OFF); if (button_flag_isink3 == 0) pmic_set_register_value(PMIC_ISINK_CH3_EN, NLED_OFF); first_time = false; } pmic_set_register_value(PMIC_RG_DRV_32K_CK_PDN, 0x0); /* Disable power down */ pmic_set_register_value(PMIC_RG_DRV_ISINK1_CK_PDN, 0); pmic_set_register_value(PMIC_RG_DRV_ISINK1_CK_CKSEL, 0); pmic_set_register_value(PMIC_ISINK_CH1_MODE, ISINK_PWM_MODE); pmic_set_register_value(PMIC_ISINK_CH1_STEP, ISINK_3); /* 16mA */ pmic_set_register_value(PMIC_ISINK_DIM1_DUTY, 15); pmic_set_register_value(PMIC_ISINK_DIM1_FSEL, ISINK_1KHZ); /* 1KHz */ if (level) pmic_set_register_value(PMIC_ISINK_CH1_EN, NLED_ON); else pmic_set_register_value(PMIC_ISINK_CH1_EN, NLED_OFF); mutex_unlock(&leds_pmic_mutex); return 0; } mutex_unlock(&leds_pmic_mutex); return -1; }上述pmic_set_register_value的操作就是對ISINK具體寄存器的操作,本文不作深入研究
android4.4 webview chromium是單進程的,圖中所有組件都運行在Browser進程中。 按從上而下的順序介紹這張圖中與顯示網頁相關的chromiu
通過Espresso測試錄制器來創建UI注意:Espresso測試記錄器在Android Stuido 2.2中只是一個測試版。工具Espresso測試錄制器可以讓你不寫
在介紹本文動畫效果實現之前,先來介紹屬性動畫相關的幾個知識點。ValueAnimator與ObjectAnimator。 Interpolator插值器與TypeEval
QQ5.0側滑效果實現方案有很多方式,今天我們使用ViewDragHelper來實現一下。先上效果圖:①自定義控件SlidingMenu繼承FrameLayout,放在F