【Linux 设备驱动 - RIPC Driver】

1. RIPC Driver

该驱动主要用来在 AP 侧拷贝 Modem 侧产生的 DSP log,DSP log 主要用于通信相关问题。Modem 通信产生的 log 会放到一个buf中,然后通过配置IPCM触发中断告知AP侧,AP 侧收到中断后会进行 DSP log 的 copy。

1.1 Device Tree

Linux 驱动的开发离不开设备树的配置,在设备树的配置过程中,设备树结构体是一个关键点,如何组织设备树结构需要注意。

当前设备树的组织是按照总线进行的, USB总线,AXI总线,APB总线都是“挂在”SoC下面的,其中sulog和aux_adb都是挂在APB总线下面的,所以节点也是再APB节点下面。

        soc {
                #address-cells = <2>;
                #size-cells = <2>;
                compatible = "simple-bus";
                interrupt-parent = <&gic>;
                ranges;

                usb: usb@0 {
                        compatible  = "demo,usb2";
                        reg = <0 0xc0000000 0 0x4000>;
                        interrupts = <0 77 0x4>;
						...
                        status = "ok";
                };

                axi@d4200000 {  /* AXI */
                        compatible = "demo,axi-bus", "simple-bus";
                        #address-cells = <1>;
                        #size-cells = <1>;
                        reg = <0 0xb6000000 0 0x00200000>;
                        ranges = <0 0 0 0xffffffff>;

                        sdh2: sdh@d4281000 {
                                compatible = "demo,demo-mmc";
                                reg = <0xb8281000 0x120>;
                                interrupts = <0 IRQ_DEMO_MMC2 0x4>;
								...
						}

                        usbphy: usbphy@b4262800 {
                                compatible = "demo,hsphy";
								...
                                status = "disabled";
                        };
                  }; /* AXI */
                  
                apb@c4000000 {  /* APB */
                        compatible = "demo,apb-bus", "simple-bus";
                        #address-cells = <1>;
                        #size-cells = <1>;
                        reg = <0 0xb9000000 0 0x00100000>;
                        ranges = <0 0 0 0xffffffff>;

                        sulog: sulog@b40b0100 {
                                compatible = "demo,demo-sulog";
                                reg = <0xd40b0100 0x1c>;
                                interrupts = <0 IRQ_DEMO_RIPC 0x4>;
								...
                                status = "ok";
                        };
                        aux_adc: aux_adc@d409010c {
                                compatible = "demo,auxadc";
                                #io-channel-cells = <1>;
                                io-channel-ranges;
								...
                                status = "ok";
                        };                  

1.2 设备驱动初始化

在设备树匹配成功后,在 probe 函数中主要完成:

  • 使用devm_kzalloc为私有结构体分配 memory;
  • 对设备进行解析获取设备基地址;
  • 对设备基地址使用 devm_ioremap_resource 进行 map;
  • 获取中断号并注册中断;
  • 获取clk句柄并disable clk;
  • 注册 misc 设备;
  • 创建 sys 文件;
  • 初始化等待队列;
  • 创建工作队列;
  • 初始化工作;
  • 分配乒乓 buffer。

1.2.1 中断 handler

当 Modem 侧触发中断后,AP侧中断处理函数主要完成一下内容:

  • 读取 RIPC log size 的寄存器,获取 modem 发过来的log 大小;
  • 读取 RIPC 寄存器获取 modem log 存放的物理地址;
  • 启动 tasklet 来 copy modem log;
  • clear RIPC 中断寄存器。

1.2.2 tasklet 处理

底层在 tasklet 中 copy 完 modem log 之后有两种处理方式:

  • native 层通过读取文件的方式将 log 写到磁盘中;
  • 通过 USB 将 log 传给 PC 端,直接 PC 端显示。

具体使用哪种方式来处理 Modem log,是根据上层 (Android)的设置, 手机界面配置后抓取 log 的方式后,会触发 native 层配置函数调用,native 层最终调用 driver 底层的配置函数。

1.2.3.1 Modem log copy到文件系统

根据上节内容可知,在 tasklet 通过调用 memcpy 将 modem log 拷贝到乒乓buf之后,会调用 wake_up 函数来唤醒 user space 层的读 log 进程。

user space 的 read 函数在没有log可读得时候,通过调用 wait_event_interruptible函数将进程挂到等待队列中,在驱动层完成 modem log 的 copy 之后,通过 wake_up函数来唤醒等待队列中的 read 进程。

1.2.4 问题思考

在 RIPC 驱动中,当发生中断的时候会在中断处理函数中 调用 tasklet_schedule,然后再在 tasklet 的 callback 中完成 memcy copy,从 Modem 与 AP 之间的共享memory 将 DSP 拷贝到 乒乓 buf 中,然后再在 tasklet 中调用 wakeup 函数来唤醒userspace 的 read 进程,然后再使用 copy_to_user 将数据从 kernel space 拷贝到 user space。 这个过程中存在两个疑问:

  • Q1: 两次内存数据的copy为何不合并成一次?
  • Q2:copy_to(from)_user 和memcpy的底层实现差异在哪里为何不使用memcpy直接进行内存的拷贝,因为内核空间可以直接访问用户空间地址(arm64不行)?

对于上面两个问题的回答:

  • A1: 因为每次 RIPC 中断传过来的数据量可能只是1k左右,驱动会先将这1k存到乒乓 buf 中,当中断再次到来的时候再使用 memcpy 将数据 copy 到 乒乓buf 的时候会判断当前 乒乓buf 的量是否超过 high_watermark,如果超过就会唤醒userspace 的 read 进程,并将乒乓buf 的数据一次拷贝完成,这样的优点是不用每次 RIPC 中断来了都要进行 memcpycopy_to_user 的拷贝及 wakeup userspace read 进程。
  • A2: arm64 memcpy 实现
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

主公讲 ARM

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

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

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

打赏作者

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

抵扣说明:

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

余额充值