LingYun IoT Studio NB-IoT research project
guowenxue
2018-11-19 3509b6dfaad073c2b7887031c917c12b8ed5caa1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
/*********************************************************************************
 *      Copyright:  (C) guowenxue<guowenxue@gmail.com>
 *                  All rights reserved.
 *
 *       Filename:  module.c
 *    Description:  This is the GPRS Power/SIM card control source code
 *                 
 *        Version:  1.0.0(02/07/2012~)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "02/07/2012 03:08:25 PM"
 *                 
 ********************************************************************************/
 
#include "cp_gprs.h"
#include "cp_proc.h"
 
int init_gsm_module(modinfo_t *module)
{
    int                   i=0;
    char                  devname[DEVNAME_LEN];
 
    log_info("Initialize GSM module context\n");
 
    memset(module, 0, sizeof(*module));
 
    module->event = REQ_POWER_RESET;  /* Request to power on GPRS */
 
    pthread_mutex_init(&module->lock, NULL); 
 
    if( !(module->gsmport=comport_init(GSM_DATAPORT, 115200, "8N1N")) )
    {
        return -1;
    }
 
    /*  Initialize the GPRS dataport pointer */
    module->comport_cnt = MAX_DATAPORT;
    for(i=0; i<module->comport_cnt; i++)
    {
        snprintf(devname, DEVNAME_LEN, "%s%d", CMUX_DATAPORT, i+CMUX_PORT_START_INDEX);
        module->comport[i] = comport_init(devname, 115200, "8N1N");
        if(NULL == module->comport[i])
            goto ERROR;
 
        //log_dbg("Initialise comport [%p] on %s on baudrate 115200 with 8N1N\n", module->comport[i], devname);
    } 
    
    return 0;
 
ERROR:
    while(i-- >= 0)
    {
        comport_term(module->comport[i]);
    }
    return -2;
}
 
 
/*
 *  Description: close all the GPRS control port device. 
 *
 * Return Value: NONE
 */
void close_gsm_dataport(modinfo_t *module)
{
    int  i;
    
    for(i=0; i<module->comport_cnt; i++)
    {
        log_nrml("Close GPRS dataport %s=>[fd:%d]\n", module->comport[i]->dev_name, module->comport[i]->fd);
        comport_close(module->comport[i]);
    }
 
    return ;
}
 
/*
 *  Description: open all the GPRS data port device. 
 *
 * Return Value: 0: Open successfully  !0: Open failure
 */
int open_gsm_dataport(modinfo_t *module)
{
    int i, retval = -1;
 
    for(i=0; i<module->comport_cnt; i++)
    {
        if( (retval=comport_open(module->comport[i])) < 0)
        {
            log_err("Open GPRS dataport %s failure: %d\n", module->comport[i]->dev_name, retval);
            goto ERROR;
        } 
        log_nrml("Open GPRS dataport[%p] %s=>[fd:%d]\n", module->comport, module->comport[i]->dev_name, module->comport[i]->fd);
    }
 
    return 0;
 
ERROR:
    close_gsm_dataport(module);
    return retval;
}
 
 
/*
 *  Description: Alloc a comport from the comport poor for used, for we must make sure 
 *               PPP thread can grasp a comport to work, so we will always preserved
 *               first comport for PPP thread.
 *              
 * Return Value: NULL: No comport left  !NULL: The alloced comport pointer
 */
comport_t *alloc_gsm_dataport(char *who, modinfo_t *module)
{
    static int     lock = 0;
    int            i;
    comport_t       *comport = NULL;
 
    if(lock)
        return NULL;
 
    lock = 1;
 
    /* Preserve first comport for PPP thread */
    for(i=0; i<module->comport_cnt; i++)
    {
        if(module->comport[i]->used != YES)
        {
            comport = module->comport[i];
            comport->used = YES;
            break;
        }
    }
 
    lock = 0;
 
    if(comport)
    {
        log_dbg("%s allocate GPRS dataport[%d] %s=>[fd:%d] ok\n", who, i, comport->dev_name, comport->fd);
        module->users++;
    }
    else
    {
        log_dbg("%s allocate GPRS dataport failure\n", who);
    }
 
 
    return comport;
}
 
/*
 *  Description: Free a comport to the comport poor when we no need it.
 *
 * Return Value: NONE
 */
void free_gsm_dataport(char *who, modinfo_t *module, comport_t  **comport)
{
    int            i;
 
    /* Preserve first comport for PPP thread */
    for(i=0; i<module->comport_cnt; i++)
    {
        if(module->comport[i] == *comport)
        {
            log_dbg("%s free GPRS dataport %s=>[fd:%d]\n", who, (*comport)->dev_name, (*comport)->fd);
            module->comport[i]->used = NO;
            module->users--;
            *comport = NULL;
            break;
        }
    }
 
    return ;
}
 
 
/*
 *  Description: Power on the GPRS module and open all the gprs data port. For when GPRS power off,
 *               the GPRS module USB convert serial comport device lost, so we must bind them together.
 * Return Value: 0: Open successfully  !0: Open failure
 */
int power_on_module(modinfo_t *module)
{
    int     rv = 0;
    log_dbg("Turn on GPRS module now\n");
 
    micro_second_sleep(500);
 
    if( comport_open(module->gsmport) < 0)
    {
        log_err("Open GSM serial port [%s] failure\n", module->gsmport->dev_name);
        return -2;
    }
 
    if( !atcmd_check_power_on(module->gsmport) ) 
    {
        log_err("Send ATE0 check GPRS module AT command failure\n", module->gsmport->dev_name);
        return -2;
    }
 
    if( 0 != open_gsm_dataport(module) )
    {
        rv = -3;
        log_err("Open GPRS module comport failure\n");
        goto cleanup;
    }
 
cleanup:
 
    if(!rv)
    {
        module->ready = MODULE_READY;
        log_nrml("Turn on GPRS module succesfully\n");
    }
    else
    {
        module->ready = NOT_READY;
        log_nrml("Turn on GPRS module failed, rv=%d\n", rv);
    }
 
    module->ready = 0!=rv ? NOT_READY : MODULE_READY;
    memset(&(module->reg), 0, sizeof(reginfo_t));
 
    return rv;
}
 
/*
 *  Description: Power off the GPRS module and close all the gprs data port. For when GPRS power off,
 *               the GPRS module USB convert serial comport device lost, so we must bind them together.
 * Return Value: 0: Open successfully  !0: Open failure
 */
int power_off_module(modinfo_t *module)
{
    while(module->users)
    {
        log_warn("%d application still use the GPRS module dataport, wait released...\n", module->users);
        sleep(1);
    }
 
    log_nrml("Turn off GPRS module now\n");
    close_gsm_dataport(module);
 
    comport_close(module->gsmport);
 
    module->ready = NOT_READY;
    memset(&(module->reg), 0, sizeof(reginfo_t));
 
    micro_second_sleep(800);
    return 0;
}
 
void set_module_event(modinfo_t *module, int request)
{
    log_dbg("Set the module event request[%d]\n", request);
    pthread_mutex_lock(&module->lock);
    module->event = request;
    pthread_mutex_unlock(&module->lock);
}
 
/*
 *  Description: Set the GPRS power status according to the module request event. 
 *               the GPRS module USB convert serial comport device lost, so we must bind them together.
 * Return Value: 0:Set GPRS power status successfully  !0: Open failure
 */
int set_module_event_power(modinfo_t *module)
{
    int           rv = 0;
    switch (module->event)
    { 
        case REQ_POWER_ON:
            rv = power_on_module(module);
            break; 
 
        case REQ_POWER_OFF: 
            rv = power_off_module(module);
            break; 
        
        case REQ_POWER_RESET: 
            rv = power_off_module(module);
            rv = power_on_module(module); 
            break;
    } 
    
    if( rv )
    {
        log_nrml("Response for the GPRS request event %d failure, rv=%d\n", module->event, rv);
        return rv;
    }
    else
    { 
        log_nrml("Response for the GPRS request event %d and clear this event ok\n", module->event);
        set_module_event(module, REQ_EVENT_NONE);
        return 0;
    }
}
 
 
int atcmd_inspect_status(modinfo_t *module)
{
    comport_t        *comport;
    reginfo_t   *reg = &(module->reg);   /* SIM card Register information  */ 
    char            *who = "atcmd_inspect_status()";
 
    static unsigned long start_time ;
 
    /* Every 10 seconds we will inspect it */
    if(start_time && time_elapsed(start_time) < 10000)
    {
        return 0;
    }
 
    start_time= time_now();
 
    comport = alloc_gsm_dataport(who, module);
    if(NULL != comport)
    {
        log_dbg("Alloc dataport %s=>[fd:%d]\n", comport->dev_name, comport->fd);
    }
    else
        return -1;
 
    reg->signal = atcmd_check_gprs_signal(comport);
 
    atcmd_check_gprs_location(comport, &reg->loc);
 
    log_dbg("Free dataport %s=>[fd:%d]\n", comport->dev_name, comport->fd);
    free_gsm_dataport(who, module, &comport);
 
    return 0;
}
 
 
/*
 * Description: Check the AT command and SIM card ready or not
 * Input value: times:  Max check times
 *Return Value: 1: Already OK   0: Still on test  -1: Failure
 *
 */ 
int atcmd_check_module_ready(comport_t *comport, int times)
{
    int           rv;
    static int    fail_cnt = 0;
 
    /* Send ATE0 to check GPRS module AT ready or not  */
    rv = atcmd_check_at_ready(comport);
    if(rv) 
        goto cleanup;
 
    /* Send AT+CPIN? to check SIM card valid or not  */
    rv = atcmd_check_sim_valid(comport);
 
cleanup:
    if(rv)
    {
        if(fail_cnt < times)
        {
            fail_cnt ++;
            return 0;
        }
        else
        {
            fail_cnt = 0;
            return -1;
        }
    }
    else
    {
        fail_cnt = 0;
        return 1;
    }
}
 
int atcmd_check_power_on(comport_t *comport)
{
    int           i, rv = 0;
 
    for(i=0; i<10; i++)
    {
        if( !atcmd_check_at_ready(comport) )
        {
            rv = 1;
            break;
        }
    }
 
    return rv;
}
 
/*
 * Some preset command here, such as set Module as 2G mode
 */ 
int atcmd_module_preset(comport_t *comport)
{
    int           retval = 0;
 
    /* Send AT+COPS=0 to set GPRS module auto select an operator  */
    if(0 != (retval=send_atcmd_check_ok(comport, "AT+COPS=0\r", 3000)) )
    {
        log_warn("AT+COPS=0 Set Auto Select Carrier:     [FAILED]\n");
        return -2;
    } 
    log_nrml("AT+COPS=0 Set Auto Select Carrier:   [OK]\n");
 
    return retval;
}
 
 
int atcmd_check_hwinfo(comport_t *comport, hwinfo_t *hw, int times)
{
    int           rv;
    static int    fail_cnt = 0;
 
    /* Send AT+CGMM to check GPRS module hardware name and type */
    rv = atcmd_check_gprs_name(comport, hw->model);
    if(rv)
        goto cleanup;
 
    /* Send AT+CGMR to check GPRS module hardware version */
    rv = atcmd_check_gprs_version(comport, hw->mrev);
    if(rv)
        goto cleanup;
 
    /* Send AT+CGSN to check GPRS module hardware IEMI number*/
    rv = atcmd_check_gprs_iemi(comport, hw->iemi);
 
cleanup:
    if(rv)
    {
        if(fail_cnt < times)
        {
            fail_cnt ++;
            return 0;
        }
        else
        {
            fail_cnt = 0;
            return -1;
        }
    }
    else
    {
        fail_cnt = 0;
        return 1;
    }
}
 
 
int atcmd_check_regist(comport_t *comport, reginfo_t *reg, int times)
{
    int           rv = 0;
    static int    fail_cnt = 0;
 
    /* Send AT+CSQ to check GPRS signal strength */
    reg->signal = atcmd_check_gprs_signal(comport);
    if(reg->signal<2 && reg->signal>31)
    {
        rv = -1;
        goto cleanup;
    }
 
    /* Send AT+CREG? to check SIM card regist to network or not */
    reg->type = atcmd_check_gprs_register(comport);
    
    if(REG_HOMEWORK==reg->type || REG_ROAMING== reg->type)
    {
        /* SIM card register successfully */
        log_nrml("SIM card register successfully.\n");
        rv = 0;
    }
    else if(REG_DENIED==reg->type)
    {
        /* SIM card can not register, so request to switch SIM card */
        log_err("SIM card regist denied.\n");
        rv = -2;
        goto cleanup;
    }
    else
    {
        log_err("SIM card regist failure.\n");
        rv = -3;
        goto cleanup;
    }
 
    rv = atcmd_check_gprs_carrier(comport, reg->carrier);
    if(rv)
    {
        rv = -4;
        log_err("Check SIM card acrrier failure.\n");
        goto cleanup;
    }
 
    rv = atcmd_check_gprs_mcc_mnc(comport, reg->loc.mcc_mnc);
    if(rv)
    {
        rv = -4;
        log_err("Check SIM card register MCC-MNC failure\n");
        goto cleanup;
    }
 
 
cleanup:
    if(rv)
    {
        if(fail_cnt < times)
        {
            fail_cnt ++;
            return 0;
        }
        else
        {
            fail_cnt = 0;
            return -1;
        }
    }
    else
    {
        fail_cnt = 0;
        return 1;
    }
 
}
 
int atcmd_check_network_info(comport_t *comport, reginfo_t *reg)
{
    int           retval = 0;
 
    /* Send AT+COPS to check SIM card registed network carrier */
    retval = atcmd_check_gprs_carrier(comport, reg->carrier);
    if(retval) return -1;
 
    /* Send AT+QGSMLOC=1 to check GPRS module location */
    retval = atcmd_check_gprs_location(comport, &(reg->loc));
    if(retval) return -3;
 
    return retval;
}
 
 
int atcmd_check_ready(modinfo_t *module)
    int             rv;
    comport_t        *comport;
    char            *who = "atcmd_check_ready()";
 
    log_nrml("AT command check GPRS registry status now\n");
    /* Alloc a comport to send AT command */
    comport = alloc_gsm_dataport(who, module);
    if(NULL == comport)
    {
        log_warn("Alloc dataport for %s() failure.\n", __FUNCTION__);
        return -1;
    }
    log_dbg("Alloc dataport %s=>[fd:%d]\n", comport->dev_name, comport->fd);
 
    switch(module->ready)
    {
        case MODULE_READY:
            rv = atcmd_check_module_ready(comport, 10);
            if(rv > 0)
            {
                log_nrml("GPRS AT command ready and SIM card valid successfully\n");
                module->ready++;   /* Continue to check, not break */
            }
            else if(rv == 0)
            {
                log_nrml("GPRS AT command ready and SIM card valid check continue...\n");
            }
            else if(rv < 0)
            {
                log_nrml("GPRS AT command ready and SIM card valid check failed\n");
                set_module_event(module, REQ_POWER_RESET);
            }
            break;
 
        case CHECK_HWINFO:
            rv = atcmd_check_hwinfo(comport, &(module->hw), 10);
            if(rv > 0)
            {
                log_nrml("Check the GPRS Module hardware information successfully\n");
                module->ready++;   /* Continue to check, not break */
            }
            else if(rv == 0)
            {
                log_nrml("Check the GPRS Module hardware information continue...\n");
            }
            else if(rv < 0)
            {
                log_nrml("Check the GPRS Module hardware information failed...\n");
                set_module_event(module, REQ_POWER_RESET);
            }
            break;
 
        case CHECK_REGISTRY:
            rv = atcmd_check_regist(comport, &(module->reg), 10);
            if(rv > 0)
            {
                log_nrml("Check the SIM card registry successfully\n");
                module->ready++;   /* Continue to check, not break */
            }
            else if(rv == 0)
            {
                log_nrml("Check the SIM card registry continue...\n");
            }
            else if(rv < 0)
            {
                log_nrml("Check the SIM card registry failed\n");
                set_module_event(module, REQ_POWER_RESET);
            }
            break;
    } 
    
    free_gsm_dataport(who, module, &comport);
    return rv;
}
 
 
/*
 * Description: Terminate the control thread work context 
 * Return Value: NONE
 */
void ctrl_thread_term(void *thread_arg)
{
    int                i=0;
    modinfo_t        *module = (modinfo_t *)thread_arg;
 
    log_nrml("start terminate GPRS main(control) thread\n");
    
    power_off_module(module);
 
    for(i=0; i<module->comport_cnt; i++)
    {
        comport_term(module->comport[i]);
    } 
}
 
 
/* Description: Continue the main thread as the control thread, which used to control/inspect the GPRS 
 *              module power status. If it exit, then whole program exit.So when it exit, we must 
 *              make sure all other threads exit.
 *
 *Return Value: NONE
 */
void ctrl_thread_start(void *thread_arg)
{
    modinfo_t        *module = (modinfo_t *)thread_arg;
 
    log_nrml("GPRS power control thread start running\n");
 
    while( !g_cp_signal.stop )
    {
        /*  If someone request to change the power status, then set the power*/
        if(module->event)
        {
            set_module_event_power(module);
        }
 
        if(ALL_READY == module->ready)
        {
            /* If GPRS already registed then we just need to inspect the GPRS 
             * module status, such as signal, location */
            atcmd_inspect_status(module);
        }
        else
        {
            /* If GPRS module just already power on, then we use AT command to 
             * check the SIM card can work or not now. */
            if( ON == module->pwr_status )
            {
                atcmd_check_ready(module);
            }
        }
 
        sleep(1);
    }
 
    /* thread exit  */
    ctrl_thread_term(thread_arg);
 
    return ;
}