2016年10月6日 星期四

[Embedded] 取出背景行程的 STDOUT 訊息

在 linux 系統,若需要觀察系統中某行程的輸出訊息 (stdout, stderr),常見的做法是在執行檔案時直接將輸出導入至某個檔案。如下:
1. 分別導出 stdout 與 stderr
executable_file > stdout.txt 2> stderr.txt
2. 將 stdout 與 stderr 導出至同一檔案
executable > output.txt 2>&1
如果是目前已經在執行的行程,要如何在不重啟的情況之下,將 stdout, stderr 轉導向其他檔案呢?比方說某個正在執行的 CGI 程式,如何知道其輸出訊息?

2016年9月13日 星期二

ONVIF -- Profile C and Profile A

最近 ONVIF發布了 Profile A,看來與 Profile C 是很類似的東西,以Profile C為例,定義了兩種角色,Camera或 NVR 可以扮演 ONVIF Client 的角色來控制門禁系統。

  • ONVIF Device,需支援兩項功能。 
  • provide information regarding the PACS related entities included in the system as well as the status of these entities, including standardized PACS related events. 
  • provides basic door control 
  • ONVIF Client,需支援三項功能。 
  • get information regarding the PACS related entities. 
  • do basic door control. 
  • retrieve and receive standardized PACS related events.

    下面將針對這兩種Profile作一比較。

    Prfoile A 與 Profile C 的相同部分

    1. 若一個設備屬於 Physical Access Control System (PACS) 的一部分,便可宣稱支援 ONVIF Profile C 或 Profile A
    要宣稱支援 ONVIF Profile C,要回應下列 scope
     • onvif://www.onvif.org/Profile/C 
    要宣稱支援 ONVIF Profile A,要回應下列 scope
     • onvif://www.onvif.org/Profile/A

    2. 若要支援 Profile C/A,需遵守下列規定
    • 需遵循 ONVIF Core Specification v2.3 或是更新的規範。
    • 需支援 HTTP digest authentication
    • 需支援 pull points 的方式取得 Event, 並且最少要有兩個 pull points
    • 需支援 GetWsdlUrl

    Prfoile A 與 Profile C 的不同

    1. 應用領域不同
    Profile C 官方定義  
    Device
    provide information regarding the PACS related entities included in the system as well as the status of these entities, including standardized PACS related events. This device also provides basic door control
    Client
    get information regarding the PACS related entities and do basic door control.
    It can also retrieve and receive standardized PACS related events.
    Profile A 官方定義 
    Device
    provide functionality to retrieve information, status and
    events and to configure the PACS related entities such as access rules, credentials and schedules. 
    Client
    provide configurations of access rules,credentials and schedules.
    The client can also retrieve and receive standardized PACS related events.
    2. ProfileA 涵蓋了以下規範
    • ONVIF Network Interface Specification
    • Access Rules Service Specification
    • Credential Service Specification
    • Schedule Service Specification
    個人認知:若只要簡單控制門禁,只要實作Profile C,若需要較複雜的控制邏輯,則需要同時實作Profile C 與 Profile A。

    參考資料:
    1. 2013_12_ONVIF_Profile_C_Specification_v1-0.pdf
    2. ONVIF_Profile_A_Spec_RCv1-1.pdf

    2016年9月12日 星期一

    ONVIF -- media2 簡介

    2016/06 ONVIF 發布 Media2 規格書,我將值得注意的地方作一整理,如下:

    1. VideoEncoderConfiguration 內的 Encoding 已可支援 H265
    原本作法(MEDIA) 直接定義 enum { 'JPEG', 'MPEG4', 'H264' }
    新作法(MEDIA2) 改為採用 IANA 定義的字串,例如: "video/H265",如此支援的媒體類型就更廣了。詳細列表可參考 http://www.iana.org/assignments/media-types/media-types.xhtml
       
    2. GetProfile 修改
    原本作法會回 VideoAnalyticsConfiguration
    新作法只會回 Analytics(只有Token,Name, 沒有 Configuration

    3. Set synchronization point
    可強制 Camera 作出三種行為
    a. 立刻送出 iFrame,
    b. 提供使用者訂閱Event的目前狀態。
    c. 提供 PTZ status

    4. VideoRateControl2 新增 ConstantBitRate
    原本的 RateControl 支援下列三項設定
    • FrameRateLimit
    • EncodingInterval
    • BitrateLimit
    現在新增 ConstantBitRate,如此便能夠與 Camera內部設定有一致的對應關係。

    5. MetadataConfiguration 支援 CompressionType.
    為了節省空間,Camera 回傳的 Metadata 除了原本的 XML 格式,還可使用
    • GZIP 或
    • EXI(Efficient XML Interchange Format)       

    6. 新增 SetVideoSourceMode 與 GetVideoSourceModes
    可用來切換 NTSC/PAL ,或是切換 FOV(4:3/16:9) 。
       
    7. 新增 GetVideoEncoderInstances
    可得知每個 VideoSource 最多可同時支援幾條 live stream.

    8. VideoSourceConfigurationExtension2 支援 LensDescription
    使用者可利用此資訊修正鏡頭失真問題(lens distortion)

    參考資料:

    1. http://www.onvif.org/specs/srv/media/ONVIF-Media2-Service-Spec-v1606.pdf    

    2016年7月20日 星期三

    [Embedded] U-Boot的開機流程


    以 U-Boot source code 為例,整理 embedded system 的開機流程

    1.  開機流程
    CPU上電後,會到固定位置讀取指令並且執行,不同架構其位置不同,比方說 ARM 結構的 CPU 會從位址 0x00000000 開始執行,MIPS結構的 CPU 會從位址 0xBFC00000 開始,。 
    若要手動搞定一個嵌入式系統,首先要作的便是安排系統記憶體的編排方式,透過編寫 linker script,可以控制 CPU 上電後會先開始執行的指令,因而讓開機時便開始執行 U-Boot。 
    以下是一個 linker script 例子,此例中,會將 start.o 擺放在 0x00000000 的位置。系統開機時便會在從此處開始取出指令開始執行。一般作法在 start.o 會做些硬體的初始化,然後再執行 u-boot.  (此例子取自 u-boot-2013.04-rc1.tar.bz2 ,參考\board\samsung\smdk6400\u-boot-nand.ld)

    2.  U-Boot 有兩種工作模式
    (1)啟動載入(Boot loading)模式。
    上電後,Bootloader 從板子上的某個固態存放裝置上將作業系統載入到RAM中運行
    (2)下載(Downloading)模式。
    在這種模式下,開發人員可以使用各種命令,(xmodem, tftp, usb and etc...)通過串口連接或網路連接等通信手段從主機(Host)下載檔案(比如內核映射、檔案系統映射),將它們直接放在記憶體運行或是燒入Flash類固態存放裝置中。

    3. U-Boot啟動過程分為兩個階段
    a. Phase 1,
    硬體設備初始化, 準備 RAM 空間(對DDR晶片初始化), 設置 CPU 工作模式為管理模式(SVC)複製 U-Boot程式至 RAM, 啟動 MMU, 設置堆疊,  跳轉到 phase2的程式碼進入點. 
    b. Phase 2,
    初始化要使用的硬體設備, 檢測系統記憶體映象 (memory map), 指定初始化函數清單(如:cpu_init, interrupt_init),為核心設定啟動參數, 進入 U-Boot 命令列 (無窮迴圈)

    4.從 U-Boot 程式碼分析其流程

    start.s (程式碼及註解請看這裡)
    禁止 FIQ, IRQ中斷,並將 ARM CPU 設定為 SVC(Supervisor Calls) 模式 
    設定 Cache和 MMU
    若是從 nand boot,則應該已經做了硬體的Reset,因此不需清除Cache與暫停MMU。否則要 
    • 設定 Cache,此時應該先讓 ICache, DCache, TLB 都失效。
    • 設定 MMU,包含 Endian, Address Align Check, 並通知 MMU 不要使用 ICache, DCache。
    • 設定CPU內的記憶體 TCM(Tightly-coupled memory),此部分也是可分為 Instruction TCM, Data TCM
    設定週邊設備(peripheral)對應的記憶體位址 
    執行 lowlevel_init()
    此函數用來初始化板子相關設定,程式碼會放在 board 目錄下,例如:board/samsung/smdk6400/lowlevel_init.s,此函數會設定LED,WatchDog,Interrupt,System Clock,Serial Port,NAND Flash,DRAM。建置一個小的 Stack,清除BSS段。 
    執行 _main() 
    此函數用來建立C語言的執行環境,執行結束之後會返回 start.s。實作可參考 crt0.s。
    relocate code
    將 ELF 對應的各個區塊,複製到記憶體內。然後啟動MMU,致能TLB。此時還不需要 ICache, DCache,因此不用變更設定。 
    最後設定 exception handler,處理異常事件

    crt0.s (C-runtime startup Code,編譯成 Thumb code),程式碼位置在 arch/arm/lib/crt0.s,主要動作如下
    配置一小塊堆疊,用來放置 160bytes 的 Global Data,將此記憶體位置紀錄在 r8,供後續使用。 
    呼叫 board_init_f(ulong bootflag),此函數是U-Boot執行的第一個 C 函數,程式碼在arch\arm\lib\board.c,此函數會清空 Global Data,將 init_sequence[] 內定義的所有函數都執行一遍,其初始化工作可能包含 serial, baudrate, consol, system information, i2c 跟 dram,並規劃SDRAM空間配置,由位址最大處往前,分別分配 TLB, .Bss, .Data, .Text等區段,接著再分配 malloc空間,global data, abort_stack。

    如果是 non-SPL build,會繼續下列流程
    呼叫 relocate_code(addr_sp, gd, addr_moni),跳回到 start.S中,此時會將 u-boot 程式碼複製到 SDRAM中。複製成功後,執行 bx lr,跳回 crt0.S。 
    呼叫 c_runtime_cpu_setup,此時一樣會跳至 start.S,然後立刻透過 bx lr 返回 crt0.S。 
    清空BSS,控制LED閃爍。
    呼叫 board_init_r(gd_t *id, ulong dest_addr),此函數是 U-Boot 執行的第二個 C函數,也是做些初始化動作,注意的是許多外接設備都在此時才初始化,如 :Ethernet, I2Cfast, LCD, KEYBOARD, USB, SCSI, KGDB等, 最後會進入一個無窮迴圈裡,持續的執行 main_loop(), 
    main_loop()的主要作用就是 U-Boot 啟動管理,此時可能會 delay 數秒 (CONFIG_BOOTDELAY),等待使用者輸入指令。main_loop()的實作可參考 common/main.c。此函數會取得環境參數中的 bootcmd,執行 bootcmd 載入linux kernel,後續任務便由 kernel 接手。 
    可透過 bootm 命令載入 kernel,例如:"bootm 0x80700000"。
    cmd_bootm.c
    設定 kernel 的 entry point 
    取出 zImage 所儲存的 64 bytes header,判斷 magic number是否正確? 
    設定 ramdisk 
    取得 fdt (flattened device tree),設定各項周邊設備。 
    執行 kernel,函數宣告為 void (*theKernel) (int, char **, char **, int *);,此函數內容還需要再研究。

    以ARM開機為例,做個簡單總結,CPU開機時會取出 0x00000000 處的程式碼 (start.s),初始化相關設備,並準備好C語言執行環境,將 u-boot 程式 relocate 至 SDRAM,執行 u-boot 程式,初始化所有設備,最後由 u-boot 傳入各項參數,啟動 kernel。

    2016年6月18日 星期六

    [Study] SCRUM 用一半的時間,做兩倍的事

    『反應速度最快的人就能存活』,本書作者Jeff SUTHERLAND所給出的建議,我會在心裡好好的記住這句話。

    2016年6月8日 星期三

    好碼共享

    這裡會收集一些實用的程式寫法。

    1. hex2bin: 將字串 "F9" 轉成數值 0xF9 (取自 rtmpdump)
    #define HEX2BIN(a)      (((a)&0x40) ? ((a)&0xf) + 9:  ((a)&0xf)) //這個轉換方式超棒
    int hex2bin(char *str, char **hex)
    {
      char *ptr;
      int i, l = strlen(str);
      if (l & 1)
      return 0;
      *hex = malloc(l/2);
      ptr = *hex;
      if (!ptr)
        return 0;
      for (i=0; i    *ptr++ = (HEX2BIN(str[i]) << 4) | HEX2BIN(str[i+1]);
      return l/2;
    }

    2. linker script 內的 ALIGN 作法
    #define ALIGN(exp)   (. + exp - 1) & ~(exp - 1)
    // Return the result of the current location counter (.) aligned to the next exp boundary. exp must be an expression whose value is a power of two. 
    3. linux kernel.h 內的幾個 Macro
    #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
    #define container_of(ptr, type, member) ({    \
    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
    (type *)( (char *)__mptr - offsetof(type,member) );})
    #define min(x, y) ({    \
    typeof(x) _min1 = (x);    \
    typeof(y) _min2 = (y);    \
    (void) (&_min1 == &_min2);    \ /* type check*/
    _min1 < _min2 ? _min1 : _min2; }) 
    此處可參考 http://stackoverflow.com/questions/5595593/min-macro-in-kernel-h

    4. 建議配置記憶體的方法 p = kmalloc(sizeof(*p), ...); ,此作法好處是好處是萬一pointer p的型別改變時,不會因為忘記同步更該型別的名稱而出錯。

    5. TBD