9/03/2011

TI 128x BT/GPS/FM Share transport design and architecture (2)

Trace the ST related source code

預設OMAP4會把ST driver直接built in進kernel,另外GPS和FM driver則會compile成模組型式 :

CONFIG_TI_ST=y
CONFIG_ST_FM=m
CONFIG_ST_GPS=m

GPS & FM driver path : kernel/drivers/staging/ti-st/

ST driver path : kernel/drivers/misc/ti-st

從上面知道,ST driver包含3個sub-moudules,所以這個目錄底下就有相對應的source code:

st_core.c     st_kim.c       st_ll.c

這三個檔案會被compile成叫st_drv.o....

UIM就有點小麻煩...因為TI在OMAP4 Blaze BSP裡面放了3個UIM code...

分別叫 "uim" , "uim-rfkill" , "uim-sysfs",該UIM的路徑是在mydroid/hardware/ti/wpan/ti_st/

之前知道UIM一支deamon,是系統boot時就開始run了,所以馬上就可以知道TI會在init.rc加入UIM service ...

service uim /system/bin/uim-sysfs
    user root         
    group media bluetooth                                                                                                 
    oneshot

uim-sysfs加上oneshot,可以知道TI所使用的是 "uim-sysfs" ,從裡面的uim.c瞭解它會把st/bt/fm/gps等driver一個個insmod進去,利用lstat來檢查.ko是否存在。

因為現在是built-in,所以UIM並不會跑這段...

而是在更早之前,st_kim的init func是__init,所以在kernel一起來時就會register,而且還是register一個platform_drive的結構 :

static struct platform_driver kim_platform_driver = {
    .probe = kim_probe,
    .remove = kim_remove,
    .suspend = kim_suspend,
    .resume = kim_resume,
    .driver = {                                                                                                             
        .name = "kim",
        .owner = THIS_MODULE,
    }, 
};  

出現了platform_driver結構,就一定會有想對應的platform_device結構來讓kernel assigned,所以可以在arch/arm/mach-omap2/board-4430sdp.c找到 :

struct ti_st_plat_data wilink_pdata = {
    .nshutdown_gpio = 55,
    .dev_name = BLUETOOTH_UART_DEV_NAME,
    .flow_cntrl = 1,
    .baud_rate = 3000000,
    .suspend = plat_kim_suspend,
    .resume = plat_kim_resume,
}; 
static struct platform_device wl128x_device = {
    .name       = "kim",
    .id     = -1,
    .dev.platform_data = &wilink_pdata,
}; 
static struct platform_device btwilink_device = {
    .name = "btwilink",
    .id = -1,                                                                                                               
};   

nshutdown_gpio是BT/GPS/FM Enable pin...所以要改GPIO直接改這。(不太清楚TI怎麼去控制單一GPIO來防止3個device打架...還是真的3個只能開1個 !? 手邊沒平台...哎)

dev_name是UART deivce name...像是/dev/ttyxx之類的,所以要修改其它port,就修改這個定義。

接下來就是kim probe了,為每個device建構結構.....分配記憶體位址.....取得private data等等..

但這時就會去呼叫st_core,通知st_core initialization的時候到了....

int st_core_init(struct st_data_s **core_data)
{
    .....
    err = tty_register_ldisc(N_TI_WL, &st_ldisc_ops);
    .....
}

st_core 就最重要的就是註冊Line Discipline了。

什麼是Line Discipline ? 對 TTY device 的 I/O 動作,會經由 TTY core 送給 TTY driver,而資料往來於 TTY 與 TTY driver 的過程中...

可以這麼想,連結TTY Core和TTY driver資料往來的線,就叫Line Discipline。

Line Discipline是被設計成可以抽換的,例如,今天我想要讓這個TTY device是接收的資料是數據機而不要來自終端機,就把N_TTY改成N_PPP。

想要只接收藍芽的資料,就改成N_HCI....

簡言而之,就是Line Discipline會按照某個特殊的協議進行格式化,然後再送給TTY driver,讓TTY driver可以跟特定的硬體做溝通。

現在TI自己定義了一個N_TI_WL這個Line Discipline,所以porting時就要注意,要在kernel裡定義該Line Discipline....可以在kernel/include/linux/tty.h加入該定義。

接著st_kim會去request GPIO,並把GPIO設成輸出模式....

後面最後一道程序滿重要的,st_kim會create entries sysfs :

kim_gdata->kim_pdev = pdev;
init_completion(&kim_gdata->kim_rcvd);
init_completion(&kim_gdata->ldisc_installed);
   
status = sysfs_create_group(&pdev->dev.kobj, &uim_attr_grp);
if (status) {                                                                                                           
    pr_err("failed to create sysfs entries");
     return status;
}
   
/* copying platform data */
strncpy(kim_gdata->dev_name, pdata->dev_name, UART_DEV_NAME_LEN);
kim_gdata->flow_cntrl = pdata->flow_cntrl;
kim_gdata->baud_rate = pdata->baud_rate;
pr_info("sysfs entries created\n");

其中uim_attr_grp的結構為 :

/* structures specific for sysfs entries */
static struct kobj_attribute ldisc_install =
__ATTR(install, 0444, (void *)show_install, NULL);
   
static struct kobj_attribute uart_dev_name =
__ATTR(dev_name, 0444, (void *)show_dev_name, NULL);
       
static struct kobj_attribute uart_baud_rate =
__ATTR(baud_rate, 0444, (void *)show_baud_rate, NULL);
   
static struct kobj_attribute uart_flow_cntrl =
__ATTR(flow_cntrl, 0444, (void *)show_flow_cntrl, NULL);
   
static struct attribute *uim_attrs[] = {
    &ldisc_install.attr,
    &uart_dev_name.attr,
    &uart_baud_rate.attr,
    &uart_flow_cntrl.attr,
    NULL,
}; 
   
static struct attribute_group uim_attr_grp = {                                                                              
    .attrs = uim_attrs,
};  

TI會去Create這些sysfs的最主要目的就是讓UIM能在user space透過該檔案系統去open在kernel space的st_core

到這裡st_kim和st_core的initialization就結束了,回頭繼續看UIM

UIM會去check剛剛的sysfs這些路徑有沒有建立成功 :

#define INSTALL_SYSFS_ENTRY "/sys/devices/platform/kim/install"
#define DEV_NAME_SYSFS      "/sys/devices/platform/kim/dev_name"                                                            
#define BAUD_RATE_SYSFS     "/sys/devices/platform/kim/baud_rate"
#define FLOW_CTRL_SYSFS     "/sys/devices/platform/kim/flow_cntrl"

如果都有,而且BT/GPS/FM等相關的.ko和裝置節點都有存在的話,就會去create rfkill的路徑了 :

if (change_rfkill_perms() < 0) {                                                                                        
     /* possible error condition */
     UIM_ERR("rfkill not enabled in st_drv - BT on from UI might fail\n");
}   

這樣一來,bluedroid (system/bluetooth/bluedroid/bluetooth.c)就可以透過rfkill來控制BT power。

然後UIM就去open st_core,讓st_core能成功install line discipline.

st_fd = open(INSTALL_SYSFS_ENTRY, O_RDONLY);
if (st_fd < 0) {
      UIM_DBG("unable to open %s (%s)", INSTALL_SYSFS_ENTRY,
      strerror(errno));
      remove_modules();
      return -1;
}

到這裡,UIM的任務就算暫時結束了 !

註 : hciattach的opation部份被改"-l"...它做的也只是print每個attach的結構,並沒做任何事... 
      所以hciattach的功用完全被UIM/KIM替代掉了,因為KIM己經把/dev/ttyx帶起來了 XD (如果    
      不用GPS的話...ST應該可以放一邊吧0.0?)

No comments:

Post a Comment