Newer
Older
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
bdaddr_t bdaddr;
uuid_t svc_uuid;
uint32_t range = 0x0000ffff;
sdp_list_t *response_list, *search_list, *attrid_list;
int status, port;
sdp_list_t *proto_list;
sdp_record_t *sdprec;
str2ba(addr, &bdaddr);
port = 0;
session = sdp_connect(BDADDR_ANY, &bdaddr, SDP_RETRY_IF_BUSY);
if (!session) {
ast_debug(1, "sdp_connect() failed on device %s.\n", addr);
return 0;
}
sdp_uuid32_create(&svc_uuid, profile);
search_list = sdp_list_append(0, &svc_uuid);
attrid_list = sdp_list_append(0, &range);
response_list = 0x00;
status = sdp_service_search_attr_req(session, search_list, SDP_ATTR_REQ_RANGE, attrid_list, &response_list);
if (status == 0) {
if (response_list) {
sdprec = (sdp_record_t *) response_list->data;
proto_list = 0x00;
if (sdp_get_access_protos(sdprec, &proto_list) == 0) {
port = sdp_get_proto_port(proto_list, RFCOMM_UUID);
sdp_list_free(proto_list, 0);
}
sdp_record_free(sdprec);
sdp_list_free(response_list, 0);
} else
ast_debug(1, "No responses returned for device %s.\n", addr);
} else
ast_debug(1, "sdp_service_search_attr_req() failed on device %s.\n", addr);
sdp_list_free(search_list, 0);
sdp_list_free(attrid_list, 0);
sdp_close(session);
return port;
}
static sdp_session_t *sdp_register(void)
{
uint32_t service_uuid_int[] = {0, 0, 0, GENERIC_AUDIO_SVCLASS_ID};
uint8_t rfcomm_channel = 1;
const char *service_name = "Asterisk PABX";
const char *service_dsc = "Asterisk PABX";
const char *service_prov = "Asterisk";
uuid_t root_uuid, l2cap_uuid, rfcomm_uuid, svc_uuid, svc_class1_uuid, svc_class2_uuid;
sdp_list_t *l2cap_list = 0, *rfcomm_list = 0, *root_list = 0, *proto_list = 0, *access_proto_list = 0, *svc_uuid_list = 0;
sdp_data_t *channel = 0;
sdp_session_t *session = 0;
sdp_record_t *record = sdp_record_alloc();
sdp_uuid128_create(&svc_uuid, &service_uuid_int);
sdp_set_service_id(record, svc_uuid);
sdp_uuid32_create(&svc_class1_uuid, GENERIC_AUDIO_SVCLASS_ID);
sdp_uuid32_create(&svc_class2_uuid, HEADSET_PROFILE_ID);
svc_uuid_list = sdp_list_append(0, &svc_class1_uuid);
svc_uuid_list = sdp_list_append(svc_uuid_list, &svc_class2_uuid);
sdp_set_service_classes(record, svc_uuid_list);
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
root_list = sdp_list_append(0, &root_uuid);
sdp_set_browse_groups( record, root_list );
sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
l2cap_list = sdp_list_append(0, &l2cap_uuid);
proto_list = sdp_list_append(0, l2cap_list);
sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
channel = sdp_data_alloc(SDP_UINT8, &rfcomm_channel);
rfcomm_list = sdp_list_append(0, &rfcomm_uuid);
sdp_list_append(rfcomm_list, channel);
sdp_list_append(proto_list, rfcomm_list);
access_proto_list = sdp_list_append(0, proto_list);
sdp_set_access_protos(record, access_proto_list);
sdp_set_info_attr(record, service_name, service_prov, service_dsc);
if (!(session = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, SDP_RETRY_IF_BUSY)))
ast_log(LOG_WARNING, "Failed to connect sdp and create session.\n");
else {
if (sdp_record_register(session, record, 0) < 0) {
ast_log(LOG_WARNING, "Failed to sdp_record_register error: %d\n", errno);
return NULL;
}
}
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
sdp_data_free(channel);
sdp_list_free(rfcomm_list, 0);
sdp_list_free(root_list, 0);
sdp_list_free(access_proto_list, 0);
sdp_list_free(svc_uuid_list, 0);
return session;
}
/*
Thread routines
*/
/*!
* \brief Handle the BRSF response.
* \param pvt a mbl_pvt structure
* \param buf a null terminated buffer containing an AT message
* \retval 0 success
* \retval -1 error
*/
static int handle_response_brsf(struct mbl_pvt *pvt, char *buf)
{
struct msg_queue_entry *entry;
if ((entry = msg_queue_head(pvt)) && entry->expected == AT_BRSF) {
if (hfp_parse_brsf(pvt->hfp, buf)) {
ast_debug(1, "[%s] error parsing BRSF\n", pvt->id);
goto e_return;
}
if (msg_queue_push(pvt, AT_OK, AT_BRSF)) {
ast_debug(1, "[%s] error handling BRSF\n", pvt->id);
goto e_return;
}
msg_queue_free_and_pop(pvt);
} else if (entry) {
David Brooks
committed
ast_debug(1, "[%s] received unexpected AT message 'BRSF' when expecting %s, ignoring\n", pvt->id, at_msg2str(entry->expected));
} else {
David Brooks
committed
ast_debug(1, "[%s] received unexpected AT message 'BRSF'\n", pvt->id);
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
}
return 0;
e_return:
msg_queue_free_and_pop(pvt);
return -1;
}
/*!
* \brief Handle the CIND response.
* \param pvt a mbl_pvt structure
* \param buf a null terminated buffer containing an AT message
* \retval 0 success
* \retval -1 error
*/
static int handle_response_cind(struct mbl_pvt *pvt, char *buf)
{
struct msg_queue_entry *entry;
if ((entry = msg_queue_head(pvt)) && entry->expected == AT_CIND) {
switch (entry->response_to) {
case AT_CIND_TEST:
if (hfp_parse_cind_test(pvt->hfp, buf) || msg_queue_push(pvt, AT_OK, AT_CIND_TEST)) {
ast_debug(1, "[%s] error performing CIND test\n", pvt->id);
goto e_return;
}
break;
case AT_CIND:
if (hfp_parse_cind(pvt->hfp, buf) || msg_queue_push(pvt, AT_OK, AT_CIND)) {
ast_debug(1, "[%s] error getting CIND state\n", pvt->id);
goto e_return;
}
break;
default:
ast_debug(1, "[%s] error getting CIND state\n", pvt->id);
goto e_return;
}
msg_queue_free_and_pop(pvt);
} else if (entry) {
David Brooks
committed
ast_debug(1, "[%s] received unexpected AT message 'CIND' when expecting %s, ignoring\n", pvt->id, at_msg2str(entry->expected));
} else {
David Brooks
committed
ast_debug(1, "[%s] received unexpected AT message 'CIND'\n", pvt->id);
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
}
return 0;
e_return:
msg_queue_free_and_pop(pvt);
return -1;
}
/*!
* \brief Handle OK AT messages.
* \param pvt a mbl_pvt structure
* \param buf a null terminated buffer containing an AT message
* \retval 0 success
* \retval -1 error
*/
static int handle_response_ok(struct mbl_pvt *pvt, char *buf)
{
struct msg_queue_entry *entry;
if ((entry = msg_queue_head(pvt)) && entry->expected == AT_OK) {
switch (entry->response_to) {
David Brooks
committed
/* initialization stuff */
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
case AT_BRSF:
ast_debug(1, "[%s] BSRF sent successfully\n", pvt->id);
/* If this is a blackberry do CMER now, otherwise
* continue with CIND as normal. */
if (pvt->blackberry) {
if (hfp_send_cmer(pvt->hfp, 1) || msg_queue_push(pvt, AT_OK, AT_CMER)) {
ast_debug(1, "[%s] error sending CMER\n", pvt->id);
goto e_return;
}
} else {
if (hfp_send_cind_test(pvt->hfp) || msg_queue_push(pvt, AT_CIND, AT_CIND_TEST)) {
ast_debug(1, "[%s] error sending CIND test\n", pvt->id);
goto e_return;
}
}
break;
case AT_CIND_TEST:
ast_debug(1, "[%s] CIND test sent successfully\n", pvt->id);
ast_debug(2, "[%s] call: %d\n", pvt->id, pvt->hfp->cind_map.call);
ast_debug(2, "[%s] callsetup: %d\n", pvt->id, pvt->hfp->cind_map.callsetup);
Matthew Nicholson
committed
ast_debug(2, "[%s] service: %d\n", pvt->id, pvt->hfp->cind_map.service);
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
if (hfp_send_cind(pvt->hfp) || msg_queue_push(pvt, AT_CIND, AT_CIND)) {
ast_debug(1, "[%s] error requesting CIND state\n", pvt->id);
goto e_return;
}
break;
case AT_CIND:
ast_debug(1, "[%s] CIND sent successfully\n", pvt->id);
/* check if a call is active */
if (pvt->hfp->cind_state[pvt->hfp->cind_map.call]) {
ast_verb(3, "Bluetooth Device %s has a call in progress - delaying connection.\n", pvt->id);
goto e_return;
}
/* If this is NOT a blackberry proceed with CMER,
* otherwise send CLIP. */
if (!pvt->blackberry) {
if (hfp_send_cmer(pvt->hfp, 1) || msg_queue_push(pvt, AT_OK, AT_CMER)) {
ast_debug(1, "[%s] error sending CMER\n", pvt->id);
goto e_return;
}
} else {
if (hfp_send_clip(pvt->hfp, 1) || msg_queue_push(pvt, AT_OK, AT_CLIP)) {
ast_debug(1, "[%s] error enabling calling line notification\n", pvt->id);
goto e_return;
}
}
break;
case AT_CMER:
ast_debug(1, "[%s] CMER sent successfully\n", pvt->id);
/* If this is a blackberry proceed with the CIND test,
* otherwise send CLIP. */
if (pvt->blackberry) {
if (hfp_send_cind_test(pvt->hfp) || msg_queue_push(pvt, AT_CIND, AT_CIND_TEST)) {
ast_debug(1, "[%s] error sending CIND test\n", pvt->id);
goto e_return;
}
} else {
if (hfp_send_clip(pvt->hfp, 1) || msg_queue_push(pvt, AT_OK, AT_CLIP)) {
ast_debug(1, "[%s] error enabling calling line notification\n", pvt->id);
goto e_return;
}
}
break;
case AT_CLIP:
ast_debug(1, "[%s] caling line indication enabled\n", pvt->id);
if (hfp_send_ecam(pvt->hfp) || msg_queue_push(pvt, AT_OK, AT_ECAM)) {
ast_debug(1, "[%s] error enabling Sony Ericsson call monitoring extensions\n", pvt->id);
goto e_return;
}
break;
case AT_ECAM:
ast_debug(1, "[%s] Sony Ericsson call monitoring is active on device\n", pvt->id);
if (hfp_send_vgs(pvt->hfp, 15) || msg_queue_push(pvt, AT_OK, AT_VGS)) {
ast_debug(1, "[%s] error synchronizing gain settings\n", pvt->id);
goto e_return;
}
pvt->timeout = -1;
pvt->hfp->initialized = 1;
ast_verb(3, "Bluetooth Device %s initialized and ready.\n", pvt->id);
break;
case AT_VGS:
ast_debug(1, "[%s] volume level synchronization successful\n", pvt->id);
/* set the SMS operating mode to text mode */
Matthew Nicholson
committed
if (pvt->has_sms) {
if (hfp_send_cmgf(pvt->hfp, 1) || msg_queue_push(pvt, AT_OK, AT_CMGF)) {
ast_debug(1, "[%s] error setting CMGF\n", pvt->id);
goto e_return;
}
}
break;
case AT_CMGF:
ast_debug(1, "[%s] sms text mode enabled\n", pvt->id);
/* turn on SMS new message indication */
if (hfp_send_cnmi(pvt->hfp) || msg_queue_push(pvt, AT_OK, AT_CNMI)) {
ast_debug(1, "[%s] error setting CNMI\n", pvt->id);
goto e_return;
}
break;
case AT_CNMI:
ast_debug(1, "[%s] sms new message indication enabled\n", pvt->id);
pvt->has_sms = 1;
break;
David Brooks
committed
/* end initialization stuff */
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
case AT_A:
ast_debug(1, "[%s] answer sent successfully\n", pvt->id);
pvt->needchup = 1;
break;
case AT_D:
ast_debug(1, "[%s] dial sent successfully\n", pvt->id);
pvt->needchup = 1;
pvt->outgoing = 1;
mbl_queue_control(pvt, AST_CONTROL_PROGRESS);
break;
case AT_CHUP:
ast_debug(1, "[%s] successful hangup\n", pvt->id);
break;
case AT_CMGS:
ast_debug(1, "[%s] successfully sent sms message\n", pvt->id);
pvt->outgoing_sms = 0;
break;
case AT_VTS:
ast_debug(1, "[%s] digit sent successfully\n", pvt->id);
break;
Matthew Nicholson
committed
case AT_CUSD:
ast_debug(1, "[%s] CUSD code sent successfully\n", pvt->id);
break;
case AT_UNKNOWN:
default:
David Brooks
committed
ast_debug(1, "[%s] received OK for unhandled request: %s\n", pvt->id, at_msg2str(entry->response_to));
break;
}
msg_queue_free_and_pop(pvt);
} else if (entry) {
David Brooks
committed
ast_debug(1, "[%s] received AT message 'OK' when expecting %s, ignoring\n", pvt->id, at_msg2str(entry->expected));
} else {
David Brooks
committed
ast_debug(1, "[%s] received unexpected AT message 'OK'\n", pvt->id);
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
}
return 0;
e_return:
msg_queue_free_and_pop(pvt);
return -1;
}
/*!
* \brief Handle ERROR AT messages.
* \param pvt a mbl_pvt structure
* \param buf a null terminated buffer containing an AT message
* \retval 0 success
* \retval -1 error
*/
static int handle_response_error(struct mbl_pvt *pvt, char *buf)
{
struct msg_queue_entry *entry;
if ((entry = msg_queue_head(pvt))
&& (entry->expected == AT_OK
|| entry->expected == AT_ERROR
|| entry->expected == AT_CMS_ERROR
|| entry->expected == AT_CMGR
|| entry->expected == AT_SMS_PROMPT)) {
switch (entry->response_to) {
David Brooks
committed
/* initialization stuff */
case AT_BRSF:
ast_debug(1, "[%s] error reading BSRF\n", pvt->id);
goto e_return;
case AT_CIND_TEST:
ast_debug(1, "[%s] error during CIND test\n", pvt->id);
goto e_return;
case AT_CIND:
ast_debug(1, "[%s] error requesting CIND state\n", pvt->id);
goto e_return;
case AT_CMER:
ast_debug(1, "[%s] error during CMER request\n", pvt->id);
goto e_return;
case AT_CLIP:
ast_debug(1, "[%s] error enabling calling line indication\n", pvt->id);
goto e_return;
case AT_VGS:
ast_debug(1, "[%s] volume level synchronization failed\n", pvt->id);
David Brooks
committed
/* this is not a fatal error, let's continue with initialization */
/* set the SMS operating mode to text mode */
if (hfp_send_cmgf(pvt->hfp, 1) || msg_queue_push(pvt, AT_OK, AT_CMGF)) {
ast_debug(1, "[%s] error setting CMGF\n", pvt->id);
goto e_return;
}
break;
case AT_CMGF:
Matthew Nicholson
committed
pvt->has_sms = 0;
ast_debug(1, "[%s] error setting CMGF\n", pvt->id);
ast_debug(1, "[%s] no SMS support\n", pvt->id);
break;
case AT_CNMI:
Matthew Nicholson
committed
pvt->has_sms = 0;
ast_debug(1, "[%s] error setting CNMI\n", pvt->id);
ast_debug(1, "[%s] no SMS support\n", pvt->id);
break;
case AT_ECAM:
ast_debug(1, "[%s] Mobile does not support Sony Ericsson extensions\n", pvt->id);
/* this is not a fatal error, let's continue with the initialization */
if (hfp_send_vgs(pvt->hfp, 15) || msg_queue_push(pvt, AT_OK, AT_VGS)) {
ast_debug(1, "[%s] error synchronizing gain settings\n", pvt->id);
goto e_return;
}
pvt->timeout = -1;
pvt->hfp->initialized = 1;
ast_verb(3, "Bluetooth Device %s initialized and ready.\n", pvt->id);
break;
David Brooks
committed
/* end initialization stuff */
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
case AT_A:
ast_debug(1, "[%s] answer failed\n", pvt->id);
mbl_queue_hangup(pvt);
break;
case AT_D:
ast_debug(1, "[%s] dial failed\n", pvt->id);
pvt->needchup = 0;
mbl_queue_control(pvt, AST_CONTROL_CONGESTION);
break;
case AT_CHUP:
ast_debug(1, "[%s] error sending hangup, disconnecting\n", pvt->id);
goto e_return;
case AT_CMGR:
ast_debug(1, "[%s] error reading sms message\n", pvt->id);
pvt->incoming_sms = 0;
break;
case AT_CMGS:
ast_debug(1, "[%s] error sending sms message\n", pvt->id);
pvt->outgoing_sms = 0;
break;
case AT_VTS:
ast_debug(1, "[%s] error sending digit\n", pvt->id);
break;
Matthew Nicholson
committed
case AT_CUSD:
ast_verb(0, "[%s] error sending CUSD command\n", pvt->id);
break;
case AT_UNKNOWN:
default:
David Brooks
committed
ast_debug(1, "[%s] received ERROR for unhandled request: %s\n", pvt->id, at_msg2str(entry->response_to));
break;
}
msg_queue_free_and_pop(pvt);
} else if (entry) {
David Brooks
committed
ast_debug(1, "[%s] received AT message 'ERROR' when expecting %s, ignoring\n", pvt->id, at_msg2str(entry->expected));
} else {
David Brooks
committed
ast_debug(1, "[%s] received unexpected AT message 'ERROR'\n", pvt->id);
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
}
return 0;
e_return:
msg_queue_free_and_pop(pvt);
return -1;
}
/*!
* \brief Handle AT+CIEV messages.
* \param pvt a mbl_pvt structure
* \param buf a null terminated buffer containing an AT message
* \retval 0 success
* \retval -1 error
*/
static int handle_response_ciev(struct mbl_pvt *pvt, char *buf)
{
int i;
switch (hfp_parse_ciev(pvt->hfp, buf, &i)) {
case HFP_CIND_CALL:
switch (i) {
case HFP_CIND_CALL_NONE:
ast_debug(1, "[%s] line disconnected\n", pvt->id);
if (pvt->owner) {
ast_debug(1, "[%s] hanging up owner\n", pvt->id);
if (mbl_queue_hangup(pvt)) {
ast_log(LOG_ERROR, "[%s] error queueing hangup, disconnectiong...\n", pvt->id);
return -1;
}
}
pvt->needchup = 0;
pvt->needcallerid = 0;
pvt->incoming = 0;
pvt->outgoing = 0;
break;
case HFP_CIND_CALL_ACTIVE:
if (pvt->outgoing) {
ast_debug(1, "[%s] remote end answered\n", pvt->id);
mbl_queue_control(pvt, AST_CONTROL_ANSWER);
} else if (pvt->incoming && pvt->answered) {
ast_setstate(pvt->owner, AST_STATE_UP);
} else if (pvt->incoming) {
/* user answered from handset, disconnecting */
ast_verb(3, "[%s] user answered bluetooth device from handset, disconnecting\n", pvt->id);
mbl_queue_hangup(pvt);
return -1;
}
break;
}
break;
case HFP_CIND_CALLSETUP:
switch (i) {
case HFP_CIND_CALLSETUP_NONE:
if (pvt->hfp->cind_state[pvt->hfp->cind_map.call] != HFP_CIND_CALL_ACTIVE) {
if (pvt->owner) {
if (pvt->hfp->sent_alerting == 1) {
handle_response_busy(pvt);
}
if (mbl_queue_hangup(pvt)) {
ast_log(LOG_ERROR, "[%s] error queueing hangup, disconnectiong...\n", pvt->id);
return -1;
}
}
pvt->needchup = 0;
pvt->needcallerid = 0;
pvt->incoming = 0;
pvt->outgoing = 0;
}
break;
case HFP_CIND_CALLSETUP_INCOMING:
ast_debug(1, "[%s] incoming call, waiting for caller id\n", pvt->id);
pvt->needcallerid = 1;
pvt->incoming = 1;
break;
case HFP_CIND_CALLSETUP_OUTGOING:
if (pvt->outgoing) {
ast_debug(1, "[%s] outgoing call\n", pvt->id);
} else {
ast_verb(3, "[%s] user dialed from handset, disconnecting\n", pvt->id);
return -1;
}
break;
case HFP_CIND_CALLSETUP_ALERTING:
if (pvt->outgoing) {
ast_debug(1, "[%s] remote alerting\n", pvt->id);
mbl_queue_control(pvt, AST_CONTROL_RINGING);
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
}
break;
}
break;
case HFP_CIND_NONE:
ast_debug(1, "[%s] error parsing CIND: %s\n", pvt->id, buf);
break;
}
return 0;
}
/*!
* \brief Handle AT+CLIP messages.
* \param pvt a mbl_pvt structure
* \param buf a null terminated buffer containing an AT message
* \retval 0 success
* \retval -1 error
*/
static int handle_response_clip(struct mbl_pvt *pvt, char *buf)
{
char *clip;
struct msg_queue_entry *msg;
struct ast_channel *chan;
if ((msg = msg_queue_head(pvt)) && msg->expected == AT_CLIP) {
msg_queue_free_and_pop(pvt);
pvt->needcallerid = 0;
if (!(clip = hfp_parse_clip(pvt->hfp, buf))) {
ast_debug(1, "[%s] error parsing CLIP: %s\n", pvt->id, buf);
}
if (!(chan = mbl_new(AST_STATE_RING, pvt, clip, NULL, NULL))) {
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
ast_log(LOG_ERROR, "[%s] unable to allocate channel for incoming call\n", pvt->id);
hfp_send_chup(pvt->hfp);
msg_queue_push(pvt, AT_OK, AT_CHUP);
return -1;
}
/* from this point on, we need to send a chup in the event of a
* hangup */
pvt->needchup = 1;
if (ast_pbx_start(chan)) {
ast_log(LOG_ERROR, "[%s] unable to start pbx on incoming call\n", pvt->id);
mbl_ast_hangup(pvt);
return -1;
}
}
return 0;
}
/*!
* \brief Handle RING messages.
* \param pvt a mbl_pvt structure
* \param buf a null terminated buffer containing an AT message
* \retval 0 success
* \retval -1 error
*/
static int handle_response_ring(struct mbl_pvt *pvt, char *buf)
{
if (pvt->needcallerid) {
ast_debug(1, "[%s] got ring while waiting for caller id\n", pvt->id);
return msg_queue_push(pvt, AT_CLIP, AT_UNKNOWN);
} else {
return 0;
}
}
/*!
* \brief Handle AT+CMTI messages.
* \param pvt a mbl_pvt structure
* \param buf a null terminated buffer containing an AT message
* \retval 0 success
* \retval -1 error
*/
static int handle_response_cmti(struct mbl_pvt *pvt, char *buf)
{
int index = hfp_parse_cmti(pvt->hfp, buf);
if (index > 0) {
ast_debug(1, "[%s] incoming sms message\n", pvt->id);
if (hfp_send_cmgr(pvt->hfp, index)
|| msg_queue_push(pvt, AT_CMGR, AT_CMGR)) {
ast_debug(1, "[%s] error sending CMGR to retrieve SMS message\n", pvt->id);
return -1;
}
pvt->incoming_sms = 1;
return 0;
} else {
ast_debug(1, "[%s] error parsing incoming sms message alert, disconnecting\n", pvt->id);
return -1;
}
}
/*!
* \brief Handle AT+CMGR messages.
* \param pvt a mbl_pvt structure
* \param buf a null terminated buffer containing an AT message
* \retval 0 success
* \retval -1 error
*/
static int handle_response_cmgr(struct mbl_pvt *pvt, char *buf)
{
char *from_number = NULL, *text = NULL;
struct ast_channel *chan;
struct msg_queue_entry *msg;
if ((msg = msg_queue_head(pvt)) && msg->expected == AT_CMGR) {
msg_queue_free_and_pop(pvt);
if (hfp_parse_cmgr(pvt->hfp, buf, &from_number, &text)) {
ast_debug(1, "[%s] error parsing sms message, disconnecting\n", pvt->id);
return -1;
}
ast_debug(1, "[%s] successfully read sms message\n", pvt->id);
pvt->incoming_sms = 0;
/* XXX this channel probably does not need to be associated with this pvt */
if (!(chan = mbl_new(AST_STATE_DOWN, pvt, NULL, NULL, NULL))) {
ast_debug(1, "[%s] error creating sms message channel, disconnecting\n", pvt->id);
return -1;
}
ast_channel_exten_set(chan, "sms");
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
pbx_builtin_setvar_helper(chan, "SMSSRC", from_number);
pbx_builtin_setvar_helper(chan, "SMSTXT", text);
if (ast_pbx_start(chan)) {
ast_log(LOG_ERROR, "[%s] unable to start pbx on incoming sms\n", pvt->id);
mbl_ast_hangup(pvt);
}
} else {
ast_debug(1, "[%s] got unexpected +CMGR message, ignoring\n", pvt->id);
}
return 0;
}
/*!
* \brief Send an SMS message from the queue.
* \param pvt a mbl_pvt structure
* \param buf a null terminated buffer containing an AT message
* \retval 0 success
* \retval -1 error
*/
static int handle_sms_prompt(struct mbl_pvt *pvt, char *buf)
{
struct msg_queue_entry *msg;
if (!(msg = msg_queue_head(pvt))) {
ast_debug(1, "[%s] error, got sms prompt with no pending sms messages\n", pvt->id);
return 0;
}
if (msg->expected != AT_SMS_PROMPT) {
ast_debug(1, "[%s] error, got sms prompt but no pending sms messages\n", pvt->id);
return 0;
}
if (hfp_send_sms_text(pvt->hfp, msg->data)
|| msg_queue_push(pvt, AT_OK, AT_CMGS)) {
msg_queue_free_and_pop(pvt);
ast_debug(1, "[%s] error sending sms message\n", pvt->id);
return 0;
}
msg_queue_free_and_pop(pvt);
return 0;
}
Matthew Nicholson
committed
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
/*!
* \brief Handle CUSD messages.
* \param pvt a mbl_pvt structure
* \param buf a null terminated buffer containing an AT message
* \retval 0 success
* \retval -1 error
*/
static int handle_response_cusd(struct mbl_pvt *pvt, char *buf)
{
char *cusd;
if (!(cusd = hfp_parse_cusd(pvt->hfp, buf))) {
ast_verb(0, "[%s] error parsing CUSD: %s\n", pvt->id, buf);
return 0;
}
ast_verb(0, "[%s] CUSD response: %s\n", pvt->id, cusd);
return 0;
}
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
/*!
* \brief Handle BUSY messages.
* \param pvt a mbl_pvt structure
* \retval 0 success
* \retval -1 error
*/
static int handle_response_busy(struct mbl_pvt *pvt)
{
pvt->hangupcause = AST_CAUSE_USER_BUSY;
pvt->needchup = 1;
mbl_queue_control(pvt, AST_CONTROL_BUSY);
return 0;
}
/*!
* \brief Handle NO DIALTONE messages.
* \param pvt a mbl_pvt structure
* \param buf a null terminated buffer containing an AT message
* \retval 0 success
* \retval -1 error
*/
static int handle_response_no_dialtone(struct mbl_pvt *pvt, char *buf)
{
ast_verb(1, "[%s] mobile reports NO DIALTONE\n", pvt->id);
pvt->needchup = 1;
mbl_queue_control(pvt, AST_CONTROL_CONGESTION);
return 0;
}
/*!
* \brief Handle NO CARRIER messages.
* \param pvt a mbl_pvt structure
* \param buf a null terminated buffer containing an AT message
* \retval 0 success
* \retval -1 error
*/
static int handle_response_no_carrier(struct mbl_pvt *pvt, char *buf)
{
ast_verb(1, "[%s] mobile reports NO CARRIER\n", pvt->id);
pvt->needchup = 1;
mbl_queue_control(pvt, AST_CONTROL_CONGESTION);
return 0;
}
static void *do_monitor_phone(void *data)
{
struct mbl_pvt *pvt = (struct mbl_pvt *)data;
struct hfp_pvt *hfp = pvt->hfp;
char buf[350];
int t;
at_message_t at_msg;
struct msg_queue_entry *entry;
David Brooks
committed
/* Note: At one point the initialization procedure was neatly contained
* in the hfp_init() function, but that initialization method did not
* work with non standard devices. As a result, the initialization
* procedure is not spread throughout the event handling loop.
*/
David Brooks
committed
/* start initialization with the BRSF request */
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
ast_mutex_lock(&pvt->lock);
pvt->timeout = 10000;
if (hfp_send_brsf(hfp, &hfp_our_brsf) || msg_queue_push(pvt, AT_BRSF, AT_BRSF)) {
ast_debug(1, "[%s] error sending BRSF\n", hfp->owner->id);
goto e_cleanup;
}
ast_mutex_unlock(&pvt->lock);
while (!check_unloading()) {
ast_mutex_lock(&pvt->lock);
t = pvt->timeout;
ast_mutex_unlock(&pvt->lock);
if (!rfcomm_wait(pvt->rfcomm_socket, &t)) {
ast_debug(1, "[%s] timeout waiting for rfcomm data, disconnecting\n", pvt->id);
ast_mutex_lock(&pvt->lock);
if (!hfp->initialized) {
if ((entry = msg_queue_head(pvt))) {
switch (entry->response_to) {
case AT_CIND_TEST:
if (pvt->blackberry)
ast_debug(1, "[%s] timeout during CIND test\n", hfp->owner->id);
else
ast_debug(1, "[%s] timeout during CIND test, try setting 'blackberry=yes'\n", hfp->owner->id);
break;
case AT_CMER:
if (pvt->blackberry)
ast_debug(1, "[%s] timeout after sending CMER, try setting 'blackberry=no'\n", hfp->owner->id);
else
ast_debug(1, "[%s] timeout after sending CMER\n", hfp->owner->id);
break;
default:
ast_debug(1, "[%s] timeout while waiting for %s in response to %s\n", pvt->id, at_msg2str(entry->expected), at_msg2str(entry->response_to));
break;
}
}
}
ast_mutex_unlock(&pvt->lock);
goto e_cleanup;
}
if ((at_msg = at_read_full(hfp->rsock, buf, sizeof(buf))) < 0) {
ast_debug(1, "[%s] error reading from device: %s (%d)\n", pvt->id, strerror(errno), errno);
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
break;
}
ast_debug(1, "[%s] %s\n", pvt->id, buf);
switch (at_msg) {
case AT_BRSF:
ast_mutex_lock(&pvt->lock);
if (handle_response_brsf(pvt, buf)) {
ast_mutex_unlock(&pvt->lock);
goto e_cleanup;
}
ast_mutex_unlock(&pvt->lock);
break;
case AT_CIND:
ast_mutex_lock(&pvt->lock);
if (handle_response_cind(pvt, buf)) {
ast_mutex_unlock(&pvt->lock);
goto e_cleanup;
}
ast_mutex_unlock(&pvt->lock);
break;
case AT_OK:
ast_mutex_lock(&pvt->lock);
if (handle_response_ok(pvt, buf)) {
ast_mutex_unlock(&pvt->lock);
goto e_cleanup;
}
ast_mutex_unlock(&pvt->lock);
break;
case AT_CMS_ERROR:
case AT_ERROR:
ast_mutex_lock(&pvt->lock);
if (handle_response_error(pvt, buf)) {
ast_mutex_unlock(&pvt->lock);
goto e_cleanup;
}
ast_mutex_unlock(&pvt->lock);
break;
case AT_RING:
ast_mutex_lock(&pvt->lock);
if (handle_response_ring(pvt, buf)) {
ast_mutex_unlock(&pvt->lock);
goto e_cleanup;
}
ast_mutex_unlock(&pvt->lock);
break;
case AT_CIEV:
ast_mutex_lock(&pvt->lock);
if (handle_response_ciev(pvt, buf)) {
ast_mutex_unlock(&pvt->lock);
goto e_cleanup;
}
ast_mutex_unlock(&pvt->lock);
break;
case AT_CLIP:
ast_mutex_lock(&pvt->lock);
if (handle_response_clip(pvt, buf)) {
ast_mutex_unlock(&pvt->lock);
goto e_cleanup;
}
ast_mutex_unlock(&pvt->lock);
break;
case AT_CMTI:
ast_mutex_lock(&pvt->lock);
if (handle_response_cmti(pvt, buf)) {
ast_mutex_unlock(&pvt->lock);
goto e_cleanup;
}
ast_mutex_unlock(&pvt->lock);
break;
case AT_CMGR:
ast_mutex_lock(&pvt->lock);
if (handle_response_cmgr(pvt, buf)) {
ast_mutex_unlock(&pvt->lock);
goto e_cleanup;
}
ast_mutex_unlock(&pvt->lock);
break;
case AT_SMS_PROMPT:
ast_mutex_lock(&pvt->lock);
if (handle_sms_prompt(pvt, buf)) {
ast_mutex_unlock(&pvt->lock);
goto e_cleanup;
}
ast_mutex_unlock(&pvt->lock);
break;
Matthew Nicholson
committed
case AT_CUSD:
ast_mutex_lock(&pvt->lock);
if (handle_response_cusd(pvt, buf)) {
ast_mutex_unlock(&pvt->lock);
goto e_cleanup;
}
ast_mutex_unlock(&pvt->lock);
break;
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
case AT_BUSY:
ast_mutex_lock(&pvt->lock);
if (handle_response_busy(pvt)) {
ast_mutex_unlock(&pvt->lock);
goto e_cleanup;
}
ast_mutex_unlock(&pvt->lock);
break;
case AT_NO_DIALTONE:
ast_mutex_lock(&pvt->lock);
if (handle_response_no_dialtone(pvt, buf)) {
ast_mutex_unlock(&pvt->lock);
goto e_cleanup;
}
ast_mutex_unlock(&pvt->lock);
break;
case AT_NO_CARRIER:
ast_mutex_lock(&pvt->lock);
if (handle_response_no_carrier(pvt, buf)) {
ast_mutex_unlock(&pvt->lock);
goto e_cleanup;
}
ast_mutex_unlock(&pvt->lock);
break;
case AT_ECAM:
ast_mutex_lock(&pvt->lock);
if (hfp_parse_ecav(hfp, buf) == 7) {
if (handle_response_busy(pvt)) {
ast_mutex_unlock(&pvt->lock);
goto e_cleanup;
}
}
ast_mutex_unlock(&pvt->lock);
break;
case AT_UNKNOWN:
ast_debug(1, "[%s] ignoring unknown message: %s\n", pvt->id, buf);
break;
case AT_PARSE_ERROR:
ast_debug(1, "[%s] error parsing message\n", pvt->id);
goto e_cleanup;
case AT_READ_ERROR:
ast_debug(1, "[%s] error reading from device: %s (%d)\n", pvt->id, strerror(errno), errno);
goto e_cleanup;
default:
break;
}
}
e_cleanup: