mirror of
https://gitee.com/moluo-tech/AT-Command.git
synced 2025-02-05 17:28:23 +08:00
首次提交
This commit is contained in:
parent
270cdfa47c
commit
7878c6874c
36
README.en.md
36
README.en.md
@ -1,36 +0,0 @@
|
||||
# AT Command
|
||||
|
||||
#### Description
|
||||
一种AT命令通信管理模块,支持裸机和OS版本。适用于modem、WIFI模块、蓝牙通信。
|
||||
|
||||
#### Software Architecture
|
||||
Software architecture description
|
||||
|
||||
#### Installation
|
||||
|
||||
1. xxxx
|
||||
2. xxxx
|
||||
3. xxxx
|
||||
|
||||
#### Instructions
|
||||
|
||||
1. xxxx
|
||||
2. xxxx
|
||||
3. xxxx
|
||||
|
||||
#### Contribution
|
||||
|
||||
1. Fork the repository
|
||||
2. Create Feat_xxx branch
|
||||
3. Commit your code
|
||||
4. Create Pull Request
|
||||
|
||||
|
||||
#### Gitee Feature
|
||||
|
||||
1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
|
||||
2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
|
||||
3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
|
||||
4. The most valuable open source project [GVP](https://gitee.com/gvp)
|
||||
5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
|
||||
6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
|
36
README.md
36
README.md
@ -1,30 +1,32 @@
|
||||
# AT Command
|
||||
|
||||
#### 介绍
|
||||
一种AT命令通信管理模块,支持裸机和OS版本。适用于modem、WIFI模块、蓝牙通信。
|
||||
一种AT命令通信管理模块(支持单行发),支持裸机和OS版本。适用于modem、WIFI模块、蓝牙通信。
|
||||
|
||||
#### 软件架构
|
||||
软件架构说明
|
||||
|
||||
|
||||
#### 安装教程
|
||||
|
||||
1. xxxx
|
||||
2. xxxx
|
||||
3. xxxx
|
||||
|
||||
at_chat.c at_chat.h用于无OS版本,使用链式队列及异步回调方式处理AT命令收发,支持URC处理。
|
||||
at_core.c at_core.h用于OS版本
|
||||
#### 使用说明
|
||||
|
||||
1. xxxx
|
||||
2. xxxx
|
||||
3. xxxx
|
||||
##### at_chat 模块(无OS)
|
||||
|
||||
#### 参与贡献
|
||||
1. 定义AT管理器
|
||||
at_core_t at;
|
||||
|
||||
2. AT管理器配置参数
|
||||
const char at_core_conf_t conf = {
|
||||
};
|
||||
|
||||
1. Fork 本仓库
|
||||
2. 新建 Feat_xxx 分支
|
||||
3. 提交代码
|
||||
4. 新建 Pull Request
|
||||
3. 初始化AT管理器
|
||||
at_core_init(&at, conf);
|
||||
|
||||
4. 将AT管理器放入任务中轮询
|
||||
at_poll_task(&at);
|
||||
|
||||
5. 发送单行命令
|
||||
|
||||
at_send_singlline(&at, NULL, "AT+CSQ?");
|
||||
|
||||
|
||||
#### 码云特技
|
||||
|
487
at_chat.c
Normal file
487
at_chat.c
Normal file
@ -0,0 +1,487 @@
|
||||
/*******************************************************************************
|
||||
* @brief AT ͨ<EFBFBD>Ź<EFBFBD><EFBFBD><EFBFBD>(<EFBFBD><EFBFBD>OS<EFBFBD>汾)
|
||||
*
|
||||
* Change Logs
|
||||
* Date Author Notes
|
||||
* 2016-01-22 Morro Initial version.
|
||||
* 2018-02-11 Morro ʹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʽ<EFBFBD><EFBFBD><EFBFBD>й<EFBFBD><EFBFBD><EFBFBD>AT<EFBFBD><EFBFBD>ҵ
|
||||
* 2020-05-21 Morro ֧<EFBFBD>ֶ<EFBFBD><EFBFBD><EFBFBD>AT<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
*******************************************************************************/
|
||||
|
||||
#include "at_chat.h"
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
//<2F><>ʱ<EFBFBD>ж<EFBFBD>
|
||||
#define AT_IS_TIMEOUT(start, time) (at_get_ms() - (start) > (time))
|
||||
|
||||
/*ATCOMM work type -----------------------------------------------------------*/
|
||||
#define AT_TYPE_WORK 0 /*<2A><><EFBFBD><EFBFBD> --------------*/
|
||||
#define AT_TYPE_CMD 1 /*<2A><><EFBFBD><D7BC><EFBFBD><EFBFBD> ----------*/
|
||||
#define AT_TYPE_MULTILINE 3 /*<2A><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ----------*/
|
||||
#define AT_TYPE_SINGLLINE 4 /*<2A><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ----------*/
|
||||
|
||||
typedef int (*base_work)(at_core_t *ac, ...);
|
||||
|
||||
static void at_send_line(at_core_t *ac, const char *fmt, va_list args);
|
||||
|
||||
static const inline at_core_conf_t *__get_adapter(at_core_t *ac)
|
||||
{
|
||||
return &ac->cfg;
|
||||
}
|
||||
|
||||
static bool is_timeout(at_core_t *ac, unsigned int ms)
|
||||
{
|
||||
return AT_IS_TIMEOUT(ac->resp_timer, ms);
|
||||
}
|
||||
/*
|
||||
* @brief <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
*/
|
||||
static void send_data(at_core_t *ac, const void *buf, unsigned int len)
|
||||
{
|
||||
ac->cfg.write(buf, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief <EFBFBD><EFBFBD>ʽ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӡ
|
||||
*/
|
||||
static void print(at_core_t *ac, const char *cmd, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, cmd);
|
||||
at_send_line(ac, cmd, args);
|
||||
va_end(args);
|
||||
}
|
||||
/*
|
||||
* @brief <EFBFBD><EFBFBD>ȡ<EFBFBD><EFBFBD>ǰ<EFBFBD><EFBFBD><EFBFBD>ݽ<EFBFBD><EFBFBD>ճ<EFBFBD><EFBFBD><EFBFBD>
|
||||
*/
|
||||
static unsigned int get_recv_count(at_core_t *ac)
|
||||
{
|
||||
return ac->rcv_cnt;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief <EFBFBD><EFBFBD>ȡ<EFBFBD><EFBFBD><EFBFBD>ݻ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
*/
|
||||
static char *get_recv_buf(at_core_t *ac)
|
||||
{
|
||||
return (char *)ac->cfg.rcv_buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݻ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
*/
|
||||
static void recv_buf_clear(at_core_t *ac)
|
||||
{
|
||||
ac->rcv_cnt = 0;
|
||||
}
|
||||
|
||||
/*ǰ<><C7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ִ<EFBFBD>*/
|
||||
static char *search_string(at_core_t *ac, const char *str)
|
||||
{
|
||||
return strstr(get_recv_buf(ac), str);
|
||||
}
|
||||
|
||||
/*ǰ<><C7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ִ<EFBFBD>*/
|
||||
static bool at_isabort(at_core_t *ac)
|
||||
{
|
||||
return ac->cursor ? ac->cursor->abort : 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* @brief ATִ<EFBFBD>лص<EFBFBD>
|
||||
*/
|
||||
static void do_at_callback(at_core_t *a, at_item_t *i, at_callback_t cb, at_return ret)
|
||||
{
|
||||
at_response_t r;
|
||||
if (cb) {
|
||||
r.param = i->param;
|
||||
r.recvbuf = get_recv_buf(a);
|
||||
r.recvcnt = get_recv_count(a);
|
||||
r.ret = ret;
|
||||
cb(&r);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief AT<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
* @param[in] cfg - AT<EFBFBD><EFBFBD>Ӧ
|
||||
*/
|
||||
void at_core_init(at_core_t *ac, const at_core_conf_t cfg)
|
||||
{
|
||||
at_env_t *e;
|
||||
ac->cfg = cfg;
|
||||
e = &ac->env;
|
||||
ac->rcv_cnt = 0;
|
||||
|
||||
e->is_timeout = is_timeout;
|
||||
e->printf = print;
|
||||
e->recvbuf = get_recv_buf;
|
||||
e->recvclr = recv_buf_clear;
|
||||
e->recvlen = get_recv_count;
|
||||
e->find = search_string;
|
||||
e->abort = at_isabort;
|
||||
}
|
||||
/*<2A><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ҵ<EFBFBD><D2B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>*/
|
||||
static bool add_work(at_core_t *ac, void *params, void *info, int type)
|
||||
{
|
||||
at_item_t *i;
|
||||
ac->cfg.lock();
|
||||
if (list_empty(&ac->ls_idle)) //<2F><EFBFBD><DEBF><EFBFBD>at_item
|
||||
return NULL;
|
||||
i = list_first_entry(&ac->ls_idle, at_item_t, node);//<2F>ӿ<EFBFBD><D3BF><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD>ҵ
|
||||
i->info = (void *)info;
|
||||
i->param = (void *)params;
|
||||
i->state = AT_STATE_WAIT;
|
||||
i->type = type;
|
||||
i->abort = 0;
|
||||
list_move_tail(&i->node, &ac->ls_ready); //<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
ac->cfg.unlock();
|
||||
return i != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief ִ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
*/
|
||||
static int do_work_handler(at_core_t *ac)
|
||||
{
|
||||
at_item_t *i = ac->cursor;
|
||||
return ((int (*)(at_env_t *e))i->info)(i->param);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* @brief ͨ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ִ<EFBFBD><EFBFBD>
|
||||
* @param[in] a - AT<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
* @return 0 - <EFBFBD><EFBFBD><EFBFBD>ֹ<EFBFBD><EFBFBD><EFBFBD>,<EFBFBD><EFBFBD>0 - <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
******************************************************************************/
|
||||
static int do_cmd_handler(at_core_t *a)
|
||||
{
|
||||
at_item_t *i = a->cursor;
|
||||
at_env_t *e = &a->env;
|
||||
const at_cmd_t *c = (at_cmd_t *)i->info;
|
||||
switch(e->state) {
|
||||
case 0: /*<2A><><EFBFBD><EFBFBD>״̬ ------------------------------------------------------*/
|
||||
c->sender(e);
|
||||
e->state++;
|
||||
e->reset_timer(a);
|
||||
e->recvclr(a);
|
||||
break;
|
||||
case 1: /*<2A><><EFBFBD><EFBFBD>״̬ ------------------------------------------------------*/
|
||||
if (search_string(a, c->matcher)) {
|
||||
do_at_callback(a, i, c->cb, AT_RET_OK);
|
||||
return true;
|
||||
} else if (search_string(a, "ERROR")) {
|
||||
if (++e->i >= c->retry) {
|
||||
do_at_callback(a, i, c->cb, AT_RET_ERROR);
|
||||
return true;
|
||||
}
|
||||
e->state = 2; /*<2A><><EFBFBD><EFBFBD>֮<EFBFBD><D6AE><EFBFBD><EFBFBD>ʱһ<CAB1><D2BB>ʱ<EFBFBD><CAB1>*/
|
||||
e->reset_timer(a); /*<2A><><EFBFBD>ö<EFBFBD>ʱ<EFBFBD><CAB1>*/
|
||||
} else if (e->is_timeout(a, c->timeout)) {
|
||||
if (++e->i >= c->retry) {
|
||||
do_at_callback(a, i, c->cb, AT_RET_TIMEOUT);
|
||||
return true;
|
||||
}
|
||||
e->state = 0; /*<2A><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ״̬*/
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (e->is_timeout(a, 500))
|
||||
e->state = 0; /*<2A><><EFBFBD>س<EFBFBD>ʼ״̬*/
|
||||
break;
|
||||
default:
|
||||
e->state = 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* @brief <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
* @param[in] a - AT<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
* @return 0 - <EFBFBD><EFBFBD><EFBFBD>ֹ<EFBFBD><EFBFBD><EFBFBD>,<EFBFBD><EFBFBD>0 - <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
******************************************************************************/
|
||||
static int send_signlline_handler(at_core_t *a)
|
||||
{
|
||||
at_item_t *i = a->cursor;
|
||||
at_env_t *e = &a->env;
|
||||
const char *cmd = (const char *)i->param;
|
||||
at_callback_t cb = (at_callback_t)i->info;
|
||||
|
||||
switch(e->state) {
|
||||
case 0: /*<2A><><EFBFBD><EFBFBD>״̬ ------------------------------------------------------*/
|
||||
e->printf(a, cmd);
|
||||
e->state++;
|
||||
e->reset_timer(a);
|
||||
e->recvclr(a);
|
||||
break;
|
||||
case 1: /*<2A><><EFBFBD><EFBFBD>״̬ ------------------------------------------------------*/
|
||||
if (search_string(a, "OK")) {
|
||||
do_at_callback(a, i, cb, AT_RET_OK);
|
||||
return true;
|
||||
} else if (search_string(a, "ERROR")) {
|
||||
if (++e->i >= 3) {
|
||||
do_at_callback(a, i, cb, AT_RET_ERROR);
|
||||
return true;
|
||||
}
|
||||
e->state = 2; /*<2A><><EFBFBD><EFBFBD>֮<EFBFBD><D6AE><EFBFBD><EFBFBD>ʱһ<CAB1><D2BB>ʱ<EFBFBD><CAB1>*/
|
||||
e->reset_timer(a); /*<2A><><EFBFBD>ö<EFBFBD>ʱ<EFBFBD><CAB1>*/
|
||||
} else if (e->is_timeout(a, 3000 + e->i * 2000)) {
|
||||
if (++e->i >= 3) {
|
||||
do_at_callback(a, i, cb, AT_RET_TIMEOUT);
|
||||
return true;
|
||||
}
|
||||
e->state = 0; /*<2A><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ״̬*/
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (e->is_timeout(a, 500))
|
||||
e->state = 0; /*<2A><><EFBFBD>س<EFBFBD>ʼ״̬*/
|
||||
break;
|
||||
default:
|
||||
e->state = 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/*******************************************************************************
|
||||
* @brief <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
* @param[in] a - AT<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
* @return 0 - <EFBFBD><EFBFBD><EFBFBD>ֹ<EFBFBD><EFBFBD><EFBFBD>,<EFBFBD><EFBFBD>0 - <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
******************************************************************************/
|
||||
static int send_multiline_handler(at_core_t *a)
|
||||
{
|
||||
at_item_t *i = a->cursor;
|
||||
at_env_t *e = &a->env;
|
||||
const char **cmds = (const char **)i->param;
|
||||
at_callback_t cb = (at_callback_t)i->info;
|
||||
switch(e->state) {
|
||||
case 0:
|
||||
if (cmds[e->i] == NULL) { /*<2A><><EFBFBD><EFBFBD>ִ<EFBFBD><D6B4><EFBFBD><EFBFBD><EFBFBD><EFBFBD>*/
|
||||
do_at_callback(a, i, cb, AT_RET_OK);
|
||||
return true;
|
||||
}
|
||||
e->printf(a, "%s\r\n", cmds[e->i]);
|
||||
e->recvclr(a); /*<2A><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>*/
|
||||
e->reset_timer(a);
|
||||
e->state++;
|
||||
break;
|
||||
case 1:
|
||||
if (search_string(a, "OK")){
|
||||
e->state = 0;
|
||||
e->i++;
|
||||
e->i = 0;
|
||||
} else if (search_string(a, "ERROR")) {
|
||||
if (++e->j >= 3) {
|
||||
do_at_callback(a, i, cb, AT_RET_ERROR);
|
||||
return true;
|
||||
}
|
||||
e->state = 2; /*<2A><><EFBFBD><EFBFBD>֮<EFBFBD><D6AE><EFBFBD><EFBFBD>ʱһ<CAB1><D2BB>ʱ<EFBFBD><CAB1>*/
|
||||
e->reset_timer(a); /*<2A><><EFBFBD>ö<EFBFBD>ʱ<EFBFBD><CAB1>*/
|
||||
} else if (e->is_timeout(a, 3000)) {
|
||||
do_at_callback(a, i, cb, AT_RET_TIMEOUT);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
e->state = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
* @param[in] fmt - <EFBFBD><EFBFBD>ʽ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
* @param[in] args - <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>б<EFBFBD>
|
||||
*/
|
||||
static void at_send_line(at_core_t *ac, const char *fmt, va_list args)
|
||||
{
|
||||
char buf[MAX_AT_CMD_LEN];
|
||||
int len;
|
||||
const at_core_conf_t *adt = __get_adapter(ac);
|
||||
len = vsnprintf(buf, sizeof(buf), fmt, args);
|
||||
|
||||
recv_buf_clear(ac); //<2F><><EFBFBD>ս<EFBFBD><D5BD>ջ<EFBFBD><D5BB><EFBFBD>
|
||||
send_data(ac, buf, len);
|
||||
send_data(ac, "\r\n", 2);
|
||||
}
|
||||
/*
|
||||
* @brief urc <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
* @param[in] urc
|
||||
* @return none
|
||||
*/
|
||||
static void urc_handler_entry(at_core_t *ac, char *urc, unsigned int size)
|
||||
{
|
||||
int i, n;
|
||||
utc_item_t *tbl = ac->cfg.utc_tbl;
|
||||
for (i = 0; i < ac->cfg.urc_tbl_count; i++){
|
||||
n = strlen(tbl->prefix);
|
||||
if (strncmp(urc, tbl->prefix, n) == 0)
|
||||
tbl[i].handler(urc, size);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief urc <EFBFBD><EFBFBD><EFBFBD>մ<EFBFBD><EFBFBD><EFBFBD>
|
||||
* @param[in] buf - <EFBFBD><EFBFBD><EFBFBD>ݻ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
* @return none
|
||||
*/
|
||||
static void urc_recv_process(at_core_t *ac, char *buf, unsigned int size)
|
||||
{
|
||||
char *urc_buf;
|
||||
unsigned short urc_size;
|
||||
urc_buf = (char *)ac->cfg.urc_buf;
|
||||
urc_size = ac->cfg.urc_bufsize;
|
||||
if (size == 0 && ac->urc_cnt > 0) {
|
||||
if (AT_IS_TIMEOUT(ac->urc_timer, 2000)){
|
||||
urc_handler_entry(ac, urc_buf, ac->urc_cnt);
|
||||
ac->rcv_cnt = 0;
|
||||
}
|
||||
} else {
|
||||
ac->urc_timer = at_get_ms();
|
||||
while (size--) {
|
||||
if (*buf == '\n') {
|
||||
urc_buf[ac->urc_cnt] = '\0';
|
||||
urc_handler_entry(ac, urc_buf, ac->urc_cnt);
|
||||
} else {
|
||||
urc_buf[ac->urc_cnt++] = *buf++;
|
||||
if (ac->urc_cnt >= urc_size)
|
||||
ac->urc_cnt = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief ָ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ӧ<EFBFBD><EFBFBD><EFBFBD>մ<EFBFBD><EFBFBD><EFBFBD>
|
||||
* @param[in] buf -
|
||||
* @return none
|
||||
*/
|
||||
static void resp_recv_process(at_core_t *ac, const char *buf, unsigned int size)
|
||||
{
|
||||
char *rcv_buf;
|
||||
unsigned short rcv_size;
|
||||
|
||||
rcv_buf = (char *)ac->cfg.rcv_buf;
|
||||
rcv_size = ac->cfg.rcv_bufsize;
|
||||
|
||||
if (ac->rcv_cnt + size >= rcv_size) //<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
ac->rcv_cnt = 0;
|
||||
|
||||
memcpy(rcv_buf, buf, size);
|
||||
ac->rcv_cnt += size;
|
||||
rcv_buf[ac->rcv_cnt] = '\0';
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief ִ<EFBFBD><EFBFBD>AT<EFBFBD><EFBFBD>ҵ
|
||||
* @param[in] a - AT<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
* @param[in] work - AT<EFBFBD><EFBFBD>ҵ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
* @param[in] params -
|
||||
*/
|
||||
bool at_do_work(at_core_t *ac, int (*work)(at_env_t *e), void *params)
|
||||
{
|
||||
return add_work(ac, params, (void *)work, AT_TYPE_WORK);
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief ִ<EFBFBD><EFBFBD>ATָ<EFBFBD><EFBFBD>
|
||||
* @param[in] a - AT<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
* @param[in] cmd - cmd<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
*/
|
||||
bool at_do_cmd(at_core_t *ac, void *params, const at_cmd_t *cmd)
|
||||
{
|
||||
return add_work(ac, params, (void *)cmd, AT_TYPE_CMD);
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief <EFBFBD><EFBFBD><EFBFBD>͵<EFBFBD><EFBFBD><EFBFBD>AT<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
* @param[in] ac - AT<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
* @param[in] cb - ִ<EFBFBD>лص<EFBFBD>
|
||||
* @param[in] singlline - <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
* @note <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ִ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֮ǰ,singlline<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ч
|
||||
*/
|
||||
bool at_send_singlline(at_core_t *ac, at_callback_t cb, const char *singlline)
|
||||
{
|
||||
return add_work(ac, (void *)singlline, (void *)cb, AT_TYPE_SINGLLINE);
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief <EFBFBD><EFBFBD><EFBFBD>Ͷ<EFBFBD><EFBFBD><EFBFBD>AT<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
* @param[in] ac - AT<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
* @param[in] cb - ִ<EFBFBD>лص<EFBFBD>
|
||||
* @param[in] multiline - <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
* @note <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ִ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֮ǰ,multiline
|
||||
*/
|
||||
bool at_send_multiline(at_core_t *ac, at_callback_t cb, const char **multiline)
|
||||
{
|
||||
return add_work(ac, multiline, (void *)cb, AT_TYPE_MULTILINE);
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief ǿ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֹAT<EFBFBD><EFBFBD>ҵ
|
||||
*/
|
||||
|
||||
void at_item_abort(at_item_t *i)
|
||||
{
|
||||
i->abort = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief ATæ<EFBFBD>ж<EFBFBD>
|
||||
* @return true - <EFBFBD><EFBFBD>ATָ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ִ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
*/
|
||||
bool at_core_busy(at_core_t *ac)
|
||||
{
|
||||
return !list_empty(&ac->ls_ready);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* @brief AT<EFBFBD><EFBFBD>ҵ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
******************************************************************************/
|
||||
static void at_work_manager(at_core_t *ac)
|
||||
{
|
||||
register at_item_t *cursor = ac->cursor;
|
||||
at_env_t *e = &ac->env;
|
||||
/*ͨ<>ù<EFBFBD><C3B9><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ---------------------------------------------------------*/
|
||||
static int (*const work_handler_table[])(at_core_t *) = {
|
||||
do_work_handler,
|
||||
do_cmd_handler,
|
||||
send_signlline_handler,
|
||||
send_multiline_handler
|
||||
};
|
||||
if (ac->cursor == NULL) {
|
||||
if (list_empty(&ac->ls_ready)) //<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊ<EFBFBD><CEAA>
|
||||
return;
|
||||
e->i = 0;
|
||||
e->j = 0;
|
||||
e->state = 0;
|
||||
e->params = cursor->param;
|
||||
e->recvclr(ac);
|
||||
e->reset_timer(ac);
|
||||
ac->cursor = list_first_entry(&ac->ls_ready, at_item_t, node);
|
||||
}
|
||||
/*<2A><><EFBFBD><EFBFBD>ִ<EFBFBD><D6B4><EFBFBD><EFBFBD><EFBFBD><EFBFBD>,<2C><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>뵽<EFBFBD><EBB5BD><EFBFBD>й<EFBFBD><D0B9><EFBFBD><EFBFBD><EFBFBD> ------------------------------------*/
|
||||
if (work_handler_table[cursor->type](ac) || cursor->abort) {
|
||||
ac->cfg.lock();
|
||||
list_move_tail(&ac->cursor->node, &ac->ls_idle);
|
||||
ac->cursor = NULL;
|
||||
ac->cfg.unlock();
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* @brief AT<EFBFBD><EFBFBD>ѯ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
*/
|
||||
void at_poll_task(at_core_t *ac)
|
||||
{
|
||||
char rbuf[32];
|
||||
int read_size;
|
||||
read_size = __get_adapter(ac)->read(rbuf, sizeof(rbuf));
|
||||
urc_recv_process(ac, rbuf, read_size);
|
||||
resp_recv_process(ac, rbuf, read_size);
|
||||
at_work_manager(ac);
|
||||
}
|
||||
|
||||
|
139
at_chat.h
Normal file
139
at_chat.h
Normal file
@ -0,0 +1,139 @@
|
||||
/*******************************************************************************
|
||||
* @brief AT ͨ<EFBFBD>Ź<EFBFBD><EFBFBD><EFBFBD>(<EFBFBD><EFBFBD>OS<EFBFBD>汾)
|
||||
*
|
||||
* Change Logs
|
||||
* Date Author Notes
|
||||
* 2016-01-22 Morro Initial version.
|
||||
* 2018-02-11 Morro ʹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʽ<EFBFBD><EFBFBD><EFBFBD>й<EFBFBD><EFBFBD><EFBFBD>AT<EFBFBD><EFBFBD>ҵ
|
||||
* 2020-05-21 Morro ֧<EFBFBD>ֶ<EFBFBD><EFBFBD><EFBFBD>AT<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
*******************************************************************************/
|
||||
#ifndef _ATCHAT_H_
|
||||
#define _ATCHAT_H_
|
||||
|
||||
#include "at_util.h"
|
||||
#include <list.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define MAX_AT_CMD_LEN 128
|
||||
|
||||
struct at_core;
|
||||
|
||||
/*urc<72><63><EFBFBD><EFBFBD><EFBFBD><EFBFBD> -----------------------------------------------------------------*/
|
||||
typedef struct {
|
||||
const char *prefix; //<2F><>Ҫƥ<D2AA><C6A5><EFBFBD><EFBFBD>ͷ<EFBFBD><CDB7>
|
||||
void (*handler)(char *recvbuf, int size);
|
||||
}utc_item_t;
|
||||
|
||||
typedef struct {
|
||||
unsigned int (*write)(const void *buf, unsigned int len); /*<2A><><EFBFBD>ͽӿ<CDBD>*/
|
||||
unsigned int (*read)(void *buf, unsigned int len); /*<2A><><EFBFBD>սӿ<D5BD>*/
|
||||
void (*lock)(void); /*<2A><><EFBFBD><EFBFBD>,<2C><><EFBFBD><EFBFBD>OS*/
|
||||
void (*unlock)(void); /*<2A><><EFBFBD><EFBFBD>,<2C><><EFBFBD><EFBFBD>OS*/
|
||||
/*Events -----------------------------------------------------------------*/
|
||||
void (*before_at)(void); /*<2A><>ʼִ<CABC><D6B4>AT*/
|
||||
void (*after_at)(void);
|
||||
void (*error)(void);
|
||||
utc_item_t *utc_tbl; /*utc <20><>*/
|
||||
unsigned char *urc_buf; /*urc<72><63><EFBFBD>ջ<EFBFBD><D5BB><EFBFBD><EFBFBD><EFBFBD>*/
|
||||
unsigned char *rcv_buf;
|
||||
unsigned short urc_tbl_count;
|
||||
unsigned short urc_bufsize; /*urc<72><63><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>С*/
|
||||
unsigned short rcv_bufsize; /*<2A><><EFBFBD>ջ<EFBFBD><D5BB><EFBFBD><EFBFBD><EFBFBD>*/
|
||||
}at_core_conf_t;
|
||||
|
||||
/*AT<41><54>ҵ<EFBFBD><D2B5><EFBFBD>л<EFBFBD><D0BB><EFBFBD>*/
|
||||
typedef struct {
|
||||
int i,j,state;
|
||||
void *params;
|
||||
void (*reset_timer)(struct at_core *ac);
|
||||
bool (*is_timeout)(struct at_core *ac, unsigned int ms); /*ʱ<><CAB1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ж<EFBFBD>*/
|
||||
void (*printf)(struct at_core *ac, const char *fmt, ...);
|
||||
char * (*find)(struct at_core *ac, const char *expect);
|
||||
char * (*recvbuf)(struct at_core *ac); /*ָ<><D6B8><EFBFBD><EFBFBD><EFBFBD>ջ<EFBFBD><D5BB><EFBFBD><EFBFBD><EFBFBD>*/
|
||||
unsigned int(*recvlen)(struct at_core *ac); /*<2A><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ܳ<EFBFBD><DCB3><EFBFBD>*/
|
||||
void (*recvclr)(struct at_core *ac); /*<2A><><EFBFBD>ս<EFBFBD><D5BD>ջ<EFBFBD><D5BB><EFBFBD><EFBFBD><EFBFBD>*/
|
||||
bool (*abort)(struct at_core *ac); /*<2A><>ִֹ<D6B9><D6B4>*/
|
||||
}at_env_t;
|
||||
|
||||
/*AT<41><54><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ӧ<EFBFBD><D3A6>*/
|
||||
typedef enum {
|
||||
AT_RET_OK = 0, /*ִ<>гɹ<D0B3>*/
|
||||
AT_RET_ERROR, /*ִ<>д<EFBFBD><D0B4><EFBFBD>*/
|
||||
AT_RET_TIMEOUT, /*<2A><>Ӧ<EFBFBD><D3A6>ʱ*/
|
||||
AT_RET_ABORT, /*ǿ<><C7BF><EFBFBD><EFBFBD>ֹ*/
|
||||
}at_return;
|
||||
|
||||
/*AT<41><54>Ӧ */
|
||||
typedef struct {
|
||||
void *param;
|
||||
char *recvbuf;
|
||||
unsigned short recvcnt;
|
||||
at_return ret;
|
||||
}at_response_t;
|
||||
|
||||
typedef void (*at_callback_t)(at_response_t *r);
|
||||
|
||||
/*AT״̬ */
|
||||
typedef enum {
|
||||
AT_STATE_IDLE, /*<2A><><EFBFBD><EFBFBD>״̬*/
|
||||
AT_STATE_WAIT, /*<2A>ȴ<EFBFBD>ִ<EFBFBD><D6B4>*/
|
||||
AT_STATE_EXEC, /*<2A><><EFBFBD><EFBFBD>ִ<EFBFBD><D6B4>*/
|
||||
}at_work_state;
|
||||
|
||||
/*AT<41><54>ҵ<EFBFBD><D2B5>*/
|
||||
typedef struct {
|
||||
at_work_state state : 3;
|
||||
unsigned char type : 3;
|
||||
unsigned char abort : 1;
|
||||
void *param;
|
||||
void *info;
|
||||
struct list_head node;
|
||||
}at_item_t;
|
||||
|
||||
/*AT<41><54><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ------------------------------------------------------------------*/
|
||||
typedef struct at_core{
|
||||
at_core_conf_t cfg;
|
||||
at_env_t env;
|
||||
at_item_t tbl[10];
|
||||
at_item_t *cursor;
|
||||
struct list_head ls_ready, ls_idle; /*<2A><><EFBFBD><EFBFBD>,<2C><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ҵ<EFBFBD><D2B5>*/
|
||||
unsigned int resp_timer;
|
||||
unsigned int urc_timer;
|
||||
at_return ret;
|
||||
//urc<72><63><EFBFBD>ռ<EFBFBD><D5BC><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ӧ<EFBFBD><D3A6><EFBFBD>ռ<EFBFBD><D5BC><EFBFBD><EFBFBD><EFBFBD>
|
||||
unsigned short urc_cnt, rcv_cnt;
|
||||
unsigned char suspend: 1;
|
||||
}at_core_t;
|
||||
|
||||
typedef struct {
|
||||
void (*sender)(at_env_t *e); /*<2A>Զ<EFBFBD><D4B6>巢<EFBFBD><E5B7A2><EFBFBD><EFBFBD> */
|
||||
const char *matcher; /*<2A><><EFBFBD><EFBFBD>ƥ<EFBFBD>䴮 */
|
||||
at_callback_t cb; /*<2A><>Ӧ<EFBFBD><D3A6><EFBFBD><EFBFBD> */
|
||||
unsigned char retry; /*<2A><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ<EFBFBD><D4B4><EFBFBD> */
|
||||
unsigned short timeout; /*<2A><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʱʱ<CAB1><CAB1> */
|
||||
}at_cmd_t;
|
||||
|
||||
void at_core_init(at_core_t *ac, const at_core_conf_t cfg);
|
||||
|
||||
/*<2A><><EFBFBD>͵<EFBFBD><CDB5><EFBFBD>AT<41><54><EFBFBD><EFBFBD>*/
|
||||
bool at_send_singlline(at_core_t *ac, at_callback_t cb, const char *singlline);
|
||||
/*<2A><><EFBFBD>Ͷ<EFBFBD><CDB6><EFBFBD>AT<41><54><EFBFBD><EFBFBD>*/
|
||||
bool at_send_multiline(at_core_t *ac, at_callback_t cb, const char **multiline);
|
||||
/*ִ<><D6B4>AT<41><54><EFBFBD><EFBFBD>*/
|
||||
bool at_do_cmd(at_core_t *ac, void *params, const at_cmd_t *cmd);
|
||||
/*<2A>Զ<EFBFBD><D4B6><EFBFBD>AT<41><54>ҵ*/
|
||||
bool at_do_work(at_core_t *ac, int (*work)(at_env_t *e), void *params);
|
||||
|
||||
void at_item_abort(at_item_t *it); /*<2A><>ֹ<EFBFBD><D6B9>ǰ<EFBFBD><C7B0>ҵ*/
|
||||
|
||||
bool at_core_busy(at_core_t *ac);
|
||||
|
||||
void at_suspend(at_core_t *ac);
|
||||
|
||||
void at_resume(at_core_t *ac);
|
||||
|
||||
|
||||
void at_poll_task(at_core_t *ac);
|
||||
|
||||
|
||||
#endif
|
703
list.h
Normal file
703
list.h
Normal file
@ -0,0 +1,703 @@
|
||||
#ifndef _LINUX_LIST_H
|
||||
#define _LINUX_LIST_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#define typeof (struct list_head)
|
||||
|
||||
|
||||
#undef offsetof
|
||||
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
|
||||
|
||||
/**
|
||||
* container_of - cast a member of a structure out to the containing structure
|
||||
* @ptr: the pointer to the member.
|
||||
* @type: the type of the container struct this is embedded in.
|
||||
* @member: the name of the member within the struct.
|
||||
*
|
||||
*/
|
||||
#ifdef __ICCARM__ /* IAR, and the 'ptr' must be 'struct list_head *' type, 'member' must be 'struct list_head' type */
|
||||
#define container_of(ptr, type, member) ( \
|
||||
(type *)( (char *)(ptr) - offsetof(type,member) ))
|
||||
#else
|
||||
#define container_of(ptr, type, member) ({ \
|
||||
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
|
||||
(type *)( (char *)__mptr - offsetof(type,member) );})
|
||||
#endif
|
||||
|
||||
/* copy from <linux/poison.h>, */
|
||||
/*
|
||||
* used to verify that nobody uses non-initialized list entries.
|
||||
*/
|
||||
#define LIST_POISON1 ((void *) 0x0)
|
||||
#define LIST_POISON2 ((void *) 0x0)
|
||||
|
||||
|
||||
#ifndef ARCH_HAS_PREFETCH
|
||||
#define ARCH_HAS_PREFETCH
|
||||
static inline void prefetch(const void *x) {;}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simple doubly linked list implementation.
|
||||
*
|
||||
* Some of the internal functions ("__xxx") are useful when
|
||||
* manipulating whole lists rather than single entries, as
|
||||
* sometimes we already know the next/prev entries and we can
|
||||
* generate better code by using them directly rather than
|
||||
* using the generic single-entry routines.
|
||||
*/
|
||||
|
||||
struct list_head {
|
||||
struct list_head *next, *prev;
|
||||
};
|
||||
|
||||
#define LIST_HEAD_INIT(name) { &(name), &(name) }
|
||||
|
||||
#define LIST_HEAD(name) \
|
||||
struct list_head name = LIST_HEAD_INIT(name)
|
||||
|
||||
static inline void INIT_LIST_HEAD(struct list_head *list)
|
||||
{
|
||||
list->next = list;
|
||||
list->prev = list;
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert a new entry between two known consecutive entries.
|
||||
*
|
||||
* This is only for internal list manipulation where we know
|
||||
* the prev/next entries already!
|
||||
*/
|
||||
static inline void __list_add(struct list_head *new,
|
||||
struct list_head *prev,
|
||||
struct list_head *next)
|
||||
{
|
||||
next->prev = new;
|
||||
new->next = next;
|
||||
new->prev = prev;
|
||||
prev->next = new;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_add - add a new entry
|
||||
* @new: new entry to be added
|
||||
* @head: list head to add it after
|
||||
*
|
||||
* Insert a new entry after the specified head.
|
||||
* This is good for implementing stacks.
|
||||
*/
|
||||
static inline void list_add(struct list_head *new, struct list_head *head)
|
||||
{
|
||||
__list_add(new, head, head->next);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_add_tail - add a new entry
|
||||
* @new: new entry to be added
|
||||
* @head: list head to add it before
|
||||
*
|
||||
* Insert a new entry before the specified head.
|
||||
* This is useful for implementing queues.
|
||||
*/
|
||||
static inline void list_add_tail(struct list_head *new, struct list_head *head)
|
||||
{
|
||||
__list_add(new, head->prev, head);
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete a list entry by making the prev/next entries
|
||||
* point to each other.
|
||||
*
|
||||
* This is only for internal list manipulation where we know
|
||||
* the prev/next entries already!
|
||||
*/
|
||||
static inline void __list_del(struct list_head *prev, struct list_head *next)
|
||||
{
|
||||
next->prev = prev;
|
||||
prev->next = next;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_del - deletes entry from list.
|
||||
* @entry: the element to delete from the list.
|
||||
* Note: list_empty() on entry does not return true after this, the entry is
|
||||
* in an undefined state.
|
||||
*/
|
||||
static inline void list_del(struct list_head *entry)
|
||||
{
|
||||
__list_del(entry->prev, entry->next);
|
||||
entry->next = LIST_POISON1;
|
||||
entry->prev = LIST_POISON2;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_replace - replace old entry by new one
|
||||
* @old : the element to be replaced
|
||||
* @new : the new element to insert
|
||||
*
|
||||
* If @old was empty, it will be overwritten.
|
||||
*/
|
||||
static inline void list_replace(struct list_head *old,
|
||||
struct list_head *new)
|
||||
{
|
||||
new->next = old->next;
|
||||
new->next->prev = new;
|
||||
new->prev = old->prev;
|
||||
new->prev->next = new;
|
||||
}
|
||||
|
||||
static inline void list_replace_init(struct list_head *old,
|
||||
struct list_head *new)
|
||||
{
|
||||
list_replace(old, new);
|
||||
INIT_LIST_HEAD(old);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_del_init - deletes entry from list and reinitialize it.
|
||||
* @entry: the element to delete from the list.
|
||||
*/
|
||||
static inline void list_del_init(struct list_head *entry)
|
||||
{
|
||||
__list_del(entry->prev, entry->next);
|
||||
INIT_LIST_HEAD(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_move - delete from one list and add as another's head
|
||||
* @list: the entry to move
|
||||
* @head: the head that will precede our entry
|
||||
*/
|
||||
static inline void list_move(struct list_head *list, struct list_head *head)
|
||||
{
|
||||
__list_del(list->prev, list->next);
|
||||
list_add(list, head);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_move_tail - delete from one list and add as another's tail
|
||||
* @list: the entry to move
|
||||
* @head: the head that will follow our entry
|
||||
*/
|
||||
static inline void list_move_tail(struct list_head *list,
|
||||
struct list_head *head)
|
||||
{
|
||||
__list_del(list->prev, list->next);
|
||||
list_add_tail(list, head);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_is_last - tests whether @list is the last entry in list @head
|
||||
* @list: the entry to test
|
||||
* @head: the head of the list
|
||||
*/
|
||||
static inline int list_is_last(const struct list_head *list,
|
||||
const struct list_head *head)
|
||||
{
|
||||
return list->next == head;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_empty - tests whether a list is empty
|
||||
* @head: the list to test.
|
||||
*/
|
||||
static inline int list_empty(const struct list_head *head)
|
||||
{
|
||||
return head->next == head;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_empty_careful - tests whether a list is empty and not being modified
|
||||
* @head: the list to test
|
||||
*
|
||||
* Description:
|
||||
* tests whether a list is empty _and_ checks that no other CPU might be
|
||||
* in the process of modifying either member (next or prev)
|
||||
*
|
||||
* NOTE: using list_empty_careful() without synchronization
|
||||
* can only be safe if the only activity that can happen
|
||||
* to the list entry is list_del_init(). Eg. it cannot be used
|
||||
* if another CPU could re-list_add() it.
|
||||
*/
|
||||
static inline int list_empty_careful(const struct list_head *head)
|
||||
{
|
||||
struct list_head *next = head->next;
|
||||
return (next == head) && (next == head->prev);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_is_singular - tests whether a list has just one entry.
|
||||
* @head: the list to test.
|
||||
*/
|
||||
static inline int list_is_singular(const struct list_head *head)
|
||||
{
|
||||
return !list_empty(head) && (head->next == head->prev);
|
||||
}
|
||||
|
||||
static inline void __list_cut_position(struct list_head *list,
|
||||
struct list_head *head, struct list_head *entry)
|
||||
{
|
||||
struct list_head *new_first = entry->next;
|
||||
list->next = head->next;
|
||||
list->next->prev = list;
|
||||
list->prev = entry;
|
||||
entry->next = list;
|
||||
head->next = new_first;
|
||||
new_first->prev = head;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_cut_position - cut a list into two
|
||||
* @list: a new list to add all removed entries
|
||||
* @head: a list with entries
|
||||
* @entry: an entry within head, could be the head itself
|
||||
* and if so we won't cut the list
|
||||
*
|
||||
* This helper moves the initial part of @head, up to and
|
||||
* including @entry, from @head to @list. You should
|
||||
* pass on @entry an element you know is on @head. @list
|
||||
* should be an empty list or a list you do not care about
|
||||
* losing its data.
|
||||
*
|
||||
*/
|
||||
static inline void list_cut_position(struct list_head *list,
|
||||
struct list_head *head, struct list_head *entry)
|
||||
{
|
||||
if (list_empty(head))
|
||||
return;
|
||||
if (list_is_singular(head) &&
|
||||
(head->next != entry && head != entry))
|
||||
return;
|
||||
if (entry == head)
|
||||
INIT_LIST_HEAD(list);
|
||||
else
|
||||
__list_cut_position(list, head, entry);
|
||||
}
|
||||
|
||||
static inline void __list_splice(const struct list_head *list,
|
||||
struct list_head *prev,
|
||||
struct list_head *next)
|
||||
{
|
||||
struct list_head *first = list->next;
|
||||
struct list_head *last = list->prev;
|
||||
|
||||
first->prev = prev;
|
||||
prev->next = first;
|
||||
|
||||
last->next = next;
|
||||
next->prev = last;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_splice - join two lists, this is designed for stacks
|
||||
* @list: the new list to add.
|
||||
* @head: the place to add it in the first list.
|
||||
*/
|
||||
static inline void list_splice(const struct list_head *list,
|
||||
struct list_head *head)
|
||||
{
|
||||
if (!list_empty(list))
|
||||
__list_splice(list, head, head->next);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_splice_tail - join two lists, each list being a queue
|
||||
* @list: the new list to add.
|
||||
* @head: the place to add it in the first list.
|
||||
*/
|
||||
static inline void list_splice_tail(struct list_head *list,
|
||||
struct list_head *head)
|
||||
{
|
||||
if (!list_empty(list))
|
||||
__list_splice(list, head->prev, head);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_splice_init - join two lists and reinitialise the emptied list.
|
||||
* @list: the new list to add.
|
||||
* @head: the place to add it in the first list.
|
||||
*
|
||||
* The list at @list is reinitialised
|
||||
*/
|
||||
static inline void list_splice_init(struct list_head *list,
|
||||
struct list_head *head)
|
||||
{
|
||||
if (!list_empty(list)) {
|
||||
__list_splice(list, head, head->next);
|
||||
INIT_LIST_HEAD(list);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* list_splice_tail_init - join two lists and reinitialise the emptied list
|
||||
* @list: the new list to add.
|
||||
* @head: the place to add it in the first list.
|
||||
*
|
||||
* Each of the lists is a queue.
|
||||
* The list at @list is reinitialised
|
||||
*/
|
||||
static inline void list_splice_tail_init(struct list_head *list,
|
||||
struct list_head *head)
|
||||
{
|
||||
if (!list_empty(list)) {
|
||||
__list_splice(list, head->prev, head);
|
||||
INIT_LIST_HEAD(list);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* list_entry - get the struct for this entry
|
||||
* @ptr: the &struct list_head pointer.
|
||||
* @type: the type of the struct this is embedded in.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*/
|
||||
#define list_entry(ptr, type, member) \
|
||||
container_of(ptr, type, member)
|
||||
|
||||
/**
|
||||
* list_first_entry - get the first element from a list
|
||||
* @ptr: the list head to take the element from.
|
||||
* @type: the type of the struct this is embedded in.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*
|
||||
* Note, that list is expected to be not empty.
|
||||
*/
|
||||
#define list_first_entry(ptr, type, member) \
|
||||
list_entry((ptr)->next, type, member)
|
||||
|
||||
/**
|
||||
* list_for_each - iterate over a list
|
||||
* @pos: the &struct list_head to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
*/
|
||||
#define list_for_each(pos, head) \
|
||||
for (pos = (head)->next; prefetch(pos->next), pos != (head); \
|
||||
pos = pos->next)
|
||||
|
||||
/**
|
||||
* __list_for_each - iterate over a list
|
||||
* @pos: the &struct list_head to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
*
|
||||
* This variant differs from list_for_each() in that it's the
|
||||
* simplest possible list iteration code, no prefetching is done.
|
||||
* Use this for code that knows the list to be very short (empty
|
||||
* or 1 entry) most of the time.
|
||||
*/
|
||||
#define __list_for_each(pos, head) \
|
||||
for (pos = (head)->next; pos != (head); pos = pos->next)
|
||||
|
||||
/**
|
||||
* list_for_each_prev - iterate over a list backwards
|
||||
* @pos: the &struct list_head to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
*/
|
||||
#define list_for_each_prev(pos, head) \
|
||||
for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \
|
||||
pos = pos->prev)
|
||||
|
||||
/**
|
||||
* list_for_each_safe - iterate over a list safe against removal of list entry
|
||||
* @pos: the &struct list_head to use as a loop cursor.
|
||||
* @n: another &struct list_head to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
*/
|
||||
#define list_for_each_safe(pos, n, head) \
|
||||
for (pos = (head)->next, n = pos->next; pos != (head); \
|
||||
pos = n, n = pos->next)
|
||||
|
||||
/**
|
||||
* list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
|
||||
* @pos: the &struct list_head to use as a loop cursor.
|
||||
* @n: another &struct list_head to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
*/
|
||||
#define list_for_each_prev_safe(pos, n, head) \
|
||||
for (pos = (head)->prev, n = pos->prev; \
|
||||
prefetch(pos->prev), pos != (head); \
|
||||
pos = n, n = pos->prev)
|
||||
|
||||
/**
|
||||
* list_for_each_entry - iterate over list of given type
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*/
|
||||
#define list_for_each_entry(pos, head, member) \
|
||||
for (pos = list_entry((head)->next, typeof(*pos), member); \
|
||||
prefetch(pos->member.next), &pos->member != (head); \
|
||||
pos = list_entry(pos->member.next, typeof(*pos), member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_reverse - iterate backwards over list of given type.
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*/
|
||||
#define list_for_each_entry_reverse(pos, head, member) \
|
||||
for (pos = list_entry((head)->prev, typeof(*pos), member); \
|
||||
prefetch(pos->member.prev), &pos->member != (head); \
|
||||
pos = list_entry(pos->member.prev, typeof(*pos), member))
|
||||
|
||||
/**
|
||||
* list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
|
||||
* @pos: the type * to use as a start point
|
||||
* @head: the head of the list
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*
|
||||
* Prepares a pos entry for use as a start point in list_for_each_entry_continue().
|
||||
*/
|
||||
#define list_prepare_entry(pos, head, member) \
|
||||
((pos) ? : list_entry(head, typeof(*pos), member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_continue - continue iteration over list of given type
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*
|
||||
* Continue to iterate over list of given type, continuing after
|
||||
* the current position.
|
||||
*/
|
||||
#define list_for_each_entry_continue(pos, head, member) \
|
||||
for (pos = list_entry(pos->member.next, typeof(*pos), member); \
|
||||
prefetch(pos->member.next), &pos->member != (head); \
|
||||
pos = list_entry(pos->member.next, typeof(*pos), member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_continue_reverse - iterate backwards from the given point
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*
|
||||
* Start to iterate over list of given type backwards, continuing after
|
||||
* the current position.
|
||||
*/
|
||||
#define list_for_each_entry_continue_reverse(pos, head, member) \
|
||||
for (pos = list_entry(pos->member.prev, typeof(*pos), member); \
|
||||
prefetch(pos->member.prev), &pos->member != (head); \
|
||||
pos = list_entry(pos->member.prev, typeof(*pos), member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_from - iterate over list of given type from the current point
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*
|
||||
* Iterate over list of given type, continuing from current position.
|
||||
*/
|
||||
#define list_for_each_entry_from(pos, head, member) \
|
||||
for (; prefetch(pos->member.next), &pos->member != (head); \
|
||||
pos = list_entry(pos->member.next, typeof(*pos), member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @n: another type * to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*/
|
||||
#define list_for_each_entry_safe(pos, n, head, member) \
|
||||
for (pos = list_entry((head)->next, typeof(*pos), member), \
|
||||
n = list_entry(pos->member.next, typeof(*pos), member); \
|
||||
&pos->member != (head); \
|
||||
pos = n, n = list_entry(n->member.next, typeof(*n), member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_safe_continue
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @n: another type * to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*
|
||||
* Iterate over list of given type, continuing after current point,
|
||||
* safe against removal of list entry.
|
||||
*/
|
||||
#define list_for_each_entry_safe_continue(pos, n, head, member) \
|
||||
for (pos = list_entry(pos->member.next, typeof(*pos), member), \
|
||||
n = list_entry(pos->member.next, typeof(*pos), member); \
|
||||
&pos->member != (head); \
|
||||
pos = n, n = list_entry(n->member.next, typeof(*n), member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_safe_from
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @n: another type * to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*
|
||||
* Iterate over list of given type from current point, safe against
|
||||
* removal of list entry.
|
||||
*/
|
||||
#define list_for_each_entry_safe_from(pos, n, head, member) \
|
||||
for (n = list_entry(pos->member.next, typeof(*pos), member); \
|
||||
&pos->member != (head); \
|
||||
pos = n, n = list_entry(n->member.next, typeof(*n), member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_safe_reverse
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @n: another type * to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*
|
||||
* Iterate backwards over list of given type, safe against removal
|
||||
* of list entry.
|
||||
*/
|
||||
#define list_for_each_entry_safe_reverse(pos, n, head, member) \
|
||||
for (pos = list_entry((head)->prev, typeof(*pos), member), \
|
||||
n = list_entry(pos->member.prev, typeof(*pos), member); \
|
||||
&pos->member != (head); \
|
||||
pos = n, n = list_entry(n->member.prev, typeof(*n), member))
|
||||
|
||||
/*
|
||||
* Double linked lists with a single pointer list head.
|
||||
* Mostly useful for hash tables where the two pointer list head is
|
||||
* too wasteful.
|
||||
* You lose the ability to access the tail in O(1).
|
||||
*/
|
||||
|
||||
struct hlist_head {
|
||||
struct hlist_node *first;
|
||||
};
|
||||
|
||||
struct hlist_node {
|
||||
struct hlist_node *next, **pprev;
|
||||
};
|
||||
|
||||
#define HLIST_HEAD_INIT { .first = NULL }
|
||||
#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }
|
||||
#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
|
||||
static inline void INIT_HLIST_NODE(struct hlist_node *h)
|
||||
{
|
||||
h->next = NULL;
|
||||
h->pprev = NULL;
|
||||
}
|
||||
|
||||
static inline int hlist_unhashed(const struct hlist_node *h)
|
||||
{
|
||||
return !h->pprev;
|
||||
}
|
||||
|
||||
static inline int hlist_empty(const struct hlist_head *h)
|
||||
{
|
||||
return !h->first;
|
||||
}
|
||||
|
||||
static inline void __hlist_del(struct hlist_node *n)
|
||||
{
|
||||
struct hlist_node *next = n->next;
|
||||
struct hlist_node **pprev = n->pprev;
|
||||
*pprev = next;
|
||||
if (next)
|
||||
next->pprev = pprev;
|
||||
}
|
||||
|
||||
static inline void hlist_del(struct hlist_node *n)
|
||||
{
|
||||
__hlist_del(n);
|
||||
n->next = LIST_POISON1;
|
||||
n->pprev = LIST_POISON2;
|
||||
}
|
||||
|
||||
static inline void hlist_del_init(struct hlist_node *n)
|
||||
{
|
||||
if (!hlist_unhashed(n)) {
|
||||
__hlist_del(n);
|
||||
INIT_HLIST_NODE(n);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
|
||||
{
|
||||
struct hlist_node *first = h->first;
|
||||
n->next = first;
|
||||
if (first)
|
||||
first->pprev = &n->next;
|
||||
h->first = n;
|
||||
n->pprev = &h->first;
|
||||
}
|
||||
|
||||
/* next must be != NULL */
|
||||
static inline void hlist_add_before(struct hlist_node *n,
|
||||
struct hlist_node *next)
|
||||
{
|
||||
n->pprev = next->pprev;
|
||||
n->next = next;
|
||||
next->pprev = &n->next;
|
||||
*(n->pprev) = n;
|
||||
}
|
||||
|
||||
static inline void hlist_add_after(struct hlist_node *n,
|
||||
struct hlist_node *next)
|
||||
{
|
||||
next->next = n->next;
|
||||
n->next = next;
|
||||
next->pprev = &n->next;
|
||||
|
||||
if(next->next)
|
||||
next->next->pprev = &next->next;
|
||||
}
|
||||
|
||||
#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
|
||||
|
||||
#define hlist_for_each(pos, head) \
|
||||
for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
|
||||
pos = pos->next)
|
||||
|
||||
#define hlist_for_each_safe(pos, n, head) \
|
||||
for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
|
||||
pos = n)
|
||||
|
||||
/**
|
||||
* hlist_for_each_entry - iterate over list of given type
|
||||
* @tpos: the type * to use as a loop cursor.
|
||||
* @pos: the &struct hlist_node to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the hlist_node within the struct.
|
||||
*/
|
||||
#define hlist_for_each_entry(tpos, pos, head, member) \
|
||||
for (pos = (head)->first; \
|
||||
pos && ({ prefetch(pos->next); 1;}) && \
|
||||
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
|
||||
pos = pos->next)
|
||||
|
||||
/**
|
||||
* hlist_for_each_entry_continue - iterate over a hlist continuing after current point
|
||||
* @tpos: the type * to use as a loop cursor.
|
||||
* @pos: the &struct hlist_node to use as a loop cursor.
|
||||
* @member: the name of the hlist_node within the struct.
|
||||
*/
|
||||
#define hlist_for_each_entry_continue(tpos, pos, member) \
|
||||
for (pos = (pos)->next; \
|
||||
pos && ({ prefetch(pos->next); 1;}) && \
|
||||
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
|
||||
pos = pos->next)
|
||||
|
||||
/**
|
||||
* hlist_for_each_entry_from - iterate over a hlist continuing from current point
|
||||
* @tpos: the type * to use as a loop cursor.
|
||||
* @pos: the &struct hlist_node to use as a loop cursor.
|
||||
* @member: the name of the hlist_node within the struct.
|
||||
*/
|
||||
#define hlist_for_each_entry_from(tpos, pos, member) \
|
||||
for (; pos && ({ prefetch(pos->next); 1;}) && \
|
||||
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
|
||||
pos = pos->next)
|
||||
|
||||
/**
|
||||
* hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
|
||||
* @tpos: the type * to use as a loop cursor.
|
||||
* @pos: the &struct hlist_node to use as a loop cursor.
|
||||
* @n: another &struct hlist_node to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the hlist_node within the struct.
|
||||
*/
|
||||
#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \
|
||||
for (pos = (head)->first; \
|
||||
pos && ({ n = pos->next; 1; }) && \
|
||||
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
|
||||
pos = n)
|
||||
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user