2020年5月9日 星期六

Linux Kernel(18.2)- SysCall mount


簡單記錄一下Linux 4.19-rc8從mount system call到呼叫file_system_type.mount()的call flow
SYSCALL_DEFINE5(mount)
  |--> ksys_mount()
    |--> do_mount()
      |--> do_new_mount()
        |--> type = get_fs_type()
        |--> vfs_kern_mount(type)
          |--> mount_fs(type)
            |--> type->mount()


SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
        char __user *, type, unsigned long, flags, void __user *, data)
{
    return ksys_mount(dev_name, dir_name, type, flags, data);
}

int ksys_mount(char __user *dev_name, char __user *dir_name, char __user *type,
        unsigned long flags, void __user *data)
{
  int ret;
  char *kernel_type;
  char *kernel_dev;
  void *options;

  kernel_type = copy_mount_string(type);
  ...

  kernel_dev = copy_mount_string(dev_name);
  ...

  options = copy_mount_options(data);
  ...

  ret = do_mount(kernel_dev, dir_name, kernel_type, flags, options);
  ...
  
  return ret;
}

long do_mount(const char *dev_name, const char __user *dir_name,
  const char *type_page, unsigned long flags, void *data_page)
{
  struct path path;
  unsigned int mnt_flags = 0, sb_flags;
  int retval = 0;

  retval = user_path(dir_name, &path);

  if (flags & MS_REMOUNT)
    retval = do_remount(&path, flags, sb_flags, mnt_flags, data_page);
  else if (flags & MS_BIND)
    retval = do_loopback(&path, dev_name, flags & MS_REC);
  else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
    retval = do_change_type(&path, flags);
  else if (flags & MS_MOVE)
    retval = do_move_mount(&path, dev_name);
  else
    retval = do_new_mount(&path, type_page, sb_flags, mnt_flags, dev_name, data_page);
  ...

  return retval;
}

static int do_new_mount(struct path *path, const char *fstype, int sb_flags,
   int mnt_flags, const char *name, void *data)
{
  struct file_system_type *type;
  struct vfsmount *mnt;
  int err;

  type = get_fs_type(fstype);
  ...

  mnt = vfs_kern_mount(type, sb_flags, name, data);
  ...

  put_filesystem(type);
  ...
  err = do_add_mount(real_mount(mnt), path, mnt_flags);
  ...
  return err;
}

struct vfsmount *
vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
{
  struct mount *mnt;
  mnt = alloc_vfsmnt(name);
  ...

  root = mount_fs(type, flags, name, data);
  ...
  return &mnt->mnt;    
}

struct dentry *
mount_fs(struct file_system_type *type, int flags, const char *name, void *data)
{
  struct dentry *root;
  ...

  root = type->mount(type, flags, name, data)
  ...

  return root;
}



    參考資料:
  • https://lkml.org/lkml/2018/3/16/905, fs: add ksys_mount() helper; remove in-kernel calls to sys_mount()
  • https://www.halolinux.us/kernel-architecture/the-mount-system-call.html, The Mount System Call




2020年5月3日 星期日

Yocto Project Reference Manual - extrausers.bbclass


extrausers.bbclass這個class允許我們在image中加入新的userand group,透過EXTRA_USERS_PARAMS 來設定,如:
新增兩個user,"tester-jim"與"tester-sue" ,其密碼都是tester01
     inherit extrausers
     EXTRA_USERS_PARAMS = "\
         useradd -P tester01 tester-jim; \
         useradd -P tester01 tester-sue; \
         "

也可以用於變更(設定)密碼
     inherit extrausers
     EXTRA_USERS_PARAMS = "\
         usermod -P 1876*18 root; \
         "

更複雜的例子如下
     inherit extrausers
     EXTRA_USERS_PARAMS = "\
         useradd -p '' tester; \
         groupadd developers; \
         userdel nobody; \
         groupdel -g video; \
         groupmod -g 1020 developers; \
         usermod -s /bin/sh tester; \
         "


    參考資料:
  • https://www.yoctoproject.org/docs/1.8/ref-manual/ref-manual.html#ref-classes-extrausers, extrausers.bbclass




2020年4月26日 星期日

how to include a static library in SDK?


這天忽然想build一個static binary, 才發現SDK沒有static library, 於是只好重新build一下SDK with static package.

brook@vista:~/oe-src$ . /opt/oecore-x86_64/environment-setup-cortexa7-neon-vfpv4-oe-linux-gnueabi brook@vista:~/oe-src$ ${CC} -static kobj.c /opt/oecore-x86_64/sysroots/x86_64-oesdk-linux/usr/libexec/arm-oe-linux-gnueabi/gcc/arm-oe-linux-gnueabi/5.3.0/real-ld: error: cannot find -lc /opt/oecore-x86_64/sysroots/cortexa7-neon-vfpv4-oe-linux-gnueabi/usr/lib/crt1.o(.text+0x28): error: undefined reference to '__libc_start_main' /opt/oecore-x86_64/sysroots/cortexa7-neon-vfpv4-oe-linux-gnueabi/usr/lib/crt1.o(.text+0x2c): error: undefined reference to 'abort' /opt/oecore-x86_64/sysroots/cortexa7-neon-vfpv4-oe-linux-gnueabi/usr/lib/crt1.o(.text+0x30): error: undefined reference to '__libc_csu_fini' /opt/oecore-x86_64/sysroots/cortexa7-neon-vfpv4-oe-linux-gnueabi/usr/lib/crt1.o(.text+0x38): error: undefined reference to '__libc_csu_init' /tmp/ccDJTrij.o:kobj.c:function init_hotplug_sock: error: undefined reference to 'bzero' /tmp/ccDJTrij.o:kobj.c:function init_hotplug_sock: error: undefined reference to 'getpid' /tmp/ccDJTrij.o:kobj.c:function init_hotplug_sock: error: undefined reference to 'socket' /tmp/ccDJTrij.o:kobj.c:function init_hotplug_sock: error: undefined reference to 'perror' /tmp/ccDJTrij.o:kobj.c:function init_hotplug_sock: error: undefined reference to 'setsockopt' /tmp/ccDJTrij.o:kobj.c:function init_hotplug_sock: error: undefined reference to 'bind' /tmp/ccDJTrij.o:kobj.c:function init_hotplug_sock: error: undefined reference to 'perror' /tmp/ccDJTrij.o:kobj.c:function init_hotplug_sock: error: undefined reference to 'close' /tmp/ccDJTrij.o:kobj.c:function main: error: undefined reference to 'memset' /tmp/ccDJTrij.o:kobj.c:function main: error: undefined reference to 'recv' /tmp/ccDJTrij.o:kobj.c:function main: error: undefined reference to 'puts' collect2: error: ld returned 1 exit status


這是因為沒有加入staticdev-pkgs
SDKIMAGE_FEATURES += "staticdev-pkgs"
SDKIMAGE_FEATURES相當於IMAGE_FEATURES, 但SDKIMAGE_FEATURES用於建立SDK image用, 如
$ bitbake -c populate_sdk imagename
SDKIMAGE_FEATURES可用的參數與IMAGE_FEATURES相同, 條列如下
  • allow-empty-password: Allows Dropbear and OpenSSH to accept root logins and logins from accounts having an empty password string.
  • dbg-pkgs: Installs debug symbol packages for all packages installed in a given image.
  • debug-tweaks: Makes an image suitable for development (e.g. allows root logins without passwords and enables post-installation logging). See the 'allow-empty-password', 'empty-root-password', and 'post-install-logging' features in this list for additional information.
  • dev-pkgs: Installs development packages (headers and extra library links) for all packages installed in a given image.
  • doc-pkgs: Installs documentation packages for all packages installed in a given image.
  • empty-root-password: Sets the root password to an empty string, which allows logins with a blank password.
  • package-management: Installs package management tools and preserves the package manager database.
  • post-install-logging: Enables logging postinstall script runs to the /var/log/postinstall.log file on first boot of the image on the target system.
  • ptest-pkgs: Installs ptest packages for all ptest-enabled recipes.
  • read-only-rootfs: Creates an image whose root filesystem is read-only. See the "Creating a Read-Only Root Filesystem" section in the Yocto Project Development Manual for more information.
  • splash: Enables showing a splash screen during boot. By default, this screen is provided by psplash, which does allow customization. If you prefer to use an alternative splash screen package, you can do so by setting the SPLASH variable to a different package name (or names) within the image recipe or at the distro configuration level.
  • staticdev-pkgs: Installs static development packages, which are static libraries (i.e. *.a files), for all packages installed in a given image.
Some image features are available only when you inherit the core-image class. The current list of these valid features is as follows:
  • eclipse-debug: Provides Eclipse remote debugging support.
  • hwcodecs: Installs hardware acceleration codecs.
  • nfs-server: Installs an NFS server.
  • qt4-pkgs: Supports Qt4/X11 and demo applications.
  • ssh-server-dropbear: Installs the Dropbear minimal SSH server.
  • ssh-server-openssh: Installs the OpenSSH SSH server, which is more full-featured than Dropbear. Note that if both the OpenSSH SSH server and the Dropbear minimal SSH server are present in IMAGE_FEATURES, then OpenSSH will take precedence and Dropbear will not be installed.
  • tools-debug: Installs debugging tools such as strace and gdb. For information on GDB, see the "Debugging With the GNU Project Debugger (GDB) Remotely" section in the Yocto Project Development Manual. For information on tracing and profiling, see the Yocto Project Profiling and Tracing Manual.
  • tools-profile: Installs profiling tools such as oprofile, exmap, and LTTng. For general information on user-space tools, see the "User-Space Tools" section in the Yocto Project Application Developer's Guide.
  • tools-sdk: Installs a full SDK that runs on the device.
  • tools-testapps: Installs device testing tools (e.g. touchscreen debugging).
  • x11: Installs the X server.
  • x11-base: Installs the X server with a minimal environment.
  • x11-sato: Installs the OpenedHand Sato environment.
    參考資料:
  • https://www.yoctoproject.org/pipermail/yocto/2017-March/035377.html




2020年4月19日 星期日

ANSI C - identifiers naming rule


通常static變數/函數, 我都會以_var, _func為命名方式, 也符合C99 7.1.3標準,
7.1.3 Reserved identifiers

Each header declares or defines all identifiers listed in its associated subclause, and optionally declares or defines identifiers listed in its associated future library directions subclause and identifiers which are always reserved either for any use or for use as file scope identifiers.
-- All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use.
-- All identifiers that begin with an underscore are always reserved for use as identifiers with file scope in both the ordinary and tag name spaces.

2020年2月29日 星期六

Linux Kernel(17.2)- Common Device Tree API


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";

這章節介紹幾個API,用於存取這些常見的資料型態,會以下的DTS的內容進行parse
/ {
  node1 {
    compatible = "brook,dts-test";
    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 {
    compatible = "brook,dts-test";
    an-empty-property;
    a-cell-property = <1 2 3 4>; /* each number (cell) is a uint32 */
    child-node@1 {
    };
    child-node@2 {
    };
  };
};


  • of_match_device(): Sanity check for device that device is matching with the node
  • of_property_read_string(): To read string property
  • of_property_count_strings(): Find and return the number of strings from a multiple strings property.
  • of_property_read_string_index(): Read a string with index from string-list property.
  • of_find_property(): Find and return the property pointer by giving named
  • of_property_for_each_u32(): A macro to iterate the property to get all values
  • of_property_read_bool(): Returns true if the property exist false otherwise
  • of_find_node_by_name(): Find a node by its "name" property
  • for_each_child_of_node(): Traverse all child device node for current device node

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

static struct of_device_id brook_dt_id[] = {
    {
     .compatible = "brook,dts-test",
    },
    {}
};

MODULE_DEVICE_TABLE(of, brook_dt_id);

static int brook_dts_probe(struct platform_device *pdev)
{
    const struct of_device_id *of_id;
    struct device_node *node, *child;
    struct property *pp;
    int i, ret, of_cnt, len;
    const char *str;
    const __be32 *cur;
    u32 val;

    // sanity check of_match_device() allows to get the matching entry
    of_id = of_match_device(brook_dt_id, &pdev->dev);
    if (!of_id) {
        pr_err("%s: of_id is NULL\n", __func__);
        return -1;
    }

    node = pdev->dev.of_node;
    // To read string property
    ret = of_property_read_string(node, "a-string-property", &str);
    if (!ret) {
        printk("a-string-property: %s\n", str);
    } else {
        printk("no a-string-property\n");
    }

    // Find and return the number of strings from a multiple strings property.
    of_cnt = of_property_count_strings(node, "a-string-list-property");
    if (of_cnt) {
        for (i = 0; i < of_cnt; i++) {
            ret = of_property_read_string_index(node, "a-string-list-property", i, &str);
            if (!ret) {
                printk("a-string-list-property[%d]: %s\n", i, str);
            }
        }
    }

    // to find aproperty named if arg2
    pp = of_find_property(node, "a-byte-data-property", &len);
    if (pp) {
        u8 *u8p = pp->value;
        printk("a-byte-data-property: len:%d, pp->len:%d", len, pp->length);
        for (i = 0; i < len; i++) {
            printk("a-byte-data-property:[%d] = %02x\n", i, u8p[i]);
        }
    }

    // to find aproperty named if arg2
    of_property_for_each_u32(node, "a-cell-property", pp, cur, val) {
        printk("a-cell-property: %04x/%04x\n", *cur, val);
    }

    // Returns true if the property exist false otherwise
    if (of_property_read_bool(node, "an-empty-property")) {
        printk("has an-empty-property\n");
    } else {
        printk("no an-empty-property\n");
    }

    // Traverse all child device node for current device node
    for_each_child_of_node(node, child) {
        printk("child name: %s\n", child->name);
    }

    // Find a node by its "name" property
    child = of_find_node_by_name(node, "child-node1");
    if (child) {
        printk("%s has a child %s\n", node->name, child->name);
    }

    return 0;
}

static int brook_dts_remove(struct platform_device *pdev)
{
    return 0;
}

static struct platform_driver brook_dts_platform_driver = {
    .probe = brook_dts_probe,
    .remove = brook_dts_remove,
    .driver = {
        .owner = THIS_MODULE,
        .name = "brook-dts",
        .of_match_table = brook_dt_id,
    },
};


static int __init brook_init_module(void)
{
    return platform_driver_register(&brook_dts_platform_driver);
}

static void __exit brook_exit_module(void)
{
    platform_driver_unregister(&brook_dts_platform_driver);
}

module_init(brook_init_module);
module_exit(brook_exit_module);
MODULE_LICENSE("GPL");

執行結果如下:
/ # insmod dts.ko
a-string-property: A string
a-string-list-property[0]: first string
a-string-list-property[1]: second string
a-byte-data-property: len:4, pp->len:4
a-byte-data-property:[0] = 01
a-byte-data-property:[1] = 23
a-byte-data-property:[2] = 34
a-byte-data-property:[3] = 56
no an-empty-property
child name: child-node1
child name: child-node2
node1 has a child child-node1
no a-string-property
a-cell-property: 1000000/0001
a-cell-property: 2000000/0002
a-cell-property: 3000000/0003
a-cell-property: 4000000/0004
has an-empty-property
child name: child-node
child name: child-node


以下摘錄http://www.myexception.cn/linux-unix/1910031.html, linux下devicetree中常用的of函數
linux下devicetree中常用的of函数
从device_node中获取信息:

int of_property_read_u8_array(const struct device_node *np, const char *propname,u8 *out_values, size_t sz);

int of_property_read_u16_array(const struct device_node *np, const char *propname,u16 *out_values, size_t sz);

int of_property_read_u32_array(const struct device_node *np, const char *propname,u32 *out_values, size_t sz);

从设备结点np中读取属性名为propname,类型为8、16、32、位整型数组的属性值,并放入out_values,sz指明了要读取的个数。


static inline int of_property_read_u8(const struct device_node *np,const char *propname,u8 *out_value) 

static inline int of_property_read_u16(const struct device_node *np,const char *propname,u8 *out_value) 

static inline int of_property_read_u32(const struct device_node *np,const char *propname,u8 *out_value) 

从设备结点np中读取属性名为propname,类型为8、16、32位的属性值,并放入out_values。实际上这里调用的就是sz为1的XXX_array函数。

 

int of_property_read_u32_index(const struct device_node *np,const char*propname,u32 index, u32 *out_value)

从设备结点np中读取属性名为propname的属性值中第index个u32数值给out_value

 

int of_property_read_u64(conststruct device_node *np, const char *propname,u64 *out_value)

从设备结点np中读取属性名为propname,类型为64位的属性值,并放入out_values

 

int of_property_read_string(struct device_node *np, const char *propname,const char**out_string)

从设备结点np中读取属性名为propname的字符串型属性值

 

int of_property_read_string_index(struct device_node *np, const char *propname,intindex, const char **output)

从设备结点np中读取属性名为propname的字符串型属性值数组中的第index个字符串

 

int of_property_count_strings(struct device_node *np, const char *propname)

从设备结点np中读取属性名为propname的字符串型属性值的个数

 

unsigned int irq_of_parse_and_map(struct device_node *dev, int index)

从设备节点dev中读取第index个irq号

 

int of_irq_to_resource(struct device_node *dev, int index, struct resource *r)

从设备节点dev中读取第index个irq号,并填充一个irq资源结构体

 

int of_irq_count(struct device_node *dev)

获取设备节点dev的irq个数


static inline bool of_property_read_bool(const struct device_node *np,const char *propname);

如果设备结点np含有propname属性,则返回true,否则返回false。一般用于检查空属性是否存在。

 

struct property* of_find_property(const struct device_node *np,const char *name,int *lenp)

根据name参数,在指定的设备结点np中查找匹配的property,并返回这个property

 

const void * of_get_property(const struct device_node *np, const char *name,int *lenp)

根据name参数,在指定的设备结点np中查找匹配的property,并返回这个property的属性值


struct device_node* of_get_parent(const struct device_node *node)

获得node节点的父节点的device node


int of_device_is_compatible(const struct device_node *device,const char *compat);

判断设备结点device的compatible属性是否包含compat指定的字符串


从of_allnodes中查找信息:

struct device_node* of_find_node_by_path(const char *path)
根据路径参数,在全局链表of_allnodes中,查找匹配的device_node


struct device_node* of_find_node_by_name(struct device_node *from,const char *name)
则根据name在全局链表of_allnodes中查找匹配的device_node,若from=NULL表示从头开始查找


struct device_node* of_find_node_by_type(struct device_node *from,const char *type)

根据设备类型在全局链表of_allnodes中查找匹配的device_node


struct device_node * of_find_compatible_node(struct device_node *from, const char*type, const char,*compatible);

根据compatible的属性值在全局链表of_allnodes中查找匹配的device_node,大多数情况下,from、type为NULL。

 

struct device_node* of_find_node_with_property(struct device_node *from,const char *prop_name)

根据节点属性的name在全局链表of_allnodes中查找匹配的device_node

 

struct device_node* of_find_node_by_phandle(phandle handle)

根据phandle在全局链表of_allnodes中查找匹配的device_node

 

杂:

void __iomem* of_iomap(struct device_node *node, int index);

通过设备结点直接进行设备内存区间的 ioremap(),index是内存段的索引。若设备结点的reg属性有多段,可通过index标示要ioremap的是哪一段,只有1段的情况,index为0

 

unsigned long __init of_get_flat_dt_root(void)

用来查找在dtb中的根节点,好像返回的都是0


int of_alias_get_id(struct device_node *np, const char *stem)

获取节点np对应的aliasid号

 

struct device_node* of_node_get(struct device_node *node)

void of_node_put(struct device_node *node)

device node计数增加/减少


const struct of_device_id* of_match_node(const struct of_device_id *matches,const struct device_node*node)

将matches数组中of_device_id结构的name和type与device node的compatible和type匹配,返回匹配度最高的of_device_id结构


platform_device和resource相关:

int of_address_to_resource(struct device_node *dev, int index,struct resource *r)

根据设备节点dev的reg属性值,填充资源结构体r。Index参数指明了使用reg属性中第几个属性值,一般设置为0,表示第一个。


struct platform_device* of_device_alloc(struct device_node *np,const char *bus_id,struct device *parent)

根据device node,bus_id以及父节点创建该设备的platform_device结构,同时会初始化它的resource成员。

 

int of_platform_bus_probe(struct device_node *root,const struct of_device_id *matches,struct device *parent)

遍历of_allnodes中的节点挂接到of_platform_bus_type总线上,由于此时of_platform_bus_type总线上还没有驱动,所以此时不进行匹配

 

int of_platform_populate(struct device_node *root,const struct of_device_id *matches,const struct of_dev_auxdata *lookup,struct device *parent)

遍历of_allnodes中的所有节点,生成并初始化所以节点的platform_device结构



struct platform_device* of_find_device_by_node(struct device_node *np)

根据device_node查找返回该设备对应的platform_device结构

    參考資料:
  • http://www.myexception.cn/linux-unix/1910031.html, linux下devicetree中常用的of函數
  • https://saurabhsengarblog.wordpress.com/2015/11/28/device-tree-tutorial-arm/, Device Tree Tutorial (ARM)
  • https://www.cnblogs.com/xiaojiang1025/p/6368260.html, Linux内核 设备树操作常用API





2020年2月28日 星期五

Linux Kernel(16.2)- PHY Abstraction Layer I


Purpose

多數的network devices都是透過MAC去存取PHY上的register,而network device driver就是利用這些register去決定如何配置該網路裝置, PHY的register都是遵守相同的標準,因而建立PHY Abstraction Layer的目的就是減少network device driver的loading,因為PHY的driver在此了, 三個目的如下:
  • 1) Increase code-reuse
  • 2) Increase overall code-maintainability
  • 3) Speed development time for new network drivers, and for new systems


The MDIO bus

多數的network devices都是透過所謂的management bus與PHY溝通,不同的network devices會使用不同的bus, 這些bus要被註冊到對應的network devices上,這些bus的interface必須遵守以下規則
  1. read and write function must be implemented.
    int write(struct mii_bus *bus, int mii_id, int regnum, u16 value);
    int read(struct mii_bus *bus, int mii_id, int regnum);
    mii_id是PHY在該bus上的ID
  2. reset function可有可無
  3. probe function是必要的,用以設定PHY driver所需的任何東西,
可以參考drivers/net/ethernet/freescale/fsl_pq_mdio.c為例的mdio bus driver,以下為我摘要的片段
static struct platform_driver fsl_pq_mdio_driver = {
    .driver = {
        .name = "fsl-pq_mdio",
        .of_match_table = fsl_pq_mdio_match,
    },
    .probe = fsl_pq_mdio_probe,
    .remove = fsl_pq_mdio_remove,
};

static int fsl_pq_mdio_probe(struct platform_device *pdev)
{
    struct device_node *np = pdev->dev.of_node;
    struct mii_bus *new_bus;

    new_bus = mdiobus_alloc_size(sizeof(*priv));
    if (!new_bus)
        return -ENOMEM;

    priv = new_bus->priv;

    new_bus->name = "Freescale PowerQUICC MII Bus",
    new_bus->read = &fsl_pq_mdio_read;
    new_bus->write = &fsl_pq_mdio_write;
    new_bus->reset = &fsl_pq_mdio_reset;

    new_bus->parent = &pdev->dev;
    platform_set_drvdata(pdev, new_bus);
    err = of_mdiobus_register(new_bus, np);

    return 0;
}

/*
 * Read the bus for PHY at addr mii_id, register regnum, and return the value.
 * Clears miimcom first.
 *
 * All PHY operation done on the bus attached to the local interface, which
 * may be different from the generic mdio bus.  This is helpful in programming
 * interfaces like the TBI which, in turn, control interfaces like on-chip
 * SERDES and are always tied to the local mdio pins, which may not be the
 * same as system mdio bus, used for controlling the external PHYs, for eg.
 */
static int fsl_pq_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
{
    struct fsl_pq_mdio_priv *priv = bus->priv;
    struct fsl_pq_mii __iomem *regs = priv->regs;
    unsigned int timeout;
    u16 value;

    /* Set the PHY address and the register address we want to read */
    iowrite32be((mii_id << 8) | regnum, ®s->miimadd);

    /* Clear miimcom, and then initiate a read */
    iowrite32be(0, ®s->miimcom);
    iowrite32be(MII_READ_COMMAND, ®s->miimcom);

    /* Wait for the transaction to finish, normally less than 100us */
    timeout = MII_TIMEOUT;
    while ((ioread32be(®s->miimind) &
           (MIIMIND_NOTVALID | MIIMIND_BUSY)) && timeout) {
        cpu_relax();
        timeout--;
    }

    if (!timeout)
        return -ETIMEDOUT;


    /* Grab the value of the register from miimstat */
    value = ioread32be(®s->miimstat);

    dev_dbg(&bus->dev, "read %04x from address %x/%x\n", value, mii_id, regnum);
    return value;
}

/*
 * Write value to the PHY at mii_id at register regnum, on the bus attached
 * to the local interface, which may be different from the generic mdio bus
 * (tied to a single interface), waiting until the write is done before
 * returning. This is helpful in programming interfaces like the TBI which
 * control interfaces like onchip SERDES and are always tied to the local
 * mdio pins, which may not be the same as system mdio bus, used for
 * controlling the external PHYs, for example.
 */
static int fsl_pq_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
        u16 value)
{
    struct fsl_pq_mdio_priv *priv = bus->priv;
    struct fsl_pq_mii __iomem *regs = priv->regs;
    unsigned int timeout;

    /* Set the PHY address and the register address we want to write */
    iowrite32be((mii_id << 8) | regnum, ®s->miimadd);

    /* Write out the value we want */
    iowrite32be(value, ®s->miimcon);

    /* Wait for the transaction to finish */
    timeout = MII_TIMEOUT;
    while ((ioread32be(®s->miimind) & MIIMIND_BUSY) && timeout) {
        cpu_relax();
        timeout--;
    }

    return timeout ? 0 : -ETIMEDOUT;
}

/* Reset the MIIM registers, and wait for the bus to free */
static int fsl_pq_mdio_reset(struct mii_bus *bus)
{
    struct fsl_pq_mdio_priv *priv = bus->priv;
    struct fsl_pq_mii __iomem *regs = priv->regs;
    unsigned int timeout;

    mutex_lock(&bus->mdio_lock);

    /* Reset the management interface */
    iowrite32be(MIIMCFG_RESET, ®s->miimcfg);

    /* Setup the MII Mgmt clock speed */
    iowrite32be(MIIMCFG_INIT_VALUE, ®s->miimcfg);

    /* Wait until the bus is free */
    timeout = MII_TIMEOUT;
    while ((ioread32be(®s->miimind) & MIIMIND_BUSY) && timeout) {
        cpu_relax();
        timeout--;
    }

    mutex_unlock(&bus->mdio_lock);

    if (!timeout) {
        dev_err(&bus->dev, "timeout waiting for MII bus\n");
        return -EBUSY;
    }

    return 0;
}


(RG)MII/electrical interface considerations

Due to this design decision, a 1.5ns to 2ns delay must be added between the clock line (RXC or TXC) and the data lines to let the PHY (clock sink) have enough setup and hold times to sample the data lines correctly.



我也摘錄一段Qualcomm的EMAC probe部分,
https://github.com/mauronofrio/android_kernel_nubia_msm8953/blob/master/drivers/net/ethernet/qualcomm/emac/emac_phy.c
int emac_phy_config_external(struct platform_device *pdev,
                 struct emac_adapter *adpt)
{
    struct device_node *np = pdev->dev.of_node;
    struct mii_bus *mii_bus;
    int ret;

    /* Create the mii_bus object for talking to the MDIO bus */
    mii_bus = devm_mdiobus_alloc(&pdev->dev);
    adpt->mii_bus = mii_bus;

    if (!mii_bus)
        return -ENOMEM;

    mii_bus->name = "emac-mdio";
    snprintf(mii_bus->id, MII_BUS_ID_SIZE, "%s", pdev->name);
    mii_bus->read = emac_mdio_read;
    mii_bus->write = emac_mdio_write;
    mii_bus->parent = &pdev->dev;
    mii_bus->priv = adpt;

    if (ACPI_COMPANION(&pdev->dev)) {
        u32 phy_addr;

        ret = mdiobus_register(mii_bus);
        if (ret) {
            emac_err(adpt, "could not register mdio bus\n");
            return ret;
        }
        ret = device_property_read_u32(&pdev->dev, "phy-channel",
                           &phy_addr);
        if (ret)
            /* If we can't read a valid phy address, then assume
             * that there is only one phy on this mdio bus.
             */
            adpt->phydev = phy_find_first(mii_bus);
        else
            adpt->phydev = mii_bus->phy_map[phy_addr];
    } else {
        struct device_node *phy_np;

        ret = of_mdiobus_register(mii_bus, np);

        if (ret) {
            emac_err(adpt, "could not register mdio bus\n");
            return ret;
        }

        phy_np = of_parse_phandle(np, "phy-handle", 0);
        adpt->phydev = of_phy_find_device(phy_np);
        of_node_put(phy_np);
    }

    if (!adpt->phydev) {
        emac_err(adpt, "could not find external phy\n");
        mdiobus_unregister(mii_bus);
        return -ENODEV;
    }

    if (!adpt->phydev->phy_id) {
        emac_err(adpt, "External phy is not up\n");
        mdiobus_unregister(mii_bus);
        return -EPROBE_DEFER;
    }

    if (adpt->phydev->drv) {
        emac_dbg(adpt, probe, "attached PHY driver [%s] ",
             adpt->phydev->drv->name);
        emac_dbg(adpt, probe, "(mii_bus:phy_addr=%s, irq=%d)\n",
             dev_name(&adpt->phydev->dev), adpt->phydev->irq);
    }

    /* Set initial link status to false */
    adpt->phydev->link = 0;
    return 0;
}

/**
 * of_mdiobus_register - Register mii_bus and create PHYs from the device tree
 * @mdio: pointer to mii_bus structure
 * @np: pointer to device_node of MDIO bus.
 *
 * This function registers the mii_bus structure and registers a phy_device
 * for each child node of @np.
 */
int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
{
    struct device_node *child;
    const __be32 *paddr;
    bool scanphys = false;
    int addr, rc, i;

    /* Mask out all PHYs from auto probing.  Instead the PHYs listed in
     * the device tree are populated after the bus has been registered */
    mdio->phy_mask = ~0;

    /* Clear all the IRQ properties */
    if (mdio->irq)
        for (i=0; iirq[i] = PHY_POLL;

    mdio->dev.of_node = np;

    /* Register the MDIO bus */
    rc = mdiobus_register(mdio);
    if (rc)
        return rc;


    /* Loop over the child nodes and register a phy_device for each one */
    for_each_available_child_of_node(np, child) {
        addr = of_mdio_parse_addr(&mdio->dev, child);
        if (addr < 0) {
            scanphys = true;
            continue;
        }

        rc = of_mdiobus_register_phy(mdio, child, addr);
        if (rc)
            continue;
    }

    if (!scanphys)
        return 0;

    /* auto scan for PHYs with empty reg property */
    for_each_available_child_of_node(np, child) {
        /* Skip PHYs with reg property set */
        paddr = of_get_property(child, "reg", NULL);
        if (paddr)
            continue;

        for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
            /* skip already registered PHYs */
            if (mdio->phy_map[addr])
                continue;

            /* be noisy to encourage people to set reg property */
            dev_info(&mdio->dev, "scan phy %s at address %i\n",
                 child->name, addr);

            rc = of_mdiobus_register_phy(mdio, child, addr);
            if (rc)
                continue;
        }
    }

    return 0;
}



這兩個sample code都是簡略的顯示management bus的部分,主要展示mdio bus device driver的基本寫法,填寫name、read()、write()、reset()後使用of_mdiobus_register()註冊,

    參考資料:
  • kernel/msm-4.14/Documentation/networking/phy.txt



2020年2月9日 星期日

busybox init flow


Linux在啟動kernel後,便由kernel載入init程式,由init程式完成餘下的啟動過程,簡略片段如下
Kernel 4.14
start_kernel()
  |--> rest_init()
    |--> pid = kernel_thread(kernel_init, NULL, CLONE_FS);
      |--> kernel_init()
       if (!try_to_run_init_process("/sbin/init") ||
          !try_to_run_init_process("/etc/init") ||
          !try_to_run_init_process("/bin/init") ||
          !try_to_run_init_process("/bin/sh"))
          return 0;
       panic("No working init found.  Try passing init= option to kernel. "
            "See Linux Documentation/admin-guide/init.rst for guidance.");

接著就會交由init開始一連串的init過程,busybox的init會去讀取/etc/inittab設定來進行init,如果沒有/etc/inittab,會執行以下預設的inittab內容
::sysinit:/etc/init.d/rcS
::askfirst:/bin/sh
::ctrlaltdel:/sbin/reboot
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r
::restart:/sbin/init
tty2::askfirst:/bin/sh
tty3::askfirst:/bin/sh
tty4::askfirst:/bin/sh

其格式為<id>:<runlevels>:<action>:<process>,說明如下
<id>: 與傳統init意思不同, 會把stdin/stdout設為/dev/<id>

<runlevels>: busybox會ignore該欄位

<action>: 有效的action包含sysinit, wait, once, respawn, askfirst, shutdown, restart and ctrlaltdel.
  "sysinit" 第一個被執行的action, init會等待"sysinit"執行完成後, 再執行wait與once
  "askfirst"/"respawn" 接著被執行, 但是askfirst執行前會先印"Please press Enter to activate this console". 

<process>: 要執行的命令

inittab的action執行code flow如下
# busybox v1.32

static pid_t run(const struct init_action *a)
{
  ...
  if (BB_MMU && (a->action_type & ASKFIRST)) {
    static const char press_enter[] ALIGN1 =
#ifdef CUSTOMIZED_BANNER
#include CUSTOMIZED_BANNER
#endif
        "\nPlease press Enter to activate this console. ";
    full_write(STDOUT_FILENO, press_enter, sizeof(press_enter) - 1);
    ...
  }
  ...
}

static void run_actions(int action_type)
{
  struct init_action *a;

  for (a = G.init_action_list; a; a = a->next) {
    if (!(a->action_type & action_type))
      continue;

    if (a->action_type & (SYSINIT | WAIT | ONCE | CTRLALTDEL | SHUTDOWN)) {
        pid_t pid = run(a);
      if (a->action_type & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN))
          waitfor(pid);
    }
    if (a->action_type & (RESPAWN | ASKFIRST)) {
      /* Only run stuff with pid == 0. If pid != 0,
       * it is already running
       */
      if (a->pid == 0)
        a->pid = run(a);
    }
  }
}

int init_main(int argc UNUSED_PARAM, char **argv)
{
  ...
  /* Check if we are supposed to be in single user mode */
  if (argv[1]
   && (strcmp(argv[1], "single") == 0 || strcmp(argv[1], "-s") == 0 || LONE_CHAR(argv[1], '1'))
  ) {
    /* ??? shouldn't we set RUNLEVEL="b" here? */
    /* Start a shell on console */
    new_init_action(RESPAWN, bb_default_login_shell, "");
  } else {
    /* Not in single user mode - see what inittab says */

    /* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined,
    * then parse_inittab() simply adds in some default
    * actions (i.e., INIT_SCRIPT and a pair
    * of "askfirst" shells) */
    parse_inittab();
  }

  ...
  /* Now run everything that needs to be run */
  /* First run the sysinit command */
  run_actions(SYSINIT);
  check_delayed_sigs(&G.zero_ts);
  /* Next run anything that wants to block */
  run_actions(WAIT);
  check_delayed_sigs(&G.zero_ts);
  /* Next run anything to be run only once */
  run_actions(ONCE);
  ...
  while (1) {
    ...
    /* (Re)run the respawn/askfirst stuff */
    run_actions(RESPAWN | ASKFIRST);
    ...
  }
  ...
}


如果要進入signle user mode,就需要傳遞"single"或"-S"給init當參數,要把字串放在kernel parameters "--" 之後
The kernel parses parameters from the kernel command line up to “–”; 
if it doesn’t recognize a parameter and it doesn’t contain a ‘.’, 
the parameter gets passed to init: parameters with ‘=’ go into init’s environment, 
others are passed as command line arguments to init.
Everything after “–” is passed as an argument to init.

如:
qemu-system-arm -M vexpress-a9 -m 512M -kernel ./linux/arch/arm/boot/zImage -dtb \
./linux/arch/arm/boot/dts/vexpress-v2p-ca9.dtb -initrd ./initrd-arm.img \
-nographic -append "console=ttyAMA0 -- single"

busybox並不支援runlevel
    參考資料:
  • https://en.wikipedia.org/wiki/Init, init
  • https://en.wikipedia.org/wiki/Runlevel, runlevel
  • https://git.busybox.net/busybox/tree/examples/inittab, busybox example for inittab
  • https://www.itread01.com/p/1350587.html, kernel 啟動流程之 【裝置驅動載入】 學習筆記
  • https://www.itread01.com/content/1543246633.html, busybox(一)淺析

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




熱門文章