U-boot是一个常用的嵌入式系统启动加载器,它可以支持多种设备和文件系统,比如USB、SATA、FAT、EXT等。在U-boot阶段,我们有时候需要从U盘/SD卡中读取一些文件,并对文件内容进行解析和处理,比如获取一些配置参数或者加载一些镜像。这篇博客将介绍两种在Rockchip u-boot阶段读取U盘内容并解析的方法:命令行方式和代码方式。
命令行方式
命令行方式是指在U-boot的控制台中输入一些命令来操作U盘和文件系统,这种方式比较简单和直观,但是功能有限,只能实现一些基本的操作。以下是命令行方式的具体步骤:
- 启动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盘内容并解析的方法:命令行方式和代码方式。命令行方式比较简单和直观,但是功能有限,只能实现一些基本的操作。