初级驱动调试笔记-总结


记录下基本的初级驱动调试的心得,比较适合没有什么经验的新手小白。

一、拿到开发板的第一步

1)找硬件工程师确定开发板板子正常。
2)找到debug串口
3)烧录镜像(首先编译过)
4)验证小系统(能不能进入kernel,是否能够在串口进行命令操作,一般来说能够在串口进行操作都可以认为是小系统ok)
5)开始调试

二、调试第一步

1)找到正确的编译和烧录方式。

2)修改对应的板级设备树文件(首先要对设备树有基础的了解)
(1)首先关掉大多数的外设开关,这样看log的时候会少很多的东西,不至于眼花缭乱。
举个例子来说:

&micfil {
	status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_pdm>;
    assigned-clocks = <&clk IMX8MP_CLK_PDM>;
    assigned-clock-parents = <&clk IMX8MP_AUDIO_PLL1_OUT>;
    assigned-clock-rates = <196608000>;
};

这里有一个 status = "okay"; 这就是一个开关,相当于打开了这个外设,这个开关不会受到之前的影响,他会覆盖之前的设置,所以不用去关心之前是什么状态。
我们关闭这个外设只需要将status = "okay";改为status = "disabled";就可以了。
比如你调试lvds你就可把audio,camera,wifi这些关掉,当你的lvds调试ok了,再把下一个你需要调试的打开。这样即避免了冲突,也可以减少log的复杂度。

3)修改gpio,匹配驱动
(这是nxp平台的模板,rk的模板类似甚至更加简单,rk的可以去firefly看对应的文章 https://wiki.t-firefly.com/zh_CN/AIO-3288C/driver_gpio.html

    ap6256_wifi: ap6256_wifi {
        /*用于匹配对应的驱动名称,具有唯一性*/
        compatible = "android,bcmdhd_wlan";
      	/*pinctrl子系统,一般用于配置gpio的拉高拉低*/  
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_wlan>;
        /*对应的gpio*/
        gpio_wl_reg_on = <&gpio3 0 GPIO_ACTIVE_HIGH>;   
        gpio_wl_host_wake = <&gpio3 7 GPIO_ACTIVE_LOW>; 
    };
	/*对应的pinctrl子系统配置,也就是配置gpio的上拉和下拉*/
    pinctrl_wlan: wlangrp {
        fsl,pins = <
            MX8MP_IOMUXC_NAND_DATA01__GPIO3_IO07    0x16 
            MX8MP_IOMUXC_NAND_ALE__GPIO3_IO00   0x41  
        >;
    };

配置gpio的时候需要看是不是有冲突,如果有冲突需要修改对应的gpio,否则不会生效。检查是否生效可以在开发板里面执行命令

#  cat /d/gpio
or
# cat sys/kernel/debug/gpio

可以看到对应的gpio状态(自己去了解以下gpio号与gpio组的关系)

gpiochip0: GPIOs 0-31, parent: platform/fdd60000.gpio, gpio0:
 gpio-5   (                    |vcc_sd              ) out hi    
 gpio-6   (                    |spk-ctl             ) out lo      

gpiochip1: GPIOs 32-63, parent: platform/fe740000.gpio, gpio1:

gpiochip2: GPIOs 64-95, parent: platform/fe750000.gpio, gpio2:
 gpio-73  (                    |reset               ) out lo    
 gpio-77  (                    |bt_default_rts      ) out hi       
 gpio-81  (                    |bt_default_wake     ) in  lo    

gpiochip3: GPIOs 96-127, parent: platform/fe760000.gpio, gpio3:
 gpio-99  (                    |enable              ) in  hi    
 gpio-100 (                    |eeprom-wp           ) out lo    

gpiochip4: GPIOs 128-159, parent: platform/fe770000.gpio, gpio4:
 gpio-136 (                    |reset               ) out lo    
 gpio-138 (                    |vcc_camera          ) out hi

不是所有的gpio都需要去配置pinctrl有的也可能不需要,看情况而定。

4)驱动匹配调试
如果你的驱动匹配没问题,gpio的状态也正确,那么下一步就是检查你的对应的节点有没有生成,如果有,那么你的驱动就好了。你可以在驱动的 probe(驱动注册) 函数里面添加打印,在 probe 函数里面去看是否是正确返回。
举个例子:
这是一个rk817的驱动注册函数

static int rk817_platform_probe(struct platform_device *pdev)
{
    struct rk808 *rk817 = dev_get_drvdata(pdev->dev.parent);
    struct rk817_codec_priv *rk817_codec_data;
    int ret;
		/*这里添加一个打印,验证是否是调用了这个函数,如果这个函数没有调用,那么你就需要看看设备树的匹配和对应的Makefile文件是否打开*/
	printk ("验证函数是否进入");	
    DBG("%s\n", __func__);//其实这也是一个打印,但是为了我们明显我们可以增加一个

	.........................
   
    platform_set_drvdata(pdev, rk817_codec_data);

    ret = rk817_codec_parse_dt_property(&pdev->dev, rk817_codec_data);
    if (ret < 0) {
        dev_err(&pdev->dev, "%s() parse device tree property error %d\n",
            __func__, ret);
        goto err_;
    }

    rk817_codec_data->regmap = devm_regmap_init_i2c(rk817->i2c,
                        &rk817_codec_regmap_config);
    if (IS_ERR(rk817_codec_data->regmap)) {
        ret = PTR_ERR(rk817_codec_data->regmap);
        dev_err(&pdev->dev, "failed to allocate register map: %d\n",
            ret);
        goto err_;
    }
  
   ......................................
       ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_dev_rk817,
                          rk817_dai, ARRAY_SIZE(rk817_dai));
    if (ret < 0) {
        dev_err(&pdev->dev, "%s() register codec error %d\n",
            __func__, ret);
        goto err_;
    }
		/*这里在再添加一个打印信息*/
	printk ("验证函数是否正确返回");	
    return 0;
err_:
	printk ("验证函数错误返回");	
    return ret;
}

通过驱动的基本打印信息我们就可以看到你的驱动是否正确注册,如果正确注册,那么就继续看下一步,如果没有,就需要看看错在那里(一般就是设备树的gpio冲突了,或则没没有匹配上)

5)节点查询
这里说几个简单的例子,其余的可以自己查手册或资料:

背光:

# cd /sys/class/backlight
/sys/class/backlight # ls
lvds_backlight
/*如果背光ok,那么在/sys/class/backlight 目录下会有对应的背光节点*/
/sys/class/backlight/lvds_backlight # ls
actual_brightness  bl_power  brightness  device  max_brightness  power  scale  subsystem  type  uevent
/*可以设置背光的占空比,来设置屏幕亮度*/

屏幕:

# cd /sys/class/graphics
/sys/class/graphics #ls
fb0
/*如果屏幕设置正确,驱动正常,在/sys/class/graphics目录下会有一个对应的fb节点*/

wifi:

# ifconifg -a
wlan0     Link encap:Ethernet  HWaddr 10:2c:6b:69:14:ba  Driver bcmsdh_sdmmc
          BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0 
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 
          collisions:0 txqueuelen:1000 
          RX bytes:0 TX bytes:0 
/*会有对应的 wlan0生成,没有也不要慌,去点击一下wifi的开关后,再执行,看有没有,或则看log有没有对应的模块加载*/

三、Makefile说明

3.1 内核版本说明

对于安卓来说,在kernel的目录下会有一个Makefile文件

kernel_imx$ ls -l Makefile 
-rw-rw-r-- 1 cluo cluo 68126 127 01:42 Makefile

我们打开这个Makefile,在最前面就是对内核版本的说明

# SPDX-License-Identifier: GPL-2.0
VERSION = 5
PATCHLEVEL = 10
SUBLEVEL = 52
EXTRAVERSION =
NAME = Dare mighty things

/*内核版本为 5.10.52*/

要了解这个原因是因为有的时候会问你你的内核版本是多少,你要知道在那里看。

3.2 Makefile文件

在进入内核后,你会看到每一个文件夹都会有一个对应的Makefile文件。你可以把这个想成一个套娃,里面的Makefile文件会被外面的Makefile限制,如果外面的Makefile没有给你权限,你里面的Makefile也不会其作用。
我们主要关注的是.c文件夹所在的Makefile
我们随便找一个驱动文件夹,进入到对应的目录:

kernel_imx/drivers/bus$ ls

arm-cci.c            bt1-axi.c       imx-weim.c  mips_cdmm.c    omap_l3_noc.h   qcom-ebi2.c      tegra-aconnect.c  ts-nbus.c
arm-integrator-lm.c  da8xx-mstpri.c  Kconfig     moxtet.c       omap_l3_smx.c   simple-pm-bus.c  tegra-gmi.c       uniphier-system-bus.c
brcmstb_gisb.c       fsl-mc          Makefile    mvebu-mbus.c   omap_l3_smx.h   sun50i-de2.c     ti-pwmss.c        vexpress-config.c
bt1-apb.c            hisi_lpc.c      mhi         omap_l3_noc.c  omap-ocp2scp.c  sunxi-rsb.c      ti-sysc.c

# vi Makefile 

# Interconnect bus drivers for ARM platforms
obj-$(CONFIG_ARM_CCI)           += arm-cci.o
obj-$(CONFIG_ARM_INTEGRATOR_LM) += arm-integrator-lm.o
obj-$(CONFIG_HISILICON_LPC)     += hisi_lpc.o
obj-$(CONFIG_BRCMSTB_GISB_ARB)  += brcmstb_gisb.o
obj-$(CONFIG_MOXTET)            += moxtet.o

我们在Makefile里面可以看到这些东西,发现 xxx.o 文件和外面的 xxx.c文件名字是一样的。至于前面的 CONFIG_ARM_CCI 我们可以理解为一个开关,他是遍不编译对应的.C文件的控制。
如果没有Makefile文件我们就需要去把对应的.c文件一个一个的编译,内核里面那么多.c怎么可能一个一个的编译,所以才会使用Makefile的文件去编译。(Makefile又叫做自动编译脚本)

# vi Makefile 

# Interconnect bus drivers for ARM platforms
obj-$(CONFIG_ARM_CCI)           += arm-cci.o
obj-$(CONFIG_ARM_INTEGRATOR_LM) += arm-integrator-lm.o
obj-$(CONFIG_HISILICON_LPC)     += hisi_lpc.o
obj-$(CONFIG_BRCMSTB_GISB_ARB)  += brcmstb_gisb.o
obj-$(CONFIG_MOXTET)            += moxtet.o

如果我们把他改一改可以么!可以。

# Interconnect bus drivers for ARM platforms
obj-y			+= arm-cci.o
obj-y 			+= arm-integrator-lm.o
obj-y			+= hisi_lpc.o
obj-y			+= brcmstb_gisb.o
obj-y			+= moxtet.o

这样也是可以的,而且看上去简单不少,但是为什么还是会使用前面那种长串的呢,因为规范。当然我们也可以用后面这种方式,这种方式可以作为你的验证,如果验证成功后还是希望可以改为标准的config来进行控制。

3.3 Makefile 与Kconfig的关系

Makefile与Kconfig可以说是形影不离的好兄弟,有其中一个在就会有宁外一个在。
我们打开刚才目录对应的Kconfig文件:

config ARM_CCI
        bool

config ARM_CCI400_COMMON
        bool
        select ARM_CCI

config ARM_CCI400_PORT_CTRL
        bool
        depends on ARM && OF && CPU_V7
        select ARM_CCI400_COMMON
        help
          Low level power management driver for CCI400 cache coherent
          interconnect for ARM platforms.

config ARM_INTEGRATOR_LM
        bool "ARM Integrator Logic Module bus"
        depends on HAS_IOMEM
        depends on ARCH_INTEGRATOR || COMPILE_TEST
        default ARCH_INTEGRATOR
        help
          Say y here to enable support for the ARM Logic Module bus
          found on the ARM Integrator AP (Application Platform)
....

你会发现如果把刚才Makefile的文件里面的内容与之做比对,那么是不是把有点类似。只不过在Makefile里面config是大写,并与之联系起来,Kconfig里面是小写,但是他们后面的名字都是独一无二的,就好像我们的身份证是确定 驱动.c文件的唯一证明,如果他重复了,那系统就不知道谁是谁了。如果这里还不明白可以自己去看看书或者对应专题说明。

3.4 defconfig与Makefile和Kconfig的说明

之前说了Makefile和Kconfig,那么我添加一个驱动是不是修改了Makefile和Kconfig就可以了,不是的,因为系统为了整体大小,不会把所有的驱动都添加进去,这个时候就需要一个守门员判断那些驱动要,那些驱动不要,这个看门员就是defconfig文件,那么defconfig文件在那里呢;

ernel_imx/arch/arm64/configs/xxxx_defconfig

# vi  xxxx_defconfig
CONFIG_ARM_CCI400_COMMON=y
CONFIG_ARM_CCI400_PORT_CTRL=y
# CONFIG_ARM_CCI400_PORT_CTRL is not set 

如果这里面是 = y 那就是将他编译到对应的系统中去,如果是is not set 那就是不编译。

3.5 Makefile总结

简单总结一下Makefile的东西
比如我们新添加一个一个对应的驱动 名字叫做 test.c
我们如何将他编译到对应的文件里面去呢
1)最简单的
我们在对应的驱动目录的Makefiel里面添加

obj-y = test.o

这样我们就不需要修改其余的地方,直接可以把这个驱动编译。

2)第二种
第一部修改Makefile

obj-(CONFIG_TEST) = test.o

第二步修改Kconfig

config TEST 
 default y /*这里添加这句就需要在defconfig里面去配置*/

3)第三种
第一部修改Makefile

obj-(CONFIG_TEST) = test.o

第二步修改Kconfig

config TEST 
 ...

第三步修改对应的defcofig

CONFIG_TEST=y

这里的说明并不是很详细,可以自己去配合源码理解下。

四、调试第二步

确定编译生效。这里说的编译生效指的是对应的驱动编译生效。
安卓编译完成过后会生成一个对应的out文件夹,我们可以通过这个文件夹去查看是否有对应的驱动文件生成,如果你改了没有身成驱动,拿起不是白改。

/*这个目录下的文件系统其实和内核里面的是一样的,你在那里添加了驱动,就在那个目录下看是否有对应的.o文件产生*/
/out/target/product/evk_8mp/obj/KERNEL_OBJ/drivers$  ls

acpi        bluetooth    cpufreq  edac      hid         iio           mailbox  modules.order  nvme   pinctrl   ras         slimbus    thermal  virtio
amba        built-in.a   cpuidle  extcon    hwmon       input         md       mtd            nvmem  platform  regulator   soc        trusty   watchdog
android     bus          crypto   firewire  hwspinlock  interconnect  media    mux            of     pnp       remoteproc  soundwire  tty      xen
ata         cdrom        dax      firmware  hwtracing   iommu         memory   mxc            opp    power     reset       spi        uio
auxdisplay  char         devfreq  fpga      i2c         irqchip       mfd      net            pci    pps       rpmsg       spmi       usb
base        clk          dma      gpio      i3c         leds          misc     nfc            perf   ptp       rtc         staging    vfio
block       clocksource  dma-buf  gpu       idle        macintosh     mmc      nvdimm         phy    pwm       scsi        tee        video

如果没有你就不用烧录了,先看看为什么没有对应的.o文件再调试吧。
其实这个因该放在前面的,最开始就要确定编译是否有用,但是一般来说如果是官方支持的话其实不用去担心这个,一般来说都是ok的。

五、调试第三步

看log,我们可以通过串口和adb工具去查看开发版的log,从而进一步定位问题。
看log是很重要的事,他是直接反映开发板情况的东西,最开始的我不会看log,吃了很多亏。
在串口下

/*我们可以执行*/
dmesg | grep xxx
/*对某一部分的log进行筛选查看*/

当然也可以全部的看,因为串口是实时打印的信息,我们可以往上翻挨着挨着查看对应的log。

在adb下:
因为安卓他的串口只会打印内核的东西,对于安卓来说,可能还有java和hal层的东西需要查看,所以我们会使用到adb工具。

/*在没有进入到adb里面时候*/
# adb logcat  //可以查看所有的log,但是很多很杂

# adb logcat -b all | grep -E -i "xxx|xxx|xxx" //可以进行关键字搜索来查看后面不能有空格。

六、一些工具的使用

i2c工具是我们常用的工具,因为一些外设我们是通过i2c与cpu进行通信,我们可以使用i2c工具来判断是否外设是不是好的,或者是不是有问题.

   /*xx是 i2c总线几 0就是0 1就是1 */
 #  i2cdetect -y xx
evk_8mp:/ # i2cdetect -y 0                                                                                                                                
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- UU -- -- -- -- --
20: -- -- -- -- -- UU -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --  

/*如果出现uu就证明驱动是ok的,如果没有出现uu但是36 50这种数字,就证明是外设已经扫描到了*/

我们也可以使用

# i2cdump -f -y 0 0x25   
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f    0123456789abcdef
00: 30 c0 e0 c0 04 80 00 4c a1 50 00 00 29 1c 20 1c    0?????.L?P..)? ?
10: d9 1c 14 ca 14 14 49 14 14 09 6c 09 30 09 14 00    ??????I???l?0??.
30: 0 c0 e0 c0 04 80 00 4c a1 50 00 00 29 1c 20 1c    0?????.L?P..)? ?
40: 0 c0 e0 c0 04 80 00 4c a1 50 00 00 29 1c 20 1c    0?????.L?P..)? ?
50: 0 c0 e0 c0 04 80 00 4c a1 50 00 00 29 1c 20 1c    0?????.L?P..)? ?

我们可以通过 i2dump 工具去查看寄存器地址的值。
i2c工具是我们常用的工具,想要了解的可以去看看一。

我们调试外设的时候,可以先用工具扫一扫,如果没有扫到可以看看硬件是不是有问题,或则叫硬件工程师看看对应的电压等等。

这次的文章就写到这里了

  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

永不秃头的程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值