Gameshell LaucherGo on Raspberry Pi

OS

  • Raspberry Pi OS Lite

Required packages

  • xserver-xorg
  • xinit
  • x11-xserver-utils
  • aria2c
  • feh

Source Repositories

Hardware

  • Raspberry Pi 3 or 4
  • 4.3inch Capacitive Touch Display for Raspberry Pi

Prepare the account

Checkout source repositories

 git clone https://github.com/clockworkpi/launcher git clone https://github.com/clockworkpi/launchergo git clone https://github.com/clockworkpi/LauncherGoDev git clone https://github.com/cuu/gsnotify 

Compile gsnotify

The gsnotify-arm, which is carried with the repository, cannot be executed on Raspberry Pi. We must build a new one.

 cd gsnotify go build -o gsnotify-arm main.go cp gsnotify-arm /home/cpi/launcher/sys.py/gsnotify/ 

Compile launchergo

The build process lasts very long time. Be patient !

 cd /home/cpi/LauncherGoDev ./build.sh ./deploy.sh 

Start launchergo

 cd /home/cpi/launchergo sudo startx /home/cpi/launchergo/.xinitrc -- -xf86config /home/cpi/launchergo/.xorg_lima.conf -nocursor -logfile /tmp/xorg.log > /tmp/x.log 2>&1 

Enjoy !

SenseCAP M1 Raspberry Pi Shield

Overview

SenseCAP M1 is an indoor hotspot Helium miner. The brand one costs about 200 Euro.

There are also many SenseCAP M1 Raspberry Pi shield available on eBay. Some of them are sold together with a WM1302 LoRaWAN gateway module. But they are much cheaper and only about 40 Euro. It is a very good choice if you want to build a LoRaWAN gateway.

In this article I will neither talk about Helium network nor SenseCAP M1 miner. I will only show some information I found about the pi shield.

On website a block diagram of SenseCAP M1 can be found:

Beside the WM1302 there are some other interfaces which are very useful:

  1. 1x WM1302
  2. 1x USB-C charging port
  3. 1x User button
  4. 1x Three pin socket for fan
  5. 1x User LED
  6. Crypto authentication chip ATECC608

Pinout

The only issue is that there is no schematic available on the internet. After searching the internet and my own research I found that the pinout definition of SenseCAP is almost the same with Seeed Studio WM1302 Pi Hat. The pinout is shown below:

User LED

In oder to activate the user led, you need to add lines in /boot/config.txt :

 dtoverlay=gpio-led,gpio=22,label=lorawan,trigger=heartbeat 

User Button

The pinout of user button is currently unknown.

ATECC608

using flowing command to detect the chip

 sudo i2cdetect -y 1 
 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- 39 -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: 60 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- -- 

where the 0x60 is the address of ATECC608 chip, the 0x39 could be the address of temperature sensor.

The easy way to communicate with this chip is to use the cryptoauthtools written by Python

Read the information from ATECC608 using the following command:

 sudo python3 info.py -i i2c 

You may get the following information:

 Device Part: ATECC608 Serial number: 01 23 E3 D7 2F E5 F6 09 EE Configuration Zone: 01 23 E3 D7 00 00 60 02 2F E5 F6 09 EE C1 39 00 C0 00 00 00 97 20 97 20 97 20 97 20 97 20 97 20 97 20 AF 8F 97 20 97 20 97 20 97 20 97 20 97 20 97 20 97 20 FF FF 97 20 00 00 00 00 FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF 00 00 00 00 00 00 33 00 33 00 33 00 33 00 33 00 33 00 33 00 33 00 33 00 33 00 33 00 33 00 33 00 33 00 33 00 33 00 Check Device Locks Config Zone is locked Data Zone is locked Loading Public key -----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEc4bZ+lFUN3TRuEfiwBx0YvTJGblh P2gwy+56j+lk4Jxg5+SWteJ10rRV3zK0IT1lfMCstbNW5NNopURkLYL9FQ== -----END PUBLIC KEY----- Done 

References:

  1. https://www.seeedstudio.com/WM1302-LoRaWAN-Gateway-Module-SPI-EU868-p-4889.html?queryID=10b94b0d972e982eaa0bc7d0bd0de998&objectID=4889&indexName=bazaar_retailer_products
  2. https://www.seeedstudio.com/WM1302-Pi-Hat-p-4897.html?queryID=ae954dcd8a0390def3d53d396e5732fd&objectID=4897&indexName=bazaar_retailer_products
  3. https://www.sensecapmx.com/docs/sensecap-m1/overview/
  4. https://github.com/seeed-lora/WM1302-doc

RAK WisCellular Part2: Connect with Raspberry Pi

In the last post long time ago, I have brief introduced the RAK WisCelluar NB-IoT board and showed the control interface through AT command. Originally RAK WisCelluar is designed for Arduino compatible boards like Arduino UNO. Thanks to the Micro USB A interface, it has the oppertunity to connect other board which has USB port. In this post I would like to share my experince with ModemManage and NetworkManager in Raspberry Pi.

  1. Hardwares
    • 1 Raspberry Pi 3
    • 1 RAK WisCelluar (BG96)
    • SIM card from HOLOGRAM
    • 2 USB Type A calbes
    • Power adapter
  2. Softwares
    • Raspbian Buster
    • ModemManager
    • NetworkManager
    • picocom
  3. Check BG96 Information
    • VENDOR ID: 0x2C7C
    • PRODUCT ID: 0x0296
  4. Start-up
    Firstly, power up Raspberry Pi, at the same time the red led of power indicator on RAK WikCellular will also on. Secondly, reset RAK WikCellular by pushing PWRKEY button, the green LED will be always on and the blue LED will slowly blinky ( off is long than on).

In the dmesg, you will get:

In the lsusb, you will get USB information from BG96

In the above two pictures, they show that the Linux has found the BG96 chip and created four cdc-wdm ports ( /dev/ttyUSB0 – /dev/ttyUSB2) for the commnication like AT command and NMEA and one /dev/cdc-wdm0 port from qmi_wwan driver.

  1. Check modem information with mmcli
 nmcli -m 0 

You may find that the BG96 modem is in the state “disabled”. So we need to “enable” it through

 sudo mmcli -m 0 --enable 

The state maybe be changed from “searching” to “registered” in a long time, until it finds and registers with a base station nearby.

The picture above shows that the WisCellular has registered with Telekom Deutschland with the old access tech GSM but not Cat-M1.

  1. Use CAT-M1 (LTE-M)

At first, search base stations nearby:

 $ sudo picocom -b 115200 /dev/ttyUSB2 

The BG96 has found 3 base stations which are supporting GSM, from that only Telekom has the capability of LTE Cat-M1.

The Default searching sequence of BG96 is Automatic, GSM, LTE Cat M1 and LTE Cat NB1. With the following AT commands sequence in picocom, the BG96 is forced to connect Cat-M1 only.

 $ AT+QCFG="nwscanseq",02,1 $ AT+QCFG="nwscanmode",3,1 $ AT+QCFG="iotopmode",0,1 $ AT&W 

After a moment, the BG96 will find and register in the Telekom Cat-M network. The blue LED is still blinky but on is longer than off.

  1. Change to Raw IP Mode

BG96 has only support of raw IP mode (IP packets not encapsulated in Ethernet frames).
It should be manually enabled every time after the modem is reset.

 $ sudo ifconfig wwan0 down $ echo 1 | sudo tee /sys/class/net/wwan0/qmi/raw_ip > /dev/null $ sudo ifconfig wwan0 up 
  1. Add a connection with APN
 $ sudo nmcli c add type gsm ifname "cdc-wdm0" apn "HOLOGRAM" 

At the same the WisCelluar will get a IP address from the gateway. And it is also ready for the internet connection.

  1. Ping something
 $ ping www.google.com 
  1. Hologram Diagram

You will also get data usage information from Hologram console

Have fun!

References:
1. https://www.mail-archive.com/modemmanager-devel@lists.freedesktop.org/msg05512.html
2. https://www.freedesktop.org/software/ModemManager/man/1.0.0/mmcli.8.html
3. https://bacnh.com/quectel-linux-usb-drivers-troubleshooting/

STM32F746 Discovery and U-boot

  1. Compile U-boot
 $ git clone git://git.denx.de/u-boot.git $ cd u-boot $ make ARCH=arm CROSS_COMPILE=arm-none-eabi- stm32f746-discovery_defconfig $ make ARCH=arm CROSS_COMPILE=arm-none-eabi- menuconfig deselect "SPL / TPL --> Activate Falcon Mode" $ make ARCH=arm CROSS_COMPILE=arm-none-eabi- 

2. Flash U-boot

 #!/bin/bash openocd -f interface/stlink-v2-1.cfg -f board/stm32f7discovery.cfg \ -c "init" \ -c "reset init" \ -c "flash probe 0" \ -c "flash info 0" \ -c "flash write_image erase $UBOOT_PATH/spl/u-boot-spl.bin 0x08000000" \ -c "flash write_image erase $UBOOT_PATH/u-boot.bin 0x08008000" \ -c "reset run" \ -c "shutdown" 

Install libubox, uci, ubus and procd on Ubuntu

Requirements:

  1. sudo apt install lua5.1
  2. sudo apt install liblua5.1-0-dev
  3. sudo apt install libjson-c-dev

libubox
$ git clone git://git.openwrt.org/project/libubox.git
$ cd libubox
$ mkdir build
$ cd build
$ cmake ..
$ make
$ sudo make install

ubus
$ git clone git://git.openwrt.org/project/ubus.git
$ cd ubus
$ mkdir build
$ cmake ..
$ make
$ sudo make install

uci
$ git clone git://git.openwrt.org/project/uci.git
$ cd uci
$ mkdir build
$ cmake ..
$ make
$ sudo make install

procd
$ git clone git://git.openwrt.org/project/procd.git
$ cd procd
$ mkdir build
$ cmake ..
$ make
$ sudo make install

ldconfig
$ sudo ldconfig /usr/local/lib

A simple test of the brand new LoRa packet forwarder “basicstation” in OpenWRT

What is Basic Station

NOTE: The current basicstation package in lora-feed can be compiled with OpenWRT master tree without extra effort. Please have a try.

The previous Lora packet forward is well known as packet_forwarder. It is widely used in most homebrew and commercial LoRa gateways. But it has no official updates for quite a long time. The successor from TheThingsNetwork has appear for a while and also already stopped developing.

Basic Station is officially a new implementation of LoRa packet forwarder, which further can be remotely managed by some Configuration and Update Server (CUPS). The original version of Basic Station is build with Make for PC and RPi(Debian) which have normal glibc library. In this small post I will show to compile Basic Station under OpenWRT environment with musl library.

Hardware

  1. RPi 2 or 3
  2. Ic880a
  3. RPi to iC8880a HAT

Software

  1. OpenWRT Toolchain/Build for RPi
  2. basicstation with OpenWRT support link

Setup

  1. Locate the staging dir of toolchain name or build environment
  2. Fill them to the following script
 export STAGING_DIR=$YOUR_STAGING_DIR export TOOLCHAIN_DIR=$STAGING_DIR/$YOUR_TOOLCHAIN_NAME export LDCFLAGS=$TOOLCHAIN_DIR/usr/lib export LD_LIBRARY_PATH=$TOOLCHAIN_DIR/usr/lib export PATH=$TOOLCHAIN_DIR/bin:$PATH 

Take the RPi I use for instance, the toolchain name is toolchain-arm_cortex-a7+neon-vfpv4_gcc-7.4.0_musl_eabi

  1. Make a soft link for the toolchain in $HOME
 ln -s $TOOLCHAIN_DIR $HOME/toolchain-rpi 
  1. Clone the source code
 git clone https://github.com/xueliu/basicstation cd basicstation git checkout feature/openwrt-musl 
  1. Compile
 make platform=openwrt variant=std 
  1. Copy
    • In the source code folder, a new folder called build-rpi-std, which holds the binary and libraries, is generated;
    • Copy build-rpi-std/bin/station to the bin/ or /usr/bin in RPi;
    • Copy the folder example/live-s2.sm.tc and all of its content to the $HOME of RPi;
  2. Run
    In folder live-s2.sm.tc call the program live-s2.sm.tc with the path of spi-dev which connects the iC880a.
 RADIODEV=/dev/spidev0.0 station 

Enjoy.

RAK WisCellular Part 1: Overview

What is WisCellular ?

The WisCellular is a NB-IoT Arduino shield which is a upgraded version of WisLTE with global regulatory certification. I am very luck to the one of ten Betas selected for evaluating it before launching.

Which Specifications of WisCellular ? *

  • Cellular Module – Quectel BG96
  • Cellular Network
    • 2G/GSM (GPRS/EPGRS)
    • 4G/LTE-M (Cat M1)
    • NB-IoT (Cat NB1)
  • Global Bands
    • EDGE/EGPRS: 850/900/1800/1900MHz
    • LTE FDD: B1/B2/B3/B4/B5/B8/B12/B13/B18/B19/B20/B26/B28
    • LTE TDD: B39 (For Cat M1 Only)
  • Data Rate
    • Cat M1: Max. 300Kbps (DL), Max. 375Kbps (UL)
    • Cat NB1: Max. 32Kbps (DL), Max. 70Kbps (UL)
    • EDGE: Max. 296Kbps (DL), Max. 236.8Kbps (UL)
    • GPRS: Max. 107Kbps (DL), Max. 85.6Kbps (UL)
  • Voice – VoLTE (Cat M1 Only)
  • SMS – Point-to-point MO and MT, SMS Cell Broadcast, Text and PDU Mode
  • GNSS (optional) – GPS
  • Peripheral Interface
    • Micro USB port
    • Arduino Headers
    • 8-pin PCM Audio Header
    • 6-pin Digital/Analog I/O Header
    • 4-pin Debug Header
    • Micro SIM Card Slot
  • LED – Red (Board power-on), Green (Cellular module power-on), and Blue (Network status)
  • Power Consumption
    Min 10uA @ Power Saving Mode (PSM) for 4G/LTE Cat M1 and Cat NB1
    Max 190mA @ 23dBm for 4G/LTE Cat M1 and 78mA @23dBm for 4G/TE Cat NB1
  • Dimensions – 68.58×53.34 mm
  • Temperature Range
    • Operating: default: -35°C ~ +75°C; extended: -40°C ~ +85°C
    • Storage: -40°C ~ +90°C
  • Standards – 3GPP E-UTRA Release 13
  • Compliance – FCC, CE, WEEE, RoHS, Japan TELEC/JATE

What are inside package?

I received a paper box with WisCellular shield, Type A to Micro B USB cable, a LTE antenna, a GPS antenna and a tape for fixing GPS antenna.

Hardware required

  • WisCellular shield
  • USB Cable
  • Hologram global IoT SIM card

Software required

  • QNavigator 1.4 link

In the first review I will not write any code or command. But we could still get a lot of information and test many functions of WisCellular . Let’s start!

Register Hologram SIM card

At first, please follow the tutorial to register your Hologram SIM card.

Install USB driver link

Download and click, continue and continue until the installation is finished.

Connect the WisCellular

  • Connect WisCellular with a Windows PC via a Type A to Micro B USB cable. Click the PWRKEY of WisCellular. After a few second you will see the green light will on and meanwhile three virtual COM ports are detected in the “Device Manager”:
    Capture

    • Quectel USB AT Port – AT command port, it is the primier interface for testing;
    • Quectel USB DM Port – Diagnose port, it is used for FWupgrade and catching log;
    • Quectel USB NMEA Port – GNSS port, it is used for streaming GNSS data;
  • Open QNavigator.
  • Select UMTS/HSPA even though BG96 only supports GPRS, since I want to test GNSS function. Then click Next and Start until the main interface is shown
    QNavigator_1

  • Click the serial port icon to setup parameters
    QNavigator_2

  • Select the AT port shown above. The other parameters are shown below

    • Baudrate: 115200
    • DatabBits: 8
    • StopBits: 1
    • Parity: None
    • FlowCtrl: None
      QNavigator_7
  • Click “Connect to module”. The QNavigator will try to talk with BG96 via AT commands. You will get a not important warning message and at the same time different kinds of information are also requested and shown.
    QNavigator_3

Do you have NB-IoT service nearby ?

I think the NB-IoT service is not supported every where in Germany. We could use a simple command to test whether the base station nearby has support of NB-IoT.

  • Click “AT Command” in the sidebar and find the COPS command. Click “Send” button and wait a few seconds, BG96 will show the information of the base stations nearby. From the last number of each group, the services can be identified:
    • 0 = GSM
    • 8 = LTE Cat M1
    • 9 = LTE Cat NB1

It is easy to see, in the area where I am living, there is currently no NB-IoT service :(.QNavigator_4

GNSS test

  • Click “AT Command” in the sidebar. The system will select the right serial port for you. Then you can simply click the “Connect” button.
    QNavigator_5

  • The GNSS function will be activated. After a about 1 minute the position information will be shown below.
    QNavigator_6

[Dissecting OpenWRT] #4: /lib/functions/network.sh

OpenWRT/LEDE has alread provided us some useful shell functions to find information from physical and logical interfaces. In this post I will show you some functions from this file.

  1. Inquire the first IP address of logical interface: network_get_ipaddr
    Create a shell script called test.sh and fill it with
 #!/bin/sh . /lib/functions/network.sh if [ "$#" -ne 1 ];then echo "Syntax:$0 interface" exit 1 fi network_get_ipaddr ip $1 echo $ip 

you can use like this

root@OpenWrt:/# chmod a+x test.sh root@OpenWrt:/# ./test.sh lan 
  1. Inquire L3 network device: network_get_device
 #!/bin/sh . /lib/functions/network.sh if [ "$#" -ne 1 ];then echo "Syntax:$0 interface" exit 1 fi network_get_device ifname $1 echo $ifname 

you can use like this

root@OpenWrt:/# ./test.sh lan br-lan 
  1. Inquire the first IPv4 subnet of logical interface: network_get_subnet
 #!/bin/sh . /lib/functions/network.sh if [ "$#" -ne 1 ];then echo "Syntax:$0 interface" exit 1 fi network_get_subnet subnet $1 echo $subnet 

you can use like this

root@OpenWrt:/# ./test.sh lan 192.168.10.1/24 
  1. Inquire IPv4 gateway of logical interface: network_get_gateway
 #!/bin/sh . /lib/functions/network.sh if [ "$#" -ne 1 ];then echo "Syntax:$0 interface" exit 1 fi network_get_gateway gateway $1 echo $gateway 

you can use like this

root@OpenWrt:/# ./test.sh lan 192.168.10.1 
  1. Inquire DNS server of logical interface: network_get_dnsserver
 #!/bin/sh . /lib/functions/network.sh if [ "$#" -ne 1 ];then echo "Syntax:$0 interface" exit 1 fi network_get_dnsserver dnsserver $1 echo $dnsserver 

you can use like this

root@OpenWrt:/# ./test.sh lan 192.168.10.1 
  1. Inquire protocol of logical interface: network_get_protocol
 #!/bin/sh . /lib/functions/network.sh if [ "$#" -ne 1 ];then echo "Syntax:$0 interface" exit 1 fi network_get_protocol proto $1 echo $proto 

you can use like this

root@OpenWrt:/# ./test.sh lan static 
  1. Inquire current state of logical interface: network_is_up
 #!/bin/sh . /lib/functions/network.sh if [ "$#" -ne 1 ];then echo "Syntax:$0 interface" exit 1 fi network_get_protocol $1 if [ $? = 0 ];then echo "up" else echo "down" fi 

you can use like this

root@OpenWrt:/# ./test.sh lan up root@OpenWrt:/# ifconfig eth0 down root@OpenWrt:/# ./test.sh lan down 

enjoy network.sh. 🙂

 

Build LoRaWAN Gateway with Ci40 and iC880a

In this post I will show you how to setup an OpenWRT based LoRaWAN gateway with Ci40 and iC880a. It has a user-friedly interface to config parameters.

#Hareware#

  1. IMST iC880a SPI concentrator board
  2. Pigtail for antenna
  3. Ci40
  4. Power Supply 2A with micro USB
  5. MicroSD card
  6. RPi to iC8880a interface

Ci40 has a 40-pin header which is though not 100% compatible with Raspberry Pi. The header make it possible to connect lots of Raspberry Pi HATs such as this backplane from Tindie.

Pins

iC880a Description RPi pin Ci40 pin
21 Supply 5V 2 5v to Raspberry Pi board
22 GND 6 GND
13 Reset 22 MFIO_21
14 SPI CLK 23 SPI_M0_MCLK
15 SPI MISO 21 SPI_M0_MISO
16 SPI MOSI 19 SPI_M0_MOSI
17 SPI Nss 24 SPI_M0_CS2 (MFIO_28)

Setting up the software

OpenWrt Preparation

At first, please following the instruction to prepare the OpenWrt on SD card. I prefer the SD card boot not normal because I want to save lifecycles of NAND FLASH on my board.

you don’t need to compile your own OpenWrt with lora-feed. I have prepared compiled ipk packages for this tutorial. Downloading these packages and copy to the SD card.

Install LoRa packages

From computer log in your Ci40 from serial or ssh.

$ opkg install libc_1.1.15-1_pistachio.ipk
$ opkg install libloragw_5.0.1-1_pistachio.ipk
$ opkg install lora-gateway-tests_5.0.1-1_pistachio.ipk
$ opkg install lora-gateway-utils_5.0.1-1_pistachio.ipk
$ opkg install packet-forwarder-utils_4.0.1-1_pistachio.ipk
$ opkg packet-forwarder_4.0.1-1_pistachio.ipk
$ opkg luci-app-pkt-fwd_1.0-1_all.ipk

You have all the necessary programs to start your LoRa gateway.
In my configuration, I use global_conf.json only and it will be automatically generated by Lua script. So please do not edit this file. The default configuration is for basic EU686 without GPS and beacon supports. You can change the configuration from LuCI interface as you wish.

Start LoRa gateway

Run the packet-forwarder by

$ /etc/init.d/lora_pkg_fwd start

If you want to automatically start after boot, you can do it by

$ /etc/init.d/lora_pkg_fwd enable

Meet /etc/preinit again

*/etc/preinit is the init program after Linux kernel booted. It is briefly introduced in OpenWRT/LEDE: System Boot Sequence. This time I will meet it again and dive into it to search some internals.

As a reference, the code of preinit is shown below. And it is assumed that $PREINIT is already initialized.

 #!/bin/sh # Copyright (C) 2006-2016 OpenWrt.org # Copyright (C) 2010 Vertical Communications [ -z "$PREINIT" ] && exec /sbin/init export PATH="%PATH%" pi_ifname= pi_ip=192.168.1.1 pi_broadcast=192.168.1.255 pi_netmask=255.255.255.0 fs_failsafe_ifname= fs_failsafe_ip=192.168.1.1 fs_failsafe_broadcast=192.168.1.255 fs_failsafe_netmask=255.255.255.0 fs_failsafe_wait_timeout=2 pi_suppress_stderr="y" pi_init_suppress_stderr="y" pi_init_path="%PATH%" pi_init_cmd="/sbin/init" . /lib/functions.sh . /lib/functions/preinit.sh . /lib/functions/system.sh boot_hook_init preinit_essential boot_hook_init preinit_main boot_hook_init failsafe boot_hook_init initramfs boot_hook_init preinit_mount_root for pi_source_file in /lib/preinit/*; do . $pi_source_file done boot_run_hook preinit_essential pi_mount_skip_next=false pi_jffs2_mount_success=false pi_failsafe_net_message=false boot_run_hook preinit_main 

The first part includes three group of parameters. They are initilized for the functions which are located in hook list.

Then we come to five hook lists which are initilized by boot_hook_init which is located in /lib/function/boot.sh. The code of boot_hook_init is show below.

 boot_hook_init() { local hook="${1}_hook" # add suffix _hook to every hook lister name export -n "PI_STACK_LIST=${PI_STACK_LIST:+$PI_STACK_LIST }$hook" export -n "$hook=" } 

The point of this function is the command export -n. The option -n means remove the export property from each NAME. It marks each NAME not export to the environment and child-env, but other functions in this shell can still use this variable.

 for pi_source_file in /lib/preinit/*; do . $pi_source_file done } 

preinit is then looking for and excute every files located /lib/preinit/. The name of such file is starting with a number and then the name of function. Each file has similar structure as

 function_name1() { #STATEMENTS } ... function_name2() { #STATEMENTS } boot_hook_add hook_name function_name1 boot_hook_add hook_name function_name2 

There can be multiple functions and later using boot_hook_add to add into a certain hook linker. So what happens in boot_hook_add is shown below

 boot_hook_add() { local hook="${1}_hook${PI_HOOK_SPLICE:+_splice}" local func="${2}" [ -n "$func" ] && { local v; eval "v=\$$hook" export -n "$hook=${v:+$v }$func" # add function to responding hook list } } 

After adding functions, preinit excutes the functions located in preinit_essential by

 boot_run_hook preinit_essential 

here the definition of boot_run_hook is shown below

 boot_run_hook() { local hook="$1" local func while boot_hook_shift "$hook" func; do local ran; eval "ran=\$PI_RAN_$func" [ -n "$ran" ] || { export -n "PI_RAN_$func=1" $func "$1" "$2" # excute the function } done } 

Here boot_hook_shift loops every functions in “$hook” until the last one. It returns 0 as true when there is still functions in “$hook” and returns 1 as false when reaching the end. The code of boot_hook_shift is shown below:

 boot_hook_shift() { local hook="${1}_hook" local rvar="${2}" local v; eval "v=\$$hook" # $$hook includes all the registered functions [ -n "$v" ] && { local first="${v%% *}" # take the first function every time [ "$v" != "${v#* }" ] && \ export -n "$hook=${v#* }" || \ export -n "$hook=" export -n "$rvar=$first" # export the first function for other ones return 0 } return 1 }