天天看點

38 全志平台的script.bin在linux核心裡的應用分析

在全志平台裡,script.bin用于指定SOC控制器和GPIO等相關的配置. script.bin是由script.fex用工具轉換得來.

在uboot啟動系統時, uboot的環境變量bootcmd為:

也就是說script.bin在核心啟動前需要放在記憶體位址0x43000000.

在核心源碼”arch/arm/mach-sunxi/sys_config.c”裡對script.bin進行處理.

int __init script_init(void)
 {
     int     i, j, count;
 
     script_origin_head_t        *script_hdr = __va(SYS_CONFIG_MEMBASE); //SYS_CONFIG_MEMBASE就是記憶體位址
 
     script_origin_main_key_t    *origin_main;
     script_origin_sub_key_t     *origin_sub;
 
     script_main_key_t           *main_key;
     script_sub_key_t            *sub_key, *tmp_sub, swap_sub;
    ...
           

script_init函數是在核心啟動時,在源碼”arch/arm/kernel/setup.c”調用的:

#ifdef CONFIG_ARCH_SUNXI
     /* init sys_config script parse */
     script_init();
 #endif
           

//

通過閱讀sys_config.c裡的源碼可得知,script.bin其實也是一個文本檔案而已,裡面的内容是按結構體類型進行存放.

在script.bin檔案裡:

先存放檔案頭, 接着存放所有的主鍵項, 每個主鍵項的子鍵又集中存放在一塊區域裡, 每個子鍵項的内容又存放在另一個地方.

用到的類型:

#pragma pack(1)

typedef struct
{
    char name[]; //名字
    int  offset; //以4位元組為機關, 此子鍵的值與頭位址的偏移.如果此鍵是關于gpio, 子鍵的值的位址還需減32位元組
    struct {
        u32 cnt : ;  // 以4位元組為機關,表示子鍵的值的長度
        u32 type: ;  //type of sub key, int(1) / string(2) / gpio(4/5)
    }pattern;
} script_origin_sub_key_t; //子鍵的類型


typedef struct
{
    char name[]; //主鍵的名字
    int  sub_cnt; //在此主鍵下有多少個子鍵
    int  offset; // 以4位元組為機關,表示此主鍵的子鍵開始内容與script.bin的開始位址的偏移 
} script_origin_main_key_t; //主鍵的類型


typedef struct
{
    unsigned int main_cnt; //主鍵的個數
    unsigned int length; //值為0, 不明
    unsigned int version[]; //版本
    script_origin_main_key_t    main_key; //第一個主鍵,主鍵就是在script.fex裡如:[product]
} script_origin_head_t;  //script.bin的檔案頭 


// sprite_gpio0 = port:PL10<1><default><default><default>
typedef struct {
    char    gpio_name[]; // 沒用
    int     port; // PL
    int     port_num; // 10
    int     mul_sel; // 1
    int     pull; 
    int     drv_level;
    int     data;
} script_origin_gpio_t;  //當子鍵是一個關于IO口的項時,它的值就是這種類型


#pragma pack()
           

/

script.bin裡的檔案排列:

檔案頭

[主鍵結構體]
[主鍵結構體]
[主鍵結構體]
[主鍵結構體]
...

[子鍵結構體]
[子鍵結構體]
[子鍵結構體]
[子鍵結構體]
...

[子鍵的值]
[子鍵的值]
[子鍵的值]
[子鍵的值]
           

在主鍵結構體裡都有offset成員來記錄它的第1個子鍵與檔案頭的偏移, 在子鍵結構體由成員offset成員記錄它的值與檔案頭偏移的位置.

檔案頭裡有記錄主鍵的個數,每個主鍵結構體裡由成員sub_cnt記錄屬于此主鍵有多少個子鍵.

擷取script.bin裡所有主鍵,子鍵及其值的測試代碼:

app.c

int main(void)
{
    int fd;
    script_origin_head_t *head;

    fd = open("./script.bin", O_RDONLY);
    if (fd < )
    {
        perror("open script.bin");
        return ;
    }

    struct stat sbuf;
    fstat(fd, &sbuf);
    head = mmap(NULL, sbuf.st_size, PROT_READ, MAP_SHARED, fd, );
    if (MAP_FAILED == head)
    {
        perror("mmap");
        return ;
    } 

    ////////////////
    script_origin_main_key_t *main = &head->main_key;
    script_origin_sub_key_t  *sub;
    script_origin_gpio_t *gpio;
    int i, j;

    for (i = ; i < head->main_cnt; i++)
    {
        printf("[%s]:\n", main[i].name);    
        sub = (char *)head + main[i].offset * 4; //sub指針指向每個主鍵的第1個子鍵結構體
        for (j = 0; j < main[i].sub_cnt; j++)
        {
            printf("%s = ", sub[j].name);
            if (sub[j].pattern.type >= 4) //子鍵是關于gpio的
            {
                gpio = (char *)head + sub[j].offset * 4-32;
                printf("%d, %d, %d, %d, %d\n", gpio->port, gpio->port_num, gpio->mul_sel, gpio->pull, gpio->drv_level);
            }
            if ( == sub[j].pattern.type) //子鍵的值是int
                printf("%d\n", *(int *)((char *)head+sub[j].offset*4));
            if ( == sub[j].pattern.type) //子鍵的值是字元串
                printf("%s\n", (char *)head+sub[j].offset*4);

        }
        printf("\n");
    }   

    ///////////////
    munmap(head, sbuf.st_size);
    close(fd);
    return 0;
}
           

在linux核心源碼”sys_config.c”裡, 周遊script.bin的内容處理後,用哈希表存放起來.

struct gpio_config {
    u32 gpio;       /* gpio global index, must be unique */
    u32     mul_sel;    /* multi sel val: 0 - input, 1 - output... */
    u32     pull;       /* pull val: 0 - pull up/down disable, 1 - pull up... */
    u32     drv_level;  /* driver level val: 0 - level 0, 1 - level 1... */
    u32 data;       /* data val: 0 - low, 1 - high, only vaild when mul_sel is input/output */
};

typedef union {
    int                 val;
    char                *str;
    struct gpio_config  gpio;
} script_item_u; //每個子鍵的值隻會是三種類型中的一種. int,  string, gpio

typedef struct {
    char                        name[SCRIPT_NAME_SIZE_MAX]; //子鍵名
    script_item_u               *value;  //子鍵的值
    script_item_value_type_e    type;   //子鍵值的類型
    int                         hash;  //子鍵的哈希值
    void                        *next;  
} script_sub_key_t; //用于存放處理過後的子鍵的項,挂載到主鍵哈希表的元素裡。每個元素就是一個主鍵.

typedef struct {
    char                name[SCRIPT_NAME_SIZE_MAX];
    script_sub_key_t    *subkey;
    script_item_u       *subkey_val;
    script_item_u       *gpio;
    int                 gpio_cnt;
    int                 hash;
    void                *next;
} script_main_key_t; //用于存放處理過後的主鍵的哈希表

static script_main_key_t   *g_script; //哈希表, 在驅動代碼就可以通過哈希表查找相應的主鍵或子鍵的值
           

//

同時, “sys_config.c”裡也提供了導出函數,供裝置驅動代碼裡擷取script.bin裡相應項的配置值.

script_item_value_type_e
script_get_item(char *main_key, char *sub_key, script_item_u *item)
{
    ...
}
EXPORT_SYMBOL(script_get_item);

//用法:  script_get_item("target", "boot_clock", &item);

int script_get_pio_list(char *main_key, script_item_u **list) //用于擷取一個主鍵下所有關于gpio項的值
{
    ...
}
EXPORT_SYMBOL(script_get_pio_list);

//用法: script_get_pio_list("gpio_para", &list);

           

///

在"sys_config.c"裡還提供了使用者調用的接口:

)   //讀取一個主鍵下所有子鍵的接口
    echo "gpio_para" > /sys/class/script/dump
    cat /sys/class/script/dump


)  //指定讀取一個主鍵下的一個子鍵的值(字元串有亂碼)
    echo "spi_board0 modalias" > /sys/class/script/get_item
    cat /sys/class/scripit/get_item