oFono学习笔记——GATChat(2):发送AT命令

摘要:

本文主要描述了GAtChat如何发送AT命令的全过程

1. GAtChat AT命令发送接口

在GAtChat库当中,根据AT命令返回结果的不同,GAtChat定义了四种不同的发送接口:一般发送接口,表单发送接口,PDU表单发送接口以及等待提示发送接口。

 1 /*一般发送接口*/
 2 guint g_at_chat_send(GAtChat *chat, const char *cmd,
 3             const char **prefix_list, GAtResultFunc func,
 4             gpointer user_data, GDestroyNotify notify)
 5 {
 6     return at_chat_send_common(chat->parent, chat->group,
 7                     cmd, prefix_list, 0, NULL,
 8                     func, user_data, notify);
 9 }
10 
11 /*表单发送接口*/
12 guint g_at_chat_send_listing(GAtChat *chat, const char *cmd,
13                 const char **prefix_list,
14                 GAtNotifyFunc listing, GAtResultFunc func,
15                 gpointer user_data, GDestroyNotify notify)
16 {
17     if (listing == NULL)
18         return 0;
19 
20     return at_chat_send_common(chat->parent, chat->group,
21                     cmd, prefix_list, 0,
22                     listing, func, user_data, notify);
23 }
24 
25 /*PDU表单发送接口*/
26 guint g_at_chat_send_pdu_listing(GAtChat *chat, const char *cmd,
27                 const char **prefix_list,
28                 GAtNotifyFunc listing, GAtResultFunc func,
29                 gpointer user_data, GDestroyNotify notify)
30 {
31     if (listing == NULL)
32         return 0;
33 
34     return at_chat_send_common(chat->parent, chat->group,
35                     cmd, prefix_list,
36                     COMMAND_FLAG_EXPECT_PDU,
37                     listing, func, user_data, notify);
38 }
39 
40 /*等待提示发送接口*/
41 guint g_at_chat_send_and_expect_short_prompt(GAtChat *chat, const char *cmd,
42                         const char **prefix_list,
43                         GAtResultFunc func,
44                         gpointer user_data,
45                         GDestroyNotify notify)
46 {
47     return at_chat_send_common(chat->parent, chat->group,
48                     cmd, prefix_list,
49                     COMMAND_FLAG_EXPECT_SHORT_PROMPT,
50                     NULL, func, user_data, notify);
51 }

1.1 一般发送接口

应对绝大部分的AT命令。这类AT命令的特点是命令的回复比较简单。

比如:ATE0。关闭串口回显,这种命令只会返回OK或者ERROR。

1.2 表单发送接口

应对返回结果是表单类型的AT命令,这类AT命令的返回结果的特点是,返回的每一条表单结果都有一个固定的prefix(前缀)

比如:用AT+CPBR=1,99来读取电话本,命令的返回结果为:

AT+CPBR=1,99
+CPBR: 1,"931123456",129,"Ilkka"
+CPBR: 2,"9501234567",129,""
+CPBR: 4,"901234567",129,"Hesari"
OK

1.3 PDU表单发送接口

应对返回结果是表单并带有PDU数据的AT命令,这类AT命令的特点是返回结果不仅有固定的前缀,还有PDU信息

比如:利用AT+CMGL读取已经收到的短信,并以PDU方式显示出来。起返回结果是:

AT+CMGL=4
+CMGL: 1,0,,39
07911326040011F5240B911326880736F40000111081017362401654747A0E4ACF41F4329E0E6A97E7F3F0B90C8A01
+CMGL: 2,0,,39
07911326040011F5240B911326880736F40000111081017323401654747A0E4ACF41F4329E0E6A97E7F3F0B90C9201
OK

1.4 等待提示发送接口

应对的是AT命令发送过程中需要等待GSM/GPRS模块确认再继续发送的命令。

比如:利用AT+CMGS命令发送短信,当命令发出后,需要等待模块返回'>'确认后才能继续发送短信内容。

AT+CMGS=17
>
0891683108705505f011000b813120882624f700f1ff0361f118
+CMGS: 2
OK

2. at_chat_send_common分析

在上面说到的发送接口中,我们可以看到,四个接口都是对at_chat_send_common进行的一个封装。可见发送AT命令的核心就在这个函数中:

 1 static guint at_chat_send_common(struct at_chat *chat, guint gid,
 2                     const char *cmd,
 3                     const char **prefix_list,
 4                     guint flags,
 5                     GAtNotifyFunc listing,
 6                     GAtResultFunc func,
 7                     gpointer user_data,
 8                     GDestroyNotify notify)
 9 {
10     struct at_command *c;
11 
12     if (chat == NULL || chat->command_queue == NULL)
13         return 0;
14         
15     /*创建AT命令*/
16     c = at_command_create(gid, cmd, prefix_list, flags, listing, func,
17                 user_data, notify, FALSE);
18     if (c == NULL)
19         return 0;
20         
21     c->id = chat->next_cmd_id++;
22 
23     /*将创建好的AT命令添加到发送队尾*/
24     g_queue_push_tail(chat->command_queue, c);
25         
26     /*激活writer将AT命令发送给模块*/
27     if (g_queue_get_length(chat->command_queue) == 1)
28         chat_wakeup_writer(chat);
29 
30     return c->id;
31 }

通过分析这个函数我们可以知道AT命令是如何发送到BP中去的。首先,会创建一个AT命令结构体。如果结构体成功创建了,就把它添加到命令发送队列的尾部。这个命令发送队列,就是上一篇文章中提到的command_queue。最后,检查发送队列,如果队列中又且只有一个命令等待发送就激活writer将AT命令发送给BP,否则说明现在writer已经在激活状态,无需激活。可见command_queue的长度是激活writer的一个关键。

2.1 struct at_command

 1 struct at_command {
 2     char *cmd;
 3     char **prefixes;
 4     guint flags;
 5     guint id;
 6     guint gid;
 7     GAtResultFunc callback;
 8     GAtNotifyFunc listing;
 9     gpointer user_data;
10     GDestroyNotify notify;
11 };

这里对at_command中的字段做一些说明:

cmd 存储AT命令
prefixes AT命令返回结果的前缀
flags
AT命令的返回类型,一般情况下为0。
当返回结果中包含PDU时,flags=COMMAND_FLAG_EXPECT_PDU。                 
当返回包含提示符时, flag=COMMAND_FLAG_EXPECT_SHORT_PROMPT。
id
AT命令的ID(1~(2^16 - 1)),用来唯一标识每一条命令。
当id=0时,说明这是一条weakup AT命令。
gid AT命令的组ID,表明该AT命令属于那一个GAtChat
callback 一般AT命令返回结果的处理回调函数
listing 表单AT命令的返回结果处理回调函数
user_data 用于装载AT发送/返回的数据
notify AT命令被GAtChat从队列中删除后的通知函数

3.chat_wakeup_writer

下面我们来分析writer是如何weakup。代码比较长,一下代码只截取重要的部分进行说明

gatchat.c

1 static gboolean can_write_data(gpointer data);
2 
3 static void chat_wakeup_writer(struct at_chat *chat)
4 {
5     g_at_io_set_write_handler(chat->io, can_write_data, chat);
6 }

gatio.c

 1 gboolean g_at_io_set_write_handler(GAtIO *io, GAtIOWriteFunc write_handler,
 2                     gpointer user_data)
 3 {
 4         /*...*/
 5     io->write_handler = write_handler;
 6     io->write_data = user_data;
 7 
 8     if (io->use_write_watch == TRUE)
 9         io->write_watch = g_io_add_watch_full(io->channel,
10                 G_PRIORITY_HIGH,
11                 G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
12                 can_write_data, io,
13                 write_watcher_destroy_notify);
14     else
15         io->write_watch = g_idle_add(call_blocking_read, io);
16 
17     return TRUE;
18 }
19 
20 static gboolean can_write_data(GIOChannel *channel, GIOCondition cond,
21                 gpointer data)
22 {
23     GAtIO *io = data;
24 
25     if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
26         return FALSE;
27 
28     if (io->write_handler == NULL)
29         return FALSE;
30 
31     return io->write_handler(io->write_data);
32 }

 当调用chat_wakeup_writer时,GAtChat会将具体的AT命令发送函数通过g_at_io_set_write_handler注册到GAtIO模块当中。在g_at_io_set_write_handler之中,gatio会向主事件循环中添加一个写监控(watch),当指定io口可以写的时候,系统会自动调用gatio.c中的can_write_data函数,通过这个函数调用,GAtChat所定义的AT命令发送函数。

3.1 can_write_data 分析

通过上面的分析,发送和AT命令发送相关的细节集中在gatchat.c当中的can_write_data之中。所以下面对这个函数的关键部分做简要分析:

 1 static gboolean can_write_data(gpointer data)
 2 {
 3     cmd = g_queue_peek_head(chat->command_queue);
 4 
 5     /*...*/
 6 
 7     if (chat->wakeup) {
 8         if (chat->wakeup_timer == NULL) {
 9             wakeup_first = TRUE;
10             chat->wakeup_timer = g_timer_new();
11 
12         } else if (g_timer_elapsed(chat->wakeup_timer, NULL) >
13                 chat->inactivity_time)
14             wakeup_first = TRUE;
15     }
16 
17     if (chat->cmd_bytes_written == 0 && wakeup_first == TRUE) {
18         cmd = at_command_create(0, chat->wakeup, none_prefix, 0,
19                     NULL, wakeup_cb, chat, NULL, TRUE);
20         if (cmd == NULL)
21             return FALSE;
22 
23         g_queue_push_head(chat->command_queue, cmd);
24 
25         len = strlen(chat->wakeup);
26 
27         chat->timeout_source = g_timeout_add(chat->wakeup_timeout,
28                         wakeup_no_response, chat);
29     }
30     /*...*/
31 
32     bytes_written = g_at_io_write(chat->io,
33                     cmd->cmd + chat->cmd_bytes_written,
34                     towrite);
35      /*...*/
36     /*
37      * If we're expecting a short prompt, set the hint for all lines
38      * sent to the modem except the last
39      */
40     if ((cmd->flags & COMMAND_FLAG_EXPECT_SHORT_PROMPT) &&
41             chat->cmd_bytes_written < len &&
42             chat->syntax->set_hint)
43         chat->syntax->set_hint(chat->syntax,
44                     G_AT_SYNTAX_EXPECT_SHORT_PROMPT);
45 
46     /* Full command submitted, update timer */
47     /*...*/
48 
49     return FALSE;
50 }    

当发现IO可写时,Chat会从command_queue中取出AT命令(但不删除),紧接着检查模块是否需要weakup,如果需要weakup操作,就自动生成一个weakup AT命令,然后将这条命令添加到发送队列的队头位置,优先发送weakup命令激活模块,否则发送一开始选中的AT命令。接下来进入真正的发送部分,将选定的AT命令通过g_at_io_write发送出去。最后,根据AT命令的返回类型,设置相对应的hint,完成AT命令的发送。

4.总结

以上就是AT命令在GATChat中发送的过程,如果发现其中存在错误的话,欢迎指正。

 

posted @ 2013-07-02 14:59  ZHX_1Q89  阅读(1381)  评论(0编辑  收藏  举报