Chinaunix首页 | 论坛 | 博客
  • 博客访问: 181266
  • 博文数量: 13
  • 博客积分: 265
  • 博客等级: 二等列兵
  • 技术积分: 402
  • 用 户 组: 普通用户
  • 注册时间: 2012-07-16 17:19
文章分类

全部博文(13)

文章存档

2014年(2)

2013年(2)

2012年(9)

我的朋友

分类: LINUX

2012-10-19 13:39:33

注册中断处理函数

首先是注册中断函数来处理蓝牙硬件得到数据的事件。这是一个标准的tty串口类的实现。

  1. serial core对应硬件抽象驱动(drivers/tty/serial/pxa.c,module_init时被调用入口函数)
  2.                     |
  3.                     | uart_register_driver(&serial_pxa_reg),注册uart driver,这里定了tty
  4.                     | 设备的名字 /dev/ttyS%d,然后platform_driver_register(&serial_pxa_driver)
  5.                     | 里面注册了一个probe函数,platform总线扫描到设备时会调用serial_pxa_probe增加
  6.                     | 一个对应的uart_pxa_port(函数uart_add_one_port),设置uport有对应的内存,op
  7.                     | s,以及中断号。这里关注下uart的注册函数
  8.                     |
  9. serial core(drivers/tty/serial/serial_core.c,函数uart_register_driver)
  10.                     |
  11.                     | normal->driver_state = drv
  12.                     | tty_set_operations(normal, &uart_ops);
  13.                     | 给uart driver设置了自己的ops,着重关注下open函数,当用户空间尝试用open函数打
  14.                     | 一个uart设备时,uart_open会被tty_open调用(retval = tty->ops->open)
  15.                     |
  16.                    .....
  17.                     |
  18. 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)
  1. // drivers/tty/serial/pxa.c
  2. static inline irqreturn_t serial_pxa_irq(int irq, void *dev_id)
  3. {
  4.      struct uart_pxa_port *up = dev_id;
  5.      unsigned int iir, lsr;
  6.      iir = serial_in(up, UART_IIR);
  7.      if (iir & UART_IIR_NO_INT)
  8.          return IRQ_NONE;
  9.      lsr = serial_in(up, UART_LSR);
  10.      if (lsr & UART_LSR_DR)
  11.          receive_chars(up, &lsr); // 接收数据的中断处理
  12.      check_modem_status(up);
  13.      if (lsr & UART_LSR_THRE)
  14.          transmit_chars(up); // 发送数据的中断处理
  15.      return IRQ_HANDLED;
  16. }

数据接收和数据发送都不是阻塞的,可以看出这是一个全双工的传输。


数据接收

然后是数据接收流程(这里是被动接收数据,由中断调起):

  1. receive_chars调用uart_insert_char获得数据并且写到tty结构体的buffer中,通过tty_flip_buffer_push统一推送到hci uart层
  2.                 |
  3.                 |
  4. tty core(drivers/tty/tty_buffer.c,函数tty_flip_buffer_push,调用flush_to_ldisc交给ldisc去处理数据)
  5.                 |
  6.                 | 通过tty_ldisc_ref(tty)找到ldisc指针,并通过disc->ops->receive_buf
  7.                 | 让hci的ldisc去处理这些数据
  8.                 |
  9. hci uart(drivers/bluetooth/hci_ldisc.c,函数hci_uart_tty_receive)
  10.                 |
  11.                 | 调用hu->proto->recv交给proto处理数据
  12.                 |
  13. 对应的proto层(drivers/bluetooth/hci_h4.c或者hci_bcsp.c, recv,调用hci_recv_stream_fragment或者hci_recv_frame将数据推送给hci层,也就是bluez的kernel协议栈去处理数据包。这部分处理取决于bcsp协议或者h4协议,需要参看对应文档)
  14.                 |
  15. hci层(net/bluetooth/hci_core.c,函数hci_recv_frame通过tasklet异步调用hci_rx_task,调用hci_send_to_sock通过已经连接并且正在监听的sockt将数据拷贝发送给HAL,并且根据skb的类型作对应处理——这步才是关键,类型见代码)

  1. // net/bluetooth/hci_core.c
  2. static void hci_rx_task(unsigned long arg)
  3. {
  4.     ....
  5.     /* Process frame */
  6.     switch (bt_cb(skb)->pkt_type) {
  7.         case HCI_EVENT_PKT: // hci event,通常是保存下来,以便上层查询时告知
  8.             hci_event_packet(hdev, skb);
  9.             break;
  10.         case HCI_ACLDATA_PKT: // l2cap协议,如果上层有BTPROTO_L2CAP类型socket,
  11.                               // 能够收到这些数据包
  12.             hci_acldata_packet(hdev, skb);
  13.             break;
  14.         case HCI_SCODATA_PKT: // 如果上层有BTPROTO_SCO类型socket,能够收到这些数据包
  15.             hci_scodata_packet(hdev, skb);
  16.             break;
  17.         default: // 丢掉
  18.             kfree_skb(skb);
  19.             break;
  20.         ....
  21. }
这块有个疑问,就是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写一遍。所以数据接收 流程简言之,就是:

  1. tty uart层中断(drivers/tty/serial/pxa.c)
  2.                  |
  3. tty core(drivers/tty/tty_buffer.c)
  4.                  |
  5. hci uart层(drivers/bluetooth/hci_ldisc.c,hci_bcsp.c)
  6.                  |
  7. hci层(net/bluetooth/hci_core.c)
  8.                  |
  9. hci层l2cap(net/bluetooth/l2cap_core.c)
  10.                  |
  11. hci层rfcomm(net/bluetooth/rfcomm/core.c)
  12.                  |
  13. HAL层(external/bluetooth/bluez/)

数据发送

基本上是数据接收的一个逆过程

  1. hci层(net/bluetooth/hci_sock.c),hci_sock_sendmsg或者hci_cmd_task
  2.                |
  3.                | 把msg打包成sk_buff结构体,放到hci_dev的raw_q里面
  4.                | 然后通过tasklet_schedule(&hdev->tx_task)唤醒
  5.                | 初始化的hci_tx_task,在这个函数里面调用hci_send_frame
  6.                | 发送hci_dev里面的raw_q
  7.                |
  8. hci层(net/bluetooth/hci_core.c),hci_send_frame
  9.                |
  10.                | 调用hdev->send(skb)将sk_buff发送
  11.                |
  12. hci uart层(drivers/bluetooth/hci_ldisc.c),hci_uart_send_frame
  13.                |
  14.                | hu->proto->enqueue(hu, skb)将sk_buff放proto对应的
  15.                | sk_buff_head里面然后调用hci_uart_tx_wakeup(hu)用一个wihle
  16.                | 循环发送sk_buff_head里面的所有sk_buff,并且调用hci_uart_tx_complete
  17.                | 进行发送的包的统计。发送数据是通过调用tty的方法实现
  18.                | 具体代码为tty->ops->write(tty, skb->data, skb->len)
  19.                | 这里面的ops是在pxa.c里面的初始化函数通过uart_register_driver注册的
  20.                |
  21. serial core(drivers/tty/serial/serial_core.c),uart_write
  22.                |
  23.                | uart_state *state = tty->driver_data,通过state获得缓冲区
  24.                | circ = &state->xmit,将需要放送的数据(也就是sk_buff)放到
  25.                | 缓冲区准备发送(注意这个xmit稍候会发现用到),发送的方式是写完数据到缓冲区后
  26.                | 调用uart_start(tty)唤醒对应端口,然后通过port->ops->start_tx(port)发送
  27.                |
  28. tty serial层硬件抽象驱动(drivers/tty/serial/pxa.c),serial_pxa_start_tx
  29.                |
  30.                | serial_out(up, UART_IER, up->ier),调用writel方法enable发送的port
  31.                | 的传输中断,真正的中断响应见前面注册的irq响应函数serial_pxa_irq;中断响应后,
  32.                | 会调具体的函数去真正发送数据
  33.                |
  34. tty serial层硬件抽象驱动(drivers/tty/serial/pxa.c),transmit_chars
  35.                |
  36.                | serial_out(up, UART_TX, xmit->buf[xmit->tail]),把前面提到的缓冲区的内容
  37.                | 往对应的芯片管脚一个char一个char的写,每次中断会发送fifosize/2个字节;其中
  38.                | fifosize对于pxa来说固定是64。当xmit里面的内容写完,会disable传输中断
  39.                | 具体波特率在这个过程怎么生效,还有待考察。
阅读(6936) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~