blob: 1d752ed88866c55bbafc8179fd63edc6f3093df7 [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001/******************************************************************************
2*(C) Copyright 2008 Marvell International Ltd.
3* All Rights Reserved
4******************************************************************************/
5/*****************************************************************************
6* Implementation for multiple clients AT commands
7*
8* Implemented with a Match table, where each line consists of an AT commands
9* couple and a mutex. When enabled, the first AT Command which matches an entry
10* in the match table will take the mutex, causing consecutive clients to sleep
11* until completion.
12*
13*****************************************************************************/
14#include <pthread.h>
15#include "utlError.h"
16#include "atcmd_mult.h"
17#include "atcmd_mult_table.h"
18
19#define DEBUG
20#if defined(DEBUG)
21#define TRACE(CAT) DBGMSG(CAT, "(%s) %s:%d: I am here\n", __func__, __FILE__, __LINE__)
22#define ENTER(CAT) DBGMSG(CAT, "(%s) %s:%d: ENTER\n", __func__, __FILE__, __LINE__)
23#define EXIT(CAT) DBGMSG(CAT, "(%s) %s:%d: EXIT\n", __func__, __FILE__, __LINE__)
24#define LOGD(CAT, fmt, args ...) DBGMSG(CAT, "(%s) %s:%d: "fmt, __func__, __FILE__, __LINE__, ##args)
25#define LOGE(CAT, fmt, args ...) ERRMSG(CAT, "(%s) %s:%d: "fmt, __func__, __FILE__, __LINE__, ##args)
26#define LOGI(CAT, fmt, args ...) INFOMSG(CAT, "(%s) %s:%d: "fmt, __func__, __FILE__, __LINE__, ##args)
27#else
28#define TRACE()
29#define LOGD(fmt, args ...)
30#define LOGE(fmt, args ...)
31#define LOGI(fmt, args ...)
32#endif
33
34#define ARRAY_SIZE(v) (sizeof(v) / sizeof((v)[0]))
35#define MAX_PRINT_SIZE 128
36/**
37 * struct atcmd_entry
38 * describes a specific AT command using name and operation
39 * e.g. AT+COPS=? --> name: COPS op:=?
40 */
41struct atcmd_entry {
42 char *name;
43 utlAtCommandType_T type;
44 utlAtRequestType_T op;
45};
46
47/**
48 * struct table entry
49 * consists of an at commands couple and a mutex
50 */
51struct table_entry {
52 struct atcmd_entry cmd[NUM_ENTRIES];
53 pthread_mutex_t *lock;
54 unsigned int parser_id; /* owner parser id */
55};
56
57static struct table_entry *match_table;
58static ssize_t match_table_size;
59
60/**
61 * atcmd2op
62 * returns the request utlAtRequestType_T enum.
63 *
64 * @param atcmd at command full name, e.g. +COPS=?
65 *
66 * @return request type enum
67 * Example:
68 * name - "+COPS=?"
69 * return value - utlAT_REQUEST_TYPE_SYNTAX
70 */
71static inline utlAtRequestType_T atcmd2op(const char *atcmd)
72{
73 if (!atcmd)
74 return utlAT_REQUEST_TYPE_UNKNOWN;
75
76 if (strstr(atcmd, "%")) /* match any */
77 return utlAT_REQUEST_TYPE_ALL;
78 if (strstr(atcmd, "=?"))
79 return utlAT_REQUEST_TYPE_SYNTAX;
80 if (strstr(atcmd, "="))
81 return utlAT_REQUEST_TYPE_SET;
82 if (strstr(atcmd, "?"))
83 return utlAT_REQUEST_TYPE_GET;
84
85 return utlAT_REQUEST_TYPE_UNKNOWN;
86}
87
88/**
89 * atcmd2type
90 * get at command type enum from a give AT command string
91 *
92 * @param atcmd string containing AT command
93 *
94 * @return AT command type enum
95 */
96static inline utlAtCommandType_T atcmd2type(const char *atcmd)
97{
98 if (!atcmd)
99 return utlAT_COMMAND_TYPE_UNKNOWN;
100 if (atcmd2op(atcmd) == utlAT_REQUEST_TYPE_UNKNOWN)
101 return utlAT_COMMAND_TYPE_BASIC;
102 return utlAT_COMMAND_TYPE_EXTENDED;
103}
104
105/**
106 * op2str
107 *
108 * @param op operation
109 *
110 * @return string representing the requested operation
111 */
112static inline const char *op2str(utlAtRequestType_T op)
113{
114 switch (op) {
115 case utlAT_REQUEST_TYPE_GET: return "?";
116 case utlAT_REQUEST_TYPE_SYNTAX: return "=?";
117 case utlAT_REQUEST_TYPE_SET: return "=";
118 default: break;
119 }
120 return "#?#"; /* UNKNOWN */
121}
122
123/**
124 * atcmdncmp
125 * compare two at commands with fixed len
126 *
127 * @param e1 first at command
128 * @param e2 seccond at command
129 * @param len num of characters to compare
130 *
131 * @return 1 if e1 equals e2, 0 otherwise
132 */
133static inline int atcmdncmp(struct atcmd_entry *e1, struct atcmd_entry *e2,
134 ssize_t len)
135{
136 if ((e1->type == e2->type) && (e1->op == e2->op))
137 return !strncmp(e1->name, e2->name, len);
138 return 0;
139}
140
141/**
142 * atcmdcmp
143 * compare two at commands
144 *
145 * @param e1 first at command
146 * @param e2 seccond at command
147 *
148 * @return 1 if e1 equals e2, 0 otherwise
149 */
150static inline int atcmdcmp(struct atcmd_entry *e1, struct atcmd_entry *e2)
151{
152 if ((e1->type == e2->type) && (e1->op == e2->op))
153 return !strcmp(e1->name, e2->name);
154 return 0;
155}
156
157/**
158 * entry_match
159 * check for any match between two table entries
160 *
161 * @param e1 first entry
162 * @param e2 seccond entry
163 *
164 * @return 1 for match, 0 otherwise
165 */
166static inline int entry_match(struct table_entry *e1, struct table_entry *e2)
167{
168 int i,j;
169
170 for (i = 0; i < NUM_ENTRIES; i++) {
171 for (j = 0; j < NUM_ENTRIES; j++) {
172 if (atcmdcmp(&e1->cmd[i], &e2->cmd[j]))
173 return 1;
174 }
175 }
176
177 return 0;
178}
179
180/**
181 * merge_locks
182 *
183 * Change all lines with lock2 to use lock1, and free lock2
184 *
185 * @param lock1 lock to be used for lines containing lock 2
186 * @param lock2
187 */
188static inline void merge_locks(pthread_mutex_t *lock1, pthread_mutex_t *lock2)
189{
190 struct table_entry *e;
191 int i;
192
193 ASSERT(lock1);
194 ASSERT(lock2);
195
196 for (i = 0; i < match_table_size; i++) {
197 e = &match_table[i];
198 if (e->lock == lock2)
199 e->lock = lock1;
200 }
201 free(lock2);
202}
203
204/**
205 * match_table_init
206 *
207 * This function builds the actual match table according to the const table
208 * supplied in atcmd_mult_table.h.
209 *
210 * Algorithm:
211 *
212 * First run - parse all lines in the original table to at command name and op,
213 * and set the line lock to NULL.
214 *
215 * Second run - initialize locks which defines the logical relation between AT
216 * commands - at first initialize locks per line, then merge locks for lines
217 * sharing AT commands.
218 *
219 * Output of the algorithm is a table where each line points to a lock.
220 * If 2 lines points to the same lock, they are logically merged - so all
221 * AT commands in these lines will block each other.
222 *
223 * @return 0 for success, error code otherwise
224 */
225static int match_table_init(void)
226{
227 struct table_entry *e1, *e2;
228 struct atcmd_entry *cmd;
229 const char *atcmd_str;
230 ssize_t i, j;
231 pthread_mutexattr_t attrmutex;
232
233 ENTER(match_table_init);
234
235 pthread_mutexattr_init(&attrmutex);
236 pthread_mutexattr_setpshared(&attrmutex, PTHREAD_PROCESS_SHARED);
237
238 match_table_size = ARRAY_SIZE(g_match_table);
239 match_table = malloc(match_table_size * sizeof(struct table_entry));
240 ASSERT(match_table);
241
242 /* Build dynamic match table - stage 1*/
243 for (i = 0; i < match_table_size; i++) {
244 match_table[i].lock = NULL;
245 match_table[i].parser_id = 0x0e0e0e0e;
246 cmd = match_table[i].cmd;
247 for (j = 0; j < NUM_ENTRIES; j++) {
248 LOGE(match_table_init1, "g_match_table[%d][%d]=%s", i, j, g_match_table[i][j]);
249 atcmd_str = g_match_table[i][j];
250 if (!atcmd_str) {
251 cmd[j].op = -1;
252 cmd[j].name = NULL;
253 cmd[j].type = -1;
254 } else {
255 cmd[j].op = atcmd2op(atcmd_str);
256 cmd[j].name = strdup(atcmd_str);
257 cmd[j].type = atcmd2type(atcmd_str);
258 }
259 }
260 }
261
262 /* Build dynamic match table - stage 2*/
263 for (i = 0; i < match_table_size; i++) {
264 e1 = &match_table[i];
265 ASSERT(!e1->lock);
266
267 /* try to find a matching previously initialized table entry*/
268 for (j = 0; j < i; j++) {
269 e2 = &match_table[j];
270 ASSERT(e2->lock);
271 if (entry_match(e1, e2)) {
272 if (!e1->lock) /* first match - use match lock*/
273 e1->lock = e2->lock;
274 else if (e1->lock != e2->lock) /* second match - merge locks */
275 merge_locks(e1->lock, e2->lock);
276 }
277 }
278 /* have we found a match? */
279 if (e1->lock)
280 continue;
281
282 /* no match found, initialize lock*/
283 e1->lock = malloc(sizeof(pthread_mutex_t));
284 ASSERT(e1->lock);
285 pthread_mutex_init(e1->lock, &attrmutex);
286 }
287
288 EXIT(match_table_init2);
289
290 return 0;
291}
292
293
294/**
295 * match_table_dump
296 *
297 * dump the built match table
298 */
299static void match_table_dump(void)
300{
301#ifdef DEBUG
302 struct table_entry *e;
303 char buf[MAX_PRINT_SIZE];
304 int i, j, len = 0;
305
306 memset(buf, 0, sizeof(buf));
307
308 LOGI(match_table_dump, "match table dump");
309 LOGI(match_table_dump1, "================");
310
311 for (i = 0; i < NUM_ENTRIES; i++) {
312 len += snprintf(&buf[len], MAX_PRINT_SIZE - len, " cmd%d |", i);
313 ASSERT(len < MAX_PRINT_SIZE);
314 }
315 LOGI(match_table_dump2, "%s mutex |", buf);
316
317 for (i = 0; i < match_table_size; i++) {
318 len = 0;
319 memset(buf, 0, sizeof(buf));
320 e = &match_table[i];
321 for (j = 0; j < NUM_ENTRIES; j++) {
322 len += snprintf(&buf[len], MAX_PRINT_SIZE - len, "%12s", e->cmd[j].name);
323 ASSERT(len < MAX_PRINT_SIZE);
324 }
325 len += snprintf(&buf[len], MAX_PRINT_SIZE - len, "%13p", e->lock);
326 LOGI(match_table_dump3, "%s", buf);
327 }
328
329 LOGI(match_table_dump4, "match table dump done");
330#endif /* DEBUG */
331}
332
333/**
334 * match
335 * iterate through the match table looking for at command match
336 *
337 * @param atcmd at command name string
338 * @param op operation string
339 *
340 * @return table_entry pointer if a match was found, NULL otherwise
341 */
342static struct table_entry *match(const char *atcmd, utlAtCommandType_T type,
343 utlAtRequestType_T op)
344{
345 struct table_entry *e;
346 ssize_t i, j;
347
348 //ENTER(match);
349
350 //LOGD(match1, "command=%s, type=%d, op=%d\n", atcmd, type, op);
351 if (type == utlAT_COMMAND_TYPE_EXACTION)
352 return NULL; /* not supported */
353
354 for (i = 0; i < match_table_size; i++) {
355 e = &match_table[i];
356 for (j = 0; j < NUM_ENTRIES; j++) {
357 //LOGD(match2, "entry: command=%s, type=%d, op=%d\n",
358 // e->cmd[j].name, e->cmd[j].type, e->cmd[j].op);
359 if (e->cmd[j].type != type)
360 continue;
361 if ((type == utlAT_COMMAND_TYPE_EXTENDED) &&
362 (e->cmd[j].op != utlAT_REQUEST_TYPE_ALL) &&
363 (e->cmd[j].op != op))
364 continue;
365 if (!strncmp(atcmd, e->cmd[j].name, strlen(atcmd))) {
366 //LOGD(match3, "MATCH found (AT%s%s)\n", atcmd,
367 // type == utlAT_COMMAND_TYPE_EXTENDED ?
368 // op2str(op) : "");
369 return e;
370 }
371 }
372 }
373
374 //EXIT(match4);
375 return NULL;
376}
377
378/**
379 * type2op
380 * translate from utlAtRequestType_T to utlAtParameterOp_T
381 *
382 * @param type
383 *
384 * @return
385 */
386static inline utlAtParameterOp_T type2op(utlAtRequestType_T type)
387{
388 switch(type) {
389 case utlAT_REQUEST_TYPE_SYNTAX:
390 return utlAT_PARAMETER_OP_SYNTAX;
391 case utlAT_REQUEST_TYPE_SET:
392 return utlAT_PARAMETER_OP_SET;
393 case utlAT_REQUEST_TYPE_GET:
394 return utlAT_PARAMETER_OP_GET;
395 default:
396 return utlAT_PARAMETER_OP_UNKNOWN;
397 }
398}
399
400/**
401 * is_proxy
402 * check if given command is a proxy command
403 *
404 * @param parser
405 * @param cmd AT Command to check
406 * @param type request type
407 *
408 * @return 1 if proxy, 0 otherwise
409 */
410static int is_proxy(const utlAtParser_P parser, const utlAtCommand_P2c cmd)
411{
412 unsigned int id;
413 utlAtParameterOp_T op;
414
415 if (!parser || !parser->call_backs.arg_p ||
416 !parser->call_backs.isProxyReq_function_p)
417 return 0;
418
419 op = (cmd->type == utlAT_COMMAND_TYPE_EXTENDED) ?
420 type2op(cmd->type) :
421 type2op(utlAT_REQUEST_TYPE_SET); /* basic command */
422
423 id = *(unsigned int*)(parser->call_backs.arg_p);
424
425 return parser->call_backs.isProxyReq_function_p(cmd->name_p, op, id);
426}
427
428/**
429 * atmcd_mult_lock
430 * Lock an at command table entry if found
431 *
432 * @param parser
433 * @param cmd
434 * @param type
435 *
436 * @return -1 for error, 0 otherwise
437 */
438utlReturnCode_T atmcd_mult_lock(const utlAtParser_P parser,
439 utlAtCommand_P2c cmd, utlAtRequestType_T type)
440{
441 struct table_entry *e;
442
443 //ENTER(atmcd_mult_lock);
444
445 if (!parser || !parser->call_backs.arg_p || !cmd || !cmd->name_p) {
446 LOGE(atmcd_mult_lock1, "ERROR: Invalid params! parser=0x%x, cmd=0x%x\n", parser, cmd);
447 return -1;
448 }
449
450 if (is_proxy(parser, cmd))
451 return 0;
452
453 LOGD(atmcd_mult_lock2, "command=AT%s%s\n", cmd->name_p,
454 cmd->type == utlAT_COMMAND_TYPE_EXTENDED ? op2str(type) : "");
455
456 e = match(cmd->name_p, cmd->type, type);
457 if (e) {
458 LOGD(atmcd_mult_lock3, "LOCK [0x%x]: AT%s%s\n", e->lock, cmd->name_p,
459 cmd->type == utlAT_COMMAND_TYPE_EXTENDED ? op2str(type) : "");
460 pthread_mutex_lock(e->lock);
461 e->parser_id = *(unsigned int*)parser->call_backs.arg_p;
462 }
463
464 //EXIT(atmcd_mult_lock4);
465 return 0;
466}
467
468/**
469 * atmcd_mult_unlock
470 * unlock a matching command table entry if
471 * found
472 *
473 * @param parser
474 * @param cmd
475 * @param type
476 *
477 * @return -1 for error, 0 otherwise
478 */
479utlReturnCode_T atmcd_mult_unlock(const utlAtParser_P parser,
480 utlAtCommand_P2c cmd, utlAtRequestType_T type)
481{
482 struct table_entry *e;
483
484 ENTER(atmcd_mult_unlock);
485
486 if (!parser || !parser->call_backs.arg_p || !cmd || !cmd->name_p) {
487 LOGE(atmcd_mult_unlock1, "ERROR: Invalid params! parser=0x%x, cmd=0x%x\n", parser, cmd);
488 return -1;
489 }
490
491 LOGD(atmcd_mult_unlock2, "command=AT%s%s\n", cmd->name_p,
492 cmd->type == utlAT_COMMAND_TYPE_EXTENDED ? op2str(type) : "");
493
494 e = match(cmd->name_p, cmd->type, type);
495 if (e) {
496 if (e->parser_id == *(unsigned int*)parser->call_backs.arg_p) {
497 LOGD(atmcd_mult_unlock3, "UNLOCK [0x%x]: AT%s%s\n", e->lock, cmd->name_p,
498 cmd->type == utlAT_COMMAND_TYPE_EXTENDED ? op2str(type) : "");
499 pthread_mutex_unlock(e->lock);
500 e->parser_id = 0x0e0e0e0e;
501 }
502 }
503
504 EXIT(atmcd_mult_unlock4);
505 return 0;
506}
507
508
509/**
510 * atcmd_mult_init
511 * initialize multiple clinets support
512 * functionality.
513 *
514 * @return 0 for success, error code otherwise
515 */
516int atcmd_mult_init(void)
517{
518 static int init_done = 0;
519
520 ENTER(atcmd_mult_init);
521 if (init_done)
522 return 0;
523
524 match_table_init();
525 match_table_dump();
526 init_done = 1;
527
528 EXIT(atcmd_mult_init1);
529 return 0;
530}