U-boot是一个常用的嵌入式系统启动加载器,它可以支持多种设备和文件系统,比如USB、SATA、FAT、EXT等。在U-boot阶段,我们有时候需要从U盘/SD卡中读取一些文件,并对文件内容进行解析和处理,比如获取一些配置参数或者加载一些镜像。这篇博客将介绍两种在Rockchip u-boot阶段读取U盘内容并解析的方法:命令行方式和代码方式。
系列文章:
Android存储分区与Rockchip平台的分区命名及U-Boot配置
Rockchip u-boot阶段命令行和代码方式读取u盘内容并解析
命令行方式
命令行方式是指在U-boot的控制台中输入一些命令来操作U盘和文件系统,这种方式比较简单和直观,但是功能有限,只能实现一些基本的操作。以下是命令行方式的具体步骤:
- 上电瞬间 通过串口 ctrl+c 启动U-boot命令行模式,并插入U盘到主板上。
- (必须)输入usb start命令来启动USB子系统,并检测U盘的设备信息。例如:
=> usb dev 0 USB is stopped. Please issue 'usb start' first. //必须先执行usb start 不然执行其他usb xxx命令是不能用的 => usb start starting USB... Bus dwc3@fcc00000: Register 2000140 NbrPorts 2 Starting the controller USB XHCI 1.10 Bus dwc3@fd000000: Register 2000140 NbrPorts 2 Starting the controller USB XHCI 1.10 Bus usb@fd800000: USB EHCI 1.00 Bus usb@fd840000: USB OHCI 1.0 Bus usb@fd880000: USB EHCI 1.00 Bus usb@fd8c0000: USB OHCI 1.0 scanning bus dwc3@fcc00000 for devices... 1 USB Device(s) found scanning bus dwc3@fd000000 for devices... 2 USB Device(s) found scanning bus usb@fd800000 for devices... 1 USB Device(s) found scanning bus usb@fd840000 for devices... 1 USB Device(s) found scanning bus usb@fd880000 for devices... 1 USB Device(s) found scanning bus usb@fd8c0000 for devices... 1 USB Device(s) found scanning usb for storage devices... 1 Storage Device(s) found
- 输入usb info命令来查看U盘的详细信息,包括设备号、厂商、产品、类型和容量等。例如:
=> usb info 1: Hub, USB Revision 3.0 - U-Boot XHCI Host Controller - Class: Hub - PacketSize: 512 Configurations: 1 - Vendor: 0x0000 Product 0x0000 Version 1.0 Configuration: 1 - Interfaces: 1 Self Powered 0mA Interface: 0 - Alternate Setting 0, Endpoints: 1 - Class Hub - Endpoint 1 In Interrupt MaxPacket 8 Interval 255ms 1: Hub, USB Revision 3.0 - U-Boot XHCI Host Controller - Class: Hub - PacketSize: 512 Configurations: 1 - Vendor: 0x0000 Product 0x0000 Version 1.0 Configuration: 1 - Interfaces: 1 Self Powered 0mA Interface: 0 - Alternate Setting 0, Endpoints: 1 - Class Hub - Endpoint 1 In Interrupt MaxPacket 8 Interval 255ms 2: Mass Storage, USB Revision 2.0 - SanDisk Cruzer Glide 20042605620A22C31651 - Class: (from Interface) Mass Storage - PacketSize: 64 Configurations: 1 - Vendor: 0x0781 Product 0x5575 Version 1.38 Configuration: 1 - Interfaces: 1 Bus Powered 200mA Interface: 0 - Alternate Setting 0, Endpoints: 2 - Class Mass Storage, Transp. SCSI, Bulk only - Endpoint 1 In Bulk MaxPacket 512 - Endpoint 2 Out Bulk MaxPacket 512 1: Hub, USB Revision 2.0 - u-boot EHCI Host Controller - Class: Hub - PacketSize: 64 Configurations: 1 - Vendor: 0x0000 Product 0x0000 Version 1.0 Configuration: 1 - Interfaces: 1 Self Powered 0mA Interface: 0 - Alternate Setting 0, Endpoints: 1 - Class Hub - Endpoint 1 In Interrupt MaxPacket 8 Interval 255ms 1: Hub, USB Revision 1.10 - U-Boot Root Hub - Class: Hub - PacketSize: 8 Configurations: 1 - Vendor: 0x0000 Product 0x0000 Version 0.0 Configuration: 1 - Interfaces: 1 Self Powered 0mA Interface: 0 - Alternate Setting 0, Endpoints: 1 - Class Hub - Endpoint 1 In Interrupt MaxPacket 2 Interval 255ms 1: Hub, USB Revision 2.0 - u-boot EHCI Host Controller - Class: Hub - PacketSize: 64 Configurations: 1 - Vendor: 0x0000 Product 0x0000 Version 1.0 Configuration: 1 - Interfaces: 1 Self Powered 0mA Interface: 0 - Alternate Setting 0, Endpoints: 1 - Class Hub - Endpoint 1 In Interrupt MaxPacket 8 Interval 255ms 1: Hub, USB Revision 1.10 - U-Boot Root Hub - Class: Hub - PacketSize: 8 Configurations: 1 - Vendor: 0x0000 Product 0x0000 Version 0.0 Configuration: 1 - Interfaces: 1 Self Powered 0mA Interface: 0 - Alternate Setting 0, Endpoints: 1 - Class Hub - Endpoint 1 In Interrupt MaxPacket 2 Interval 255ms => usb storage Device 0: Vendor: SanDisk Rev: 1.26 Prod: Cruzer Glide Type: Removable Hard Disk Capacity: 7633.5 MB = 7.4 GB (15633408 x 512) =>
- 输入part usb 命令来查看U盘的分区表,包括分区号、起始扇区、扇区数、UUID和类型等。例如:
=> usb part Partition Map for USB device 0 -- Partition Type: DOS Part Start Sector Num Sectors UUID Type 4 256 15633152 cad4ebea-04 0c Boot
- 输入fs_set_blk_dev <dev[:part]> 命令来设置U盘为当前的设备和文件系统,其中interface为usb,dev为设备号,part为分区号,fstype为文件系统类型,可以是FS_TYPE_ANY、FS_TYPE_FAT、FS_TYPE_EXT等。例如:
=> fs_set_blk_dev usb 0:4 FS_TYPE_ANY
- 输入fs_size 命令来获取U盘中某个文件的大小,并赋值给一个变量。例如:
=> fs_size uboot_file file_size 24 bytes read in 22 ms (1000 Bytes/s)
- 输入
fatload usb 0:4 <addr> <filename>
命令来从U盘读取某个文件的内容,并存储到指定的内存地址。其中filename
为文件名,addr
为内存地址。例如:
=> fatload usb 0:4 0x007ef000 uboot_file reading uboot_file 24 bytes read in 22 ms (1000 Bytes/s)
- 输入
md.b <addr> <len>
命令来查看内存中的文件内容,其中addr
为内存地址,len
为要查看的字节数。例如:
=> md.b 0x007ef000 512 007ef000: 6c 63 64 5f 78 3d 31 39 32 30 3b 0d 0a 6c 63 64 lcd_x=1920;..lcd 007ef010: 5f 79 3d 31 30 38 30 3b ff ff ff ff ff ff ff ff _y=1080;........ 007ef020: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................ 007ef030: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................ 007ef040: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................ 007ef050: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
- 根据文件内容的格式和意义,进行相应的解析和处理。例如,如果文件内容是一些键值对,我们可以使用strtok函数来分割字符串,并使用strtol函数来转换数值。例如:
char *ptr_x = strtok(buffer, "="); char *ptr_y = strtok(NULL, "="); long lcd_x = strtol(ptr_x, NULL, 10); long lcd_y = strtol(ptr_y, NULL, 10); printf("lcd_x = %ld, lcd_y = %ld\n", lcd_x, lcd_y);
代码方式
代码方式是指在U-boot的源码中编写一些函数或命令来操作U盘和文件系统,这种方式比较灵活和强大,但是需要一定的编程知识和编译环境。以下是代码方式的具体步骤:
- 进入Rockchip u-boot的源码配置和编译u-boot。
- 在drivers/usb/gadget目录下创建一个新的C文件,比如usb_file_parser.c,并在其中包含一些必要的头文件,比如common.h、command.h、fs.h、usb.h等。
- 在usb_file_parser.c文件中定义一个新的函数,比如do_read_uboot_file,并在其中实现从U盘中读取并解析文件的逻辑。可以参考以下链接中的示例代码来实现这个函数。
- 在usb_file_parser.c文件中定义一个新的命令结构体,比如read_uboot_file_cmd,并在其中指定命令名、参数个数、重复性、函数指针、描述和用法等。
- 在usb_file_parser.c文件中使用U_BOOT_CMD宏来注册这个新的命令,并将其添加到u-boot命令列表中。
- 在drivers/usb/gadget/Makefile文件中添加usb_file_parser.o到obj-y变量中,以便编
重新编译u-boot,并烧录到主板上。
```bash #include <common.h> #include <command.h> #include <fs.h> #include <usb.h> #include <stdlib.h> #define FILE_PATH "uboot_file" #define BUFFER_SIZE 512 int do_read_uboot_file(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) { char buffer[BUFFER_SIZE + 1]; loff_t file_size, actread; int ret; // 启动USB子系统 //usb_stop(); // usb_start(); //没有这个 // 设置U盘为当前设备和文件系统,根据实际情况修改设备号和分区号 if (fs_set_blk_dev("usb", "0:4", FS_TYPE_ANY)) { printf("Error setting block device.\n"); return 1; } // 获取文件大小 if (fs_size(FILE_PATH, &file_size) < 0) { printf("Error getting size of %s\n", FILE_PATH); return 1; } // 确保不尝试读取超出文件大小的内容 if (file_size > BUFFER_SIZE) { printf("File is larger than buffer. Adjust BUFFER_SIZE.\n"); return 1; } // 读取文件,选择一个合适的内存地址,避免与其他内存区域冲突 actread = 0; //ret = fs_read(FILE_PATH, (ulong)0x60000000, 0, 0, &actread); // 修改这一行 // ret = fs_read(FILE_PATH, (ulong)buffer, 0, file_size, &actread); //ret = fs_read(FILE_PATH, (ulong)0x60000000, 0, file_size, &actread); ret = fs_read(FILE_PATH, (ulong)0x7ef000, 0, file_size, &actread); if (ret) { printf("fs_read returned error code: %d\n", ret); } if (actread != file_size) { printf("Expected to read %lld bytes but read %lld bytes\n", file_size, actread); } if (ret || actread != file_size) { printf("Error reading %s\n", FILE_PATH); return 1; } // 确保字符串以null结尾 buffer[actread] = '\0'; // 解析文件内容 char *ptr_x = strstr(buffer, "lcd_x="); char *ptr_y = strstr(buffer, "lcd_y="); if (ptr_x && ptr_y) { int lcd_x = simple_strtoul(ptr_x + 6, NULL, 10); int lcd_y = simple_strtoul(ptr_y + 6, NULL, 10); printf("lcd_x = %d, lcd_y = %d\n", lcd_x, lcd_y); } else { printf("Failed to parse the file\n"); } return 0; } U_BOOT_CMD( read_uboot_file, // command name 1, // maxargs 1, // repeatable do_read_uboot_file, // command function "Read and parse the uboot_file from USB", // description "This command reads the uboot_file from USB and parses lcd_x and lcd_y values" // usage );
- 在增加fs.c的fs_read函数中增加更多的printf语句,以获取更详细的调试信息。
int fs_read(const char *filename, ulong addr, loff_t offset, loff_t len, loff_t *actread) { struct fstype_info *info = fs_get_info(fs_type); void *buf; int ret; // 增加的打印 printf("fs_read called with:\n"); printf("filename: %s\n", filename); printf("addr: %lx\n", addr); printf("offset: %lld\n", offset); printf("len: %lld\n", len); buf = map_sysmem(addr, len); if (!buf) { printf("Error mapping memory at address %lx\n", addr); return -1; } ret = info->read(filename, buf, offset, len, actread); // 增加的打印 if (ret) { printf("info->read returned error code: %d\n", ret); } if (actread && *actread != len) { printf("Expected to read %lld bytes but read %lld bytes\n", len, *actread); } unmap_sysmem(buf); /* If we requested a specific number of bytes, check we got it */ if (ret == 0 && len && *actread != len) printf("** %s shorter than offset + len **\n", filename); fs_close(); return ret; }
- 启动u-boot,并插入U盘到主板上。
- 输入read_uboot_file命令来执行从U盘中读取并解析文件的函数,并查看输出结果。例如:
//必须先执行usb start 不然执行其他usb xxx命令是不能用的 => usb start starting USB... Bus dwc3@fcc00000: Register 2000140 NbrPorts 2 Starting the controller USB XHCI 1.10 Bus dwc3@fd000000: Register 2000140 NbrPorts 2 Starting the controller USB XHCI 1.10 Bus usb@fd800000: USB EHCI 1.00 Bus usb@fd840000: USB OHCI 1.0 Bus usb@fd880000: USB EHCI 1.00 Bus usb@fd8c0000: USB OHCI 1.0 scanning bus dwc3@fcc00000 for devices... 1 USB Device(s) found scanning bus dwc3@fd000000 for devices... 2 USB Device(s) found scanning bus usb@fd800000 for devices... 1 USB Device(s) found scanning bus usb@fd840000 for devices... 1 USB Device(s) found scanning bus usb@fd880000 for devices... 1 USB Device(s) found scanning bus usb@fd8c0000 for devices... 1 USB Device(s) found scanning usb for storage devices... 1 Storage Device(s) found //---------------------- //uboot 反正目前一直是下面这个打印 能读到文件大小,但就是代码读不到文件内容。 //真™快吐了,折腾了我将近4小时 后面有时间在研究, => read_uboot_file fs_read called with: filename: uboot_file addr: 7ef000 offset: 0 len: 24 info->read returned error code: -1 Expected to read 24 bytes but read 0 bytes fs_read returned error code: -1 1 Expected to read 24 bytes but read 0 bytes Error reading uboot_file => //代码正确打印应该是 reading uboot_file fs_read called with: filename: uboot_file addr: 7ef000 offset: 0 len: 0 reading uboot_file Expected to read 0 bytes but read 24 bytes lcd_x = 1920, lcd_y = 1080 //命令行正确打印 => fatload usb 0:4 0x007ef000 uboot_file fs_read called with: filename: uboot_file addr: 7ef000 offset: 0 len: 0 reading uboot_file Expected to read 0 bytes but read 24 bytes => md.b 0x007ef000 512 007ef000: 6c 63 64 5f 78 3d 31 39 32 30 3b 0d 0a 6c 63 64 lcd_x=1920;..lcd 007ef010: 5f 79 3d 31 30 38 30 3b ff ff ff ff ff ff ff ff _y=1080;........ 007ef020: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................ 007ef030: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................ 007ef040: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................ 007ef050: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
20230828更新
我换了个思路 , 既然fs_read
函数死活读不到我就(参考uboot 读logo的方式)直接通过run_command
读 , 果然成功了。
但目前有个缺点就是依然需要手动执行usb start , 通过run_command("usb start", 0)
会提示Bad device usb 0
。
=> usb start starting USB... Bus dwc3@fcc00000: Register 2000140 NbrPorts 2 Starting the controller USB XHCI 1.10 Bus dwc3@fd000000: Register 2000140 NbrPorts 2 Starting the controller USB XHCI 1.10 Bus usb@fd800000: USB EHCI 1.00 Bus usb@fd840000: USB OHCI 1.0 Bus usb@fd880000: USB EHCI 1.00 Bus usb@fd8c0000: USB OHCI 1.0 scanning bus dwc3@fcc00000 for devices... 1 USB Device(s) found scanning bus dwc3@fd000000 for devices... 2 USB Device(s) found scanning bus usb@fd800000 for devices... 1 USB Device(s) found scanning bus usb@fd840000 for devices... 1 USB Device(s) found scanning bus usb@fd880000 for devices... 1 USB Device(s) found scanning bus usb@fd8c0000 for devices... 1 USB Device(s) found scanning usb for storage devices... 1 Storage Device(s) found => read_uboot_file fs_read called with: filename: uboot_file addr: 7ef000 offset: 0 len: 0 reading uboot_file Expected to read 0 bytes but read 24 bytes 24 bytes read in 32 ms (0 Bytes/s) lcd_x = 1920, lcd_y = 1080
#include <common.h> #include <command.h> #include <fs.h> #include <usb.h> #include <stdlib.h> #define FILE_PATH "uboot_file" #define BUFFER_SIZE 512 int do_read_uboot_file(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) { //char buffer[BUFFER_SIZE + 1]; //这个一定是要选一个可用的地址 不能这么干 可能读不到 char *buffer = (char *)0x007ef000; // 直接使用该地址 char cmd[BUFFER_SIZE] = {"0"}; // 添加命令缓冲区 // 初始化USB /*if (run_command("usb start", 0)) { printf("Error initializing USB.\n"); return 1; }*/ // 使用fatload命令读取文件到指定地址 sprintf(cmd, "fatload usb 0:4 0x007ef000 %s", FILE_PATH); if (run_command(cmd, 0)) { printf("Error reading %s using fatload\n", FILE_PATH); return 1; } // 解析文件内容 char *ptr_x = strstr(buffer, "lcd_x="); char *ptr_y = strstr(buffer, "lcd_y="); if (ptr_x && ptr_y) { int lcd_x = simple_strtoul(ptr_x + 6, NULL, 10); int lcd_y = simple_strtoul(ptr_y + 6, NULL, 10); printf("lcd_x = %d, lcd_y = %d\n", lcd_x, lcd_y); } else { printf("Failed to parse the file\n"); } return 0; } U_BOOT_CMD( read_uboot_file, // command name 1, // maxargs 1, // repeatable do_read_uboot_file, // command function "Read and parse the uboot_file from USB", // description "This command reads the uboot_file from USB and parses lcd_x and lcd_y values" // usage );
总结
这篇博客介绍了两种在Rockchip u-boot阶段读取U盘内容并解析的方法:命令行方式和代码方式。命令行方式比较简单和直观,但是功能有限,只能实现一些基本的操作。