hciattach 分析

本文详细介绍了hciattach工具的使用方法及内部实现原理。hciattach用于建立串口与蓝牙协议层之间的数据连接通道,文中深入解析了其命令行参数、结构体定义、初始化过程以及与内核的交互。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

hciattach的功能是:建立串口和蓝牙协议层的数据连接通道。
源代码参考 tools/hciattach.c
先看hciattach的用法
hciattach [-n] [-p] [-b] [-r] [-t timeout] [-s initial_speed] <tty> <type | id> [speed] [flow|noflow] [bdaddr]
hciattach -l

hciattach的结构体
struct uart_t {
char *type;
int  m_id;
int  p_id;
int  proto;
int  init_speed;
int  speed;
int  flags;
char *bdaddr;
int  (*init) (int fd, struct uart_t *u, struct termios *ti);
int  (*post) (int fd, struct uart_t *u, struct termios *ti);
};

从main 函数开始,
while ((opt=getopt(argc, argv, "bnpt:s:lr")) != EOF)   //为什么,请看上面的用法
解析main函数的参数,然后switch(opt) 进入选择。
init_speed = atoi(optarg);
……

n = argc - optind;
if (n < 2) {
usage();
exit(1);
}
这是参数不对,调用使用方法
for (n = 0; optind < argc; n++, optind++) {
char *opt;

opt = argv[optind];

switch(n) {
case 0:
dev[0] = 0;
if (!strchr(opt, '/'))    //查找opt中首次出现/的地方  
strcpy(dev, "/dev/");
strcat(dev, opt);    //opt追加到dev后
break;
                case 1:
                        if (strchr(argv[optind], ',')) {
int m_id, p_id;
sscanf(argv[optind], "%x,%x", &m_id, &p_id);
u = get_by_id(m_id, p_id);    //获取uart_t,返回uart[],对应其中的初始化函数等。假如是uart[1]就是bcsp
} else {
u = get_by_type(opt);
}
                ……
前面都是对hciattach用法的解析。
struct uart_t uart[] = {
{ "any",        0x0000, 0x0000, HCI_UART_H4,   115200, 115200, FLOW_CTL, NULL, NULL     },
{ "bcsp",       0x0000, 0x0000, HCI_UART_BCSP, 115200, 115200, 0,        NULL, bcsp     },
        ……
上面粗体标注  对应u->init,u->post。这里蓝牙供应商会添加自己的配置和通讯方式。


设置好了通讯方式,在初始化串口调用。
init_uart函数分析
static int init_uart(char *dev, struct uart_t *u, int send_break, int raw)
{
struct termios ti;
int fd, i;
unsigned long flags = 0;
if (raw)
flags |= 1 << HCI_UART_RAW_DEVICE;

fd = open(dev, O_RDWR | O_NOCTTY);     //可进行读写,不将此设备分配作为此进程的控制终端,即Ctrl+C 不能结束此进程
if (fd < 0) {
perror("Can't open serial port");
return -1;
}

tcflush(fd, TCIOFLUSH);    //清空数据线

if (tcgetattr(fd, &ti) < 0) {        //获得串口设置
perror("Can't get port settings");
return -1;
}

cfmakeraw(&ti);    //将终端设置为原始模式控制终端

ti.c_cflag |= CLOCAL;
if (u->flags & FLOW_CTL)    //设置流控
ti.c_cflag |= CRTSCTS;
else
ti.c_cflag &= ~CRTSCTS;    //本地连接,无调制解调器控制
if (tcsetattr(fd, TCSANOW, &ti) < 0) {    //设置串口
perror("Can't set port settings");
return -1;
}

/* Set initial baudrate */
if (set_speed(fd, &ti, u->init_speed) < 0) {    
perror("Can't set initial baud rate");
return -1;
}

tcflush(fd, TCIOFLUSH);

if (send_break) {
tcsendbreak(fd, 0);    //在串口线上发送0值
usleep(500000);    //0.5s发送一个break
}

if (u->init && u->init(fd, u, &ti) < 0)    
//所有bluez支持的蓝牙串口设备类型构成了一个uart结构数组,通过查找对应的uart类型,并调用这个uart的init成员函数。
return -1;

tcflush(fd, TCIOFLUSH);

/* Set actual baudrate */        //设置实际的波特率
if (set_speed(fd, &ti, u->speed) < 0) {
perror("Can't set baud rate");
return -1;
}

/* Set TTY to N_HCI line discipline */
i = N_HCI;
if (ioctl(fd, TIOCSETD, &i) < 0) {        //调用内核的tty_ioctl,采用ldisc机制改变hci行规
perror("Can't set line discipline");
return -1;
}

if (flags && ioctl(fd, HCIUARTSETFLAGS, flags) < 0) {    //设置串口参数
perror("Can't set UART flags");
return -1;
}
if (ioctl(fd, HCIUARTSETPROTO, u->proto) < 0) {   
 //调用内核的tty_ioctl,注册一个hci设备,设置proto为hci设备的proto操作函数集
perror("Can't set device");
return -1;
}

if (u->post && u->post(fd, u, &ti) < 0)    //调用这个uart的post成员函数
return -1;

return fd;
}
其中TIOCSETD,HCIUARTSETPROTO需要内核处理

重新回到main(),后面信号处理  

恢复行规
ld = N_TTY;
if (ioctl(n, TIOCSETD, &ld) < 0) {
perror("Can't restore line discipline");
exit(1);
}
 
到此为止,hciattach的工作介绍完毕。

 
 
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值