從很久以前就很想去撰寫這類的文章,但因為文筆不夠生動,知識技巧和技術深度其實都無法寫到自己想要的感覺,所以等了很久,雖然現在還是很肉腳,但至少我找到想要的主題,讓我們慢慢進入linux 世界吧!
先談論我的工作內容,不管在第一個工作(代理商)或是第二個工作(系統廠),其實每個工作接觸的面和點都不同,像是在代理商自家產品要很了解,尤其是在regisiter & datasheet,因為不提供total solution,不然有提供solution 是買人家的system 在po自家公司的code,非常少見所以不列入討論範圍,所以後來轉型開始往driver(kernel space)面少點深,在第二個工作已經進來三個月,就接了兩個project,第一個是keil c 8051的架構(這個找時間在額外開一個文章來討論),第二個就是從8051 porting到 MIPS linux embedded system,時間很趕沒時間去發揮到很多,對專案程度而已第二家公司摸的會比較多面但時間緊湊摸不到點,因為在這過程也在學習所以這次開始po文,當作是一個成長紀錄,廢話不多說就開始吧!!
撰寫driver有很多技巧,在你面對硬體上也有不同的特性,像是大架構arm mips 雖然大家都說他很相似,其實還是很多不同的地方,舉例來說在gpio mips就是一個單一feature 在arm 部分 就有分periph,更不用提x86 或是 其他的,在細看內容 小從簡單的gpio up/down 到 gpio 寫出 i2c 或是usb等等,要了解需求在針對需求用對方法,下列介紹就針對我現在在看的地方。
如果不知道是剛入門不知道去哪邊找資料 下載code來看,請到atmel因為那邊datasheet是開放式下載的,我看到很多論壇都在討論samsung chip,我也很想同樂樂阿,但是他的原廠網站並沒有提供資料,在加上atmel有出原廠的ek(demo board)所有資料都可以找到,也可以去這裡下載kernel code(此連結是到原廠官網),所以來吧!!想學免費的請跟著點進去自己下載來練練功,檔案很多先不管其他的。
在arch\arm\mach-at91這個路徑下,你會看到Board-ek.c,這裡面放的是driver init,如果用source insight看到的你應該可以看到function連結出去很多,若是用其他編輯軟體就要自己找一下了,在這個檔案你會看到device init 都有 platform_device_register 這個function,這個就是註冊 該drvice的function,但此時kernel尚未看的見device,這個又要討論linux kernel2.6 跟 2.4 driver的差異,晚一點在補文來討論,此時需要將init function用module_init,才算真正的完整註冊。
platform 這個概念,在開發底層驅動程序時,首先要確認的就是設備的資源信息,例如設備的地址,在2.6內核中將每個設備的資源用結構 platform_device 來描述,該結構體定義在linux\include\linux\platform_device.h 的struct platform_device 此結構有一個重要的地方就是 resource可參考程式碼,來觀看大概的流程:
static struct resource eth_resources[] = {
[0] = {
.start = AT91CAP9_BASE_EMAC,
.end = AT91CAP9_BASE_EMAC + SZ_16K - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = AT91CAP9_ID_EMAC,
.end = AT91CAP9_ID_EMAC,
.flags = IORESOURCE_IRQ,
},
};
可以看到上面其實 define resource 好了,這時候才可以用 platform_device開始填入註冊資訊
static struct platform_device at91cap9_eth_device = {
.name = "macb",
.id = -1,
.dev = {
.dma_mask = ð_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32),
.platform_data = ð_data,
},
.resource = eth_resources,
.num_resources = ARRAY_SIZE(eth_resources),
};
最後才再將相關要register 寫在init function
{
if (!data)
return;
if (data->phy_irq_pin) {
at91_set_gpio_input(data->phy_irq_pin, 0);
at91_set_deglitch(data->phy_irq_pin, 1);
}
/* Pins used for MII and RMII */
at91_set_A_periph(AT91_PIN_PB21, 0); /* ETXCK_EREFCK */
at91_set_A_periph(AT91_PIN_PB22, 0); /* ERXDV */
at91_set_A_periph(AT91_PIN_PB25, 0); /* ERX0 */
at91_set_A_periph(AT91_PIN_PB26, 0); /* ERX1 */
at91_set_A_periph(AT91_PIN_PB27, 0); /* ERXER */
at91_set_A_periph(AT91_PIN_PB28, 0); /* ETXEN */
at91_set_A_periph(AT91_PIN_PB23, 0); /* ETX0 */
at91_set_A_periph(AT91_PIN_PB24, 0); /* ETX1 */
at91_set_A_periph(AT91_PIN_PB30, 0); /* EMDIO */
at91_set_A_periph(AT91_PIN_PB29, 0); /* EMDC */
if (!data->is_rmii) {
at91_set_B_periph(AT91_PIN_PC25, 0); /* ECRS */
at91_set_B_periph(AT91_PIN_PC26, 0); /* ECOL */
at91_set_B_periph(AT91_PIN_PC22, 0); /* ERX2 */
at91_set_B_periph(AT91_PIN_PC23, 0); /* ERX3 */
at91_set_B_periph(AT91_PIN_PC27, 0); /* ERXCK */
at91_set_B_periph(AT91_PIN_PC20, 0); /* ETX2 */
at91_set_B_periph(AT91_PIN_PC21, 0); /* ETX3 */
at91_set_B_periph(AT91_PIN_PC24, 0); /* ETXER */
}
eth_data = *data;
platform_device_register(&at91cap9_eth_device);
}
static const struct machine_desc __mach_desc_##_type \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = MACH_TYPE_##_type, \
.name = _name,
#define MACHINE_END \
};