2 、 kernel 层 sysfs 接口函数的建立
这里所分析的代码基于三星公司的手机产品 i5700 。涉及到电源管理的设备,该设备驱动应该增添相应的代码以支持相应的电源管理,如 suspend 、 resume 。同时应该有相应的代码向 sysfs 提供相应的 entry 供用户使用。由于该流程分析的是 Lcd 亮度的调节,所以涉及到 Lcd 驱动向 sysfs 提供的 entry 。 suspend 、 resume 等功能在 android 层调用
int release_wake_lock(const char* id);
int acquire_wake_lock(int lock, const char* id);
时起作用。
在 i5700 中, Lcd 驱动包括两个文件,一个是与特定硬件相关的 s3cfb_s6d05a.c ,该文件实现了与硬件相关的操作,包括 GPIO 口的初始化和电源管理的相关功能函数等;一个是三星通用的 s3cfb.c ,作为桥梁作用联系着向 sysfs 提供的 entry 和 s3cfb_s6d05a.c 中相关功能函数 。
1 )、 /drivers/video/samsung/s3cfb.c
该文件向 sys 提供了三个 entry ,让我们来看看这三个 entry 的建立流程。
(1) 、 entry 对应的读、写功能函数:
三个 entry 分别是 lcd_power 、 backlight_power 、 backlight_level ,这几个 entry 在 android 系统起来后,在终端通过 adb shell ,敲入如下命令就可以看到:
# ls /sys/devices/platform/s3c-lcd
其中 *show* 表示读操作的功能函数, *store* 表示写操作的功能函数。在后面会对其中一个函数有进行详细的分析。
static int s3cfb_sysfs_show_lcd_power(struct device *dev, struct device_attribute *attr, char *buf) static int s3cfb_sysfs_store_lcd_power(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) static int s3cfb_sysfs_show_backlight_power(struct device *dev, struct device_attribute *attr, char *buf) static int s3cfb_sysfs_store_backlight_power(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) static int s3cfb_sysfs_show_backlight_level(struct device *dev, struct device_attribute *attr, char *buf) static int s3cfb_sysfs_store_backlight_level(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) |
(2) 、 entry 属性的建立:
在 内核 中, sysfs 属性一般是由 __ATTR 系列的宏来声明的,如对设备的使用 DEVICE_ATTR ,对总线使用 BUS_ATTR ,对驱动使用 DRIVER_ATTR ,对类别 (class) 使用 CLASS_ATTR, 这四个高级的宏来自于 <include/linux/device.h> 。在 s3cfb.c 里,使用的是 DEVICE_ATTR 来建立 entry 在 sysfs 中的属性。
static DEVICE_ATTR(lcd_power, 0666, s3cfb_sysfs_show_lcd_power, s3cfb_sysfs_store_lcd_power);
static DEVICE_ATTR(backlight_power, 0666, s3cfb_sysfs_show_backlight_power, s3cfb_sysfs_store_backlight_power);
static DEVICE_ATTR(backlight_level, 0644, s3cfb_sysfs_show_backlight_level, s3cfb_sysfs_store_backlight_level); |
DEVICE_ATTR 宏声明有四个参数,分别是名称、权限位、读函数、写函数。其中读函数和写函数是读写功能函数的函数名。
(3) 、 entry 的创建
entry 的创建是通过函数 device_create_file 完成,在 static int __inits3cfb_probe(struct platform_device *pdev) 函数内实现的。
ret = device_create_file(&(pdev->dev), &dev_attr_backlight_power);
if (ret < 0) printk(KERN_WARNING "s3cfb: failed to add entries/n");
ret = device_create_file(&(pdev->dev), &dev_attr_backlight_level);
if (ret < 0) printk(KERN_WARNING "s3cfb: failed to add entries/n");
ret = device_create_file(&(pdev->dev), &dev_attr_lcd_power);
if (ret < 0) printk(KERN_WARNING "s3cfb: failed to add entries/n"); |
通过以上简单的三个步骤,就可以在 shell 终端查看到这三个 entry 了。当我们将数据 echo 到这几个 entry 中时,在上层实际上完成了一次 write 操作,对应到 kernel ,分别调用了 lcd 驱动中的三个 *store* 。同理,当我们 cat 一个 entry 时则会调用 *show* 。通过在 *show* 和 *store* 中插桩就可以看到效果。到这里,只是简单的建立了 android 层到 kernel 的桥梁,真正实现对硬件操作的,还是在 *show* 和 *store* 中完成的。
(4) 、 backlight_level entry 写函数分析
static int s3cfb_sysfs_store_backlight_level(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { unsigned long value = simple_strtoul(buf, NULL, 10);
if (value < s3c_fimd.backlight_min || value > s3c_fimd.backlight_max) return -ERANGE;
s3cfb_set_backlight_level(value);
return len; } |
这里调用了 s3cfb_set_backlight_level(value);
static void s3cfb_set_backlight_level(int to) { backlight_level = to;
if (s3c_fimd.set_brightness) (s3c_fimd.set_brightness)(to); } |
在这个函数里, s3c_fimd.set_brightness 是一个函数指针,如果不为空,刚调用 (s3c_fimd.set_brightness)(to); 。在这里还看不到到底调用了哪个函数,在 s3cfb_s6d05a.c 中有能该函数指针的初始化以及最终功能函数的实现。
2 )、 /drivers/video/samsung/s3cfb_s6d05a.c
上文提到的函数接口的初始化是在 static voids3cfb_set_fimd_info(void) 中完成的:
s3c_fimd.set_lcd_power = lcd_power_ctrl; s3c_fimd.set_backlight_power = backlight_power_ctrl; s3c_fimd.set_brightness = backlight_level_ctrl; |
因此 (s3c_fimd.set_brightness)(to); 实际上是调用了 backlight_level_ctrl(to);
void backlight_level_ctrl(s32 value) { if ((value < BACKLIGHT_LEVEL_MIN) || /* Invalid Value */ (value > BACKLIGHT_LEVEL_MAX) || (value == backlight_level)) /* Same Value */ return;
if (backlight_power) backlight_ctrl(value);
backlight_level = value; } |
这个函数实现的是 LCD 背光的调节,可以参考程杰SX的博客:
http://hi.baidu.com/aokikyon/blog/item/ea947e36e42949d0a2cc2b55.html