系统移植

系统移植第一天

 
 

1 基础理论

1.1 什么是嵌入式

以应用为中心、以计算机技术为基础、软件硬件可裁剪、适应应用系统,对功能、可靠性、成本、体积、功耗严格要求的专用计算机系统。

可裁剪(软硬件)

1.2 什么是系统

1.2.1 硬件系统

冯诺依曼结构: 控制器,运算器,输入,输出, 存储器

传统的硬件架构: cpu,内存,北桥(高速设备)+ 南桥(慢速设备) --》 芯片组, 显卡 ,外设等等

SOC(system on chip) 片上系统 ,一个芯片集成了硬件系统的主要部件 (内存和外存 引出去的目的: 方便定制)

arm: a r m 三个系列

a系列(高性能) a9 a53 a7x m系列 (低功耗,相对51高性能)m0 m3 m4 m7 r系列 (高实时,高可靠性)

4412—》 cortex-a9 armv7 stm32f103rct6 --》 cortex-m3

1.2.2 软件系统

1.2.2.1 无操作系统(裸机)

应用–》 驱动–》硬件 stm32中 写程序 调用驱动程序 控制硬件

1.2.2.2 有操作系统

应用程序 --》 系统调用–》 内核 --》 驱动–》硬件

2 什么是移植

将一些功能 搬移到 指定的硬件平台上去

2.1 系统移植四大件(五大件)

(1) 交叉编译器 cross - complier (2) 启动引导程序 BootLoader (3) 内核(linux操作系统)kernel (4) 根文件系统 rootfs

(5)应用程序

2.2 来装个系统

2.2.1 交叉编译器 --》 gcc

2.2.2

bios --》BootLoader bios: basic input output system bios --> uefi t通用外部固件接口

上电的第一段代码 : 初始化,检查硬件–》 引导系统–》 给系统传参

比如我们的电脑 可以选择 硬盘,光驱,优盘,网络(无盘工作站)来装系统或者运行系统

2.2.3

内核–》 操作系统内核 (windows NT)

2.2.4

根文件系统 --》 c盘的文件,功能性的,非操作系统的

2.3 以人为类比

BootLoader 闹钟,妈

kernel 灵魂

rootfs 肉体,器官

3 交叉编译器

一般整个工程项目开始前,交叉编译器必须定下来,后期不会做较大更改

gcc(本平台生成本平台目标机代码)

x86 —》 x86 程序

cross 交叉(本平台实现 目标机为arm 的代码)

x86 —》 arm 程序

一般情况下,包含在硬件厂商提供的 板级支持包内 (BSP board support package)

编译器提供网站下载 https://www.linaro.org/latest/downloads/

arm 嵌入式设备 交叉编译器提供组织

也可以自己制作

crosstool-ng 工具 可以生成 交叉编译器

3.1 交叉编译器的配置

1. 上传gcc文件压缩包

2. 解压缩

linux@ubuntu:~$ tar xvf gcc-4.6.4.tar.xz

3. ~/gcc-4.6.4/bin/arm-none-linux-gnueabi-gcc ./1.c
是可以编译的,但是不方便  每次要找 gcc的路径

4. sudo vi /etc/bash.bashrc

在最后一行追加

export PATH=$PATH:/home/linux/gcc-4.6.4/bin

注意点  是 $ 美元 不是 s
bin 后面不要加斜杠
上面64行 fi 不能乱改

5. source /etc/bash.bashrc
类似于 改完参数之后点应用

6. 输入 arm-n tab键能补齐

arm-none-linux-gnueabi-

7. 或者输入  arm-none-linux-gnueabi-gcc -v
能显示版本信息即可

3.2 常见命令

readelf

-h 显示 elf 头部信息
-s 显示段信息

size

计算各个段的大小

nm

显示符号

strip

删除符号 --》 精简文件 --》嵌入式必须

objdump

反汇编 -D

objcopy

拷贝或转换 目标文件 拷贝二进制 -O binary 注: 大写O

4 环境搭建

BootLoader: 本身在 板子的emmc内部 已烧写完毕

内核文件 uImage 是通过tftp传输到内存地址的 exyons4412-fs4412.dtb 设备树文件 也是tftp

根文件系统 /rootfs是通过 nfs的方式 远程同步的(挂载)

4.1 tftp 和 nfs

tftp 简单文件传输协议 udp

重启大法:

sudo service tftp-hpa restart

nfs 网络文件系统 (无盘工作站)

4.2 ubbot 常用命令

先在设备管理器里面看一下端口是几 putty 选 serial 波特率 115200 com几写好 左下角流控制设置为 none 保存

注意拨码开关

011x 是emmc上启动 uboot 100x 是sd卡 启动uboot

上电 敲任意键暂停

如果bootcmd内有内容 则会倒计时等待 不敲任意键则会执行 bootcmd

setenv bootcmd
saveenv

显示 uboot支持的命令 (不是linux的)

(1)help + 命令 类似于man的作用

(2)printenv 打印所有的环境信息

(3)setenv

Usage:
setenv [-f] name value ...
- [forcibly] set environment variable 'name' to 'value ...'
setenv [-f] name
- [forcibly] delete environment variable 'name'

setenv + 变量名 + 值 则设置这个值 只加变量名 则清空这个值

(4)saveenv

(5) md
memory display 显示内存的内容

md + 地址(16进制) (6)mm nm6 mm 修改后自增地址 (要算) nm 修改后 地址不变

LED3   

gpx1_0   GPX1CON  输入输出模式

0x1100_0C20   [3:0]  0x1 = Output

(1)  md 0x11000c20

(2)  nm 0x11000c20
--》0x1
--》 q
--》  md 0x11000c20  验证

dat      GPX1DAT  实际填入的数值
0x1100_0C24   0x1
(3)   nm 0x11000c24  
--> 0x1  亮
--> 0x0  灭

4.3 挂板子

设置板子和Ubuntu的 ip地址

建议 Ubuntu 设置为 192.168.2.200 右上角标记 新建一个 取名叫board 填入 192.168.2.200 255.255.255.0 192.168.2.1 保存

板子设置为  192.168.2.100
网关gateway 192.168.2.1
netmask 255.255.255.0
板子上:

ipaddr 板子的地址
serverip 是Ubuntu的地址

(1)
setenv ipaddr 192.168.2.100  (251)
setenv serverip 192.168.2.200 (250)
setenv gatewayip 192.168.2.1
saveenv

(2)
ping 192.168.2.200

看到alive 说明成功

--》 网线没插
--》 虚拟机的地址不是这个
--》 虚拟机的网络设置不是桥接 而是 nat
--》 wifi开着
--》 网线有问题


(3)tftp

Usage:
tftpboot 内存地址 (serverip地址,可忽略)文件名

在Ubuntu上 拷贝  uImage 和exyons4412-fs4412.dtb
到 /tftpboot

(1)sudo cp ./uImage /tftpboot
(2)sudo cp ./exyons4412-fs4412.dtb /tftpboot


板子上
(3)tftp 41000000 uImage
(4)tftp 42000000 exynos4412-fs4412.dtb
(5)bootm 41000000 - 42000000

bootm 内核 设备树 文件系统(ramdisk)

bootcmd 可以 将连续的操作保存着,如果bootcmd 不是缺省 则 延时会启用

setenv bootcmd tftp 41000000 uImage \; tftp 42000000 exynos4412-fs4412.dtb \; bootm 41000000 - 42000000

注意反斜杠  和 41000000  42000000 之间的 -号 两边加空格

saveenv

此时完成上面操作会出错

VFS: Cannot open root device “(null)” or unknown-block(0,0):

还差 nfs 根文件系统没挂上

bootargs 需要设置 bootargs 其实是 uboot给内核的传参

bootargs 几个要素: (1) root=?什么类型 nand启动 还是emmc启动 还是其他网络启动呢??

root=/dev/nfs   指定了  使用是 nfs方式

(2)nfsroot 在哪里? nfsroot=ip:/路径 读写

设置前缺认在不在这个目录

nfsroot=192.168.2.200:/source/rootfs rw

(3) 第一个启动的程序是谁

init=/linuxrc   第一个程序

(4) 从哪里显示

console=ttySAC2,115200

(5) 板子的ip地址

ip=192.168.2.100

(4)和(5)的原因是 uboot完成启动内核后,结束了工作,不存在了,所以串口输出 和 ip地址需要通过传参的方式发给内核

setenv bootargs root=/dev/nfs nfsroot=192.168.2.200:/source/rootfs rw console=ttySAC2,115200 init=/linuxrc ip=192.168.2.100

rm: can’t remove ‘hehe’: Permission denied

sudo chmod 777 /source/rootfs

作业:

挂板子成功,编译一个helloworld 在板子上运行

 

 

系统移植第二天

markdown

Bootloader

(面试)u-boot 和 bootloader 之间的区别 bootloader中的一种

讯为itop 友善之臂 u-boot无资料

启动引导程序

http://www.denx.de/wiki/U-Boot/Documentation ftp://ftp.denx.de/pub/u-boot

不是越新越好 最合适的版本:u-boot-2013.01.tar.bz2

目录结构

平台相关:arch board

我们参考的板子是 origen 参考板 评估板

平台无关: common uboot的命令源码 include lib 等

编译u-boot

make [board_name]_config
make origen_config

make fs4412_config

错误:

  1 linux@ubuntu:~/u-boot-2013.01$ make fs4412_config make: *** No rule to make target `fs4412_config’. Stop. make: *** [fs4412_config] Error 1 

解决方法:找到 no rule 在哪里 

 

1 linux@ubuntu:~/u-boot-2013.01$ grep “No rule to make target” * ./* -nF

 

mkconfig:25: echo “make: *** No rule to make target `$2_config’. Stop.” >&2 ./mkconfig:25: echo “make: *** No rule to make target `$2_config’. Stop.” >&2 

 

–》vi mkconfig +25

–》 boards.cfg 里面有匹配规则

 

1)target --》 origen

(2)arch --》 arm

(3)cpu --》 armv7

(4)board name --》 origen

(5)vendor --》 samsung

(6)soc --> exynos

(7)option

 

 

 

操作 复制这一行 在下一行粘贴 把目标改掉

1 linux@ubuntu:~/u-boot-2013.01$ make fs4412_config Configuring for fs4412 board…

下一步 vi Makefile +184 平台 写成arm 交叉编译器: 修改成 

arm-none-linux-gnueabi-

 

下一步 make

错误: from lib/asm-offsets.c:18: /home/linux/u-boot-2013.01/include/config.h:10:28: fatal error: configs/fs4412.h: No such file or directory compilation terminated. make: *** [lib/asm-offsets.s] Error 1

其实是在 include/configs/ 只有origen.h 没有fs4412.h

在顶层目录

 

1  cp ./include/configs/origen.h ./include/configs/fs4412.h
2 
3 操作: 
41)make clean 
52)make fs4412_config
63)make

 

生成了三个文件: u-boot.bin 二进制 u-boot.lds 链接脚本 u-boot.map

三星支持 u-boot.bin的格式

ubuntu:
sudo cp ./u-boot.bin /tftpboot
上电板子
板子上:
1 tftp 41000000 u-boot.bin
2 go 41000000

加载uboot失败

(1) 地址不对 查看 u-boot.map 提示 _start地址在 0x43e00000 (三星定义)

(2)串口没有设置过

u-boot 生成过程

make 出来的 u-boot.bin

–>

439 $(obj)u-boot.bin: $(obj)u-boot 440 $(OBJCOPY) ${OBJCFLAGS} -O binary $< $@ 441 $(BOARD_SIZE_CHECK)

找obj 143 obj := $(OBJTREE)/

找objtree

124 OBJTREE := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))

找 OBJCOPY 找不到了 我们要是用 vi -t

建立tags 索引

ctags -R  

搜索的时候 在这个tags存在的目录下搜索即可

vi -t OBJCOPY config.mk 内 定位到了
150 OBJCOPY = $(CROSS_COMPILE)objcopy

关于链接脚本

 OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
  OUTPUT_ARCH(arm)
  ENTRY(_start)
  SECTIONS
  {
   . = 0x00000000;
   . = ALIGN(4);
   .text :
   {
   __image_copy_start = .;
   arch/arm/cpu/armv7/start.o (.text*)
   *(.text*)
   }
   . = ALIGN(4);
  .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
  . = ALIGN(4);
   .data : {
   *(.data*)
   }

11 arch/arm/cpu/armv7/start.o (.text*)

本质 : u-boot

入口 是 start.S

汇编代码分析

vi ./arch/arm/cpu/armv7/start.S

//1 初始化异常向量表 
40 ldr pc, _undefined_instruction

41 ldr pc, _software_interrupt

42 ldr pc, _prefetch_abort

43 ldr pc, _data_abort

44 ldr pc, _not_used

45 ldr pc, _irq

46 ldr pc, _fiq

//2 设置arm指令集

127 bl save_boot_params

128/*  set the cpu to SVC32 mode */

131 mrs r0, cpsr

132 bic r0, r0, #0x1f

133 orr r0, r0, #0xd3

134 msr cpsr,r0

–》

152 /* the mask ROM code should have PLL and others stable */

153 #ifndef CONFIG_SKIP_LOWLEVEL_INIT 154 bl cpu_init_cp15

155 bl cpu_init_crit

156 #endif

157

158 bl _main

159

//3 cpu_init_cp15 //关mmu(内存管理单元)

280 *

281 * cpu_init_cp15 282 *

283 * Setup CP15 registers (cache, MMU, TLBs). The I-cache is turned on unless

284 * CONFIG_SYS_ICACHE_OFF is defined.
 *
***********************************************************************/

287 ENTRY(cpu_init_cp15)
289 * Invalidate L1 I/D* /
291 mov r0, #0 @ set up for MCR

292 mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs

293 mcr p15, 0, r0, c7, c5, 0 @ invalidate icache

294 mcr p15, 0, r0, c7, c5, 6 @ invalidate BP array

295 mcr p15, 0, r0, c7, c10, 4 @ DSB

296 mcr p15, 0, r0, c7, c5, 4 @ ISB

297 298 / 299 * disable MMU stuff and caches*/
301 mrc p15, 0, r0, c1, c0, 0

302 bic r0, r0, #0x00002000 @ clear bits 13 (–V-)

303 bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM)

304 orr r0, r0, #0x00000002 @ set bit 1 (–A-) Align

305 orr r0, r0, #0x00000800 @ set bit 11 (Z—) BTB

306 #ifdef CONFIG_SYS_ICACHE_OFF

307 bic r0, r0, #0x00001000 @ clear bit 12 (I) I-cache

308 #else

309 orr r0, r0, #0x00001000 @ set bit 12 (I) I-cache

310 #endif

311 mcr p15, 0, r0, c1, c0, 0

312 mov pc, lr @ back to my caller

313 ENDPROC(cpu_init_cp15)

314

//4 cpu_init_crit 其他板级初始化

324 ENTRY(cpu_init_crit)

325 /* * Jump to board specific initialization…  The Mask ROM willhave already initializedbasic memory. Go here to bump up clock rate and handle wake up conditions.330 */ 
331 b lowlevel_init @ go setup pll,mux,memory
332 ENDPROC(cpu_init_crit)

==》 b lowlevel_init

vi -t 是类似效果 在vi 内选中 关键字 ctrl + ‘]’ 前进 ctrl + ‘t’ 后退

追出来的第五十八个 跟origen 有关

81 /* init system clock /

82 bl system_clock_init
84 / Memory initialize /

85 bl mem_ctrl_asm_init
 88 / *for UART */

89 bl uart_asm_init

90 bl tzpc_init

91 pop {pc} 初始化话 时钟 初始化 内存 初始化串口

找 _main 在start.s ctrl +]了 _main 选择了第一个arm平台 相关的 .s内 找到了真正的main

98 /* 99 * Set up initial C runtime environment and callboard_init_f(0). */

初始化 c运行的环境
166 /* call board_init_r / 167 ldr pc, =board_init_r / this is auto-relocated! */

 

调用了 board_init_r arch/arm/lib/board.c 板级相关的信息

运行在43e00000 不行的原因

涉及到一个 trustzone

 让u-boot 起来 屏蔽掉 tsz lowlevel_init.s (58那个) 

 

88 /* for UART */ 
89 bl uart_asm_init
90 // bl tzpc_init 
91 pop {pc}

 

 

 

三星的真实

Exynos 4412 SCP has 64 KB ROM (iROM) and 256 KB SRAM (iRAM) as internal memory.

内置 64k的只读存储器 irom 256k 内存 iram

The boot loader is comprises the first and the second boot loaders. The characteristics of these boot loaders are:  iROM: It is a small and simple code to initiate SOC. It is implemented on internal ROM of SOC.  First boot loader (BL1): It is chip-specific and stored in external memory device.  Second boot loader (BL2): It is platform-specific and stored in external memory device. User should build and store this in an external memory device. It is not provided by Samsung

三星管 内部的 iROM 和 启动代码第一段 BL1 芯片相关

不管 BL2 平台相关

 iROM is placed in internal 64 KB ROM. It initializes basic system functions such as clock and stack.  iROM loads BL1 image from a specific booting device to internal 256 KB SRAM. The booting device is selected by Operating Mode (OM) pins. According to the secure boot key values, iROM may do an integrity check on BL1 image.  BL1 initializes system clock, and DRAM controller. After initializing DRAM controller, it loads OS image from the booting device to DRAM. According to the secure boot key values, BL1 can do an integrity check on the OS image.  After the booting completes, BL1 jumps to the operating system.

 

系统移植第三天

【1】u-boot

(1)uboot 是bootloader的一种

(2)uboot 作用: 初始化硬件,引导内核启动,给内核传参

(3)【面试题】你所使用的uboot的启动流程

宏观: 上电之后,4412内的irom代码(固化代码,无源码 bl0)将启动,从OM开关选择从 emmc或者sd卡启动; 接下来 将从只读存储器(emmc)读取 bl1(加载,验证,解密)到iram, 运行bl1,bl1会去读取 bl2(14k是u-boot.bin的一部分)到iram;最后再加载 u-boot.bin到内存(dram)有时候,又称之为 bl3。

具体:分两个阶段: 第一段: 初始化异常向量表 关中断 关mmu 初始化系统时钟 初始化内存 初始化串口 清bss段

第二阶段: 进一步初始化外围设备

u-boot的网卡移植

使用srom的方式 挂载了 dm9000;只要uboot阶段 能识别网卡 就能使用 ping 等命令

自制u-boot 出现错误:
ORIGEN # ping 192.168.2.250
Unknown command 'ping' - try 'help'
思考:
(1)common的目录 然而并没有  cmd_ping的源码  
(2)grep "ping" * -n 看不出来 通过百度 找到 do_ping
  vi -t do_ping 找到函数描述
 276 #if defined(CONFIG_CMD_PING)
说明 该函数 依赖于 
 defined(CONFIG_CMD_PING)
通过追踪 宏 发现定义位置 都在板子的头文件里
1 vsp ./include/configs/fs4412.h
2 85 #undef CONFIG_CMD_PING  
85 #undef CONFIG_CMD_PING  发现需要 改成 #define 重新 编译 (clean config make)
得到一对错误
error:/home/linux/u-boot-2013.01/net/ping.c:69: undefined reference to `eth_halt'
说明其他位置也要打开某些 宏的定义
90 #undef CONFIG_CMD_NET
修改成define
原因:网卡没初始化,或者设置呢
vi /arch/arm/lib/board.c  
(看到很多外设的初始化信息函数)
665 #if defined(CONFIG_CMD_NET)
666     puts("Net:   ");
667     eth_initialize(gd->bd);
668 #if defined(CONFIG_RESET_PHY_R)
669     debug("Reset Ethernet PHY\n");
670     reset_phy();
671 #endif
==》 board_eth_init  板子初始化 网卡
vi -t 发现  跟板子相关  origen
发现跟三星的其他板子都有 ,咱自己的没有
进入 2410 看一眼
133
134 #ifdef CONFIG_CMD_NET
135 int board_eth_init(bd_t *bis)
136 {
137     int rc = 0;
138 #ifdef CONFIG_CS8900
139     rc = cs8900_initialize(0, CONFIG_CS8900_BASE);
140 #endif
141     return rc;
142 }
143 #endif
意思就是说咱自己要定义一个  函数 fs4412内 定义这个类似的函数 我们在 board/samsung/origen/origen.c 内 依葫芦画瓢
110 int board_eth_init(bd_t *bis)
111 {
112     int rc = 0;
113
114     rc = dm9000_initialize(bis);
115
116     return rc;
117 }
#endif
/home/linux/u-boot-2013.01/board/samsung/origen/origen.c:114: undefined reference to `dm9000_initialize'
说没定义这个初始化的 函数 设备驱动有关系
追 cs8900_initialize 看到了这个东西在 driver下面的 net下面
思考:在dm9000下应该有吧
626 int dm9000_initialize(bd_t *bis)
627 {
628     struct eth_device *dev = &(dm9000_info.netdev);
629
630     /* Load MAC address from EEPROM */
631     dm9000_get_enetaddr(dev);
632
633     dev->init = dm9000_init;
634     dev->halt = dm9000_halt;
635     dev->send = dm9000_send;
636     dev->recv = dm9000_rx;
637     sprintf(dev->name, "dm9000");
638
639     eth_register(dev);
640
641     return 0;
642 }
 
明明有定义 未找到  
         --》 Makefile 或者 .h的锅
在 driver/net/Makefile
38 COBJS-$(CONFIG_DRIVER_DM9000) += dm9000x.o    
也就是说要想正常编译 
          必须定义  CONFIG_DRIVER_DM9000
          在 fs4412.h 倒数第二行 定义 CONFIG_DRIVER_DM9000
#define CONFIG_DRIVER_DM9000
重新编译 又是一堆  
 dm9000x.c: In function 'dm9000_outblk_8bit':
 思考: 网卡驱动有了,网卡在哪个引脚上?
    vi -t DM9000_DATA  宏 发现别人的板子有定义 参考 随便找了一个发现 别人有定义宏
80 #define CONFIG_DM9000_BASE      0x2c000000
81 #define DM9000_IO           CONFIG_DM9000_BASE
82 #define DM9000_DATA         (CONFIG_DM9000_BASE + 0x400)
83 #define CONFIG_DM9000_USE_16BIT     1
84 #define CONFIG_DM9000_NO_SROM       1
85 #undef  CONFIG_DM9000_DEBUG
得看芯片手册了:原理图上的 cs  片选  --》 BUF_Xm0cs1
 
官方手册  19 SROM Controller 章节 有关于这部分的地址
      nGCS[3:0] Output Bank selection signal Xm0CSn_x muxed
    找到 nGCS[3:0] Xm0CSn_x
        ---》
            0x0500_0000 0x0600_0000 16 MB Bank1 of SMC
#define CONFIG_DM9000_BASE      0x5000000
#define DM9000_IO           CONFIG_DM9000_BASE
#define DM9000_DATA         (CONFIG_DM9000_BASE + 4)
再到 fs4412.h 内追加
再编译,上板子
ping 192.168.2.200
第一个error 没设置 ipaddr
setenv ipaddr IP地址即可
第二个error 没 ethaddr
setenv ethaddr 00:01:02:03:04:05
MAC地址要设置  12个16进制数 第一个字节最好为0
 

【2】kernel

www.kernel.org

目录结构

arch 平台相关 其他的 平台无关: 驱动 工具 头文件,文档等

(0) make mrproper
清洁代码 ,比如换平台
make mrproper
 CLEAN   scripts/basic
 CLEAN   scripts/kconfig
 CLEAN   include/config include/generate
(1)交叉编译器 Makefile
198 ARCH        ?= $(SUBARCH)
199 CROSS_COMPILE   ?= $(CONFIG_CROSS_COMPILE:"%"=%)
改成  ------->
ARCH        ?= arm
CROSS_COMPILE   ?= arm-none-linux-gnueabi-
(2)exynos 是我们的默认配置 make exynos_config  找不到
  本质 :
cp ./arch/arm/configs/exynos_defconfig ./.config
  (3)  
make menuconfig
退出要选 yes
"make config"      Plain text interface. 文本对话
"make menuconfig"  Text based color menus, radiolists & dialogs. 基于文本,彩色菜单 选择等
  "make nconfig"     Enhanced text based color menus.
 "make xconfig"     X windows (Qt) based configuration tool.
 "make gconfig"     X windows (Gtk) based configuration tool. 图形界面
(4)
 make uImage -j(cpu个数)
(5)
sudo cp ./arch/arm/boot/uImage /tftboot/
VFS: Cannot open root device "nfs" or unknown-block(0,255): error -6
vi -t EAGAIN
9 #define ENXIO        6  /* No such device or address */

注意: 需要安装两个包

libncurses5-dev (关系到 menuconfig的图形界面)

sudo apt-get install uboot-mkimage uboot-mkimage(关系到uImage的格式)

make menuconfig 窗口不能太小

 

系统移植第四天

内核移植

根据自己的硬件,修改配置,生成对应的内核镜像

目录

平台相关: arch cpu平台的支持

其他重要的目录:

【非常具有参考价值-- 内核man手册】Documentation

【驱动参考宝典】driver 包含 驱动源码 和 Makefile

例如:

linux-3.14/drivers/usb/serial 目录下有 ch341.c 而同目录的Makefile有

16 obj-$(CONFIG_USB_SERIAL_CH341)          += ch341.o

本质: ch341将会编译生成 ch341.o 最后加入到 内核镜像内

同目录内 有一个 Kconfig(菜单)

99 config USB_SERIAL_CH341
100     tristate "USB Winchiphead CH341 Single Port Serial Driver"
101     help
102       Say Y here if you want to use a Winchiphead CH341 single port
103       USB to serial adapter.
104
105       To compile this driver as a module, choose M here: the
106       module will be called ch341.

tristate state 状态 tri- 三个 是,否,模块

对应的右bool 是还是否

–》 重新编译内核

(1)make mrproper

(2)修改Makefile arm arm-none-linux-gnueabi- 注意末尾不要加空格

(3)拷贝 cp ./arh/arm/configs/exynos-defconfig ./.config

  (4) make menuconfig 退出选yes

.config 被修改了

.config 给 Makefile 是用来生成 uImage的菜谱以及注意事项

(5)make uImage -j8 (8是核心数)

生成的文件 uImage 在 arch/arm/boot/uImage

生成过程:

OBJCOPY arch/arm/boot/zImage
 Kernel: arch/arm/boot/zImage is ready
 UIMAGE  arch/arm/boot/uImage
Image Name:   Linux-3.14.0
Created:      Mon Sep 10 09:44:30 2018
Image Type:   ARM Linux Kernel Image (uncompressed)
Data Size:    2756512 Bytes = 2691.91 kB = 2.63 MB
Load Address: 40008000
Entry Point:  40008000

分析得出

Image --》 zImage --》 uImage

压缩的zImage 再加64k的头信息 得 uImage

命令 查看uImage的头信息

mkimage -l ./uImage
Image Name:   Linux-3.14.0
Created:      Mon Sep 10 09:44:30 2018
Image Type:   ARM Linux Kernel Image (uncompressed)未压缩
Data Size:    2756512 Bytes = 2691.91 kB = 2.63 MB
Load Address: 40008000
Entry Point:  40008000

设备树生成

?设备树

什么是设备树

目的:硬件信息与驱动剥离 详细请见驱动部分 make dtbs

 DTC     arch/arm/boot/dts/exynos4210-origen.dtb
 DTC     arch/arm/boot/dts/exynos4210-smdkv310.dtb
 DTC     arch/arm/boot/dts/exynos4210-trats.dtb
 DTC     arch/arm/boot/dts/exynos4210-universal_c210.dtb
 DTC     arch/arm/boot/dts/exynos4412-odroidx.dtb
 DTC     arch/arm/boot/dts/exynos4412-origen.dtb
 DTC     arch/arm/boot/dts/exynos4412-smdk4412.dtb
 DTC     arch/arm/boot/dts/exynos4412-tiny4412.dtb
 DTC     arch/arm/boot/dts/exynos4412-trats2.dtb
 DTC     arch/arm/boot/dts/exynos5250-arndale.dtb
 DTC     arch/arm/boot/dts/exynos5250-smdk5250.dtb
 DTC     arch/arm/boot/dts/exynos5250-snow.dtb
 DTC     arch/arm/boot/dts/exynos5420-arndale-octa.dtb
 DTC     arch/arm/boot/dts/exynos5420-smdk5420.dtb
 DTC     arch/arm/boot/dts/exynos5440-sd5v1.dtb
 DTC     arch/arm/boot/dts/exynos5440-ssdk5440.dtb

dtc 是 device tree complier 编译器 设备树编译器 拿源码 dts source 生成 dtb bin

为啥没有我们的fs4412呢?

  进去找 先复制 exynos4412-origen.dts exynos4412-fs4412.dts

    在顶层目录 make dtbs 依然没现象

      谁的锅? dts 目录下Makefile的问题

vi 这个Makefile 匹配origen 发现没有 +=fs4412

64 dtb-$(CONFIG_ARCH_EXYNOS) += exynos4210-origen.dtb \
65     exynos4210-smdkv310.dtb \
66     exynos4210-trats.dtb \
67     exynos4210-universal_c210.dtb \
68     exynos4412-odroidx.dtb \
69     exynos4412-origen.dtb \
70     exynos4412-smdk4412.dtb \
71     exynos4412-tiny4412.dtb \
72     exynos4412-trats2.dtb \
73     exynos5250-arndale.dtb \
74     exynos5250-smdk5250.dtb \
75     exynos5250-snow.dtb \
76     exynos5420-arndale-octa.dtb \
77     exynos5420-smdk5420.dtb \
复制 origen 一行 改成 fs4412

69     exynos4412-origen.dtb \
70     exynos4412-fs4412.dtb \

再回顶层目录

make dtbs
  DTC     arch/arm/boot/dts/exynos4412-fs4412.dtb

----->生成成功

sudo cp uImage 和设备树到板子运行看看

--------> 发现板子挂了

开始不从 Makefile 和 .config着手 从菜单开始考虑

Linux/arm 3.14.0 Kernel Configuration ──────────────────────────────┐
  │  Arrow keys navigate the menu.  <Enter> selects submenus ---> (or empty submenus ----).           │
  │  Highlighted letters are hotkeys.  Pressing <Y> includes, <N> excludes, <M> modularizes features. │
  │  Press <Esc><Esc> to exit, <?> for Help, </> for Search.  Legend: [*] built-in  [ ] excluded      │
  │  <M> module  < > module capable      

 

y 选中 n 不选 m 是模块

?帮助 esc esc 退出 选择yes 则保存到 .config

/ 查找

我们在 make menuconfig的时候

------>在 device driver –》 character device 查到的东西 来自于目录 ./driver/char/Kconfig

8 config XIEYUXING
 9     bool "XIEYUXING device support"
10     help
11       Say Y here if you want to support the xieyuxing
12       When in doubt, say "N".

从新make menuconfig 在原位置看到

  [ ] XIEYUXING device support (NEW)   

[] 代表双选 Y or N --》 bool

如果我们 输入' /'匹配 XIEYUXING 则查到
Symbol: XIEYUXING [=n]                                                                            
  │ Type  : boolean                                                                                   
  │ Prompt: XIEYUXING device support                                                                  
  │   Location:                                                                                       
  │     -> Device Drivers                                                                             
  │ (1)   -> Character devices                                                                       
  │   Defined at drivers/char/Kconfig:8 

----------->

esc 退出保存后 查看顶层目录的 .config
1118 # Character devices
1119 #
1120 CONFIG_XIEYUXING=y
 

再去 driver/cha/Makefile 查看没有 xxx匹配的规则,说明我们要自己加

obj-$(CONFIG_XIEYUXING)    += xxxx.o

在同目录内 编写 xxx.c

---->再回到顶层目录

make uImage显示

CHK     include/config/kernel.release
CHK     include/generated/uapi/linux/version.h
CHK     include/generated/utsrelease.h
make[1]: `include/generated/mach-types.h' is up to date.
CALL    scripts/checksyscalls.sh
CHK     include/generated/compile.h
CC      drivers/char/xieyuxing.o
drivers/char/xieyuxing.c:1:19: fatal error: stdio.h: No such file or directory
compilation terminated.
make[2]: *** [drivers/char/xieyuxing.o] Error 1
make[1]: *** [drivers/char] Error 2
make[1]: *** Waiting for unfinished jobs....
make: *** [drivers] Error 2
make: *** Waiting for unfinished jobs....
 
CC      drivers/char/xxx.o

说明我们想添加驱动的目的达到了如果我们把char目录下Kconfig的bool修改成 tristate进入 make menuconfig 选m 则顶层目录的 .config

1120 CONFIG_XIEYUXING=m

完成配置 内核 和设备树 实现第一天的效果

原因: 目前挂板子不成功的原因

(1)uImage 没有配置完全 网卡驱动? nfs支持?

(2)设备树? 有没有dm9000的配置?

[1] dm9000

find -name dm9000.c  ./drivers/net/ethernet/davicom/dm9000.c
vi davicom下面的Makefile
有:
5 obj-$(CONFIG_DM9000) += dm9000.o
再去找  顶层目录下 找CONFIG_DM9000
894 # CONFIG_DM9000 is not set
--------->说明我们要去 设置
      make menuconfig 配置就可以了
  在 里面输入 /查找 DM9000
894 # CONFIG_DM9000 is not set
Symbol: DM9000 [=n]                                                                               
 │ Type  : tristate                                                                                  
 │ Prompt: DM9000 support                                                                            
 │   Location:                                                                                       
 │     -> Device Drivers                                                                             
 │       -> Network device support (NETDEVICES [=y])                                                 
 │ (1)     -> Ethernet driver support (ETHERNET [=y])                                                
 │   Defined at drivers/net/ethernet/davicom/Kconfig:5                                               
 │   Depends on: NETDEVICES [=y] && ETHERNET [=y] && (ARM [=y] || BLACKFIN || MIPS || COLDFIRE)      
 │   Selects: CRC32 [=y] && MII [=y]  

 

  按照方法去打开每一个 选项 再拷贝 再运行 错误如下:
[    1.435000] VFS: Cannot open root device "nfs" or unknown-block(0,255): error -6
[    1.440000] Please append a correct "root=" boot option; here are the available partitions:
[    1.450000] Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,255)
  错误中提示 : NFS未配置,rootfs?

网上资料很多 ,但是如何自己思考配置 更多要借助 "/"作用

root -->

Symbol: ROOT_NFS [=n]                                                                                                        
 │ Type  : boolean                                                                                                              
 │ Prompt: Root file system on NFS                                                                                              
 │   Location:                                                                                                                  
 │     -> File systems                                                                                                          
 │ (3)   -> Network File Systems (NETWORK_FILESYSTEMS [=y])                                                                     
 │   Defined at fs/nfs/Kconfig:159                                                                                              
 │   Depends on: NETWORK_FILESYSTEMS [=y] && NFS_FS [=n]=y && IP_PNP [=n]     

然而我们找不到"Root file system on NFS" 我们在依赖上却 IP_PNP 找 IP_PNP

│ Symbol: IP_PNP [=n]                                                                                                          
  │ Type  : boolean                                                                                                              
  │ Prompt: IP: kernel level autoconfiguration                                                                                   
  │   Location:                                                                                                                  
  │     -> Networking support (NET [=y])                                                                                         
  │       -> Networking options                                                                                                 
  │ (1)     -> TCP/IP networking (INET [=y])                                                                                     
  │   Defined at net/ipv4/Kconfig:107                                                                                            
  │   Depends on: NET [=y] && INET [=y]     

选上 kernel level autoconfiguration即可

--------->找 NFS_FS

Symbol: NFS_FS [=n]                                                                                                          
 │ Type  : tristate                                                                                                             
 │ Prompt: NFS client support                                                                                                   
 │   Location:                                                                                                                 
 │     -> File systems                                                                                                          
 │ (1)   -> Network File Systems (NETWORK_FILESYSTEMS [=y])                                                                     
 │   Defined at fs/nfs/Kconfig:1                                                                                                
 │   Depends on: NETWORK_FILESYSTEMS [=y] && INET [=y] && FILE_LOCKING [=y]                                                     
 │   Selects: LOCKD [=n] && SUNRPC [=n] && NFS_ACL_SUPPORT [=n] 

 

配置好后 还有 NFS_ACL_SUPPORT没有
又依赖
NFS_V3_ACL [=n] || NFSD [=n]
Symbol: NFS_V3_ACL [=n]                                                                                                                           
 │ Type  : boolean                                                                                                                                   
 │ Prompt: NFS client support for the NFSv3 ACL protocol extension                                                                                   
 │   Location:                                                                                                                                       
 │     -> File systems                                                                                                                               
 │       -> Network File Systems (NETWORK_FILESYSTEMS [=y])                                                                                         
 │         -> NFS client support (NFS_FS [=y])                                                                                                       
 │ (2)       -> NFS client support for NFS version 3 (NFS_V3 [=y])                                                                                   
 │   Defined at fs/nfs/Kconfig:52                                                                                                                    
 │   Depends on: NETWORK_FILESYSTEMS [=y] && NFS_V3 [=y]      
下面是百度上的资料:
进入[*] Networking support  --->
 
                         Networking options  --->
 
                            选中[*]   IP: kernel level autoconfiguration
 
 
                            再进入File systems  --->
 
                                       [*] Network File Systems  --->
 
                            选中如下选项:
 
                                   <*>   NFS client support
 
                                   <*>     NFS client support for NFS version 2
 
                                   <*>     NFS client support for NFS version 3
 
                                   [*]       NFS client support for the NFSv3 ACL protocol extension
 
                                   <*>     NFS client support for NFS version 4
 
                                   [*]     Provide swap over NFS support
 
                                   [*]   NFS client support for NFSv4.1
 
                                   [*]     NFS client support for NFSv4.2
 
                                   [*]     NFSv4.1 client support for migration
 
                                   [*]   Root file system on NFS

 

设备树

描述板子的硬件信息 分离设备信息 和驱动

19     model = "Insignal Origen evaluation board based on Exynos4412";
20     compatible = "insignal,origen4412", "samsung,exynos4412";
 

compatible 用于驱动和设备树匹配的子节点:

 22     memory {
 23         reg = <0x40000000 0x40000000>;
 24     };

内存地址 地址信息 <1,2> 1:起始地址 2:偏移大小

我们如何添加 dm9000的信息 到 设备树(dts)里面去呢 ?

(1) 百度 必然要找 fs4412相关的信息 :

dm9000具体怎么设置 ,每一家板子商的寄存器地址都不一样

(2) Documentation 找找看?? 配设备树,-dm9000有关系 猜一下路径

linux-3.14/Documentation/devicetree/bindings/net/ davicom-dm9000.txt
 

查看

 1Davicom DM9000 Fast Ethernet controller
 2
 3 Required properties:
 4 - compatible = "davicom,dm9000";
 5 - reg : physical addresses and sizes of registers, must contain 2 entries:
 6     first entry : address register,
 7     second entry : data register.
 8 - interrupt-parent : interrupt controller to which the device is connected
 9 - interrupts : interrupt specifier specific to interrupt controller
10
11 Optional properties:
12 - local-mac-address : A bytestring of 6 bytes specifying Ethernet MAC address
13     to use (from firmware or bootloader)
14 - davicom,no-eeprom : Configuration EEPROM is not available
15 - davicom,ext-phy : Use external PHY
16
17 Example:
18
19     ethernet@18000000 {
20         compatible = "davicom,dm9000";
21         reg = <0x18000000 0x2 0x18000004 0x2>;
22         interrupt-parent = <&gpn>;
23         interrupts = <7 4>;
24         local-mac-address = [00 00 de ad be ef];
25         davicom,no-eeprom;
26     };

  

手册建议我们要改

ethernet@18000000  名字上的地址也要改
21         reg = <0x18000000 0x2 0x18000004 0x2>;
22         interrupt-parent = <&gpn>;
23         interrupts = <7 4>;

(1)我们要修改寄存器 Bank1 of SMC 片选 cs 1 找到 0x5000000 6个零

地址寄存器:0x5000000 数据寄存器:0x5000004 reg = <0x5000000 0x2 0x5000004 0x2>;

(2) 找中断源 INT 找到 DM9000_IRQ 找到 XEINT6

找到了 GPX0_6 估算 中断父节点 是 gpx0

(3)设置中断值 不太清楚 再去参考别人的

16 #include “exynos4412.dtsi”

参考 linux-3.14/Documentation/devicetree/bindings/interrupt-controller/interrupts.txt

13   Example:
 14     interrupt-parent = <&intc1>;
 15     interrupts = <5 0>, <6 0>;
 
 69   b) two cells
  70   ------------
  71   The #interrupt-cells property is set to 2 and the first cell defines the
  72   index of the interrupt within the controller, while the second cell is used
  73   to specify any of the following flags:
  74     - bits[3:0] trigger type and level flags
  75         1 = low-to-high edge triggered
  76         2 = high-to-low edge triggered
  77         4 = active high level-sensitive
  78         8 = active low level-sensitive
 
找到两个值怎么填  第几组 <6 4>

修改完之后从新生成

22     ethernet@5000000 {
23         compatible = "davicom,dm9000";
24         reg = <0x5000000 0x2 0x5000004 0x2>;
25         interrupt-parent = <&gpx0>;
26         interrupts = <6 4>;
27         local-mac-address = [00 00 de ad be ef];
28         davicom,no-eeprom;
29     };

------->然后

make dtbs
  DTC     arch/arm/boot/dts/exynos4412-fs4412.dtb
  sudo cp arch/arm/boot/uImage  /tftpboot/
  sudo cp arch/arm/boot/dts/exynos4412-fs4412.dtb /tftpboot/

 

在 driver/clk/clk.code
529 static bool clk_ignore_unused;
修改为 static bool clk_ignore_unused=true;

-------->最后再 make uImage 拷贝到 /tftpboot 下

 

系统移植第五天

摄像头移植

(1)内核中的摄像头驱动 zc3xx 中星微

find . -name zc3xx.c  ./drivers/media/usb/gspca/zc3xx.c

 

make menuconfig

 

 

 

┌───────────────────────────────────────── Search Results ──────────────────────────────────────────┐

│ Symbol: USB_GSPCA_ZC3XX [=n]                                                                      
│ Type  : tristate                                                                                  
│ Prompt: ZC3XX USB Camera Driver                                                                   
│   Location:                                                                                       
│     -> Device Drivers                                                                             
│ (1)   -> Multimedia support (MEDIA_SUPPORT [=n])                                                  
│         -> Media USB Adapters (MEDIA_USB_SUPPORT [=n])                                            
│           -> GSPCA based webcams (USB_GSPCA [=n])                                                 
│   Defined at drivers/media/usb/gspca/Kconfig:425                                                  
│   Depends on: USB [=y] && MEDIA_SUPPORT [=n] && MEDIA_USB_SUPPORT [=n] && MEDIA_CAMERA_SUPPORT [= n
 

 

发现最后一项gspca based 没这个菜单

Symbol: USB_GSPCA [=n]                                                                                                                                                  
 │ Type  : tristate                                                                                                                                                        
 │ Prompt: GSPCA based webcams                                                                                                                                             
 │   Location:                                                                                                                                                             
 │     -> Device Drivers                                                                                                                                                   
 │       -> Multimedia support (MEDIA_SUPPORT [=y])                                                                                                                        
 │ (1)     -> Media USB Adapters (MEDIA_USB_SUPPORT [=y])                                                                                                                  
 │   Defined at drivers/media/usb/gspca/Kconfig:1                                                                                                                          
 │   Depends on: USB [=y] && MEDIA_SUPPORT [=y] && MEDIA_USB_SUPPORT [=y] && MEDIA_CAMERA_SUPPORT [=n] && VIDEO_V4L2 [=n]           

 

  找到 meida_camera_support
Symbol: MEDIA_CAMERA_SUPPORT [=n]                                                                                                                 
│ Type  : boolean                                                                                                                                   
│ Prompt: Cameras/video grabbers support                                                                                                            
│   Location:                                                                                                                                       
│     -> Device Drivers                                                                                                                             
│ (1)   -> Multimedia support (MEDIA_SUPPORT [=y])                                                                                                  
│   Defined at drivers/media/Kconfig:21                                                                                                             
│   Depends on: MEDIA_SUPPORT [=y]  

 

          

(2)设备树

–》 配置usb的设备树 –》重新dtc生成 设备树文件 具体参考《系统移植实验手册》

然而板子没起来

usb支持 和 摄像头的一个UVC配置

make menuconfig
Device Drivers  --->
[*] USB support  --->
<*>     EHCI HCD (USB 2.0) support
<*>     EHCI support for Samsung S5P/EXYNOS SoC Series
<*>     USB Mass Storage support
<*>   USB3503 HSIC to USB20 Driver
USB Physical Layer drivers  --->
<*> Samsung USB 2.0 PHY controller Driver
SCSI device support  --->
<*> SCSI device support
<*> SCSI disk support
      <*> SCSI generic support

 

菜单:
Device Drivers
-》Multimedia support
    -》Media USB Adapters
--- Media USB Adapters                                                                        │ │
│ │             *** Webcam devices ***                                                     │ │
│ │       <*>   USB Video Class (UVC)                                                  │ │
│ │       [*]     UVC input events device support     

 

       

验证结果:

ls /dev/video0 说明成功

 

根文件系统 (rootfs)

1.容易混淆的概念

文件系统 : 虚拟文件系统 (VFS)

磁盘文件系统: fat fat32 ntfs 根文件系统: 操作系统锁必须的 工具,库,目录等。

概念:根文件系统(root filesystem)是存放运行、维护系统所必须的各种工具软件、库文件、脚本、配置文件和其他特殊文件的地方,也可以安装各种软件包

实质 --》 rootfs.tar.gz

busybox的作用:生成 目标机的rootfs

目的缘由: 嵌入式设备普遍存储器不大,功能单一;

信念: 只用 20%的代码 实现 80%的功能

移植rootfs

busybox

(1) tar xvf busybox
 
(2) cd
 
(3) make menuconfig

(4) 修改交叉编译器

Location:                                                                                          │
 │     -> Busybox Settings                                                                │
 │       -> Build Options     
BusyBox 1.22.1 Configuration

 

进入输入 然后tab到ok上保存
(arm-none-linux-gnueabi-) Cross Compiler prefix
(5) make
(6) make install
 
结果:
linux@ubuntu:~/busybox-1.22.1$ cd _install/
linux@ubuntu:~/busybox-1.22.1/_install$ ls
bin  linuxrc  sbin  usr

 

  比较目录,没有的在_install内加
bin  linuxrc  sbin  usr
 
bin  dev  etc  lib  linuxrc  mjpg  mnt  proc  root  sbin  sys  tmp  usr  var  www
 
mkdir dev etc lib mnt proc root sys tmp var

 

回到顶层,然后更改_install目录名

mv ./_install  rootfs

 

 
给以前的备份一下:  
mv /source/rootfs backup_rootfs
 
mv ./rootfs /source/
出现错误 Failed to execute /linuxrc (error -2).

缺少依赖库,交叉编译器里有

cp  -arf ~/gcc-4.6.4/arm-arm1176jzfssf-linux-gnueabi/lib/* /source/rootfs/lib/

能挂载 又出错误 :缺少文件夹

can’t run ‘/etc/init.d/rcS’: No such file or directory

 

cd /source/rootfs/etc/
mkdir init.d
touch rcS

又有 can’t run ‘/etc/init.d/rcS’: Permission denied

  给权限------------->chmod 77 /source/rootfs/etc/init.d/rcS

移植sqlite3

想法?

x86 : dpkg -i *deb
 
 sudo apt-get install libsqlite3-dev sqlite3 libsqlite3-0

sqlite3 是执行软件 libsqlite3 是执行库 libsqlite3-dev 含头文件的

没有arm格式,需要移植arm格式的

(1)下载 https://www.sqlite.org/2018/sqlite-autoconf-3240000.tar.gz
2)上传到虚拟机 解压缩    tar xvf + 源码包
 
(3) 进入目录
 
./configure --host=arm-none-linux-gnueabi --prefix=/home/linux/sqlite-arm
 
前面的是交叉编译器
后面的是结果生成的目录
 
(3)vi Makefile
修改一个 -DPACKAGE_STRING=\"sqlite\ 3.24.0\"3.24.0 前面的 '\'和空格删掉
4)make

(5)make install
 
 
(6)拷贝 sqlite-arm下的bin下的sqlite3 到板子上去
 
板子上电 执行sqlite 是可以的
 
(7) sqlite-arm 下的 lib的库 拷贝过去 给 程序用
 
sudo cp libsqlite3.so.0.8.6  /source/rootfs/lib/
 
库的软链接 名字叫 libsqlite3.so.0
 
cd /source/rootfs/lib
ln -s libsqlite3.so.0.8.6  libsqlite3.so.0
 
测试:

arm-none-linux-gnueabi-gcc sqlite_test.c -lsqlite3
sqlite_test.c:6:21: fatal error: sqlite3.h: No such file or directory
compilation terminated.
 
 
找不到头文件
-I 头文件位置
-L 动态库位置
 
arm-none-linux-gnueabi-gcc sqlite_test.c -lsqlite3 -I /home/linux/sqlite-arm/include/ -L /home/linux/sqlite-arm/lib/

 

转载于:https://www.cnblogs.com/hslixiqian/p/9629688.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值