From 7878c6874c028a55dfe65e55a672522c8714328f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=AD=94=E7=BD=97=E6=8A=80=E6=9C=AF?= Date: Sat, 13 Jun 2020 12:24:28 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A6=96=E6=AC=A1=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.en.md | 36 --- README.md | 36 +-- at_chat.c | 487 +++++++++++++++++++++++++++++++++++ at_chat.h | 139 ++++++++++ list.h | 703 +++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1348 insertions(+), 53 deletions(-) delete mode 100644 README.en.md create mode 100644 at_chat.c create mode 100644 at_chat.h create mode 100644 list.h diff --git a/README.en.md b/README.en.md deleted file mode 100644 index 386591d..0000000 --- a/README.en.md +++ /dev/null @@ -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/) diff --git a/README.md b/README.md index 8eb9c7a..4f8fc77 100644 --- a/README.md +++ b/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?"); #### 码云特技 diff --git a/at_chat.c b/at_chat.c new file mode 100644 index 0000000..35d7cfd --- /dev/null +++ b/at_chat.c @@ -0,0 +1,487 @@ +/******************************************************************************* +* @brief AT ͨŹ(OS汾) +* +* Change Logs +* Date Author Notes +* 2016-01-22 Morro Initial version. +* 2018-02-11 Morro ʹʽйATҵ +* 2020-05-21 Morro ֶ֧AT +*******************************************************************************/ + +#include "at_chat.h" +#include +#include +#include + +//ʱж +#define AT_IS_TIMEOUT(start, time) (at_get_ms() - (start) > (time)) + +/*ATCOMM work type -----------------------------------------------------------*/ +#define AT_TYPE_WORK 0 /* --------------*/ +#define AT_TYPE_CMD 1 /*׼ ----------*/ +#define AT_TYPE_MULTILINE 3 /* ----------*/ +#define AT_TYPE_SINGLLINE 4 /* ----------*/ + +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 + */ +static void send_data(at_core_t *ac, const void *buf, unsigned int len) +{ + ac->cfg.write(buf, len); +} + +/* + * @brief ʽӡ + */ +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 ȡǰݽճ + */ +static unsigned int get_recv_count(at_core_t *ac) +{ + return ac->rcv_cnt; +} + +/* + * @brief ȡݻ + */ +static char *get_recv_buf(at_core_t *ac) +{ + return (char *)ac->cfg.rcv_buf; +} + +/* + * @brief ݻ + */ +static void recv_buf_clear(at_core_t *ac) +{ + ac->rcv_cnt = 0; +} + +/*ǰִ*/ +static char *search_string(at_core_t *ac, const char *str) +{ + return strstr(get_recv_buf(ac), str); +} + +/*ǰִ*/ +static bool at_isabort(at_core_t *ac) +{ + return ac->cursor ? ac->cursor->abort : 1; +} + + +/* + * @brief ATִлص + */ +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 + * @param[in] cfg - ATӦ + */ +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; +} +/*ҵ*/ +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)) //޿at_item + return NULL; + i = list_first_entry(&ac->ls_idle, at_item_t, node);//ӿȡҵ + 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); // + ac->cfg.unlock(); + return i != 0; +} + +/* + * @brief ִ + */ +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 ִͨ + * @param[in] a - AT + * @return 0 - ֹ,0 - + ******************************************************************************/ +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: /*״̬ ------------------------------------------------------*/ + c->sender(e); + e->state++; + e->reset_timer(a); + e->recvclr(a); + break; + case 1: /*״̬ ------------------------------------------------------*/ + 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; /*֮ʱһʱ*/ + e->reset_timer(a); /*öʱ*/ + } 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; /*һ״̬*/ + } + break; + case 2: + if (e->is_timeout(a, 500)) + e->state = 0; /*سʼ״̬*/ + break; + default: + e->state = 0; + } + return false; +} + +/******************************************************************************* + * @brief + * @param[in] a - AT + * @return 0 - ֹ,0 - + ******************************************************************************/ +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: /*״̬ ------------------------------------------------------*/ + e->printf(a, cmd); + e->state++; + e->reset_timer(a); + e->recvclr(a); + break; + case 1: /*״̬ ------------------------------------------------------*/ + 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; /*֮ʱһʱ*/ + e->reset_timer(a); /*öʱ*/ + } 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; /*һ״̬*/ + } + break; + case 2: + if (e->is_timeout(a, 500)) + e->state = 0; /*سʼ״̬*/ + break; + default: + e->state = 0; + } + return false; +} +/******************************************************************************* + * @brief + * @param[in] a - AT + * @return 0 - ֹ,0 - + ******************************************************************************/ +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) { /*ִ*/ + do_at_callback(a, i, cb, AT_RET_OK); + return true; + } + e->printf(a, "%s\r\n", cmds[e->i]); + e->recvclr(a); /**/ + 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; /*֮ʱһʱ*/ + e->reset_timer(a); /*öʱ*/ + } 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 + * @param[in] fmt - ʽ + * @param[in] args - б + */ +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); //սջ + send_data(ac, buf, len); + send_data(ac, "\r\n", 2); +} +/* + * @brief urc + * @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 մ + * @param[in] buf - ݻ + * @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 ָӦմ + * @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) // + ac->rcv_cnt = 0; + + memcpy(rcv_buf, buf, size); + ac->rcv_cnt += size; + rcv_buf[ac->rcv_cnt] = '\0'; + +} + +/* + * @brief ִATҵ + * @param[in] a - AT + * @param[in] work - ATҵ + * @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 ִATָ + * @param[in] a - AT + * @param[in] cmd - cmd + */ +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 ͵AT + * @param[in] ac - AT + * @param[in] cb - ִлص + * @param[in] singlline - + * @note ִ֮ǰ,singllineʼЧ + */ +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 ͶAT + * @param[in] ac - AT + * @param[in] cb - ִлص + * @param[in] multiline - + * @note ִ֮ǰ,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 ǿֹATҵ + */ + +void at_item_abort(at_item_t *i) +{ + i->abort = 1; +} + +/* + * @brief ATæж + * @return true - ATִָ + */ +bool at_core_busy(at_core_t *ac) +{ + return !list_empty(&ac->ls_ready); +} + +/******************************************************************************* + * @brief ATҵ + ******************************************************************************/ +static void at_work_manager(at_core_t *ac) +{ + register at_item_t *cursor = ac->cursor; + at_env_t *e = &ac->env; + /*ͨù ---------------------------------------------------------*/ + 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)) //Ϊ + 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); + } + /*ִ,뵽й ------------------------------------*/ + 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ѯ + */ +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); +} + + diff --git a/at_chat.h b/at_chat.h new file mode 100644 index 0000000..8869f87 --- /dev/null +++ b/at_chat.h @@ -0,0 +1,139 @@ +/******************************************************************************* +* @brief AT ͨŹ(OS汾) +* +* Change Logs +* Date Author Notes +* 2016-01-22 Morro Initial version. +* 2018-02-11 Morro ʹʽйATҵ +* 2020-05-21 Morro ֶ֧AT +*******************************************************************************/ +#ifndef _ATCHAT_H_ +#define _ATCHAT_H_ + +#include "at_util.h" +#include +#include + +#define MAX_AT_CMD_LEN 128 + +struct at_core; + +/*urc -----------------------------------------------------------------*/ +typedef struct { + const char *prefix; //Ҫƥͷ + void (*handler)(char *recvbuf, int size); +}utc_item_t; + +typedef struct { + unsigned int (*write)(const void *buf, unsigned int len); /*ͽӿ*/ + unsigned int (*read)(void *buf, unsigned int len); /*սӿ*/ + void (*lock)(void); /*,OS*/ + void (*unlock)(void); /*,OS*/ + /*Events -----------------------------------------------------------------*/ + void (*before_at)(void); /*ʼִAT*/ + void (*after_at)(void); + void (*error)(void); + utc_item_t *utc_tbl; /*utc */ + unsigned char *urc_buf; /*urcջ*/ + unsigned char *rcv_buf; + unsigned short urc_tbl_count; + unsigned short urc_bufsize; /*urcС*/ + unsigned short rcv_bufsize; /*ջ*/ +}at_core_conf_t; + +/*ATҵл*/ +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); /*ʱж*/ + void (*printf)(struct at_core *ac, const char *fmt, ...); + char * (*find)(struct at_core *ac, const char *expect); + char * (*recvbuf)(struct at_core *ac); /*ָջ*/ + unsigned int(*recvlen)(struct at_core *ac); /*ܳ*/ + void (*recvclr)(struct at_core *ac); /*սջ*/ + bool (*abort)(struct at_core *ac); /*ִֹ*/ +}at_env_t; + +/*ATӦ*/ +typedef enum { + AT_RET_OK = 0, /*ִгɹ*/ + AT_RET_ERROR, /*ִд*/ + AT_RET_TIMEOUT, /*Ӧʱ*/ + AT_RET_ABORT, /*ǿֹ*/ +}at_return; + +/*ATӦ */ +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, /*״̬*/ + AT_STATE_WAIT, /*ȴִ*/ + AT_STATE_EXEC, /*ִ*/ +}at_work_state; + +/*ATҵ*/ +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 ------------------------------------------------------------------*/ +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; /*,ҵ*/ + unsigned int resp_timer; + unsigned int urc_timer; + at_return ret; + //urcռ, Ӧռ + unsigned short urc_cnt, rcv_cnt; + unsigned char suspend: 1; +}at_core_t; + +typedef struct { + void (*sender)(at_env_t *e); /*Զ巢 */ + const char *matcher; /*ƥ䴮 */ + at_callback_t cb; /*Ӧ */ + unsigned char retry; /*Դ */ + unsigned short timeout; /*ʱʱ */ +}at_cmd_t; + +void at_core_init(at_core_t *ac, const at_core_conf_t cfg); + +/*͵AT*/ +bool at_send_singlline(at_core_t *ac, at_callback_t cb, const char *singlline); +/*ͶAT*/ +bool at_send_multiline(at_core_t *ac, at_callback_t cb, const char **multiline); +/*ִAT*/ +bool at_do_cmd(at_core_t *ac, void *params, const at_cmd_t *cmd); +/*ԶATҵ*/ +bool at_do_work(at_core_t *ac, int (*work)(at_env_t *e), void *params); + +void at_item_abort(at_item_t *it); /*ֹǰҵ*/ + +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 diff --git a/list.h b/list.h new file mode 100644 index 0000000..565be8b --- /dev/null +++ b/list.h @@ -0,0 +1,703 @@ +#ifndef _LINUX_LIST_H +#define _LINUX_LIST_H + +#include + +#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 , */ +/* + * 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