创建字符设备

1 创建设备号 alloc_chrdev_region 2.创建cdev cdev_init 3.添加一个 cdev,完成字符设备注册到内核 cdev_add 4.创建类 class_create 5.创建设备 device_create

1.内核空间与用户空间数据

copy_from_user 和copy_to_user 俩个函数来完成。 copy_from_user 函数是将用户空间的数据拷贝到内核空间。 copy_to_user 函数是将内核空间的数据拷贝到用户空间

2.container_of 函数

通过结构体变量中某个成员的首地址进而获得整个结构体变量的首地址 container_of(ptr,type,member) 参数含义: ptr 是结构体变量中某个成员的地址。 type 是结构体的类型 member 是该结构体变量的具体名字

3.并发与竞争

3.1什么是竞争?

多个程序同时访问一个共享资源产生的问题就叫做竞争。竞争产生的根本原因就是 Linux 系统的并发访问

竞争产生的原因如下所示: (1)多线程的并发访问 (2)中断程序的并发访问 (3)抢占式并发访问

3.2什么是并发?

所谓并发,就是通过算法将 CPU 资源合理地分配给多个任务,当一个任务执行 I/O 操作 时,CPU 可以转而执行其它的任务,等到 I/O 操作完成以后,或者新的任务遇到 I/O 操作时,CPU 再回到原来的任务继续执行

3.3解决竞争问题

原子操作、自旋锁、信号量、互斥体

原子操作只能对整形变量或者位进行保护,而对于结构体或者其他类型的共享资源,原子操作就力不从心了

自旋锁是为了保护共享资源提出的一种锁机制。自旋锁(spin lock)是一种非阻塞锁,也就是说,如果某线程需要获取锁,但该锁已经被其他线程占用时,该线程不会被挂起,而是在不断的消耗 CPU 的时间,不停的试图获取锁

自旋锁死锁发生存在两种情况: 进程 A 拥有自旋锁,中断到来,CPU 执行中断函数,中断处理函数,中断处理函数需要获得自旋锁,访问共享资源,此时无法获得锁,只能自旋,从而产生死锁

对于中断引发的死锁,最好的解决方法就是在获取锁之前关闭本地中断,Linux 内核在 “/include/linux/spinlock.h”文件中提供了相应的 API 函数

信号量也是解决竞争的一种常用方法,与自旋锁不同的是,信号量会使等待的线程进入休眠状态,适用于那些占用资源比较久的场合

互斥锁会导致休眠,所以在中断里面不能用互斥锁。同一时刻只能有一个线程持有互斥锁,并且只有持有者才可以解锁,并且不允许递归上锁和解锁

4.Linux 定期器

硬件为内核提供了一个系统定时器来计算流逝的时间(即基于未来时间点的计时方式,以当前时刻为计时开始的起点,以未来的某一时刻为计时的终点),内核只有在系统定时器的帮助下才能计算和管理时间,但是内核定时器的精度并不高,所以不能作为高精度定时器使用。并且内核定时器的运行没有周期性,到达计时终点后会自动关闭。如果要实现周期性定时,就要在定时处理函数中重新开启定时器

补充:高级定时器

5.Linux 内核打印

dmseg 命令 查看 kmsg 文件,cat /proc/kmsg 调整内核打印等级cat /proc/sys/kernel/printk

6.驱动调试方法

printk 函数打印 dump_stack 函数 WARN_ON(condition)函数

7.中断

中断是指在 CPU 正常运行期间,由外部或内部事件引起的一种机制。当中断发生时,CPU会停止当前正在执行的程序,并转而执行触发该中断的中断处理程序。处理完中断处理程序后,CPU 会返回到中断发生的地方,继续执行被中断的程序。中断机制允许 CPU 在实时响应外部或内部事件的同时,保持对其他任务的处理能力

7.1中断的上下半部

中断上文是中断服务程序的第一部分 它主要处理一些紧急且需要快速响应的任务,包括保存寄存器状态、更新计数器等,以便在中断处理完成后能够正确地返回到中断前的执行位置

中断下文是中断服务程序的第二部分 中断下文负责处理那些不能立即完成的、需要更多时间的任务。这些任务可 能包括复杂的计算、访问外部设备或进行长时间的数据处理等

中断下文的一种实现方式——tasklet Tasklet 绑定的函数在同一时间只能在一个 CPU 上运行,因此不会出现并发冲突,tasklet 绑定的函数中不能调用可能导致休眠的函数,否则可能引起内核异常

一般步骤: 首先定义了 my_tasklet_handler 作为 tasklet 的处理函数。 然后,声明了一个名为 my_tasklet 的 tasklet 结构体, 并使用 tasklet_init 函数对其进行初始化。调用 tasklet_schedule 函数,我们调度(触发)了 my_tasklet 的执行。调度 tasklet 只是将 tasklet 标记为需要执行,并不会立即执行 tasklet 的处理函数。实际的执行时间取决于内核的调度和处理机制

7.2中断子系统框架

由上到下分别为用户层、通用层、硬件相关层和硬件层

7.3中断控制器 GIC

中断控制器 GIC(Generic Interrupt Controller)是中断子系统框架硬件层中的一个关键组件,用于管理和控制中断,接收来自各种中断源的中断请求,并根据预先配置的中断优先级、屏蔽和路由规则,将中断请求分发给适当的处理器核心或中断服务例程

7.4中断线程化

将中断处理程序从主线程中独立出来,创建一个专门的线程来处理这些中断事件。 这样,主线程就不再受到中断的干扰,可以专注于自己的工作,不再频繁地被打断。 中断线程化的核心思想是将中断处理和主线程的工作分开,让它们可以并行执行。中断线程负责处理中断事件,而主线程负责执行主要的工作任务。这样一来,不仅可以减少切换的开销,还可以提高整个程序的响应速度和性能

中断线程化的处理仍然可以看作是将原来的中断上半部分和中断下半部分。上半部分还是用来处理紧急的事情,下半部分也是出路比较耗时的操作,但是下半部分会交给一个专门的内核线程来处理。这个内核线程只用于这个中断。当发生中断的时候,会唤醒这个内核线程,然后由这个内核线程来执行中断下半部分的函数

request_threaded_irq是 Linux 内核中用于请求并注册一个线程化的中断处理函数的函数

int request_threaded_irq(unsigned int irq, irq_handler_t handler,

irq_handler_t thread_fn, unsigned long irqflags, const char *devname, void *dev_id);

handler:是在发生中断时首先要执行的处理程序,非常类似于顶半部,该函数最后会返回 IRQ_WAKE_THREAD 来唤醒中断,一般 handler 设为 NULL,用系统提供的默认处理

thread_fn:线程化的中断处理函数,非常类似于底半部。如果此处设置为 NULL 则表示没有使用中断线程化

8平台总线模型

平台总线(Platform bus)是 Linux 内核中提供的一种虚拟总线,用于管理和组织与特定硬件平台相关的设备和驱动。它充当了平台设备(platform device)和平台驱(platform driver)之间的桥梁,负责将它们进行匹配和绑定。 平台总线优势如下所示: (1)设备与驱动的分离 设备代码放在device.c 文件中(或者设备树),驱动代码放在 driver.c 文件中 (2)提高代码的重用性:平台总线模型使得相同类型的设备可以共享相同的驱动代码 (3)减少重复性代码:在传统的设备驱动模型中,如果有多个相同类型的设备存在,就 需要为每个设备编写独立的驱动代码

9.虚拟文件系统 ConfigFS

9.1常用的虚拟文件系统

虚拟文件系统提供了一个内核抽象层,使得应用程序可以通过统一的文件访问接口操作不同类型的文件和设备 procfs 是一个虚拟文件系统,提供了对系统内核运行时状态的访问接口。它以文件和目录的形式表示内核中的进程,设备,驱动程序和其他系统信息。通过读取和写入 procfs 中的文件,可以获取和修改有关系统状态的信息 sysfs 是一个虚拟文件系统,用于表示系统中的设备,驱动程序和其他内核对象 configfs 是一个虚拟文件系统,用于动态配置和管理内核对象,允许用户在运行时添加,修改和删除内核对象,而无需重新编译内核或重新启动系统

10.设备模型基本框架-kobject 和 kset

11.pinctrl子系统

12.GPIO 子系统

13.输入子系统

在 Linux 中,input 子系统是专门为处理输入类设备而设计的一个子系统或框架 Input 子系统可以分为事件处理层、设备驱动层、核心层三层

固定输入设备的设备节点: 不同厂家和型号的外设在内核启动时加载的顺序可能会不同。例如,触摸板和 USB 转串口等设备,这会导致在/dev/input 目录下创建的 evdevx 节点(其中 x=0,1,2,3…)不同

解决思路如下:通过分析 evdev.c 驱动程序,我们确定设备节点是在 evdev_connect 函数中创建的。因此,只需要在 evdev_connect 函数中针对需要固定设备节点的设备单独创建一个设备节点即可

14.调节LCD

查看外设Datasheet,了解电子特性、启动时序、屏幕参数查看原理图,确定相关IO引脚修改DTS编译&运行&调试

屏幕参数: 常见问题

在点亮屏后刚开始有开机 logo 闪烁,向右偏移了近半个屏幕的长度,等问题。 重新确认 clock-frequence 后发现少打了一个 0 ,修改后解决了闪烁大偏移的问题。画面偏移 稍微降低 hs_clk ,由 504 降低到 496 解决。垂直方向会显示多一点内容。 调整 VFP 后解决,将 VFP 增大为 15 。有黑边 稍微增大 VBP 后解决,将 VBP 增大为 15。开机 android 最左边会被裁剪一部分 增大 HBP 后解决,将 HBP 由 10 增加到 30。显示偏移、图像位置偏差 timing 中的参数设置有误。优先确认。 看着图像调节前扫、回扫进行左右上下移动白屏 随机出现白屏有可能是静电问题,把LCD拿到头发上擦几下,如果很容易出现白屏那肯定就是静电问题了。另外一个在有Backend IC的情况下,也有可能bypass没处理好。 結束開機logo至android動畫出現之間出現閃屏或者閃白光的情況。原因:在這個時間點kernel會會對屏再次初始化,我們可以軟件上屏蔽第一次初始化動作從而解决。屏在进出睡眠或者显示过程中白屏 喚醒屏幕閃白光問題,說白了是背光早亮了,很有可能是下序列mdelay太久,改小點就沒有這個問題了。根本原因屏幕初始化序列下慢了。 sleep out(0x11)和 display on(0x29)之间需要 mdelay(120ms)左右。花屏 说明 lcd 初始化成功,但是没有 rgb 刷过来。 timing 中的参数设置有误。优先确认 pclk。 花屏 还可能是总线速度有问题。 开机就花屏最简单的解决方式是,在 Init 结束的地方加一个刷黑屏的功能。也可以在睡眠函数里加延时函数。屏幕闪烁 pclk 有问题 在最开始的时候,我的 pclk 漏了一个 0 ,为之前的 1/10 此时就有图像闪烁问题。 proch 有问题 在调试完后,我尝试将 proch 增加到极限,发现会出现图像闪烁的问题。屏幕抖动 测时序,延时不足屏幕闪动 通过调节电压来稳定,一般调节的电压为VRL、VRH、VDV和VCM唤醒闪屏问题 这是由於每次重新RST下序列過程delay久了導致,适當減少delay時間屏幕唤醒显示灰色底面 寄存器没有使能外部升压电路水波纹 通常都是rgb interface polarity導致,需要調整pclk hsync vsync de極性使之符合平台極性调节对比度 VRL、VRH、VDV和VCM,这些电压也可以用来调节亮暗(对比度) 也可以通过调节Gamma值来实现,要调节的对象为 PRP、PRN、VRP、VRN 等确认有没有 framebuffer 输出 要是改动了display这块的clk很有可能没有buffer输出的,可以通过cat /dev/graphyics/fb0 查看有没有输出字符 如果有说明是 mipi 还没有调通,如果没有说明是 fb 有问题

15.调节TP

TP驱动主要是通过IIC和IC通,通过输入子系统上报触摸事件

tp调试时的问题排查 1.当我们调试出现问题时,首先检查电压; 2.在porbe 函数里面加一句打印,确认驱动已经正常注册进系统了; 3.确认中断是否注册上了,可以adb shell cat /proc/interrupters, getevent工具可以监听中断事件,如果没有就查下打印,一般申请中断失败,都是IO 口被其他驱动或虚拟设备占用; 4.如果注册上了,但是触摸没中断.就用示波器勾下中断脚是否有波形,如果没有就代表触摸屏本身有问题,换一个试一下; 5.如果有波形,但是adb shell cat /proc/interrupters,中断号对应的中断数没有增加,一般都是中断脚被复用为其他作用,比如PWM; 6.一般这时候上层都会有点出来,大致分为三种情况:

出现鼠标,或者只有一个圆圈出来,这个是属于input 创建时候的有问题,可以参考正常的驱动;出现上下或左右反了,这个是要改驱动里面的上报函数;如果上下左右正常了,但是会出现无法画到四个边的情况,这个是属于TP 里面的软件问题,需要联系TP 厂的FAE,进行修改;

16.调节WIFI

STA 模式 和 AP 模式 Sta模式: Station, 类似于无线终端,sta本身并不接受无线的接入,它可以连接到AP,一般无线网卡即工作在该模式。 AP模式: Access Point,提供无线接入服务,允许其它无线设备接入,提供数据访问,一般的无线路由/网桥工作在该模式下。AP和AP之间允许相互连接

在使用的过程主要是wpa_suplicant和hostapd两个命令去设置为STA和AP模式 如果需要在设置为AP模式下还需要该热点能上网的话,就还需要桥接网口或者4G让该热点能上网

设置为STA模式:

ifconfig wlan0 up

wpa_supplicant -i wlan0 -Dnl80211 -c /etc/wpa_supplicant.conf -t &

udhcpc -iwlan0 -q

设置为AP模式:

ifconfig wlan0 up

hostapd hostapd.conf &

ip link set dev ap-br0 up

ip link set eth0 master ap-br0

SDIO 接口的 WiFi,首先,它是一个 SDIO卡 设备,然后具备了 WiFi 的功能,所以 SDIO 接口的 WiFi 驱动就是在 WiFi 驱动外面套上了一个 SDIO 驱动 的外壳

mmc 子系统 设备驱动层(WiFi 设备): | 核心层(向上向下提供接口) 注册mmc总线,SDIO总线 | 主机驱动层(实现 SDIO 驱动)主要是针对不同的芯片使用的host的控制器,在这里面当和设备树匹配之后,会创建一个host,把这个host添加到mmc总线上,会检测卡是否插入(中断检测某个GPIO,轮询检测),检测到卡之后,会注册SDIO驱动到添加到host

WiFi驱动移植流程

BSP已支持WiFiBSP未支持WiFi WiFi驱动放到kernel里面 将供应商提供rtl8821CU WiFi驱动复制到kernel/drivers/net/wireless/rockchip_wlan/这个目录下面,同时修改当前目录下的Kconfig文件和Makefile文件

常见排查问题:

检查DTS 关注时钟、IO、WiFi_chip_type

&sdio0 {

clock-frequency = <100000000>; /*时钟修改的地方 如果SDIO读写识别,可调整时钟*/

clock-freq-min-max = <200000 100000000>; /*时钟修改的地方*/

supports-sdio; /*SDIO功能*/

bus-width = <4>; /*4线模式,调试可改为1线测试*/

disable-wp;

cap-sd-highspeed; /*highspeed的SDIO外设*/

cap-sdio-irq; /*SDIO中断*/

keep-power-in-suspend; /*睡眠不断电*/

mmc-pwrseq = <&sdio_pwrseq>; /*电源控制*/

non-removable;

num-slots = <1>;

pinctrl-names = "default";

pinctrl-0 = <&sdio0_bus4 &sdio0_cmd &sdio0_clk>;

sd-uhs-sdr104; /* SDIO3.0 模式*/

status = "okay";

};

sdio_pwrseq: sdio-pwrseq {

compatible = "mmc-pwrseq-simple";

clocks = <&rk808 1>; /*提供32.768 LPO*/

clock-names = "ext_clock"; /*外部时钟源*/

pinctrl-names = "default";

pinctrl-0 = <&WiFi_enable_h>;

/*

* On the module itself this is one of these (depending

* on the actual card populated):

* - SDIO_RESET_L_WL_REG_ON

* - PDN (power down when low)

*/

//控制WiFi电源的GPIO

reset-gpios = <&gpio0 10 GPIO_ACTIVE_LOW>; /* GPIO0_B2 */

};

wireless-wlan {

compatible = "wlan-platdata";

rockchip,grf = <&grf>;

WiFi_chip_type = "ap6255";

sdio_vref = <1800>;

//控制WiFi中断的GPIO

WiFi,host_wake_irq = <&gpio0 3 GPIO_ACTIVE_HIGH>; /* GPIO0_a3 */

status = "okay";

};

wireless-bluetooth {

compatible = "bluetooth-platdata";

clocks = <&rk808 1>;

clock-names = "ext_clock";

//WiFi-bt-power-toggle;

uart_rts_gpios = <&gpio2 19 GPIO_ACTIVE_LOW>; /* GPIO2_C3 */

pinctrl-names = "default", "rts_gpio";

pinctrl-0 = <&uart0_rts>;

pinctrl-1 = <&uart0_gpios>;

//BT,power_gpio = <&gpio3 19 GPIO_ACTIVE_HIGH>; /* GPIOx_xx */

BT,reset_gpio = <&gpio0 9 GPIO_ACTIVE_HIGH>; /* GPIO0_B1 */

BT,wake_gpio = <&gpio2 26 GPIO_ACTIVE_HIGH>; /* GPIO2_D2 */

BT,wake_host_irq = <&gpio0 4 GPIO_ACTIVE_HIGH>; /* GPIO0_A4 */

status = "okay";

};

排查DTS对应的GPIO设置 确定复用引脚已配置、方向配置、测试高低电平

使用io命令shell io -4 -w 0xFF77e008 0x0fff0000 sysfs使用echo命令shell

echo xxx > /sys/class/gpio/export

echo out > /sys/class/gpio/gpioxxx/direction

echo 0 > /sys/class/gpio/gpioxxx/value

echo 1 > /sys/class/gpio/gpioxxx/value

使用debugfs查看

检查电压电平 电源电压,VDDIO、VBAT的电压,WiFi_REG_ON上电及复位控制信号 电源控制:

echo 0 > /sys/class/rkWiFi/power //对WiFi模块掉电

echo 1 > /sys/class/rkWiFi/power //对WiFi模块上电

echo 1 > /sys/class/rkWiFi/driver //命令会调用模块的驱动的初始化操作,初始化成功后

看到wlan0 节点;

如果执行上面命令对模块进行上下电,而实际测量对应管脚不受控,可以通过io 命令读取对应的寄存器,确认是否写入,如果正确写入但是实际测量不受控请检查硬件部分; 对于sdio接口的模块,执行” echo 1 > /sys/class/rkWiFi/driver”命令 ,正常情况下 sdio_clk 和sdio_cmd 能够测量到相关波形,内核打印上能够看到如下打印,如果没有测量到波形也没有看到如下打印,根据配置文档检查是否正确配置sdio;

WL_REG_ON 主要用于上电,休眠的时候,请保持该GPIO上电,否则会丢失WiFi内部的状态,导致WiFi唤醒失败; WL_HOST_WAKE 主要用于WiFi设备有数据的时候,唤醒CPU,进入中断,关于中断流程上面已经说过。

检查时钟 32.768K的LPO, 37.4M晶振主时钟,SDIO_CLK、SDIO_CMD 的波形 同时测量执行echo 1 > /sys/class/rkWiFi/driver 时 外部晶体是否有起振,如果扫描时没有起振检查下硬件;同时建议测量外部晶体频偏,频偏比较大情况下,会出现能扫描到模块但是初始化失败;除检查晶振外,正基系列还需要外部32k,测量32k的峰峰值(峰峰值>=0.7VDDIO && 峰峰值 <= 1VDDIO);【注:频偏和峰峰值一定要测量检查,频偏过大峰峰值不对会影响WiFi(扫描连接热点)和蓝牙(扫描连接设备))】 尝试单线模式 如果降低clk依然不行,考虑使用sdio单线模式方法如下 &sdio { … bus-width = <1>;

使用 sdio 单线模式。如果单线模式可以而使用4线模式不行,检查硬件sdio_data0~sdio_data3 四根线的线序是否弄错; 如果降低clk,使用单线模式均不可以检查下是否是使用最新的sdk代码和最新的WiFi驱动(ftp服务器上有相关patch); 上述检查均无结果,check 图纸 是否周围器件有贴错器件;

检查模块能否处于工作状态 netcfg wlan0 up 或busybox ifconfig wlan0 up //执行完成后检查 wlan0 是否处于up状态;如果没有处于up状态;做如下检查确认 1 确认相关固件是否存在(正基系列,通过看内核日志可以看到),固件不存在考虑到ftp下载固件;此时如果还报其他错误从两个方面排查1 上电时序,2检查sdio部分走线; 2 尝试使用原始最新的sdk代码做测试;(有客户出现过,上层做了相关修改导致WiFi初始化成功,但是执行netcfg wlan0 up 报告无法识别 ioctl 命令等奇怪错误,原生sdk生成的sysytem.img 没有问题) 执行iwlist wlan0 scanning ,测试扫描热点是否正常(3368平台下执行iwlist 命令有问题,忽略此步骤) 确认Android层是否能够打开 述检查各个步骤可以工作,而通过上层settings界面打开失败;以下几个方面排查 1 dts中的WiFi_type配置是否正确;cat /sys/class/rkWiFi/chip 确认 下 打印的结果和你的模块是否匹配 2 确认 wpa_supplicant 相关服务是否生成,libhardware_leacy 启动的wpa服务是否正确; 3 抓取logcat 日志上传readmine

其他问题 无法连接热点 1.无法连接热点,正基系列模块检查确认晶振频偏和32k峰峰值; rtl模块考虑驱动配置是否正确,是否匹配; 2.检查确认p2p wlan0 的mac地址是否一致如果检查是否有调用rockchip_WiFi_mac_addr读取mac地址,如果有考虑直接在该函数中return -1; 3.检查确认是否有做RF指标测试以及天线匹配测试 4.上述检查没有问题,做如下测试 (首先用给手机连接所测试的热点做确认) 1 连接无加密热点 2 连接加密热点 测试能否连接成功,并记录对应的logcat 日志与内核日志(开机到打开WiFi以及连接热点的整个过程) softap 无法打开(正基系列的) 1.查看打开热点时的内核日志,确认下 下载固件是否正确 ,正基系列的模块 softap 下载的固件一般是带ap后缀结尾的; 2.固件下载没有问题 ,考虑使用原始的sdk代码做测试 P2P 问题 P2p 无法打开:确认是否有p2p节点,有p2p节点的检查确认mac地址是否与wlan0 一样,如果一样按照热点问题中的step 2 处理; 第一次开机能够打开,重启后无法打开:考虑检查上电时序,目前遇到都是rtl的模块出现过,问题在于chipen 脚不受控,建议做成受控,在重启时对chipen脚下电;可以通过如下方法实现net/rfkill-wlan.c中的rfkill_wlan_driver 中增加shutdown函数 在该函数中对chip_en 下电; 休眠唤醒出现WiFi无法打开: 1 对比检查休眠唤醒前后 sdio 的iomux 是否发生变更 2 对比 休眠前后以及休眠中 WiFi的外围供电是否发生变更吞吐率问题 8. pcb检查,一定要让模块原厂检查确认 pcb是否存在问题 9. RF指标确认是否ok 10. 天线是否做过匹配 11. Sdio 接口的可以考虑 提到sdio的clk 启用sdio3.0【前提 平台支持 sdio3.0 ,模块支持sdio3.0】 12.常用指令

打开和关闭WiFi 打开WiFi指令 svc WiFi enable 关闭WiFi指令 svc WiFi disable 查看wpa_supplicant是不是已经起来了 ps | grep WiFi 打开和关闭蓝牙BT: 关闭:adb shell settings put global bluetooth_on 0 打开:adb shell settings put global bluetooth_on 1 查询:adb shell settings get global bluetooth_on 查询蓝牙开启状态: adb shell settings get global bluetooth_on =》0: 未开启 1: 已开启 获取mac地址:

17.调节蓝牙

蓝牙一般是使用串口接口 常见问题是无法打开蓝牙,可以手动拉bluez 比如使用hci命令去手动up一个hci节点,加载蓝牙固件,如果能正常扫描到蓝牙,基本可以确认硬件没什么问题;其次就是要注意时钟问题,也可能导致打不开蓝牙

18.调节4G

4G按照供应商提供的文档操作,像更新最新的代码,添加VID,PID到USB里面,设置PPPD拨号的VPN,在Linux系统的话基本可以搞定,不需要加载ridl库,这个是在Android中需要添加的,也是比较麻烦的。

19.调节网口

网口用的是MDIO,和RMII接口 内部 MAC 通过 MII/RMII 接口来与外部的 PHY 芯片连接,完成网络数据传输 MDIO 和MDC 这两根线访问 PHY 芯片的任意一个寄存器

一般内核中可以使用通用的网口驱动就可以使能以太网,不过芯片商一般都会做一些针对自己家的主控的修改

一般来说主要是针对网口的灯做一些定制,这部分就要通过查看PHY芯片的手册,修改对应的寄存器的值,phy_register_fixup_for_uid 函数,这是一个linux 中的通用函数,用于修正设置指定uid的PHY,比如:

topeet@ubuntu:~/RK_android11/rk_android11.0_sdk_211130/kernel$ git diff drivers/net/ethernet/stmicro/stmmac/stmmac_main.c

diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c

index cff46c49b72f..b08899e69dea 100644

--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c

+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c

@@ -58,6 +58,13 @@

#define STMMAC_ALIGN(x) ALIGN(ALIGN(x, SMP_CACHE_BYTES), 16)

#define TSO_MAX_BUFF_SIZE (SZ_16K - 1)

+

+#define RTL_8211F_PHY_ID 0x001cc916

+#define RTL_8211F_PHY_ID_MASK 0x001fffff

+#define RTL_8211F_PAGE_SELECT 0x1f

+#define RTL_8211F_LCR_ADDR 0x10

+#define RTL_8211F_EEELCR_ADDR 0x11

+

/* Module parameters */

#define TX_TIMEO 5000

static int watchdog = TX_TIMEO;

@@ -4260,6 +4267,30 @@ static int stmmac_hw_init(struct stmmac_priv *priv)

return 0;

}

+static int phy_rtl8211f_led_fixup(struct phy_device *phydev)

+{

+ u32 val, val2;

+ pr_info("terry in : %s\n", __func__);

+ /*switch to page0xd04*/

+ phy_write(phydev, RTL_8211F_PAGE_SELECT, 0xd04);

+ /*set led1(green) Link 10/100/1000M, and set led2(yellow) Link

+ 10/100/1000M+Active*/

+ val = phy_read(phydev, RTL_8211F_LCR_ADDR);

+ val |= (1<<5);

+ val |= (1<<8);

+ val &= (~(1<<9));

+ val |= (1<<10);

+ val |= (1<<11);

+ phy_write(phydev, RTL_8211F_LCR_ADDR, val);

+ /*set led1(green) EEE LED function disabled so it can keep on when linked*/

+ val2 = phy_read(phydev, RTL_8211F_EEELCR_ADDR);

+ val2 &= (~(1<<2));

+ phy_write(phydev, RTL_8211F_EEELCR_ADDR, val2);

+ /*switch back to page0*/

+ phy_write(phydev,RTL_8211F_PAGE_SELECT, 0xa42);

+ return 0;

+}

+

/**

* stmmac_dvr_probe

* @device: device pointer

@@ -4452,6 +4483,11 @@ int stmmac_dvr_probe(struct device *device,

netdev_warn(priv->dev, "%s: failed debugFS registration\n",

__func__);

#endif

+ /* register the PHY board fixup */

+ ret = phy_register_fixup_for_uid(RTL_8211F_PHY_ID, RTL_8211F_PHY_ID_MASK, phy_rtl8211f_led_fixup);

+ if (ret) {

+ dev_warn(priv->device, "Cannot register PHY board fixup, terry in :%s.\n", __func__);

+ }

return ret;

另外还可以使用ethtool工具去修改

20.调节红外遥控

21.emmc

22.nfc

23.串口

异步全双工,低–>高 通用异步收发器即通用的串行、异步通信总线,该总线有两条数据线,实现全双工的发送和接收 说明:一次只能发送一个字节,检验位可以有可以无,每次发送都是这样的格式

串口通信存在的问题: 1)串口只是规定了协议,即帧格式,用高电平表示1,低电平表示0,但是在不同的处理器中这个定义是不一样的机电气特性不一样,所以这两个处理器是不能直接连 2)抗干扰能力差,使用的是 TTL 信号表示0和1, 3)通信距离短,只能用于一个电路板上的两个不同芯片之间的通信

由于串口通信存在的问题就出现了RS232、RS485协议,但是软件编程还是串口那样编程,因为改的只是硬件上的电性

24.IIC

串行同步半双工 高---->低 起始:在SCL为高电平时,SDA由高变低 停止:在SCL为高电平时,SDA由低变高

时序

25.SPI

全双工、同步串行,高—>低

26.PWM

27.mipi

MIPI(Mobile Industry Processor Interface) MIPI联盟下面有不同的WorkGroup,分别定义了一系列的手机内部接口标准,比如: 摄像头接口CSI(DisplaySerialInterface)、 显示接口DSI(DisplayCommandSet)、 射频接口DigRF、 麦克风/喇叭接口SLIMbus等 可以看出无论是CSI还是DSI还是其他都是mipi接口

MIPI CSI2一般会有1对I2C通信引脚,1对MIPI差分时钟引脚和1~4对MIPI差分数据信号引脚,如图所示:

28.secure boot

29.JNI AIDL

30.ALSA

31.rtc

RTC中文叫实时时钟,是Linux内核比较简单的字符驱动,Linux系统中一切于时间有关的功能都依赖于RTC

hwclock -f /dev/rtc0 -w

代码流程: 主要是通过IIC通信,然后初始化RTC芯片,设置函数操作集rtc_class_ops,该函数里的函数指针会对应到接口文件的函数,这样上层就可以去读取、设置rtc时间

32. iio

33.看门狗

看门狗有内部看门狗、外部看门狗 作用:防止代码跑偏,死机,能狗重启. 原理: 外部看门狗,一上电就会计时,如果在规定的时间内,主控主芯片没有去改变外部看门狗芯片的电平(该步骤叫喂狗),就会产生一个复位电平到主控芯片,从而复位。 内部看门狗,也叫软件看门狗,通过软件定时器模拟的喂狗操作,可以通过提供的接口文件去改变设置喂狗的时间。

通常: 喂狗需要在u-boot和kernel两个地方喂狗

推荐阅读

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。