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的工作介绍完毕。