內核中line discipline的註冊流程以及BT hciattach進程的啓動

以hci_ldisc.c爲例,梳理內核中線路規程的註冊流程

我們的N_HCI的註冊過程如下:
bluetooth/hci_ldisc.c
     module_init(hci_uart_init);
           tty_register_ldisc(N_HCI, &hci_uart_ldisc) //hci_uart_ldisc包含N_HCI線路規程的一系列回調

drivers/tty/tty_ldisc.c
int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc)
     tty_ldiscs[disc] = new_ldisc;  //將線路規程操作,保存到全局數組
     new_ldisc->num = disc;
     new_ldisc->refcount = 0;

這樣,用戶就可以通過調用IOCTL的TIOSETD命令,來設置該規程

以hciattach爲例:
hciattach進程的啓動
以a20 4.1 realtek爲例:
init.sun6i.rc:
# 3. realtek rtl8723as bt hciattach
service hciattach /system/bin/logwrapper /system/bin/hciattach -n -s 115200 /dev/ttyS1 rtk_h5 1500000
   user root
   group bluetooth net_bt_admin
   disabled
   oneshot

hciattach的代碼位於:
external/bluetooth/bluez/tools/hciattach.c
external/bluetooth/bluez/tools/hciattach_rtk.c

hciattach.c
      main
          首先,解析參數,這個參數決定了用那一套uart接口。
          { "rtk_h5",     0x0000, 0x0000, HCI_UART_3WIRE, 115200, 1500000, FLOW_CTL,0, NULL, realtek_init, realtek_post},
          n = init_uart(dev, u, send_break, raw); // 初始化uart dev=/dev/ttyS1 u爲rtk_h5這套參數,
               int fd = open(dev, O_RDWR | O_NOCTTY); //打開串口
               u->init(fd, u, &ti);//調用回調初始化函數
                    rtk_init_h5(fd, ti); //做下載firmware的準備工作
                    rtk_config(fd, proto, speed, ti); //下載藍牙firmware
               set_speed(fd, &ti, u->init_speed);//設置初始波特率 115200
               int i = N_HCI;
               ioctl(fd, TIOCSETD, &i);//設置tty爲N_HCI線路規程
               ioctl(fd, HCIUARTSETPROTO, u->proto); //設置爲HCI_UART_3WIRE
               u->post(fd, u, &ti);//調用回調函數post,//設置高速模式

內核對TIOSETD的處理
在drivers/tty/tty_io.c種,處理TIOSETD
long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) //tty設備的IOCTL回調處理函數
     return tiocsetd(tty, p);  //設置線路規程
          return tty_set_ldisc(tty, ldisc); //tty = N_HCI
drivers/tty/tty_ldisc.c
int tty_set_ldisc(struct tty_struct *tty, int ldisc)
     struct tty_ldisc *new_ldisc = tty_ldisc_get(ldisc); //講N_HCI轉換爲tty_ldisc的數據結構,其中包含ops回調,其實就是通過N_HCI作爲索引在一個tty_ldisc數組中取出相應的item,這個tty_ldisc數組,就是hci_uart_ldisc,上面講了,是通過通過int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc)註冊。
     work = tty_ldisc_halt(tty);//終止原來線路規
     tty_ldisc_assign(tty, new_ldisc);
     tty_set_termios_ldisc(tty, ldisc); //設置新的線路規程
     tty_ldisc_open(tty, new_ldisc); //調用hci_uart_ldisc的open回調,做一些清空buffer等初始化工作
     tty->ops->set_ldisc(tty); //調用hci_uart_ldisc設置到tty_struct中去

對HCIUARTSETPROTO的處理
現在,剛剛的串口設備已經使用了新的線路規程,它將處理HCIUARTSETPROTO命令,但是在/drivers/tty/tty_io.c中,如果發現該CMD處理不了,就調用對應線路規程的ops中的ioctl來處理:
long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
     ld->ops->ioctl(tty, file, cmd, arg); //調用到hci_uart_ldisc中的hci_uart_tty_ioctl函數

hci_ldisc.c
hci_uart_tty_ioctl
     hci_uart_set_proto(hu, arg);//arg = HCI_UART_3WIRE
          struct hci_uart_proto *p = hci_uart_get_proto(id);//獲取hci協議,裏面包含一系列open等回調。
               其實這裏,就是根據id作爲索引,獲取hci_uart_proto數組的一個item,這些item,通過hci_uart_register_proto來註冊填充。
          hci_uart_register_dev(hu);//註冊該hci設備
               struct hci_dev *hdev; 構造一個hci_dev結構,填入hci_uart相關回調。
               hci_register_dev(hdev);//註冊設備
                    //首先遍歷當前所有hci設備,這些設備都保存到一個全局數組hci_dev_list,獲取hci設備的後綴id
                    sprintf(hdev->name, "hci%d", id);//保存名字
                    hci_register_sysfs(hdev);//在sys文件系統中,註冊一個設備節點。
                         
                    hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev,
                           RFKILL_TYPE_BLUETOOTH, &hci_rfkill_ops, hdev); 
                    rfkill_register(hdev->rfkill);//註冊rfkill節點

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章