0%

内核编译

jetson-linux编译内核

本次博客记录基于x86_64平台交叉编译jetson nano的linux内核

顶层目录

首先进入到顶层目录

1
cd /home/jym/code/linux/ec20_driver_pro

顶层目录分布如下:

kernel_out为编译输出目标文件夹

gcc-linaro-x86_64_aarch64-linux-gnu为交叉编译器,下载地址Linaro Releases

其他为kernel_src.tar解压得到;内核相关文件在${PWD}/kernel/kernel-4.9

image-20240819121818432

设置环境变量

1
2
3
4
export CROSS_COMPILE_AARCH64=~/home/jym/code/linux/ec20_driver_pro/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu- #尽量使用绝对路径或在内核顶层目录使用相对路径

之前方法是以下:
export CROSS_COMPILE=~/home/jym/code/linux/ec20_driver_pro/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu- #尽量使用绝对路径或在内核顶层目录使用相对路径

设置好环境变量之后,下面有两种方法编译内核:一是英伟达官方的自动编译脚本nvbuild.sh,在顶层目录执行以下命令即可:

1
./nvbuild.sh -o $PWD/kernel_out

下面对另一种方法进行介绍

手动编译

编译的项目有:config配置文件,Imagedtbsmodules

下面操作均在顶层目录下进行

生成config配置文件

1
2
3
make -C ${PWD}/kernel/kernel-4.9/ ARCH=arm64 LOCALVERSION="-tegra" CROSS_COMPILE="${CROSS_COMPILE_AARCH64}"  O=${PWD}/kernel_out  tegra_defconfig

#make ARCH=arm64 LOCALVERSION="-tegra" CROSS_COMPILE="${CROSS_COMPILE_AARCH64}" tegra_defconfig

生成menuconfig配置

1
make -C ${PWD}/kernel/kernel-4.9/ ARCH=arm64 LOCALVERSION="-tegra" CROSS_COMPILE="${CROSS_COMPILE_AARCH64}"  O=${PWD}/kernel_out  menuconfig

生成Image

1
make -C ${PWD}/kernel/kernel-4.9/ ARCH=arm64 LOCALVERSION="-tegra" CROSS_COMPILE="${CROSS_COMPILE_AARCH64}"  O=${PWD}/kernel_out  Image -j12

生成dtb

主要的设备树文件:tegra194-p2888-0001-p2822-0000.dtb
关注的目录为:
kernel_src/hardware/nvidia/platform/t19x/galen/kernel-dts
关注的文件:
tegra234-p3767-0003-p3768-0000-a0.dts

1
make -C ${PWD}/kernel/kernel-4.9/ ARCH=arm64 LOCALVERSION="-tegra" CROSS_COMPILE="${CROSS_COMPILE_AARCH64}"  O=${PWD}/kernel_out  dtbs -j12

/home/ada/jetson/nvdia_35_4/kernel_src/hardware/nvidia/platform/t23x/p3768/kernel-dts/tegra234-p3767-0003-p3768-0000-a0.dts
生成的文件在这:
./kernel_out/arch/arm64/boot/dts/nvidia/tegra234-p3767-0003-p3768-0000-a0.dtb

编译模块

编译

1
make -C ${PWD}/kernel/kernel-4.9/ ARCH=arm64 LOCALVERSION="-tegra" CROSS_COMPILE="${CROSS_COMPILE_AARCH64}"  O=${PWD}/kernel_out  modules -j12

安装模块 按照linux内核文件架构安装在kernel_out中

1
make -C ${PWD}/kernel/kernel-4.9/ ARCH=arm64 LOCALVERSION="-tegra" CROSS_COMPILE="${CROSS_COMPILE_AARCH64}"  O=${PWD}/kernel_out INSTALL_MOD_STRIP=1 modules_install INSTALL_MOD_PATH=modules

清除构建

1
make -C ${PWD}/kernel/kernel-5.10/ ARCH=arm64 LOCALVERSION="-tegra" CROSS_COMPILE="${CROSS_COMPILE_AARCH64}"  O=${PWD}/kernel_out clean

移植移远4G模块

修改内核文件

option.c

修改 [KERNEL_SRC] /kernel/kernel-5.10/drivers/usb/serial/option.c 文件

1.添加USB设备VID和PID

1
2
3
4
5
6
7
8
9
static const struct usb_device_id option_ids[] = {
/****************** modify *************************/
{ USB_DEVICE(0x2C7C, 0x6005) },/* Quectel EC200A: add VID and PID */
/****************** modify *************************/
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) },
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) },
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_LIGHT) },
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_QUAD) },
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_QUAD_LIGHT) },

image-20240820131402657

2.添加掉电恢复机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
static struct usb_serial_driver option_1port_device = {
.driver = {
.owner = THIS_MODULE,
.name = "option1",
},
.description = "GSM modem (1-port)",
.id_table = option_ids,
.num_ports = 1,
.probe = option_probe,
.open = usb_wwan_open,
.close = usb_wwan_close,
.dtr_rts = usb_wwan_dtr_rts,
.write = usb_wwan_write,
.write_room = usb_wwan_write_room,
.chars_in_buffer = usb_wwan_chars_in_buffer,
.tiocmget = usb_wwan_tiocmget,
.tiocmset = usb_wwan_tiocmset,
.ioctl = usb_wwan_ioctl,
.attach = option_attach,
.release = option_release,
.port_probe = usb_wwan_port_probe,
.port_remove = usb_wwan_port_remove,
.read_int_callback = option_instat_callback,
#ifdef CONFIG_PM
.suspend = usb_wwan_suspend,
.resume = usb_wwan_resume,
/****************** modify *************************/
.reset_resume = usb_wwan_resume, /*Quectel EC200A: add reset resume*/
/****************** modify *************************/
#endif
};

image-20240820131549697

usb_wwan.c

修改 [KERNEL_SRC] /kernel/kernel-5.10/drivers/usb/serial/usb_wwan.c 文件

添加零包机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
static struct urb *usb_wwan_setup_urb(struct usb_serial_port *port,
int endpoint,
int dir, void *ctx, char *buf, int len,
void (*callback) (struct urb *))
{
struct usb_serial *serial = port->serial;
struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial);
struct urb *urb;

urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */
if (!urb)
return NULL;

usb_fill_bulk_urb(urb, serial->dev,
usb_sndbulkpipe(serial->dev, endpoint) | dir,
buf, len, callback, ctx);

if (intfdata->use_zlp && dir == USB_DIR_OUT)
urb->transfer_flags |= URB_ZERO_PACKET;


/****************** modify *************************/

/*Quectel 2C7C modules: add the zero packet mechanism*/
if(dir == USB_DIR_OUT){
struct usb_device_descriptor *desc = &serial->dev->descriptor;
if(desc->idVendor == cpu_to_le16(0x2c7c))
urb->transfer_flags |= URB_ZERO_PACKET;
}
/****************** modify *************************/

return urb;
}

image-20240819123646238

配置并编译内核

在下载完内核源码之后,我们可以首先手动编译一次,这样我们后续修改文件再次编译内核的时候速度会更快。

执行命令按照手动编译步骤进行即可。

  • 生成config配置文件
  • 生成menuconfig配置
  • 生成Image
  • 编译模块

在我们配置menuconfig时按照默认配置是可以编译出ko文件的,有时候我们需要编译出特定几个模块,需要在menuconfig中手动进行配置。

image-20240820132308979

在menuconfig下按下/键为查找(同vim) 比如我们需要如下图所示USB driver for GSM and CDMA modems,我们可以利用vscode查找功能在linux内核源码中查找对应模块。

image-20240820132459718

如下图为查找到内容,此时我们直接在menuconfig中键入USB_SERIAL_OPTION进行查询即可对其进行操作

image-20240820132704868

同样,笔者在第一次编译时并未编译出来qcserial.ko文件,我们直接在vscode中暴力搜索qcserial,

image-20240820132946467

从图中可以看到其对应配置为CONFIG_USB_SERIAL_QUALCOMM,选择它按下M即可将其编译成模块,便于后续移植。

image-20240820133113728

image-20240820133123001

配置好之后再次从menuconfig后面执行一遍手动编译操作即可

移植加载内核

将对应.ko文件复制到内核对应文件夹下使用uname -r查看对应内核版本

1
2
3
$sudo cp option.ko /lib/modules/4.9.253-tegra/kernel/drivers/usb/serial
$sudo cp usb_wwan.ko /lib/modules/4.9.253-tegra/kernel/drivers/usb/serial
$sudo cp qcserial.ko /lib/modules/4.9.253-tegra/kernel/drivers/usb/serial

加载模块

1
2
$sudo depmod -a
$sudo reboot

驱动测试

1
2
3
4
5
$sudo modprobe usbmon    # 加载usbmon驱动模块
$sudo dmesg #接入USB设备时输出对应信息
$lsmod #查看已加载的内核模块
$lsusb #出现移远4G模块
$nmcli device status #会出现ttyUSB1和ppp0两个