linux设备树笔记__基于msm8x10的基本分析
2015-04-17 16:38
615 查看
由文章,linux设备树笔记__dts基本概念及语法,我们知道了基本概念,知道了大概的设备树节点及其属性,而节点下的属性大多是自定义,除了保留的几个属性,大多从.dts是无法知道其用途的,这个就需要看驱动是如何解析属性值的了,这点也可作技术细节的部分隐藏。
在源码的msm8x12\kernel\arch\arm\boot\dts下有很多xxx.dts,xxx.dtsi,一般一个board就有一个.dts,所以你能看见很多dts文件,即使一个SOC也可能有不同的dts,这样不同的dts会共用很多的设备,那么.dtsi就是起共用设备树的作用,一个板子dts可以像C语言一样来包含dtsi,如/include/ "msm8612-qrd-camera-sensor.dtsi"。
msm8x12\kernel\arch\arm\mach-msm\Makefile.boot关于编译msm8x12的dts部分。
在msm8x12的SOC我们有3个板子型号,假设我们选择的是d500的型号,那么我们一般的不同硬件配置相关都是在board-seuci-d500.dtb中修改,其他的dtb目标文件都是共有的硬件配置,因为我们都是一样的SOC嘛。
其中model和compatible每个板子都有,前者是SOC描述,后者是board型号,后者一般用公司名加上soc型号组合,多个值表示兼容board,并且不要和其他board重名,qcom,board-id是自定义的,有的板子还有qcom,msm-id。
板子中的设备节点一般通过compatible来标示查找的,下面提取board-seuic-d500.dtb的部分设备进行解析并查看驱动是如何利用的:
对应的驱动在msm8x12\kernel\drivers\leds\leds-gpio.c,驱动中使用of相关驱动来解析dts,且全局宏为CONFIG_OF_GPIO。
驱动中在probe前要匹配的关键字是compatible的值,如下,并且匹配优先级of_match_table > id_table > driver.name,且不管是id_table还是of_match_table都要以一个空元素结尾。
gpio_led_probe->gpio_leds_create_of()函数就是解析上述LED设备节点的过程:
trigger的意义及用法见我的另一篇文章《linux驱动____LED子系统笔记》。
NOTE:另外需要注意的是,对于属性名,是否可以随意定义,要看of的API代码,比如gpios属性名是API里使用的,用来指示使用的gpio号及有效标志位,就必须这么用不能改名;
下面再举1个触屏的例子,同一个I2C总线挂了多个设备,DTS代码只贴出2个:
驱动解析在msm8x12\kernel\drivers\input\touchscreen\synaptics_dsx\synaptics_dsx_i2c.c
dts设备匹配驱动,我们的DTS中compatible = "synaptics,dsx"与of_match_table是中的值是一致的,匹配成功!
在synaptics_rmi4_i2c_probe()->parse_dt()中,
of_get_named_gpio_flags(np, "synaptics,irq-gpio", 0, NULL)从DTS中获取中断gpio即触屏的中断连接到soc哪个GPIO上,然后调用request_threaded_irq申请中断及处理函数。
of_property_read_u32(np, "synaptics,irq-on-state", &value),中断中使用:
of_property_read_string(np, "synaptics,pwr-reg-name", &name)对应于DTS的synaptics,pwr-reg-name = "vdd",实际上是regulator的id,根据这个"vdd"的id,可查找到系统中的vdd-supply,然后控制供电行为的enable和disable。那么此处是如何做的呢?!
synaptics_rmi4_probe()->
synaptics_rmi4_get_reg()->
regulator_get(rmi4_data->pdev->dev.parent,bdata->pwr_reg_name)->
_regulator_get->
regulator_dev_lookup->
of_get_regulator->
regnode = of_parse_phandle(dev->of_node, prop_name, 0)
最后一个函数使用snprintf(prop_name, 32, "%s-supply", supply),也就是说返回的regnode是对应于id为“vdd-supply”的设备节点;说到底对于struct regulator *regulator_get(struct device *dev, const char *id),他对于dts的处理就是查找id为"$(id)-supply"对应的dts属性值指向的regulator并返回(此处对应于DTS的vdd-supply
= <&pm8110_l19>,就是查找pm8110_119所引用的regulator并返回),这样驱动就能去控制供电行为了。这样在触屏suspend()和resume()是去对应调用regulator_disable()和regulator_enable()进行供电的关和开。
其他DTS属性大多是这样用的。
在源码的msm8x12\kernel\arch\arm\boot\dts下有很多xxx.dts,xxx.dtsi,一般一个board就有一个.dts,所以你能看见很多dts文件,即使一个SOC也可能有不同的dts,这样不同的dts会共用很多的设备,那么.dtsi就是起共用设备树的作用,一个板子dts可以像C语言一样来包含dtsi,如/include/ "msm8612-qrd-camera-sensor.dtsi"。
msm8x12\kernel\arch\arm\mach-msm\Makefile.boot关于编译msm8x12的dts部分。
# MSM8610 zreladdr-$(CONFIG_ARCH_MSM8610) := 0x00008000 dtb-$(CONFIG_ARCH_MSM8610) += msm8610-v1-cdp.dtb dtb-$(CONFIG_ARCH_MSM8610) += msm8610-v2-cdp.dtb dtb-$(CONFIG_ARCH_MSM8610) += msm8610-v1-mtp.dtb dtb-$(CONFIG_ARCH_MSM8610) += msm8610-v2-mtp.dtb dtb-$(CONFIG_ARCH_MSM8610) += msm8610-rumi.dtb dtb-$(CONFIG_ARCH_MSM8610) += msm8610-sim.dtb dtb-$(CONFIG_ARCH_MSM8610) += msm8610-v1-qrd-skuaa.dtb dtb-$(CONFIG_ARCH_MSM8610) += msm8610-v1-qrd-skuab.dtb dtb-$(CONFIG_ARCH_MSM8610) += msm8610-v2-qrd-skuaa.dtb dtb-$(CONFIG_ARCH_MSM8610) += msm8610-v2-qrd-skuab.dtb ifeq ($(CONFIG_ARCH_MSM8610_CB01),y) dtb-$(CONFIG_ARCH_MSM8610) += board-seuic-cb01.dtb endif ifeq ($(CONFIG_ARCH_MSM8610_D330S),y) dtb-$(CONFIG_ARCH_MSM8610) += board-seuic-d330s.dtb endif ifeq ($(CONFIG_ARCH_MSM8610_D500),y) dtb-$(CONFIG_ARCH_MSM8610) += board-seuic-d500.dtb endif ifeq ($(CONFIG_ARCH_MSM8610_D510),y) dtb-$(CONFIG_ARCH_MSM8610) += board-seuic-d510.dtb endif
在msm8x12的SOC我们有3个板子型号,假设我们选择的是d500的型号,那么我们一般的不同硬件配置相关都是在board-seuci-d500.dtb中修改,其他的dtb目标文件都是共有的硬件配置,因为我们都是一样的SOC嘛。
/ { model = "Qualcomm MSM 8610v2 QRD SKUAB D510"; compatible = "qcom,msm8610-qrd", "qcom,msm8610", "qcom,qrd"; qcom,board-id = <0x6000b 3>; };
其中model和compatible每个板子都有,前者是SOC描述,后者是board型号,后者一般用公司名加上soc型号组合,多个值表示兼容board,并且不要和其他board重名,qcom,board-id是自定义的,有的板子还有qcom,msm-id。
板子中的设备节点一般通过compatible来标示查找的,下面提取board-seuic-d500.dtb的部分设备进行解析并查看驱动是如何利用的:
LED设备:
&soc { ... gpio-leds { compatible = "gpio-leds";//驱动要匹配的关键字 status = "ok"; /* scan_led */ red1_led { gpios = <&pm8110_gpios 1 0x0>; label = "scan"; linux,default-trigger = "heartbeat"; default-state = "off"; }; /* charge_ing */ red2_led { gpios = <&pm8110_gpios 3 0x0>; label = "red1"; linux,default-trigger = "battery-charging"; default-state = "off"; retain-state-suspended = "yes"; }; /* charge_complete */ green_led { gpios = <&pm8110_gpios 2 0x0>; label = "green1"; linux,default-trigger = "battery-full"; default-state = "off"; retain-state-suspended = "yes"; }; }; ... };设备树中的SOC下一级的设备gpio-leds,有三个子节点,分别是扫描灯,充电中指示灯,充电完成指示灯,下面以充电完成指示灯来说明。
对应的驱动在msm8x12\kernel\drivers\leds\leds-gpio.c,驱动中使用of相关驱动来解析dts,且全局宏为CONFIG_OF_GPIO。
驱动中在probe前要匹配的关键字是compatible的值,如下,并且匹配优先级of_match_table > id_table > driver.name,且不管是id_table还是of_match_table都要以一个空元素结尾。
static const struct of_device_id of_gpio_leds_match[] = { { .compatible = "gpio-leds", }, {}, }; static struct platform_driver gpio_led_driver = { .probe = gpio_led_probe, .remove = __devexit_p(gpio_led_remove), .driver = { .name = "leds-gpio", .owner = THIS_MODULE, .of_match_table = of_gpio_leds_match, }, }; module_platform_driver(gpio_led_driver);匹配成功后才会调用probe,其中module_platform_driver(gpio_led_driver)是一个模块初始化和退出的宏:
#define module_platform_driver(__platform_driver) \ static int __init __platform_driver##_init(void) \ { \ return platform_driver_register(&(__platform_driver)); \ } \ module_init(__platform_driver##_init); \ static void __exit __platform_driver##_exit(void) \ { \ platform_driver_unregister(&(__platform_driver)); \ } \ module_exit(__platform_driver##_exit);
gpio_led_probe->gpio_leds_create_of()函数就是解析上述LED设备节点的过程:
static struct gpio_leds_priv * __devinit gpio_leds_create_of(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node, *child; struct gpio_leds_priv *priv; int count = 0, ret; /* count LEDs in this device, so we know how much to allocate */ for_each_child_of_node(np, child) count++; if (!count) return NULL; priv = kzalloc(sizeof_gpio_leds_priv(count), GFP_KERNEL); if (!priv) return NULL; for_each_child_of_node(np, child) { struct gpio_led led = {}; enum of_gpio_flags flags; const char *state; const char *retain_state; led.gpio = of_get_gpio_flags(child, 0, &flags);//获取gpio序号及有效标志,实际上是通过gpios属性获取 led.active_low = flags & OF_GPIO_ACTIVE_LOW;//如为OF_GPIO_ACTIVE_LOW即值为1,那么点亮set 0就是set 1,要进行逻辑取反 led.name = of_get_property(child, "label", NULL) ? : child->name;//label属性为子节点设备名 led.default_trigger =//默认的LED trigger of_get_property(child, "linux,default-trigger", NULL); state = of_get_property(child, "default-state", NULL);//开机默认的LED状态 if (state) { if (!strcmp(state, "keep")) led.default_state = LEDS_GPIO_DEFSTATE_KEEP; else if (!strcmp(state, "on")) led.default_state = LEDS_GPIO_DEFSTATE_ON; else led.default_state = LEDS_GPIO_DEFSTATE_OFF; } retain_state = of_get_property(child, "retain-state-suspended", NULL);//休眠时LED的状态 if (retain_state) { if (!strcmp(retain_state, "yes")) led.retain_state_suspended = 1; else led.retain_state_suspended = 0; } ret = create_gpio_led(&led, &priv->leds[priv->num_leds++],//创建LED设备 &pdev->dev, NULL); if (ret < 0) { of_node_put(child); goto err; } } return priv; err: for (count = priv->num_leds - 2; count >= 0; count--) delete_gpio_led(&priv->leds[count]); kfree(priv); return NULL; }如驱动中所示,大多通过of_xxx的API函数来解析dts设备节点属性,属性就是KEY-VALUE结构,至于值的意义完全由驱动来控制。比如of_get_property(child, "linux,default-trigger", NULL),其中child是当前设备节点,参数二就是待查找的属性,返回值是对应属性的值,此处就是对应于DTS的linux,default-trigger = "battery-full",进行解析,返回值应该为“battery-full”,至于这个LED
trigger的意义及用法见我的另一篇文章《linux驱动____LED子系统笔记》。
NOTE:另外需要注意的是,对于属性名,是否可以随意定义,要看of的API代码,比如gpios属性名是API里使用的,用来指示使用的gpio号及有效标志位,就必须这么用不能改名;
/** * of_get_gpio_flags() - Get a GPIO number and flags to use with GPIO API * @np: device node to get GPIO from * @index: index of the GPIO * @flags: a flags pointer to fill in * * Returns GPIO number to use with Linux generic GPIO API, or one of the errno * value on the error condition. If @flags is not NULL the function also fills * in flags for the GPIO. */ static inline int of_get_gpio_flags(struct device_node *np, int index, enum of_gpio_flags *flags) { return of_get_named_gpio_flags(np, "gpios", index, flags); }
而对于像"linux,default-trigger"是可以随便定义的,因为它唯一使用的地方就是使用of相关API在驱动中获取属性值,完全由驱动开发者的思维逻辑控制的。
触屏:下面再举1个触屏的例子,同一个I2C总线挂了多个设备,DTS代码只贴出2个:
&soc { ... i2c@f9923000{ /*synaptics v2.3 DS4/DS5*/ synaptics_dsx@20 {//节点一般以设备名@addr组合 compatible = "synaptics,dsx";//触屏设备名 reg = <0x20>;//从设备I2C地址 interrupt-parent = <&msmgpio>;//I2C的中断给谁处理 interrupts = <1 0x2002>;//I2C中断给msmgpio处理,后面详细解释 vdd-supply = <&pm8110_l19>;//I2C设备的VDD由电源管理芯片pm8110的pin_114供电 vcc_i2c-supply = <&pm8110_l14>;//与上类似 synaptics,pwr-reg-name = "vdd";//供电名 synaptics,bus-reg-name = "vcc_i2c";//与上类似 synaptics,irq-gpio = <&msmgpio 1 0x2008>;//具体中断资源连那个gpio synaptics,irq-flags = <0x2002>;/* IRQF_ONESHOT | IRQF_TRIGGER_FALLING */ synaptics,power-delay-ms = <160>; synaptics,reset-delay-ms = <100>; synaptics,swap-axes; //synaptics,x-flip; synaptics,y-flip; synaptics,irq-on-state = <0>; }; akm09911@d { compatible = "qcom,nfc-akm09911";//电子罗盘设备名 reg = <0x0d>;//I2C从地址 vdd-supply = <&pm8110_l19>; vio-supply = <&pm8110_l14>; akm09911,layout = <3>; //厂商定制的芯片贴片时的布局标志 akm09911,reset-pin = <35>; }; } ... }
驱动解析在msm8x12\kernel\drivers\input\touchscreen\synaptics_dsx\synaptics_dsx_i2c.c
dts设备匹配驱动,我们的DTS中compatible = "synaptics,dsx"与of_match_table是中的值是一致的,匹配成功!
#ifdef CONFIG_OF static struct of_device_id synaptics_rmi4_of_match_table[] = { { .compatible = "synaptics,dsx", }, {}, }; MODULE_DEVICE_TABLE(of, synaptics_rmi4_of_match_table); #else #define synaptics_rmi4_of_match_table NULL #endif static const struct i2c_device_id synaptics_rmi4_id_table[] = { {"synaptics_dsx_i2c", 0}, {}, }; MODULE_DEVICE_TABLE(i2c, synaptics_rmi4_id_table); static struct i2c_driver synaptics_rmi4_i2c_driver = { .driver = { .name = I2C_DRIVER_NAME, .owner = THIS_MODULE, .of_match_table = synaptics_rmi4_of_match_table, }, .probe = synaptics_rmi4_i2c_probe, .remove = __devexit_p(synaptics_rmi4_i2c_remove), .id_table = synaptics_rmi4_id_table, };
在synaptics_rmi4_i2c_probe()->parse_dt()中,
static int parse_dt(struct device *dev, struct synaptics_dsx_board_data *bdata) { int retval; u32 value; const char *name; struct property *prop; struct device_node *np = dev->of_node; bdata->irq_gpio = of_get_named_gpio_flags(np,//中断gpio号,忽略flag "synaptics,irq-gpio", 0, NULL); retval = of_property_read_u32(np, "synaptics,irq-on-state",//中断处理时gpio电平是H还是L &value); if (retval < 0) bdata->irq_on_state = 0; else bdata->irq_on_state = value; retval = of_property_read_u32(np, "synaptics,irq-flags", &value);//中断触发处理标志 if (retval < 0) return retval; else bdata->irq_flags = value; retval = of_property_read_string(np, "synaptics,pwr-reg-name", &name);//供电名,作为后面regulator_get的参数id if (retval == -EINVAL) bdata->pwr_reg_name = NULL; else if (retval < 0) return retval; else bdata->pwr_reg_name = name; retval = of_property_read_string(np, "synaptics,bus-reg-name", &name);//同上 if (retval == -EINVAL) bdata->bus_reg_name = NULL; else if (retval < 0) return retval; else bdata->bus_reg_name = name; if (of_property_read_bool(np, "synaptics,power-gpio")) {//DTS未使用到。上电时,power_gpio初始状态 bdata->power_gpio = of_get_named_gpio_flags(np, "synaptics,power-gpio", 0, NULL); retval = of_property_read_u32(np, "synaptics,power-on-state", &value); if (retval < 0) return retval; else bdata->power_on_state = value; } else { bdata->power_gpio = -1; } if (of_property_read_bool(np, "synaptics,power-delay-ms")) {//电源上电或resume时要等待的时间 retval = of_property_read_u32(np, "synaptics,power-delay-ms", &value); if (retval < 0) return retval; else bdata->power_delay_ms = value; } else { bdata->power_delay_ms = 0; } if (of_property_read_bool(np, "synaptics,reset-gpio")) {//DTS未使用 bdata->reset_gpio = of_get_named_gpio_flags(np, "synaptics,reset-gpio", 0, NULL); retval = of_property_read_u32(np, "synaptics,reset-on-state", &value); if (retval < 0) return retval; else bdata->reset_on_state = value; retval = of_property_read_u32(np, "synaptics,reset-active-ms", &value); if (retval < 0) return retval; else bdata->reset_active_ms = value; } else { bdata->reset_gpio = -1; } if (of_property_read_bool(np, "synaptics,reset-delay-ms")) {//执行soft-reset时的等待时间 retval = of_property_read_u32(np, "synaptics,reset-delay-ms", &value); if (retval < 0) return retval; else bdata->reset_delay_ms = value; } else { bdata->reset_delay_ms = 0; } if (of_property_read_bool(np, "synaptics,max-y-for-2d")) {//DTS未使用,触屏报点的Y方向最大值 retval = of_property_read_u32(np, "synaptics,max-y-for-2d", &value); if (retval < 0) return retval; else bdata->max_y_for_2d = value; } else { bdata->max_y_for_2d = -1; } bdata->swap_axes = of_property_read_bool(np, "synaptics,swap-axes");//触屏报点X,Y交换 bdata->x_flip = of_property_read_bool(np, "synaptics,x-flip");//X值变为关于X中轴对称 bdata->y_flip = of_property_read_bool(np, "synaptics,y-flip");//Y值变为关于Y中轴对称 prop = of_find_property(np, "synaptics,cap-button-codes", NULL);//DTS未使用 if (prop && prop->length) { bdata->cap_button_map->map = devm_kzalloc(dev, prop->length, GFP_KERNEL); if (!bdata->cap_button_map->map) return -ENOMEM; bdata->cap_button_map->nbuttons = prop->length / sizeof(u32); retval = of_property_read_u32_array(np, "synaptics,cap-button-codes", bdata->cap_button_map->map, bdata->cap_button_map->nbuttons); if (retval < 0) { bdata->cap_button_map->nbuttons = 0; bdata->cap_button_map->map = NULL; } } else { bdata->cap_button_map->nbuttons = 0; bdata->cap_button_map->map = NULL; } prop = of_find_property(np, "synaptics,vir-button-codes", NULL);//DTS未使用 if (prop && prop->length) { bdata->vir_button_map->map = devm_kzalloc(dev, prop->length, GFP_KERNEL); if (!bdata->vir_button_map->map) return -ENOMEM; bdata->vir_button_map->nbuttons = prop->length / sizeof(u32); bdata->vir_button_map->nbuttons /= 5; retval = of_property_read_u32_array(np, "synaptics,vir-button-codes", bdata->vir_button_map->map, bdata->vir_button_map->nbuttons * 5); if (retval < 0) { bdata->vir_button_map->nbuttons = 0; bdata->vir_button_map->map = NULL; } } else { bdata->vir_button_map->nbuttons = 0; bdata->vir_button_map->map = NULL; } return 0; }
of_get_named_gpio_flags(np, "synaptics,irq-gpio", 0, NULL)从DTS中获取中断gpio即触屏的中断连接到soc哪个GPIO上,然后调用request_threaded_irq申请中断及处理函数。
of_property_read_u32(np, "synaptics,irq-on-state", &value),中断中使用:
static irqreturn_t synaptics_rmi4_irq(int irq, void *data) { struct synaptics_rmi4_data *rmi4_data = data; const struct synaptics_dsx_board_data *bdata = rmi4_data->hw_if->board_data; if (gpio_get_value(bdata->irq_gpio) != bdata->irq_on_state)//当前中断线gpio值与irq_on_state值一致才会报点 goto exit; synaptics_rmi4_sensor_report(rmi4_data); exit: return IRQ_HANDLED; }of_property_read_u32(np, "synaptics,irq-flags", &value)是request_threaded_irq()申请中断处理资源的中断方式标志,对应于DTS中的synaptics,irq-flags = <0x2002>,那么这个值是什么意思,就是使用标准的中断方式定义,在msm8x12\kernel\include\linux\interrupt.h中有定义:
//hardware interrupt line behaviour flags #define IRQF_TRIGGER_NONE 0x00000000 #define IRQF_TRIGGER_RISING 0x00000001 #define IRQF_TRIGGER_FALLING 0x00000002 #define IRQF_TRIGGER_HIGH 0x00000004 #define IRQF_TRIGGER_LOW 0x00000008 #define IRQF_TRIGGER_MASK (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | \ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING) #define IRQF_TRIGGER_PROBE 0x00000010 //used only by the kernel as part of the irq handling routines. #define IRQF_DISABLED 0x00000020 #define IRQF_SAMPLE_RANDOM 0x00000040 #define IRQF_SHARED 0x00000080 #define IRQF_PROBE_SHARED 0x00000100 #define __IRQF_TIMER 0x00000200 #define IRQF_PERCPU 0x00000400 #define IRQF_NOBALANCING 0x00000800 #define IRQF_IRQPOLL 0x00001000 #define IRQF_ONESHOT 0x00002000 #define IRQF_NO_SUSPEND 0x00004000 #define IRQF_FORCE_RESUME 0x00008000 #define IRQF_NO_THREAD 0x00010000 #define IRQF_EARLY_RESUME 0x00020000 #define IRQF_TIMER (__IRQF_TIMER | IRQF_NO_SUSPEND | IRQF_NO_THREAD)中断标志定义都是掩码,基本常用的就是上下降沿、高低电平,还有常用的IRQF_DISABLED进行或组合。上述DTS的中断标志0x2002就是IRQF_ONESHOT | IRQF_TRIGGER_FALLING,即下降沿触发中断,且在中断thread未完成前不开放中断。
of_property_read_string(np, "synaptics,pwr-reg-name", &name)对应于DTS的synaptics,pwr-reg-name = "vdd",实际上是regulator的id,根据这个"vdd"的id,可查找到系统中的vdd-supply,然后控制供电行为的enable和disable。那么此处是如何做的呢?!
synaptics_rmi4_probe()->
synaptics_rmi4_get_reg()->
regulator_get(rmi4_data->pdev->dev.parent,bdata->pwr_reg_name)->
_regulator_get->
regulator_dev_lookup->
of_get_regulator->
regnode = of_parse_phandle(dev->of_node, prop_name, 0)
最后一个函数使用snprintf(prop_name, 32, "%s-supply", supply),也就是说返回的regnode是对应于id为“vdd-supply”的设备节点;说到底对于struct regulator *regulator_get(struct device *dev, const char *id),他对于dts的处理就是查找id为"$(id)-supply"对应的dts属性值指向的regulator并返回(此处对应于DTS的vdd-supply
= <&pm8110_l19>,就是查找pm8110_119所引用的regulator并返回),这样驱动就能去控制供电行为了。这样在触屏suspend()和resume()是去对应调用regulator_disable()和regulator_enable()进行供电的关和开。
其他DTS属性大多是这样用的。
相关文章推荐
- Linux 2.6下SPI设备模型--------基于AT91RM9200分析
- Linux 2.6下SPI设备模型--------基于AT91RM9200分析
- Linux 2.6下SPI设备模型--------基于AT91RM9200分析
- Linux 2.6下SPI设备模型--------基于AT91RM9200分析
- 基于mini6410的linux驱动学习总结(五 字符设备驱动程序实例分析(虚拟设备驱动))
- Linux I2C设备驱动分析 基于2440 2.6.32内核
- linux 下块设备驱动开发学习笔记 2(sbull驱动分析)
- pci-e转sata控制器siI3124驱动分析笔记(基于linux)
- Linux 2.6.23下SPI设备模型--------基于AT91RM9200分析
- Linux设备模型分析之基本数据结构
- Linux设备模型分析之kobject(基于3.10.1内核)
- Linux设备模型分析之kset(基于3.10.1内核)
- Linux设备模型分析之kset(基于3.10.1内核)
- Linux设备模型分析之bus(基于3.10.1内核)
- Linux设备模型分析之device(基于3.10.1内核)
- Linux设备模型分析之device_driver(基于3.10.1内核)
- Linux设备驱动程序架构分析之platform(基于3.10.1内核)
- Linux设备驱动程序架构分析之I2C架构(基于3.10.1内核)
- 学习笔记 --- LINUX MTD设备之NANDFLASH驱动分析
- Linux设备模型分析之device(基于3.10.1内核)