蓝牙介绍
一、蓝牙简介
是一个短距离无线通信标准,适用于手机、计算机和其他电子设备之间的通信。蓝牙采用分散式网络结构以及快跳频和短包技术,支持点对点及点对多点通信,工作在全球通用的 2.4GHz ISM(即工业、科学、医学)频段。采用时分双工传输方案实现全双工传输。在Linux 中,通常使用的蓝牙协议栈实现是 BlueZ(其他协议还有很多,NOKIA,BCM 都有开发,这里就不一一列举了)。
蓝牙基本知识:
? 采用跳频技术,数据包短,抗信号衰减能力强;
? 采用快速跳频和前向纠错方案以保证链路稳定,减少同频干扰和远距离传输时的 随机噪声影响;
? 使用2.4GHzISM频段,无须申请许可证;
? 可同时支持数据、音频、视频信号;
蓝牙2.0:2004年推出,虽然传输距离短,传输速度慢,但是已经基本满足数据传输需求。
蓝牙3.0:2009年推出,传输速度达24Mbps,是蓝牙2.0 的八倍。能轻松用于录像机至高清电视,pc至打印机之间的资料传输。
蓝牙4.0:2010年推出,传输距离达100m,(3.0传输距离10m)。功耗低,成本低。
二、蓝牙协议简介
1,HCI协议编程
HCI是沟通上层协议以及程序与底层硬件协议的通道。所以,通过HCI发送的Command都是上层协议或者应用程序发送给Bluetooth Dongle的。它命令Bluetooth Dongle(或其中的硬件协议)去做什么何种动作。
? 得到Host上插入Dongle数目以及Dongle信息:
分配一个空间给 hci_dev_list_req。这里面将放所有Dongle信息。
struct hci_dev_list_req *dl;
struct hci_dev_req *dr;
struct hci_dev_info di;
int i;
if(!(dl = malloc(HCI_MAX_DEV * sizeof(struct hci_dev_req) + sizeof(uint16_t)))) {
perror("Can't allocate memory");
exit(1);
}
dl->dev_num= HCI_MAX_DEV;
dr =dl->dev_req;
// 打开一个HCI socket.
if ((ctl = socket(AF_BLUETOOTH, SOCK_RAW,BTPROTO_HCI)) < 0) {
perror("Can't open HCI socket.");
exit(1);
}
// 使用HCIGETDEVLIST,得到所有dongle的Device ID。存放在dl中。
if(ioctl(ctl, HCIGETDEVLIST, (void *) dl) < 0) {
perror("Can't get device list");
exit(1);
}
// 使用HCIGETDEVINFO,得到对应Device ID的Dongle信息。
di.dev_id = (dr+i)->dev_id;
ioctl(ctl, HCIGETDEVINFO, (void *) &di);
? UP和DownBluetooth Dongle:
ioctl(ctl, HCIDEVUP, hdev)
ioctl(ctl, HCIDEVDOWN, hdev)
ctl:为使用socket(AF_BLUETOOTH,SOCK_RAW, BTPROTO_HCI)打开的Socket.
hdev: Dongle Device ID.(所以上面的Socket不需要bind,因为这边指定了)
? 打开一个HCISocket---int hci_open_dev(int dev_id):
这个function用来打开一个HCI Socket。它首先打开一个HCI protocol的Socket,并将此Socket与deviceID=参数dev_id的Dongle绑定起来。只有bind后,它才将Socket句柄与Dongle对应起来。
注意,所有的HCI Command发送之前,都需要使用 hci_open_dev打开并绑定。
? 关闭一个HCISocket:
int hci_close_dev(int dd) //简单的关闭使用hci_open_dev打开的Socket。
? 向HCI Socket(对应一个Dongle)发送 request:
int hci_send_req(int dd, struct hci_request*r, int to)
BlueZ提供这个function非常有用,它可以实现一切Host向Modules发送Command的功能。
参数1:HCI Socket。
参数2:Command内容。
参数3:以milliseconds为单位的timeout.
下面详细解释此function和用法:
当应用程序需要向Dongle(对应为一个bind后的Socket)发送Command时,调用此function.
其中,参数一dd对应一个使用hci_open_dev()打开的Socket(Dongle)。
参数三to则为等待Dongle执行并回复命令结果的timeout.以毫秒为单位。
参数二hci_request * r 最为重要,首先看它的结构:
struct hci_request {
uint16_t ogf; //Opcode Group
uint16_t ocf; //Opcode Command
int event; //此Command产生的Event类型。
void *cparam; //Command 参数
int clen; //Command参数长度
void *rparam; //Response 参数
int rlen; //Response 参数长度
};
至于event.如果设置,它会被setsockopt设置于Socket。
例1:得到某个连接的Policy Setting.
HCI Spec以及~/include/net/bluetooth/hci.h中均可看到,OGF=OGF_LINK_POLICY(0x02). OCF=OCF_READ_LINK_POLICY(0x0C).
因为这个Command用来读取某个ACL连接的Policy Setting。所以输入参数即为此连接Handle.
返回参数则包含3部分,status(Command是否顺利执行), handle(连接Handle)。policy(得到的policy值)
这就又引入了一个新问题,如何得到某个ACL连接的Handle。
可以使用ioctl HCIGETCONNINFO得到ACL 连接Handle。
ioctl(dd, HCIGETCONNINFO, (unsigned long)cr);
Connect_handle =htobs(cr->conn_info->handle);
所以完整的过程如下:
struct hci_request HCI_Request;
read_link_policy_cp Command_Param;
read_link_policy_rp Response_Param;
// 1.得到ACL Connect Handle
if (ioctl(dd, HCIGETCONNINFO, (unsignedlong) cr) < 0)
{
return -1;
}
Connect_handle =htobs(cr->conn_info->handle);
memset(&HCI_Request, 0,sizeof(HCI_Request));
memset(&Command_Param, 0 ,sizeof(Command_Param));
memset(&Response_Param, 0 , sizeof(Response_Param));
// 2.填写Command输入参数
Command_e = Connect_handle;
HCI_ = OGF_LINK_POLICY; //Command组ID
HCI_ = OCF_READ_LINK_POLICY;//Command ID
HCI_m = &Command_Param;
HCI_ = READ_LINK_POLICY_CP_SIZE;
HCI_m = &Response_Param;
HCI_ = READ_LINK_POLICY_RP_SIZE;
if (hci_send_req(dd, &HCI_Request, to)< 0)
{
perror("\nhci_send_req()");
return -1;
}
//如果返回值状态不对
if(Response_s) {
return -1;
}
//得到当前policy
*policy = Response_y;
? 几个更基础的function:
static inline void bacpy(bdaddr_t *dst,const bdaddr_t *src) //bdaddr copy
static inline int bacmp(const bdaddr_t*ba1, const bdaddr_t *ba2)//bdaddr 比较
? 得到指定DongleBDAddr:
int hci_read_bd_addr(int dd, bdaddr_t*bdaddr, int to);
参数1:HCI Socket,使用hci_open_dev()打开的Socket(Dongle)。
参数2:输出参数,其中会放置bdaddr.
参数3:以milliseconds为单位的timeout.
? 读写DongleName:
int hci_read_local_name(int dd, int len,char *name, int to)
int hci_write_local_name(int dd, const char*name, int to)
参数1:HCI Socket,使用hci_open_dev()打开的Socket(Dongle)。
参数2:读取或设置Name。
参数3:以milliseconds为单位的timeout.
注意:这里的Name与IOCTL HCIGETDEVINFO 得到hci_dev_info中的name不同。
? 得到HCIVersion:
int hci_read_local_version(int dd, structhci_version *ver, int to)
? 得到已经UP的Dongle BDaddr:
int hci_devba(int dev_id, bdaddr_t *bdaddr);
dev_id: Dongle Device ID.
bdaddr:输出参数,指定Dongle如果UP, 则放置其BDAddr。
? 得到DongleInfo:
int hci_devinfo(int dev_id, structhci_dev_info *di)
dev_id: Dongle Device ID.
di: 此Dongle信息。
出错返回 -1。
注意,这个Function的做法与3.0的方法完全一致。
? 从hciX中得到X:
int hci_devid(const char *str)
str: 类似 hci0这样的字串。
如果hciX对应的Device ID(X)是现实存在且UP。则返回此设备Device ID。
? 得到BDADDR不等于参数bdaddr的Dongle Device ID:
int hci_get_route(bdaddr_t *bdaddr)
查找Dongle,发现Dongle Bdaddr不等于参数bdaddr的第一个Dongle,则返回此Dongle Device ID。
所以,如果: inthci_get_route(NULL),则得到第一个可用的Dongle Device ID。
? 将BDADDR转换为字符串:
int ba2str(const bdaddr_t *ba, char *str)
? 将自串转换为BDADDR:
int str2ba(const char *str, bdaddr_t *ba)
? inquiry 远程BluetoothDevice:
int hci_inquiry(int dev_id, int len, intnrsp, const uint8_t *lap, inquiry_info **ii, long flags)
hci_inquiry()用来命令指定的Dongle去搜索周围所有bluetooth device.并将搜索到的Bluetooth Devicebdaddr 传递回来。
参数1:dev_id:指定Dongle Device ID。如果此值小于0,则会使用第一个可用的Dongle。
参数2:len: 此次inquiry的时间长度(每增加1,则增加1.25秒时间)
参数3:nrsp:此次搜索最大搜索数量,如果给0。则此值会取255。
参数4:lap:BDADDR中LAP部分,Inquiry时这块值缺省为0X9E8B33.通常使用NULL。则自动设置。
参数5:ii:存放搜索到Bluetooth Device的地方。给一个存放inquiry_info指针的地址,它会自动分配空间。并把那个空间头地址放到其中。
参数6:flags:搜索flags.使用IREQ_CACHE_FLUSH,则会真正重新inquiry。否则可能会传回上次的结果。
返回值是这次Inquiry到的Bluetooth Device 数目。
注意:如果*ii不是自己分配的,而是让hci_inquiry()自己分配的,则需要调用bt_free()来帮它释放空间。
? 得到指定BDAddr的reomte device Name:
int hci_read_remote_name(int dd, constbdaddr_t *bdaddr, int len, char *name, int to)
参数1:使用hci_open_dev()打开的Socket。
参数2:对方BDAddr.
参数3:name 长度。
参数4:(out)放置name的位置。
参数5:等待时间。
? 读取连接的信号强度:
int hci_read_rssi(int dd, uint16_t handle,int8_t *rssi, int to)
注意,所有对连接的操作,都会有一个参数,handle.这个参数是连接的Handle。前面讲过如何得到连接Handle的。
2,DBUS协议
D-Bus用于进程间的通信或进程与内核的通信。最基本的D-Bus协议是一对一的通信协议。但在很多情况下,通信的一方是消息总线。消息总线是一个特殊的应用,它同时与多个应用通信,并在应用之间传递消息。其实可以理解为上下文的概念。
Bluez中dbus应用:文件系统启一个线程 bluetoothd,比如说配对操作,
应用程序会使用dbus 接口向bluetoothd 发送一个配对的操作,bluetoothd收到此消息之后调用内核相关的配对函数进行配对,内核配对完成之后会发送相应消息到bluetoothd,bluetoothd收到消息之后将此消息反馈给应用。
1,Bus Name
可以把Bus Name理解为连接的名称,一个Bus Name总是代表一个应用和消息总线的连接。有两种作用不同的Bus Name,一个叫公共名(well-knownnames),还有一个叫唯一名(UniqueConnectionName)。
公共名提供众所周知的服务。其他应用通过这个名称来使用名称对应的服务。可能有多个连接要求提供同个公共名的服务,即多个应用连接到消息总线,要求提供同个公共名的服务。消息总线会把这些连接排在链表中,并选择一个连接提供公共名代表的服务。可以说这个提供服务的连接拥有了这个公共名。如果这个连接退出了,消息总线会从链表中选择下一个连接提供服务。公共名是由一些圆点分隔的多个小写标志符组成的,例如“”、“”。
每个连接都有一个唯一名,当应用连接到消息总线时,消息总线会给每个应用分配一个唯一名。唯一名以“:”开头,“:”后面通常是圆点分隔的两个数字,例如“:1.0”。每个连接都有一个唯一名。在一个消息总线的生命期内,不会有两个连接有相同的唯一名。拥有公众名的连接同样有唯一名,例如在前面的图中,“”的唯一名是“:1.0”。有的连接只有唯一名,没有公众名。可以把这些名称称为私有连接,因为它们没有提供可以通过公共名访问的服务。(开发蓝牙的时候只需要了解,蓝牙使用的是公共名。)
通过公共名获取为一名例子
dbus-send--type=method_call --print-reply --system --dest=
meOwner string:
2,Object Paths
BusName确定了一个应用到消息总线的连接。在一个应用中可以有多个提供服务的对象。这些对象按照树状结构组织起来。每个对象都有一个唯一的路径(Object Paths)。或者说,在一个应用中,一个对象路径标志着一个唯一的对象。
“”有一个叫作“/org/bluez/925/hci0”的对象“”有多个对象路径。
3、Interfaces,Methods和Signals
通过对象路径,我们找到应用中的一个对象。每个对象可以实现多个接口。例如:“/org/bluez/925/hci0”的
“/TestObj”实现了以下接口:
2,dbus-send--type=method_call --print-reply --system --dest=
/ames //使用ListNames服务查看system dbus 总线名
3,dbus-send--type=method_call --print-reply --system --dest=
/spect //列出总线名下面的基本服务
4,dbus-send--type=method_call --print-reply --system --dest=
/dapters //使用system dbus 总线名为 里面的服务dapters 查看当前adapter
5,可知当前adapter object path,从而可获取当前adapter 提供的服务
dbus-send --type=method_call --print-reply--system --dest=
/org/bluez/915/spect
6,从而可以使用dbus 总线对adapter 操作,比如说获取设备参数,创建配对设备,取消配对等操作,例如获取设备参数
dbus-send --type=method_call --print-reply--system --dest=
/org/bluez/915/operties
有兴趣了解dbus 的可以看一下文档DBUS实例讲解.pdf,Dbus基础知识.docx
三、obex知识详解
OBEX是Object Exchang的简称,本来是为红外传输制定的协议,但它并不限于特定的底层传输方式,可以运行于blueteeth、usb和tcp/ip其它多种协议之上。OBEX主要是会话层协议,同时也包括应用层部分功能。它可以传输任何对象,在手机中,通常用来传输文件、图片、名片和日程等。OpenOBEX是一套开放源代码的OBEX协议实现,提供client和server两端的功能。
OBEX协议说明
1、 Connect(连接)
此操作初始化会话然后设置参数。其Request格式为
Byte 0
|
Byte 1,2
|
Byte 3
|
Byte 4
|
Byte 5,6
|
Byte 7 to n
|
0x80
|
包长度
|
OBEX版本
|
标志
|
最大OBEX包长度
|
可选Header
|
注:OBEX版本现在为1.0。
Response格式为:
Byte 0
|
Byte 1,2
|
Byte 3
|
Byte 4
|
Byte 5,6
|
Byte 7 to n
|
ResponseCode
|
包长度
|
OBEX版本
|
标志
|
最大OBEX包长度
|
可选Header
|
对于更多关于Connect的说明请参考IrOBEX
2、 Disconnect(断开当前会话)
此操作断开OBEX会话。例如断开当前FolderListing Service,然后使用Connect连接到IrMC Sync Service实现同步通讯薄等功能。
Disconnect格式为:
Byte 0
|
Bytes 1,2
|
Bytes 3 to n
|
0x81
|
包长度
|
可选Header
|
成功的断开会返回0Xa0,拒绝会返回0xD3
3、 Put操作
|
Put操作从客户端发送一个对象到服务端。一般至少含有Name和Length两个Header。对于文件而言有可能还有Date/Time Header。
Put操作的格式如下:
Byte 0
|
Bytes 1,2
|
Bytes 3 to n
|
0x02(当FinalBit设置时为0x82)
|
PacketLength包长度
|
一组Header
|
回应格式如下:
Byte 0
|
Bytes 1,2
|
Bytes 3 to n
|
ResponseCode(要求继续为0x90;成功为0xA0)
|
包长度
|
可选Header
|
关于Put操作的更详细的用法将在文件传输部分作解释。
4、 Get操作
Get操作从服务端返回一个对象。
Get操作的格式如下:
Byte 0
|
Bytes 1,2
|
Bytes 3 to n
|
0x03
|
包长度
|
一组Header
|
回应格式如下:
Byte 0
|
Bytes 1,2
|
Bytes 3 to n
|
Response Code
|
包长度
|
可选Header
|
关于Get操作更详细的用法将在文件传输部分作解释。
5、 Abort操作
Abort操作中断一个多包操作(例如发送一个大文件)。Abort操作可以包含描述中断原因的Description Header。
Abort操作的格式如下:
Byte 0
|
Bytes 1,2
|
Bytes 3 to n
|
0xFF
|
包长度
|
可选Header
|
Abort对应的应该是一个成功的操作(0xA0),指明这个操作已被接收并且服务端已经重新与客户端同步。如果返回的另外的值,客户端应该Disconnect。
6、 SetPath
SetPath操作用于切换对方的路径。通常使用一个Name Header用于指定对方路径名称。如果为空,则返回默认目录,通常为根目录
SetPath操作格式如下:
Byte 0
|
Bytes 1,2
|
Byte 3
|
Byte 4
|
Bytes 5 to n
|
0x85
|
包长度
|
Flags
|
Constants(常数)
|
可选Header
|
注:Flags可以设置Bit0和Bit1。Bit0表示退回到上一层目录;Bit1表示如果目录不存在就创建一个目录,否则返回一个错误。
回应格式如下:
Byte 0
|
Bytes 1,2
|
Bytes 3 to n
|
ResponseCode
|
包长度
|
可选Response Header
|
Header 定义
HI
|
Header名称
|
描述
|
0xC0
|
Count
|
连接中用于指名对象的数量。
|
0x01
|
Name
|
对象的名字。一般为文件名。
|
0x42
|
Type
|
对象的类型。例如text,html,binary,manufacture specific
|
0x44
0xC4
|
Time
|
时间戳。ISO 8601版本
时间戳。4Byte版本(用于兼容)
|
0x05
|
Description
|
对对象的文本描述
|
0x46
|
Target
|
操作的目的服务名
|
0x47
|
HTTP
|
一个HTTP1.x头
|
0x48
|
Body
|
对象的一部分
|
0x49
|
End of body
|
对象的最后一部分
|
0x4A
|
Who
|
OBEX Application标识,用于表明是否是同一个应用。
|
0xCB
|
Connection ID
|
用于OBEX多路连接的标识
|
0x4C
|
eters
|
扩展的应用层请求和回复信息
|
0x4D
|
enge
|
Authentication digest-challenge
|
0x4E
|
nse
|
Authentication digest-response
|
0x4F
|
Object Class
|
对象的OBEX对象类
|
0x10 to 0x2F
|
Reserved
|
保留
|
0x30 to 0x3F
|
User defined
|
用户自定义的。
|
opcode
Opcode(w/high bit set)
|
定义
|
意义
|
0x80 *
|
Connect
|
连接
|
0x81 *
|
Disconnect
|
断开连接
|
0x02(0x82)
|
Put
|
发送一个对象
|
0x03(0x83)
|
Get
|
取得一个对象
|
0x04(0x84)
|
Reserved
|
保留的
|
0x85 *
|
SetPath
|
设置路径
|
0xFF *
|
Abort
|
取消当前的操作
|
0x06到0x0F
|
Reserved
|
作为扩展保留
|
0x10到0x1F
|
User definable
|
用户自定义的
|
ResponseCode
ResponseCode
|
定义
|
0x10(0x90)
|
Continue(继续)
|
0x20(0xA0)
|
OK,Success
|
0x40(0xC0)
|
Bad Request(服务端不明白Request)
|
0x41(0xC1)
|
Unauthorized(未授权的)
|
0x43(0xC3)
|
Fobidden(禁止——服务器明白Request,但拒绝)
|
|
在有小米2 给机顶盒发送文件的打印
四、蓝牙交叉编译
1,ST交叉编译
#! /bin/bash
#指定临时安装位置到/usr/share/bluetooth,最后会被复制到install目录
TMP_PATH=/usr/share/bluetooth
INSTALL_PATH=`pwd`/install
# 清除之间编译的文件,创建安装文件夹
rm -rf install
mkdir install
rm -rf $TMP_PATH/*
rm -rf $INSTALL_PATH/*
mkdir -p $INSTALL_PATH/root
mkdir -p $INSTALL_PATH/etc
mkdir -p $INSTALL_PATH/include
mkdir -p $INSTALL_PATH/usr/bin
mkdir -p $INSTALL_PATH/usr/sbin
mkdir -p $INSTALL_PATH/$TMP_PATH
mkdir -p $INSTALL_PATH/$TMP_PATH/lib
mkdir -p $INSTALL_PATH/$TMP_PATH/share/alsa
mkdir -p $INSTALL_PATH/$TMP_PATH/var
mkdir -p $INSTALL_PATH/$TMP_PATH/etc
指定编译器路径
PATH=$PATH:/opt/STM/STLinux-2.3/devkit/sh4/bin:/opt/STM/ST40R4.4.0:/opt/STM/ST40R4.4.0/bin:/opt/STM/STMCR1.4.0/bin:/opt/STM/STWORKBENCHR4.0.1:/opt/STM/STWORKBENCHR4.0.1/bin
export PATH
#编译alsa、bluez及依赖库
rm -rf expat-2.0.1 && tar
cd expat-2.0.1
./configure --host=sh4-linux--prefix=/usr/share/Bluetooth
make -j 20 && make install&& cd -
rm -rf zlib-1.2.5 && tar
cd zlib-1.2.5
CC=sh4-linux-gcc ./configure --prefix=/usr/share/Bluetooth
make -j 20 && make install&& cd -
rm -rf alsa-lib-1.0.23 && tar 2
cd alsa-lib-1.0.23
./configure --host=sh4-linux--prefix=/usr/share/bluetooth --enable-shared --disable-python--with-versioned=no
make -j 20 && make install&& cd -
rm -rf alsa-utils-1.0.23 && tarxjvf 2
cd alsa-utils-1.0.23
./configure --host=sh4-linux--prefix=/usr/share/bluetooth CPPFLAGS=-I/usr/share/bluetooth/includeLDFLAGS=-L/usr/share/bluetooth/lib --disable-alsamixer --disable-xmlto--disable-nls
make -j 20 && make install&& cd -
rm -rf dbus-1.2.26 && tar
cd dbus-1.2.26
./configure --host=sh4-linux--prefix=/usr/share/bluetooth CPPFLAGS=-I/usr/share/bluetooth/includeLDFLAGS=-L/usr/share/bluetooth/lib --with-xml=expat --without-x--enable-selinux=no
make -j 20 && make install&& cd -
rm -rf glib-2.24.0 && tar 2
cd glib-2.24.0
./configure --host=sh4-linux--prefix=/usr/share/bluetooth CFLAGS=-I/usr/share/bluetooth/includeLDFLAGS=-L/usr/share/bluetooth/lib glib_cv_stack_grows=yes glib_cv_uscore=yesac_cv_func_posix_getpwuid_r=yes ac_cv_func_posix_getgrgid_r=yes
make -j 20 && make install&& cd -
rm -rf bluez-4.95 && tar
cd bluez-4.95
./configure --host=sh4-linux--prefix=/usr/share/bluetoothPKG_CONFIG_PATH=/usr/share/bluetooth/lib/pkgconfigALSA_CFLAGS=-I/usr/share/bluetooth/include ALSA_LIBS=-L/usr/share/bluetooth/lib--disable-gstreamer --enable-hid2hci --enable-hidd --enable-alsa --enable-audio--enable-service --enable-tools --enable-serial --enable-input --enable-static--enable-shared --enable-test
make -j 20 && make install&& cd -
# bluez安装后的头文件hic_lib.h需要修改,用已修改好的替换
cp modify/hci_all/include/bluetooth/
rm -rf openobex-1.3 && tar
cd openobex-1.3
./configure --prefix=$TMP_PATH--host=sh4-linux CC=sh4-linux-gcc CFLAGS=-I$TMP_PATH/includeLDFLAGS=-L$TMP_PATH/lib --enable-apps BLUEZ_LIBS=-lbluetooth
# 打开obex的蓝牙选项
cp ../modify/obex_config.h config.h
make -j 20 && make install&& cd -
cp -fr cfg/* $INSTALL_PATH/
#创建两个用户 messagebus和 lp
# 使用dbus 需要创建messagebus 用户,lp 用户
sed '$amessagebus:$1$84bC0OFA$m9ubAM6KGAmarFZOlbvCz.:1000:1000:LinuxUser,,,:/home/messagebus:/bin/sh' $INSTALL_PATH/etc/passwd >./tmp
sed '$alp:$1$pytSBKNZ$95Roy6wGH3V444ZOyZwjK0:1001:1001:Linux User,,,:/home/lp:/bin/sh'./tmp >$INSTALL_PATH/etc/passwd
sed '$a messagebus:x:1000:'$INSTALL_PATH/etc/group >./tmp
sed '$a lp:x:1001:' ./tmp>$INSTALL_PATH/etc/group
#拷贝相关文件到install 目录下
cd $TMP_PATH/bin
cp -lrf dbus-daemon hidd l2ping hcitoolsdptool rfcomm dbus-cleanup-sockets dbus-launch dbus-monitor dbus-senddbus-uuidgen aplay arecord $INSTALL_PATH/usr/bin/
cd -
cd $TMP_PATH
cp -rf $TMP_PATH/include/*$INSTALL_PATH/include
cp -rf $TMP_PATH/sbin/*$INSTALL_PATH/usr/sbin
cp -rf $TMP_PATH/lib/*$INSTALL_PATH/$TMP_PATH/lib
cp -rf $TMP_PATH/share/alsa/*$INSTALL_PATH/$TMP_PATH/share/alsa
cp -rf $TMP_PATH/var/*$INSTALL_PATH/$TMP_PATH/var
cp -rf $TMP_PATH/etc/*$INSTALL_PATH/$TMP_PATH/etc
cd -
#创建bluez启动自动化脚本
cd $INSTALL_PATH/etc
echo "rm -rf$TMP_PATH/var/run/dbus/pid" >bluez_init
echo"LD_LIBRARY_PATH=\$LD_LIBRARY_PATH:$TMP_PATH/lib
export LD_LIBRARY_PATH">>bluez_init
echo "touch$TMP_PATH/var/lib/dbus/machine-id
dbus-uuidgen >$TMP_PATH/var/lib/dbus/machine-id">>bluez_init
echo "dbus-daemon--config-file=$TMP_PATH/etc/dbus-1/" >>bluez_init
echo "bluetoothd --udev">>bluez_init
cd -
rm tmp
编译完成之后还需要将install目录下文件挪到rootfs里面,以下是在stapp makefile里面添加的部分逻辑,基本功能是编译九州添加的逻辑代码,将以上编译的文件挪到rootfs里面。
ALSA_DIR = ../bluetooth
INCLUDE_DIR = ../include
LIB_DIR = ../share/target7162_A27/lib
ROOTFS_DIR = ../share/target7162_A27
BLUEZ_API_DIR_HI = $(ALSA_DIR)/hi_api
BLUEZ_API_DIR = $(ALSA_DIR)/bluetooth_utils
bluetooth:
# cd$(ALSA_DIR)/src;tar -zxf bluez_;chmod 755 -R install;cd -
# @echo"tar -zxf bluez_ done"
@-cp-rf $(ALSA_DIR)/src/install/include/* $(ALSA_DIR)/include
@-cp-rf $(ALSA_DIR)/src/install/usr/share/bluetooth/lib/* $(ALSA_DIR)/lib
make-C $(BLUEZ_API_DIR) //九州内部代码
make-C $(BLUEZ_API_DIR_HI) //九州内部代码
bluez_install: bluetooth
@-cp-rvf $(BLUEZ_API_DIR)/*.h $(INCLUDE_DIR)
@-cp-rvf $(BLUEZ_API_DIR)/lib* $(LIB_DIR)
@-cp-rvf $(BLUEZ_API_DIR)/*.so $(ROOTFS_DIR)/usr/lib
@-cp-rvf $(BLUEZ_API_DIR)/*.a $(ROOTFS_DIR)/usr/lib
@-cp-rvf $(BLUEZ_API_DIR_HI)/bluetooth*.h $(INCLUDE_DIR)
@-cp-rvf $(BLUEZ_API_DIR_HI)/lib* $(LIB_DIR)
@-cp-rvf $(BLUEZ_API_DIR_HI)/*.so $(ROOTFS_DIR)/usr/lib
@-cp-rvf $(BLUEZ_API_DIR_HI)/*.a $(ROOTFS_DIR)/usr/lib
mkdir-p $(INCLUDE_DIR)/alsa_bluez
mkdir-p $(LIB_DIR)/alsa_bluez
@-cp-rf $(ALSA_DIR)/src/install/include/* $(INCLUDE_DIR)/alsa_bluez
@-cp-rf $(ALSA_DIR)/src/install/usr/share/bluetooth/lib/* $(LIB_DIR)/alsa_bluez
@-cp-rf $(ALSA_DIR)/bluetooth_utils/bt_test $(ROOTFS_DIR)/bin
@-cp-rf $(ALSA_DIR)/src/install/etc/* $(ROOTFS_DIR)/etc
# @-cp-rf $(ALSA_DIR)/src/install/kmod/* $(ROOTFS_DIR)/kmod
foraaa in $$(find $(ALSA_DIR)/src/install/usr/share/bluetooth/lib -name"*.a");do rm -rf $$aaa;done
@-cp-rf $(ALSA_DIR)/src/install/usr/* $(ROOTFS_DIR)/usr
cp../bluetooth/src/expat-2.0.1/.libs/.1.5.2../share/target7162_A27/lib
,在启用bluez_init之前还需要对蓝牙残留文件做删除。
以下代码是在profile 添加的
if [ -f /usr/share/bluetooth/var/run/]; then
rm/usr/share/bluetooth/var/run/
fi
rm -rf/usr/share/bluetooth/var/lib/bluetooth/*
rm -rf /usr/share/bluetooth/var/run/dbus/*
cd /etc
source bluez_init
如果启动的时候出现找不到.2,需要做一下操作,其他错误类似
/usr/share/bluetooth/lib # ln .3.11.3 .2
如果有些dongle 插入之后一直打印 unkown handle。。。错误,是因为kernel版本过低
需要修改kernel/driver/bluetooth/hci_usb.c
// {USB_DEVICE(0x0a12, 0x0001), .driver_info = HCI_CSR },
{USB_DEVICE(0x0a12, 0x0001), .driver_info = HCI_BROKEN_ISOC },
2,HISI编译
在这里就不重复说了。
. ./655V300DDR256M
如果是下载最新的标准版,需要先make build(生成rootfs_full)
cd/home/work/HISI/3716SDK0A1/source/msp/component/alsa/src
. ./(生成alsa/src/install)
cd /home/work/HISI/3716SDK0A1
gmake bluez_install (这个只是把alsa/src/install里面的东西拷贝到rootfs_full里面)
其中需要修改/home/work/HISI/3716SDK0A1的makefile
1, 拷贝头文件
2, 拷贝lib
3, 修改etc 下面的passwd group
Passwd修改如下
Group修改如下
4, 在kmod 里面添加bluez_init bluetoothd_server
Bluez_init
Bluetooth_server
5, 删除.a文件
6, 拷贝usr 下面的命令动态库 配置文件到文件系统下面
7, 拷贝obex_test agent 命令到文件系统下面
8,gmake rootfs
8, 修改bin 目录下rcS_MV300 makefile
Rcs修改如下
这里的makefile 是ysstb 目录下面的makefile,在gmake all 的时候添加BT_CFLAGS,BT_LDFLAGS
10,修改/home/work/HISI/3716SDK0A1/pub/hi3716mv300/rootbox,将.asoundrc 放在根目录下
11,编译驱动(将驱动部分代码蓝牙编译)
Driver 下makefile 修改
在link_path 添加 driver$(LOCALIZATION_STRING)/ysbluetooth
同时 也需要修改,把蓝牙的相关目录添加进去
12,在ysstb 下gmake
五、蓝牙在HISI盒子上使用
在机顶盒启动之后 ps 能看到dbus-daemon 和bluetoothd –udev进程
? 蓝牙扫描
? 蓝牙配对
? 文件传输
如果发送文件,需要获取远端OBEX Object Push 服务channel。
# sdptool browse 49:F2:1C:4E:66:12
Browsing 49:F2:1C:4E:66:12 ...
Service Name: Voiceg ateway
Service RecHandle: 0x10001
Service Class ID List:
"Handsfree Audio Gateway" (0x111f)
"Generic Audio" (0x1203)
Protocol Descriptor List:
"L2CAP" (0x0100)
"RFCOMM" (0x0003)
Channel: 2
Language Base Attr List:
code_ISO639: 0x656e
encoding: 0x6a
base_offset: 0x100
Profile Descriptor List:
"Handsfree" (0x111e)
Version: 0x0105
Service Name: AUDIO Gateway
Service RecHandle: 0x10002
Service Class ID List:
"Headset Audio Gateway" (0x1112)
"Generic Audio" (0x1203)
Protocol Descriptor List:
"L2CAP" (0x0100)
"RFCOMM"(0x0003)
Channel: 1
Language Base Attr List:
code_ISO639: 0x656e
encoding: 0x6a
base_offset: 0x100
Profile Descriptor List:
"Headset" (0x1108)
Version: 0x0100
Service Name: Serial Port0
Service RecHandle: 0x10003
Service Class ID List:
"Serial Port" (0x1101)
Protocol Descriptor List:
"L2CAP" (0x0100)
"RFCOMM" (0x0003)
Channel: 11
Language Base Attr List:
code_ISO639: 0x656e
encoding: 0x6a
base_offset: 0x100
Service Name: Dial-up Networking
Service RecHandle: 0x10004
Service Class ID List:
"Dialup Networking" (0x1103)
"Generic Networking" (0x1201)
Protocol Descriptor List:
"L2CAP" (0x0100)
"RFCOMM" (0x0003)
Channel: 9
Language Base Attr List:
code_ISO639: 0x656e
encoding: 0x6a
base_offset: 0x100
Profile Descriptor List:
"Dialup Networking" (0x1103)
Version: 0x0100
Service Name: Advanced Audio
Service RecHandle: 0x10005
Service Class ID List:
"Audio Source" (0x110a)
Protocol Descriptor List:
"L2CAP" (0x0100)
PSM: 25
"AVDTP" (0x0019)
uint16: 0x100
Profile Descriptor List:
"Advanced Audio" (0x110d)
Version: 0x0100
Service RecHandle: 0x10006
Service Class ID List:
"AV Remote Target" (0x110c)
Protocol Descriptor List:
"L2CAP" (0x0100)
PSM: 23
"AVCTP" (0x0017)
uint16: 0x100
Profile Descriptor List:
"AV Remote" (0x110e)
Version: 0x0100
Service Name: Human interface device
Service Description: Human interface device
Service Provider: Mediatek Inc
Service RecHandle: 0x10007
Service Class ID List:
"Human Interface Device" (0x1124)
Protocol Descriptor List:
"L2CAP" (0x0100)
PSM: 17
"HIDP" (0x0011)
Language Base Attr List:
code_ISO639: 0x656e
encoding: 0x6a
base_offset: 0x100
Profile Descriptor List:
"Human Interface Device" (0x1124)
Version: 0x0100
Service Name: OBEX Object Push
Service RecHandle: 0x10008
Service Class ID List:
"OBEX Object Push" (0x1105)
Protocol Descriptor List:
"L2CAP" (0x0100)
"RFCOMM" (0x0003)
Channel: 4
"OBEX" (0x0008)
Language Base Attr List:
code_ISO639: 0x656e
encoding: 0x6a
base_offset: 0x100
Service Name: OBEX File Transfer
Service RecHandle: 0x10009
Service Class ID List:
"OBEX File Transfer" (0x1106)
Protocol Descriptor List:
"L2CAP" (0x0100)
"RFCOMM" (0x0003)
Channel: 3
"OBEX" (0x0008)
Language Base Attr List:
code_ISO639: 0x656e
encoding: 0x6a
base_offset: 0x100
Service Name: Imaging
Service RecHandle: 0x1000a
Service Class ID List:
"Imaging Responder" (0x111b)
Protocol Descriptor List:
"L2CAP" (0x0100)
"RFCOMM" (0x0003)
Channel: 6
"OBEX" (0x0008)
Language Base Attr List:
code_ISO639: 0x656e
encoding: 0x6a
base_offset: 0x100
Profile Descriptor List:
"Imaging" (0x111a)
Version: 0x0100
Service Name: Imaging referenced Object
Service RecHandle: 0x1000b
Service Class ID List:
"Imaging Referenced Objects" (0x111d)
Protocol Descriptor List:
"L2CAP" (0x0100)
"RFCOMM" (0x0003)
Channel: 7
"OBEX" (0x0008)
Language Base Attr List:
code_ISO639: 0x656e
encoding: 0x6a
base_offset: 0x100
Profile Descriptor List:
"Imaging" (0x111a)
Version: 0x0100
发送文件
# obex_test -b 49:F2:1C:4E:66:12 4
Using Bluetooth RFCOMM transport
OBEX Interactive test client/server.
> c //连接
Connect OK!
Version: 0x10. Flags: 0x00
> x //发送文件
PUSH filename> //需要发送的文件名字
接收文件
sdptool browse local 查看是否有 OBEXObject Push
如果没有 需要添加
sdptool add OPUSH
obex_test –b //接收文件
? 蓝牙耳机使用
蓝牙耳机使用需要在~/ 目录下面建立.asoundrc文件
内容如下:
_record{
type plug
slave {
pcm "bt_record_hw"
}
}
_record_hw{
type bluetooth
device 70:F1:A1:EE:C5:70
profile voice
}
_play{
type plug
slave {
pcm "bt_play_hw"
}
}
_play_hw{
type bluetooth
device 70:F1:A1:EE:C5:70
}
其中文件中mac 地址为蓝牙耳机mac 地址
配对之后
Apply –D bt_play //播放
Arecord –D bt_record –f S16_LE // 录制文件
备注:
蓝牙kernel 底层调试
? 修改$(kerneldir)/driver/bluetooth/Kconfig
在文件结尾添加
configCONFIG_BT_HCI_CORE_DEBUG
bool "CONFIG_BT_HCI_CORE_DEBUG"
default y
help
CONFIG_BT_HCI_CORE_DEBUG.
config CONFIG_BT_RFCOMM_DEBUG
bool "CONFIG_BT_RFCOMM_DEBUG"
default y
help
CONFIG_BT_RFCOMM_DEBUG.
config CONFIG_BT_SOCK_DEBUG
bool "CONFIG_BT_SOCK_DEBUG"
default y
help
CONFIG_BT_SOCK_DEBUG.
config CONFIG_BT_HCI_SOCK_DEBUG
bool "CONFIG_BT_SOCK_DEBUG"
default y
help
CONFIG_BT_HCI_SOCK_DEBUG.
config CONFIG_BT_HCIUSB_DEBUG
bool "CONFIG_BT_HCIUSB_DEBUG"
default y
help
CONFIG_BT_HCIUSB_DEBUG.
config CONFIG_BT_L2CAP_DEBUG
bool "CONFIG_BT_L2CAP_DEBUG"
default y
help
CONFIG_BT_L2CAP_DEBUG.
? 同时在hci_core.c或者 hci_usb.c 或者其他蓝牙相关文件里面加上以下代码
#undef BT_DBG
#undef BT_ERR
#define BT_DBG(fmt, arg...)printk("%s: " fmt "\n" , __FUNCTION__ , ## arg)
#defineBT_ERR(fmt, arg...) printk("%s: " fmt "\n" , __FUNCTION__ ,## arg)
HISI NFS 调试
? 打开NFS 挂载文件系统功能
注释3716SDK0A1\Makefile中的603行,因为每次编译HISI都会通过603使用默认的.config
进入Y:\work\HISI\bluetooth\3716SDK0A1\source\osdrv\kernel\linux-2.6.35
Make menuconfig 修改kernel配置
Networking options 下面
Network file systems下面
然后开机启动盒子进入fastboot,输入以下命令,然后就可以方面的使用NFS文件系统了
set bootargs mem=128Mconsole=ttyAMA0,115200 root=/dev/nfs nfsroot= mmz=ddr,0,0x88000000,128M DmxPoolBufSize=0x200000LogBufSize=0x80000mtdparts=hinand:768K(fastboot),256K(bootargs),1M(stbinfo),8M(loader),8M(loaderbak),5M(kernel),50M(rootfs),35M(elf),8M(ui),8M(flashdata),256K(baseparam),1M(logo),2M(fastplay),-(others)