Rockchip настраивает данные вендоров, а затем u-boot передает данные ядру через cmdline.

В последнее время задумался над некоторыми интересными функциями, такими как настройка логотипов, динамическое обновление параметров экрана, динамическое переключение дтс и другие функции (правда, я раньше это делал на платформе мтк, но платформа другая и это было давно назад), поэтому я исследовал его последние два дня. Ознакомьтесь с контентом, связанным с uboot.

Этот контент занял у меня много времени, и я столкнулся со многими подводными камнями и необъяснимыми проблемами, но, наконец, понял и переварил их. Так что не читайте содержание. Есть только несколько моментов, которые вам нужно начать, чтобы по-настоящему понять и освоить.

Тогда приступим, я запишу результаты, на которые потратил большую часть дня, надеясь помочь нуждающимся друзьям.

фон

На чипе Rockchip имеется специальный раздел, называемый хранилищем поставщиков, который используется для хранения определяемых пользователем данных, таких как серийные номера, MAC-адреса, данные калибровки и т. д. Доступ к разделу хранилища поставщика осуществляется с помощью команды MMC. Нет необходимости монтировать файловую систему, и его можно использовать как в u-boot, так и в ядре. Размер и расположение раздела хранилища поставщика можно настроить в файле dts, обычно 4 МБ.

В некоторых сценариях может потребоваться прочитать некоторые данные из раздела хранилища поставщика в u-boot, а затем передать их ядру через параметр cmdline, чтобы их мог использовать драйвер или приложение в ядре. Например, нам может потребоваться сохранить определяемый пользователем цвет или значение яркости светодиода в разделе хранилища поставщика, затем прочитать его в u-boot и передать драйверу светодиода в ядре через параметр cmdline для достижения заданного пользователем значения. Светодиодный эффект.

Ссылка: введение в использование интерфейса этапа VendorStorage uboot/kernel/user space серии Rockchip (2)

Выполнение

Чтобы реализовать эту функцию, мне нужно изменить код u-boot и ядра. Конкретные шаги заключаются в следующем:

1. Определите два идентификатора в u-boot для хранения пользовательских данных.

В u-boot каждый элемент данных в разделе хранения поставщика идентифицируется уникальным идентификатором, который представляет собой 16-битное целое число. Требуемый идентификатор можно определить в файле u-boot/arch/arm/include/asm/arch-rockchip/vendor.h, например:

#define USER_CUSTOM1_ID                        18
#define USER_CUSTOM2_ID                        19

Здесь я определил два идентификатора, 18 и 19 соответственно, для хранения двух определяемых пользователем элементов данных.

2. Считайте пользовательские данные в разделе хранилища поставщика в u-boot и установите их как переменную среды.

В u-boot мы можем использовать функциюvendor_storage_read для чтения определенного элемента данных в разделе хранилища поставщика.Эта функция должна передать три параметра: идентификатор, указатель буфера и размер буфера. Мы можем добавить две функции в файл u-boot/arch/arm/mach-rockchip/board.c для чтения данных, соответствующих двум определенным нами идентификаторам, и установки их в качестве переменных среды, например:

// USER_CUSTOM1_ID 和  USER_CUSTOM2_ID 已经在某个头文件中定义了,如果没有,请定义它


static int rockchip_get_user_custom1_value(void)
{
    
    

#ifdef CONFIG_ROCKCHIP_VENDOR_PARTITION
    char custom_value[VENDOR_SN_MAX + 20] = {
    
    0};  // 用于存储USER_CUSTOM1_ID的值
    //char key[VENDOR_SN_MAX] = "user_custom1_value";
    //char buf[VENDOR_SN_MAX + 20];  // 为键和值预留足够的空间
    // 读取 USER_CUSTOM1_ID 的值
    int custom_ret = vendor_storage_read(USER_CUSTOM1_ID, custom_value, sizeof(custom_value) - 1);
    if (custom_ret > 0) {
    
    
        printf("--3> USER_CUSTOM1_ID value: %s\n", custom_value);
        env_set("user_custom1_value", custom_value);  // 设置为U-Boot环境变量
        
        //sprintf(buf, "%s", custom_value);
        //env_set(key, buf);
    } else {
    
    
        printf("%s: vendor_storage_read for USER_CUSTOM1_ID failed %d\n", __func__, custom_ret);
    }
#endif
    return 0;
}
static int rockchip_get_user_custom2_value(void)
{
    
    
#ifdef CONFIG_ROCKCHIP_VENDOR_PARTITION
    char custom2_value[VENDOR_SN_MAX] = {
    
    0};  // 用于存储USER_CUSTOM2_ID的值

    // 读取 USER_CUSTOM2_ID 的值
    int custom2_ret = vendor_storage_read(USER_CUSTOM2_ID, custom2_value, sizeof(custom2_value) - 1);
    if (custom2_ret > 0) {
    
    
        printf("--> USER_CUSTOM2_ID value: %s\n", custom2_value);
        env_set("user_custom2_value", custom2_value);  // 设置为U-Boot环境变量
    } else {
    
    
        printf("%s: vendor_storage_read for USER_CUSTOM2_ID failed %d\n", __func__, custom2_ret);
    }
#endif

    return 0;
}
/*
需要增加到
int board_late_init(void)
{
  printf("%s: leon->board_late_init enter2 \n",__func__);
  rockchip_get_user_custom2_value();
  rockchip_get_user_custom1_value();
*/

Здесь определены две функции, rockchip_get_user_custom1_value и rockchip_get_user_custom2_value, которые используются для чтения значений USER_CUSTOM1_ID и USER_CUSTOM2_ID в разделе хранилища поставщика и присваивают им две переменные среды user_custom1_value и user_custom2_value. Мы можем распечатать прочитанное значение в функции для отладки.

3. Вставьте переменные среды в параметры cmdline в u-boot и передайте их ядру.

В u-boot мы можем использовать функцию env_get, чтобы получить значение переменной среды, а затем использовать функцию snprintf, чтобы вставить ее в параметр cmdline. Мы можем изменить функцию boot_prep_linux в файле u-boot/arch/arm/lib/bootm.c, например:

static void boot_prep_linux(bootm_headers_t *images)
{
    
    
//-----------------star
#ifdef CONFIG_ROCKCHIP_VENDOR_PARTITION
       char *commandline = NULL;
       //这里有个坑,512 会出问题 所以搞大一点。
       char new_bootargs[2048] = {
    
    0};  // 增加缓冲区大小以容纳更多的内容
       char* old_bootargs = env_get("bootargs");
       char *user_custom1_value = env_get("user_custom1_value");
       char *user_custom2_value = env_get("user_custom2_value");

       strncpy(new_bootargs, old_bootargs, sizeof(new_bootargs) - 1);

       if(user_custom1_value){
    
    
           snprintf(new_bootargs + strlen(new_bootargs), sizeof(new_bootargs) - strlen(new_bootargs), " user_custom1_value=%s", user_custom1_value);
       }

       if(user_custom2_value){
    
    
           snprintf(new_bootargs + strlen(new_bootargs), sizeof(new_bootargs) - strlen(new_bootargs), " user_custom2_value=%s", user_custom2_value);
       }

       env_set("bootargs", new_bootargs);
       commandline = env_get("bootargs");
#else
       char *commandline  = env_get("bootargs");
#endif
//-----------------end

	if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len) {
    
    

Здесь сначала получаются исходные параметры cmdline, а затем определяется, есть ли переменные среды для пользовательских данных. Если да, то они встраиваются в параметры cmdline, а затем переменные среды bootargs сбрасываются и передаются в указатель командной строки. Таким образом, когда u-boot запускает ядро, эти пользовательские данные будут переданы ядру в виде параметров командной строки.

4. Разобрать параметры командной строки в ядре и получить пользовательские данные.

В ядре мы можем использовать функцию strstr, чтобы определить, содержит ли параметр cmdline пары ключ-значение необходимых пользовательских данных. Если да, проанализировать их и сохранить в соответствующих переменных. Мы можем добавить функцию в файл kernel/drivers/leds/led_control.c ( драйвер, который я написал сам, подойдет любой. Я просто проверяю, правильность печати ) для распечатки значения пользовательских данных, например :

/*不要用这个 这个不会截取的,
[    5.256936] user_custom1_value: CUSTOM_20230826 earlycon=uart8250,mmio32,0xfe660000 androidboot.boot_devices=fe310000.sdhci,fe330000.nandc
[    5.256953] user_custom2_value not found
*/
/*static void printk_custom_cmdline(void)
{
    //-----------------------打印commandline参数
    char *custom1_ptr = NULL;
    char *custom2_ptr = NULL;

    custom1_ptr = strstr(boot_command_line, "user_custom1_value=");
    custom2_ptr = strstr(boot_command_line, "user_custom2_value=");

    if (custom1_ptr) {
        custom1_ptr += strlen("user_custom1_value=");
        printk(KERN_INFO "user_custom1_value: %s\n", custom1_ptr);
    } else {
        printk(KERN_INFO "user_custom1_value not found\n");
    }

    if (custom2_ptr) {
        custom2_ptr += strlen("user_custom2_value=");
        printk(KERN_INFO "user_custom2_value: %s\n", custom2_ptr);
    } else {
        printk(KERN_INFO "user_custom2_value not found\n");
    }
    //-----------------------打印commandline参数
}*/
/*
这个打印就是正常的
[    5.257462] user_custom1_value: CUSTOM_20230826
[    5.257479] user_custom2_value not found
*/
static void printk_custom_cmdline(void)
{
    
    
    //-----------------------打印commandline参数
    char *custom1_ptr = NULL;
    char *custom1_end = NULL;
    char *custom2_ptr = NULL;
    char *custom2_end = NULL;

    custom1_ptr = strstr(boot_command_line, "user_custom1_value=");
    custom2_ptr = strstr(boot_command_line, "user_custom2_value=");

    if (custom1_ptr) {
    
    
        custom1_ptr += strlen("user_custom1_value=");
        custom1_end = strchr(custom1_ptr, ' ');  // 查找空格,表示参数的结束
        if (!custom1_end)  // 如果没有找到空格,使用字符串结束作为结束
            custom1_end = custom1_ptr + strlen(custom1_ptr);
        printk(KERN_INFO "user_custom1_value: %.*s\n", (int)(custom1_end - custom1_ptr), custom1_ptr);
    } else {
    
    
        printk(KERN_INFO "user_custom1_value not found\n");
    }

    if (custom2_ptr) {
    
    
        custom2_ptr += strlen("user_custom2_value=");
        custom2_end = strchr(custom2_ptr, ' ');
        if (!custom2_end)
            custom2_end = custom2_ptr + strlen(custom2_ptr);
        printk(KERN_INFO "user_custom2_value: %.*s\n", (int)(custom2_end - custom2_ptr), custom2_ptr);
    } else {
    
    
        printk(KERN_INFO "user_custom2_value not found\n");
    }
    //-----------------------打印commandline参数
}

5. Используйте пользовательские данные в ядре для управления цветом и яркостью светодиода.

В ядре мы можем установить цвет и яркость светодиода в соответствии со значением пользовательских данных для достижения пользовательских светодиодных эффектов. Мы можем изменить функциюled_init_state в файле kernel/drivers/leds/led_control.c, например:
(Этот шаг больше не важен. После получения данных на шаге 4, что вам следует делать в соответствии с вашей собственной ситуацией? Это просто моя собственная логика.)

static void led_init_state(void)
{
    
    
    int i,j;
    char *custom1_ptr = NULL;
    char *custom2_ptr = NULL;
    int custom1_value = 0;
    int custom2_value = 0;

    // 获取用户自定义数据的值
    custom1_ptr = strstr(boot_command_line, "user_custom1_value=");
    custom2_ptr = strstr(boot_command_line, "user_custom2_value=");

    if (custom1_ptr) {
    
    
        custom1_ptr += strlen("user_custom1_value=");
        custom1_value = simple_strtol(custom1_ptr, NULL, 10); // 转换为整数
    }

    if (custom2_ptr) {
    
    
        custom2_ptr += strlen("user_custom2_value=");
        custom2_value = simple_strtol(custom2_ptr, NULL, 10); // 转换为整数
    }

    // 根据用户自定义数据的值来设置LED的颜色和亮度
    for(i=0;i<MAX_LED;i++)
    {
    
    
        for(j=0;j<MAX_COLOR;j++)
        {
    
    
            if(j == 0) // 红色
            {
    
    
                led_state[i][j] = (custom1_value >> 16) & 0xff; // 取用户自定义数据的高8位作为红色值
            }
            else if(j == 1) // 绿色
            {
    
    
                led_state[i][j] = (custom1_value >> 8) & 0xff; // 取用户自定义数据的中8位作为绿色值
            }
            else if(j == 2) // 蓝色
            {
    
    
                led_state[i][j] = custom1_value & 0xff; // 取用户自定义数据的低8位作为蓝色值
            }
            else if(j == 3) // 亮度
            {
    
    
                led_state[i][j] = custom2_value; // 取用户自定义数据的另一个值作为亮度值
            }
            
            printk("led_init %d,%d,%d\n",i,j,led_state[i][j]);
        }
    }

}

Здесь мы сначала получаем значения пользовательских данных, а затем на их основе устанавливаем цвет и яркость светодиода. Предположим, что первое значение пользовательских данных представляет собой 24-битное целое число, представляющее цвет RGB. Выньте старшие 8 бит, средние 8 бит и младшие 8 бит как значения красного, зеленого и синего. . Предположим, что второе значение пользовательских данных представляет собой 8-битное целое число, представляющее яркость, и используйте его непосредственно в качестве значения яркости. Это позволяет управлять цветом и яркостью светодиода на основе данных, хранящихся пользователем в разделе хранения поставщика.

6. Возникшие проблемы

Вопрос 1: Застрял Enter fastboot...OK, это из-за того, что места в cmdline не хватает, плюс наша кастомизация, я изначально написал 512 чего мало, а позже поменял на 2048.

board_late_init: leon->board_late_init enter2 
USER_CUSTOM1_ID value: CUSTOM_20230826
rockchip_set_serialno: leon->rockchip_set_serialno enter4 
enter fastboot!
load_bmp_logo 尝试从MMC加载 logo.bmp...
512 bytes read in 40 ms (11.7 KiB/s)
170326 bytes read in 45 ms (3.6 MiB/s)
Rockchip UBOOT DRM driver version: v1.0.1
VOP have 1 active VP
vp0 have layer nr:6[0 2 4 1 3 5 ], primary plane: 4
vp1 have layer nr:0[], primary plane: 0
vp2 have layer nr:0[], primary plane: 0
disp info 0, type:11, id:0
xfer: num: 2, addr: 0x50
[dw_hdmi_i2c_read] i2c read reg[0x01] no interrupt
xfer: num: 2, addr: 0x50
[dw_hdmi_i2c_read] i2c read reg[0x01] no interrupt
xfer: num: 2, addr: 0x50
[dw_hdmi_i2c_read] i2c read reg[0x01] no interrupt
xfer: num: 2, addr: 0x50
[dw_hdmi_i2c_read] i2c read reg[0x01] no interrupt
xfer: num: 2, addr: 0x50
[dw_hdmi_i2c_read] i2c read reg[0x01] no interrupt
can't get edid block:0
failed to get edid
base_parameter.mode:1920x1080
mode:1920x1080
hdmi@fe0a0000:  detailed mode clock 148500 kHz, flags[5]
    H: 1920 2008 2052 2200
    V: 1080 1084 1089 1125
bus_format: 100a
VOP update mode to: 1920x1080p0, type: HDMI0 for VP0
VOP VP0 enable Smart0[654x258->654x258@633x411] fmt[2] addr[0x7df2a000]
CEA mode used vic=16
final pixclk = 148500000 tmdsclk = 148500000
PHY powered down in 0 iterations
PHY PLL locked 1 iterations
PHY powered down in 0 iterations
PHY PLL locked 1 iterations
sink has audio support
hdmi_set_clk_regenerator: fs=48000Hz ftdms=148.500MHz N=6144 cts=148500
CLK: (sync kernel. arm: enter 816000 KHz, init 816000 KHz, kernel 0N/A)
  apll 1416000 KHz
  dpll 780000 KHz
  gpll 1188000 KHz
  cpll 1000000 KHz
  npll 1200000 KHz
  vpll 24000 KHz
  hpll 148000 KHz
  ppll 200000 KHz
  armclk 1416000 KHz
  aclk_bus 150000 KHz
  pclk_bus 100000 KHz
  aclk_top_high 500000 KHz
  aclk_top_low 400000 KHz
  hclk_top 150000 KHz
  pclk_top 100000 KHz
  aclk_perimid 300000 KHz
  hclk_perimid 150000 KHz
  pclk_pmu 100000 KHz
Net:   eth1: ethernet@fe010000, eth0: ethernet@fe2a0000
switch to partitions #0, OK
mmc0(part 0) is current device
Enter fastboot...OK

Вопрос 2: Сначала я запутался при отладке.Этот env_set("bt_mac", "112233445566");метод не будет работать и приведет к сбою uboot.

static int rockchip_get_bt_mac(void)
{
    
    
  
#ifdef CONFIG_ROCKCHIP_VENDOR_PARTITION

       char buf[ARP_HLEN_ASCII + 1];
       u8 ethaddr[ARP_HLEN];
       int ret;
        //不能直接这么写,哎 写java写习惯了。会导致uboot 崩掉
       //env_set("test_var", "112233445566");
       printf("%s: leon->rockchip_get_bt_mac enter 3 \n",__func__);
       ret = vendor_storage_read(BT_MAC_ID, ethaddr, sizeof(ethaddr));

       if (ret > 0 && is_valid_ethaddr(ethaddr)) {
    
    
               sprintf(buf, "%pM", ethaddr);
               env_set("bt_mac", buf);
       }
#endif
       return 0;

}
............
load_bmp_logo 尝试从MMC加载 logo_kernel.bmp...
512 bytes read in 45 ms (10.7 KiB/s)
170326 bytes read in 49 ms (3.3 MiB/s)
vp0, plane_mask:0x3f, primary-id:4, curser-id:-1
vp1, plane_mask:0x0, primary-id:0, curser-id:-1
vp2, plane_mask:0x0, primary-id:0, curser-id:-1
Adding bank: 0x00200000 - 0x08400000 (size: 0x08200000)
Adding bank: 0x09400000 - 0x80000000 (size: 0x76c00000)
"Synchronous Abort" handler, esr 0x86000004

* Reason:        Exception from an Instruction abort
* PC         =   61705f77e4380e70
* LR         =   61705f77e4380e70
* SP         =   000000007b9f7800
* ESR_EL2    =   0000000086000004
* Reloc Off  =   000000007d352000

x0 : 0000000000000000 x1 : 000000007b9f76f8
x2 : 0000000000000008 x3 : 0000000000000008
x4 : 000000000a117a6c x5 : 0000000000000029
x6 : 000000000a11f935 x7 : 000000000a117a74
x8 : 00000000000179fc x9 : 0000000000000008
x10: 000000007b9f75ac x11: 000000000a100000
x12: 00000000000179fc x13: 000000007b9f766c
x14: 000000000a100000 x15: 00000000ffffffff
x16: 0000000000000003 x17: 000000000c563004
x18: 000000007b9ffcf8 x19: 646e6120373d7472
x20: 746f6f6264696f72 x21: 78756e696c65732e
x22: 000000007b9f7b18 x23: 0000000000000000
x24: 0000000000000200 x25: 0000000000000000
x26: 000000007dd5431c x27: 0000000000280000
x28: 0000000000000000 x29: 6f6f6c206f722074


Call trace:
  PC:	[< 61705f77e4380e70 >]
  LR:	[< 61705f77e4380e70 >]

Stack:
	[< 61705f77e4380e70 >]

Copy info from "Call trace..." to a file(eg. dump.txt), and run
command in your U-Boot project: ./scripts/stacktrace.sh dump.txt 

Resetting CPU ...

### ERROR ### Please RESET the board ###

проверять

В простых настройках
установите для инструмента RK DeviInfoWriteTool значение Custom 1, измените его на ручной и измените идентификатор на 18, чтобы он соответствовал добавленному нами идентификатору (#define USER_CUSTOM1_ID 18).
Вставьте сюда описание изображения
Установите Пользовательский 2, измените его на ручной и измените идентификатор на 19, чтобы он соответствовал добавленному нами идентификатору (#define USER_CUSTOM2_ID 19).
Вставьте сюда описание изображения
Запись данных
Пользовательский 1, запись данных: lcd_x: 112233
Пользовательский 2, запись данных: lcd_y: 445566
Вставьте сюда описание изображения

Перезапустите, чтобы увидеть печать журнала последовательного порта.

# ---->uboot 阶段打印
board_late_init: leon->board_late_init enter3 
--3> USER_CUSTOM2_ID value: lcd_y:445566
--3> USER_CUSTOM1_ID value: lcd_x:112233
# ---->kernel 阶段打印
[    0.000000] Kernel command line: storagemedia=emmc androidboot.storagemedia=emmc androidboot.mode=normal  androidboot.dtb_idx=0 androidboot.dtbo_idx=0  androidboot.verifiedbootstate=orange androidboot.slot_suffix= androidboot.serialno=SN20230826005 console=ttyFIQ0 androidboot.baseband=N/A androidboot.wificountrycode=CN androidboot.veritymode=enforcing androidboot.hardware=rk30board androidboot.console=ttyFIQ0 androidboot.verifiedbootstate=orange firmware_class.path=/vendor/etc/firmware init=/init rootwait ro loop.max_part=7 androidboot.selinux=permissive buildvariant=userdebug user_custom1_value=lcd_x:112233 user_custom2_value=lcd_y:445566 earlycon=uart8250,mmio32,0xfe660000 androidboot.boot_devices=fe310000.sdhci,fe330000.nandc


[    5.229307] user_custom1_value: lcd_x:112233
[    5.229324] user_custom2_value: lcd_y:445566

Подведем итог

С помощью описанных выше шагов мы реализовали функцию пользовательских хранилищ данных Rockchip, а затем u-boot передает данные в ядро ​​через cmdline. Таким образом, мы можем хранить некоторые пользовательские данные в разделе хранилища поставщика, затем читать их в u-boot и передавать ядру через параметр cmdline, чтобы они могли использоваться драйвером или приложением в ядре. Этот метод позволяет легко реализовать некоторые пользовательские функции, такие как светодиодные эффекты, параметры экрана и т. д.

Я надеюсь, что этот блог будет вам полезен. Если у вас есть вопросы или предложения, оставляйте их в комментариях. Спасибо!
Если вы хорошо пишете, пожалуйста, поддержите меня!

Supongo que te gusta

Origin blog.csdn.net/SHH_1064994894/article/details/132508787
Recomendado
Clasificación