dm3730之支持Peripheral 启动系统

时间:2023-01-02 08:42:47
开发板:DM3730
虚拟机:ubuntu 14.04
编译器:arm-none-linux-gnueabi
uboot:U-Boot 2010.06-00166-gbf92a97
开发板内核:Linux-2.6.37
文件系统:ext3

一般芯片的启动机制都在芯片手册上有讲解 比如我这款a8的dm3730的启动机制大致如下
romcode->x-load(MLO)->u-boot.bin->uImage->rootfs
u-boot我选择的是用uart的方式启动 因为usb启动的话需要usb是host并且满足一下的协议才能发送相应的image
dm3730之支持Peripheral 启动系统
uart启动uboot的协议如上
具体的ASCI ID
dm3730之支持Peripheral 启动系统
先是ROMCODE发送ASIC ID给host 然后host接受到后就发送Boot message 和size of image ,image
协议看似复杂 其实用工具就可以实现 我们可以使用两个tools就是peserial,ukermit
使用方法是 调好sys_boot[4:0]然后上电下载就好了

也可以自行去分析在pserial和ukermit里怎么实现传输文件的 在这里简单的分析下pserial协议下载

//pserial.c
int main(int argc, char **argv)
{
signed char ret = 0;
unsigned char buff[MAX_BUF];
/* 0xF0030002表示继续从uart或者usb启动 */
unsigned int download_command = 0xF0030002;
char *port = NULL;
char *second_file = NULL;
char *appname = argv[0];
int c;
int read_size = 0;
unsigned int asic_id = 0;
char verbose = 0;
/* Option validation */
opterr = 0;
/* 分析参数 给相关的变量赋值 */
while ((c = getopt(argc, argv, PORT_ARG ":" SEC_ARG ":" VERBOSE_ARG)) !=
-1)
switch (c) {
case VERBOSE_ARG_C:
verbose = 1;
break;
case PORT_ARG_C:
port = optarg;
break;
case SEC_ARG_C:
second_file = optarg;
break;
case '?':
if ((optopt == SEC_ARG_C) || (optopt == PORT_ARG_C)) {
APP_ERROR("Option -%c requires an argument.\n",
optopt)
} else if (isprint(optopt)) {
APP_ERROR("Unknown option `-%c'.\n", optopt)
} else {
APP_ERROR("Unknown option character `\\x%x'.\n",
optopt)
}
usage(appname);
return 1;
default:
abort();
}
if ((port == NULL) || (second_file == NULL)) {
APP_ERROR("Error: Not Enough Args\n")
usage(appname);
return -1;
}
/* 打开端口 也就是/dev/ttyUSB0等 */
/* Setup the port */
ret = s_open(port);
if (ret != SERIAL_OK) {
APP_ERROR("serial open failed\n")
return ret;
}
/* 根据uart需求来配置端口 这里设置115200 偶校验 等 */
ret = s_configure(115200, EVENPARITY, ONE_STOP_BIT, 8);
if (ret != SERIAL_OK) {
s_close();
APP_ERROR("serial configure failed\n")
return ret;
}

/* Read ASIC ID */
/* 读取ASIC ID 开发板上电 ROMCODE回发送ASIC ID到此 这里接受到后分析数据 */
printf("Waiting For Device ASIC ID: Press Ctrl+C to stop\n");
while (!ret) {
ret = s_read(buff, 1);
if (buff[0] != 0x04)
ret = 0;
}

ret = 0;
while (!ret) {
ret = s_read(buff, ASIC_ID_SIZE);
if ((ret != ASIC_ID_SIZE)) {
APP_ERROR("Did not read asic ID ret = %d\n", ret)
s_close();
return ret;
}
}
if (verbose) {
int i = 0;
for (i = 0; i < ASIC_ID_SIZE; i++)
printf("[%d] 0x%x[%c]\n", i, buff[i], buff[i]);
}
asic_id = (buff[3] << 8) + buff[4];
switch (asic_id) {
case ASIC_ID_OMAP4430:
printf("ASIC ID Detected: OMAP 4430 with ROM Version"
" 0x%02x%02x\n", buff[5], buff[6]);
read_size = 59;
break;
case ASIC_ID_OMAP3430:
case ASIC_ID_OMAP3630:
printf("ASIC ID Detected: OMAP %04x with ROM Version"
" 0x%02x%02x\n", asic_id, buff[5], buff[6]);
read_size = 50;
break;
default:
printf("ASIC ID Detected: 0x%02x 0x%02x 0x%02x 0x%02x\n",
buff[3], buff[4], buff[5], buff[6]);
read_size = 50;
break;
}

ret = 0;
while (!ret) {
ret = s_read(buff, read_size);
if ((ret != read_size)) {
APP_ERROR("Did not read asic ID ret = %d\n", ret)
s_close();
return ret;
}
}

if (verbose) {
int i = 0;
for (i = 0; i < read_size; i++)
printf("[%d] 0x%x[%c]\n", i, buff[i], buff[i]);
}
/* 发送boot message */
/* Send the Download command */
printf("Sending 2ndFile:\n");
ret =
s_write((unsigned char *)&download_command,
sizeof(download_command));
if (ret != sizeof(download_command)) {
APP_ERROR("oppps!! did not actually manage to send command\n")
return -1;
}
/* 发送image size 和image */
if (send_file(second_file) != 0) {
APP_ERROR("send file failed!\n")
}
/* 关闭端口 */
ret = s_close();
if (ret != SERIAL_OK) {
APP_ERROR("serial close failed\n")
return ret;
}
printf("\nFile download completed.\n");

return ret;
}

整个程序就是根据手册上的协议来接受或者发送

接着就是kermit下载uboot到x-load接收 不过这个太复杂了 我可分析不了 我只是个做底层的 没太多精力放在协议上哈哈
接着是启动后的uboot要用到usb启动内核和文件系统 那么首先就是要uboot里先扫描到usb设备 才能读写
具体的验证方法是usb start命令
这个命令也就是调用usb_init()这个函数同样也可以走马观花的大概分析下

int usb_init(void)
{
int result;

running = 0;
dev_index = 0;
asynch_allowed = 1;
usb_hub_reset();
/* init low_level USB */
printf("USB: ");
/* usb相关控制器的初始化 */
result = usb_lowlevel_init();
/* if lowlevel init is OK, scan the bus for devices
* i.e. search HUBs and configure them */
if (result == 0) {
printf("scanning bus for devices... ");
running = 1;
/* 扫描设备 */
usb_scan_devices();
usb_started = 1;
return 0;
} else {
printf("Error, couldn't init Lowlevel part\n");
usb_started = 0;
return -1;
}
}

usb_lowlevel_init()函数就是初始化相关的控制器 基本都是在手册上的寄存器之类的 比如我这里使用的就是musb otg 所以就是初始化otg controler之类的寄存器
然后就是设备扫描部分

void usb_scan_devices(void)
{
int i;
struct usb_device *dev;

/* first make all devices unknown */
/* 将usb_device结构体清零 也就是把数据擦除 */
for (i = 0; i < USB_MAX_DEVICE; i++) {
memset(&usb_dev[i], 0, sizeof(struct usb_device));
usb_dev[i].devnum = -1;
}
dev_index = 0;
/* device 0 is always present (root hub, so let it analyze) */
/* */
/* 初始化usb_device */
dev = usb_alloc_new_device();
/* 这里才是真正去扫描 */
if (usb_new_device(dev))
printf("No USB Device found\n");
else
printf("%d USB Device(s) found\n", dev_index);
/* insert "driver" if possible */
#ifdef CONFIG_USB_KEYBOARD
drv_usb_kbd_init();
USB_PRINTF("scan end\n");
#endif
}
int usb_new_device(struct usb_device *dev)
{
int addr, err;
int tmp;
unsigned char tmpbuf[USB_BUFSIZ];

/* We still haven't set the Address yet */
addr = dev->devnum;
dev->devnum = 0;

#ifdef CONFIG_LEGACY_USB_INIT_SEQ
/* this is the old and known way of initializing devices, it is
* different than what Windows and Linux are doing. Windows and Linux
* both retrieve 64 bytes while reading the device descriptor
* Several USB stick devices report ERR: CTL_TIMEOUT, caused by an
* invalid header while reading 8 bytes as device descriptor. */
dev->descriptor.bMaxPacketSize0 = 8; /* Start off at 8 bytes */
dev->maxpacketsize = PACKET_SIZE_8;
dev->epmaxpacketin[0] = 8;
dev->epmaxpacketout[0] = 8;

err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8);
if (err < 8) {
printf("\n USB device not responding, " \
"giving up (status=%lX)\n", dev->status);
return 1;
}
#else
/* This is a Windows scheme of initialization sequence, with double
* reset of the device (Linux uses the same sequence)
* Some equipment is said to work only with such init sequence; this
* patch is based on the work by Alan Stern:
* http://sourceforge.net/mailarchive/forum.php?
* thread_id=5729457&forum_id=5398
*/
/* 发送要获取64字节usb描述符类的请求 */
struct usb_device_descriptor *desc;
int port = -1;
struct usb_device *parent = dev->parent;
unsigned short portstatus;

/* send 64-byte GET-DEVICE-DESCRIPTOR request. Since the descriptor is
* only 18 bytes long, this will terminate with a short packet. But if
* the maxpacket size is 8 or 16 the device may be waiting to transmit
* some more, or keeps on retransmitting the 8 byte header. */

desc = (struct usb_device_descriptor *)tmpbuf;
dev->descriptor.bMaxPacketSize0 = 64; /* Start off at 64 bytes */
/* Default to 64 byte max packet size */
dev->maxpacketsize = PACKET_SIZE_64;
dev->epmaxpacketin[0] = 64;
dev->epmaxpacketout[0] = 64;

err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, 64);
if (err < 0) {
USB_PRINTF("usb_new_device: usb_get_descriptor() failed\n");
return 1;
}

dev->descriptor.bMaxPacketSize0 = desc->bMaxPacketSize0;

/* find the port number we're at */
if (parent) {
int j;

for (j = 0; j < parent->maxchild; j++) {
if (parent->children[j] == dev) {
port = j;
break;
}
}
if (port < 0) {
printf("usb_new_device:cannot locate device's port.\n");
return 1;
}

/* reset the port for the second time */
err = hub_port_reset(dev->parent, port, &portstatus);
if (err < 0) {
printf("\n Couldn't reset port %i\n", port);
return 1;
}
}
#endif

dev->epmaxpacketin[0] = dev->descriptor.bMaxPacketSize0;
dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0;
switch (dev->descriptor.bMaxPacketSize0) {
case 8:
dev->maxpacketsize = PACKET_SIZE_8;
break;
case 16:
dev->maxpacketsize = PACKET_SIZE_16;
break;
case 32:
dev->maxpacketsize = PACKET_SIZE_32;
break;
case 64:
dev->maxpacketsize = PACKET_SIZE_64;
break;
}
dev->devnum = addr;

err = usb_set_address(dev); /* set address */

if (err < 0) {
printf("\n USB device not accepting new address " \
"(error=%lX)\n", dev->status);
return 1;
}

wait_ms(10); /* Let the SET_ADDRESS settle */

tmp = sizeof(dev->descriptor);

err = usb_get_descriptor(dev, USB_DT_DEVICE, 0,
&dev->descriptor, sizeof(dev->descriptor));
if (err < tmp) {
if (err < 0)
printf("unable to get device descriptor (error=%d)\n",
err);
else
printf("USB device descriptor short read " \
"(expected %i, got %i)\n", tmp, err);
return 1;
}
/* correct le values */
le16_to_cpus(&dev->descriptor.bcdUSB);
le16_to_cpus(&dev->descriptor.idVendor);
le16_to_cpus(&dev->descriptor.idProduct);
le16_to_cpus(&dev->descriptor.bcdDevice);
/* only support for one config for now */
/* */
usb_get_configuration_no(dev, &tmpbuf[0], 0);
usb_parse_config(dev, &tmpbuf[0], 0);
usb_set_maxpacket(dev);
/* we set the default configuration here */
if (usb_set_configuration(dev, dev->config.desc.bConfigurationValue)) {
printf("failed to set default configuration " \
"len %d, status %lX\n", dev->act_len, dev->status);
return -1;
}
USB_PRINTF("new device strings: Mfr=%d, Product=%d, SerialNumber=%d\n",
dev->descriptor.iManufacturer, dev->descriptor.iProduct,
dev->descriptor.iSerialNumber);
memset(dev->mf, 0, sizeof(dev->mf));
memset(dev->prod, 0, sizeof(dev->prod));
memset(dev->serial, 0, sizeof(dev->serial));
if (dev->descriptor.iManufacturer)
usb_string(dev, dev->descriptor.iManufacturer,
dev->mf, sizeof(dev->mf));
if (dev->descriptor.iProduct)
usb_string(dev, dev->descriptor.iProduct,
dev->prod, sizeof(dev->prod));
if (dev->descriptor.iSerialNumber)
usb_string(dev, dev->descriptor.iSerialNumber,
dev->serial, sizeof(dev->serial));
USB_PRINTF("Manufacturer %s\n", dev->mf);
USB_PRINTF("Product %s\n", dev->prod);
USB_PRINTF("SerialNumber %s\n", dev->serial);
/* now prode if the device is a hub */
usb_hub_probe(dev, 0);
return 0;
}

其实最终都是要到usb驱动里 根据usb设备的发包或者信息之类的传输 来确认usb的设备信息 具体的原理我也不甚了解 大概也就懂的大部分要做的事~

这里的dm3730用的是musb otg所以就在otg接口插上u盘然后 usb start扫描 然后扫描到设备后通过fatload命令下载内核到ram中然后bootm ,bootm的实现原理看我的上一篇转载的文章 讲的挺全的
uboot启动内核和文件系统 其实就是两件事
1.u盘启动盘的制作
2.uboot中环境变量的设置

还有一点特别需要注意的是usb otg是分为host 和 gadget模式的

.usb 有host和otg之分,host只能当主控来用,即去识别u盘;

而otg既可以当主控来用,也可以当从来用。

当otg当主的时候,就主动去之别u盘;当otg当从的时候,有以下几种种功能:

                1.打开 Serial Gadget (with CDC ACM and CDC OBEX support) ,这个时候选用 mv_gadget.c   pxa_comp.c   serial.c  ,把核心盘模拟成usbclient,把设备当成一个串口来用。 会在/dev/下生成一个设备节点,/dev/ttyGS0.通过映射之后,用getty /dev/ttyGS0 115200 ,这样就可以在pc上看到我们的串口,其实就是把我们的串口映射到pc上;

另外,当我们把usbmMode设为client之后,就可以在pc上安装驱动,这样就会在pc端被模拟成串口,这个时候就可以用串口工具进行通讯了。

   设备端建议采用minicomm测试,而不是用cat

2.打开File-backed Storage Gadget ,这个时候选择mv_gadget.c pxa_comp.c file_storage.c,这个时候,其实是把核心板模拟成upan,具体做法如下;

1.生成文件 dd if=/dev/zero of=./vfat.bin bs=1M count=10
2.格式化./newfs_msdos -F 32 ./vfat.bin
3.建立目录 mkdir vfat_mount
4.挂载目录mount -t vfat vfat.bin ./vfat_mount/
5.加载驱动 insmod g_file_storage.ko file=./vfat.bin stall=0 removable=1
6.cd vfat_mount ,写一个文件,sync ,插拔usb先,pc上可以看到实时看到设备上写的新东西。
7.电脑上些东西 ,设备端sync ,然后 umount ./vfat_mount/ mount -t vfat ./vfat.bin ./vfat_mount/,这样就可以在设备端实时看到pc端更新的东西