1. 背景
回顾一下当今智能手机的架构。目前,很多智能手机都是采用双处理器架构:一个处理器用来运行操作系统相关的工作,应用程序都是运行在这个上面,所以这个处理器又叫Application Processor(AP),一个处理器用来负责和无线射频通讯相关的工作,即Baseband Processor(BP)。AP与BP之间最常见的是使用串口相连接。AP与BP之间通讯协议就是AT命令。
当下,每一个Mobile OS都有自己的一套机制来解决AP与BP之间的AT通讯问题。Android中使用Rild,而在Tizen IVI中,则是使用Ofono这个开源架构。而oFono中使用GATChat这个库来实现AP与BP之间AT通讯。下面就是对GATChat这个库的一些分析。
2. GAtChat结构体分析
在gatchat.c中定义了GAtChat
1 /*gatchat.h*/ 2 typedef struct _GAtChat GAtChat; 3 4 /*gatchat.c*/ 5 struct _GAtChat { 6 gint ref_count; 7 struct at_chat *parent; 8 guint group; 9 GAtChat *slave; 10 };
2.1 ref_count
因为GAtChat这个库是基于GLib来编写的,因此其中借鉴了GLib的一些思想。当结构体中ref_count减为0时,当前GAtChat将会被删除。每一次引用和解引用都会修改ref_count的值。
1 GAtChat *g_at_chat_ref(GAtChat *chat) 2 { 3 if (chat == NULL) 4 return NULL; 5 6 g_atomic_int_inc(&chat->ref_count);/*ref_count+1*/ 7 8 return chat; 9 } 10 11 void g_at_chat_unref(GAtChat *chat) 12 { 13 gboolean is_zero; 14 15 if (chat == NULL) 16 return; 17 18 /*ref_count-1, 并判断ref_count是否为0*/ 19 is_zero = g_atomic_int_dec_and_test(&chat->ref_count); 20 21 if (is_zero == FALSE) 22 return; 23 /*当ref_count为0,删除当前chat*/ 24 if (chat->slave != NULL) 25 g_at_chat_unref(chat->slave); 26 27 at_chat_cancel_group(chat->parent, chat->group); 28 g_at_chat_unregister_all(chat); 29 at_chat_unref(chat->parent); 30 31 g_free(chat); 32 }
2.2 struct at_chat
在gatchat.c中定义了at_chat结构体:
1 struct at_chat { 2 gint ref_count; /* Ref count */ 3 guint next_cmd_id; /* Next command id */ 4 guint next_notify_id; /* Next notify id */ 5 guint next_gid; /* Next group id */ 6 GAtIO *io; /* AT IO */ 7 GQueue *command_queue; /* Command queue */ 8 guint cmd_bytes_written; /* bytes written from cmd */ 9 GHashTable *notify_list; /* List of notification reg */ 10 GAtDisconnectFunc user_disconnect; /* user disconnect func */ 11 gpointer user_disconnect_data; /* user disconnect data */ 12 guint read_so_far; /* Number of bytes processed */ 13 gboolean suspended; /* Are we suspended? */ 14 GAtDebugFunc debugf; /* debugging output function */ 15 gpointer debug_data; /* Data to pass to debug func */ 16 char *pdu_notify; /* Unsolicited Resp w/ PDU */ 17 GSList *response_lines; /* char * lines of the response */ 18 char *wakeup; /* command sent to wakeup modem */ 19 gint timeout_source; 20 gdouble inactivity_time; /* Period of inactivity */ 21 guint wakeup_timeout; /* How long to wait for resp */ 22 GTimer *wakeup_timer; /* Keep track of elapsed time */ 23 GAtSyntax *syntax; 24 gboolean destroyed; /* Re-entrancy guard */ 25 gboolean in_read_handler; /* Re-entrancy guard */ 26 gboolean in_notify; 27 GSList *terminator_list; /* Non-standard terminator */ 28 guint16 terminator_blacklist; /* Blacklisted terinators */ 29 };
看到这个结构体第一感觉会比较晕,有些在不同的应用场合下并不会用到,这里挑出一些比较重要的做说明:
1. io,这里决定了AT命令使用怎样的IO方式传送给BP。IO的方式可以是UART,USB,Socket等等。。。
2. commond_quque字段,这说明了所有的AT命令都是放到这个队列中进行缓存再依次发送的。
3. pdu_notify字段, 这个字段根据注释可以猜测是用来存储unsolicited response(非请求结果码)以及PDU信息的。
4. response_lines字段,这是一个GList类型,所以它是用来存储AT命令返回的solicited response(请求结果码信息)。
5. syntax字段, 类型为GATSyntax,这说明这个结构负责和AT命令语法相关的内容,也就是说AT命令的语法语义分析都是在这里进行的。
6. terminator_list字段,这个GList负责存储非标准的结束符,比如标准AT命令都是以OK结尾,而你的模块是以XX结尾,这样你就需要将XX添加到terminator_list中来。
2.3 group
GAtChat的组标示,可以把一个GAtChat分成若干个组,每一个组负责不同的工作。比如一个组负责和设备信息相关的AT命令的处理,一个组负责和GPRS相关的AT命令的处理。每创建一个分组需要调用g_at_chat_clone函数
1 GAtChat *g_at_chat_clone(GAtChat *clone) 2 { 3 GAtChat *chat; 4 5 if (clone == NULL) 6 return NULL; 7 8 chat = g_try_new0(GAtChat, 1); 9 if (chat == NULL) 10 return NULL; 11 12 chat->parent = clone->parent; 13 chat->group = chat->parent->next_gid++; /*创建分组*/ 14 chat->ref_count = 1; 15 g_atomic_int_inc(&chat->parent->ref_count); 16 17 if (clone->slave != NULL) 18 chat->slave = g_at_chat_clone(clone->slave); 19 20 return chat; 21 }
2.4 GAtChat *slave
字面理解这个一个slave chat。多数情况下,GPRS/GSM/3G模块上只有一个物理UART,为了让设备可以上网和打电话,我们需要在操作系统中虚拟出两个UART,一个UART专门负责AT命令的发送以及接收。另一个UART负责网络数据的透传。在oFono中,一般使用slave chat来做AT命令的收发。
3.总结
以上是我个人对GAtChat的一些理解,并不一定保证正确性。要是各位大大发现其中有错误的话,欢迎指正。接下来的文章将对GAtChat这个库是如何发送以及接收AT命令的全过程进行分析。