DAVINCI DM3730开发攻略――U-BOOT-2010.06的移植

时间:2022-05-27 22:50:01

说来惭愧,又很长时间没更新文章了,本来这篇文章可以春节过来搞定的,结果春节回到公司,大客户一直要求抓紧时间设计DM3730平台的720P宽动态低照度相机产品,和另外两款多网口的DM3730产品的样机,南京老客户也在催DM3730 3G工业级板子样机,刚搞定两个老客户,又有两个新客户找我们设计DM3730新产品,一个是3G+WIFI 4CIF视频分析产品,另一个客户人脸识别车载方面的产品,整个桐烨科技都很忙。现在又不敢招人,毕竟冷酷的现实不得不面对:这个掌握无敌的宇宙真理国度经济不断下行,搞得第一季度我们也没有什么大单子,毕竟给客户做新样机是没什么钱赚的,都是培训客户。虽然现在是春天,但经济的寒冬才来临,通货膨胀造成公司运营成本飞涨,总感觉很累,有时自己很想静心钻进本公司新产品的研发去,但是客户那边有些关键的软件问题还得本人亲自解决,累也得坚持下去。

上篇文章介绍DM3730xloader的移植,这边介绍DM37302 boot阶段移植:u-boot-2010.06的移植,其实本人已经写过DM6446DM36X平台UBOOT的文章,但是由于平台和版本不一样,所以这里单独一篇,对有些人来说可能写得比较肤浅,请不要见笑,目的是完善自己的DM3730开发攻略,让有兴趣在这方面开发的生手提供一点点帮助。这个u-boot-2010.06源码存放的位置在《DAVINCIDM3730开发攻略――DVSDK4_03和双核CODEC机制介绍.doc》已经介绍。下面我们开始DM3730进行u-boot-2010.06的移植工作。


一、裁剪和交叉编译环境变量设置

   u-boot-2010.06-psp04.02.00.07.sdk改名u-boot-2010.06,简洁;

   1、总的Makefile修改


   对u-boot-2010.06/Makefile进行修改,

   屏蔽第139140行:

   #      examples/standalone

   #      examples/api

   然后把u-boot-2010.06/examples文件夹删除掉,简洁;


   第159行的修改为:

   CROSS_COMPILE = arm-arago-linux-gnueabi-

   这个就是DVSDK4――03自带的交叉编译工具,关于这个交叉编译工具,TI做得很全面了,提供很 多免费的应用程序LIB等等。


   第244行修改为:

   #LIBS += api/libapi.a

   屏蔽,然后把u-boot-2010.06/api,简洁,和项目不相关;


  2、涉及到交叉编译环境的u-boot-2010.06\arch\arm\config.mk24行改为:

  CROSS_COMPILE =arm-arago-linux-gnueabi-


二:删除多余文件夹

   这个DVSDK4_03自带的u-boot-2010.06-psp04.02.00.07.sdk很占空间,也比较乱。很不便于开发和备份,我们就是要把68M字节的变成20M字节简化版(bz2gz压缩的一般才3.4M的大小)。

注意,下面介绍的有些目录下是删除文件夹,有些是删除文件,不要搞混;

   1、删除u-boot-2010.06/arch/除了arm文件夹外其他所有文件夹

其他非arm平台去掉,占空间,��嗦。


   2、删除u-boot-2010.06/arch/armarm_cortexa8外的其他所有文件夹

DM3730属于arm cortex-A8架构(前几篇文章写成COTEX-A8了,系本人简写错误,有时本人直接跟客户就是ARM-A8的称呼)。DM6446DM6467DM6467TDM365DM368都是比较差劲的ARM926EJS架构。

   3、删除u-boot-2010.06/arch/arm/arm_cortexa8/omap3文件外,删除其他文件夹,

就是删除mx51s5pc1xxti81xx文件夹,DM3730/DM3725芯片属于OMAP3平台,这里边还包括OMAP35302010年拿来做MOTO 智能手机的OMAP3630等芯片。

   4、保留u-boot-2010.06/arch/arm/include/asm/下面的三个文件夹:

arch-omaparch-omap3proc-armv,同这个目录下的那些.h文件不要删除,这一点要注意一下。


   5、删除u-boot-2010.06/board/ti外的其他所有文件夹;

  UBOOT版本越来越高,新的厂商的板子不断加进去,很多,很烦,很占空间,对我们专注开发某个平台不好,所以我们接把不相关的板子平台去掉。


   6、删除u-boot-2010.06/board/tievm外的其他所有文件夹和文件;

同时TI文件夹也保留好几家第三方的板子,比如最典型的beagle xM,还有ti DM8148 DM8168的。我们直接使用ti evm板,或者参考其他ARM 学习板有关UBOOT移植的移植,重新起个个人的名字或公司名字,修改对应Makefile等等,太多文章介绍了。


   7、删除u-boot-2010.06/include/configsomap3_evm.h其他文件(注意这里是文件);

这个目录下的头文件和平台有关,DM3730使用的是omap3_evm.h,其他头文件可以干掉。

   8、删除u-boot-2010.06/ nand_splonenand_ipl

目前在DAVINCI平台还没发觉用到onenand的东西,可以去掉;


   好了,经过上面的删除工作,这个u-boot-2010.06已经很简洁了,压缩备份很方便,当然还可以删除更简洁的,这里就不详细说了。


三:编译u-boot-2010.06

u-boot-2010.06/下生成build-u-boot-all.shbuild-u-boot-tmp.sh的两个文件:

build-u-boot-all.sh内容为:


exportPATH=$PATH:/home/davinci/dm3730/dvsdk4_03/linux-devkit/bin:

make distclean

makeomap3_evm_config

make

cp u-boot.bindm3730_uboot.bin

cp u-boot.bin/tftpboot/dm3730_uboot.bin


上面的会直接读取总的Makefile3000多行的:

omap3_evm_config : unconfig

      @$(MKCONFIG) $(@:_config=) armarm_cortexa8 evm ti omap3

编译参数,这样会自动去选择编译,u-boot-2010.06\arch\arm\cpu\arm_cortexa8\omap3\里边的源码和u-boot-2010.06\board\ti\evm\里边的源码,并且指定include u-boot-2010.06/include/configs/omap3_evm.h的平台头文件。


build-u-boot-tmp.sh内容为


exportPATH=$PATH:/home/davinci/dm3730/dvsdk4_03/linux-devkit/bin:

make

cp u-boot.bindm3730_uboot.bin

cp u-boot.bin/tftpboot/dm3730_uboot.bin


对这两个sh文件进行:

chmod +x build-u-boot-all.sh

chmod +x build-u-boot-tmp.sh

然后第一次或者每次做了make distclean动作后,都有先执行./ build-u-boot-all.sh,进行omap3_evm_config。以后改动改动源码的时候可以不要重复make distcleanmake omap3_evm_config,直接使用./build-u-boot-tmp.sh编译就可以了。build-u-boot-all.shbuild-u-boot-tmp.sh文件自动帮你copy文件到主机tftp server对应的目录/tftpboot/


四、修改移植

上面第三点已经讲明如何链接编译omap3_evm_config ,那么我们现在可以进行对应的移植和修改源码。

1、针对DM3730芯片,我们先从u-boot-2010.06/include/configs/omap3_evm.h这个文件修改,让他指向对应的芯片平台;


#defineCONFIG_ARMCORTEXA8  1     /* This is an ARM V7 CPU core */

#defineCONFIG_OMAP                            1     /*in a TI OMAP core */

#defineCONFIG_OMAP34XX           1     /* which is a 34XX */

#defineCONFIG_OMAP3430             1     /* which is in a 3430 */

#defineCONFIG_OMAP3_EVM        1     /* working with EVM*/


这个头文件最前面的宏定义讲明了DM3730所属的ARM架构,T也属于TI 公司 OMAP系列当中的OMAP34XX家族的芯片,OMAP3430-à3530--à3630-àDM3730是软件硬件架构一脉相承的芯片系列。我们选择TI EVM板子模式。


/*

* select serial console configuration

*/

#if 1

#defineCONFIG_CONS_INDEX             3

#defineCONFIG_SYS_NS16550_COM3        OMAP34XX_UART3

#defineCONFIG_SERIAL3                3     /* UART3 on TY OMAP3 EVM */

#else

#defineCONFIG_CONS_INDEX             2

#defineCONFIG_SYS_NS16550_COM2        OMAP34XX_UART2

#defineCONFIG_SERIAL2                2     /* UART2 on TY OMAP3 EVM TEST */

#endif

TI EVM定义的LINUX调试串口指定是UART1DM3730DM6446一样一共有3UART,都可以用来做LINUX软件调试的串口,我们的板子也和其他公司一样使用UART3做为调试串口,而不是TIEVM指定的UART1,因为上篇文章《DAVINCIDM3730开发攻略――xload-1.51移植》提到过DM3730BOOT MODE,有种BOOT模式使用了SD-àNAND-àUART3,所以我们这里使用UART3。而往下看找到有关UBOOT串口调试的BOOTDELAY


/* Environmentinformation */

#defineCONFIG_BOOTDELAY 1

这个UBOOTdelay等待用户按键调试时间太长了,把10秒改成1秒,有些产品为了加快BOOT 时间,卖出去的产品不需要调试,也可以直接改为0


继续往下看,

/* commands toinclude */

#include<config_cmd_default.h>


这里边定义了很多CONFIG_CMD_XXX的功能,如果让UBOOT编译出来的文件比较小,可以使用#undef把某些不常用的功能屏蔽掉。


#defineCONFIG_CMD_EXT2           /* EXT2 Support               */

#defineCONFIG_CMD_FAT              /* FAT support                  */

#defineCONFIG_CMD_JFFS2    /* JFFS2 Support              */

#define CONFIG_CMD_MTDPARTS    /* Enable MTD parts commands */

#define CONFIG_MTD_DEVICE    /* needed for mtdparts commands */

#define MTDIDS_DEFAULT                   "nand0=nand"

#define MTDPARTS_DEFAULT             "mtdparts=nand:256k(x-loader),"\

                                  "256k(u-boot-env),1280k(u-boot),"\

                                  "5m(kernel),-(fs)"

上面红色字体是本人添加修改的,使能在UBOOT里边使用MTD分区。

同时我们打算使用256K字节做为u-boot 参数保存空间,偏移地址是x040000的地址,所以omap3_evm.h后面(320行左右的地方)提到的

#defineONENAND_ENV_OFFSET         0x00040000 /*environment starts here */

#defineSMNAND_ENV_OFFSET            0x00040000 /*environment starts here */

改成上面的0x00040000


这里首先声明桐烨科技的DM3730板子NAND512M BYTEDDR也是512M BYTE,网口使用DM9000,不同公司的开发板估计有不同的配置,这个信息很重要,下面NAND分区定义和程序烧写都需要这了解这方面的信息。


然后到看到#define CONFIG_EXTRA_ENV_SETTINGS 的修改:

定义这个CONFIG_EXTRA_ENV_SETTINGS前,需要补充一些NAND 存放XLAODUBOOTKERNEL等等知识,本人在上篇《DAVINCI DM3730开发攻略――xload-1.51移植》提到UBOOT 编译处理的BIN文件存放在NAND FLASH的地方,这里再更详细描述一下:


//0x000000000000-0x000000040000: "X-Loader"(DM3730 xlaod存放在NAND的位置)

//0x000000040000-0x000000080000: "U-Boot Env"(uboot 参数存放在NAND的位置)

//0x000000080000-0x000000200000: "U-Boot"(uboot 本身存放在NAND的位置)

//0x000000200000-0x000000C00000: "Kernel"(kernel存放在NAND的位置)

//0x000000C00000-0x000008400000: "ubifs0"(主要的文件系统UBIFS存放在NAND的位置)

//0x000008400000-0x00000FC00000: "ubifs1"(备用的UBIFS存放在NAND的位置,也可以不要)

//0x00000FC00000-0x000010000000: "user data"(保存一些用户自己定义的数据)

//loadaddr ==0x80300000TFTP 下载XLAOD,UBOOT,KERNELBIN文件到内存的起始偏移地址)

//ubifs loadaddr== 0x81000000TFTP 下载比较大的UBIFSBIN文件到内存起始偏移地址)

//u-boot codeaddress == 0x80E80000UBOOT本身存放到内存运行的偏移地址)



这是我们修改后的#define CONFIG_EXTRA_ENV_SETTINGS源码:

#define CONFIG_EXTRA_ENV_SETTINGS\

      "loadaddr=0x80300000\0" \

      "rdaddr=0x81000000\0"\

      "console=ttyS2,115200n8\0" \

      "mpurate=1000\0" \

      "vram=12M\0" \

      "dvimode=1280x720MR-16@60\0" \

      "defaultdisplay=dvi\0" \

      "nandroot=ubi0:rootfs\0" \

      "nandrootfstype=ubifs\0" \

      "nandargs=setenv bootargsconsole=${console} rw mem=120M@0x80000000 mem=256M@0xA0000000 " \

             "mpurate=${mpurate} " \

             "vram=${vram} " \

             "ip=192.168.1.188:192.168.1.252:192.168.1.1:255.255.255.0:tgt:eth0:off"\

             "omapdss.def_disp=${defaultdisplay}" \

             "omapfb.mode=dvi:${dvimode}" \

             "ubi.mtd=4 "\

             "root=${nandroot} " \

             "rootfstype=${nandrootfstype}" \

             "init=/initandroidboot.console=ttyS2\0" \

      "nfstvargs=setenv bootargsconsole=ttyS2,115200n8 rw mem=120M@0x80000000 mem=256M@0xA0000000 mpurate=1000vram=12M omapfb.mode=tv:720x576@25 omapdss.def_disp=tvip=192.168.1.188:192.168.1.252:192.168.1.1:255.255.255.0:tgt:eth0:offroot=/dev/nfsnfsroot=192.168.1.252:/home/davinci/dm3730/dvsdk4_03/filesystem/dm3730rootfs,nolock\0"\

      "nfslcdargs=setenv bootargsconsole=ttyS2,115200n8 rw mem=120M@0x80000000 mem=256M@0xA0000000 mpurate=1000vram=12M omapfb.mode=dvi:${dvimode} omapdss.def_disp=${defaultdisplay}init=/init androidboot.console=ttyS2ip=192.168.1.188:192.168.1.252:192.168.1.1:255.255.255.0:tgt:eth0:offroot=/dev/nfsnfsroot=192.168.1.252:/home/davinci/dm3730/dvsdk4_03/filesystem/dm3730rootfs,nolock\0"\

      "tftpboot=tftp 80300000dm3730_kernel.bin; bootm 80300000\0" \

      "userboot=nand read ${loadaddr}200000 400000; bootm ${loadaddr}\0" \

      "erase_env=nand erase 4000040000\0" \

      "eraseall=nand erase\0" \

      "updatexload=tftp 80300000dm3730_xload.bin;nand erase 0 40000;nandecc hw;nand write.i 80300000 0${filesize}\0" \

      "updateuboot=tftp 80300000dm3730_uboot.bin;nand erase 80000 180000;nandecc sw;nand write.i 80300000 80000${filesize}\0" \

      "updatekernel=tftp 80300000dm3730_kernel.bin;nand erase 200000 500000;nandecc sw;nand write.i 80300000200000 ${filesize}\0" \

      "updaterootfs=tftp 81000000dm3730_ubifs.bin;nand erase C00000 7800000;nandecc sw;nand write.i 81000000C00000 ${filesize}\0" \

      "nandboot=echo Booting from nand...; " \

             "run nandargs; " \

             "nand read ${loadaddr} 200000400000; " \

             "bootm ${loadaddr}\0" \

      "nfstvboot=echo Booting from nfs...; " \

             "run nfstvargs; " \

             "nand read ${loadaddr} 200000400000; " \

             "bootm ${loadaddr}\0" \

      "nfslcdboot=echo Booting from nfs...; " \

             "run nfslcdargs; " \

             "nand read ${loadaddr} 200000400000; " \

             "bootm ${loadaddr}\0" \

      " fact_update =nand erase;mmc init;"\

            "fatloadmmc 0 80300000 dm3730_xload.bin;nandecc hw;nand write.i 80300000 0 ${filesize};"\

            "fatloadmmc 0 80300000 dm3730_uboot.bin;nandecc sw;nand write.i 80300000 80000${filesize}; "\

            "fatloadmmc 0 80300000 dm3730_kernel.bin;nandecc sw;nand write.i 80300000 200000${filesize}; "\

            "fatloadmmc 0 81000000 dm3730_ubifs.bin;nandecc sw;nand write.i 81000000 C00000${filesize};"


注意:上面源码格式很讲究,特别是( \   “”   \0  ;  $)等标点符号,mpurate=1000表示跑1GA8;自从我们的linux-2.6.32支持ubifs 文件系统后,我们再也不用所谓的JFFS2YAFFS2了。我们公司支持512M 的内存而且CS0片选信号指向0x80000000(前段),第2段的片选信号CS1指向0xA0000000(后段)。为什么这样分配呢?前面120M和后面256M都是给LINUX系统的,而前段256M-120M=136M是给DSP用的,当然DSP里边还包括CMEM共享内存。这前段256M的内存分配是动态的,你可以分配80MLINUX,那么DSPCMEM就可分配更多的空间了。至于DSP里边如何细分,和应用程序使用的loadmodule.sh这个文件如何配合,这里不重点论述,那是在以后的内核移植再说。还有,有些其他开发板公司的内存如果只有256M字节,那么mem=256M@0xA0000000必须去掉,否则内核根本起不来。


上面有很多BOOT方式,比如nandboot,nfstvboot, tftpboot,我们直接使用run tftpboot就可以通过网络动态下载TFTPSERVER里边的dm3730_kernel.bin,然后使用默认的bootargs UBOOT参数进行NFS调试,或者run nfstvboot,也可以NFSrun nfstvboot表示视频输出是TV输出,这个和内核有关,以后内核移植有个地方有选择的编译;run nfslcdboot表示从LCD接口输出视频,可以跑android安卓文件系统。


有时客户在uboot命令行对bootargs进行设置,使用到这个刚设置好的bootargs,可以通过使用上面的userboot进行启动。

set bootcmd “run userboot”

saveern

同样使用nandboot也可以使用setbootcmd “run nandboot”saveenv的命令实现。


在测试的时候,我们和主机TFTP配合,直接通过run updatexload去网络升级xlaodrun updateuboot去升级烧写ubootrun updatekernel去烧写内核,当然了,你的tftpboot目录下要有对应的BIN文件,这里就不多说了,以前的DM6446DM36X开发攻略都提示过。



继续往下看代码:

#defineCONFIG_BOOTCOMMAND "run nandboot"

#defineCONFIG_BOOTARGS \

"console=ttyS2,115200n8rw mem=120M@0x80000000 mem=256M@0xA0000000 mpurate=1000 vram=12Momapfb.mode=dvi:1280x720@60 omapdss.def_disp=lcdip=192.168.1.188:192.168.1.252:192.168.1.1:255.255.255.0:tgt:eth0:offroot=/dev/nfsnfsroot=192.168.1.252:/home/davinci/dm3730/dvsdk4_03/filesystem/dm3730rootfs,nolock"

这个是默认的bootargs的参数,默认使用nandboot


   U-BOOT保存在NAND FLASH的参数,可以在U-BOOT命令行使用pri命令或pritenv命令看看。


最后面在#if defined(CONFIG_CMD_NET)的后面加上:

/* DM9000 */

#defineCONFIG_NET_MULTI                1

#defineCONFIG_NET_RETRY_COUNT          20

#defineCONFIG_DRIVER_DM9000            1

#defineCONFIG_DM9000_BASE              0x2c000000

#defineDM9000_IO                      CONFIG_DM9000_BASE

#defineDM9000_DATA                    (CONFIG_DM9000_BASE + 0x400)

#defineCONFIG_DM9000_USE_16BIT         1

#defineCONFIG_DM9000_NO_SROM           1

#undef  CONFIG_DM9000_DEBUG


#defineCONFIG_ETHADDR          88:11:22:33:44:77

#defineCONFIG_IPADDR           192.168.1.188

#defineCONFIG_SERVERIP         192.168.1.252

#defineCONFIG_GATEWAYIP        192.168.1.1

#defineCONFIG_NETMASK          255.255.255.0

我们自己修改的UBOOT和内核能够把MAC ETHADDR)地址传给内核;

这个MAC正规的申请途径可以GOOGLE一下,而测试调试可以随便定义;

板子的静态IP是定义192.168.1.188,而服务器主机的地址是192.168.1.252,不同公司有不同公司的网段和IP地址,用户可以修改。

好了,omap3_evm.h已经修改完毕。

2、修改u-boot-2010.06\arch\arm\cpu\arm_cortexa8\omap3\

这里边没什么好改动的,估计要改动的就是lowlevel_init.S,去配不同的PLL,这个参考手册见sprugn4q.pdf。然后就是注意修改mem.c里边的gpmc_init()的BOOT模式,到底内核是从MMC读还是从NAND读到内存。


3、修改u-boot-2010.06\board\ti\evm\

这里边的Evm.hEvm.c就是对DM3730管脚复用配置再次进行处理,在上篇《DAVINCI DM3730开发攻略――xload-1.51移植》也提到过,DM3730管脚复用比较复杂,有7种不同的模式,m0~m7,在evm.h代码里边:

/*

* IEN  -Input Enable

* IDIS - Input Disable

* PTD  -Pull type Down

* PTU  -Pull type Up

* DIS  -Pull type selection is inactive

* EN   -Pull type selection is active

* M0   -Mode 0

* The commented string gives the final muxconfiguration for that pin

*/

#define MUX_EVM()\

这个就说明了你使用的某个管脚是M0默认模式,还是M4GPIO模式,而GPIO模式是使用IEN(输入)还是IDIS(输出 ),GPIO管脚上拉PTU还是下拉PTD,上拉下拉是否要使能DISEN

MUX_VAL这种格式宏定义在u-boot-2010.06\arch\arm\include\asm\arch-omap3\mux.h定义,指向对应的寄存器。


举个例子:

比如I2C3,DM3730一共有4I2C总线,我们板子只需要I2C1I2C2两个总线就够用了,那么多余的I2C3I2C4可以当作GPIO使用。


      MUX_VAL(CP(I2C3_SCL),         (IEN | PTU | EN  | M0)) /*I2C3_SCL*/\

      MUX_VAL(CP(I2C3_SDA),        (IEN | PTU | EN  | M0)) /*I2C3_SDA*/\


如果是这样的代码,表示你使用I2C3使用M0模式,那这两个管脚使用I2C的模式,而不是GPIO

      MUX_VAL(CP(I2C3_SCL),         (IEN | PTU | EN  | M4)) /*GPIO184*/\

      MUX_VAL(CP(I2C3_SDA),        (IEN | PTU | EN  | M4)) /*GPIO185*/\

这样的代码表示I2C3被用来做GPIOIEN表示用作输入,内部上拉电阻,而且上拉电阻使能,

MUX_VAL(CP(I2C3_SCL),        (IDIS | PTU | EN  | M4)) /*GPIO184*/\

      MUX_VAL(CP(I2C3_SDA),        (IDIS | PTU | EN  | M4)) /*GPIO185*/\

这样的代码表示I2C3被用来做GPIOIEN表示用作输出,内部上拉电阻,而且上拉电阻使能。

其实也很好理解。我们在UBOOT详细定义了管脚复用,那么以后在内核编译的时候,首先内核make menuconfig里边要去掉MUX这个选项,这样内核就没必要再做一次管脚配置了。


写到这,对DM3730 UBOOT的移植应该有个头绪了,具体的应用,一些BUG,需要具体问题具体分析,不同的网口芯片,不同的NANDDDR芯片有不同的驱动,这里就不多说了。U-BOOT的移植在ARM平台来说,大同小异,虽然一个是ARM926,一个是A8,或者A9A12A15,但对于软件工程师来说,如果不涉及到汇编代码的编写,其实都是一样的C语言,一样的UBOOT软件架构。UBOOT的目的无外乎就是把linux内核给跑起来,把一些参数传给内核,板子启动的时候做些初始化的工作,或者一些测试调试工作。真正体现产品的功能价值就是内核和对应的应用程序。



(声明:

   桐烨科技DM3730/DM6446的板子和其他公司的开发板不一样,特别是DM3730的板子,目前国内好多家公司都只提供ARM(CORTEX-A8)的应用例子,很少介绍如何添加客户自己的算法到DSP端的例子,有些需要做DSP算法的人贪便宜,结果买这些便宜的板子回去花大量时间来学习,迟迟搞不清楚整个架构,浪费的这些时间难道不是资金吗?我们桐烨科技的板子都帮你采集好YUV格式的视频图像,并教会你如何把这个原始的图像数据放到DSP端进行处理,然后再教会你如何传处理过的图像数据和参数到ARM端。同时提醒客户还要注意一些冒牌的公司,特别是杭州有家没道德的公司直接拿我们桐烨科技的DM3730开发板图片放到他们公司网站上,欺骗其他人,我们桐烨科技从来没有想到让其他公司做代理。


本文出自 “集成系统-踏上文明的征程” 博客,请务必保留此出处http://zjbintsystem.blog.51cto.com/964211/1399719