2004年09月23日

首先要记住通信是双方的,双方必须协商通信参数,或者说通信参数一致.
要在redhat下使用串口设备, 串口设备的基本通信参数就是波特率.
好了,正式开始;
1.在windows平台下烧写蓝牙模块,
1.1. execute blueflash
    烧写hci_bc02.xdv文件
1.2. execute pstool
    配置参数
       hci host interface: bcsp
       uart baud rate: 115200 (别的值也可以,只要与主机端匹配)
1.3. execute bluetest
    we can test is paras correct to write?
2.在redhat9下使用串口蓝牙设备

2.1. 加载模块

modprobe bluez

modprobe l2cap

modprobe hci_uart

(注意,l2cap和hci_uart模块的加载顺序可以交换)

hcid

hciattach -n /dev/tts/2 bcsp 115200 &

sdpd(此命令可以暂不执行)

hciconfig

运行hciconfig后,会出现串口蓝牙设备的信息

开发板买来后的基本工作:

1.在开发板提供的内核上贴bluez-kernel patch;(注意内核的版本号)

打完补丁后,内核要重新交叉编译;

2.交叉编译bluez-lib以及bluez-util;(主要是调整编译选项,注意安装目录)

应该请教一下别人;

3.阅读使用蓝牙上网的文章

5.网络浏览器

4.可不可以移植gnome or kde的蓝牙子系统到嵌入式平台上去呢?

需要发信询问。

hcid的配置文件可以随意指定,参看man hcid;

hcid -f /xxx/xxx/hcid.conf

-f config file
Use alternate configuration file instead of /etc/bluetooth/hcid.conf

2004年09月22日

memcpy()

SYNOPSIS 

#include <string.h> void *memcpy(void *dest, const void *src, size_t n); 

The memcpy() function copies n bytes from memory area src to memory area dest. The memory areas may not overlap.

拷贝以src指向的地址空间开始的n byte;

RETURN VALUE 

The memcpy() function returns a pointer to dest.

协议一般都实现为模块,

profile一般都实现为用户程序。

理解:profile是对协议的具体使用,用户程序使用相应的模块提供的函数;

如rfcomm模块,dund用户程序

bnep模块,pand用户程序

另外,一般的bluez-util用户程序都由相应的配置文件;

如/etc/hcid.conf配置hcid

 

bluez内核协议栈在linux的两个目录下:

/dirvers/bluetooth:

/net/bluetooth:

阅读对以目录下的makefile文件:

/dirvers/bluetooth/makefile:

#
# Makefile for the Linux Bluetooth HCI device drivers
#

//可见dirvers主要提供各种接口的硬件驱动;

O_TARGET := bluetooth.o

list-multi := hci_uart.o

obj-$(CONFIG_BLUEZ_HCIUSB) += hci_usb.o
obj-$(CONFIG_BLUEZ_HCIVHCI) += hci_vhci.o

obj-$(CONFIG_BLUEZ_HCIUART)  += hci_uart.o
uart-y     := hci_ldisc.o
uart-$(CONFIG_BLUEZ_HCIUART_H4)  += hci_h4.o
uart-$(CONFIG_BLUEZ_HCIUART_BCSP) += hci_bcsp.o

obj-$(CONFIG_BLUEZ_HCIBFUSB) += bfusb.o

obj-$(CONFIG_BLUEZ_HCIDTL1) += dtl1_cs.o
obj-$(CONFIG_BLUEZ_HCIBT3C) += bt3c_cs.o
obj-$(CONFIG_BLUEZ_HCIBLUECARD) += bluecard_cs.o
obj-$(CONFIG_BLUEZ_HCIBTUART) += btuart_cs.o

include $(TOPDIR)/Rules.make

hci_uart.o: $(uart-y)
 $(LD) -r -o $@ $(uart-y)

又因为在redhat下使用usb dongle的步骤:

[root@localhost man5]# modprobe bluez
[root@localhost man5]# modprobe l2cap
[root@localhost man5]# modprobe hci_usb
[root@localhost man5]# hcid
[root@localhost man5]# sdpd
[root@localhost man5]# hciconfig

bluez模块的堆叠方式:bluez–>l2cap–>hci-(XXX)–>上层协议模块(maybe bnep,……)

/net/bluetooth/makefile:

#
# Makefile for the Linux Bluetooth subsystem
#

//蓝牙子系统,可以理解为linux下蓝牙系统实现为网络接口;

O_TARGET    := bluetooth.o

list-multi  := bluez.o
export-objs := syms.o l2cap.o

bluez-objs  := af_bluetooth.o hci_core.o hci_conn.o hci_event.o hci_sock.o lib.o syms.o

obj-$(CONFIG_BLUEZ) += bluez.o
obj-$(CONFIG_BLUEZ_L2CAP) += l2cap.o
obj-$(CONFIG_BLUEZ_SCO) += sco.o

subdir-$(CONFIG_BLUEZ_RFCOMM) += rfcomm
subdir-$(CONFIG_BLUEZ_BNEP) += bnep
subdir-$(CONFIG_BLUEZ_CMTP) += cmtp
subdir-$(CONFIG_BLUEZ_HIDP) += hidp

ifeq ($(CONFIG_BLUEZ_RFCOMM),y)
obj-y += rfcomm/rfcomm.o
endif

ifeq ($(CONFIG_BLUEZ_BNEP),y)
obj-y += bnep/bnep.o
endif

ifeq ($(CONFIG_BLUEZ_CMTP),y)
obj-y += cmtp/cmtp.o
endif

ifeq ($(CONFIG_BLUEZ_HIDP),y)
obj-y += hidp/hidp.o
endif

include $(TOPDIR)/Rules.make

bluez.o: $(bluez-objs)
 $(LD) -r -o $@ $(bluez-objs)

可以看出此文件生成以下内核模块:(从下往上)

bluez.o; l2cap; sco.o; rfcomm.o; bnep.o; cmtp.o; hidp.o

modprobe bluez
modprobe hci_uart UART support. Optional
modprobe hci_usb USB support. Optional
modprobe l2cap

与王文东的调用顺序不一样,那个能工作呢?

应该是有一定的顺序加载模块的,就如insmod可能不好使;

2004年09月21日

从readme知道,u-boot 支持smdk2410开发板;

modprobe 可以加载模块和其向关联的模块;
insmod 就没有这个功能;
The behavior of modprobe(8) (and depmod(8) ) can be modified
by the (optional) configuration file /etc/modules.conf.
modules.conf文件是modprobe程序的配置文件。
modules.conf文件的语法:
alias etho ne
//别名,我们使用modprobe etho加载模块时,实际上系统会加载ne.o模块;
keep
path=A_PATH
//path,指定在那查找模块
modules.conf的手册页
http://www.die.net/doc/linux/man/man5/modules.conf.5.html

bluez实现是网络设备,由/net/bluetooth推断出,
linux的网络子系统被设计成完全协议无关。
模块加载:
网络驱动程序为每个新检测到的接口在一个网络设备的全局列表中插入一个数据结构。
我猜测:(摘自hci_core.c)
 struct hci_inquiry_req ir;
 struct hci_dev *hdev;
hdev_list是hci网络设备的全局列表,
extern struct list_head hdev_list;全局变量
hci_dev是每一个hci设备对应的数据结构;
struct hci_dev {
 struct list_head list;//构成hci_dev设备链表
 spinlock_t lock;
 atomic_t  refcnt;

 char  name[8];
 unsigned long flags;
 __u16  id;
 __u8   type;
 bdaddr_t bdaddr;//设备地址
 __u8  features[8];

 __u16  pkt_type;
 __u16  link_policy;
 __u16  link_mode;

 unsigned long quirks;

 atomic_t  cmd_cnt;
 unsigned int  acl_cnt;
 unsigned int  sco_cnt;

 unsigned int acl_mtu;
 unsigned int  sco_mtu;
 unsigned int acl_pkts;
 unsigned int sco_pkts;

 unsigned long   cmd_last_tx;
 unsigned long   acl_last_tx;
 unsigned long   sco_last_tx;
 
 struct tasklet_struct  cmd_task;
 struct tasklet_struct rx_task;
 struct tasklet_struct  tx_task;

 struct sk_buff_head rx_q;
 struct sk_buff_head  raw_q;
 struct sk_buff_head  cmd_q;

 struct sk_buff      *sent_cmd;

 struct semaphore req_lock;
 wait_queue_head_t req_wait_q;
 __u32   req_status;
 __u32   req_result;

 struct inquiry_cache  inq_cache;
 struct conn_hash  conn_hash;

 struct hci_dev_stats  stat;

 void   *driver_data;
 void   *core_data;

 atomic_t   promisc;

 int (*open)(struct hci_dev *hdev);
 int (*close)(struct hci_dev *hdev);
 int (*flush)(struct hci_dev *hdev);
 int (*send)(struct sk_buff *skb);
 void (*destruct)(struct hci_dev *hdev);
 int (*ioctl)(struct hci_dev *hdev, unsigned int cmd, unsigned long arg);
};
加载模块,是usb dongle设备正常使用的步骤;
[root@localhost man5]# modprobe bluez
[root@localhost man5]# modprobe l2cap
[root@localhost man5]# modprobe hci_usb
[root@localhost man5]# hcid
[root@localhost man5]# sdpd
[root@localhost man5]# hciconfig
最底层的模块式bluez,
#ifdef MODULE
module_init(bluez_init);
int bluez_init(void)
{
 BT_INFO(“BlueZ Core ver %s Copyright (C) 2000,2001 Qualcomm Inc”,
   VERSION);
 BT_INFO(“Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>”);

 proc_mkdir(“bluetooth”, NULL);

 sock_register(&bluez_sock_family_ops);

 /* Init sockets */
 hci_sock_init();

 return 0;
}
struct net_proto_family bluez_sock_family_ops =
{
 PF_BLUETOOTH, bluez_sock_create
};

static int bluez_sock_create(struct socket *sock, int proto)
{
 if (proto >= BLUEZ_MAX_PROTO)
  return -EINVAL;

#if defined(CONFIG_KMOD)
 if (!bluez_proto[proto]) {
  char module_name[30];
  sprintf(module_name, “bt-proto-%d”, proto);
  request_module(module_name);
 }
#endif

 if (!bluez_proto[proto])
  return -ENOENT;

 return bluez_proto[proto]->create(sock, proto);
 //proto:协议代码
 //socket:当前协议的socket

2004年09月20日

static int __init bnep_init_module(void)
{
 l2cap_load();

 bnep_crc32_init();
 bnep_sock_init();

……

//上面这条语句将注册bnep的socket操作函数集;

int bnep_sock_init(void)
{
 bluez_sock_register(BTPROTO_BNEP, &bnep_sock_family_ops);
 return 0;
}
 

//可以看到所有的蓝牙协议都是用bluez_sock_register注册对应的socket操作函数集;

#define BTPROTO_L2CAP   0
#define BTPROTO_HCI     1
#define BTPROTO_SCO    2
#define BTPROTO_RFCOMM 3
#define BTPROTO_BNEP 4
#define BTPROTO_CMTP 5
#define BTPROTO_HIDP 6

#define SOL_HCI     0
#define SOL_L2CAP   6
#define SOL_SCO     17
#define SOL_RFCOMM  18

static struct net_proto_family bnep_sock_family_ops = {
 family: PF_BLUETOOTH,
 create: bnep_sock_create
};

static int bnep_sock_create(struct socket *sock, int protocol)
{
 struct sock *sk;

 BT_DBG(“sock %p”, sock);

 if (sock->type != SOCK_RAW)
  return -ESOCKTNOSUPPORT;

 sock->ops = &bnep_sock_ops;

……

static struct proto_ops bnep_sock_ops = {
 family:     PF_BLUETOOTH,
 release:    bnep_sock_release,
 ioctl:      bnep_sock_ioctl,
 bind:       sock_no_bind,
 getname:    sock_no_getname,
 sendmsg:    sock_no_sendmsg,
 recvmsg:    sock_no_recvmsg,
 poll:       sock_no_poll,
 listen:     sock_no_listen,
 shutdown:   sock_no_shutdown,
 setsockopt: sock_no_setsockopt,
 getsockopt: sock_no_getsockopt,
 connect:    sock_no_connect,
 socketpair: sock_no_socketpair,
 accept:     sock_no_accept,
 mmap:       sock_no_mmap
};


 

static void __exit bnep_cleanup_module(void)
{
 bnep_sock_cleanup();
 bnep_crc32_cleanup();
}

module_init(bnep_init_module);

加载bnep模块时,调用bnep_init_module函数,
module_exit(bnep_cleanup_module);

MODULE_DESCRIPTION(“BlueZ BNEP ver ” VERSION);
MODULE_AUTHOR(“David Libault <
david.libault@inventel.fr>, Maxim Krasnyanskiy <maxk@qualcomm.com>”);
MODULE_LICENSE(“GPL”);

bluez-utils->main->

while ((opt=getopt_long(argc, argv, main_sopts, main_lopts, NULL)) != -1) {

getopt_long and getopt函数的man手册在

http://jamesthornton.com/linux/man/getopt_long.3.html

Name

getopt – Parse command line options 解析命令行参数(选项)

Synopsis(语法)

#include  int getopt(int argc, char * const argv[], const char *optstring); extern char *optarg;extern int optind, opterr, optopt; #define _GNU_SOURCE #include  int getopt_long(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex); int getopt_long_only(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex);

Description

The getopt() function parses the command line arguments. Its arguments argc and argv are the argument count and array as passed to the main() function on program invocation. An element of argv that starts with `-’ (and is not exactly “-” or “–”) is an option element. The characters of this element (aside from the initial `-’) are option characters.//argv以’-'开始的元素叫一个选项单元,这个元素的字符(除去’-'的部分)叫选项字符。

 If getopt() is called repeatedly, it returns successively each of the option characters from each of the option elements.

重复的调用getopt(),它会依次返回每一个选项单元的选项字符。

If getopt() finds another option character, it returns that character, updating the external variable optind and a static variable nextchar so that the next call to getopt() can resume the scan with the following option character or argv-element.

如果getopt()又发现了一个选项字符,它就返回这个字符,更新全局变量optind和静态变量nextchar,这样下次调用getopt()时会接着扫瞄剩下的选项字符。

If there are no more option characters, getopt() returns -1. Then optind is the index in argv of the first argv-element that is not an option.

如果再扫瞄不到选项字符,getopt()返回-1。optind是第一个不是选项的argv字符数组的下标。

optstring is a string containing the legitimate option characters.

optstring是包含合法选项字符的字符串。

 If such a character is followed by a colon, the option requires an argument, so getopt places a pointer to the following text in the same argv-element, or the text of the following argv-element, in optarg.

如果这个字符串中的字符后紧跟一个冒号(;),说明这个选项需要一个参数,则getopt函数就将指向与这个字符是同一个argv单元的紧跟着的文本的指针存放在参数optarg中。

 Two colons mean an option takes an optional arg; if there is text in the current argv-element, it is returned in optarg, otherwise optarg is set to zero. This is a GNU extension. If optstring contains W followed by a semicolon, then -W foo is treated as the long option –foo. (The -W option is reserved by POSIX.2 for implementation extensions.) This behaviour is a GNU extension, not available with libraries before GNU libc 2.

两个冒号表示这个选项可以有一个可选的参数,如果有的话,就由optarg返回,否则optarg为零。

By default, getopt() permutes the contents of argv as it scans, so that eventually all the non-options are at the end. Two other modes are also implemented. If the first character of optstring is `+’ or the environment variable POSIXLY_CORRECT is set, then option processing stops as soon as a non-option argument is encountered. If the first character of optstring is `-’, then each non-option argv-element is handled as if it were the argument of an option with character code 1. (This is used by programs that were written to expect options and other argv-elements in any order and that care about the ordering of the two.) The special argument `–’ forces an end of option-scanning regardless of the scanning mode.

If getopt() does not recognize an option character, it prints an error message to stderr, stores the character in optopt, and returns `?’. The calling program may prevent the error message by setting opterr to 0.

如果getopt()不认识你给的选项字符,它向stderr打印一条错误信息,并将这个选项字符存在optopt中,并且返回’?'。

If getopt() finds an option character in argv that was not included in optstring, or if it detects a missing option argument, it returns `?’ and sets the external variable optopt to the actual option character.

If the first character of optstring is a colon (`:’), then getopt() returns `:’ instead of `?’ to indicate a missing option argument. If an error was detected, and the first character of optstring is not a colon, and the external variable opterr is nonzero (which is the default), getopt() prints an error message.

The getopt_long() function works like getopt() except that it also accepts long options, started out by two dashes. Long option names may be abbreviated if the abbreviation is unique or is an exact match for some defined option. A long option may take a parameter, of the form –arg=param or –arg param.

getopt_long()函数的工作方式与getopt()相同,除了它还可以处理长选项参数(由–开始)。

longopts is a pointer to the first element of an array of struct option declared in as

struct option { const char *name; int has_arg; int *flag; int val; };
static struct option main_lopts[] = { { "help",     0, 0, 'h' }, { "listen",   0, 0, 's' }, { "connect",  1, 0, 'c' }, { "search",   2, 0, 'Q' }, { "kill",     1, 0, 'k' }, { "killall",  0, 0, 'K' }, { "role",     1, 0, 'r' }, { "service",  1, 0, 'd' }, { "device",   1, 0, 'i' }, { "source",   1, 0, 'S' }, { "nosdp",    0, 0, 'D' }, { "list",     0, 0, 'l' }, { "show",     0, 0, 'l' }, { "nodetach", 0, 0, 'n' }, { "persist",  2, 0, 'p' }, { "encrypt",  0, 0, 'E' }, { "master",   0, 0, 'M' }, { "cache",    0, 0, 'C' }, { "pidfile",  1, 0, 'P' }, { "autozap",  0, 0, 'z' }, { 0, 0, 0, 0 }};
由于main_lopt的flag都是0,所以getopt_long()函数返回val,
注意到结构option的name成员和val成员的对应关系,
name: 长选项 ----val:短选项
 while ((opt=getopt_long(argc, argv, main_sopts, main_lopts, NULL)) != -1) {  switch(opt) {  case 'l':   mode = SHOW;   detach = 0;   break;
  case 's':   mode = LISTEN;   break;
  case 'c':   mode = CONNECT;   dst  = strdup(optarg);   break;
//注意这里的c带一个参数,当前的optarg指向那个参数,
The strdup() function returns a pointer to a new string 
which is a duplicate of the string s. 
static char main_sopts[] = "hsc:k:Kr:i:S:lnp::DQ::EMC::P:z";

The meanings of the different fields are:

name
is the name of the long option.
has_arg
is: no_argument (or 0) if the option does not take an argument, required_argument (or 1) if the option requires an argument, or optional_argument (or 2) if the option takes an optional argument.
0:这个长选项不需要参数
1:需要一个参数
2:有一个可选参数
flag
specifies how results are returned for a long option. If flag is NULL, then getopt_long() returns val. (For example, the calling program may set val to the equivalent short option character.) Otherwise, getopt_long() returns 0, and flag points to a variable which is set to val if the option is found, but left unchanged if the option is not found.
定义长选项的结果如何返回。如果flag等于NULL,那么getopt_long返回val。否则,getopt_long()返回0,flag指向一个变量,如果扫瞄到这个选项,这个变量设置为val,否则这个变量的值保持不变。
val
is the value to return, or to load into the variable pointed to by flag.

The last element of the array has to be filled with zeroes.

int getopt_long(int argc, char * const argv[],           const char *optstring,
          const struct option *longopts, int *longindex);

If longindex is not NULL, it points to a variable which is set to the index of the long option relative to longopts.

getopt_long_only() is like getopt_long(), but `-’ as well as `–’ can indicate a long option. If an option that starts with `-’ (not `–’) doesn’t match a long option, but does match a short option, it is parsed as a short option instead.

Return Value

The getopt() function returns the option character if the option was found successfully, `:’ if there was a missing parameter for one of the options, `?’ for an unknown option character, or -1 for the end of the option list.

getopt_long() and getopt_long_only() also return the option character when a short option is recognized. For a long option, they return val if flag is NULL, and 0 otherwise. Error and -1 returns are the same as for getopt(), plus `?’ for an ambiguous match or an extraneous parameter.

getopt_long()当识别了一个短选项时,也返回这个选项字符。对于长选项,如果flag等于NULL,返回val,否则返回0。

Environment Variables

POSIXLY_CORRECT
If this is set, then option processing stops as soon as a non-option argument is encountered.
_ _GNU_nonoption_argv_flags_
This variable was used by bash 2.0 to communicate to GNU libc which arguments are the results of wildcard expansion and so should not be considered as options. This behaviour was removed in bash version 2.01, but the support remains in GNU libc.

Example

The following example program illustrates the use of getopt_long() with most of its features.

#include  /* for printf */ 
#include  /* for exit */ 
#include  int main (int argc, char **argv) 
{ int c; 
int digit_optind = 0; 
while (1) 
{ int this_option_optind = optind ? optind : 1; 
int option_index = 0; 
static struct option long_options[] = 
{ {"add", 1, 0, 0}, 
{"append", 0, 0, 0}, 
{"delete", 1, 0, 0}, 
{"verbose", 0, 0, 0}, 
{"create", 1, 0, 'c'}, 
{"file", 1, 0, 0}, 
{0, 0, 0, 0} }; 
c = getopt_long (argc, argv, "abc:d:012",短选项 
long_options, &option_index); 
if (c == -1) break; 
switch (c) { 
case 0: printf ("option %s", long_options[option_index].name);
            返回0的情况出现在长选项中,当flag不等于NULL时 
if (optarg) printf (" with arg %s", optarg); printf ("\n"); break; 
case '0': 
case '1': 
case '2': 
if (digit_optind != 0 && digit_optind != this_option_optind) 
printf ("digits occur in two different argv-elements.\n"); 
digit_optind = this_option_optind; 
printf ("option %c\n", c); break; 
case 'a': printf ("option a\n"); 
break; case 'b': printf ("option b\n"); break; case 'c': printf ("option c with value `%s'\n", optarg); break; case 'd': printf ("option d with value `%s'\n", optarg); break; case '?': break; default: printf ("?? getopt returned character code 0%o ??\n", c); } } if (optind < argc) { printf ("non-option ARGV-elements: "); while (optind < argc) printf ("%s ", argv[optind++]); printf ("\n"); } exit (0); } 

Bugs

The POSIX.2 specification of getopt() has a technical error described in POSIX.2 Interpretation 150. The GNU implementation (and probably all other implementations) implements the correct behaviour rather than that specified.

Conforming to

getopt():
POSIX.2, provided the environment variable POSIXLY_CORRECT is set. Otherwise, the elements of argv aren’t really const, because we permute them. We pretend they’re const in the prototype to be compatible with other systems.