2021/12/25

ref:Inotify Watches Limit

就是在 /etc/sysctl.d/ 下新增一個 10-idea.conf
fs.inotify.max_user_watches = 524288
然後 run
sudo sysctl -p --system

2021/12/23

systemd service delay 40 sec

單純的只是要比原來的啟動時間(dep) delay 40 sec 的話。
有很多作法,timer, target. 最後這個最簡單: 就是用 Sleep
然後要加上
TimeoutStartSec=infinity
這樣就不會出現 Error。
然後加上一個 Pre
ExecStartPre=/bin/sleep 40
這個地方如果有 run script,也可以在 script 中 sleep。

這樣,systemctl start 時,如果在 sleep 中,會看到狀態是 sleep 40

2021/12/21

sshd ClientAliveInterval, ClientAliveMax

sshd 的 config file (sshd_config),有兩項設定:
ClientAliveInterval
ClientAliveMax
是用來偵測/決定 Client 是不是存在的設定。

如果沒有收到任何 Client送來的封包,超過 ClientAliveInterval,就會送一個 null request packet 給 client,提醒他。
如果還是沒有收到,等 ClientAliveInterval 後,再送一次。
這樣一直送超過 ClientAliveMax 次之後,就把這個 Client 連線踢掉。

其中,如果有任一次收到,就重新計數。

所以,如果要 1min timeout 的話,有幾種設法:
ClientAliveInterval 10
ClientAliveMax 5
或是
ClientAliveInterval 60
ClientAliveMax 0
後者的設法,就是不用送 null request 給 client 了,時間到直接切掉。

在 server 端,用 who 可以看到連線的 idle time。
但是這個 idle time 只代表 client 沒有輸入任何 command 的時間(idle)。
跟 沒有收到封包 不一樣。
因為 有些(大部分) ssh client 都會定期送 keepalive 封包給 server。
如果開啟 sshd debug mode (-d)就可以定期看到 (about 30sec)
debug1: Got 100/24 for keepalive
debug1: Got 100/25 for keepalive
debug1: Got 100/26 for keepalive
debug1: Got 100/27 for keepalive
如果收到這格,sshd 就會reset ClientAliveInterval 的計數。
但是,who 不會清調 idle 的計數。
所以,只要 ssh client 維持連線,即使不輸入任何東西,sshd 端 who 的 idle 時間超過了 ClientAliveInterval*(ClientAliveMax+1),一樣不會被切掉。

2021/12/14

official docker support nvidia

真的變得這麼簡單?
基本就照這一篇 的說明,把 install command copy-paste 就可以了。
就是...
加入 nvidia-docker 的 apt repo
apt install nvidia-docker2,重新啟動docker。就可以了。

然後 tesorflow 也提供支援 nvidia-docker 的image

2021/11/22

build static-linked git from source

因為要在自己的 target board 上 run,一個一個找 so copy 太麻煩,所以 build 一版 statis linked version 好了。
先是 configure,新的要自己 build 了,clone 下來後,我選 2.28.1..
make configure
之後,就可以 run configure 了。
git 的 configure 沒有支援 static ,所以是要自己加進 CFLAGS 中。
但是只家 -static 的話,build 還是 fail,說一些 linked library function undefined。
參考這個 link
$ ./configure --without-tcltk --without-curl --without-openssl CFLAGS="${CFLAGS}  -static -static-libgcc" NO_OPENSSL=1
之後就 build 成功了。
build git 需要 gettext。剩下的 make fail 都是 host build package 缺。

這樣build 好的 git 就可以放到 target board 上用了。
target board 有提供 ssh server 連線的話,可以用 git clone ssh:// 這個 link 來 clone。
這時候,就要把剛剛build 好的 folder 中, git-upload-pack 也放到 target board PATH 所在的folder 上。
git clone ssh:// 會叫起 targetboard 上的 git-upload-pack 來做傳輸。

2021/10/30

sdkmanager Error : Exception in thread "main" java.lang.NoClassDefFoundError:

從 studio run sdkmanager OK,但是從 command line run sdkmanager 時出現錯誤:
  dkmanager 
Exception in thread "main" java.lang.NoClassDefFoundError: javax/xml/bind/annotation/XmlSchema
	at com.android.repository.api.SchemaModule$SchemaModuleVersion.(SchemaModule.java:156)
	at com.android.repository.api.SchemaModule.(SchemaModule.java:75)
	at com.android.sdklib.repository.AndroidSdkHandler.(AndroidSdkHandler.java:81)
	at com.android.sdklib.tool.sdkmanager.SdkManagerCli.main(SdkManagerCli.java:73)
	at com.android.sdklib.tool.sdkmanager.SdkManagerCli.main(SdkManagerCli.java:48)
Caused by: java.lang.ClassNotFoundException: javax.xml.bind.annotation.XmlSchema
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
	... 5 more
一堆人說,用studio 的 sdkmanager 安裝 commandline-tools 之後就好了。
結果裝了還是一樣 的錯。

後來發現...
裝了 cmdline-tools 後,裡面也有一個 sdkmanager, run 這個 sdkmanager 就不會有 error 了。
這兩個 sdkmanager 都是 shell script,所以來diff 一下...
cmdline-tools/latest/bin$ diff sdkmanager ../../../tools/bin/sdkmanager 
5c5
< ##  sdkmanager start up script for Linux
---
> ##  sdkmanager start up script for UN*X
31c31
< DEFAULT_JVM_OPTS='-Dcom.android.sdklib.toolsdir=$APP_HOME'
---
> DEFAULT_JVM_OPTS='"-Dcom.android.sdklib.toolsdir=$APP_HOME"'
67c67
< CLASSPATH=$APP_HOME/lib/sdkmanager-classpath.jar
---
> CLASSPATH=$APP_HOME/lib/dvlib-26.0.0-dev.jar:$APP_HOME/lib/jimfs-1.1.jar:$APP_HOME/lib/jsr305-1.3.9.jar:$APP_HOME/lib/repository-26.0.0-dev.jar:
$APP_HOME/lib/j2objc-annotations-1.1.jar:$APP_HOME/lib/layoutlib-api-26.0.0-dev.jar:$APP_HOME/lib/gson-2.3.jar:$APP_HOME/lib/httpcore-4.2.5.jar:
$APP_HOME/lib/commons-logging-1.1.1.jar:$APP_HOME/lib/commons-compress-1.12.jar:$APP_HOME/lib/annotations-26.0.0-dev.jar:
$APP_HOME/lib/error_prone_annotations-2.0.18.jar:$APP_HOME/lib/animal-sniffer-annotations-1.14.jar:$APP_HOME/lib/httpclient-4.2.6.jar:
$APP_HOME/lib/commons-codec-1.6.jar:$APP_HOME/lib/common-26.0.0-dev.jar:$APP_HOME/lib/kxml2-2.3.0.jar:$APP_HOME/lib/httpmime-4.1.jar:
$APP_HOME/lib/annotations-12.0.jar:$APP_HOME/lib/sdklib-26.0.0-dev.jar:$APP_HOME/lib/guava-22.0.jar
果然就是 classpath,猜是 tools/bin 是給 studio 用的,它已經default 有一些 jar 了。
cmdline-tools 的才是給 command line 用的。


其實這是要 Accept Android SDK/tool licenses 時遇到的。
用 sdkmanager --licenses 來 accept 所有 license。
accept 之後,會在 Android/Sdk/License 中出現很多 license 檔。

2021/10/24

tensorflow lite android example, build

lite 的 example 跟 tensor 是合在一起的,所以要整個 clone。

雖然說明說,就用 studio 開啟project 然後 build 就可以了,結果 fail,說 :app task 少一個...wrapper (?)
只好用手動,參考 tools 下的 script,是 call project 下的 gradlew 來 build 的,
奇怪 run build_all_android... 卻沒有 build,手動 run build_android_app.sh + example_path 也一樣,一直說沒有 gradlw。

手動的話,就是到 project 下 run
./gradlew assembleRelease 
有碰到兩個 error,一個說沒有 ANDROID_SDK_ROOT 變數,就 export 一下,指定到 Sdk folder
另外一個說沒有需要的 Ndk 版本 (跟有安裝的不一樣),所以參考這一篇,安裝特定版本的 Ndk 後,就 OK 了。


使用 studio 開啟 'android' folder 是可以 build 的,但是一旦接受建議,把 gradlew 從 4.0 升級到 7.0 之後,就有 error 了。
遵照這一頁:Allow insecure protocols, android gradle,修改的話...
diff --git a/lite/examples/object_detection/android/build.gradle b/lite/examples/object_detection/android/build.gradle
index 8ccd3140..b820d78d 100644
--- a/lite/examples/object_detection/android/build.gradle
+++ b/lite/examples/object_detection/android/build.gradle
@@ -7,7 +7,7 @@ buildscript {
         mavenLocal()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:4.0.0'
+        classpath 'com.android.tools.build:gradle:7.0.3'
         classpath 'de.undercouch:gradle-download-task:4.0.2'
         // NOTE: Do not place your application dependencies here; they belong
         // in the individual module build.gradle files
@@ -19,6 +19,7 @@ allprojects {
         google()
         mavenCentral()
         maven {
+           allowInsecureProtocol = true
             name 'ossrh-snapshot'
             url 'http://oss.sonatype.org/content/repositories/snapshots'
         }
第一個 diff 是 studio upgrade 時改的,第二格是要自己加的。
加完後,重新 build 就 OK了。
.. 但是用 studio build 真的比較久,build 完電腦還當機。
用 command line 比較快。


認真看一下 error message:
Could not resolve all dependencies for configuration ':app:taskApiDebugRuntimeClasspath'.
Using insecure protocols with repositories, without explicit opt-in, is unsupported. 
Switch Maven repository 'ossrh-snapshot(http://oss.sonatype.org/content/repositories/snapshots)' 
to redirect to a secure protocol (like HTTPS) or allow insecure protocols. 
See
allowInsecureProtocol
for more details. 

大部分的 project 都只需要改這些 build.gradle ,有些要改manifest。為了方便,fork 一份到自幾的 github,其中 branch: gradle7 就是為了新版 android studio 的新版 gradle 修改的

2021/10/13

eMMC Life (狀態,壽命)

有一個 tool: mmc-utils。 可以用來讀取 eMMC 的register。

就拉下來 make 就可以,在 x86 跟 ARM (pi ubuntu aarch64) 都 build OK。

然後就可以看
# /data/mmc extcsd read /dev/mmcblk0
=============================================
  Extended CSD rev 1.8 (MMC 5.1)
=============================================

Card Supported Command sets [S_CMD_SET: 0x01]
HPI Features [HPI_FEATURE: 0x01]: implementation based on CMD13
Background operations support [BKOPS_SUPPORT: 0x01]
Max Packet Read Cmd [MAX_PACKED_READS: 0x3f]
Max Packet Write Cmd [MAX_PACKED_WRITES: 0x3f]
Data TAG support [DATA_TAG_SUPPORT: 0x01]
Data TAG Unit Size [TAG_UNIT_SIZE: 0x02]
Tag Resources Size [TAG_RES_SIZE: 0x00]
Context Management Capabilities [CONTEXT_CAPABILITIES: 0x05]
Large Unit Size [LARGE_UNIT_SIZE_M1: 0x07]
Extended partition attribute support [EXT_SUPPORT: 0x03]
Generic CMD6 Timer [GENERIC_CMD6_TIME: 0x0a]
Power off notification [POWER_OFF_LONG_TIME: 0x3c]
Cache Size [CACHE_SIZE] is 65536 KiB
Background operations status [BKOPS_STATUS: 0x00]
1st Initialisation Time after programmed sector [INI_TIMEOUT_AP: 0x1e]
Power class for 52MHz, DDR at 3.6V [PWR_CL_DDR_52_360: 0x00]
Power class for 52MHz, DDR at 1.95V [PWR_CL_DDR_52_195: 0x00]
Power class for 200MHz at 3.6V [PWR_CL_200_360: 0x00]
Power class for 200MHz, at 1.95V [PWR_CL_200_195: 0x00]
Minimum Performance for 8bit at 52MHz in DDR mode:
 [MIN_PERF_DDR_W_8_52: 0x00]
 [MIN_PERF_DDR_R_8_52: 0x00]
TRIM Multiplier [TRIM_MULT: 0x02]
Secure Feature support [SEC_FEATURE_SUPPORT: 0x55]
Boot Information [BOOT_INFO: 0x07]
 Device supports alternative boot method
 Device supports dual data rate during boot
 Device supports high speed timing during boot
Boot partition size [BOOT_SIZE_MULTI: 0x20]
Access size [ACC_SIZE: 0x07]
High-capacity erase unit size [HC_ERASE_GRP_SIZE: 0x01]
 i.e. 512 KiB
High-capacity erase timeout [ERASE_TIMEOUT_MULT: 0x01]
Reliable write sector count [REL_WR_SEC_C: 0x01]
High-capacity W protect group size [HC_WP_GRP_SIZE: 0x10]
 i.e. 8192 KiB
Sleep current (VCC) [S_C_VCC: 0x07]
Sleep current (VCCQ) [S_C_VCCQ: 0x07]
Sleep/awake timeout [S_A_TIMEOUT: 0x11]
Sector Count [SEC_COUNT: 0x00e90000]
 Device is block-addressed
Minimum Write Performance for 8bit:
 [MIN_PERF_W_8_52: 0x00]
 [MIN_PERF_R_8_52: 0x00]
 [MIN_PERF_W_8_26_4_52: 0x00]
 [MIN_PERF_R_8_26_4_52: 0x00]
Minimum Write Performance for 4bit:
 [MIN_PERF_W_4_26: 0x00]
 [MIN_PERF_R_4_26: 0x00]
Power classes registers:
 [PWR_CL_26_360: 0x00]
 [PWR_CL_52_360: 0x00]
 [PWR_CL_26_195: 0x00]
 [PWR_CL_52_195: 0x00]
Partition switching timing [PARTITION_SWITCH_TIME: 0x02]
Out-of-interrupt busy timing [OUT_OF_INTERRUPT_TIME: 0x0a]
I/O Driver Strength [DRIVER_STRENGTH: 0x1f]
Card Type [CARD_TYPE: 0x57]
 HS200 Single Data Rate eMMC @200MHz 1.8VI/O
 HS Dual Data Rate eMMC @52MHz 1.8V or 3VI/O
 HS eMMC @52MHz - at rated device voltage(s)
 HS eMMC @26MHz - at rated device voltage(s)
CSD structure version [CSD_STRUCTURE: 0x02]
Command set [CMD_SET: 0x00]
Command set revision [CMD_SET_REV: 0x00]
Power class [POWER_CLASS: 0x00]
High-speed interface timing [HS_TIMING: 0x02]
Erased memory content [ERASED_MEM_CONT: 0x00]
Boot configuration bytes [PARTITION_CONFIG: 0x08]
 Boot Partition 1 enabled
 No access to boot partition
Boot config protection [BOOT_CONFIG_PROT: 0x00]
Boot bus Conditions [BOOT_BUS_CONDITIONS: 0x0e]
High-density erase group definition [ERASE_GROUP_DEF: 0x01]
Boot write protection status registers [BOOT_WP_STATUS]: 0x00
Boot Area Write protection [BOOT_WP]: 0x00
 Power ro locking: possible
 Permanent ro locking: possible
 ro lock status: not locked
User area write protection register [USER_WP]: 0x00
FW configuration [FW_CONFIG]: 0x00
RPMB Size [RPMB_SIZE_MULT]: 0x04
Write reliability setting register [WR_REL_SET]: 0x1f
 user area: the device protects existing data if a power failure occurs during a write operation
 partition 1: the device protects existing data if a power failure occurs during a write operation
 partition 2: the device protects existing data if a power failure occurs during a write operation
 partition 3: the device protects existing data if a power failure occurs during a write operation
 partition 4: the device protects existing data if a power failure occurs during a write operation
Write reliability parameter register [WR_REL_PARAM]: 0x14
 Device supports the enhanced def. of reliable write
Enable background operations handshake [BKOPS_EN]: 0x00
H/W reset function [RST_N_FUNCTION]: 0x01
HPI management [HPI_MGMT]: 0x01
Partitioning Support [PARTITIONING_SUPPORT]: 0x07
 Device support partitioning feature
 Device can have enhanced tech.
Max Enhanced Area Size [MAX_ENH_SIZE_MULT]: 0x0001d2
 i.e. 3817472 KiB
Partitions attribute [PARTITIONS_ATTRIBUTE]: 0x00
Partitioning Setting [PARTITION_SETTING_COMPLETED]: 0x00
 Device partition setting NOT complete
General Purpose Partition Size
 [GP_SIZE_MULT_4]: 0x000000
 [GP_SIZE_MULT_3]: 0x000000
 [GP_SIZE_MULT_2]: 0x000000
 [GP_SIZE_MULT_1]: 0x000000
Enhanced User Data Area Size [ENH_SIZE_MULT]: 0x000000
 i.e. 0 KiB
Enhanced User Data Start Address [ENH_START_ADDR]: 0x00000000
 i.e. 0 bytes offset
Bad Block Management mode [SEC_BAD_BLK_MGMNT]: 0x00
Periodic Wake-up [PERIODIC_WAKEUP]: 0x00
Program CID/CSD in DDR mode support [PROGRAM_CID_CSD_DDR_SUPPORT]: 0x01
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[127]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[126]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[125]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[124]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[123]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[122]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[121]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[120]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[119]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[118]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[117]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[116]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[115]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[114]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[113]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[112]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[111]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[110]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[109]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[108]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[107]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[106]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[105]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[104]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[103]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[102]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[101]]: 0x05
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[100]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[99]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[98]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[97]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[96]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[95]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[94]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[93]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[92]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[91]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[90]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[89]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[88]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[87]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[86]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[85]]: 0x01
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[84]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[83]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[82]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[81]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[80]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[79]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[78]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[77]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[76]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[75]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[74]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[73]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[72]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[71]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[70]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[69]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[68]]: 0xc8
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[67]]: 0xc8
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[66]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[65]]: 0x00
Vendor Specific Fields [VENDOR_SPECIFIC_FIELD[64]]: 0x0f
Native sector size [NATIVE_SECTOR_SIZE]: 0x00
Sector size emulation [USE_NATIVE_SECTOR]: 0x00
Sector size [DATA_SECTOR_SIZE]: 0x00
1st initialization after disabling sector size emulation [INI_TIMEOUT_EMU]: 0x00
Class 6 commands control [CLASS_6_CTRL]: 0x00
Number of addressed group to be Released[DYNCAP_NEEDED]: 0x00
Exception events control [EXCEPTION_EVENTS_CTRL]: 0x0000
Exception events status[EXCEPTION_EVENTS_STATUS]: 0x0000
Extended Partitions Attribute [EXT_PARTITIONS_ATTRIBUTE]: 0x0000
Context configuration [CONTEXT_CONF[51]]: 0x00
Context configuration [CONTEXT_CONF[50]]: 0x00
Context configuration [CONTEXT_CONF[49]]: 0x00
Context configuration [CONTEXT_CONF[48]]: 0x00
Context configuration [CONTEXT_CONF[47]]: 0x00
Context configuration [CONTEXT_CONF[46]]: 0x00
Context configuration [CONTEXT_CONF[45]]: 0x00
Context configuration [CONTEXT_CONF[44]]: 0x00
Context configuration [CONTEXT_CONF[43]]: 0x00
Context configuration [CONTEXT_CONF[42]]: 0x00
Context configuration [CONTEXT_CONF[41]]: 0x00
Context configuration [CONTEXT_CONF[40]]: 0x00
Context configuration [CONTEXT_CONF[39]]: 0x00
Context configuration [CONTEXT_CONF[38]]: 0x00
Context configuration [CONTEXT_CONF[37]]: 0x00
Packed command status [PACKED_COMMAND_STATUS]: 0x00
Packed command failure index [PACKED_FAILURE_INDEX]: 0x00
Power Off Notification [POWER_OFF_NOTIFICATION]: 0x01
Control to turn the Cache ON/OFF [CACHE_CTRL]: 0x01
eMMC Firmware Version: 
eMMC Life Time Estimation A [EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_A]: 0x08
eMMC Life Time Estimation B [EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_B]: 0x08
eMMC Pre EOL information [EXT_CSD_PRE_EOL_INFO]: 0x01
Secure Removal Type [SECURE_REMOVAL_TYPE]: 0x39
 information is configured to be removed using a vendor defined
 Supported Secure Removal Type:
  information removed by an erase of the physical memory
  information removed using a vendor defined
Command Queue Support [CMDQ_SUPPORT]: 0x01
Command Queue Depth [CMDQ_DEPTH]: 16
Command Enabled [CMDQ_MODE_EN]: 0x00
根據working with eMMC 的說明:

It is possible to get an estimation on the health status of the device by checking the parameters
EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_A and EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_B.
The estimation is given in steps of 10% so a value of 0x01 means that 0% to 10% life time used.
This functionality was introduced in eMMC 5.0.
# mmc extcsd read /dev/mmcblk2 | grep EXT_CSD_DEVICE_LIFE_TIME_EST
eMMC Life Time Estimation A [EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_A]: 0x01
eMMC Life Time Estimation B [EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_B]: 0x01  
所以上面 example 的 eMMC 壽命已經到 70~80% 了

另外一個 command 是 fstrim
Usage:
 fstrim [options] 

Discard unused blocks on a mounted filesystem.

Options:
 -a, --all           trim all supported mounted filesystems
 -A, --fstab         trim all supported mounted filesystems from /etc/fstab
 -o, --offset   the offset in bytes to start discarding from
 -l, --length   the number of bytes to discard
 -m, --minimum  the minimum extent length to discard
 -v, --verbose       print number of discarded bytes
 -n, --dry-run       does everything, but trim

 -h, --help          display this help
 -V, --version       display version
用 -v 來看,真的會顯示 discard 的 size...
ref:

2021/10/12

some memos's for anaysis log

用 sed 來處理log 會用到的 pattern:

刪除行:

刪除10~20 行:
sed -i '10,20d' myfile

刪除包含'my abc' 的那些行
sed -i '/my abc/d' myfile

要是太多,只要挑出幾個,可以用 grep:
包含 'Booting ', 'my abc', 'is OK' 的那幾行:
grep 'Booting \|my abc\|is OK' myfile

找 8 digits 16 hex
grep -E '0x[0-9A-Fa-f]{8}' myfile
-- repeat 8 次 {8} 這個 option 需要 -E 選項

這個用在 sed 的話,就要加 -r 選項
sed -i -r '/0x[0-9A-Fa-f]{8}/d' myfile

Bookmark: Chromium OS for raspberry pi

從 Link 就知道是 FydeOS build 的。
所以他們的 github 上還有一些其他的 port.

2021/10/8

armbian , for banana pi

超古老 banana pi (M1)。找得到還有支援的 distribution,就剩下 armbian
目前 debian official 已經是 11 (bull's eye),armbian for bananpi M1 最新的 stable 只到 10 (buster),但是至少 kernel 是 5.10
-- 原來有支援 debian 跟 ubuntu。各自依照 distribution 來表示。

一樣,download xz 解開,dd 到 sd card 上。
只有一個 partition,mount 進來後,已經有 /boot,不是像 raspberry pi 一樣有獨立的 /boot partition (fat)。

開機,在 console 有輸出.. 一直到 kernel booting 後就沒了 (猜是display 在 hdmi),等30 sec.. 後就出現 prompt,要求輸入 root password。
root password 有嚴格的檢查,不能包含在 dictionary 裡面的詞。(但是 login 後可以改)。

login 後 keyboard mapping 有問題,要
export TERM=vt100
才會正常。

修改 /boot/armbianEnv.txt,把 console=both 改成 serial,kernel log 就會出現在 console 上了。
開進 shell , keyboard mapping 也對了 (雖然 TERM 是 linux)

run armbian-config..

很方便,竟然有 hostspot...,而且自己外接的 wifi dongle 自動偵測到 (rt2800usb)
一段自動偵測,設定之後,會出現 interface 給你選,這個要選 ethernet。
然後就會用 default 的 SSID, password 啟動。
iptables, ip_forwards 都會設好。

要改 SSID, password 的話,再選一次 hostapd,選 advance ,就可以 edit ssid, passowrd.

hostapd, dnsmasq 都是 systemctl service,和 iptables 的設定各是:
/etc/hostapd.conf
/etc/dnsmasq.conf
/etc/iptables.ipv4.nat

使用心得... 比以前的(?) 好很多,作為 wifi router,都沒有以前動不動就 kernel trape 的現象,連 driver 也不像以前常常出現 warnning message...

mender.io : dump content from *.mender

要從 mender file 中拿到安裝檔,script...,要用 dump command:
~$ mender-artifact dump --help
NAME:
   mender-artifact dump - Dump contents from Artifacts

USAGE:
   mender-artifact dump [command options] <Artifact>

DESCRIPTION:
   Dump various raw files from the Artifact. These can be used to create a new Artifact with the same components.

OPTIONS:
   --files value      Dump all included files in the first payload into given folder
   --meta-data value  Dump the contents of the meta-data field in the first payload into given folder
   --print-cmdline    Print the command line that can recreate the same Artifact with the components being dumped. 
                      If all the components are being dumped, a nearly identical Artifact can be created. 
                      Note that timestamps will cause the checksum of the Artifact to be different, 
                      and signatures can not be recreated this way. The command line will only use long option names.
   --print0-cmdline   Same as 'print-cmdline', except that the arguments are separated by a null character (0x00).
   --scripts value    Dump all included state scripts into given folder
   
上面help 中的value 是說你要 dump 到那一個目錄...

討論區文章的說明,mender 可以直接用 tar 解開。
實際測試,tar 解開的不包含 scripts 和 print-cmdline 的內容。
大概跟 --files 的輸出一樣。

所以要拿到 state_scripts ,還是要用 dump command


另外,做 delta update 時,會確認 base image 的 checksum 和 delta ota package 中的 base image 是不是一樣。
是用 sha256sum 來確認的。
sha256sum <your partition> 

2021/10/5

kernel console , buf length and sync async

是說.. 現在 kernel printk 都已經是 async 了。
但是要 force sync 的話,還是可以,用printk.synchronous = 1,加到 boot cmdline

printk log buffer length 也可以改,有兩種方法..
一個是改 kernel,一個是用 boot cmdline

CONFIG_LOG_BUF_SHIFT : 可以用 menuconfig 設定。

log_buf_len=65536 : 可以加在 boot cmdline

banana pi, console UART

主要是 banana pi 的 console uart pin 跟 raspberry pi 不一樣。
不是在 gpio 那兩排,而是在中間突出的一小塊。

bookmark: A Minimum Complete Tutorial of Linux ext4 File System

上面 e2fsprog 裡免的 debugfs :
# debugfs -R "ls -l" /dev/sda6
  2   40755 (2)   1001   1001    4096 17-Sep-2013 04:03 .
  2   40755 (2)   1001   1001    4096 17-Sep-2013 04:03 ..
 16  100644 (1)   1001   1001    9085 17-Sep-2013 04:03 avserver.conf
 17  100644 (1)   1001   1001    2177 17-Sep-2013 04:03 bash.bashrc
 26  100644 (1)   1001   1001     722 17-Sep-2013 04:03 crontab
還有 cat 的方法:
進入 debugfs,argument 是 ext4 的 image:
$ debugfs my.ext4
debugfs:
然後就可以在 debugfs 這個 prompt 下命令。
像 ls -l 或是 cat /etc/os-release...

repo forall .. variable name

repo forall 可以用的 變數,在 subcmds/forall.py 中有說明
# Environment

pwd is the project's working directory.  If the current client is
a mirror client, then pwd is the Git repository.

REPO_PROJECT is set to the unique name of the project.

REPO_PATH is the path relative the the root of the client.

REPO_REMOTE is the name of the remote system from the manifest.

REPO_LREV is the name of the revision from the manifest, translated
to a local tracking branch.  If you need to pass the manifest
revision to a locally executed git command, use REPO_LREV.

REPO_RREV is the name of the revision from the manifest, exactly
as written in the manifest.

REPO_COUNT is the total number of projects being iterated.

REPO_I is the current (1-based) iteration count. Can be used in
conjunction with REPO_COUNT to add a simple progress indicator to your
command.

REPO__* are any extra environment variables, specified by the
"annotation" element under any project element.  This can be useful
for differentiating trees based on user-specific criteria, or simply
annotating tree details.
test...
strong
em

2021/10/4

#define PKTLINK(skb)                    (((struct sk_buff*)(skb))->prev)
然後...
        /* queueing chains not allowed and no segmented SKB (Kernel-3.18.y) */
        ASSERT(!((PKTLINK(p) != NULL) && (PKTLINK(p) != p)));

2021/9/30

udhcpc and dnsmasq -- release ip on exit

主要是要測試 udhcpc 的 -R (release ip) 功能。

所以準備兩台機器
  • server : run dnsmasq
  • client : run udhcpc
server 起來之後,可以在 /var/lib/dnsmasq/dnsmasq.leases 看到配發的 ip.


所以 client 用:
udhcpc -iwlan0 -R
可以看到取得 ip,然後用 ip addr 看 wlan0 有 ip address。
在 server 上 cat /var/lib/dnsmasq/dnsmasq.leases 可以看到 client 的 wlan0 mac 和配發的 ip

用 ps 看,udhcpc 在背景。
這時候在 client 上 kill udhcpc
再用 ip addr 看,wlan0 已經沒有 ip 了。
然後 server 的 /var/lib/dnsmasq/dnsmasq.leases 中,client 的那一行也被清掉。


如果不加上 -R
udhcpc -iwlan0
一樣,client 取得 ip,udhcpc 在背景。
server 的 dnsmasq.leases 有 client 的 mac 跟 ip

client kill udhcpc,之後確認 udhcpc 沒有在背景。
用 ip addr 看,wlan0 的 ip 還在。
server 的 dnsmasq.leases 中,client 的 ip 也還在。

如果不用 -R,然後要 udhcpc 取得 ip 後就退出,不要在背景。用 -q。
測試取得 ip 後, udhcpc 的確不會退到背景。


其他

udhcpc 在取得ip 後,會 執行 /usr/share/udhcpc/default.script
這個script 會依照 udhcpc call 他時給的參數,做:
  • 設定 interface 的 ip
  • 設定 route
  • 設定 resvolv.conf
其中 resolv.conf 是 /tmp/resolv.conf
猜是因為有些統是 readonly root,所以這樣,讓 /etc/resolv.conf link 到 /tmp/resolv.conf

2021/9/23

build and run LVGL example in linux

本來像依照這一篇 的說明,在 framebuffer 下 run demo。
但是...
這一篇 說中。在 X windows 下測試 framebuffer 程是會失敗,因為會被 x windows update 調。
... 所以可以用 Ctrl-Alt-F1/2/... 切到console 來測試。
不然,就要用 SDL ..

這篇 也是一樣。

用SDL 得話:這一篇 可以正常動作。


framebuffer port:
git clone https://github.com/lvgl/lv_port_linux_frame_buffer.git
cd lv_port_linux_frame_buffer/
git submodule update --init --recursive
make

SDL 比較麻煩,上面文章說要拉新版 SDL 自己 build。
git clone https://github.com/libsdl-org/SDL.git
cd SDK
git checkout release-2.0.10
configure , make and install
./configure --host=x86-linux --prefix=/usr --disable-x11 --enable-debug -disable-voodoo -disable-mmx --disable-see --enable-sdl --enable-jpeg --enable-zlib --enable-png --disable-gif --enable-freetype --disable-video4linux --disable-video4linux2 --with-gfxdrivers=none --with-inputdrivers=linuxinput
make
sudo make install
default install path 是 /usr/lib/libSDL2.so 會自動安裝 pkgconfig

lvgl 的 SDL port 要用
git clone --recursive https://github.com/littlevgl/pc_simulator_sdl_eclipse.git
就一樣,make 後就可以 run 了,直接可以在 X 上 run ...


2024/02/07

現在有更方便的方法了。

這個: 直接config 好 Makefile 了。所以直接 make 就可以。
看Makefile 就知道最後 run ./build/bin/demo

這個 port 也寫好了 vs code 的 workspace。
所以在 clone 下來的 folder 用 'code .' 開啟,之後會問你要不要開啟 workspace,回 OK
之後就可以用 F5 (starting debugging) 來 build 和 run 了。


要 try v8 的 lvgl,所以.. 一樣checkout lv_sim_vscode_sdl 後,
到 lvgl 去 checkout v8.3.0
到 lv_driver 去 checkout v8.3.0
然後修改 Makefile,LV_DRIVER 由 X11 改 SDL2 (因為 X11 要最新版本的 lv_driver 才有支援)
另外,v8.3.0 的 test/makefile/test.c 中,有 main(),也要改成 _main,因為 demo 已經有 main() 了。

之後一樣,在主folder 開啟 vscode,open workspace,F5 - debug and run,OK

Hello X Window

從網路上 copy 的 hello X window
/*
 *  This is a sample X11 program using Xt and the Athena widget set that
 *  simply puts up a main window with the text "hello" in it.
 */

#include >X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Xaw/Label.h>

int main(int argc, char* argv[])
{
    XtAppContext app_context;
    Widget toplevel, hello;

    toplevel = XtVaAppInitialize(
        &app_context,
        "XHello",
        NULL, 0,
        &argc, argv,
        NULL,
        NULL);

    hello = XtVaCreateManagedWidget("hello", labelWidgetClass, toplevel, NULL);

    XtRealizeWidget(toplevel);
    XtAppMainLoop(app_context);
}
build 的 command:
gcc helloxwindow.c -lXt -lXaw
可以試試看後面的 -l 缺的化,各是會出現什麼Error..
Xaw:
helloxwindow.c:(.text+0x5d): undefined reference to `labelWidgetClass

Xt
undefined reference to symbol 'XtRealizeWidget'

都沒有的話:
helloxwindow.c:(.text+0x4e): undefined reference to `XtVaAppInitialize'
helloxwindow.c:(.text+0x5d): undefined reference to `labelWidgetClass'
helloxwindow.c:(.text+0x7a): undefined reference to `XtVaCreateManagedWidget'
helloxwindow.c:(.text+0x8a): undefined reference to `XtRealizeWidget'
helloxwindow.c:(.text+0x96): undefined reference to `XtAppMainLoop'

2021/9/20

Stereo matching

筆記一下 stereo matching。

matching 就是找A影像中某一區域是在B影像的哪裡?
所以是找最相似的。

但是影像這麼大一塊區域要怎麼找?
因為epipoloar line 的定義,所以只要沿著epicpolar line找就可以。

因為投影的關係,所有點的epipoloar line都會交會在epipoloar hole那一點。
為了好做?跟減少變形差異,要先把投影拉平。
這個就是 image rectification。
經過image rectification 後,epipolar line 會變成平行的水平線。

Epipolar hole, line and plan

這一篇說明比較清楚,用在兩個相機的系統上,用來描述這兩個相機的影像的交互關係。
有一個觀點就是兩個相機的焦點的連線。
就好像解幾何題目畫的輔助線,這條連接兩個相機焦點的線,叫他 base line。

這條base line 和各(/兩)個相機成像平面的交/切點,就叫epipolar hole(有兩個,一個相機一個)。
空間上的任一點,和 base line 就可以形成(/定義)一個 epipolar plan。
epipolar plan 和 相機成像平面的切線(intersection),叫epipolar line (一個相機一條)。

空間任一點,其實代表就是相機拍攝的物件在空間的位置。

2021/9/15

WM8974 driver : 增加一個control : mic bias

發現沒有寫,所以現在補一下..

Datasheet 上..
Datasheet P 19:

* Register R44: Input Control
* Bit 8
* Label: MBVSEL
* Default: 0
* Description: Microphone Bias Voltage Control
    0: 0.9 x AVDD
    1: 0.75 x AVDD
所以在 wm8974.c 中加一個 amixer control:
diff --git a/linux-4.14/sound/soc/codecs/wm8974.c b/linux-4.14/sound/soc/codecs/wm8974.c
index 61fbd7c55..7a1c492d2 100644
--- a/linux-4.14/sound/soc/codecs/wm8974.c
+++ b/linux-4.14/sound/soc/codecs/wm8974.c
@@ -66,6 +66,7 @@ static const char *wm8974_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" };
 static const char *wm8974_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" };
 static const char *wm8974_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" };
 static const char *wm8974_alc[] = {"ALC", "Limiter" };
+static const char *wm8974_mbvsel[]= {"0.9", "0.75"};
 
 static const struct soc_enum wm8974_enum[] = {
        SOC_ENUM_SINGLE(WM8974_COMP, 1, 4, wm8974_companding), /* adc */
@@ -85,6 +86,7 @@ static const struct soc_enum wm8974_enum[] = {
 
        SOC_ENUM_SINGLE(WM8974_EQ5,  5, 4, wm8974_eq5),
        SOC_ENUM_SINGLE(WM8974_ALC3,  8, 2, wm8974_alc),
+       SOC_ENUM_SINGLE(WM8974_INPUT, 8, 2, wm8974_mbvsel),
 };
 
 static const char *wm8974_auxmode_text[] = { "Buffer", "Mixer" };
@@ -131,6 +133,7 @@ SOC_ENUM("Equaliser EQ4 Bandwidth", wm8974_enum[9]),
 SOC_ENUM("EQ4 Cut Off", wm8974_enum[10]),
 SOC_SINGLE_TLV("EQ4 Volume", WM8974_EQ4,  0, 24, 1, eq_tlv),
 
+
 SOC_ENUM("Equaliser EQ5 Bandwidth", wm8974_enum[11]),
 SOC_ENUM("EQ5 Cut Off", wm8974_enum[12]),
 SOC_SINGLE_TLV("EQ5 Volume", WM8974_EQ5,  0, 24, 1, eq_tlv),
@@ -151,6 +154,8 @@ SOC_SINGLE("ALC Capture Hold", WM8974_ALC2,  4, 7, 0),
 SOC_SINGLE("ALC Capture Target", WM8974_ALC2,  0, 15, 0),
 
 SOC_ENUM("ALC Capture Mode", wm8974_enum[13]),
+SOC_ENUM("MIC Bias Voltage", wm8974_enum[14]),
+
 SOC_SINGLE("ALC Capture Decay", WM8974_ALC3,  4, 15, 0),
 SOC_SINGLE("ALC Capture Attack", WM8974_ALC3,  0, 15, 0),
 
這樣就多一個
numid=38,iface=MIXER,name='MIC Bias Voltage'
cget, set
# amixer cget numid=38
numid=38,iface=MIXER,name='MIC Bias Voltage'
  ; type=ENUMERATED,access=rw------,values=1,items=2
  ; Item #0 '0.9'
  ; Item #1 '0.75'
  : values=1
設完後,cat register-map 來看,reg 2C bit 8 有改變。

2021/9/13

bookmark : hacking CAR canbus

ref: 作者用 can-dsub, dsub-usb 然後用 usb-can bus driver 在 linux 上開啟一個 socket 來 dump CAN bus data。
然後 log,配合 影像做 reverse engineering。

最後還附上hack 完的 source code. (在 github : toyothack)。

另外的,當然是 openpilot 最多,他們有一個opendbc 描述已經decode 的車輛canbus 格式。

use repo to manage multiple projects

ref: 要用 repo/manifest 來管理現有 一堆 projects。
只要 create 一個 git project,裡面就是 manifest 的 xml 檔。
其中要有一個 default.xml, repo 會用這個做 主要的 manifest.xml
repo init -u mymanifest
mymanifest 就是放 default.xml 的 project folder


舉例來說:

假設myrepo 專案有 proj1, proj2 兩個 git source
.
├── proj1
└── proj2
想要用 repo/manifest 來管理。
就create 一個 manifests,裡面有 default.xml,也要用 git 管理:
<?xml version="1.0" encoding="UTF-8"?>
<manifest>
	<remote name="origin" fetch="./" />
	<default revision="master" remote="origin" />
	<project path='proj1' name='proj1' />
	<project path='prij2' name='proj2' />
</manifest>
然後 myrepo 下有三個 git projects:
.
├── manifests
│   └── default.xml
├── proj1
└── proj2

這樣,在 local 端,就可以用 repo init 來 clone 整個 project
mkdir testrepo && cd testrepo
repo init -u ~/myrepo/manifests
downloading Repo source from https://gerrit.googlesource.com/git-repo
remote: Counting objects: 2, done
remote: Finding sources: 100% (12/12)
remote: Total 12 (delta 0), reused 12 (delta 0)
Unpacking objects: 100% (12/12), done.
Downloading manifest from /home/charles-chang/myrepo/manifests/
remote: Counting objects: 15, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 15 (delta 4), reused 0 (delta 0)

Your identity is: charles.chang <charles.chang@loyaltech.com>
If you want to change this, please re-run 'repo init' with --config-name

repo has been initialized in /home/charles-chang/testrepo
之後 repo sync 就會拉下所有的 project (不包含 manifest,manifest 會在 .repo/manifests)
然後一樣在 repo init 時加上 --mirror 就可以產生 git server 端的目錄結構 (方便放在 git server)

現在看來,要先用 python script : follow repo/manifest.xml 做出 repo mirror 的結構.,重新安排一次 project 目錄結構。
之後才能讓人做 repo init, sync..
-- 新版的 repo 不從 manifest.xml parsing 了, manifest.xml 也不再是 link,而是一個 include name,include default.xml (或其他)。



repo 管理後,如果要create 一個版本, fix 住目前的 project revision,可以用 repo manifest, 如何導出並使用清單文件? 的方法。用 repo manifest,列出有家 option 和沒加 option 的結果:
一般:
charles-chang@rdsuper:~/testmirror$ repo manifest
<?xml version="1.0" encoding="UTF-8"?>
<manifest>
  <remote fetch="./" name="origin"/>
  
  <default remote="origin" revision="master"/>
  
  <project name="pp/project1" path="project1"/>
  <project name="project2" path="priject2"/>
</manifest>
加上 supress-upstream-revision
charles-chang@rdsuper:~/testmirror$ repo manifest -r --suppress-upstream-revision
<?xml version="1.0" encoding="UTF-8"?>
<manifest>
  <remote fetch="./" name="origin"/>
  
  <default remote="origin" revision="master"/>
  
  <project dest-branch="master" name="pp/project1" path="project1" revision="7c4bcfb45cd9aa2266c0acf3f850a9ea50ae5ef9"/>
  <project dest-branch="master" name="project2" path="priject2" revision="907de68553f9b6d638aceb967c7a9f55bc8fcb03"/>
</manifest>
舊可可以把這個列出的結果存成新的 myversion.xml,加到 manifests.git 中。

如果 project 在另一個 project 中...可以用 .gitignore..
例如:
.
├── priject2
│   └── pp2
└── project1
    ├── p3
    │   └── myfile
    └── pp1
p3 在 project1 裡。

manifest 是這樣:
<manifest>
	<remote name="origin" fetch="./" />
	<default revision="master" remote="origin" />
	<project path='project1' name='pp/project1' />
	<project path='priject2' name='project2' />
	<project path='project1/p3' name='p3' />
</manifest>
可以看到 project path='project1/p3' 這樣。
但是這樣在 repo sync 後,project1 下 git status 就會看到 p3 new.
所以要加 .gitignore
p3



manifest 中,project 有些會有:
 revision="36fb2aa4f7f25946e0ce3d4223095ce9601dd17f" upstream="cafe/QCLA.2.0-r00015.3"
這樣的話,project 要有 rev="36fb2aa4f7f25946e0ce3d4223095ce9601dd17f" 的 commit 存在,同時,還要有個branch nane叫 ""cafe/QCLA.2.0-r00015.3".
用 git ls-remote 可以列出 remote ref head. 可以檢查

2021/9/10

system service : report ip to another server

啟動後,等 remote server up,然後把 自己的 ip 送過去..
先把自己的 public key 送過去 (user charles-chang)。設定好 ssh 免 password.
準備 service file:
xeontitan:/etc/systemd/system# cat reportip.service 
[Unit]
Description=send my ip address file to rdsuper
Afteter=network-online.target

[Service]
User=charles-chang
WorkingDirectory=/home/charles-chang
ExecStart=/home/charles-chang/reportip.sh

[Install]
WantedBy=multi-user.target
charles-chang 自己的 shell script:
cat /home/charles-chang/reportip.sh 
#!/bin/bash
while true
do
	ping -c 1 rdsuper
	if [ $? -eq 0 ]
	then
		break
	fi
	sleep 10
done
ip addr > $HOSTNAME-ip
scp $HOSTNAME-ip rdsuper:~/
enable & start
systemctl daemon-reload
systemctl enable reportip.service
systemctl start reportip.service



改用 python 做,用 socket 直接 update /etc/hosts 的話: 叫 chatgpt 寫...
為了測試,先改 /etc/hosts_u 系統測試 OK 再改 /etc/hosts..

~$ cat updatehosts.py
import socket

def update_hosts_file(ip, hostname):
    # Read the current contents of /etc/hosts
    with open('/etc/hosts_u', 'r') as file:
        lines = file.readlines()

    # Check if the hostname already exists in the file
    exists = False
    for i, line in enumerate(lines):
        if hostname in line:
            # Update the existing entry
            lines[i] = f"{ip} {hostname}\n"
            exists = True
            break

    # If the hostname does not exist, add a new entry
    if not exists:
        lines.append(f"{ip} {hostname}\n")

    # Write the updated contents back to /etc/hosts
    with open('/etc/hosts_u', 'w') as file:
        file.writelines(lines)

def start_server(port):
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.bind(('0.0.0.0', port))
    server_socket.listen(5)
    print(f"Listening on port {port}...")

    while True:
        client_socket, client_address = server_socket.accept()
        print(f"Connection from {client_address}")

        data = client_socket.recv(1024).decode('utf-8')
        if data:
            ip, hostname = data.split(',')
            update_hosts_file(ip, hostname)
            print(f"Updated /etc/hosts with IP: {ip}, Hostname: {hostname}")

        client_socket.close()

if __name__ == "__main__":
    start_server(61234)
然後 systemd service file:
$ cat updatehosts.service
[Unit]
Description=Sender Python Service
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/bin/python3 /home/charles-chang/updatehosts.py
Restart=on-failure

[Install]
WantedBy=multi-user.target

至於 sender 端,因為要等 dhcp ready...
要注意 nic interface name 是不是 eno1,不一定是。
要依照自己的系統修改。

$ cat sender_updatehosts.py
import socket
import time
import netifaces

SERVER_IP = '192.168.147.182' 
SERVER_PORT = 61234            

def get_ip_address(interface):
    try:
        ip_address = netifaces.ifaddresses(interface)[netifaces.AF_INET][0]['addr']
    except (ValueError, KeyError):
        ip_address = None
    hostname = socket.gethostname()
    return ip_address, hostname

def send_data(ip, hostname):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.connect((SERVER_IP, SERVER_PORT))
        message = f"{ip},{hostname}"
        s.sendall(message.encode('utf-8'))
        response = s.recv(1024)
        print('Received', repr(response))

def main():
    interface = 'eno1'
    
    while True:
        ip, hostname = get_ip_address(interface)
        if ip:
            break
        print("Waiting for valid IP address...")
        time.sleep(5)

    send_data(ip, hostname)

if __name__ == "__main__":
    main()

2021/9/9

script to generate script : document here

ref:
cat << EOF > aaaa
this is line one
and two
EOF
可以用來產生 aaaa,內容就是 this is line one,
command 意思是 一直到 EOF,都 cat 出來。
後面的 > aaaa 就是把 cat 出來的東西都放到 aaaa 中。

這個command在 shell script中來create 一個 shell script。
也就是在 "this is line one" 到 EOF 的區域放要產生的 shell script 內容。

因為都是 shell script ,所以為了保險,不要讓 這個 script 處理內容出現問題,可以用
cat << "EOF"
代表 把 EOF 之間的內容都當作 字串處理。
這樣就不用擔心特殊字元的問題。

當然,特殊字元也可以用 跳脫符號 "\",但是只會對特殊字元跳脫。

2021/9/5

Mobilenet

我一直以為是數學上相等的(推導),一直到這一篇才比較清楚。
mobilenet 利用計算量少的方式,產生一樣維度的output featuremap。
至於等不等效,就用實際training 來證明。
原來是一次conv計算+一個active function 就可以得到 feature map。
變成兩次conv計算,兩次active functiom 才能得到(一樣大小.數量的)feature map。也就是說,網路的深度增加了。

depth 指的是 color,例如 RGB 三個就是 depth。而不是網路的深度(層數)
原來的 convolution 都是一個kernel 就要包含三個 matrix (R.G.B)分別對 input 的 R.G.B 做各自的 convolution 後,經過 active function加起來變成一個 matrix (feature map)。
depthwise就是三個 R.G.B 做完各自的 convolution 後,用各自的 active function 做出各自的 matrix (feature map),不要變成一個。
之後再用 1x3 的 kernel 把 R.G.B feature map 經果 activae function 轉成一個 matrix。
這樣舊跟以往的 convolution 一樣的。

做 R.G.B convolution 時,維持分開,不用一個acitvie function 加在一起,所以說是 depthwise separable convolution

2021/9/2

mender : modify product device to use local demo server

先啟動 demo server。
在 source code 中找到 demo.crt
修改 /etc/mender/mender/conf :
參考mender.io build and install client from sources 看設定為 demo 的 conf:

device 的部份:
  • 依照 mender.conf 的 Servers : ServerURL 作為 update server
  • ServerCertificate 有的話,用這個作為 跟 server 溝通的憑證
# diff /data/backup/mender.conf /etc/mender/mender.conf 
--- /data/backup/mender.conf
+++ /etc/mender/mender.conf
@@ -24,13 +24,13 @@
     "StateScriptRetryTimeoutSeconds": 0,
     "StateScriptRetryIntervalSeconds": 0,
     "ModuleTimeoutSeconds": 0,
-    "ServerCertificate": "",
+    "ServerCertificate": "/data/demo.crt",
     "ServerURL": "",
     "UpdateLogPath": "",
-    "TenantToken": "eyJI......",
+    "TenantToken": "",
     "Servers": [
         {
-            "ServerURL": "https://hosted.mender.io"
+            "ServerURL": "https://docker.mender.io"
         }
     ]
 }
ServerURL 好像一定要概,不然會出現 Error:
level=error msg="Failure occurred while executing authorization request: Method: Post, URL: https://host.mender.io/api/devices/v1/authentication/auth_requests" func="github.com/mendersoftware/me
level=error msg="Authorize failed: transient error: authorization request failed: transient error: authorization request failed: Unknown url.Error type: Host validation error" func="github.com/m
level=info msg="State transition: authorize [Sync] -> authorize-wait [Idle]" func=github.com/mendersoftware/mender/app.transitionState file="/home/pi/mender-src/app/mender.go:461"
一定要用 docker.mender.io 的原因可能是因為 demo server 的 hosts 有加:
127.0.0.1 s3.docker.mender.io
127.0.0.1 docker.mender.io
demo.crt copy 過來,mender.conf只要改成這樣, demo server 上就可以看到 request device

demo server 基本照這個
其中,docker 的機器要改 /etc/hosts,加上上面的 mender.io 指到 127.0.0.1

2021/8/27

mender : 開啟 debug log

ref: mender default log level 是 info。有自己的 logfile,可以指定,例如 /data/mender/deployXXXX.log
mender 也會 log 到 system logger。但是不是所有的 log 都會。
可以在啟動mender 的systemd script 中,加入 log-level
ExecStart=/usr/bin/mender --log-level debug daemon
這樣 systemlog 也會有詳細的 debug log (包含 src line no).
Aug 27 06:41:40 Ambarella user.info mender[392]: time="2021-08-27T06:41:40Z" level=info msg="State transition: check-wait [Idle] -> update-check [Sync]" func=github.com/mendersoftware/mender/app.transitionState file="/home/pi/mender-src/app/mender.go:461"
Aug 27 06:41:40 Ambarella user.info mender[392]: time="2021-08-27T06:41:40Z" level=info msg="Correct request for getting image from: https://s3.amazonaws.com/hosted-mender-artifacts/60114bfe1e73ac58902b5283/600f0b00-c182-4347-8fdc-e48e3ec40751?X-Amz-Algorithm=AWS4-HMAC-S
Aug 27 06:41:40 Ambarella user.info mender[392]: time="2021-08-27T06:41:40Z" level=info msg="State transition: update-check [Sync] -> update-fetch [Download_Enter]" func=github.com/mendersoftware/mender/app.transitionState file="/home/pi/mender-src/app/mender.go:461"
Aug 27 06:41:40 Ambarella user.info mender[392]: time="2021-08-27T06:41:40Z" level=info msg="Running Mender client version: 2.6.1" func="github.com/mendersoftware/mender/app.(*DeploymentLogManager).Enable" file="/home/pi/mender-src/app/deployment_logger.go:143"
Aug 27 06:41:41 Ambarella user.info mender[392]: time="2021-08-27T06:41:41Z" level=info msg="State transition: update-fetch [Download_Enter] -> update-store [Download_Enter]" func=github.com/mendersoftware/mender/app.transitionState file="/home/pi/mender-src/app/mender.g
Aug 27 06:41:42 Ambarella user.info mender[392]: time="2021-08-27T06:41:42Z" level=info msg="No public key was provided for authenticating the artifact" func=github.com/mendersoftware/mender/installer.ReadHeaders file="/home/pi/mender-src/installer/installer.go:109"
Aug 27 06:41:42 Ambarella user.info mender[392]: time="2021-08-27T06:41:42Z" level=info msg="Opening device \"/dev/mmcblk0p8\" for writing" func=github.com/mendersoftware/mender/installer.bdevice.Open file="/home/pi/mender-src/installer/block_device.go:66"
Aug 27 06:41:42 Ambarella user.info mender[392]: time="2021-08-27T06:41:42Z" level=info msg="Native sector size of block device /dev/mmcblk0p8 is 512 bytes. Mender will write in chunks of 1048576 bytes" func=github.com/mendersoftware/mender/installer.bdevice.Open file="/
..

2021/8/25

Azure web function, func . list supported template

ref: 其中有列出 support template 的command:
$ func templates list
C# Templates:
  Azure Blob Storage trigger
  Azure Cosmos DB trigger
  Durable Functions activity
  Durable Functions entity (class)
  Durable Functions entity (function)
  Durable Functions Entity HTTP starter
  Durable Functions HTTP starter
  Durable Functions orchestrator
  Azure Event Grid trigger
  Azure Event Hub trigger
  HTTP trigger
  IoT Hub (Event Hub)
  Kafka output
  Kafka trigger
  Azure Queue Storage trigger
  RabbitMQ trigger
  SendGrid
  Azure Service Bus Queue trigger
  Azure Service Bus Topic trigger
  SignalR negotiate HTTP trigger
  Timer trigger

Custom Templates:
  Azure Blob Storage trigger
  Azure Cosmos DB trigger
  Azure Event Grid trigger
  Azure Event Hub trigger
  HTTP trigger
  IoT Hub (Event Hub)
  Kafka trigger
  Azure Queue Storage trigger
  RabbitMQ trigger
  SendGrid
  Azure Service Bus Queue trigger
  Azure Service Bus Topic trigger
  SignalR negotiate HTTP trigger
  Timer trigger

JavaScript Templates:
  Azure Blob Storage trigger
  Azure Cosmos DB trigger
  Durable Functions activity
  Durable Functions entity
  Durable Functions Entity HTTP starter
  Durable Functions HTTP starter
  Durable Functions orchestrator
  Azure Event Grid trigger
  Azure Event Hub trigger
  HTTP trigger
  IoT Hub (Event Hub)
  Kafka output
  Kafka trigger
  Azure Queue Storage trigger
  RabbitMQ trigger
  SendGrid
  Azure Service Bus Queue trigger
  Azure Service Bus Topic trigger
  SignalR negotiate HTTP trigger
  Timer trigger

PowerShell Templates:
  Azure Blob Storage trigger
  Azure Cosmos DB trigger
  Durable Functions activity (preview)
  Durable Functions HTTP starter (preview)
  Durable Functions orchestrator (preview)
  Azure Event Grid trigger
  Azure Event Hub trigger
  HTTP trigger
  IoT Hub (Event Hub)
  Kafka output
  Kafka trigger
  Azure Queue Storage trigger
  SendGrid
  Azure Service Bus Queue trigger
  Azure Service Bus Topic trigger
  SignalR negotiate HTTP trigger
  Timer trigger

Python Templates:
  Azure Blob Storage trigger
  Azure Cosmos DB trigger
  Durable Functions activity
  Durable Functions entity
  Durable Functions HTTP starter
  Durable Functions orchestrator
  Durable Functions orchestrator
  Azure Event Grid trigger
  Azure Event Hub trigger
  HTTP trigger
  Kafka output
  Kafka trigger
  Azure Queue Storage trigger
  RabbitMQ trigger
  Azure Service Bus Queue trigger
  Azure Service Bus Topic trigger
  Timer trigger

TypeScript Templates:
  Azure Blob Storage trigger
  Azure Cosmos DB trigger
  Durable Functions activity
  Durable Functions entity
  Durable Functions Entity HTTP starter
  Durable Functions HTTP starter
  Durable Functions orchestrator
  Azure Event Grid trigger
  Azure Event Hub trigger
  HTTP trigger
  IoT Hub (Event Hub)
  Kafka output
  Kafka trigger
  Azure Queue Storage trigger
  RabbitMQ trigger
  SendGrid
  Azure Service Bus Queue trigger
  Azure Service Bus Topic trigger
  SignalR negotiate HTTP trigger
  Timer trigger

Powershell Templates:
  RabbitMQ trigger

dhcpc to resolv.conf, deal with multiple dns

# udhcpc -iwlan0 -A 1 -b 
udhcpc: started, v1.31.1
udhcpc: sending discover
udhcpc: sending select for 192.168.1.100
udhcpc: lease of 192.168.1.100 obtained, lease time 86400
deleting routers
route: SIOCDELRT: No such process
adding dns 168.95.1.1
adding dns 168.95.192.1
所以就..
# udhcpc -iwlan0 -A 1 -b 2>&1 | grep dns | sed 's/adding dns/nameserver/g' > /etc/resolv.conf
看看對不對...
# cat /etc/resolv.conf 
nameserver 168.95.1.1
nameserver 168.95.192.1

2021/8/18

uart console tool (picocom) support baudrate 1500000

果然,在 /usr/include/x86_64-linux-gnu/bits/termios.h
#define  B57600   0010001
#define  B115200  0010002
#define  B230400  0010003
#define  B460800  0010004
#define  B500000  0010005
#define  B576000  0010006
#define  B921600  0010007
#define  B1000000 0010010
#define  B1152000 0010011
#define  B1500000 0010012
#define  B2000000 0010013
#define  B2500000 0010014
#define  B3000000 0010015
#define  B3500000 0010016
#define  B4000000 0010017
有一堆高速baudrate 定義。

open source 就有這個好處。

1500000 這個 baudrate 沒 support,所以 checkout 出來改。
我用 master 的 68ca528b563b8693ae2c0835629a7457fd211599 來改:
~/picocom$ git diff
diff --git a/term.c b/term.c
index 4abe6cd..66a72ee 100644
--- a/term.c
+++ b/term.c
@@ -637,6 +637,9 @@ term_set_baudrate (int fd, int baudrate)
                case 921600:
                        spd = B921600;
                        break;
+               case 1500000:
+                       spd = B1500000;
+                       break;
 #endif
                default:
                        term_errno = TERM_EBAUD;
其實就加上去就可以了。
然後就可以用 -b1500000 了。

2021/8/17

mender : state script

都沒有文件...

mender 把更新分成幾個 state (9個?)。
然後給個 state 一個 run script 的機會 (兩個?. enter , leave)
但是說明很少。

transient error: update commit failed: statescript: The statescript version file is missing. 
This file needs to be present and contain a single number representing which version of the statescript support 
the client is using in order to successfully run statescripts on the client
只好去找 source..
    versionFilePath := filepath.Join(l.RootfsScriptsPath, "version")
    f, err := os.Open(versionFilePath)
    if err != nil && os.IsNotExist(err) {
        errmsg := "statescript: The statescript version file is missing. This file needs to be " +
            "present and contain a single number representing which version of the statescript " +
            "support the client is using in order to successfully run statescripts on the client"
        return errors.New(errmsg)
    } else if err != nil {
        return errors.Wrap(err, "statescript")
    }
    ver, err := readVersion(f)
    if _, ok := err.(readVersionParseError); ok {
        errmsg := "statescript: Failed to parse the version file in the statescript directory (%s). " +
            "The file needs to contain a single integer signifying which version of the statescript " +
            "support which this client is using"
        return fmt.Errorf(errmsg, err)
    }
所以是需要一個叫 version 的 file,裡面是數字。

然後這個 RootfsScriptsPath 就是
conf/paths.go:	DefaultRootfsScriptsPath = path.Join(GetConfDirPath(), "scripts")
應該就是 /etc/mender/scripts

version 裡面只有一個 0,然後 Error 變成
transient error: update commit failed: statescript: unsupported scripts version: 0
找到:
func NewStateScriptExecutor(config *conf.MenderConfig) statescript.Launcher {
    ret := statescript.Launcher{
        ArtScriptsPath:          config.ArtifactScriptsPath,
        RootfsScriptsPath:       config.RootfsScriptsPath,
        SupportedScriptVersions: []int{2, 3},
..
把version內容由 0 改為 3 之後,果然就 ota OK 了,沒有 Error,但是 state script 以=也沒有執行....

execute.go 有 l.get( )
func (l Launcher) get(state, action string) ([]os.FileInfo, string, error) {

    sDir := l.ArtScriptsPath
    if state == "Idle" || state == "Sync" || state == "Download" {
        sDir = l.RootfsScriptsPath
    }
...
先看script 位置,依照 state 而有不同。
path.go
    DefaultArtScriptsPath    = path.Join(GetStateDirPath(), "scripts")
    DefaultRootfsScriptsPath = path.Join(GetConfDirPath(), "scripts")

DefaultDataStore   = "/var/lib/mender"

func GetStateDirPath() string {
    return DefaultDataStore
}
func GetConfDirPath() string {
    return "/etc/mender"
}
所以 Idle, Sync, Download 是在 /etc/mender。
其他state 是 /var/lib/mender

查實際的機器, 把 state script 放到 update OS (ext4) 的 /etc/mender/scripts/ 下後。包進 ota file (mender),然後 更新到機器上。
script folder 好像會被 copy 到 /data/mender/script (就是 /var/lib/mender 的 symbolic link)。(不是全部 copy)。
而 /etc/mender/script 跟 /var/lib/mender 剛好就是上面的 RootfsScriptsPath 跟 ArtScriptsPath。

但是... RootfsScriptPath 是在ota file 的 ext4 裡, /var/lib/mender 是working 的 os..不一樣。

把一些 example 的 script copy 到 update image 的 /etc/mender/scripts 下
# ls /etc/mender/scripts/
ArtifactCommit_Enter_00   Download_Enter_00         Sync_Enter_00
ArtifactInstall_Enter_00  Download_Leave_01         version
ArtifactReboot_Enter_00   Download_Leave_02
每一個 script 都是在 /data/ 下 touch 自己的 filename.
用 mender ota 成功,到 /data 下只有看到 Sync_Enter_00
符合上面的code,, idle,sync,download 都看 /etc/mender/script
所以把 一樣的 script 都 copy 到 /var/lib/mender/scripts ...


測試,每個 script 都是
date > /data/$(basename "$0")
然後從 /data 就可以知道有哪些 script 有正確執行。
mender 的 yocto recipes 也是用一樣的 script。
-- 裡面 script 都 copy 到一樣的地方喔。


最後:

state scripts 分成兩種,一種是預先要再系統的。一種是放在 artifacr (*.mender) 。
因為 Artifact(mender) 要 download 完會有,所以download 之後的 state ,script 都可以放在 artifacr (*.mender) 中。
其他沒有 download,或是還沒download 的 state,script 就要在系統力。

系統的位置是 /etc/mender/scripts
裡面要有一個 "version" 檔,內容是一個數字,例如 3。代表 script 的版本。
放在 artifact (*.mender) 的,就要在 create mender file 時給定。
用 -s 選項就可以。
這格選項家的 state script,一定要用 Artifact 開頭,例如 ArtifactCommit_Enter_00,ArtifactInstall_Enter_00
把 scripts 都放到 state_script foldler,用 shell scipt 找出來就是...
#!/bin/bash
LIST=""
for f in state_scripts/Artifact*
do
	LIST="${LIST} -s ${f}"
done
echo ${LIST}

2021/8/12

bookmark: periodic trigger Azuer function

Azure App service 有一個簡單的 service : function。就是一個小程式。

2021/8/5

memo: 一些 ADAS 的縮寫

  • LDWS : Land Departure Warning System
  • FCWS : Forward Collision Warning System
  • Stop & Go : 開啟 ACC 時,因為交通壅塞而停止,壅塞解除,恢復行進後,自動啟動 ACCM
  • TLDS : Traffic Light Detection System
  • HMW : Headway Monitoring & Warning (前方車距偵測)
  • Cut in Warning : 旁車切入告警
  • Zigzag Driving Warning : 蛇行告警(自己還是別人?)
  • Temporal Stop Sign : 停車號誌 (Traffic Sign Recognition)
  • Reverse Driving Warning : 前車倒車告警
  • Red Light Violation Detection : 闖紅燈偵測
  • Pedestrian warning : 行人告警
  • MOD : Moving Object Detection
  • BSD : Blind Spot Detection
  • Rear ZigZag driving warning : 後方車輛蛇行告警
  • RCW : Rear Collision Warning,或是 BCW (Back Collision Warning)
  • Rear Zigzag Driving :
  • Zone30 : Zone 30 (traffic sign recognition)
  • Speed Limit Post : 速限號誌(traffic sign recognition

  • Face Detection
  • determine drowsiness or not
  • Head Pose detection : Yaw/Pitch/Roll
  • Distraction : Using Mobilephone
  • Distraction : Smoking
  • Distraction : Yawning
  • Eye ball taking
  • Wearing Mask
  • Landmark
  • Face Identification
  • Eye ball taking (Eye Gaze)
  • Seat Belt
  • OMS (Occupant Monitoring System)

2021/7/30

shell : find if contains substring in string

ref:how to check if string contains substring in bash

shell 直接有string 比對功能。所以可以用來檢查是不是包含某字串。
比較好看的是:
#!/bin/bash

STR=`uname -a`
SUB='Linux'
if [[ "$STR" == *"$SUB"* ]]; then
  echo "It's there."
fi
但是在 busybox 的 shell 不支援,所以要用另一個語法:
#!/bin/bash

STR=`uname -a`
SUB='Linux'

case $STR in

  *"$SUB"*)
    echo "It's there."
    ;;
esac

會查這個是因為要檢查 kernel 的 build date 政不正確...

2021/7/27

CCD Output Format , Dumb and Smart

CCD 依照輸出的格式,分為
  • dumb : 輸出 RAW 格式,就是 每個 pixel 的值,而且pixel 是 bayer 排列的,一行 RGRG..,下一行 GBGB..
  • smart: 輸出 CCIR 601/656, RGB888,YUV444,每個 pixel 都有完整的YUV/RGB
smart 就是把 dump 經過 debayer 後的輸出。

所以接收 RAW 格式的時候, 要處理 debayer,用內插把每一個 pixel 的三個顏色分量都復原。
這一般都是由 cpu 的 ISP (Image Signal Processor) 來做。

像 TI 的TM320DM357, Video Processing FrontEnd。
就是做類似的事情。
從這個 isp 的 datasheet 可以看到 isp 一般能處理的事情。

CCD 的話,像OV2718,輸出就只有 RAW

因為 debayer 的 algorithm 有很多,所以使用 RAW 格式的話,各家 image processor 的廠商會產出不同 image quality 。這就是各家的 know-how。

2021/7/21

         Mounting Kernel Configuration File System...
         Starting Apply Kernel Variables...
         Starting Update is Completed...

         Starting OpenSSH rsa Server Key Generation...
         Starting Login Service...
         Starting OpenSSH ed25519 Server Key Generation...
         Starting OpenSSH ecdsa Server Key Generation...

2021/7/13

mender : create release by hand

手動做 release deployment 是需要的,像使用 overlay rootfs 的時候, mender 找不到 root partition。
所以只能手動做。
官方文件是: create a snapshot on the golden device:

有 root partition 的 dump 後 (可以 loop mount 回來的 blk image)。
mender-artifact write rootfs-image -f /mnt/root-part.ext4 \
                                   -n artifact-name \
                                   --software-version 1.0 \
                                   -o snapshot-release.1.0.mender \
                                   -t device-type
就可以轉成上傳道 mender.io server 的 release 檔 (snapshot-release.1.0.mender)

另外,在 device 上, mender 也提供 dump 的功能 (跟 dd 一樣?):
USER="user"
HOST="host-ip"

mender snapshot dump --source /dev/mmcblk0p8 | ssh $USER@$HOST /bin/sh -c 'cat > $HOME/root-part.ext4`

要是要手動 install,可以 copy *.mender 到機器上之後(e.g: /data/1234.mender),用 mender 來安裝
mender -install /data/1234.mender
一樣會更新到另一個 partition,然後呼叫 fw_setenv 來更新開機參數。

2021/7/7

build and install sudo from source

ref: 一樣,.configure ,make 再 make install
configure:
一樣,--prefix= 代表 make install 要安裝的位置。

copy 到 target 上,complain libutil... 都 copy 過去,
sudo 要是 S bit set 而且 owner 要是 0 (root)
設完之後剩下 complain: /etc/sudo.conf

看一下 build 出來, install 目錄中 share/doc/sudo/examples/sudo.conf:
所以修改一下 /etc/sudo.conf
Plugin sudoers_policy /usr/lib/sudoers.so
出現 Error:
sudo: error in /etc/sudo.conf, line 14 while loading plugin "sudoers_policy"
sudo: unable to load /usr/lib/sudoers.so: /lib/libcrypt.so.1: version `XCRYPT_2.0' not found (required by /usr/lib/sudoers.so)

libcrypt copy 過去後,sshd fail to start:
Ambarella sshd[14584]: /usr/sbin/sshd: relocation error: /usr/sb
in/sshd: symbol EVP_KDF_ctrl version OPENSSL_1_1_1b not defined in file libcrypt
o.so.1.1 with link time reference
應該是系統原來就有 libcrypt,我用另一個版本覆蓋了導致。

evb 上 libcrypt.so 在 openssl 中,所以copy 過去 pi4。重新 configure
./configure --enable-openssl=/home/pi/openssl --prefix=/home/pi/sudoinstall
先 disable 全部..
  --disable-shared
        Disable dynamic shared object support.  By default, sudo
        is built with a plugin API capable of loading arbitrary
        policy and I/O logging plugins.  If the --disable-shared
        option is specified, this support is disabled and the default
        sudoers policy and I/O plugins are embedded in the sudo
        binary itself.  This will also disable the noexec option
        as it too relies on dynamic shared object support.
        
  --disable-shared-libutil
        Disable the use of the dynamic libsudo_util library.  By
        default, sudo, the sudoers plugin and the associated sudo
        utilities are linked against a shared version of libsudo_util.
        If the --disable-shared-libutil option is specified, a
        static version of the libsudo_util library will be used
        instead.  This option may only be used in conjunction with
        the --enable-static-sudoers option.

  --enable-static-sudoers
        By default, the sudoers plugin is built and installed as a
        dynamic shared object.  When the --enable-static-sudoers
        option is specified, the sudoers plugin is compiled directly
        into the sudo binary.  Unlike --disable-shared, this does
        not prevent other plugins from being used and the noexec
        option will continue to function.
一樣,都 disable,也 disable openssl,結果一樣要 XCRYOT_2.0,只插在 /etc/sudo,conf 不用了。
所以要試 cross_compile..

2021/7/6

陷阱: image length is zero in trainning dataset

就 keras 的 cats and dogs.
出現:
PIL.UnidentifiedImageError: cannot identify image file
google 一下,有人說用
import os
from PIL import Image
folder_path = 'data\img'
extensions = []
for fldr in os.listdir(folder_path):
    sub_folder_path = os.path.join(folder_path, fldr)
    for filee in os.listdir(sub_folder_path):
        file_path = os.path.join(sub_folder_path, filee)
        print('** Path: {}  **'.format(file_path), end="\r", flush=True)
        im = Image.open(file_path)
        rgb_im = im.convert('RGB')
        if filee.split('.')[1] not in extensions:
            extensions.append(filee.split('.')[1])
檢查 image 是不是合格。
果然,出現:
Traceback (most recent call last):in/cats/666.jpg  **
  File "checkimage.py", line 10, in 
    im = Image.open(file_path)
  File "/home/charles-chang/miniconda3/envs/tensorflow-gpu/lib/python3.8/site-packages/PIL/Image.py", line 2967, in open
    raise UnidentifiedImageError(
PIL.UnidentifiedImageError: cannot identify image file './cats_and_dogs_small/train/cats/666.jpg'
用 mediainfo 看 666.jpg,沒有內容。一看 file size 是 0

再加上 666 來 google ,出現這一篇
甚至把影像都看一下,確認真的是 Cats, Dogs,發現有些不正確的影像...

2021/7/5

-Werr=date-time

gcc 4.9 之後新增了一個 檢查的 option: date-time
  -Wdate-time
           Warn when macros "__TIME__", "__DATE__" or "__TIMESTAMP__"
           are encountered as they might prevent bit-wise-identical
           reproducible compilations.
這個好像是為了 reproducible build
就是讓任何人任何時間都能 build 出一樣的 binary 檔。

大概是 5.8 開始,修改了 source,不再用一些 _DATE_, _TIME_ 這種 build time 產生的 code。
因為這樣不同時間 build, binary image 就會不一樣。
然後 compiler 也增加了這個檢查 -Werror=date-time
compiler 檢查有用到DATE, TIME 這些 macro就會報錯。

所以 kernel 的 Makefile 增加了這個 compile option,配上 gcc 版本小於 4.9 的話,就會出現 Error,說不認識這個 option。
例如: rpi-linux 5.10,使用 default 的 pitools (4.8) 來 build 就會出錯。

2021/7/2

sudo ...

busybox 好像沒有包 sudo 進去。
只有:
config FEATURE_SUID_CONFIG
        bool "Enable SUID configuration via /etc/busybox.conf"
        default y
        depends on FEATURE_SUID
        help
        Allow the SUID/SGID state of an applet to be determined at runtime
        by checking /etc/busybox.conf. (This is sort of a poor man's sudo.)
        The format of this file is as follows:

        APPLET = [Ssx-][Ssx-][x-] [USER.GROUP]

        s: USER or GROUP is allowed to execute APPLET.
           APPLET will run under USER or GROUP
           (regardless of who's running it).
        S: USER or GROUP is NOT allowed to execute APPLET.
           APPLET will run under USER or GROUP.
           This option is not very sensical.
        x: USER/GROUP/others are allowed to execute APPLET.
           No UID/GID change will be done when it is run.
        -: USER/GROUP/others are not allowed to execute APPLET.

        An example might help:

        |[SUID]
        |su = ssx root.0 # applet su can be run by anyone and runs with
        |                # euid=0,egid=0
        |su = ssx        # exactly the same
        |
        |mount = sx- root.disk # applet mount can be run by root and members
        |                      # of group disk (but not anyone else)
        |                      # and runs with euid=0 (egid is not changed)
        |
        |cp = --- # disable applet cp for everyone

        The file has to be owned by user root, group root and has to be
        writeable only by root:
                (chown 0.0 /etc/busybox.conf; chmod 600 /etc/busybox.conf)
        The busybox executable has to be owned by user root, group
        root and has to be setuid root for this to work:
                (chown 0.0 /bin/busybox; chmod 4755 /bin/busybox)

        Robert 'sandman' Griebl has more information here:

2021/7/1

FVDW LVDA -- 不專心駕駛用的

Front Vehicle Departure Warning
Leading Vehicle Departure Alarm

都是一樣的東西。
就是在後車叭你之前先提醒你,該動了。

自己的速度很重要,因為只有在停著不動時,才須要通知。
要是用 GPS 的話,在橋下,隧道 就無法知道確實速度,還有 gps 速度會有約 10 sec 的延遲。

另外,比較好的還會配合紅燈轉綠燈的告警。

新的系統,配合 DMS 會作到當駕駛沒有注意前方時,才告警。

其實實做起來有點繁瑣,辨識出車尾 (要分車頭車尾,只偵測車尾)。
注意有時候路況刻意會有距離的時候 (網狀線),淨空區等待前車。都會有留距離的時候...

2021/6/26

Loading "BriskMenuFactory::BriskMenu" Failed

這個 ubuntu window manage (名子忘了),開機有時會出現 Error:
The panel encountered a problem while loading "BriskMenuFactory::BraskMenu".
有時候重開機就好,有時候會不行。

google 的結果,說暫時沒有解,在 20.10 也有一樣的問題。
遇到的時候,就 logout 再 login 一次就可以了。
-- 真神奇

2021/6/22

Try overlayfs in embeddes system.

copy initrd.img-5.4.83-v7+-overlay 出來,rename img.gz, unpack 後,
mkdir out && cd out
cpio -idv < ../initrd.img

ref: 這一篇的方法好像不需要 用到 initramfs,只需要在真正 init 起來之前做就可以。
修改 kernel boot cmd,init=/myinit
然後myinit 就啟動 /bin/sh

進入後,測試 command:
mkdir : OK
mount / /rom : OK
mount -o remount,ro /rom : fail, 說沒有 /proc/mounts

所以 myinit 要加上
mount -t proc none /proc
之後 remount OK

最後是
exec chroot /rom /linuxrc
這樣就可以了。
要測試就把上面改成
/bin/sh
就會開進 shell 測試在這時候的 command 有沒有用。

mount --move 是變更 mount 位置,這樣就不用 unmount 之後再 mount

成功! final version ( /overlay folder must be created in first):
#!/bin/sh

mount -t proc none /proc
mount -t tmpfs none /overlay

mkdir /overlay/lower
mkdir /overlay/upper
mkdir /overlay/worker
mkdir /overlay/newroot

mount / /overlay/lower
mount -o remount,ro /overlay/lower

mount -t overlay overlay -olowerdir=/overlay/lower,upperdir=/overlay/upper,workdir=/overlay/worker /overlay/newroot

exec chroot /overlay/newroot /linuxrc
測試...開機後,在 / create abc, ls to see if exist.
reboot, see abc disappear

用 mount 看:
overlay on / type overlay (rw,relatime,lowerdir=/overlay/lower,upperdir=/overlay/upper,workdir=/overlay/worker)
大概是成功了。

2021/6/16

overlay fs

用 pi 來測試功能一下,google 說 pi 的 raspi-config 就有 overlay fs 功能,給 kisok 用。

新 raspios 開啟console uart好像不用 dtoverlay disable_bt 了。
ref: uart configuration

改用 enable_uart=1
就會顯示了。(2021-1-11 的 image)
console 開到一半(通常是 usb hub init 後,就會變成亂碼...最後查到是 micro-usb power 線材的問題。
換上沒有 core 的線材就 OK 了。

用 raspi-config 開啟 overlay 功能,然後 read-only root 回答 Yes。
重開機後, / 就是 overlay 了:
mount:
overlay on / type overlay (rw,noatime,lowerdir=/lower,upperdir=/upper/data,workdir=/upper/work)
boot cmd:
$ cat /proc/cmdline 
coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_compat_alsa=0 snd_bcm2835.enable_hdmi=1 bcm2708_fb.fbwidth=656 bcm2708_fb.fbheight=416 bcm2708_fb.fbswap=1 vc_mem.mem_base=0x3ec00000 vc_mem.mem_size=0x40000000  boot=overlay console=ttyS0,115200 console=tty1 root=PARTUUID=3661545e-02 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwai
大概是:boot=overlay, root=UARTUUID...,rootfstype=ext4 之類。

raspberry pi 使用 overlayfs 作為 rootfs 可以參考overlayroot,有一堆都是寫 init script,取代 init,開機後重新 mount root。
raspberry pi 的 script 好像是 /etc/initramfs-tools/scripts/overlay

以前 raspi-config 還沒有 overlayfs 選項時,大概是用這一篇:overlay filesystem raspi 來做。
這一篇:easy change on a read only reaspberry pi 也有說明手動更改跟使用 raspi-config 修改的兩種方法。
這一篇 setting up overlay fs on raspberry pi還提供在 power off 時,把 upper layer 寫回 lower layer 的 方法,聲稱這樣可以大大個減少 sd card write 的次數 (增加壽命)

有關 overlayfs 的使用,可以參考play with overlay filesystem
任何兩個 目錄都可以疊成一個 overlay filesystem (目錄)。
以上面 ref 的例子來看。
有 upper, lower 兩個目錄:
charles-chang@beaver:~/test$ tree upper/
upper/
├── dir
│   └── g
├── e
└── f

1 directory, 3 files
charles-chang@beaver:~/test$ tree lower/
lower/
├── a
├── b
├── dir
│   ├── c
│   └── d
└── test
用 mount command 把 upper 疊在 lower 之上,最後成為 'merged' 目錄:
sudo mount -t overlay overlay -olowerdir=./lower,upperdir=./upper,workdir=./work merged
看看 merged 目錄:
tree merged/
merged/
├── a
├── b
├── dir
│   ├── c
│   ├── d
│   └── g
├── e
├── f
└── test
可以看到 upper 跟 lower合併在一起了。
因為 overlayfs 的 lower 是不能更動的,只能藉由 upper 來更動。
所以可以修改 merged 下的 file ,然後去 upper, lower 看看變化。
有一點要注意: lower 的內容在 mount 之後,就不要更動了,否則會有不可預知的問題。


在busybox 1.32.1
enable build static
make && sudo make install

2021/6/15

buildroot -- build system for qemu

buildroot 竟然變得這麼方便,幫你把 kernel 都弄好了。
clone 下來, 選 qemu 的 defconfig 後就 make,然後 kernel, dtb ,rootfs 都build 好,
接下來用 qemu 來 run 就可以。
以 qemu 來 run..

make qemu_arm_vexpress_defconfig
make

qemu-system-arm -M vexpress-a9 -smp 4 -m 1024M -kernel output/images/zImage -append "root=/dev/mmcblk0 console=ttyAMA0 loglevel=8" -dtb output/images/vexpress-v2p-ca9.dtb -sd output/images/rootfs.ext2 -nographic
就可以用 root 登入。


ref:编译运行Linux内核,制作initramfs,并通过qemu+gdb调试
用 qemu 跟 gdb 來 debug kernel:

build kernel and root (initrd):
download kernel source.
make menuconfig (不用改,只是要產生 .config)
make

build tools and shell:
download busybox
make menuconfig ( Build Option -- build static library)
make
sudo make install

create root structure:
.. 先跳過..

ref:深入理解 Linux 2.6 的 initramfs 機制 (上)
kernel 用上面的沒關係,記得 menuconfig 要改好。
做好 hello-initramfs 目錄。
build 好 kernel 的 bzImage
用下面command run..
qemu-system-x86_64 -s -kernel linux-4.9.263/arch/x86_64/boot/bzImage --append "console=ttyS0" -nographic

2021/6/10

好白痴: could not determine kind of name for C.X509_V_ERR_CA_KEY TOO_SMALL

在make go project時出現這個 error:
could not determine kind of name for C.X509_V_ERR_CA_KEY TOO_SMALL 
...
還有一堆 ssl 的 C.X509.
去看source 果然沒有宣告的地方。
但是在另一個 arm64 的機器上make 是 OK 的。
一直以為是 go version 問題(因為這個 project 曾經發生改版後,go version 過舊問題,也是搞很久)
然後 google 這格 error 竟然完全沒有!!
--- 其實這就代表 project 本身沒問題。是你自己的問題。

後來看到重點: C.

所以是 go 參考 C 的部份...
結果是這個 host 沒有安裝 libssl-dev
apt install libssl-dev 後就OK 了。

搞了兩小時....果然是白痴....

2021/6/9

bookmark: build arm natively from x86

買一片 pi4 還是用 vm ?
有 apt 的 distribution build 起來還是比較方便,不用處理 library 問題。
所以...


感覺起來還是在 pi4 上 build 比較快...(NT2000)

2021/6/8

HMW and FCW

Headway Monitor Warning , Forward Collision Warning

兩個很像,都是針對前方車輛的距離,速度做出的告警。
  • Headway Monitor Warning 是判斷安全車距,安全車距和自己的車速有關,自己的車速越快,安全車距就要越大。
  • Forward Collision Warning 是判斷撞上前車的時間,除了車距外,還跟相對速度有關。
兩個都用到和前車的距離,但是一個用自己的車速,一個用相對的車速。
車距用影像可以得到,自己的車速就要用GPS 或是車速線
相對車速就可以用影像得到。

所以兩者需要的 資料還是有一點不一樣。

但是只要做得出 HMW,就能做出 FCW,不做白不做....

2021/6/7

bookmark: bash with restricted command , rbash

project: 是在 build 的時後指定 user 和可以 run 的 command
./makefile.sh -v --init --force --test dummy date ls ssh
                                           |     |
                                           |     List of allowed commands
                                           |
                                        User's login name
m
然後就可以放在 /etc/passwd 中,讓那個 user login 後用那個 為他 build 的 shell

實際上上面的 script 會create user,並且設定 /etc/paswd,然後build 好只能run 限定 command 的 shell,然後 copy 到正確位置。

所以要有 root 權限 (useradd)

--- 要放到 embedded system 可能.....

build and run a small linux system to test busybox command

follow: linux-kernel busybox homemade linux system
大概就是...
  • download kernel source code,enable RAM initrd, 加大 ramdisk size, build bzImage.
  • download busybox, build static, make installl (create _install),然後修改一個 rootfs 的 directory structure. 然後用這個 _install folder 做出 rootfs 的 image
因為都是 native (x86_64),所以沒什麼問題。之後就可以用 qemu 開機了 (assign kernel and initrd file, and append bootcmd)
then..把 bzImage 和 rootfs.img.gz copy 出來.
qemu-system-x86_64 -kernel ./bzImage -initrd ./rootfs.img.gz -nographic -append "console=ttyS0 root=/dev/ram init=/linuxrc"
qemu default 會用 SDL,開啟 graphic console(雖然也是 console),所以要用 -nographic,然後加上 console=ttyS0,讓 console 從 ttyS0 到 command line console.
mount -o rw,remount /dev/root /
touch /etc/passwd
touch /etc/group
adduser charles
這樣,就可以su - charles,測試 root 和 charles (general user) 對 busybox command 的權限。

測試:

以 poweroff 為例,在 /sbin/poweroff,一樣 link 到 /bin/busybox
charles 執行的化,第一個,會找不到 poweroff,因為 path 中沒有 sbin
如果用 /sbin/poweroff:
poweroff: (null): Operation no permitted
但是,用 ip 來測試,一樣在 /sbin/ip。一樣說 not found,但是用 /sbin/ip 來執行,就可以正常執行了。
所以 poweroff 的 permission ,應該是設在 device 端的。
filesystem level 是沒有辦法限制 一般使用者透過 /sbin/ 來執行系統command

所以發現 busybox 這種 link 的方式,沒辦法限制一般 user 執行某些 root 才能執行的 command

mediapipe

安裝就是 git clone,但是 build 要用 bazel,說明是說有三個 option
  1. 使用 distribution 的 package manager 來安裝
  2. 使用 project 內的 setup_opencv.sh 來自動 download and install
  3. 自己參考 opencv 的文件來安裝

看一下 setup_opencv.sh:
會 /tmp/build_opencv download and build opencv 3.4,
OpenCV has been built. You can find the header files and libraries in /usr/local/include/opencv2/ and /usr/local/lib"
然後會 modify project 的相關build script/Makefile,把原來使用 package install path 的位置改道script 安裝的位置。

18.04 的話就是 package manager 安裝就可以。
然後source code 中的 build configuration 都是以 ubuntu 18.04 為準,所以要是其他 (20.04,或是option 2.3),就要修改一堆 .BUILD file 中 opencv, ffmpeg library 單裝 path

上面的,直接用 apt 安裝就可以。

example build tool 是 bazel,安裝用 bazelisk, bazelist 要用 npm 安裝,npm 要 nodejs...
也可以直接 download executable binary bazelisk,baselisk 可以直接當作 bazel 來用 (就名子不一樣)。

其他 package 依照 build hello world 的說明,用 apt 安裝就可以,
另外沒說,build hello world 還要 python, python3 跟 numpy

? install, build hello 後之後,選 python 然後說用 pip 安裝就可以?!
但是我的 python 是 3.7,所以找不到適合的 版本。
-- 只好用 conda create 一個 python=3.8 的環境,然後,conda 就可以安裝了!

用 python run example 很簡單,有些example 放在網頁的 code 是有點 bug,不過很明顯。大家都會改吧。

for python 說明頁面的最後有:building python framework. (這個可以用來 build gpu-support 的版本)

try python,installation 說明的是用 venv:
python3 -m venv mp_env && source mp_env/bin/activate
mediapipe 用 pip 安裝就可以 ...
(mp_env)$ pip install mediapipe
然後要用 python3

就可以用 python example 試試看...
face_detection:
在 venv 環境,run example 的 python code,就可以用 nb 的 webcam show 出結果。

2021/6/4

linux : tcp socket timeout

ref: 就是
 /proc/sys/net/ipv4/tcp_keepalive_time
default 是 7200 (2hrs)。

要自動的話,可以改 /etc/sysctl.conf
新的已經是 /etc/sysctl.d/
下面新增一個你的 mynet.conf
net.ipv4.tcp_keepalive_time=1800
* 注意內容表示和 /proc path 的關係,就是 /proc/sys 之後的 path,用 '.' 來代替。

2021/6/2

mender.io : mender-store 存放 update artifact 的資料

cli/command.go
commonInit( )

其中的 store 好像是放在 link 到 /usr/lib/mender (external) 的 mender-store

store (database) 相關,寫在 store/dbstore.go
裡面可以看到用 github.com/bmatsuo/lmdb-go/lmdb

所以更新紀錄會倍放到這個 db file。
因為這個folder (/usr/lib/mender) 要放到 persistant storage,然後 link 回來。
所以 system update 後,這個 folder 內容不會被變更。

這個 mender-store 用來存很多 data。例如 update 的 artifact version.
一旦有這個資訊,mender -show-artifact 就會用database 存的值,部會用 /etc/mender/artifact_info

更新 mender-store 中,artifact-info 的時機:
download, update 完,reboot 後,mender 會跟mender.io 回報,mender.io 顯示 100% 之後,mender 才會把 artifact-info (new version) 更新到 mender-store
所以update 完 reboot 的話,剛看 artifact 還會是舊的,一直到 mender report 完,才會顯示新的版本。

2021/6/1

mender.io : device 註冊到 正式版 (非 demo server)

機器上的 /etc/mender/mender.conf 中,有 TenantToken=""
把 mender.io 的 Setting -- Orgnizationand Billing 中 "Organization token" copy 貼上。

這樣就會改 register 到 自己的 mender.io 帳號。

2021/5/31

bookmark: bluethooth 5.0 and raspberry pi

ref:

Failed to Load the Server certificate.

結果是 openssl/ctx.go:
// LoadVerifyLocations tells the context to trust all certificate authorities
// provided in either the ca_file or the ca_path.
// See http://www.openssl.org/docs/ssl/SSL_CTX_load_verify_locations.html for
// more.
func (c *Ctx) LoadVerifyLocations(ca_file string, ca_path string) error {
    runtime.LockOSThread()
    defer runtime.UnlockOSThread()
    var c_ca_file, c_ca_path *C.char
    if ca_file != "" {
        c_ca_file = C.CString(ca_file)
        defer C.free(unsafe.Pointer(c_ca_file))
    }
    if ca_path != "" {
        c_ca_path = C.CString(ca_path)
        defer C.free(unsafe.Pointer(c_ca_path))
    }
    if C.SSL_CTX_load_verify_locations(c.ctx, c_ca_file, c_ca_path) != 1 {
        return errorFromErrorQueue()
    }
    runtime.KeepAlive(c)
    return nil
}
caller 是:
func loadServerTrust(ctx *openssl.Ctx, conf *Config) (*openssl.Ctx, error) {
    defaultCertDir, err := openssl.GetDefaultCertificateDirectory()
    if err != nil {
        return ctx, errors.Wrap(err, "Failed to get the default OpenSSL certificate directory. Please verify the OpenSSL setup")
    }
    sysCertsFound, err := nrOfSystemCertsFound(defaultCertDir)
    if err != nil {
        log.Warnf("Failed to list the system certificates with error: %s", err.Error())
    }

    // Set the default system certificate path for this OpenSSL context
    err = ctx.SetDefaultVerifyPaths()
    if err != nil {
        return ctx, fmt.Errorf("Failed to set the default OpenSSL directory. OpenSSL error code: %s", err.Error())
    }
    // Load the server certificate into the OpenSSL context
    err = ctx.LoadVerifyLocations(conf.ServerCert, "")
    if err != nil {
        if strings.Contains(err.Error(), "No such file or directory") {
            log.Warnf(errMissingServerCertF, conf.ServerCert)
        } else {
            log.Errorf("Failed to Load the Server certificate. Err %s", err.Error())
        }
        // If no system certificates, nor a server certificate is found,
        // warn the user, as this is a pretty common error.
        if sysCertsFound == 0 {
            log.Error(errMissingCerts)
        }
    }
    return ctx, err
}

2021/5/25

More on IIS2DLPC , Wake Up

ST 的 application note AN5201 有比較仔細的 wakeup 功能說明。

Section 5.4 Wake-up interrupt

Wakeup source 可以選 Hight-pass fileter 或 offset filter

選 offset filter 為 source 時..

三軸之中,任一軸的值大於 threshold
然後還要符合 duration,大於 threshold 的時間要超過 duration 的設定。

產生 interrupt 後的動作,如果沒有設定 latch mode,一旦 低於 threshold, interrupt 自動reset。
如果設定是 latch mode,一旦觸發 interrupt後,就會一直保持,直到 讀取了 WAKE_UP_SRC 或 ALL_INT_SRC

AN 中有使用 high pass filter 的設定範例,當sensor 有大幅度改變時..任一軸大於 threshold 後產生 interrupt。
write 64h in CTRL1(20h)
write 04h in CTRL6(25h)
write 20h in CTRL7(3fh)
write 00h in WAKE_UP_DUR(35h)
write 02h in WAKE_UP_THS(34h)
write 20h in CTRL4_INT1_PAD_CTRL(23h)
還有一個用USER OFFSET 做srce 的設定範例,採用 X-0. Y-0. Z-02 之後,大於 threshold 之後產生 interrupt (因為 duration 0)
write 04h in CTRL6(25h)
write 34h in CTRL7(35h)
write 00h in X/Y_OFS_USR(3ch,3dh)
write 40h in Z_OFS_USR(3eh)
write 00h in WAKE_UP_DUR(35h)
write 02h in WAKE_UP_THS(34h)
write 20h in CTRL4_INT1_PAD_CTRL(23h)
write 64h in CTRL1(20h)
發現 官方的 input driver 沒有宣告 CTRK7(3fh).
所以相關的 enable 也沒做,那個 input4/wake_up/enable 並沒有開啟 CTRK7 的 INTERRUPTS_ENABLE.

在 driver 的 i2c_write output log,開機有做的 register write..
[    0.945939] lis2dwi2c w 21 4c
[    0.949619] lis2dwi2c w 20 1
[    0.953205] lis2dwi2c w 22 10
[    0.956880] lis2dwi2c w 21 c
[    0.960467] lis2dwi2c w 36 1
[    0.964054] lis2dwi2c w 36 9
[    0.968054] lis2dwi2c w 30 9
[    0.971642] lis2dwi2c w 34 2
[    5.170523] lis2dwi2c w 20 11
[    5.175374] lis2dwi2c w 20 51
enable input4/wake_up/enable 的話..
[  198.030489] lis2dwi2c w 23 20
所以wakeup需要的幾乎都沒設
所以要再 enable 的時候都設好...

為了檢查,可能要把 intertupt status 讀出來,27h
                7           6         5           4              3         2       1      0
-----------------------------------------------------------------------------------------------
STATUS | 27h | FIFO_THS | WU_IA | SLEEP_STATE | DOUBLE_TAP | SINGLE_TAP | 6D_IA | FF_IA | DRDY |
在sysfs input4/wake_up 下增加一個 regv
cat 內容就是 27h 的內容。
測試..
# echo 1 > enable 
[  161.304567] --lis2dw12_enable_sensors--
[  161.308934] --lis2dw12_update_drdy_irq--
[  161.313552] lis2dwi2c w 23 20
[  161.317201] lis2dwi2c w 20 64
[  161.320851] lis2dwi2c w 25 4
[  161.324128] lis2dwi2c w 3f 20
[  161.327782] lis2dwi2c w 35 0
[  161.331009] lis2dwi2c w 34 2
[  161.334305] lis2dwi2c w 23 20
# 
# cat regv 
1
# cat regv 
0
# cat regv 
1
# cat regv 
0
----- 敲打機器 ----

# cat regv 
41
# cat regv 
40
WU_IA bit set,所以應該 OK

2021/5/24

bookmark : google 專利 : camera under the screen

原來螢幕挖一個洞,下面放一個會旋轉的菱鏡,可以控制反射到鏡頭或是一個小螢幕。
好麻煩,還要準備一個小 display...然後 camera 的進光量也會受影響。

是說這個缺口有這麼重要嗎.....其他更重要的修改還很多吧...

2021/5/21

systemd : run my script before shutdown.

ref: 所以試試看:
先產生..
# cat /etc/systemd/system/runmyshutdown.service 
[Unit]
Description=Run my custom task at shutdown
DefaultDependencies=no
Before=shutdown.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/myshutdown.sh
TimeoutStartSec=0

[Install]
WantedBy=shutdown.target
還有測試的 script:
# cat /usr/local/bin/myshutdown.sh 
#!/bin/bash
/bin/touch /data/sssss
然後..
# systemctl daemon-reload
# systemctl enable runmyshutdown
Created symlink /etc/systemd/system/shutdown.target.wants/runmyshutdown.service → /etc/systemd/system/runmyshutdown.service.
原來沒有 /etc/systemd/system/shutdown.target.wants 這個目錄。
因為 WantedBy 的關係,所以新 create。

測試,reboot 後,到 /data 去看,果然有 ssss,所以是成功了。

G Sensor: IIS2DLPC, wakeup function

Control Register 4 (23h):
  • bit 5: INT1_WU : 將 wakeup 狀態由 INT1 輸出
Status Register (27h):
  • bit 6: WU_IA : Wake Up Event Status
WAKE_UP_THS (34h):
  • bit 7: Single/Double : 0 - only single tap enabled, 1 - single and double tap enabled
  • bit 6: Sleep on : 0 - sleep disabled, 1 - enabled
  • bit 0-5 : threshold, 6bit unsigned
WAKE_UP_DUR (35h): -- 太複雜了...

WAKE_UP_SRC (38h):
  • bit 5 : Free Fall
  • bit 4 : Sleep Event Detected
  • bit 3 : Wake Up Event Detected
  • bit 2 : Wake Up Event on X
  • bit 1 : Wake Up Event on Y
  • bit 0 : Wake Up Event on Z
ALL_INT_SRC (3bh): 讀取這個 register 會 reset 所有 關聯到 INT 的 interrupt 狀態
  • bit 1: Wake Up
X/Y/Z_OFS_USR(3ch,3dh,3eh): 不知道是什麼意思
  • bit 0-7 : wo's complement user offset value on X/Y/Z-axis data, used for wakeup function
linux kernel driver 有時做出四個input device driver
  • input0 : accel
  • input1 : free_fall
  • input2 : tap
  • input3 : double_tap
  • input4 : wake_up
其中 input0, accel 的 configurable 內容有 enable, polling_rate, resolution, scale, scale_avail
其他的input1~4 都只有 enable 而已。

source code:
static DEVICE_ATTR(enable,
           S_IWUSR | S_IRUGO,
           lis2dw12_get_enable,
           lis2dw12_set_enable);
然後..
static struct attribute *lis2dw12_accel_attribute[] = {
    &dev_attr_enable.attr,
    &dev_attr_resolution.attr,
    &dev_attr_polling_rate.attr,
    &dev_attr_scale_avail.attr,
    &dev_attr_scale.attr,
    NULL,
};

static struct attribute *lis2dw12_step_ff_attribute[] = {
    &dev_attr_enable.attr,
    NULL,
};

static struct attribute *lis2dw12_tap_attribute[] = {
    &dev_attr_enable.attr,
    NULL,
};

static struct attribute *lis2dw12_double_tap_attribute[] = {
    &dev_attr_enable.attr,
    NULL,
};

static struct attribute *lis2dw12_wakeup_attribute[] = {
    &dev_attr_enable.attr,
    NULL,
};
可以看到,所有 inputX 的 attrib enable 都共用 function : lis2dw12_set_enable( )
然後 lis2dw12_set_enable( ) 依照傳入的 dev 決定是那一個 input call 的..

查 enable --- 最後到 _lis2dw21_enable_sensors( ),先..err = lis2dw12_update_drdy_irq(sdata),啟動 INT1..
CTRL4 就是控制 interrput 到 INT1 的開關
     7            6             5        4         3            2           1           0
------------------------------------------------------------------------------------------------
| INT1_6D | INT1_SINGLE_TAP | INT1_WU | INT1_FF | INT1_TAP | INT1_DIFF5 | INT1_FTH | INT1_DRDY |
------------------------------------------------------------------------------------------------
其中,INT1_WU 就是 wake up
static int lis2dw12_update_drdy_irq(struct lis2dw12_sensor_data *sdata)
{
    u8 reg_addr = LIS2DW12_CTRL4_INT1_PAD_ADDR, reg_val, reg_mask;

    switch (sdata->sindex) {
    case LIS2DW12_FF:
    case LIS2DW12_TAP:
    case LIS2DW12_DOUBLE_TAP:
    case LIS2DW12_WAKEUP:
        reg_val = lis2dw12_event_irq1_value(sdata->cdata);
        reg_mask = LIS2DW12_INT1_EVENTS_MASK;
        break;
    case LIS2DW12_ACCEL:
        return 0;
    default:
        return -EINVAL;
    }

    return lis2dw12_write_data_with_mask(sdata->cdata, reg_addr, reg_mask,
                       reg_val >> __ffs(reg_mask), true);
}
所以driver 中,Wakeup 的 threshold, duration 都是hard-code,不是 configurable.
sysfs 中只有 enable/disable 功能。就是把 interrupt 開啟/關閉。
#define LIS2DW12_WAKE_UP_THS_WU_MASK    0x3f
#define LIS2DW12_WAKE_UP_THS_WU_DEFAULT 0x02


static int lis2dw12_init_sensors(struct lis2dw12_data *cdata)
{
      ...
      err = lis2dw12_write_data_with_mask(sdata->cdata,
                      LIS2DW12_WAKE_UP_THS_ADDR,
                      LIS2DW12_WAKE_UP_THS_WU_MASK,
                      LIS2DW12_WAKE_UP_THS_WU_DEFAULT, true);
這個 write_data_with_mask( )就是 read, mask, update 的動作。
mask 就是把這幾個 bit 都 clear,然後填熱 後面的 data。
然後 DURATION 都沒設。

--- 結果沒這麼簡單...

查 application note AN5201, 5.1 Interrupt pin configutation:
All the embedded function interrupt signals are subordinate to the INTERRUPTS_ENABLE bit in register CTRL7.
If this bit is set, the embedded functions are enabled and the interrupts signals can be routed to the INT1 and/or
INT2 pins; otherwise, if this bit is not set, the embedded functions are disabled
可是 source code 中沒有看到CTRL7 的宣告,也沒有設定。(除非 reset value 是 SET)
所以 INT1 沒有動作。