当前位置:首页>Linux>手把手教你如何从Linux内核抄代码!

手把手教你如何从Linux内核抄代码!

  • 2026-08-26 02:48:21
手把手教你如何从Linux内核抄代码!

击左上方蓝色“一口Linux”,选择“设为星标”

第一时间看干货文章
☞【干货】嵌入式驱动工程师学习路线
☞【干货】Linux嵌入式知识点-思维导图-免费获取
☞【就业】一个可以写到简历的基于Linux物联网综合项目
☞【就业】简历模版

一、前言

之前写过一篇文章,

《需要多久才能看完linux内核源码?》

数以千亿级的电子设备都在运行linux,最核心的Linux内核代码总计有xxxx行,它汇集了全球几百万顶尖程序员的代码,博大精深:

  • 一套代码支持不同指令集不同厂家不同系列的SoC
  • 可以驱动数以万计的外设芯片
  • 支持各种文件系统
  • 可以稳定的调度并发任务
  • 可以支持mipi、usb、uart、iic、spi、sdio、pcie、mdio等不同类型不同速率的总线通信

可以说linux内核代码是人类智慧的结晶,要想写出高效、稳定的c代码,大家可以从linux内核中寻找灵感!

彭老师,平时在做项目中,很多小模块都是从参考的linux内核代码。

本文通过一个小例子,来教大家如何借鉴(抄袭)内核代码。

二、我的需求

用C语言实现语音压缩算法用于话音通信,要支持不同的压缩比,比如8k、2.4k等,每一种压缩比都会有一组配套的算法参数,

用C语言实现,最常见的就是通过结构体数组来管理这些参数,

那么内核大佬是如何用数组来实现的呢?

三、参考内核代码

JZ4740、JZ4760、JZ4775、JZ4780是北京君正推出的嵌入式微处理器系列,主要用于智能硬件、物联网等领域。

该系列处理器的uart控制器的ip核比较雷同,所以驱动大部分功能是相通的,

但是也有一些差别比如uart的fifo大小,有16、32、64的区别,

那么如何让同一个驱动同时支持不同的uart控制器 ?

该驱动是基于platform总线的,

所以可以根据设备树的compatible的属性信息,来区分当前的uart控制器版本。

参考驱动文件路径如下:

8250_ingenic.c drivers\tty\serial\8250

驱动信息:

staticconststructingenic_uart_configjz4740_uart_config = {
 .tx_loadsz = 8,
 .fifosize = 16,
};

staticconststructingenic_uart_configjz4760_uart_config = {
 .tx_loadsz = 16,
 .fifosize = 32,
};

staticconststructingenic_uart_configjz4780_uart_config = {
 .tx_loadsz = 32,
 .fifosize = 64,
};

staticconststructof_device_idof_match[] = {
 { .compatible = "ingenic,jz4740-uart", .data = &jz4740_uart_config },
 { .compatible = "ingenic,jz4760-uart", .data = &jz4760_uart_config },
 { .compatible = "ingenic,jz4775-uart", .data = &jz4760_uart_config },
 { .compatible = "ingenic,jz4780-uart", .data = &jz4780_uart_config },
 { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, of_match);

staticintingenic_uart_probe(struct platform_device *pdev)
{
structuart_8250_portuart = {};
conststructingenic_uart_config *cdata;
conststructof_device_id *match;

 match = of_match_device(of_match, &pdev->dev);
if (!match) {
  dev_err(&pdev->dev, "Error: No device match found\n");
return -ENODEV;
 }
 cdata = match->data;
 ……
}

内核结构体数组的初始化赋值,都是通过这种 .  加上成员名的方法,

为了保护数组内容不被其他代码修改,数组定义为const类型

强烈建议大家使用这种方法来写c代码

用来表示fifo细节的结构体:

structingenic_uart_config {
int tx_loadsz;
int fifosize;
};

设备树信息:

    uart0: serial@10030000 {
            compatible = "ingenic,jz4780-uart";
            reg = <0x100300000x100>;

            interrupt-parent = <&intc>;
            interrupts = <51>;

            clocks = <&ext>, <&cgu JZ4780_CLK_UART0>;
            clock-names = "baud", "module";

            status = "disabled";
    };

注意其中的compatible属性信息,ingenic,jz4780-uart

驱动大致原理:

  1. 设备树节点 uart0: serial@10030000 的compatible属性信息最终被内核存放到pdev->dev->of_node->properties
  2. 数组**of_match[]**,
structof_device_id {
char name[32];        
char type[32];
char compatible[128];// 存放外设名字
constvoid *data;       //存放私有信息
};
  1. 函数**of_match_device()**是内核提供的设备树匹配函数,

    该函数会遍历数组of_match[],根据比较数组的每一个元素的compatible和pdev->dev->of_node->properties,如何相同,则返回struct of_device_id类型指针,该指针指向数组的某一个元素

  2. 用于保存uart控制器的私有信息就保存在数组of_match->data中,函数**of_match_device()**会根据设备传入的名字,帮助我们找到该元素

  3. 通过变量cdata;保存私有信息,对于ingenic,jz4780-uart来说,就是变量jz4780_uart_config,

    后面就可以通过该变量

这种方法是内核最通用的处理方法,

有的外设驱动还会先通过控制总线先从外设读取一个预存在某个内部寄存器的id值,然后再根据这个id值来区分外设硬件版本。

四、我的例子

根据该思想,我们编写自己的代码:

#include<stdio.h>
#include<string.h>


#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))

structmy_private_s{
int comp_type;   
int cfg1;
int cfg2;   
int cfg3;
char compatible[32]; 
};

#define COMPRESS_TYPE1 0
#define COMPRESS_TYPE2 1
#define COMPRESS_TYPE3 3
#define COMPRESS_TYPE4 4

//比较下面两种定义区别
//struct my_private_s of_match[] = {
staticconststructmy_private_sof_match[] = {
 {
  .comp_type = COMPRESS_TYPE1,
  .cfg1 = 11,
  .cfg2 = 111,
  .cfg3 = 1111,
  .compatible = "com_type1",
 },
 {
  .comp_type = COMPRESS_TYPE2,
  .cfg1 = 22,
  .cfg2 = 222,
  .cfg3 = 2222,
  .compatible = "com_type2"
 },
 {
  .comp_type = COMPRESS_TYPE3,
  .cfg1 = 33,
  .cfg2 = 333,
  .cfg3 = 3333,
  .compatible = "com_type3"
 },
 {
  .comp_type = COMPRESS_TYPE4,
  .cfg1 = 44,
  .cfg2 = 444,
  .cfg3 = 444,
  .compatible = "com_type4"
 },

 {},
};

struct my_private_s const* of_match_device(int comp_type)
{
int i = 0;

for(i=0;i<ARRAY_SIZE(of_match);i++)
 {
if(of_match[i].comp_type == comp_type)
  {
return &of_match[i];
  }
 }
returnNULL;
}


voiddump_compress_info(const struct my_private_s *comp_type)
{
printf("\n\tcomp type:%d\n\tcfg1:%d\n\tcfg2:%d\n\tcfg3:%d\n\tname:%s\n",
  comp_type->comp_type,
  comp_type->cfg1,
  comp_type->cfg2,
  comp_type->cfg3,
  comp_type->compatible);
}


conststructmy_private_s *my_dev = NULL;

intmain()
{

 my_dev = of_match_device(COMPRESS_TYPE1);
if(my_dev == NULL)
 {
printf("[yikou] not find valid compress type\n");
return-1;
 }else{
  dump_compress_info(my_dev);
 }

//思考下面代码
//my_dev->cfg1 = 88;

return1;
}

编译测试:

注意

为了保护我们的配置信息不被恶意修改,我们必须把数组定义为const类型,防止后面维护的老铁给修改了,

结尾

更多嵌入式软件编程技巧,请关注彭老师!

end

一口Linux 

关注,回复【1024】海量Linux资料赠送

精彩文章合集

文章推荐

☞【专辑】ARM
☞【专辑】粉丝问答
☞【专辑】所有原创
☞【专辑】linux入门
☞【专辑】计算机网络
☞【专辑】Linux驱动
☞【干货】嵌入式驱动工程师学习路线
☞【干货】Linux嵌入式所有知识点-思维导图

最新文章

随机文章