注册中断处理函数
首先是注册中断函数来处理蓝牙硬件得到数据的事件。这是一个标准的tty串口类的实现。
- serial core对应硬件抽象驱动(drivers/tty/serial/pxa.c,module_init时被调用入口函数)
- |
- | uart_register_driver(&serial_pxa_reg),注册uart driver,这里定了tty
- | 设备的名字 /dev/ttyS%d,然后platform_driver_register(&serial_pxa_driver)
- | 里面注册了一个probe函数,platform总线扫描到设备时会调用serial_pxa_probe增加
- | 一个对应的uart_pxa_port(函数uart_add_one_port),设置uport有对应的内存,op
- | s,以及中断号。这里关注下uart的注册函数
- |
- serial core(drivers/tty/serial/serial_core.c,函数uart_register_driver)
- |
- | normal->driver_state = drv
- | tty_set_operations(normal, &uart_ops);
- | 给uart driver设置了自己的ops,着重关注下open函数,当用户空间尝试用open函数打
- | 开一个uart设备时,uart_open会被tty_open调用(retval = tty->ops->open)
- |
- .....
- |
- serial core(drivers/tty/serial/serial_core.c,函数uart_open-->uart_startup,调用uport->ops->startup设置端口以及对应的irq处理,也就是serial_pxa_pops里面设置的serial_pxa_startup,request_irq(up->port.irq, serial_pxa_irq, 0, up->name, up)。可以看到设置的中断处理函数是serial_pxa_irq)
- // drivers/tty/serial/pxa.c
- static inline irqreturn_t serial_pxa_irq(int irq, void *dev_id)
- {
- struct uart_pxa_port *up = dev_id;
- unsigned int iir, lsr;
- iir = serial_in(up, UART_IIR);
- if (iir & UART_IIR_NO_INT)
- return IRQ_NONE;
- lsr = serial_in(up, UART_LSR);
- if (lsr & UART_LSR_DR)
- receive_chars(up, &lsr); // 接收数据的中断处理
- check_modem_status(up);
- if (lsr & UART_LSR_THRE)
- transmit_chars(up); // 发送数据的中断处理
- return IRQ_HANDLED;
- }
数据接收和数据发送都不是阻塞的,可以看出这是一个全双工的传输。
数据接收
然后是数据接收流程(这里是被动接收数据,由中断调起):
- receive_chars调用uart_insert_char获得数据并且写到tty结构体的buffer中,通过tty_flip_buffer_push统一推送到hci uart层
- |
- |
- tty core(drivers/tty/tty_buffer.c,函数tty_flip_buffer_push,调用flush_to_ldisc交给ldisc去处理数据)
- |
- | 通过tty_ldisc_ref(tty)找到ldisc指针,并通过disc->ops->receive_buf
- | 让hci的ldisc去处理这些数据
- |
- hci uart(drivers/bluetooth/hci_ldisc.c,函数hci_uart_tty_receive)
- |
- | 调用hu->proto->recv交给proto处理数据
- |
- 对应的proto层(drivers/bluetooth/hci_h4.c或者hci_bcsp.c, recv,调用hci_recv_stream_fragment或者hci_recv_frame将数据推送给hci层,也就是bluez的kernel协议栈去处理数据包。这部分处理取决于bcsp协议或者h4协议,需要参看对应文档)
- |
- hci层(net/bluetooth/hci_core.c,函数hci_recv_frame通过tasklet异步调用hci_rx_task,调用hci_send_to_sock通过已经连接并且正在监听的sockt将数据拷贝发送给HAL,并且根据skb的类型作对应处理——这步才是关键,类型见代码)
- // net/bluetooth/hci_core.c
- static void hci_rx_task(unsigned long arg)
- {
- ....
- /* Process frame */
- switch (bt_cb(skb)->pkt_type) {
- case HCI_EVENT_PKT: // hci event,通常是保存下来,以便上层查询时告知
- hci_event_packet(hdev, skb);
- break;
- case HCI_ACLDATA_PKT: // l2cap协议,如果上层有BTPROTO_L2CAP类型socket,
- // 能够收到这些数据包
- hci_acldata_packet(hdev, skb);
- break;
- case HCI_SCODATA_PKT: // 如果上层有BTPROTO_SCO类型socket,能够收到这些数据包
- hci_scodata_packet(hdev, skb);
- break;
- default: // 丢掉
- kfree_skb(skb);
- break;
- ....
- }
这块有个疑问,就是hci的ldisc里面,bscp有给skb的pkt_type赋值,但是h4没有,不知道h4类型的proto怎么实现 的... hci_acldata_packet在调用net/bluetooth/l2cap_core.c里面的l2cap_recv_acldata往 BTPROTO_L2CAP类型socket写包,并且会通过回调函数的方法接着往BTPROTO_RFCOMM类型的socket写一遍。所以数据接收 流程简言之,就是:
- tty uart层中断(drivers/tty/serial/pxa.c)
- |
- tty core(drivers/tty/tty_buffer.c)
- |
- hci uart层(drivers/bluetooth/hci_ldisc.c,hci_bcsp.c)
- |
- hci层(net/bluetooth/hci_core.c)
- |
- hci层l2cap(net/bluetooth/l2cap_core.c)
- |
- hci层rfcomm(net/bluetooth/rfcomm/core.c)
- |
- HAL层(external/bluetooth/bluez/)
数据发送基本上是数据接收的一个逆过程- hci层(net/bluetooth/hci_sock.c),hci_sock_sendmsg或者hci_cmd_task
- |
- | 把msg打包成sk_buff结构体,放到hci_dev的raw_q里面
- | 然后通过tasklet_schedule(&hdev->tx_task)唤醒
- | 初始化的hci_tx_task,在这个函数里面调用hci_send_frame
- | 发送hci_dev里面的raw_q
- |
- hci层(net/bluetooth/hci_core.c),hci_send_frame
- |
- | 调用hdev->send(skb)将sk_buff发送
- |
- hci uart层(drivers/bluetooth/hci_ldisc.c),hci_uart_send_frame
- |
- | hu->proto->enqueue(hu, skb)将sk_buff放proto对应的
- | sk_buff_head里面然后调用hci_uart_tx_wakeup(hu)用一个wihle
- | 循环发送sk_buff_head里面的所有sk_buff,并且调用hci_uart_tx_complete
- | 进行发送的包的统计。发送数据是通过调用tty的方法实现
- | 具体代码为tty->ops->write(tty, skb->data, skb->len)
- | 这里面的ops是在pxa.c里面的初始化函数通过uart_register_driver注册的
- |
- serial core(drivers/tty/serial/serial_core.c),uart_write
- |
- | uart_state *state = tty->driver_data,通过state获得缓冲区
- | circ = &state->xmit,将需要放送的数据(也就是sk_buff)放到
- | 缓冲区准备发送(注意这个xmit稍候会发现用到),发送的方式是写完数据到缓冲区后
- | 调用uart_start(tty)唤醒对应端口,然后通过port->ops->start_tx(port)发送
- |
- tty serial层硬件抽象驱动(drivers/tty/serial/pxa.c),serial_pxa_start_tx
- |
- | serial_out(up, UART_IER, up->ier),调用writel方法enable发送的port
- | 的传输中断,真正的中断响应见前面注册的irq响应函数serial_pxa_irq;中断响应后,
- | 会调具体的函数去真正发送数据
- |
- tty serial层硬件抽象驱动(drivers/tty/serial/pxa.c),transmit_chars
- |
- | serial_out(up, UART_TX, xmit->buf[xmit->tail]),把前面提到的缓冲区的内容
- | 往对应的芯片管脚一个char一个char的写,每次中断会发送fifosize/2个字节;其中
- | fifosize对于pxa来说固定是64。当xmit里面的内容写完,会disable传输中断
- | 具体波特率在这个过程怎么生效,还有待考察。
阅读(7035) | 评论(0) | 转发(1) |