2004年10月20日

   Line disciplines
     A terminal file is used like any other file in the system in that it can
     be opened, read, and written to using standard system calls.  For each
     existing terminal file, there is a software processing module called a
     line discipline is associated with it.  The line discipline essentially
     glues the low level device driver code with the high level generic inter-
     face routines (such as read(2) and write(2)), and is responsible for
     implementing the semantics associated with the device.  When a terminal
     file is first opened by a program, the default line discipline called the
     termios line discipline is associated with the file.  This is the primary
     line discipline that is used in most cases and provides the semantics
     that users normally associate with a terminal.  When the termios line
     discipline is in effect, the terminal file behaves and is operated
     according to the rules described in termios(4).  Please refer to that man
     page for a full description of the terminal semantics.  The operations
     described here generally represent features common across all line
     disciplines, however some of these calls may not make sense in conjunc-
     tion with a line discipline other than termios, and some may not be sup-
     ported by the underlying hardware (or lack thereof, as in the case of
     ptys).

/* Initialize UART driver */
int init_uart(char *dev, struct uart_t *u, int send_break)
{
 struct termios ti;
 int  fd, i;

 fd = open(dev, O_RDWR | O_NOCTTY);//打开串口设备,其中标志

//O_RDWR,可以对此设备进行读写操作;

//O_NOCTTY,我不是很理解?

//但是不要以控制 tty 的模式,因为我们并不希望在发送 Ctrl-C
 后结束此进程

 if (fd < 0) {
  perror(“Can’t open serial port”);
  return -1;
 }

 //drop fd’s data;
 tcflush(fd, TCIOFLUSH
);//清空数据线

 if (tcgetattr(fd, &ti) < 0) {
  perror(“Can’t get port settings”);
  return -1;
 }

 cfmakeraw(&ti);

cfmakeraw sets the terminal attributes as follows://此函数设置串口终端的以下这些属性,
termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
|INLCR|IGNCR|ICRNL|IXON);
termios_p->c_oflag &= ~OPOST;
termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
termios_p->c_cflag &= ~(CSIZE|PARENB) ;
termios_p->c_cflag |=CS8;

 ti.c_cflag |= CLOCAL;//本地连接,无调制解调器控制
 if (u->flags & FLOW_CTL)
  ti.c_cflag |= CRTSCTS
;//输出硬件流控(只能在具完整线路的缆线下工作,参

//考 Serial-HOWTO 第七节)
 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) {/
/设置串口的传输速率bps, 也可以使

//用 cfsetispeed 和 cfsetospeed 来设置

  perror(“Can’t set initial baud rate”);
  return -1;
 }

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

 if (send_break)
  tcsendbreak(fd, 0);

//int tcsendbreak ( int fd, int duration );Sends a break for
//the given time.在串口线上发送0值,至少维持0.25秒。

//If duration is 0, it transmits zero-valued bits for at least 0.25 seconds, and

//not more than 0.5seconds.

 //where place register u’s init function;
 if (u->init && u->init(fd, u, &ti) < 0)

//所有bluez支持的蓝牙串口设备类型构成了一个uart结构数组,通过

//查找对应的uart类型,这个uart的init成员显示了它的init调用方法;

struct uart_t uart[] = {
 { “any”,      0×0000, 0×0000, HCI_UART_H4,   115200, 115200, FLOW_CTL, NULL },
 { “ericsson”, 0×0000, 0×0000, HCI_UART_H4,   57600,  115200, FLOW_CTL, ericsson },
 { “digi”,     0×0000, 0×0000, HCI_UART_H4,   9600,   115200, FLOW_CTL, digi },
 { “texas”,    0×0000, 0×0000, HCI_UART_H4,   115200, 115200, FLOW_CTL, texas},

 { “bcsp”,     0×0000, 0×0000, HCI_UART_BCSP, 115200, 115200, 0,        bcsp },//bcsp的init函数名为bcsp,定义在本文件中**;

  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) {//

TIOCSETD int *ldisc//改变到 i 行规,即hci行规
Change to the new line discipline pointed to by ldisc. The available line disciplines are listed in 

/* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */

/* line disciplines */
#define N_TTY  0

……

#define N_HCI  15  /* Bluetooth HCI UART */

  perror(“Can’t set line discipline”);
  return -1;
 }

 if (ioctl(fd, HCIUARTSETPROTO, u->proto) < 0) {

//设置hci设备的proto操作函数集为hci_uart操作集;
  perror(“Can’t set device”);
  return -1;
 }

 return fd;
}

2004年10月19日

hciattach.c->main.c中:

 /* 5 seconds should be enough for initialization */
 alarm(to);
 
 n = init_uart(dev, u, send_break);
 if (n < 0) {
  perror(“Can’t initialize device”);
  exit(1);
 }

 alarm(0);

这段程序的目的:如果5秒内不能完成初始化串口的任务,我们将受到alarm信号触发的信息,一般是一条出错信息;

如果5秒内完成,则解除尽该信号;

while (1) sleep(999999999);

这个程序为什么不直接结束?

 

 

hciattach

——————————————————————————–

 

SYNOPSIS
       hciattach < tty > < type | id > [ speed ] [ flow ]

 

DESCRIPTION
       Hciattach is used to attach a serial UART to the Bluetooth stack as HCI
       transport interface.

       hciattach程序用来附着一个串口UART设备作为蓝牙协议栈的hci层传输接口。

OPTIONS
       <tty>  This specifies the serial device to attach. A leading  /dev  can
              be omitted. Examples: /dev/ttyS1 ttyS2
               定义使用哪一个串口设备。
       <type | id>
              The  type  or id of the Bluetooth device that is to be attached,
              i.e. vendor or other device specific identifier. Currently  sup-
              ported types are
               定义蓝牙通过串口连接的蓝牙设备的类型或者厂家ID。目前支持的类型有
              type   description

              any    Unspecified   HCI_UART   interface,  no  vendor  specific
                     options

              ericsson
                     Ericsson based modules

              digi   Digianswer based cards

              xircom Xircom PCMCIA cards: Credit Card Adapter  and  Real  Port
                     Adapter

              csr    CSR  Casira  serial  adapter  or BrainBoxes serial dongle
                     (BL642)

              bboxes BrainBoxes PCMCIA card (BL620)

              swave  Silicon Wave kits

              bcsp   Serial adapters using CSR chips with BCSP serial protocol
                     使用CSR蓝牙核,串口传输协议使用BCSP的串口蓝牙适配器。

       Supported IDs are (manufacturer id, product id)

              0×0105, 0×080a
                     Xircom  PCMCIA  cards:  Credit Card Adapter and Real Port
                     Adapter

              0×0160, 0×0002
                     BrainBoxes PCMCIA card (BL620)

       <speed>
              The speed specifies the UART speed to use. Baudrates higher than
              115.200bps  require vendor specific initializations that are not
              implemented for all types of devices. In general  the  following

       Written by Maxim Krasnyansky <maxk@qualcomm.com>

       man page by Nils Faerber <nils@kernelconcepts.de>

BlueZ                             Jan 22 2002                     HCIATTACH(8

2004年10月18日

sdp服务记录:

sdp服务器上有一个sdp服务记录数据库,

每一条服务记录由

服务句柄

服务属性1 //对于任意一个sdp服务记录,ServiceClassIDList
attribute是最重要的,它的属性值就是UUID

服务属性1的属性值

服务属性2

……

编写Linux下的Daemon程序 作者: dagger 无崖阁 xyg.ods.org 
版本: 1.0 2003-04-08 初始版本 
一、引言 Daemon程序是一直运行的服务端程序,又称为守护进程。
本文介绍了在Linux下编写Daemon程序的步骤,并给出了例子程序。 
二、Daemon程序简介 Daemon是长时间运行的进程,通常在系统启动后就运行,
在系统关闭时才结束。一般说Daemon程序在后台运行,是因为它没有控制终端,
无法和前台的用户交互。Daemon程序一般都作为服务程序使用,等待客户端程序
与它通信。我们也把运行的Daemon程序称作守护进程。 
三、Daemon程序编写规则 
编写Daemon程序有一些基本的规则,以避免不必要的麻烦。 
1、首先是程序运行后调用fork,并让父进程退出。子进程获得一个新的进程ID,
但继承了父进程的进程组ID。 
2、调用setsid创建一个新的session,使自己成为新session和新进程组的
leader,并使进程没有控制终端(tty)。 
3、改变当前工作目录至根目录,以免影响可加载文件系统。或者也可以改变到
某些特定的目录。 
4、设置文件创建mask为0,避免创建文件时权限的影响。 
5、关闭不需要的打开文件描述符。因为Daemon程序在后台执行,不需要于终端
交互,通常就关闭STDIN、STDOUT和STDERR。其它根据实际情况处理。 
另一个问题是Daemon程序不能和终端交互,也就无法使用printf方法输出信息
了。我们可以使用syslog机制来实现信息的输出,方便程序的调试。在使用
syslog前需要首先启动syslogd程序,关于syslogd程序的使用请参考它
的man page,或相关文档,我们就不在这里讨论了。 
四、一个Daemon程序的例子 编译运行环境为Redhat Linux 8.0。 
我们新建一个daemontest.c程序,文件内容如下: 
#include  #include  #include  #include  #include  
#include  #include  
int daemon_init(void) 
{ pid_t pid; 
if((pid = fork()) < 0) return(-1); 
else if(pid != 0) exit(0); /* parent exit */ 
/* child continues */ 
setsid(); /* become session leader */ 
chdir("/"); /* change working directory */ 
umask(0); /* clear file mode creation mask */ 
close(0); /* close stdin */ 
close(1); /* close stdout */ 
close(2); /* close stderr */ 
return(0); } 
void sig_term(int signo) 
{ if(signo == SIGTERM) 
/* catched signal sent by kill(1) command */ 
 { syslog(LOG_INFO, "program terminated."); 
 closelog(); exit(0); } 
} 
int main(void) 
{ if(daemon_init() == -1) 
{ printf("can't fork self\n"); exit(0); } 
openlog("daemontest", LOG_PID, LOG_USER); 
syslog(LOG_INFO, "program started."); 
signal(SIGTERM, sig_term); /* arrange to catch the signal */ 
while(1) { sleep(1); /* put your main program here */ } 
return(0); } 
使用如下命令编译该程序: gcc -Wall -o daemontest daemontest.c 
编译完成后生成名为daemontest的程序,执行./daemontest来测试程序的运行。
使用ps axj命令可以显示系统中已运行的daemon程序的信息,包括进程ID、
session ID、控制终端等内容。 
部分显示内容: 
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND 
1098 1101 1101 1074 pts/1 1101 S 0 0:00 -bash 1 1581 777 777 ? -1 S 500 0:13 gedit 1 1650 1650 1650 ? -1 S 500 0:00 ./daemontest 794 1654 1654 794 pts/0 1654 R 500 0:00 
ps axj 从中可以看到daemontest程序运行的进程号为1650。
我们再来看看/var/log/messages文件中的信息: Apr 7 22:00:32 localhost 
daemontest[1650]: program started. 
显示了我们在程序中希望输出的信息。 
我们再使用kill 1650命令来杀死这个进程,
/var/log/messages文件中就会有如下的信息: 
Apr 7 22:11:10 localhost daemontest[1650]: program terminated.
 使用ps axj命令检查,发现系统中daemontest进程已经没有了。
 五、参考资料 
Advanced Programming in the UNIX Environment W.Richard Stevens 

void       g_main_loop_run        (GMainLoop    *loop)
{
 int open_max = sysconf(_SC_OPEN_MAX);
 struct pollfd *ufds = malloc(open_max * sizeof(struct pollfd));

 while (!loop->bail) {//外层主循环,本循环内没有函数改变loop的值,

//所以这是个无限循环;

//我猜测hcid->main.c中关于信号的处理将影响loop的值,

 memset(&sa, 0, sizeof(sa));
 sa.sa_flags = SA_NOCLDSTOP;
 sa.sa_handler = sig_term;
 //sigaction change default action by system when the first para signal happened;
 sigaction(SIGTERM, &sa, NULL);
 sigaction(SIGINT,  &sa, NULL);

sig_term的操作很简单loop->bail=1,这样g_main_loop_run就会退出无限循环;

比如,hcid是一个deamon,我们可以调用kill向hcid进程发出SIGTERM信号;
  int nfds, rc, i;
  struct watch *p, *w;

  nfds = 0;
  for (w = watch_head.next; w != NULL; w = w->next) {
   ufds[nfds].fd = w->channel->fd;
   ufds[nfds].events = w->condition;
   ufds[nfds].revents = 0;
   nfds++;
  }
  
  rc = poll(ufds, nfds, -1);

  if (rc < 0 && (errno == EINTR))

//poll’s man page http://jamesthornton.com/linux/man/poll.2.html

//EINTR A signal occurred before any requested event. 
   continue;

  if (rc < 0) {
   perror(“poll”);
   continue;
  }
 //poll的返回值大于0,是正常情况,我们处理;注意中文翻译书出错 
  p = &watch_head;
  w = watch_head.next;
  i = 0;
  while (w) {
   if (ufds[i].revents) {
    gboolean keep = w->func(w->channel, ufds[i].revents, w->user_data);
    if (!keep) {
      p->next = w->next;
      memset(w, 0, sizeof(*w));
      w = p->next;
      i++;
      continue;
    }
   } 
  
   p = w;
   w = w->next;
   i++;
  }

 }

 free(ufds);
}

Wait queues have several uses in the kernel, particularly for interrupt handling, process synchronization, and timing. Because these topics are discussed in later chapters, we’ll just say here that a process must often wait for some event to occur, such as for a disk operation to terminate, a system resource to be released, or a fixed interval of time to elapse.进程经常需要等待一些事件的发生,例如一个磁盘操作的结束,或者一个系统资源的释放等等。 Wait queues implement conditional waits on events: a process wishing to wait for a specific event places itself in the proper wait queue and relinquishes control.等待队列实现了事件上的条件等待:希望等待特定事件发生的进程将自己放在合适的队列并且放弃对cpu的控制。 Therefore, a wait queue represents a set of sleeping processes, which are woken up by the kernel when some condition becomes true. 因此,等待队列代表一个睡眠进程的集合,当条件为真时,由内核唤醒。

Wait queues are implemented as doubly linked lists whose elements include pointers to process descriptors. Each wait queue is identified by a wait queue head, a data structure of type wait_queue_head_t:

struct _ _wait_queue_head {
    spinlock_t lock;
    struct list_head task_list;
};
typedef struct _ _wait_queue_head wait_queue_head_t;

Since wait queues are modified by interrupt handlers as well as by major kernel functions, the doubly linked lists must be protected from concurrent accesses, which could induce unpredictable results (see Chapter 5). Synchronization is achieved by the lock spin lock in the wait queue head.

Elements of a wait queue list are of type wait_queue_t:

struct _ _wait_queue {
    unsigned int flags;
    struct task_struct * task;
    struct list_head task_list;
};
typedef struct _ _wait_queue wait_queue_t;

Each element in the wait queue list represents a sleeping process, which is waiting for some event to occur; its descriptor address is stored in the task field. However, it is not always convenient to wake up all sleeping processes in a wait queue.

For instance, if two or more processes are waiting for exclusive access to some resource to be released, it makes sense to wake up just one process in the wait queue. This process takes the resource, while the other processes continue to sleep. (This avoids a problem known as the “thundering herd,” with which multiple processes are awoken only to race for a resource that can be accessed by one of them, and the result is that remaining processes must once more be put back to sleep.)

Thus, there are two kinds of sleeping processes: exclusive processes (denoted by the value 1 in the flags field of the corresponding wait queue element) are selectively woken up by the kernel, while nonexclusive processes (denoted by the value 0 in flags) are always woken up by the kernel when the event occurs. A process waiting for a resource that can be granted to just one process at a time is a typical exclusive process. Processes waiting for an event like the termination of a disk operation are nonexclusive.

the wake_up_process( ) function is used to make a process runnable. It sets the process state to TASK_RUNNING and invokes add_to_runqueue( ) to insert the process in the runqueue list. It also forces the invocation of the scheduler when the process has a dynamic priority larger than that of the current process or, in SMP systems, that of a process currently executing on some other CPU

这个函数的用途:可以是一个进程可运行。它设置进程的状态为TASH_RUNNING,并且调用add_to_runqueue函数将进程插入runqueue链表。当这个进程的优先级大于现在正在cpu上执行的进程,就会调用scheduler进程调度者。

3.2.2.4 Doubly linked lists

The process list is a special doubly linked list. However, as you may have noticed, the Linux kernel uses hundreds of doubly linked lists that store the various kernel data structures.

For each list, a set of primitive operations must be implemented: initializing the list, inserting and deleting an element, scanning the list, and so on. It would be both a waste of programmers’ efforts and a waste of memory to replicate the primitive operations for each different list.

Therefore, the Linux kernel defines the list_head data structure, whose fields next and prev represent the forward and back pointers of a generic doubly linked list element, respectively. It is important to note, however, that the pointers in a list_head field store the addresses of other list_head fields rather than the addresses of the whole data structures in which the list_head structure is included (see Figure 3-4).

Figure 3-4. A doubly linked list built with list_head data structures

figs

A new list is created by using the LIST_HEAD(list_name) macro. LIST_HEAD宏产生一个新的链表。It declares a new variable named list_name of type list_head, which is the conventional first element of the new list (much as init_task is the conventional first element of the process list).

Several functions and macros implement the primitives, including those shown in the following list.

list_add(n,p)

Inserts an element pointed by n right after the specified element pointed by p (to insert n at the beginning of the list, set p to the address of the conventional first element)

list_add_tail(n,h)

Inserts an element pointed by n at the end of the list specified by the address h of its conventional first element

list_del(p)

Deletes an element pointed by p (there is no need to specify the conventional first element of the list)

list_empty(p)

Checks if the list specified by the address of its conventional first element is empty

list_entry(p,t,f)

Returns the address of the data structure of type t in which the list_head field that has the name f and the address p is included

返回数据结构t的地址,这个结构包含值等于f的list_head,地址p也在这个结构的地址范围内。

list_for_each(p,h)

Scans the elements of the list specified by the address h of the conventional first element (similar to for_each_task for the process list)