2019年8月18日 星期日

Linux Kernel(17.2)- Device Tree and Platform Device


如同Linux Kernel(15.3)- The Linux usage model for device tree data所描述,init_machine()透過呼叫of_platform_populate()建構platform device。
kernel v3.5
|-->> msm8x60_dt_init(void) @arch/arm/mach-msm/board-msm8x60.c
  |--> of_platform_populate() @drivers/of/platform.c
    |--> of_platform_bus_create() @drivers/of/platform.c
      /* Make sure it has a compatible property */ 
          那些具有compatible的node都會被轉成platform device
      if (strict && (!of_get_property(bus, "compatible", NULL))) {
        pr_debug("%s() - skipping %s, no compatible prop\n",
                 __func__, bus->full_name);
        return 0;
      }

      ...
      for_each_child_of_node(bus, child) {
        pr_debug("create child: %s\n", child->full_name);
        rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
        if (rc) {
          of_node_put(child);
          break;
        }
      }


接著會在註冊driver時, 呼叫of_driver_match_device()進行match
kernel v3.5
|--> platform_driver_register() @drivers/base/platform.c 
  |--> driver_register() @drivers/base/driver.c
    |--> bus_add_driver() @drivers/base/bus.c /* 建立sysfs file node 與 attr */  
      |--> driver_attach() @drivers/base/dd.c 
        |--> bus_for_each_dev(drv->bus, NULL, drv, __driver_attach) @bus.c  
          |--> __driver_attach @drivers/base/dd.c
            |--> driver_match_device(drv, dev) @base.h
              |--> platform_match(); @drivers/base/platform.c
                if (of_driver_match_device(dev, drv))
                  return 1;

          |--> __driver_attach @drivers/base/dd.c
            if (!dev->driver)
              driver_probe_device(drv, dev);
            |--> driver_probe_device @drivers/base/dd.c
              ret = really_probe(dev, drv);
              |--> really_probe @drivers/base/dd.c
                if (dev->bus->probe) {
                  ret = dev->bus->probe(dev);

                if (ret)
                  goto probe_failed;
                } else if (drv->probe) {
                  ret = drv->probe(dev);
                  if (ret)
                    goto probe_failed;
                }

----- @include/linux/of_device.h -----
of_driver_match_device(struct device *dev, const struct device_driver *drv) 
{
    return of_match_device(drv->of_match_table, dev) != NULL;
}

----- @drivers/of/device.c -----
const struct of_device_id *of_match_device(const struct of_device_id *matches,
        const struct device *dev)
{
    if ((!matches) || (!dev->of_node))
        return NULL;
    return of_match_node(matches, dev->of_node);
}

----- @drivers/of/base.c -----
const struct of_device_id *of_match_node(const struct of_device_id *matches,
      const struct device_node *node)
{
    if (!matches)
        return NULL;

    while (matches->name[0] || matches->type[0] || matches->compatible[0]) {
        int match = 1;
        if (matches->name[0])
            match &= node->name && !strcmp(matches->name, node->name);
        if (matches->type[0])
            match &= node->type && !strcmp(matches->type, node->type);
        if (matches->compatible[0])
            match &= of_device_is_compatible(node, matches->compatible);
        if (match)
            return matches;
        matches++;
    }
    return NULL;
}


修改一下DTS驗證一下platform device
/ {
    node1 {
        compatible = "brook,dts1";
        a-string-property = "A string";
        a-string-list-property = "first string", "second string";
        // hex is implied in byte arrays. no '0x' prefix is required
        a-byte-data-property = [01 23 34 56];
        child-node1 {
            first-child-property;
            second-child-property = <1>;
            a-string-property = "Hello, world";
        };
        child-node2 {
        };
    };
};


/ # ls /sys/bus/platform/devices
10000000.sysreg
10002000.i2c
...
node1
...

/ # cd /sys/bus/platform/devices/node1/
/sys/devices/platform/node1 # ls
driver_override  of_node          subsystem
modalias         power            uevent
/sys/devices/platform/node1 # ls of_node/
a-byte-data-property    child-node1             name
a-string-list-property  child-node2
a-string-property       compatible
/sys/devices/platform/node1 # ftpget 192.168.1.1 /tmp/brook_modules.ko /home/brook/my_driver/brook_modules.ko
/sys/devices/platform/node1 # insmod /tmp/brook_modules.ko
brook_modules: loading out-of-tree module taints kernel.
brook_init(#55)
brook_probe(#21)


基本上與傳統的platform device driver的差異是:
  1. device由DTS的compatible產生,無須呼叫platform_device_register()註冊device
  2. 需在platform_driver.driver.of_match_table中掛上of_device_id[],裡面的compatible會與DTS的compatible進行比對
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>

#include <linux/of.h>
#include <linux/of_device.h>

MODULE_AUTHOR("Brook");
MODULE_DESCRIPTION("Kernel module for demo");
MODULE_LICENSE("GPL");

#define DEVNAME "brook"

static struct platform_device brook_device = {
    .name = DEVNAME,
};

static int brook_probe(struct platform_device *pdev)
{
    pr_info("%s(#%d)\n", __func__, __LINE__);
    return 0;
}

static int brook_remove(struct platform_device *pdev)
{
    pr_info("%s(#%d)\n", __func__, __LINE__);
    return 0;
}

static struct of_device_id brook_dt_ids[] = {
    {
        .compatible = "brook,dts1",
    }, {
        .compatible = "brook,dts2",
    }, {
    }
};

MODULE_DEVICE_TABLE(of, brook_dt_ids);

static struct platform_driver brook_driver = {
    .driver = {
        .name = DEVNAME,
        .owner = THIS_MODULE,
        .of_match_table = brook_dt_ids,
    },
    .probe = brook_probe,
    .remove = brook_remove,
};

static int __init brook_init(void)
{
    int ret;
    pr_info("%s(#%d)\n", __func__, __LINE__);

    ret = platform_driver_register(&brook_driver);
    if (ret) {
        dev_err(&(brook_device.dev),
                "%s(#%d): platform_driver_register fail(%d)\n", __func__,
                __LINE__, ret);
    }
    return ret;
}
module_init(brook_init);

static void __exit brook_exit(void)
{
    dev_info(&(brook_device.dev), "%s(#%d)\n", __func__, __LINE__);
    platform_driver_unregister(&brook_driver);
}
module_exit(brook_exit);


    參考資料:
  • http://wiki.dreamrunner.org/public_html/Embedded-System/Linux-Device-Tree.html, Linux Device tree
  • Linux Kernel(15.3)- The Linux usage model for device tree data
  • http://wiki.100ask.org/%E7%AC%AC%E4%B8%89%E8%AF%BE:%E5%86%85%E6%A0%B8%E5%AF%B9%E8%AE%BE%E5%A4%87%E6%A0%91%E7%9A%84%E5%A4%84%E7%90%86, 内核对设备树的处理




Linux Kernel(17.1)- Basic Device Tree syntax


這篇會介紹一下Device Tree的基本資料型態,並透過觀察/sys/firmware/devicetree/讓你更貼近一下DT的資料結構,下面範例是在vexpress-v2p-ca9.dts中include "brook.dtsi",然後在"brook.dtsi"撰寫DT的基本語法。DT的每個node,可包含零個以上的properties或child node。
brook@vista:~/qemu/linux-arm$ vim arch/arm/boot/dts/vexpress-v2p-ca9.dts
 #include "vexpress-v2m.dtsi"
 #include "brook.dtsi" /* add this line */

brook@vista:~/qemu/linux-arm$ vim arch/arm/boot/dts/brook.dtsi 

/ {
    node1 {
        a-string-property = "A string";
        a-string-list-property = "first string", "second string";
        // hex is implied in byte arrays. no '0x' prefix is required
        a-byte-data-property = [01 23 34 56];
        child-node1 {
            first-child-property;
            second-child-property = <1>;
            a-string-property = "Hello, world";
        };
        child-node2 {
        };
    };
    node2 {
        an-empty-property;
        a-cell-property = <1 2 3 4>; /* each number (cell) is a uint32 */
        // [@], 
        //  is a simple ascii string and can be up to 31 characters in length.
        child-node@1 {
        };
        child-node@2 {
        };
    };
};


brook@vista:~/qemu/linux-arm$ export ARCH=arm
brook@vista:~/qemu/linux-arm$ export CROSS_COMPILE=arm-linux-gnueabihf-
brook@vista:~/qemu/linux-arm$ export PATH=/opt/gcc-linaro-7.4.1-2019.02-x86_64_arm-linux-gnueabihf/bin:$PATH
brook@vista:~/qemu/linux-arm$ make dtbs


properties的value可以是empty或是以下資料型態:
  • Text strings (null terminated) are represented with double quotes: string-property = "a string";
  • 'Cells' are 32 bit unsigned integers delimited by angle brackets: cell-property = <0xbeef 123 0xabcd1234>;
  • Binary data is delimited with square brackets: binary-property = [0x01 0x23 0x45 0x67];
  • Data of differing representations can be concatenated together using a comma: mixed-property = "a string", [0x01 0x23 0x45 0x67], <0x12345678>;
  • Commas are also used to create lists of strings: string-list = "red fish", "blue fish";


brook@vista:~/qemu/linux-arm# cd ..
brook@vista:~/qemu$ qemu-system-arm -M vexpress-a9 -m 512M -kernel ./linux-arm/arch/arm/boot/zImage -dtb ./linux-arm/arch/arm/boot/dts/vexpress-v2p-ca9.dtb -initrd ./initrd-arm.img -nographic -append "console=ttyAMA0"

----- boot to VM -----

Please press Enter to activate this console.
/ # ls /sys/firmware/
devicetree  fdt
/ # ls /sys/firmware/devicetree/
base
/ # ls /sys/firmware/devicetree/base/ #根目錄(/sys/firmware/devicetree/base/) 多了node1與node2
/ # ls /sys/firmware/devicetree/base/
#address-cells                 model
#size-cells                    name
aliases                        node1
arm,hbi                        node2
arm,vexpress,site              pmu
cache-controller@1e00a000      reserved-memory
chosen                         scu@1e000000
clcd@10020000                  smb@4000000
compatible                     timer@100e4000
cpus                           timer@1e000600
dcc                            virtio_mmio@10013000
hsb@e0000000                   virtio_mmio@10013200
interrupt-controller@1e001000  virtio_mmio@10013400
interrupt-parent               virtio_mmio@10013600
memory-controller@100e0000     watchdog@100e5000
memory-controller@100e1000     watchdog@1e000620
memory@60000000

/ # find /sys/firmware/devicetree/base/node1
/sys/firmware/devicetree/base/node1
/sys/firmware/devicetree/base/node1/child-node2
/sys/firmware/devicetree/base/node1/child-node2/name
/sys/firmware/devicetree/base/node1/a-string-property
/sys/firmware/devicetree/base/node1/a-string-list-property
/sys/firmware/devicetree/base/node1/a-byte-data-property
/sys/firmware/devicetree/base/node1/name
/sys/firmware/devicetree/base/node1/child-node1
/sys/firmware/devicetree/base/node1/child-node1/first-child-property
/sys/firmware/devicetree/base/node1/child-node1/second-child-property
/sys/firmware/devicetree/base/node1/child-node1/a-string-property
/sys/firmware/devicetree/base/node1/child-node1/name


/ # find /sys/firmware/devicetree/base/node2
/sys/firmware/devicetree/base/node2
/sys/firmware/devicetree/base/node2/child-node@1
/sys/firmware/devicetree/base/node2/child-node@1/name
/sys/firmware/devicetree/base/node2/child-node@2
/sys/firmware/devicetree/base/node2/child-node@2/name
/sys/firmware/devicetree/base/node2/a-cell-property
/sys/firmware/devicetree/base/node2/name
/sys/firmware/devicetree/base/node2/an-empty-property


/ # hexdump -e '8/1 "%02X ""\t"" "' -e '8/1 "%c""\n"' /sys/firmware/devicetree/base/node1/a-string-property
41 20 73 74 72 69 6E 67  A string
00

/ # hexdump -e '8/1 "%02X ""\t"" "' -e '8/1 "%c""\n"' /sys/firmware/devicetree/base/node1/a-string-list-property
66 69 72 73 74 20 73 74  first st
72 69 6E 67 00 73 65 63  ringsec
6F 6E 64 20 73 74 72 69  ond stri
6E 67 00                 ng
strings lists中的element是"0"分隔

/ # hexdump -e '8/1 "%02X ""\t"" "' -e '8/1 "%c""\n"' /sys/firmware/devicetree/base/node1/a-byte-data-property
01 23 34 56              #4V
byte-data如其名,每個值大小就是一個byte

/ # hexdump -e '8/1 "%02X ""\t"" "' -e '8/1 "%c""\n"' /sys/firmware/devicetree/base/node1/name
6E 6F 64 65 31 00        node1

/ # hexdump -e '8/1 "%02X ""\t"" "' -e '8/1 "%c""\n"' /sys/firmware/devicetree/base/node1/child-node1/first-child-property
empty

/ # hexdump -e '8/1 "%02X ""\t"" "' -e '8/1 "%c""\n"' /sys/firmware/devicetree/base/node1/child-node1/second-child-property
00 00 00 01

/ # hexdump -e '8/1 "%02X ""\t"" "' -e '8/1 "%c""\n"' /sys/firmware/devicetree/base/node1/child-node1/a-string-property
48 65 6C 6C 6F 2C 20 77  Hello, w
6F 72 6C 64 00           orld

/ # hexdump -e '8/1 "%02X ""\t"" "' -e '8/1 "%c""\n"' /sys/firmware/devicetree/base/node2/a-cell-property
00 00 00 01 00 00 00 02
00 00 00 03 00 00 00 04
cell每個value大小為32byte

/ # hexdump -e '8/1 "%02X ""\t"" "' -e '8/1 "%c""\n"' /sys/firmware/devicetree/base/node2/child-node@1/name
63 68 69 6C 64 2D 6E 6F  child-no
64 65 00                 de

/ # hexdump -e '8/1 "%02X ""\t"" "' -e '8/1 "%c""\n"' /sys/firmware/devicetree/base/node2/child-node@2/name
63 68 69 6C 64 2D 6E 6F  child-no
64 65 00                 de


    參考資料:
  • https://elinux.org/Device_Tree_Usage, Device Tree Usage
  • https://elinux.org/images/f/f9/Petazzoni-device-tree-dummies_0.pdf, device tree dumies
  • https://blog.csdn.net/RadianceBlau/article/details/70800076, Linux DTS(Device Tree Source)设备树详解之一(背景基础知识篇)




Linux Kernel(17)- Device Tree


Device tree是一個用來描述硬體的資料結構,包含了CPU、Memory、bus與周邊,DT改變了原本kernel的hard-code(table),改由bootloader傳入DTB(Device Tree Blob)給kernel。這個由SPARC-based開始的Open Firmware project於是慢慢地推廣到Arm, x86, MicroBlaze, PowerPC等平台。
這個有趣的故事可以讀一下Linux DTS(Device Tree Source)设备树详解之一(背景基础知识篇)

摘錄故事部分如下:
在Linux 2.6中,ARM架构的板极硬件细节过多地被硬编码在arch/arm/plat-xxx和arch/arm/mach-xxx,
比如板上的platform设备、resource、i2c_board_info、spi_board_info以及各种硬件的
platform_data,这些板级细节代码对内核来讲只不过是垃圾代码。而采用Device Tree后,许多硬件的细节
可以直接透过它传递给Linux,而不再需要在kernel中进行大量的冗余编码。
每次正式的linux kernel release之后都会有两周的merge window,在这个窗口期间,kernel各个部分的
维护者都会提交各自的patch,将自己测试稳定的代码请求并入kernel main line。每到这个时候,Linus
就会比较繁忙,他需要从各个内核维护者的分支上取得最新代码并merge到自己的kernel source tree中。
Tony Lindgren,内核OMAP development tree的维护者,发送了一个邮件给Linus,请求提交OMAP平台
代码修改,并给出了一些细节描述:
       1)简单介绍本次改动
       2)关于如何解决merge conficts。有些git mergetool就可以处理,不能处理的,
         给出了详细介绍和解决方案。
一切都很平常,也给出了足够的信息,然而,正是这个pull request引发了一场针对ARM linux的内核代码
的争论。我相信Linus一定是对ARM相关的代码早就不爽了,ARM的merge工作量较大倒在其次,主要是他认为
ARM很多的代码都是垃圾,代码里面有若干愚蠢的table,而多个人在维护这个table,从而导致了冲突。
因此,在处理完OMAP的pull request之后(Linus并非针对OMAP平台,只是Tony Lindgren撞在枪口上了)
,他发出了怒吼:
     Gaah.Guys, this whole ARM thing is a f*cking pain in the ass.
 
之后经过一些讨论,对ARM平台的相关code做出如下相关规范调整,这个也正是引入DTS的原因。
1、ARM的核心代码仍然保存在arch/arm目录下
2、ARM SoC core architecture code保存在arch/arm目录下
3、ARM SOC的周边外设模块的驱动保存在drivers目录下
4、ARM SOC的特定代码在arch/arm/mach-xxx目录下
5、ARM SOC board specific的代码被移除,由DeviceTree机制来负责传递硬件拓扑和硬件资源信息。
本质上,Device Tree改变了原来用hardcode方式将HW 配置信息嵌入到内核代码的方法,改用bootloader
传递一个DB的形式。
 ———————————————— 
版权声明:本文为CSDN博主「RadianceBlau」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/RadianceBlau/article/details/70800076

bootloader傳入DTB(Device Tree Blob),而DTB是由Device Tree Source透過DTC編成的binary data,關係概略如下:

DTC相關用法可以參考dtc - Device Tree Compiler
DTS語法會在後面章節介紹,基本上,DTS只描述那些無法動態偵測的設備

    參考資料
  • https://blog.csdn.net/RadianceBlau/article/details/70800076, Linux DTS(Device Tree Source)设备树详解之一(背景基础知识篇)
  • https://en.wikipedia.org/wiki/Device_tree, Device tree
  • dtc - Device Tree Compiler




2019年7月13日 星期六

BuildID[sha1] of ELF


對於BuildID的解釋, 可以在ELF, Build-ID, is there a utility to recompute it?找到一段說明, 說明如下
I think things weren't very precisely formulated. If a tool changes the build that 
creates the ELF file so that it isn't a "semantically identical" binary anymore 
then it should get a new (recalculated) build-id. But if a tool changes something 
about the file that still results in a "semantically identical" binary then the 
build-id stays the same.

What isn't precisely defined is what "semantically identical binary" means. The 
intention is that it captures everything that a build was made from. So if the 
source files used to generate a binary are different then you expect different 
build-ids, even if the binary code produced might happen to be the same.

This is why when calculating the build-id of a file through a hash algorithm you
 use not just the (allocated) code sections, but also the debuginfo sections 
(which will contain references to the source file names).

But if you then for example strip the debuginfo out (and put it into a separate 
file) then that doesn't change the build-id (the file was still created from the 
same build).

This is also why, even if you knew the precise hashing algorithm used to calculate 
the build-id, you might not be able to recalculate the build-id. Because you might 
be missing some of the original data used in the hashing algorithm to calculate 
the build-id.

Feel free to share this answer with others.


內容簡略的說就是, BuildID是"semantically identical binary", 相同的semantic所build的program才會有相同BuildID.

brook@vista:~/01$ file src/hello
src/hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=91439ef711a19bf3be7774d2c4af35746e098cc8, not stripped
brook@vista:~/01$ readelf -n src/hello

Displaying notes found at file offset 0x00000254 with length 0x00000020:
  Owner                 Data size       Description
  GNU                  0x00000010       NT_GNU_ABI_TAG (ABI version tag)
    OS: Linux, ABI: 2.6.32

Displaying notes found at file offset 0x00000274 with length 0x00000024:
  Owner                 Data size       Description
  GNU                  0x00000014       NT_GNU_BUILD_ID (unique build ID bitstring)
    Build ID: 91439ef711a19bf3be7774d2c4af35746e098cc8
brook@vista:~/01$ strip src/hello
brook@vista:~/01$ file src/hello
src/hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=91439ef711a19bf3be7774d2c4af35746e098cc8, stripped
brook@vista:~/01$ readelf -n src/hello

Displaying notes found at file offset 0x00000254 with length 0x00000020:
  Owner                 Data size       Description
  GNU                  0x00000010       NT_GNU_ABI_TAG (ABI version tag)
    OS: Linux, ABI: 2.6.32

Displaying notes found at file offset 0x00000274 with length 0x00000024:
  Owner                 Data size       Description
  GNU                  0x00000014       NT_GNU_BUILD_ID (unique build ID bitstring)
    Build ID: 91439ef711a19bf3be7774d2c4af35746e098cc8

brook@vista:~/01$ make clean && make 重build也是會得到相同的BuildID
Making clean in src
...
brook@vista:~/01$ file src/hello
src/hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=91439ef711a19bf3be7774d2c4af35746e098cc8, not stripped

brook@vista:~/01$ echo -e '\n\n\n' >> src/hello.c 即使多了幾行換行, 重build也是會得到相同的BuildID
brook@vista:~/01$ make
make  all-recursive
...
brook@vista:~/01$ file src/hello
src/hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=91439ef711a19bf3be7774d2c4af35746e098cc8, not stripped


    參考資料:
  • https://stackoverflow.com/questions/41743295/elf-build-id-is-there-a-utility-to-recompute-it, ELF, Build-ID, is there a utility to recompute it?
  • https://fedoraproject.org/wiki/Releases/FeatureBuildId, Releases/FeatureBuildId





2019年7月7日 星期日

Using openembedded SDK to build protobuf-c


brook@vista:~/protobuf$ git clone git://github.com/google/protobuf.git
Cloning into 'protobuf'...
...

brook@vista:~/protobuf/protobuf$ git checkout 3.6.x
Branch 3.6.x set up to track remote branch 3.6.x from origin.
...

brook@vista:~/protobuf/protobuf$ . /opt/oecore-x86_64/environment-setup-cortexa7-neon-vfpv4-oe-linux-gnueabi


brook@vista:~/protobuf/protobuf$ vim configure.ac 
 export CFLAGS
 export CXXFLAGS
### remove below line ###
AC_CONFIG_SUBDIRS([third_party/googletest])

brook@vista:~/home6t/protobuf/protobuf$ ./autogen.sh
+ mkdir -p third_party/googletest/m4
+ autoreconf -f -i -Wall,no-obsolete
...

brook@vista:~/protobuf/protobuf$ ./configure ${CONFIGURE_FLAGS} --prefix=/home/brook/protobuf/
configure: loading site script /opt/oecore-x86_64/site-config-cortexa7-neon-vfpv4-oe-linux-gnueabi
checking whether to enable maintainer-specific portions of Makefiles... yes
checking build system type... x86_64-pc-linux-gnu
...

brook@vista:~/protobuf/protobuf$ make all -j 8
make  all-recursive
Making install in .
make[1]: Entering directory '/home/brook/protobuf/protobuf'
...

brook@vista:~/protobuf/protobuf$ make install
Making install in .
make[1]: Entering directory '/home/brook/protobuf/protobuf'
...


change "/home/brook/protobuf/lib/libprotoc.la"
from
# Libraries that this one depends upon.
dependency_libs=' =/home6t/brook/protobuf/lib/libprotobuf.la =/usr/lib/libstdc++.la'

to
# Libraries that this one depends upon.
dependency_libs=''


brook@vista:~$ mkdir protobuf-c
brook@vista:~$ cd protobuf-c
brook@vista:~/protobuf-c$ git clone git://github.com/protobuf-c/protobuf-c.git
Cloning into 'protobuf-c'...
remote: Enumerating objects: 59, done.
remote: Counting objects: 100% (59/59), done.
...

brook@vista:~/protobuf-c/protobuf-c$ ./autogen.sh
autoreconf: Entering directory `.'
autoreconf: configure.ac: not using Gettext
...

brook@vista:~/protobuf-c/protobuf-c$ PROTOC=/usr/bin/protoc-c protobuf_CFLAGS="-I/home/brook/protobuf/include" protobuf_LIBS="-L/home/brook/protobuf/lib" ./configure ${CONFIGURE_FLAGS} --prefix=/home/brook/protobuf-c
configure: loading site script /opt/oecore-x86_64/site-config-cortexa7-neon-vfpv4-oe-linux-gnueabi
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
...

brook@vista:~/protobuf-c/protobuf-c$ make -j 8
make  all-am
make[1]: Entering directory '/home/brook/protobuf-c/protobuf-c'
  CC       protobuf-c/protobuf-c.lo
...

2019年6月23日 星期日

Build the Linux Kernel and Busybox for ARM and run them on QEMU



安裝corss-compiler
brook@vista:~/qemu$ wget http://releases.linaro.org/components/toolchain/binaries/7.4-2019.02/arm-linux-gnueabihf/gcc-linaro-7.4.1-2019.02-x86_64_arm-linux-gnueabihf.tar.xz 
brook@vista:~/qemu$ tar xvf gcc-linaro-7.4.1-2019.02-x86_64_arm-linux-gnueabihf.tar.xz
brook@vista:~/qemu$ sudo mv gcc-linaro-7.4.1-2019.02-x86_64_arm-linux-gnueabihf /opt/

編譯kernel
brook@vista:~/qemu$ cd linux-arm
brook@vista:~/qemu/linux-arm$ export ARCH=arm
brook@vista:~/qemu/linux-arm$ export CROSS_COMPILE=arm-linux-gnueabihf-
brook@vista:~/qemu/linux-arm$ export PATH=/opt/gcc-linaro-7.4.1-2019.02-x86_64_arm-linux-gnueabihf/bin:$PATH
brook@vista:~/qemu/linux-arm$ make vexpress_defconfig
brook@vista:~/qemu/linux-arm$ make -j8

編譯busybox
brook@vista:~/qemu/busybox$ export ARCH=arm
brook@vista:~/qemu/busybox$ export CROSS_COMPILE=arm-linux-gnueabihf-
brook@vista:~/qemu/busybox$ make O=/home/brook/qemu/busybox-build menuconfig
--> change to static busybox

brook@vista:~/qemu/busybox$ cd ../busybox-build
brook@vista:~/qemu/busybox-build$ make -j8
brook@vista:~/qemu/busybox-build$ cp busybox ../initrd-arm/bin/busybox

generate initrd-image
brook@vista:~/qemu$ ./linux-arm/usr/gen_initramfs_list.sh -d initrd-arm > /tmp/brook_initramfs_list
brook@vista:~/qemu$ ./linux-arm/usr/gen_init_cpio /tmp/brook_initramfs_list > initrd-arm.img


執行QEMU
brook@vista:~/qemu$ /usr/local/bin/qemu-system-arm -M vexpress-a9 -m 512M -kernel ./linux-arm/arch/arm/boot/zImage -dtb ./linux-arm/arch/arm/boot/dts/vexpress-v2p-ca9.dtb -initrd ./initrd-arm.img -nographic -append "console=ttyAMA0"



    參考資料:
  1. http://albert-oma.blogspot.com/2017/12/qemu-arm-linux.html, [QEMU] 模擬一個實際的 ARM Linux 嵌入式系統
  2. https://www.centennialsoftwaresolutions.com/blog/build-the-linux-kernel-and-busybox-for-arm-and-run-them-on-qemu, Build the Linux Kernel and Busybox for ARM and run them on QEMU





2019年6月15日 星期六

Build QEMU 4 on ubuntu 16.04


brook@vista$ ./configure --target-list="arm-softmmu,i386-softmmu,x86_64-softmmu,arm-linux-user,i386-linux-user,x86_64-linux-user" --enable-debug --enable-sdl --enable-kvm --enable-curl --enable-snappy --enable-tools

ERROR: User requested feature sdl
       configure was not able to find it.
       Install SDL2-devel

brook@vista$ sudo apt-get install libsdl2-2.0-0

brook@vista$ ./configure --target-list="arm-softmmu,i386-softmmu,x86_64-softmmu,arm-linux-user,i386-linux-user,x86_64-linux-user" --enable-debug --enable-sdl --enable-kvm --enable-curl --enable-snappy --enable-tools
Install prefix    /usr/local
BIOS directory    /usr/local/share/qemu
firmware path     /usr/local/share/qemu-firmware
binary directory  /usr/local/bin
library directory /usr/local/lib
module directory  /usr/local/lib/qemu
libexec directory /usr/local/libexec
include directory /usr/local/include
config directory  /usr/local/etc
local state directory   /usr/local/var
Manual directory  /usr/local/share/man
ELF interp prefix /usr/gnemul/qemu-%M
Source path       /home/brook/qemu4/qemu-4.0.0-rc4
GIT binary        git
GIT submodules
C compiler        cc
Host C compiler   cc
C++ compiler      c++
Objective-C compiler cc
ARFLAGS           rv
CFLAGS            -g

...
module support    no
host CPU          x86_64
host big endian   no
target list       arm-softmmu i386-softmmu x86_64-softmmu arm-linux-user i386-linux-user x86_64-linux-user
gprof enabled     no
sparse enabled    no
strip binaries    no
profiler          no
static build      no
SDL support       yes (2.0.4)
SDL image support no
GTK support       no
GTK GL support    no
VTE support       no
TLS priority      NORMAL
GNUTLS support    no
libgcrypt         no
nettle            no
libtasn1          no
PAM               no
iconv support     yes
curses support    yes
virgl support     no
curl support      yes
mingw32 support   no
Audio drivers     pa oss
Block whitelist (rw)
Block whitelist (ro)
VirtFS support    no
Multipath support no
VNC support       yes
VNC SASL support  no
VNC JPEG support  yes
VNC PNG support   yes
xen support       no
brlapi support    no
bluez  support    no
Documentation     no
PIE               yes
vde support       no
netmap support    no
Linux AIO support no
ATTR/XATTR support yes
Install blobs     yes
KVM support       yes
HAX support       no
HVF support       no
WHPX support      no
TCG support       yes
TCG debug enabled yes
TCG interpreter   no
malloc trim support yes
RDMA support      no
PVRDMA support    no
fdt support       git
membarrier        no
preadv support    yes
fdatasync         yes
madvise           yes
posix_madvise     yes
posix_memalign    yes
libcap-ng support no
vhost-net support yes
vhost-crypto support yes
vhost-scsi support yes
vhost-vsock support yes
vhost-user support yes
Trace backends    log
spice support     no
rbd support       no
xfsctl support    no
smartcard support no
libusb            no
usb net redir     no
OpenGL support    no
OpenGL dmabufs    no
libiscsi support  no
libnfs support    no
build guest agent yes
QGA VSS support   no
QGA w32 disk info no
QGA MSI support   no
seccomp support   no
coroutine backend ucontext
coroutine pool    yes
debug stack usage no
mutex debugging   yes
crypto afalg      no
GlusterFS support no
gcov              gcov
gcov enabled      no
TPM support       yes
libssh2 support   no
TPM passthrough
TPM emulator
QOM debugging     yes
Live block migration yes
lzo support       no
snappy support    yes
bzip2 support     no
lzfse support     no
NUMA host support no
libxml2           no
tcmalloc support  no
jemalloc support  no
avx2 optimization yes
replication support yes
VxHS block device no
bochs support     yes
cloop support     yes
dmg support       yes
qcow v1 support   yes
vdi support       yes
vvfat support     yes
qed support       yes
parallels support yes
sheepdog support  yes
capstone          internal
docker            yes
libpmem support   no
libudev           yes
default devices   yes

brook@vista$  make all 
  GEN     arm-softmmu/config-devices.mak.tmp
  GEN     arm-softmmu/config-devices.mak
  GEN     i386-softmmu/config-devices.mak.tmp
...

brook@vista$  sudo make all install
[sudo] password for brook:
make[1]: Entering directory '/home/brook/qemu4/qemu-4.0.0-rc4/slirp'

brook@vista$  /usr/local/bin/qemu-system-x86_64 -kernel linux/arch/x86/boot/bzImage -initrd initrd.img -curses 


    參考資料:
  • https://blog.csdn.net/candcplusplus/article/details/78320602, QEMU 2.10.1 编译安装



2019年6月1日 星期六

Protocol Buffers - for C++


Protocol buffers基本上我把他想成是一個資料庫應用的延伸, 這話該如何說呢? 基本上他就是透過名為proto的meta file, 用以描述資料形態與內容(做encode/decode).

How does the Protocol Buffers work?

先定義你的message結構, 如
message Person {
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }

  repeated PhoneNumber phone = 4;
}

再用protoc將該檔案轉成code, 目前支援(C++, Java and Python), 如下例子, 將brook.proto轉成C++(brook.pb.cc與brook.pb.h).
brook@vista:~/protobuf/CPP$ protoc --cpp_out=. brook.proto
brook@vista:~/protobuf/CPP$ ls
brook.pb.cc  brook.pb.h  brook.proto

這些code會包含存取每一個欄位的API(如下由doxygen產生的圖), 如email()與set_email(), 用以serialize/parse資料,

接著我們就可以用這些API寫一段code, 做serialize/parse資料
#include <iostream>
#include <fstream>
#include "brook.pb.h"
using namespace std;

static void _Serialize(void)
{
        Person person;
        person.set_name("John Doe");
        person.set_id(1234);
        person.set_email("jdoe@example.com");
        fstream output("myfile", ios::out | ios::binary);
        person.SerializeToOstream(&output);
}

static void _Parse(void)
{
        Person person;
        fstream input("myfile", ios::in | ios::binary);
        person.ParseFromIstream(&input);
        cout << "Name: " << person.name() << endl;
        cout << "E-mail: " << person.email() << endl;
}

int main(int argc, char *argv[])
{
        _Serialize();
        _Parse();
        return 0;
}

brook@vista:~/protobuf/CPP$ g++ main.c brook.pb.cc -lprotobuf
brook@vista:~/protobuf/CPP$ ./a.out
Name: John Doe
E-mail: jdoe@example.com


    參考資料:
  • protocol buffer簡介, https://developers.google.com/protocol-buffers/docs/overview





2019年1月13日 星期日

Note for SCons 3.0.3 User Guide, CH5 Node Objects


所有的檔案與目錄在SCons中都被視為"Nodes",善用"Node"可以使您的SConscript文件可移植且易於閱讀。

5.1. Builder Methods Return Lists of Target Nodes

所有builder methods都回傳一個Node list,用於辨識要構建的目標文件。這Node list可作為參數傳遞給其他builder methods。
brook@vista:~/scons/04.7$ cat SConstruct
a_list = Object('a.c', CCFLAGS='-DHELLO')
b_list = Object('b.c', CCFLAGS='-DWORLD')
Program(a_list + b_list)
 
brook@vista:~/scons/04.7$ scons -Q
gcc -o a.o -c -DHELLO a.c
gcc -o b.o -c -DWORLD b.c
gcc -o a a.o b.o


5.2. Explicitly Creating File and Directory Nodes

SCons的File()與Dir()可回傳file或directory Node,而Entry()可回傳file或directory Node。
hello_c = File('hello.c')
Program(hello_c)
 
classes = Dir('classes')
Java(classes, 'src')
 
xyzzy = Entry('xyzzy')


5.3. Printing Node File Names

您可以對Node執行print,打Node所代表的檔名。
brook@vista:~/scons/05.3$ cat SConstruct
object_list = Object(['a.c', 'b.c'])
program_list = Program(object_list)
print("The object file is: %s"%object_list)
print("The program file is: %s"%program_list[0])

brook@vista:~/scons/05.3$ scons -Q
The object file is: ['a.o', 'b.o']
The program file is: a
scons: `.' is up to date.


5.4. Using a Node's File Name as a String

您可以使用Python的str函數將Node轉成字串加以處理。
brook@vista:~/scons/05.5$ cat SConstruct
import os
object_list = Object(['a.c', 'b.c'])
obj_str = str(object_list[0])
print("The object file is: %s"% obj_str)
if not os.path.exists(obj_str):
    print("%s does not exist!"% obj_str)
else:
    print("%s exist!"% obj_str)
brook@vista:~/scons/05.5$ scons -Q
The object file is: a.o
a.o exist!
scons: `.' is up to date.


5.5. GetBuildPath: Getting the Path From a Node or String

env.GetBuildPath(file_or_list)可以用來取得file/Node的路徑。
brook@vista:~/scons/05.6$ cat SConstruct
object_list = Object(['a.c', 'b.c'])
env=Environment(VAR="value")
n=File("foo.c")
print(env.GetBuildPath([n, "sub/dir/$VAR", '/tmp'] + object_list))
brook@vista:~/scons/05.6$ scons -Q
['foo.c', 'sub/dir/value', '/tmp', 'a.o', 'b.o']
scons: `.' is up to date.





Note for SCons 3.0.3 User Guide, CH4 Building and Linking with Libraries


4.1. Building Libraries

在軟體開發過程中,將軟體切成數個library是很常見的,build成library在SCons中也是很容易完成的。
如果是要build成library,只需將builder method從Program換成Library即可。
Library('foo', ['f1.c', 'f2.c', 'f3.c'])

SCons會根據當前的系統,build出該Library,如Linux
% scons -Q
cc -o f1.o -c f1.c
cc -o f2.o -c f2.c
cc -o f3.o -c f3.c
ar rc libfoo.a f1.o f2.o f3.o
ranlib libfoo.a

Windows
C:\>scons -Q
cl /Fof1.obj /c f1.c /nologo
cl /Fof2.obj /c f2.c /nologo
cl /Fof3.obj /c f3.c /nologo
lib /nologo /OUT:foo.lib f1.obj f2.obj f3.obj

Library會build成static library,你也可以使用StaticLibrary來build static library
brook@vista:~/scons/04.2$ cat SConstruct
StaticLibrary('foo',['a.c', 'b.c'])

brook@vista:~/scons/04.2$ scons -Q
gcc -o a.o -c a.c
gcc -o b.o -c b.c
ar rc libfoo.a a.o b.o
ranlib libfoo.a

你也可以使用SharedLibrary 來build shared library
brook@vista:~/scons/04.3$ cat SConstruct
SharedLibrary('foo',['a.c', 'b.c'])

brook@vista:~/scons/04.3$ scons -Q
gcc -o a.os -c -fPIC a.c
gcc -o b.os -c -fPIC b.c
gcc -o libfoo.so -shared a.os b.os


4.2. Linking with Libraries

如果你的Program需要link某個Library,你只需在Program中指定$LIBS與$LIBPATH
brook@vista:~/scons/04.5$ cat SConstruct
SharedLibrary('foo',['a.c'])
StaticLibrary('bar',['b.c'])
Program('prog', 'prog.c', LIBS=['foo', 'bar'], LIBPATH='.')

brook@vista:~/scons/04.5$ scons -Q
gcc -o a.os -c -fPIC a.c
gcc -o b.o -c b.c
ar rc libbar.a b.o
ranlib libbar.a
gcc -o libfoo.so -shared a.os
gcc -o prog.o -c prog.c
gcc -o prog prog.o -L. -lfoo -lbar


4.3. Finding Libraries: the $LIBPATH Construction Variable

如果你要SCons尋找系統以外的library目錄,你指定$LIBPATH
brook@vista:~/scons/04.5$ cat SConstruct
SharedLibrary('foo',['a.c'])
StaticLibrary('bar',['b.c'])
Program('prog', 'prog.c', LIBS=['foo', 'bar'], LIBPATH=['/brook/lib', '.'])

brook@vista:~/scons/04.5$ scons -Q
gcc -o a.os -c -fPIC a.c
gcc -o b.o -c b.c
ar rc libbar.a b.o
ranlib libbar.a
gcc -o libfoo.so -shared a.os
gcc -o prog.o -c prog.c
gcc -o prog prog.o -L/brook/lib -L. -lfoo -lbar





2019年1月5日 星期六

Note for SCons 3.0.1 User Guide, CH3 Less Simple Things to Do With Builds


3.1. Specifying the Name of the Target (Output) File

當你呼叫Program()時,Scons會build出跟source file一樣檔名的程式,如果要build不同檔名的程式,可以Program()左邊參數放檔名,右邊放source file
brook@vista:~/scons/03.1$ cat SConstruct
Program('new_hello', 'hello.c')

brook@vista:~/scons/03.1$ scons -Q
gcc -o hello.o -c hello.c
gcc -o new_hello hello.o
brook@vista:~/scons/03.1$ scons -c -Q
Removed hello.o
Removed new_hello


3.2. Compiling Multiple Source Files

如果要從多個source file建立程式,只需在Program()放置python list,則會建立以第一個source file為檔名的程式檔
brook@vista:~/scons/03.1$ cat SConstruct
Program(['hello.c', 'a.c'])

brook@vista:~/scons/03.1$ scons -Q
gcc -o a.o -c a.c
gcc -o hello.o -c hello.c
gcc -o hello hello.o a.o


如果要建立不同程式名稱,只需在Program()左邊參數放檔名,右邊放source file list即可
brook@vista:~/scons/03.1$ cat SConstruct
Program('new_hello', ['hello.c', 'a.c'])

brook@vista:~/scons/03.1$ scons -Q
gcc -o a.o -c a.c
gcc -o hello.o -c hello.c
gcc -o new_hello hello.o a.o


3.3. Making a list of files with Glob

你可以適用Glob()來找尋matching的檔案,其語法可以使用*, ? and [abc]等shell的regular expression
brook@vista:~/scons/03.02$ cat SConstruct
Program('new_hello', Glob("*.c"))

brook@vista:~/scons/03.02$ scons -Q
gcc -o a.o -c a.c
gcc -o hello.o -c hello.c
gcc -o new_hello a.o hello.o


3.4. Specifying Single Files Vs. Lists of Files

SCons將所有source file視為list,只要符合list即可
# The following two calls both work correctly: 
Program('program1', 'program1.c') 
Program('program2', ['program2.c'])


common_sources = ['file1.c', 'file2.c'] 

# THE FOLLOWING IS INCORRECT AND GENERATES A PYTHON ERROR 
# BECAUSE IT TRIES TO ADD A STRING TO A LIST: 
Program('program1', common_sources + 'program1.c') 

# The following works correctly, because it's adding two 
# lists together to make another list. 
Program('program2', common_sources + ['program2.c'])


3.5. Making Lists of Files Easier to Read

SCons中的Split()可以將字串內的檔案換成list,讓developer寫一個容易閱讀的list
brook@vista:~/scons/03.4$ cat SConstruct
print('a.c b.c c.c')
print(Split('a.c b.c c.c'))
src_files = Split('a.c b.c c.c')
print(src_files)

brook@vista:~/scons/03.4$ scons -Q
a.c b.c c.c
['a.c', 'b.c', 'c.c']
['a.c', 'b.c', 'c.c']
scons: `.' is up to date.


3.6. Keyword Arguments

Python預設是positional argument(位置參數),是按順序傳入function。關鍵字參數(keyword argument),顧名思義是以關鍵字方式傳入,使用keyword argument時,對順序沒有要求。Program(target, source)
src_files = Split('main.c file1.c file2.c') 
Program(target = 'program', source = src_files)

src_files = Split('main.c file1.c file2.c') 
Program(source = src_files, target = 'program')


3.7. Compiling Multiple Programs

如果要編譯多個程式,只需多描述幾行Program()即可
brook@vista:~/scons/03.7$ cat SConstruct
Program('a.c')
Program('b.c')

brook@vista:~/scons/03.7$ scons -Q
gcc -o a.o -c a.c
gcc -o a a.o
gcc -o b.o -c b.c
gcc -o b b.o


3.8. Sharing Source Files Between Multiple Programs

SCons會自行判斷build的dependence,所以你只需照實描述各個program所需的source file即可
brook@vista:~/scons/03.7$ cat SConstruct
Program('a', ['a.c', 'comm.c'])
Program('b', ['b.c', 'comm.c'])

brook@vista:~/scons/03.7$ scons -Q
gcc -o a.o -c a.c
gcc -o comm.o -c comm.c
gcc -o a a.o comm.o
gcc -o b.o -c b.c
gcc -o b b.o comm.o


3.9. Overriding construction variables when calling a Builder

當你在呼叫這些builder時,Scons允許你帶入一些參數或覆蓋原本的參數,如 adds 'include' to $CPPPATH, 'EBUG' to $CPPDEFINES, and 'm' to $LIBS.
Program('hello', 'hello.c', parse_flags = '-Iinclude -DEBUG -lm')





2019年1月1日 星期二

Note for SCons 3.0.1 User Guide, CH2 Simple Builds


SCons是一個用Python的新一代軟體建構工具(SCons is a next-generation software construction tool),就像Make一樣可以建構軟體的工具。

CH 2.1. Build Simple C/C++ Programs

主要依具名為SConstruct的檔案進行建構,最小的SConstruct內容如下
Program('hello.c') 
這裡面包含兩個資訊,你要建構的程式名稱(hello),以及從哪個檔案建構(hello.c),"Program"在文中被稱為builder_method,主要用於告訴SCons要建構執行檔。
% scons scons: Reading SConscript files ... 
scons: done reading SConscript files. 
scons: Building targets ... 
cc -o hello.o -c hello.c 
cc -o hello hello.o 
scons: done building targets.


CH 2.2. Building Object Files

Object('hello.c') 
Objcet這個builder method用於告訴SCons如何從指定的source files中建立一個Object file
% scons scons: Reading SConscript files ... 
scons: done reading SConscript files. 
scons: Building targets ... 
cc -o hello.o -c hello.c 
scons: done building targets.


2.4. Cleaning Up After a Build

在SCons清除(Cleaning Up)一個build只需要在scons後面新增"-c"/"--clean"即可

2.5. The SConstruct File

SConstruct之於SCons就等同於Makefile之於Make,而SConstruct是個Python Script,注意,SConstruct的順序並非SCons實際執行的順序 比如該SConstruct的內容與其執行結果分別為
print("Calling Program('hello.c')") 
Program('hello.c') 
print("Calling Program('goodbye.c')") 
Program('goodbye.c') 
print("Finished calling Program()") 
hello.c先被呼叫,而後才是goodbye.c

% scons 
scons: Reading SConscript files ... 
Calling Program('hello.c') 
Calling Program('goodbye.c') 
Finished calling Program() 
scons: done reading SConscript files. 
scons: Building targets ... 
cc -o goodbye.o -c goodbye.c 
cc -o goodbye goodbye.o 
cc -o hello.o -c hello.c 
cc -o hello hello.o 
scons: done building targets. 
goodbye先被建置,而後才是hello

2.6. Making the SCons Output Less Verbose

參數"-Q"可以讓SCons輸出較少的建置訊息
brook@vista:~$ scons -Q
gcc -o hello.o -c hello.c

brook@vista:~$ scons -c
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Cleaning targets ...
Removed hello.o
scons: done cleaning targets.

brook@vista:~$ scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
gcc -o hello.o -c hello.c
scons: done building targets.



2018年11月25日 星期日

Note for LTE Frequency bands and channel bandwidths


  • Band 33 - Band 52為TDD,其餘為FDD。如果要簡單了解TDD/FDD的運作,可以看這段簡介Duplex in LTE Fundamentals of 4G LTE,在FDD的Downlink與Uplink中間會有Guard band,它將兩個頻率分開。這確保了同時使用的通信信道不會受到干擾,這將導致兩種傳輸的質量降低。TDD與FDD在Duplex in LTE Fundamentals of 4G LTE有簡單易懂的解釋。

  • LTE中定義了幾種不同的bandwidth,分別為1.4MHz、3MHz、5MHz、10MHz、15MHz與20MHz。不同的bandwidth會有不同大小的Guard band(概略約10%,實際算法可以參考LTE guard band calculation)。







2018年11月4日 星期日

AWS IoT Testing with MQTT.FX


AWS IoT基本上就是MQTT,這篇文章簡介如何Create一個AWS IoT device並使用MQTT.FX測試。

Create a IoT Device on AWS




Create a thing




Create a CERT

Enable CERT


Link CERT to instance


Download CERT
[Learn] -> [Connect to AWS IoT] -> [Configuring a device] -> [Get Start] -> [Choose a platform] / [Choose a AWS IoT Device SDK]


Check/Show your URL


Testing with MQTT.FX









2018年10月28日 星期日

Table Of Content for tag "IPv6 RFC"







2018年9月22日 星期六

note for autoconf - CH1 Introduction


Autoconf是一個用於生成shell script的工具(我們稱這個script為“configure”),可自動配置source code以適應Posix系統。“configure”可獨立運行(用戶不需要使用Autoconf)。“configure”在運行時不需要手動用戶干預; 甚至不需要指定係統類型的參數。 而且“configure”會測試source code/package所需的環境。Autoconf的目標是讓每個user都能夠輕鬆的執行“configure”。Autoconf在其目標方面非常成功,多數抱怨都是關於編寫Autoconf,而不是由此產生的“configure”的行為。


    參考資料:
  • https://www.gnu.org/software/autoconf/manual/index.html, Autoconf documentation
  • https://bootlin.com/pub/conferences/2016/elc/petazzoni-autotools-tutorial/petazzoni-autotools-tutorial.pdf, GNU Autotools: a tutorial




2018年9月9日 星期日

Mount UBI with nandsim


UBI/UBIFS無法使用loop-back方式mount, 不過我們可以使用NAND simulator將UBI燒入到nandsim中.
root@vista:~# mkfs.ubifs -m 2KiB -e 129024 -c 2048 -r ubifs-root -x zlib ubifs.img
root@vista:~# cat ubi.ini
[ubi_rfs]
mode=ubi
image=ubifs.img
vol_id=0
vol_type=dynamic
vol_name=ubi_rfs
vol_alignment=1
vol_flags=autoresize

root@vista:~# ubinize -o my.ubi -p 128KiB -m 2KiB -O 512 ubi.ini
ubinize: volume size was not specified in section "ubi_rfs", assume minimum to fit image "ubifs.img"1806336 bytes (1.7 MiB)
root@vista:~# modprobe nandsim first_id_byte=0x20 second_id_byte=0xaa third_id_byte=0x00 fourth_id_byte=0x15
root@vista:~# ubiformat /dev/mtd0 -f my.ubi
ubiformat: mtd0 (nand), size 268435456 bytes (256.0 MiB), 2048 eraseblocks of 131072 bytes (128.0 KiB), min. I/O size 2048 bytes
libscan: scanning eraseblock 2047 -- 100 % complete
ubiformat: 2048 eraseblocks have valid erase counter, mean value is 3
ubiformat: flashing eraseblock 15 -- 100 % complete
ubiformat: formatting eraseblock 2047 -- 100 % complete
ubiformat: formatting eraseblock 2047 -- 100 % complete
root@vista:~# ubiattach -m 0
UBI device number 0, total 2048 LEBs (264241152 bytes, 252.0 MiB), available 0 LEBs (0 bytes), LEB size 129024 bytes (126.0 KiB)
root@vista:~# ubinfo -a
UBI version:                    1
Count of UBI devices:           1
UBI control device major/minor: 10:55
Present UBI devices:            ubi0

ubi0
Volumes count:                           1
Logical eraseblock size:                 129024 bytes, 126.0 KiB
Total amount of logical eraseblocks:     2048 (264241152 bytes, 252.0 MiB)
Amount of available logical eraseblocks: 0 (0 bytes)
Maximum count of volumes                 128
Count of bad physical eraseblocks:       0
Count of reserved physical eraseblocks:  40
Current maximum erase counter value:     9
Minimum input/output unit size:          2048 bytes
Character device major/minor:            245:0
Present volumes:                         0

Volume ID:   0 (on ubi0)
Type:        dynamic
Alignment:   1
Size:        2002 LEBs (258306048 bytes, 246.3 MiB)
State:       OK
Name:        ubi_rfs
Character device major/minor: 245:1

root@vista:~# mount -t ubifs /dev/ubi0_0 www

Usage: mkfs.ubifs [OPTIONS] target
Make a UBIFS file system image from an existing directory tree
Options:
-r, -d, --root=DIR       build file system from directory DIR
-m, --min-io-size=SIZE   minimum I/O unit size
-e, --leb-size=SIZE      logical erase block size
-c, --max-leb-cnt=COUNT  maximum logical erase block count
-x, --compr=TYPE         compression type - "lzo", "favor_lzo", "zlib" or
                         "none" (default: "lzo")

# mkfs.ubifs -F -r </path/to/your/rootfs/tree> -m <min io size> -e <LEB size> -c 
        <Erase Blocks count> -o </path/to/output/Image.ubifs>
<min io size> Equals the page-size of the used NAND-Flash
<LEB size> Logical Erase Block size
<Erase Blocks count> maximum logical erase block count
mkfs.ubifs -m 2KiB -e 129024 -c 2048 -r ubifs-root -x zlib ubifs.img這些相關參數是先用ubiformat確認的, 不過-e是在mount failed的時候看dmesg的


Usage: ubinize [-o filename] [-p ] [-m ] [-s ] [-O ] [-e ]
                [-x ] [-Q ] [-v] [-h] [-V] [--output=] [--peb-size=]
                [--min-io-size=] [--sub-page-size=] [--vid-hdr-offset=]
                [--erase-counter=] [--ubi-ver=] [--image-seq=] [--verbose] [--help]
                [--version] ini-file
-p, --peb-size=       size of the physical eraseblock of the flash
                             this UBI image is created for in bytes,
                             kilobytes (KiB), or megabytes (MiB)
                             (mandatory parameter)
-m, --min-io-size=    minimum input/output unit size of the flash
                             in bytes
-O, --vid-hdr-offset=   offset if the VID header from start of the
                             physical eraseblock (default is the next
                             minimum I/O unit or sub-page after the EC
                             header)

# ubinize -vv -o <output image> -m <min io size> -p <PEB size>KiB 
        -s <subpage-size> -O <VID-hdr-offset> <configuration file>
<min io size> Equals the page-size of the used NAND-Flash
<PEB size> Physical Erase Block size (in KiB) - Equals the block size of
        the NAND-Flash
<Erase Blocks count> Count of the available Erase Blocks
<subpage-size> Subpage size in bytes. Default value is the minimum 
        input/output size (page-size)
<VID-hdr-offset> offset if the VID header from start of the PEB 
        (default is the next min I/O unit or sub-page after the EC header)
<configuration file> Path to a configuration file.

ubinize -o my.ubi -p 128KiB -m 2KiB -O 512 ubi.ini這些參數也是在ubiformat修正的, 如-O 512


    參考資料:
  • http://www.linux-mtd.infradead.org/faq/ubifs.html#L_loop_mount, Can UBIFS mount loop-back devices?
  • https://elinux.org/UBIFS, UBIFS
  • http://www.linux-mtd.infradead.org/faq/nand.html#L_nand_nandsim, How do I use NAND simulator?




2018年9月2日 星期日

How to clone MDM9207 codeaurora/openembedded


筆記一下How to clone MDM9207 codeaurora/openembedded
brook@vista:~/oe-9x07$ git clone https://gerrit.googlesource.com/git-repo
Cloning into 'git-repo'...
remote: Counting objects: 139, done
remote: Total 4069 (delta 2714), reused 4069 (delta 2714)
Receiving objects: 100% (4069/4069), 1.21 MiB | 0 bytes/s, done.
Resolving deltas: 100% (2714/2714), done.
Checking connectivity... done.
brook@vista:~/oe-9x07$ ln -s git-repo/repo
brook@vista:~/oe-9x07$ ./repo init -u git://codeaurora.org/quic/le/le/manifest.git -b release -m LE.UM.1.0.2-34100-9x07.xml
Get https://gerrit.googlesource.com/git-repo/clone.bundle
Get https://gerrit.googlesource.com/git-repo
Get git://codeaurora.org/quic/le/le/manifest.git
remote: Counting objects: 4009, done.
remote: Compressing objects: 100% (191/191), done.
remote: Total 4009 (delta 84), reused 0 (delta 0)        KiB/s
Receiving objects: 100% (4009/4009), 1.07 MiB | 706.00 KiB/s, done.
Resolving deltas: 100% (2191/2191), done.
From git://codeaurora.org/quic/le/le/manifest
 * [new branch]      IMM.LE.1.0 -> origin/IMM.LE.1.0
 * [new branch]      release    -> origin/release
...
...
Your identity is: Brook Kuo <rene3210 at gmail.com>
If you want to change this, please re-run 'repo init' with --config-name

repo has been initialized in /home/brook/oe-9x07

brook@vista:~/oe-9x07$ ./repo sync
Fetching project platform/vendor/qcom/titanium_32
remote: Counting objects: 5758, done.
remote: Compressing objects: 100% (375/375), done.
remote: Total 5758 (delta 305), reused 380 (delta 177)
Receiving objects: 100% (5758/5758), 1.02 MiB | 868.00 KiB/s, done.
Resolving deltas: 100% (3639/3639), done.
From git://codeaurora.org/platform/vendor/qcom/titanium_32
 * [new branch]      LA.BR.1.3.7_rb1.11 -> caf/LA.BR.1.3.7_rb1.11
...
...
Fetching projects: 100% (58/58), done.
Checking out files: 100% (52804/52804), done. files:  23% (12539/52804)
Checking out files: 100% (622/622), done. out files:  26% (167/622)
Syncing work tree: 100% (58/58), done.



    參考資料
  • https://gerrit.googlesource.com/git-repo/, repo - The Multiple Git Repository Tool
  • https://wiki.codeaurora.org/xwiki/bin/QLBEP/, Open Embedded for MSM




note - Blue Ocean Strategy


藍海策略提供了一個清晰的4步驟流程(買方效益→價格→成本→推行),協助企業擺脫競爭對手,並且建立起一片獲利強勁成長的藍海。
這套4步驟流程是環繞著藍海策略和公平程序(fair process)的概念和分析工具所設計的,透過這套流程,經理人和他們的團隊可以在研擬嚴謹和具體的策略的同時,不至於見樹不見林、掌握大願景。
    參考資料:
  • https://www.managertoday.com.tw/articles/view/51294, 8個要點,一次讀懂藍海策略

2018年9月1日 星期六

note - The Balanced ScoreCard


平衡計分卡不能為企業創造策略,但能幫組織有效執行策略。平衡計分卡以組織的共同願景與戰略為內核,將公司的願景與戰略轉化為下屬各責任部門(如各事業部)在財務(Financial)顧客(Customer)內部流程(Internal Processes)創新與學習(Innovation&Learning)等四個方面的系列具體目標(即成功的因素),並設置相應的四張計分卡,其基本框架見下圖:

優點:
企業向來以財務數字做為績效衡量的標準,但無論是業績目標或營收、投資報酬率等,都是反映過去行動所獲致的成果;簡單說,財務數字衡量的是「過去的績效」,是一種「落後指標」,而非創造未來績效的指引。
平衡計分卡的理念在於:在財務衡量(落後指標)之外,積極找出能夠創造未來財務成果的「績效驅動因素」(performance driver),也就是相較財務成果而言的「領先指標」,例如顧客滿意度、高效率的流程、員工能力、士氣等,讓「績效衡量制度」能與「策略」配合一致。
平衡計分卡不僅是一種管理手段,也體現了一種管理思想,就是:只有量化的指標才是可以考核的,必須將要考核的指標進行量化

缺點:
平衡計分卡中有一些條目是很難解釋清楚或者是衡量出來的。財務指標當然不是問題,而非財務指標往往很難去建立起來。



    參考資料:
  • http://wiki.mbalib.com/zh-tw/%E5%B9%B3%E8%A1%A1%E8%AE%A1%E5%88%86%E5%8D%A1, 平衡計分卡
  • https://www.managertoday.com.tw/articles/view/2489, 5分鐘!了解「平衡計分卡」





2018年8月25日 星期六

Using gdbserver to debug MDM9xxx


openembedded提供了一個良好的gdb環境,設定步驟如下。
brook@vista:/home/brook/apps_proc/poky/build$ tar zxvf tmp-glibc/deploy/images/mdm9607/machine-image-mdm9607-dbg.tar.gz
./
./var/
./var/lib/
./var/lib/opkg/
./var/lib/opkg/info/
...

brook@vista:/home/brook/apps_proc/poky/build$ mv usr/lib/.debug/* usr/lib/
brook@vista:/home/brook/apps_proc/poky/build$ mv lib/.debug/* lib

brook@vista:/home/brook/apps_proc/poky/build$ ln -s libconfig.so.9.2.0 usr/lib/libconfig.so.9
...

brook@vista:/home/brook/apps_proc/poky/build$ ln -s libpthread-2.22.so lib/libpthread.so.0
brook@vista:/home/brook/apps_proc/poky/build$ ln -s librt-2.22.so lib/librt.so.1
brook@vista:/home/brook/apps_proc/poky/build$ ln -s libm-2.22.so lib/libm.so.6
brook@vista:/home/brook/apps_proc/poky/build$ ln -s libc-2.22.so lib/libc.so.6
brook@vista:/home/brook/apps_proc/poky/build$ ln -s ld-2.22.so lib/ld-linux.so.3
brook@vista:/home/brook/apps_proc/poky/build$ ln -s libdl-2.22.so lib/libdl.so.2
...

brook@vista:/home/brook/apps_proc/poky/build/tmp-glibc/work/armv7a-vfp-neon-oe-linux-gnueabi/myprog/git-r0/build/src$ adb push myprog /usr/bin/myprog

brook@vista:/home/brook/apps_proc/poky/build/tmp-glibc/work/armv7a-vfp-neon-oe-linux-gnueabi/myprog/git-r0/build/src$ adb shell
sh-3.2# gdbserver :2345 /usr/bin/myprog
Process /usr/bin/myprog created; pid = 9035
Listening on port 2345


接著host端就可以開啟GDB來debug了
brook@vista:/home/brook/apps_proc/poky/build/tmp-glibc/work/armv7a-vfp-neon-oe-linux-gnueabi/myprog/git-r0/build/src$ arm-oe-linux-gnueabi-gdb myprog 
GNU gdb (GDB) 7.10.1
Copyright (C) 2015 Free Software Foundation, Inc.
...
Reading symbols from myprog...done.
(gdb) set sysroot /home/brook/apps_proc/poky/build/
(gdb) target remote 10.0.0.1:2345
Remote debugging using 10.0.0.1:2345
Reading symbols from /home/brook/apps_proc/poky/build//lib/ld-linux.so.3...done.
0xb6fceac0 in ?? ()
(gdb) b main
Breakpoint 1 at 0x7f5595ac: file /home/brook/apps_proc/poky/build/tmp-glibc/work/armv7a-vfp-neon-oe-linux-gnueabi/myprog/git-r0/src/main.c, line 207.
(gdb) c
Continuing.

Breakpoint 1, main (argc=1, argv=0xbefffdf4)
    at /home/brook/apps_proc/poky/build/tmp-glibc/work/armv7a-vfp-neon-oe-linux-gnueabi/myprog/git-r0/src/main.c:207
207     {
(gdb) set sysroot /home/brook/apps_proc/poky/build
warning: .dynamic section for "/home/brook/apps_proc/poky/build/lib/libpthread.so.0" is not at the expected address (wrong library or version mismatch?)
warning: .dynamic section for "/home/brook/apps_proc/poky/build/lib/libm.so.6" is not at the expected address (wrong library or version mismatch?)
warning: .dynamic section for "/home/brook/apps_proc/poky/build/lib/libc.so.6" is not at the expected address (wrong library or version mismatch?)
Use the "info sharedlibrary" command to see the complete listing.
Do you need "set solib-search-path" or "set sysroot"?
Reading symbols from /home/brook/apps_proc/poky/build/usr/lib/libconfig.so.9...done.
Reading symbols from /home/brook/apps_proc/poky/build/lib/libpthread.so.0...done.
Reading symbols from /home/brook/apps_proc/poky/build/lib/librt.so.1...done.
Reading symbols from /home/brook/apps_proc/poky/build/lib/libm.so.6...done.
Reading symbols from /home/brook/apps_proc/poky/build/lib/libc.so.6...done.
Reading symbols from /home/brook/apps_proc/poky/build/lib/libdl.so.2...done.
(gdb) info share
From        To          Syms Read   Shared Object Library
0x7473e7c0  0x7475a328  Yes         /home/brook/apps_proc/poky/build/lib/ld-linux.so.3
0xb6fc42d0  0xb6fca6e0  Yes         /home/brook/apps_proc/poky/build/usr/lib/libconfig.so.9
0xb6ebc210  0xb6ecb4e8  Yes         /home/brook/apps_proc/poky/build/lib/libpthread.so.0
0xb6ea2720  0xb6ea6034  Yes         /home/brook/apps_proc/poky/build/lib/librt.so.1
0xb6e28bf0  0xb6e5ac20  Yes         /home/brook/apps_proc/poky/build/lib/libm.so.6
0xb6ced280  0xb6dee638  Yes         /home/brook/apps_proc/poky/build/lib/libc.so.6
0xb6cc3928  0xb6cc4870  Yes         /home/brook/apps_proc/poky/build/lib/libdl.so.2
確認所有symbol都有找到之後, 就可以開始debug之旅了.




2018年8月11日 星期六

note for "The Art of Readable Code" - CH13 Writing Less Code


The most readable code is no code at all.
Programmer最重要的技能之一就是知道哪些code不用寫,因為寫出來就要測試&維護,越小的程式碼越容易被維護,coupling程度越低越好,最好彼此獨立,有幾個方向:
  1. Create as much generic “utility” code as possible to remove duplicated code. (See Chapter 10, Extracting Unrelated Subproblems.)
  2. Remove unused code or useless features. (See the following sidebar.)
  3. Keep your project compartmentalized into disconnected subprojects.
  4. Generally, be conscious of the “weight” of your codebase. Keep it light and nimble.

Be Familiar with the Libraries Around You

每隔一段時間應該要花一下時間,讀一下你的 standard library,目的在於能對standard library的API有概念,以便在coding時能聯想到,並大量且反覆地使用這些library。


    參考資料:
  • The Art of Readable Code



2018年7月29日 星期日

note for "The Art of Readable Code" - CH12 Turning Thoughts into Code


You do not really understand something unless you can explain it to your grandmother. 
— Albert Einstein

先用口語描述演算法後,再轉成程式碼,能讓programmer寫出更自然的code,也有助於找出可以分解的子問題。
 We are reading three row iterators in parallel.
 Whenever the rows' times don't line up, advance the rows so they do line up.
 Then print the aligned rows, and advance the rows again.
 Keep doing this until there are no more matching rows left.

上述口語轉成的code為
def PrintStockTransactions(): 
    stock_iter = ...
    price_iter = ... 
    num_shares_iter = ...

    while True:
        time = AdvanceToMatchingTime(stock_iter, price_iter, num_shares_iter) 
        if time is None:
            return
        # Print the aligned rows.
        print "@", time,
        print stock_iter.ticker_symbol,
        print price_iter.price,
        print num_shares_iter.number_of_shares
        stock_iter.NextRow()
        price_iter.NextRow()
        num_shares_iter.NextRow()


    參考資料:
  • The Art of Readable Code



note for "The Art of Readable Code" - CH11 One Task at a Time


Code should be organized so that it’s doing only one task at a time.舉個投票例子,投UP則+1,Down則-1,結果為所有投票總和,如果依據該rule,則code應該是
var vote_changed = function (vote) {
    var score = get_score();
    score += vote_value(vote);
    set_score(score);
};

基本上這個章節的概念跟前一章節差不多"將子問題抽離,讓function專注在處理問題本身上面"。其餘細節就不贅述了。

    參考資料:
  • The Art of Readable Code



2018年7月28日 星期六

note for "The Art of Readable Code" - CH10 Extracting Unrelated Subproblems


將子問題抽離,讓function專注在處理問題本身上面,首先要先了解這個function的"目的",接著抽離與"目的"無關的子問題,將這些子問題一一寫成獨立的function,如:(簡單說就主程式專注在邏輯上,細節部分就一一寫成sub function處理)
// Return which element of 'array' is closest to the given latitude/longitude.
// Models the Earth as a perfect sphere.
var findClosestLocation = function (lat, lng, array) {
    var closest;
    var closest_dist = Number.MAX_VALUE;
    for (var i = 0; i < array.length; i += 1) {
        // Convert both points to radians.
        var lat_rad = radians(lat);
        var lng_rad = radians(lng);
        var lat2_rad = radians(array[i].latitude);
        var lng2_rad = radians(array[i].longitude);
        // Use the "Spherical Law of Cosines" formula.
        var dist = Math.acos(Math.sin(lat_rad) * Math.sin(lat2_rad) +
                             Math.cos(lat_rad) * Math.cos(lat2_rad) *
                             Math.cos(lng2_rad - lng_rad));
        if (dist < closest_dist) {
             closest = array[i];
             closest_dist = dist;
        }
    }
    return closest;
};

抽離後的code
var spherical_distance = function (lat1, lng1, lat2, lng2) {
    var lat1_rad = radians(lat1);
    var lng1_rad = radians(lng1);
    var lat2_rad = radians(lat2);
    var lng2_rad = radians(lng2);
    // Use the "Spherical Law of Cosines" formula.
    return Math.acos(Math.sin(lat1_rad) * Math.sin(lat2_rad) +
                     Math.cos(lat1_rad) * Math.cos(lat2_rad) *
};

var findClosestLocation = function (lat, lng, array) {
    var closest;
    var closest_dist = Number.MAX_VALUE;
    for (var i = 0; i < array.length; i += 1) {
        var dist = spherical_distance(lat, lng, array[i].latitude, array[i].longitude);
        if (dist < closest_dist) {
            closest = array[i];
            closest_dist = dist;
        }
    }
    return closest;
};
抽離這些子問題的另一個好處容易優化,比如有更好的方式去運算spherical_distance()或是變更findClosestLocation()的運算邏輯。建立通用的function也是一個很好也很重要的習慣,當你開發一個新的program時,就可以運用這些通用function,快速建立一個prototype。

Simplifying an Existing Interface & Reshaping an Interface to Your Needs

如果既有的API不好用,那就包裝他或改寫他吧,我在A Wrap for service/thread也包裝了一些API,讓自己易於開發,有必要也會進行Reshaping/Re-factor等等步驟。

    參考資料:
  • The Art of Readable Code



2018年7月8日 星期日

Table Of Content for tag "Design Patterns with C "






2018年6月17日 星期日

Linux Kernel(15.3)- The Linux usage model for device tree data


這篇基本上就是"Documentation/devicetree/usage-model.txt", The Linux usage model for device tree data的筆記

DT(Device Tree,或稱Open Firmware Device Tree)是一個資料結構(data structure)用於讓OS讀取硬體周邊訊息動態執行,因此OS就不用hard code硬體驅動。

所謂的"bindings"就是一組通用的DTS設定,用來描述其硬體,包含了data busses, interrupt lines, GPIO connections, and peripheral devices等等。

盡可能使用現有的binding來描述硬體,以最大限度地利用現有的代碼,但由於property和node名稱只是字串,因此通過定義新property和node可以輕鬆擴展現有binding。 不要沒做功課就自己建立新的binding,i2c busses就因為沒有先確認是否已經有人定義了相關的binding,就建立了新的binding,導致現在有兩套不相容的binding發生。

DT所做的只是提供一種language將硬體設定從device driver分離開來,如此可以透過傳入不同的DT給OS,以適應不同的硬體設定裝置. 進而減少一些重複的code。

DT在Linux底下有三個主要目的
1) platform identification,
2) runtime configuration, and
3) device population.


platform identification

首先kernel會先使用DT來辨識特定的機器,並且執行相關的初始化,比如ARM會在setup_arch()呼叫setup_machine_fdt()尋找適合的DT(比對DT root底下的"compatible" property。
    compatible = "ti,omap3-beagleboard-xm", "ti,omap3450", "ti,omap3";
上面的例子定義了"ti,omap3-beagleboard-xm",也宣稱相容"OMAP 3450 SOC"與"OMAP3"系列的SoC,你會注意到這樣的宣稱會從最具體的board到SoC的家族,關於compatible的值必須進行記錄說明其含義。

runtime configuration

一般來說,DT是firmware傳遞資料給kernel的唯一方法,PowerPC呼叫of_scan_flat_dt(early_init_dt_scan_root, NULL)執行early init,ARM則是呼叫mdesc = setup_machine_fdt(__atags_pointer)

device population

在early configuration之後,kernel會用 unflatten_device_tree()將DT轉成device node tree,讓之後的init_early(), init_irq() and init_machine()等等使用,init_early()用於任何需要在啟動過程中儘早執行的設置,init_irq()用於設置中斷處理,而init_machine()負責建立Linux platform device,這裡主要呼叫of_platform_populate()建構platform device。之後的driver也是透過of_platform_populate()建構platform device。
i2c_add_driver( ) @ i2c.h  
  |-> i2c_register_driver() @i2c-core.c /* 將device_driver中的bus_type設成i2c_bus_type */  
    |--> driver_register() @driver.c  
      |--> bus_add_driver() @bus.c /* 建立sysfs file node 與 attr */  
        |--> driver_attach() @dd.c  
          |--> bus_for_each_dev(drv->bus, NULL, drv, __driver_attach) @bus.c  
            |--> __driver_attach @dd.c  
              |--> driver_match_device(drv, dev) @base.h  
                |--> i2c_device_match() @i2c-core.c  
                  /***********************************************  
                   如果是device tree, 會透過 of_driver_match_device()去做匹配的動作
                   如果不是device tree就改用 i2c_match_id()去完成匹配的動作
                  ***************************************************/ 
                  /* Attempt an OF style match */
                  if (of_driver_match_device(dev, drv))
                    return 1;

                  /* Then ACPI style match */
                  if (acpi_driver_match_device(dev, drv))
                    return 1;
                  
                  driver = to_i2c_driver(drv);
                  /* match on an id table if there is one */
                  if (driver->id_table)
                    return i2c_match_id(driver->id_table, client) != NULL;
              |--> driver_probe_device() @dd.c  
                /*如果匹配成功, 接下來就要準備 call driver 中的 probe function*/
                |--> really_probe() @dd.c  
                  |--> i2c_device_probe() @i2c-core.c  
                    |--> rt5627_i2c_probe() @rt5627.c  
from http://janbarry0914.blogspot.com/2014/08/device-tree-i2c-device-driver-match.html



2018年4月21日 星期六

A pattern for state machine II - SM framework


我將A pattern for state machine改寫成framework形式,使用者需要先使用sm_alloc()分配一個struct sm,再使用sm_fp_reg()將每個state的callback function掛上,最後有event要執行時,只要呼叫sm_run(sm, new_event)即可。
state的callback function的撰寫邏輯,大致與之前一樣,return下一個state,進入下一個state要做的動作,我都用do_state_x()包起來,而do_state_x()會return 該state。

sm.h

#ifndef SM_H
#define SM_H

struct sm {
    int cur_state;
    int prv_event;
    void *v;
};

/**
 * state function
 * @return next state
 */
typedef int (*sm_st_fp)(struct sm *sm, int new_event);
struct sm *sm_alloc(int num_of_state);
int sm_run(struct sm *s, int new_event);
int sm_fp_reg(struct sm *s, int state, sm_st_fp fp);

#endif


sm.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sm.h"

struct _sm {
    struct sm s;
    int num_of_st;
    sm_st_fp fp[0];
};

struct sm *sm_alloc(int num_of_state)
{
    struct _sm *_s;
    int sz;

    sz = sizeof(struct _sm) + sizeof(sm_st_fp) * num_of_state;
    _s = (struct _sm*) malloc(sz);
    if (!_s) {
        return NULL;
    }
    memset(_s, 0, sz);
    _s->num_of_st = num_of_state;
    return (struct sm*)_s;
}

int sm_run(struct sm *s, int new_event)
{
    struct _sm *_s = (struct _sm*) s;
    if (s->cur_state > _s->num_of_st) {
        printf("out of st\n");
        return -1;
    }

    if (!_s->fp[s->cur_state]) {
        printf("null fp\n");
        return -1;
    }

    s->cur_state = _s->fp[s->cur_state](s, new_event);
    s->prv_event = new_event;
    return 0;
}

int sm_fp_reg(struct sm *s, int state, sm_st_fp fp)
{
    struct _sm *_s = (struct _sm*) s;
    if (state > _s->num_of_st) {
        printf("out of st\n");
        return -1;
    }

    _s->fp[state] = fp;
    return 0;
}


main.c

因為是framework,所以,我把state/event都拉出來,
因此每個State Machine都應該定義自己的event與state。
enum state {
    STATE_1,
    STATE_2,
    STATE_3,
    STATE_4,
};

enum event {
    E1 = 1,
    E2,
    E3,
    E4,
};

int main(int argc, char *argv[])
{
    struct sm *s;
    char ch;
    s = sm_alloc(3);
    if (!s) {
        return -1;
    }
    sm_fp_reg(s, STATE_1, in_state_1);
    sm_fp_reg(s, STATE_2, in_state_2);
    sm_fp_reg(s, STATE_3, in_state_3);
    sm_fp_reg(s, STATE_4, in_state_4);
    while (1) {
        while (((ch = getc(stdin)) == '\n') || (ch < '0') || (ch > '4'));
        sm_run(s, ch - '0');
    }

    return 0;
}


refine callback function

因為是framework,所以,callback function的定義要改成return int。
int do_s1(void)
{
    printf("%s(#%d)\n", __FUNCTION__, __LINE__);
    return STATE_1;
}

int do_s2(void)
{
    printf("%s(#%d)\n", __FUNCTION__, __LINE__);
    return STATE_2;
}

int do_s3(void)
{
    printf("%s(#%d)\n", __FUNCTION__, __LINE__);
    return STATE_3;
}

int do_s4(void)
{
    printf("%s(#%d)\n", __FUNCTION__, __LINE__);
    return STATE_4;
}


int in_state_1(struct sm *s, int new_event)
{
    printf("%s(#%d): pre_evt:%d, new_evt:%d\n", __FUNCTION__, __LINE__, s->prv_event, new_event);
    switch (new_event) {
        case E1:
            printf("change to S2\n");
            return do_s2();
        case E2:
            printf("change to S3\n");
            return do_s3();
        default:
            printf("keep the same STATE && do nothing\n");
            return STATE_1;
    }
}

int in_state_2(struct sm *s, int new_event)
{
    printf("%s(#%d): pre_evt:%d, new_evt:%d\n", __FUNCTION__, __LINE__, s->prv_event, new_event);
    switch (new_event) {
        case E3:
            printf("change to S3\n");
            return do_s3();
        default:
            printf("keep the same STATE && do s2 again\n");
            return do_s2();     // do s2 again
    }
}

int in_state_3(struct sm *s, int new_event)
{
    printf("%s(#%d): pre_evt:%d, new_evt:%d\n", __FUNCTION__, __LINE__, s->prv_event, new_event);
    switch (new_event) {
        case E2:
            printf("change to S4\n");
            return do_s4();
        default:
            printf("keep the same STATE && do nothing\n");
            return STATE_3;
    }
}

int in_state_4(struct sm *s, int new_event)
{
    printf("%s(#%d): pre_evt:%d, new_evt:%d\n", __FUNCTION__, __LINE__, s->prv_event, new_event);
    switch (new_event) {
        case E1:
            printf("change to S2\n");
            return do_s2();
        case E3:
            printf("change to S1\n");
            return do_s1();
        default:
            printf("keep the same STATE && do again\n");
            return do_s4();
    }
}



2018年4月15日 星期日

赫茲伯格的雙因素激勵理論 - 筆記


雙因素理論(Two Factor Theory)又叫激勵保健理論(Motivator-Hygiene Theory),是美國的行為科學家弗雷德里克·赫茨伯格(Fredrick Herzberg)提出來的。
赫茨伯格著手研究哪些事情使人們在工作中快樂和滿足,哪些事情造成不愉快和不滿足。結果他發現,使員工感到滿意的都是屬於工作本身或工作內容方面的;使員工感到不滿的,都是屬於工作環境或工作關係方面的。他把前者叫做激勵因素,後者叫做保健因素。
那些能帶來積極態度、滿意和激勵作用的因素就叫做“激勵因素”,這是那些能滿足個人自我實現需要的因素,包括:具挑戰性的工作、自主權、嘉獎和升遷。

根據赫茨伯格的理論,在調動員工積極性方面,可以分別採用以下兩種基本做法:
(一)直接滿足
直接滿足,又稱為工作任務以內的滿足。它是一個人通過工作所獲得的滿足,這種滿足是通過工作本身和工作過程中人與人的關係得到的。它能使員工學習到新的知識和技能,產生興趣和熱情,使員工具有光榮感、責任心和成就感。
(二)間接滿足
間接滿足,又稱為工作任務以外的滿足。這種滿足不是從工作本身獲得的,而是在工作以後獲得的。例如晉升、授銜、嘉獎或物質報酬和福利等,就都是在工作之後獲得的。在使用這種激勵因素時,必須與個人的工作績效掛鉤。否則一味的“吃大鍋飯”,久而久之,獎金就會變成保健因素,再多也起不了激勵作用。




2018年4月7日 星期六

note for "The Art of Readable Code" - CH9 Variables and Readability


減少不必要的變數可以增加閱讀性, 比如
root_message.last_view_time = datetime.datetime.now()
會比下面code更容易理解
now = datetime.datetime.now()
root_message.last_view_time = now

下面還有幾個需要優化的的例子, 可以好好思考如何改善.
boolean done = false;
while (/* condition */ && !done) {
    ...
    if (...) {
        done = true;
        continue;
    }
}
與
var remove_one = function (array, value_to_remove) {
    var index_to_remove = null;
    for (var i = 0; i < array.length; i += 1) {
        if (array[i] === value_to_remove) {
            index_to_remove = i;
            break;
        }
    }
    if (index_to_remove !== null) {
        array.splice(index_to_remove, 1); 
    }
};


programmer都知道要盡量限縮變數的範圍, 因為可視範圍小, 要記住的變數數量也會減少, 也且可以避免global/local variable用錯的窘境.
submitted = false; // Note: global variable
var submit_form = function (form_name) {
    if (submitted) {
        return;  // don't double-submit the form
    }
    ...
    submitted = true;
};
可以被修改成
var submit_form = (function () {
    var submitted = false; // Note: can only be accessed by the function below
    return function (form_name) {
        if (submitted) {
            return;  // don't double-submit the form
        }
        ...
        submitted = true; 
    };
}());


    參考資料:
  1. The Art of Readable Code




Table Of Content for tag "The Art of Readable Code"


這是一本好書, 建議每個programmer都應該買來翻一翻




熱門文章