ti processor sdk linux am335x evm /bin/setup-uboot-env.sh hacking
#!/bin/sh #
# ti processor sdk linux am335x evm /bin/setup-uboot-env.sh hacking
# 说明:
# 本文主要对TI的sdk中的setup-uboot-env.sh脚本进行解读,是为了了解
# 其工作机制,另外了解到minicom原来是可以用脚本来和开发板进行交互的。
#
# -- 深圳 南山平山村 曾剑锋 # This distribution contains contributions or derivatives under copyright
# as follows:
#
# Copyright (c) , Texas Instruments Incorporated
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# - Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# - Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# - Neither the name of Texas Instruments nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. cwd=`dirname $`
. $cwd/common.sh do_expect() {
echo "expect {" >> $
check_status
echo " $1" >> $
check_status
echo "}" >> $
check_status
echo $ >> $
check_status
echo >> $
} echo
echo "--------------------------------------------------------------------------------"
echo "This step will set up the U-Boot variables for booting the EVM."
echo #
# Qt@aplex:~/ti-processor-sdk-linux-am335x-evm-01.00.00.00$ ifconfig | grep 'inet addr:'| grep -v '127.0.0.1' | cut -d: -f2 | awk '{ print $1 }'
# 192.168.236.130
# Qt@aplex:~/ti-processor-sdk-linux-am335x-evm-01.00.00.00$ ifconfig | grep 'inet addr:'
# inet addr:192.168.236.130 Bcast:192.168.236.255 Mask:255.255.255.0
# inet addr:127.0.0.1 Mask:255.0.0.0
# Qt@aplex:~/ti-processor-sdk-linux-am335x-evm-01.00.00.00$ ifconfig
# eth0 Link encap:Ethernet HWaddr :0c:::ca:
# inet addr:192.168.236.130 Bcast:192.168.236.255 Mask:255.255.255.0
# inet6 addr: fe80::20c:29ff:fe52:ca98/ Scope:Link
# UP BROADCAST RUNNING MULTICAST MTU: Metric:
# RX packets: errors: dropped: overruns: frame:
# TX packets: errors: dropped: overruns: carrier:
# collisions: txqueuelen:
# RX bytes: (6.4 MB) TX bytes: (164.1 KB)
# Interrupt: Base address:0x2024
#
# lo Link encap:Local Loopback
# inet addr:127.0.0.1 Mask:255.0.0.0
# inet6 addr: ::/ Scope:Host
# UP LOOPBACK RUNNING MTU: Metric:
# RX packets: errors: dropped: overruns: frame:
# TX packets: errors: dropped: overruns: carrier:
# collisions: txqueuelen:
# RX bytes: (7.0 KB) TX bytes: (7.0 KB)
#
# Qt@aplex:~/ti-processor-sdk-linux-am335x-evm-01.00.00.00$ ifconfig | grep 'inet addr:'
# inet addr:192.168.236.130 Bcast:192.168.236.255 Mask:255.255.255.0
# inet addr:127.0.0.1 Mask:255.0.0.0
# Qt@aplex:~/ti-processor-sdk-linux-am335x-evm-01.00.00.00$ ifconfig | grep 'inet addr:'| grep -v '127.0.0.1'
# inet addr:192.168.236.130 Bcast:192.168.236.255 Mask:255.255.255.0
# Qt@aplex:~/ti-processor-sdk-linux-am335x-evm-01.00.00.00$ ifconfig | grep 'inet addr:'| grep -v '127.0.0.1' | cut -d: -f2
# 192.168.236.130 Bcast
# Qt@aplex:~/ti-processor-sdk-linux-am335x-evm-01.00.00.00$ ifconfig | grep 'inet addr:'| grep -v '127.0.0.1' | cut -d: -f2 | awk '{ print $1 }'
# 192.168.236.130
# Qt@aplex:~/ti-processor-sdk-linux-am335x-evm-01.00.00.00$
#
ipdefault=`ifconfig | grep 'inet addr:'| grep -v '127.0.0.1' | cut -d: -f2 | awk '{ print $1 }'`
# platform=am335x-evm
platform=`grep PLATFORM= $cwd/../Rules.make | cut -d= -f2` echo "Autodetected the following ip address of your host, correct it if necessary"
# 采用自动获取的IP还是手动设置的IP
read -p "[ $ipdefault ] " ip
echo
if [ ! -n "$ip" ]; then
ip=$ipdefault
fi # 设置NFS根文件目录
if [ -f $cwd/../.targetfs ]; then
rootpath=`cat $cwd/../.targetfs`
else
echo "Where is your target filesystem extracted?"
read -p "[ ${HOME}/targetNFS ]" rootpath if [ ! -n "$rootpath" ]; then
rootpath="${HOME}/targetNFS"
fi
echo
fi # 设置kernel相关的变量
kernelimage="zImage-""$platform"".bin"
kernelimagesrc=`ls - $cwd/../board-support/prebuilt-images/$kernelimage`
kernelimagedefault=`basename $kernelimagesrc` # 设置Linux kernel位置,TFTP还是SD card中
echo "Select Linux kernel location:"
echo " 1: TFTP"
echo " 2: SD card"
echo
read -p "[ 1 ] " kernel if [ ! -n "$kernel" ]; then
kernel=""
fi # 设置文件系统路径,NFS还是SD card中
echo
echo "Select root file system location:"
echo " 1: NFS"
echo " 2: SD card"
echo
read -p "[ 1 ] " fs if [ ! -n "$fs" ]; then
fs=""
fi if [ "$kernel" -eq "" ]; then
echo
echo "Available kernel images in /tftproot:"
for file in /tftpboot/*; do
basefile=`basename $file`
echo " $basefile"
done
echo
echo "Which kernel image do you want to boot from TFTP?"
read -p "[ $kernelimagedefault ] " kernelimage if [ ! -n "$kernelimage" ]; then
kernelimage=$kernelimagedefault
fi
fi isBB="n"
isBBBlack="n"
isBBrevA3="n"
configBB="n" # 个人认为这里是Ubuntu PC机已经和开发板通过USB连接了,然后这里就可以自动检测出开发板的类型。
check_for_beaglebone() {
# First check if there is a rev A3 board which uses the custom VID/PID
# combination
lsusb -d 0403:a6d0 > /dev/null if [ "$?" = "0" ]
then
# We found a beaglebone
isBB="y"
isBBrevA3="y"
return
fi # Now let's check for a standard VID/PID like newer BeagleBones have
sudo lsusb -vv -d 0403:6010 | grep "Product" | grep "BeagleBone" > /dev/null if [ "$?" = "0" ]
then
isBB="y"
return
fi # Now let's check for EVM-SK
sudo lsusb -vv -d 0403:6010 | grep "Product" | grep "EVM-SK" > /dev/null if [ "$?" = "0" ]
then
isBB="y"
return
fi # Now let's check for BeagleBone Black
sudo lsusb -vv -d 0403:6001 > /dev/null if [ "$?" = "0" ]
then
isBB="y"
isBBBlack="y"
return
fi } #
# Qt@aplex:~/ti-processor-sdk-linux-am335x-evm-01.00.00.00/bin$ ls -al setupBoard.minicom
# -rw-rw-r-- 1 Qt Qt 601 Aug 14 2015 setupBoard.minicom
# Qt@aplex:~/ti-processor-sdk-linux-am335x-evm-01.00.00.00/bin$ cat setupBoard.minicom
# timeout 300
# verbose on
# expect {
# "stop autoboot:"
# }
# send " "
#
# expect {
# "U-Boot#"
# }
# send "env default -f -a"
#
# expect {
# "U-Boot#"
# }
# send "saveenv"
#
# expect {
# "U-Boot#"
# }
# send "reset"
#
# expect {
# "stop autoboot:"
# }
# send " "
#
# expect {
# "U-Boot#"
# }
# send "setenv ip_method none"
#
# expect {
# "U-Boot#"
# }
# send "setenv bootfile zImage"
#
# expect {
# "U-Boot#"
# }
# send setenv bootcmd 'mmc rescan; run loadimage; run findfdt; run loadfdt; run mmcargs; bootz ${loadaddr} - ${fdtaddr}'
#
# expect {
# "U-Boot#"
# }
# send "saveenv"
#
# expect {
# "U-Boot#"
# }
# send "boot"
#
# ! killall -s SIGHUP minicom
# Qt@aplex:~/ti-processor-sdk-linux-am335x-evm-01.00.00.00/bin$ echo "timeout 300" > $cwd/setupBoard.minicom
echo "verbose on" >> $cwd/setupBoard.minicom do_expect "\"stop autoboot:\"" "send \" \"" $cwd/setupBoard.minicom
do_expect "\"U-Boot#\"" "send \"env default -f -a\"" $cwd/setupBoard.minicom
do_expect "\"U-Boot#\"" "send \"saveenv\"" $cwd/setupBoard.minicom
do_expect "\"U-Boot#\"" "send \"reset\"" $cwd/setupBoard.minicom
do_expect "\"stop autoboot:\"" "send \" \"" $cwd/setupBoard.minicom # 根据前面的配置,将相关参数设置到setupBoard.minicom对应的文件里。
if [ "$kernel" -eq "1" ]; then
if [ "$fs" -eq "1" ]; then
#TFTP and NFS Boot
do_expect "\"U-Boot#\"" "send \"setenv serverip $ip\"" $cwd/setupBoard.minicom
do_expect "\"U-Boot#\"" "send setenv rootpath '$rootpath'" $cwd/setupBoard.minicom
do_expect "\"U-Boot#\"" "send \"setenv bootfile $kernelimage\"" $cwd/setupBoard.minicom
do_expect "\"U-Boot#\"" "send \"setenv ip_method dhcp\"" $cwd/setupBoard.minicom
do_expect "\"U-Boot#\"" "send setenv bootcmd 'run findfdt; setenv autoload no;dhcp ;tftp \${loadaddr} $kernelimage; tftp \${fdtaddr} \${fdtfile}; run netargs; bootz \${loadaddr} - \${fdtaddr}'" $cwd/setupBoard.minicom
do_expect "\"U-Boot#\"" "send \"saveenv\"" $cwd/setupBoard.minicom
do_expect "\"U-Boot#\"" "send \"boot\"" $cwd/setupBoard.minicom
else
#TFTP and SD Boot
do_expect "\"U-Boot#\"" "send \"setenv serverip $ip\"" $cwd/setupBoard.minicom
do_expect "\"U-Boot#\"" "send \"setenv bootfile $kernelimage\"" $cwd/setupBoard.minicom
do_expect "\"U-Boot#\"" "send \"setenv ip_method none\"" $cwd/setupBoard.minicom
do_expect "\"U-Boot#\"" "send setenv bootcmd 'run findfdt; setenv autoload no; dhcp ; tftp \${loadaddr} $kernelimage; tftp \${fdtaddr} \${fdtfile}; run mmcargs; bootz \${loadaddr} - \${fdtaddr}'" $cwd/setupBoard.minicom
do_expect "\"U-Boot#\"" "send \"saveenv\"" $cwd/setupBoard.minicom
do_expect "\"U-Boot#\"" "send \"boot\"" $cwd/setupBoard.minicom
fi
else
if [ "$fs" -eq "1" ]; then
#SD and NFS Boot
do_expect "\"U-Boot#\"" "send \"setenv serverip $ip\"" $cwd/setupBoard.minicom
do_expect "\"U-Boot#\"" "send setenv rootpath '$rootpath'" $cwd/setupBoard.minicom
do_expect "\"U-Boot#\"" "send \"setenv bootfile zImage\"" $cwd/setupBoard.minicom
do_expect "\"U-Boot#\"" "send \"setenv ip_method dhcp\"" $cwd/setupBoard.minicom
do_expect "\"U-Boot#\"" "send setenv bootcmd 'setenv autoload no; mmc rescan; run loadimage; run findfdt; run loadfdt; run netargs; bootz \${loadaddr} - \${fdtaddr}'" $cwd/setupBoard.minicom
do_expect "\"U-Boot#\"" "send \"saveenv\"" $cwd/setupBoard.minicom
do_expect "\"U-Boot#\"" "send \"boot\"" $cwd/setupBoard.minicom
else
#SD and SD boot.
do_expect "\"U-Boot#\"" "send \"setenv ip_method none\"" $cwd/setupBoard.minicom
do_expect "\"U-Boot#\"" "send \"setenv bootfile zImage\"" $cwd/setupBoard.minicom
do_expect "\"U-Boot#\"" "send setenv bootcmd 'mmc rescan; run loadimage; run findfdt; run loadfdt; run mmcargs; bootz \${loadaddr} - \${fdtaddr}'" $cwd/setupBoard.minicom
do_expect "\"U-Boot#\"" "send \"saveenv\"" $cwd/setupBoard.minicom
do_expect "\"U-Boot#\"" "send \"boot\"" $cwd/setupBoard.minicom fi
fi
echo "! killall -s SIGHUP minicom" >> $cwd/setupBoard.minicom echo "--------------------------------------------------------------------------------"
echo "Would you like to create a minicom script with the above parameters (y/n)?"
read -p "[ y ] " minicom
echo if [ ! -n "$minicom" ]; then
minicom="y"
fi if [ "$minicom" = "y" ]; then echo -n "Successfully wrote "
# readlink是linux系统中一个常用工具,主要用来找出符号链接所指向的位置
readlink -m $cwd/setupBoard.minicom
fi # 检查是否有设备已经连接上来了,如果有设备已经连接,那么设置对应的配置。
while [ yes ]
do
# 检查开发板类型
check_for_beaglebone if [ "$isBB" = "y" ]
then
echo ""
echo "A BeagleBone (Black) or StarterKit board has been detected"
echo "Do you want to configure U-Boot for one of the boards mentioned"
echo "above? An answer of 'n' will configure U-Boot for the"
echo "General Purpose EVM instead"
read -p "(y/n) " configBB
echo if [ "$configBB" = "y" ] || [ "$configBB" = "n" ]
then
break
else
echo "Invalid response"
echo
continue
fi
else
echo ""
echo "No BeagleBone (Black) or StarterKit detected. Assuming"
echo "general purpose evm is being used. Is this correct?"
read -p "(y/n) " validevm
echo ""
if [ "$validevm" = "y" ]
then
configBB="n"
break
else
if [ "$validevm" != "n" ]
then
echo "Invalid response"
echo
continue
fi echo "Please connect the Beaglebone (Black) or StarterKit to the PC"
echo "If your using the StarterKit board make sure it is turned on"
read -p "Press any key to try checking again." temp
fi
fi
done # 如果插入的是BB开发板
if [ "$configBB" = "y" ]
then
ftdiInstalled=`lsmod | grep ftdi_sio`
if [ -z "$ftdiInstalled" ]; then
#Add the ability to regconize the BeagleBone as two serial ports
if [ "$isBBrevA3" = "y" ]
then
echo "Finishing install by adding drivers for Beagle Bone..."
# 添加Beagle Bone USB驱动
sudo modprobe -q ftdi_sio vendor=0x0403 product=0xa6d0 #Create uDev rule
# 添加Beagle Bone udev规则
echo "# Load ftdi_sio driver including support for XDS100v2." > $cwd/99-custom.rules
echo "SYSFS{idVendor}=="0403", SYSFS{idProduct}=="a6d0", \\" >> $cwd/99-custom.rules
echo "RUN+=\"/sbin/modprobe -q ftdi_sio vendor=0x0403 product=0xa6d0\"" >> $cwd/99-custom.rules
sudo cp $cwd/99-custom.rules /etc/udev/rules.d/
rm $cwd/99-custom.rules
else
sudo modprobe -q ftdi_sio
fi
fi #infinite loop to look for board unless user asks to stop
# 循环检查开发板是否连接上PC
while [ yes ]
do
echo "Detecting connection to board..."
loopCount=0
port=`dmesg | grep FTDI | grep "tty" | tail -1 | grep "attached" | awk '{ print $NF }'`
while [ -z "$port" ] && [ "$loopCount" -ne "10" ]
do
#count to 10 and timeout if no connection is found
loopCount=$((loopCount+1)) sleep 1
port=`dmesg | grep FTDI | grep "tty" | tail -1 | grep "attached" | awk '{ print $NF }'`
done #check to see if we actually found a port
if [ -n "$port" ]; then
break;
fi #if we didn't find a port and reached the timeout limit then ask to reconnect
if [ -z "$port" ] && [ "$loopCount" = "10" ]; then
echo ""
echo "Unable to detect which port the board is connected to."
echo "Please reconnect your board."
echo "Press 'y' to attempt to detect your board again or press 'n' to continue..."
read -p "(y/n)" retryBoardDetection
fi #if they choose not to retry, ask user to reboot manually and exit
if [ "$retryBoardDetection" = "n" ]; then
echo ""
echo "Please reboot your board manually and connect using minicom."
exit;
fi
done #Change minicom to accurately reflect the bone
# 重新配饰minicom参数
minicomcfg=${HOME}/.minirc.dfl
echo "pu port /dev/$port
pu baudrate 115200
pu bits 8
pu parity N
pu stopbits 1
pu minit
pu mreset
pu mdialpre
pu mdialsuf
pu mdialpre2
pu mdialsuf2
pu mdialpre3
pu mdialsuf3
pu mconnect
pu mnocon1 NO CARRIER
pu mnocon2 BUSY
pu mnocon3 NO DIALTONE
pu mnocon4 VOICE
pu rtscts No" | tee $minicomcfg > /dev/null
check_status
fi echo "Would you like to run the setup script now (y/n)? For the general purpose evm "
echo "you must now connect the RS-232 cable to your evm now. For the Beaglebone (Black)"
echo "or StarterKit this step should of already have been done. Also connect the ethernet"
echo "cable as described in the Quick Start Guide."
echo "**Important**"
echo "Once answering 'y' on the prompt below you will have 300 seconds to turn on the"
echo "board or if it was already on reboot the board before the setup times out"
echo
echo "After successfully executing this script, your board will be set up. You will be "
echo "able to connect to it by executing 'minicom -w' or if you prefer a windows host"
echo "you can set up Tera Term as explained in the Software Developer's Guide."
echo "If you connect minicom or Tera Term and power cycle the board Linux will boot."
echo
read -p "[ y ] " minicomsetup if [ ! -n "$minicomsetup" ]; then
minicomsetup="y"
fi if [ "$minicomsetup" = "y" ]; then
cd $cwd
# 这里相当于用minicom运行minicom脚本
sudo minicom -w -S setupBoard.minicom
cd -
fi echo "You can manually run minicom in the future with this setup script using: minicom -S $cwd/setupBoard.minicom"
echo "--------------------------------------------------------------------------------"
ti processor sdk linux am335x evm /bin/setup-uboot-env.sh hacking的更多相关文章
- ti processor sdk linux am335x evm /bin/setup-package-install.sh hacking
#!/bin/sh # # ti processor sdk linux am335x evm /bin/setup-package-install.sh hacking # 说明: # 本文主要对T ...
- ti processor sdk linux am335x evm /bin/setup-minicom.sh hacking
#!/bin/sh # # ti processor sdk linux am335x evm /bin/setup-minicom.sh hacking # 说明: # 本文主要对TI的sdk中的s ...
- ti processor sdk linux am335x evm /bin/setup-targetfs-nfs.sh hacking
#!/bin/sh # # ti processor sdk linux am335x evm /bin/setup-targetfs-nfs.sh hacking # 说明: # 本文主要对TI的s ...
- ti processor sdk linux am335x evm /bin/commom.sh hacking
#!/bin/sh # # ti processor sdk linux am335x evm /bin/commom.sh hacking # 说明: # 本文主要对TI的sdk中的common.s ...
- ti processor sdk linux am335x evm /bin/setup-host-check.sh hacking
#!/bin/sh # # ti processor sdk linux am335x evm /bin/setup-host-check.sh hacking # 说明: # 本文主要对TI的sdk ...
- ti processor sdk linux am335x evm /bin/create-sdcard.sh hacking
#!/bin/bash # # ti processor sdk linux am335x evm /bin/create-sdcard.sh hacking # 说明: # 本文主要对TI的sdk中 ...
- ti processor sdk linux am335x evm /bin/unshallow-repositories.sh hacking
#!/bin/bash # # ti processor sdk linux am335x evm /bin/unshallow-repositories.sh hacking # 说明: # 本文主 ...
- ti processor sdk linux am335x evm /bin/setup-tftp.sh hacking
#!/bin/sh # # ti processor sdk linux am335x evm /bin/setup-tftp.sh hacking # 说明: # 本文主要对TI的sdk中的setu ...
- ti processor sdk linux am335x evm setup.sh hacking
#!/bin/sh # # ti processor sdk linux am335x evm setup.sh hacking # 说明: # 本文主要对TI的sdk中的setup.sh脚本进行解读 ...
随机推荐
- php缓存
APC缓存退出舞台,APCU诞生,OPCACHE升级! 对于php5.5以后的新版本开发,使用apc习惯的开发者可能会发现php.5.5以后找不到了这个组件的更新,尤其是中文phper都找不到合适的资 ...
- 【转】最短路&差分约束题集
转自:http://blog.csdn.net/shahdza/article/details/7779273 最短路 [HDU] 1548 A strange lift基础最短路(或bfs)★254 ...
- 剑指offer--面试题4
题目:替换字符串中的空格为“%20”. 说明:在浏览器的地址栏中输入某个网址,在解析过程中会看到类似“%20”的字样,这应该就是网络编程涉及的内容... 该题总体来说比较简单(连我都能想到!),个人认 ...
- 你真的知道css三种存在样式(外联样式、内部样式、内联样式)的区别吗?
css样式在html中有三种存在形态: 内联样式:<div style="display: none"></div> 内部样式: <style> ...
- 【转载】关于typedef的用法总结
不管实在C还是C++代码中,typedef这个词都不少见,当然出现频率较高的还是在C代码中.typedef与#define有些相似,但更多的是不同,特别是在一些复杂的用法上,就完全不同了,看了网上一些 ...
- PhotoshopCS4轻松将PSD分层导出为Png分层
大家在网上都下载过PSD分层素材,有时候想把素材分层导出,以往做法是一层一层导出,可是这样效率太低,其实利用PS自带功能可以轻松导出PNG分层. 我们先打开一个PSD文件. 文件有7个图层,分 ...
- POJ 3461 Oulipo(字符串匹配,KMP算法)
题意:给出几组数据,每组有字符串W和T,问你W在T中出现几次. 思路:字符串长度很大,用KMP算法. 一开始写的是:调用KMP算法查找W在T中是否匹配,若匹配,则个数+1.则接下来T的索引移动相应的距 ...
- POJ2632Crashing Robots
做模拟题做的我直接睡着了,题并不难,就是一个细心的问题,有一些细节问题注意了就差不多了,代码写的精美的一般找错误也好找一些,应该学着些好看的代码 #include<cstdio> #inc ...
- MAC OS JAVA环境变量配置
在 /etc/profile 中 加上这些 #临时提权 sudo su #输入密码 vi /etc/profile #配置JAVA_HOME,此处路径根据自己的版本填写 JAVA_HOME=&quo ...
- *[codility]MaxDoubleSliceSum
https://codility.com/demo/take-sample-test/max_double_slice_sum 两个最大子段和相拼接,从前和从后都扫一遍.注意其中一段可以为0.还有最后 ...