編輯:關於Android編程
int console_init_r(void) { char *stdinname, *stdoutname, *stderrname; struct stdio_dev *inputdev = NULL, *outputdev = NULL, *errdev = NULL; #ifdef CONFIG_CONSOLE_MUX int iomux_err = 0; #endif /* set default handlers at first */ gd->jt->getc = serial_getc; gd->jt->tstc = serial_tstc; gd->jt->putc = serial_putc; gd->jt->puts = serial_puts; gd->jt->printf = serial_printf; /*--------------------------以上為代碼段1--------------------------------------------*/ /* stdin stdout and stderr are in environment */ /* scan for it */ stdinname = getenv("stdin"); stdoutname = getenv("stdout"); stderrname = getenv("stderr"); //setenv stdout serial,vga標准輸出被重載,如果u-boot中環境變量stdou被設定,那麼stdout就被重定位 if (OVERWRITE_CONSOLE == 0) { /* if not overwritten by config switch */ 這裡OVERWRITE_CONSOLE值為1 inputdev = search_device(DEV_FLAGS_INPUT, stdinname); outputdev = search_device(DEV_FLAGS_OUTPUT, stdoutname); errdev = search_device(DEV_FLAGS_OUTPUT, stderrname); #ifdef CONFIG_CONSOLE_MUX //如setenv stdout serial,vga iomux_err = iomux_doenv(stdin, stdinname); iomux_err += iomux_doenv(stdout, stdoutname); iomux_err += iomux_doenv(stderr, stderrname); if (!iomux_err) /* Successful, so skip all the code below. */ goto done; #endif } /*--------------------------以上為代碼段2--------------------------------------------*/ /* if the devices are overwritten or not found, use default device */ if (inputdev == NULL) { inputdev = search_device(DEV_FLAGS_INPUT, "serial"); } if (outputdev == NULL) { outputdev = search_device(DEV_FLAGS_OUTPUT, "serial"); } if (errdev == NULL) { errdev = search_device(DEV_FLAGS_OUTPUT, "serial"); } /*--------------------------以上為代碼段3--------------------------------------------*/ /* Initializes output console first */ if (outputdev != NULL) { /* need to set a console if not done above. */ console_doenv(stdout, outputdev); } if (errdev != NULL) { /* need to set a console if not done above. */ console_doenv(stderr, errdev); } if (inputdev != NULL) { /* need to set a console if not done above. */ console_doenv(stdin, inputdev); } /*--------------------------以上為代碼段4--------------------------------------------*/ #ifdef CONFIG_CONSOLE_MUX done: #endif #ifndef CONFIG_SYS_CONSOLE_INFO_QUIET /*defined*/ stdio_print_current_devices(); #endif /* CONFIG_SYS_CONSOLE_INFO_QUIET */ #ifdef CONFIG_SYS_CONSOLE_ENV_OVERWRITE /*no defined*/ /* set the environment variables (will overwrite previous env settings) */ for (i = 0; i < 3; i++) { setenv(stdio_names[i], stdio_devices[i]->name); } #endif /* CONFIG_SYS_CONSOLE_ENV_OVERWRITE */ /*defined*/ gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */ print_pre_console_buffer(PRE_CONSOLE_FLUSHPOINT2_EVERYTHING_BUT_SERIAL); /*--------------------------以上為代碼段5--------------------------------------------*/ return 0; }上述程序按其實現的功能可分為5部分,為了便於分析,我們下面僅以stdout設備為例,逐步進行討論:
gd->jt->getc = serial_getc; gd->jt->tstc = serial_tstc; ....上述的代碼段設置jt操作的默認函數為串口相關函數。關於gd->jt所包含函數的使用,我們將在後續的章節中討論。
stdinname =getenv("stdin"); stdoutname = getenv("stdout"); stderrname = getenv("stderr"); /*setenv stdout serial,vga標准輸出被重載,如果u-boot中環境變量stdou被設定,那麼stdout就被重定位*/ if (OVERWRITE_CONSOLE == 0) { /* if not overwritten by config switch */ /* OVERWRITE_CONSOLE或為宏定義,或為函數返回值,這裡為返回值1*/ inputdev = search_device(DEV_FLAGS_INPUT, stdinname); outputdev = search_device(DEV_FLAGS_OUTPUT, stdoutname); errdev = search_device(DEV_FLAGS_OUTPUT, stderrname); #ifdef CONFIG_CONSOLE_MUX //如setenv stdout serial,vga iomux_err = iomux_doenv(stdin, stdinname); iomux_err += iomux_doenv(stdout, stdoutname); iomux_err += iomux_doenv(stderr, stderrname); if (!iomux_err) /* Successful, so skip all the code below. */ goto done; #endif如我們曾在u-boot中執行命令:
stdoutname = getenv("stdout"); /*setenv stdout serial,vga標准輸出被重載,如果u-boot中環境變量stdou被設定,那麼stdout就被重定位*/ ... if (OVERWRITE_CONSOLE == 0) { /* if not overwritten by config switch */ /* OVERWRITE_CONSOLE或為宏定義,或為函數返回值,這裡為返回值1*/ #ifdef CONFIG_CONSOLE_MUX //如setenv stdout serial,vga iomux_err = iomux_doenv(stdin, stdinname); ... if (!iomux_err) /* Successful, so skip all the code below. */ goto done; #endif inputdev = search_device(DEV_FLAGS_INPUT, stdinname); ...這樣,當定義了CONFIG_CONSOLE_MUX時,如果iomux_doenv的執行沒有錯誤,那麼跳過search_device。如果有錯誤,則進一步執行search_device段代碼。
#ifdef CONFIG_CONSOLE_MUX /* This tries to preserve the old list if an error occurs. */ int iomux_doenv(const int console, const char *arg) { char *console_args, *temp, **start; int i, j, k, io_flag, cs_idx, repeat; struct stdio_dev *dev; struct stdio_dev **cons_set; console_args = strdup(arg); ... i = 0; temp = console_args; for (;;) { temp = strchr(temp, ','); if (temp != NULL) { i++; temp++; continue; } /* There's always one entry more than the number of commas. */ i++; break; } start = (char **)malloc(i * sizeof(char *)); ... /* setenv stdout serial,vga 幾個用逗號分隔的參數*/ i = 0; start[0] = console_args; for (;;) { temp = strchr(start[i++], ','); if (temp == NULL) break; *temp = '\0'; start[i] = temp + 1; } /*start是一個指向字符串的指針數組。這裡start[0]指向serial, start[1]指向vga*/ /*--------------------------以上為代碼段2.1 --------------------------------------------*/ cons_set = (struct stdio_dev **)calloc(i, sizeof(struct stdio_dev *)); /*...cons_set檢查,出錯返回1*/ switch (console) { case stdout: io_flag = DEV_FLAGS_OUTPUT; break; default: /*...釋放資源start,console_args,cons_set*/ return 1; } cs_idx = 0; for (j = 0; j < i; j++) { dev = search_device(io_flag, start[j]); if (dev == NULL) continue; repeat = 0; for (k = 0; k < cs_idx; k++) { if (dev == cons_set[k]) { repeat++; break; } } if (repeat) continue; if (console_assign(console, start[j]) < 0) continue; cons_set[cs_idx++] = dev; } /*--------------------------以上為代碼段2.2 --------------------------------------------*/ free(console_args); free(start); /* failed to set any console */ if (cs_idx == 0) { free(cons_set); return 1; } else { console_devices[console] = (struct stdio_dev **)realloc(console_devices[console], cs_idx * sizeof(struct stdio_dev *)); if (console_devices[console] == NULL) { free(cons_set); return 1; } memcpy(console_devices[console], cons_set, cs_idx * sizeof(struct stdio_dev *)); cd_count[console] = cs_idx; } free(cons_set); /*--------------------------以上為代碼段2.3 --------------------------------------------*/ return 0; } #endif /* CONFIG_CONSOLE_MUX */上述程序中的stdin,stdout,stderr在include/common.h中定義:
#define stdin 0 #define stdout 1 #define stderr 2 #define MAX_FILES 3討論上述代碼之前,首先要強調的是,只有定義了CONFIG_CONSOLE_MUX,才會有函數iomux_doenv的定義和實現。
int console_assign(int file, const char *devname) { int flag; struct stdio_dev *dev; /* Check for valid file */ switch (file) { case stdin: flag = DEV_FLAGS_INPUT; break; case stdout: case stderr: flag = DEV_FLAGS_OUTPUT; break; default: return -1; } /* Check for valid device name */ dev = search_device(flag, devname); if (dev) return console_setfile(file, dev); return -1; }為了此處的討論盡可能清晰簡單,search_device函數我們放在後面分析。
static int console_setfile(int file, struct stdio_dev * dev) { int error = 0; if (dev == NULL) return -1; switch (file) { case stdin: case stdout: case stderr: /* Start new device */ if (dev->start) { error = dev->start(dev); /* If it's not started dont use it */ if (error < 0) break; } /* Assign the new device (leaving the existing one started) */ stdio_devices[file] = dev; /* * Update monitor functions * (to use the console stuff by other applications) */ switch (file) { case stdin: gd->jt->getc = getc; ... break; case stdout: ... gd->jt->printf = printf; break; } break; default: /* Invalid file ID */ error = -1; } return error; }首先嘗試啟動入口參數中的stdio設備。需要注意的是,在前面"stdio_add_devices"一節stdio設備的注冊中,只是填充了相關的結構體,如果其後沒有被使用(如serial就曾被使用了),就還未真正實際啟動被注冊的設備。而這裡,為console分配stdio設備時,就要啟動它(實際是初始化該硬件設備),因為接下就要使用該硬件完成stdio實際的硬件輸入輸出操作。上述dev->start代碼中,一旦啟動失敗(有可能已啟動,或硬件自身的原因),函數console_setfile就立即返回,返回值為0。console_setfile的上層函數也返回0,這樣就回到代碼段2.2,但接下來還是會填充cons_set,但不會在函數console_setfile中接著填充下面的stdio_devices。
程序最後將更新gd->jt函數列表。
代碼段2.3
該段代碼主要實現:
將上面代碼段查找到的設備存儲到全局變量console_devices[console]中,其設備個數存儲到全局變量cd_count[console]中。
這裡的console即stdin,stdout,stderr常量之一。當這三者之一擁有多個stdio設備時,console_devices[console]會保存這多個設備,且用cd_count[console]來記錄設備個數。如環境變量stdout的值為serial,vga,那麼console_devices[1]指向的struct stdio_dev結構體指針數組中會包含兩個指針,分別指向serial和vga設備對應的結構體地址。
cd_count[1]為console_devices[1]指向的數組的長度,這裡值為2。
我們可以在最終的輸出函數console_puts實現中看到console_devices和cd_count的使用:
static void console_puts(int file, const char *s) { int i; struct stdio_dev *dev; for (i = 0; i < cd_count[file]; i++) { dev = console_devices[file][i]; if (dev->puts != NULL) dev->puts(dev, s); } }函數iomux_doenv總結:
static struct stdio_dev *tstcdev; struct stdio_dev **console_devices[MAX_FILES]; int cd_count[MAX_FILES];其中的MAX_FILES在 include/common.h中定義為3,即stdin,stdout,stderr。
if (outputdev == NULL) { outputdev = search_device(DEV_FLAGS_OUTPUT, "serial"); }search_device函數執行設備查找,其輸入參數DEV_FLAGS_OUTPUT為stdin,stdout,stderr對應的三者之一。
struct stdio_dev *search_device(int flags, const char *name) { struct stdio_dev *dev; dev = stdio_get_by_name(name); if (dev && (dev->flags & flags)) return dev; return ((void *)0); }函數stdio_get_by_name在common/stdio.c中實現:
struct stdio_dev* stdio_get_by_name(const char *name) { struct list_head *pos; struct stdio_dev *dev; if(!name) return NULL; list_for_each(pos, &(devs.list)) { dev = list_entry(pos, struct stdio_dev, list); if(strcmp(dev->name, name) == 0) return dev; } return NULL; }在前面"stdio_add_devices"函數討論的一節中,所有注冊的stdio設備使用全局變量devs.list鏈表串接起來,stdio_get_by_name函數就是在此鏈表中查找名字為涵參name的stdio設備。我們繼續跟蹤list_entry,可以看到其定義為:
#define list_entry(ptr, type, member) \ container_of(ptr, type, member)container_of和linux驅動中用法一致,也是通過結構體成員來查找結構體自身的首址。我們在前面的"stdio_add_devices" 一節中也曾經提及過,devs.list的鏈表成員只是struct stdio_dev結構體的成員變量list,而非struct stdio_dev結構體變量本身。所以要使用container_of查找描述stdio設備的struct stdio_dev變量自身。stdio_get_by_name執行完返回到search_device後,如果找到設備,還會核對查找到的設備屬性標志是否和輸入參數的標志一致,設備屬性標志在該設備注冊時設置。
static inline void console_doenv(int file, struct stdio_dev *dev) { console_setfile(file, dev); }函數console_setfile將上述查找到的設備最終存儲在全局變量stdio_devices中。這我們在上面代碼段2.2已經討論過。最終將默認的serial設備賦值到stdio_devices中去。
gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */ print_pre_console_buffer(PRE_CONSOLE_FLUSHPOINT2_EVERYTHING_BUT_SERIAL);由於CONFIG_PRE_CONSOLE_BUFFER沒有定義,print_pre_console_buffer為空函數。
void puts(const char *s) { ... if(!gd->have_console) return pre_console_puts(s); if (gd->flags & GD_FLG_DEVINIT) { /* Send to the standard output */ fputs(stdout, s); } else { /* Send directly to the handler */ pre_console_puts(s); serial_puts(s); } }上述代碼中,如果gd->flags & GD_FLG_DEVINIT為真時,將使用fputs執行信息輸出,fputs定義為:
void fputs(int file,constchar*s) { if (file < MAX_FILES) console_puts(file, s); }函數console_puts我們在代碼段2.2中粗略提及過,其具體實現如下:
static void console_puts(int file, const char *s) { int i; struct stdio_dev *dev; for (i = 0; i < cd_count[file]; i++) { dev = console_devices[file][i]; if (dev->puts != NULL) dev->puts(dev, s); } }
可見,gd->flags & GD_FLG_DEVINIT為真時,最終將使用console_devices中注冊過的控制台函數執行相關操作。也即是,執行了代表gd->flags |= GD_FLG_DEVINIT後,gd->flags & GD_FLG_DEVINIT為真 ,代表console控制台中的相關操作函數可用了。否則使用默認的串口輸出函數serial_puts。
考慮到這樣一種情況,我們在上述代碼段5的語句
gd->flags |= GD_FLG_DEVINIT;
之前的printf輸出信息,將會使用默認的串口輸出函數serial_puts,該函數在board_f階段被注冊且其後續可用。而該代碼段之後的程序,所使用的printf,都將使用該節討論的console控制台輸出函數。
gd->flags |= GD_FLG_DEVINIT語句制造了一個這樣的分水嶺。
那麼在stdio和serial結構圖的基礎上,加上console,三者之間的結構總圖如下:
轉載請注明出處,喜歡我的可以關注我!上一節我們介紹了GSON和Volley,用GSON對返回的數據進行了初步解析,這一節我們更進一步,討論一下如何實現英文詞典。首先把JS
前言 Note: 本文中的策略適用於Android平台上android.location包中的定位API。不同於Google Location Services API,
一、問題描述Android應用中經常涉及從網絡中加載大量圖片,為提升加載速度和效率,減少網絡流量都會采用二級緩存和異步加載機制,所謂二級緩存就是通過先從內存中獲取、再從文
在windows安裝Android的開發環境不簡單也說不上算復雜,本文寫給第一次想在自己Windows上建立Android開發環境投入Android浪潮的朋友們,為了確保