内核中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来注册填充。
          p->open(hu);  //调用对应open接口,将对应的proto回调保存到hci_uart结构中
          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节点

到目前为止,hci0已经注册好了,android层的bluez可以通过AF_BLUETOOTH访问,获取HCI信息了。

你可能感兴趣的:(linux,android)