Newer
Older
return -1;
}
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
pvt->needchup = 1;
msg_queue_push(pvt, AT_OK, AT_D);
} else {
if (hsp_send_ring(pvt->rfcomm_socket)) {
ast_log(LOG_ERROR, "[%s] error ringing device\n", pvt->id);
ast_mutex_unlock(&pvt->lock);
return -1;
}
if ((pvt->ring_sched_id = ast_sched_add(pvt->sched, 6000, headset_send_ring, pvt)) == -1) {
ast_log(LOG_ERROR, "[%s] error ringing device\n", pvt->id);
ast_mutex_unlock(&pvt->lock);
return -1;
}
pvt->outgoing = 1;
pvt->needring = 1;
}
ast_mutex_unlock(&pvt->lock);
return 0;
}
static int mbl_hangup(struct ast_channel *ast)
{
struct mbl_pvt *pvt;
if (!ast_channel_tech_pvt(ast)) {
ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
return 0;
}
pvt = ast_channel_tech_pvt(ast);
ast_debug(1, "[%s] hanging up device\n", pvt->id);
ast_mutex_lock(&pvt->lock);
ast_channel_set_fd(ast, 0, -1);
close(pvt->sco_socket);
pvt->sco_socket = -1;
if (pvt->needchup) {
hfp_send_chup(pvt->hfp);
msg_queue_push(pvt, AT_OK, AT_CHUP);
pvt->needchup = 0;
}
pvt->outgoing = 0;
pvt->incoming = 0;
pvt->needring = 0;
pvt->owner = NULL;
ast_channel_tech_pvt_set(ast, NULL);
ast_mutex_unlock(&pvt->lock);
ast_setstate(ast, AST_STATE_DOWN);
return 0;
}
static int mbl_answer(struct ast_channel *ast)
{
struct mbl_pvt *pvt;
pvt = ast_channel_tech_pvt(ast);
if (pvt->type == MBL_TYPE_HEADSET)
return 0;
ast_mutex_lock(&pvt->lock);
if (pvt->incoming) {
hfp_send_ata(pvt->hfp);
msg_queue_push(pvt, AT_OK, AT_A);
pvt->answered = 1;
}
ast_mutex_unlock(&pvt->lock);
return 0;
}
static int mbl_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
{
struct mbl_pvt *pvt = ast_channel_tech_pvt(ast);
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
if (pvt->type == MBL_TYPE_HEADSET)
return 0;
ast_mutex_lock(&pvt->lock);
if (hfp_send_dtmf(pvt->hfp, digit)) {
ast_mutex_unlock(&pvt->lock);
ast_debug(1, "[%s] error sending digit %c\n", pvt->id, digit);
return -1;
}
msg_queue_push(pvt, AT_OK, AT_VTS);
ast_mutex_unlock(&pvt->lock);
ast_debug(1, "[%s] dialed %c\n", pvt->id, digit);
return 0;
}
static struct ast_frame *mbl_read(struct ast_channel *ast)
{
struct mbl_pvt *pvt = ast_channel_tech_pvt(ast);
struct ast_frame *fr = &ast_null_frame;
int r;
ast_debug(3, "*** mbl_read()\n");
while (ast_mutex_trylock(&pvt->lock)) {
CHANNEL_DEADLOCK_AVOIDANCE(ast);
}
if (!pvt->owner || pvt->sco_socket == -1) {
goto e_return;
}
memset(&pvt->fr, 0x00, sizeof(struct ast_frame));
pvt->fr.frametype = AST_FRAME_VOICE;
pvt->fr.subclass.format = DEVICE_FRAME_FORMAT;
pvt->fr.src = "Mobile";
pvt->fr.offset = AST_FRIENDLY_OFFSET;
pvt->fr.mallocd = 0;
pvt->fr.delivery.tv_sec = 0;
pvt->fr.delivery.tv_usec = 0;
pvt->fr.data.ptr = pvt->io_buf + AST_FRIENDLY_OFFSET;
do {
if ((r = read(pvt->sco_socket, pvt->fr.data.ptr, DEVICE_FRAME_SIZE)) == -1) {
if (errno != EAGAIN && errno != EINTR) {
ast_debug(1, "[%s] read error %d, going to wait for new connection\n", pvt->id, errno);
close(pvt->sco_socket);
pvt->sco_socket = -1;
ast_channel_set_fd(ast, 0, -1);
}
goto e_return;
}
pvt->fr.datalen = r;
pvt->fr.samples = r / 2;
if (pvt->do_alignment_detection)
do_alignment_detection(pvt, pvt->fr.data.ptr, r);
ast_smoother_feed(pvt->bt_in_smoother, &pvt->fr);
fr = ast_smoother_read(pvt->bt_in_smoother);
} while (fr == NULL);
fr = ast_dsp_process(ast, pvt->dsp, fr);
ast_mutex_unlock(&pvt->lock);
return fr;
e_return:
ast_mutex_unlock(&pvt->lock);
return fr;
}
static int mbl_write(struct ast_channel *ast, struct ast_frame *frame)
{
struct mbl_pvt *pvt = ast_channel_tech_pvt(ast);
struct ast_frame *f;
ast_debug(3, "*** mbl_write\n");
if (frame->frametype != AST_FRAME_VOICE) {
return 0;
}
while (ast_mutex_trylock(&pvt->lock)) {
CHANNEL_DEADLOCK_AVOIDANCE(ast);
}
ast_smoother_feed(pvt->bt_out_smoother, frame);
while ((f = ast_smoother_read(pvt->bt_out_smoother))) {
sco_write(pvt->sco_socket, f->data.ptr, f->datalen);
}
ast_mutex_unlock(&pvt->lock);
return 0;
}
static int mbl_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
{
struct mbl_pvt *pvt = ast_channel_tech_pvt(newchan);
if (!pvt) {
Matthew Nicholson
committed
ast_debug(1, "fixup failed, no pvt on newchan\n");
return -1;
}
ast_mutex_lock(&pvt->lock);
if (pvt->owner == oldchan)
pvt->owner = newchan;
ast_mutex_unlock(&pvt->lock);
return 0;
}
static int mbl_devicestate(const char *data)
{
char *device;
int res = AST_DEVICE_INVALID;
struct mbl_pvt *pvt;
device = ast_strdupa(S_OR(data, ""));
ast_debug(1, "Checking device state for device %s\n", device);
AST_RWLIST_RDLOCK(&devices);
AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
if (!strcmp(pvt->id, device))
break;
}
AST_RWLIST_UNLOCK(&devices);
if (!pvt)
return res;
ast_mutex_lock(&pvt->lock);
if (pvt->connected) {
if (pvt->owner)
res = AST_DEVICE_INUSE;
else
res = AST_DEVICE_NOT_INUSE;
Matthew Nicholson
committed
if (!mbl_has_service(pvt))
res = AST_DEVICE_UNAVAILABLE;
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
}
ast_mutex_unlock(&pvt->lock);
return res;
}
/*
Callback helpers
*/
/*
do_alignment_detection()
This routine attempts to detect where we get misaligned sco audio data from the bluetooth adaptor.
Its enabled by alignmentdetect=yes under the adapter entry in mobile.conf
Some adapters suffer a problem where occasionally they will byte shift the audio stream one byte to the right.
The result is static or white noise on the inbound (from the adapter) leg of the call.
This is characterised by a sudden jump in magnitude of the value of the 16 bit samples.
Here we look at the first 4 48 byte frames. We average the absolute values of each sample in the frame,
then average the sum of the averages of frames 1, 2, and 3.
Frame zero is usually zero.
If the end result > 100, and it usually is if we have the problem, set a flag and compensate by shifting the bytes
for each subsequent frame during the call.
If the result is <= 100 then clear the flag so we don't come back in here...
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
This seems to work OK....
*/
static void do_alignment_detection(struct mbl_pvt *pvt, char *buf, int buflen)
{
int i;
short a, *s;
char *p;
if (pvt->alignment_detection_triggered) {
for (i=buflen, p=buf+buflen-1; i>0; i--, p--)
*p = *(p-1);
*(p+1) = 0;
return;
}
if (pvt->alignment_count < 4) {
s = (short *)buf;
for (i=0, a=0; i<buflen/2; i++) {
a += *s++;
a /= i+1;
}
pvt->alignment_samples[pvt->alignment_count++] = a;
return;
}
ast_debug(1, "Alignment Detection result is [%-d %-d %-d %-d]\n", pvt->alignment_samples[0], pvt->alignment_samples[1], pvt->alignment_samples[2], pvt->alignment_samples[3]);
a = abs(pvt->alignment_samples[1]) + abs(pvt->alignment_samples[2]) + abs(pvt->alignment_samples[3]);
a /= 3;
if (a > 100) {
pvt->alignment_detection_triggered = 1;
ast_debug(1, "Alignment Detection Triggered.\n");
} else
pvt->do_alignment_detection = 0;
}
static int mbl_queue_control(struct mbl_pvt *pvt, enum ast_control_frame_type control)
{
for (;;) {
if (pvt->owner) {
if (ast_channel_trylock(pvt->owner)) {
DEADLOCK_AVOIDANCE(&pvt->lock);
} else {
ast_queue_control(pvt->owner, control);
ast_channel_unlock(pvt->owner);
break;
}
} else
break;
}
return 0;
}
static int mbl_queue_hangup(struct mbl_pvt *pvt)
{
for (;;) {
if (pvt->owner) {
if (ast_channel_trylock(pvt->owner)) {
DEADLOCK_AVOIDANCE(&pvt->lock);
} else {
if (pvt->hangupcause != 0) {
ast_channel_hangupcause_set(pvt->owner, pvt->hangupcause);
}
ast_queue_hangup(pvt->owner);
ast_channel_unlock(pvt->owner);
break;
}
} else
break;
}
return 0;
}
static int mbl_ast_hangup(struct mbl_pvt *pvt)
{
ast_hangup(pvt->owner);
}
Matthew Nicholson
committed
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
/*!
* \brief Check if a mobile device has service.
* \param pvt a mbl_pvt struct
* \retval 1 this device has service
* \retval 0 no service
*
* \note This function will always indicate that service is available if the
* given device does not support service indication.
*/
static int mbl_has_service(struct mbl_pvt *pvt)
{
if (pvt->type != MBL_TYPE_PHONE)
return 1;
if (!pvt->hfp->cind_map.service)
return 1;
if (pvt->hfp->cind_state[pvt->hfp->cind_map.service] == HFP_CIND_SERVICE_AVAILABLE)
return 1;
return 0;
}
/*
rfcomm helpers
*/
static int rfcomm_connect(bdaddr_t src, bdaddr_t dst, int remote_channel)
{
struct sockaddr_rc addr;
int s;
if ((s = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) {
ast_debug(1, "socket() failed (%d).\n", errno);
return -1;
}
memset(&addr, 0, sizeof(addr));
addr.rc_family = AF_BLUETOOTH;
bacpy(&addr.rc_bdaddr, &src);
addr.rc_channel = (uint8_t) 0;
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
ast_debug(1, "bind() failed (%d).\n", errno);
close(s);
return -1;
}
memset(&addr, 0, sizeof(addr));
addr.rc_family = AF_BLUETOOTH;
bacpy(&addr.rc_bdaddr, &dst);
addr.rc_channel = remote_channel;
if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
ast_debug(1, "connect() failed (%d).\n", errno);
close(s);
return -1;
}
return s;
}
/*!
* \brief Write to an rfcomm socket.
* \param rsock the socket to write to
* \param buf the null terminated buffer to write
*
* This function will write characters from buf. The buffer must be null
* terminated.
*
* \retval -1 error
* \retval 0 success
*/
static int rfcomm_write(int rsock, char *buf)
{
return rfcomm_write_full(rsock, buf, strlen(buf));
}
/*!
* \brief Write to an rfcomm socket.
* \param rsock the socket to write to
* \param buf the buffer to write
* \param count the number of characters from the buffer to write
*
* This function will write count characters from buf. It will always write
* count chars unless it encounters an error.
*
* \retval -1 error
* \retval 0 success
*/
static int rfcomm_write_full(int rsock, char *buf, size_t count)
{
char *p = buf;
ssize_t out_count;
ast_debug(1, "rfcomm_write() (%d) [%.*s]\n", rsock, (int) count, buf);
while (count > 0) {
if ((out_count = write(rsock, p, count)) == -1) {
ast_debug(1, "rfcomm_write() error [%d]\n", errno);
return -1;
}
count -= out_count;
p += out_count;
}
return 0;
}
/*!
* \brief Wait for activity on an rfcomm socket.
* \param rsock the socket to watch
* \param ms a pointer to an int containing a timeout in ms
* \return zero on timeout and the socket fd (non-zero) otherwise
* \retval 0 timeout
*/
static int rfcomm_wait(int rsock, int *ms)
{
int exception, outfd;
outfd = ast_waitfor_n_fd(&rsock, 1, ms, &exception);
if (outfd < 0)
outfd = 0;
return outfd;
}
#ifdef RFCOMM_READ_DEBUG
#define rfcomm_read_debug(c) __rfcomm_read_debug(c)
static void __rfcomm_read_debug(char c)
{
if (c == '\r')
ast_debug(2, "rfcomm_read: \\r\n");
else if (c == '\n')
ast_debug(2, "rfcomm_read: \\n\n");
else
ast_debug(2, "rfcomm_read: %c\n", c);
}
#else
#define rfcomm_read_debug(c)
#endif
/*!
* \brief Append the given character to the given buffer and increase the
* in_count.
*/
static void inline rfcomm_append_buf(char **buf, size_t count, size_t *in_count, char c)
{
if (*in_count < count) {
(*in_count)++;
*(*buf)++ = c;
}
}
/*!
* \brief Read a character from the given stream and check if it matches what
* we expected.
*/
static int rfcomm_read_and_expect_char(int rsock, char *result, char expected)
{
int res;
char c;
if (!result)
result = &c;
if ((res = read(rsock, result, 1)) < 1) {
return res;
}
rfcomm_read_debug(*result);
if (*result != expected) {
return -2;
}
return 1;
}
/*!
* \brief Read a character from the given stream and append it to the given
* buffer if it matches the expected character.
*/
static int rfcomm_read_and_append_char(int rsock, char **buf, size_t count, size_t *in_count, char *result, char expected)
{
int res;
char c;
if (!result)
result = &c;
if ((res = rfcomm_read_and_expect_char(rsock, result, expected)) < 1) {
return res;
}
rfcomm_append_buf(buf, count, in_count, *result);
return 1;
}
/*!
* \brief Read until \verbatim '\r\n'. \endverbatim
* This function consumes the \verbatim'\r\n'\endverbatim but does not add it to buf.
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
*/
static int rfcomm_read_until_crlf(int rsock, char **buf, size_t count, size_t *in_count)
{
int res;
char c;
while ((res = read(rsock, &c, 1)) == 1) {
rfcomm_read_debug(c);
if (c == '\r') {
if ((res = rfcomm_read_and_expect_char(rsock, &c, '\n')) == 1) {
break;
} else if (res == -2) {
rfcomm_append_buf(buf, count, in_count, '\r');
} else {
rfcomm_append_buf(buf, count, in_count, '\r');
break;
}
}
rfcomm_append_buf(buf, count, in_count, c);
}
return res;
}
/*!
* \brief Read the remainder of an AT SMS prompt.
* \note the entire parsed string is \verbatim '\r\n> ' \endverbatim
*
* By the time this function is executed, only a ' ' is left to read.
*/
static int rfcomm_read_sms_prompt(int rsock, char **buf, size_t count, size_t *in_count)
{
int res;
if ((res = rfcomm_read_and_append_char(rsock, buf, count, in_count, NULL, ' ')) < 1)
goto e_return;
return 1;
e_return:
ast_log(LOG_ERROR, "error parsing SMS prompt on rfcomm socket\n");
return res;
}
* \brief Read until a \verbatim \r\nOK\r\n \endverbatim message.
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
*/
static int rfcomm_read_until_ok(int rsock, char **buf, size_t count, size_t *in_count)
{
int res;
char c;
/* here, we read until finding a \r\n, then we read one character at a
* time looking for the string '\r\nOK\r\n'. If we only find a partial
* match, we place that in the buffer and try again. */
for (;;) {
if ((res = rfcomm_read_until_crlf(rsock, buf, count, in_count)) != 1) {
break;
}
rfcomm_append_buf(buf, count, in_count, '\r');
rfcomm_append_buf(buf, count, in_count, '\n');
if ((res = rfcomm_read_and_expect_char(rsock, &c, '\r')) != 1) {
if (res != -2) {
break;
}
rfcomm_append_buf(buf, count, in_count, c);
continue;
}
if ((res = rfcomm_read_and_expect_char(rsock, &c, '\n')) != 1) {
if (res != -2) {
break;
}
rfcomm_append_buf(buf, count, in_count, '\r');
rfcomm_append_buf(buf, count, in_count, c);
continue;
}
if ((res = rfcomm_read_and_expect_char(rsock, &c, 'O')) != 1) {
if (res != -2) {
break;
}
rfcomm_append_buf(buf, count, in_count, '\r');
rfcomm_append_buf(buf, count, in_count, '\n');
rfcomm_append_buf(buf, count, in_count, c);
continue;
}
if ((res = rfcomm_read_and_expect_char(rsock, &c, 'K')) != 1) {
if (res != -2) {
break;
}
rfcomm_append_buf(buf, count, in_count, '\r');
rfcomm_append_buf(buf, count, in_count, '\n');
rfcomm_append_buf(buf, count, in_count, 'O');
rfcomm_append_buf(buf, count, in_count, c);
continue;
}
if ((res = rfcomm_read_and_expect_char(rsock, &c, '\r')) != 1) {
if (res != -2) {
break;
}
rfcomm_append_buf(buf, count, in_count, '\r');
rfcomm_append_buf(buf, count, in_count, '\n');
rfcomm_append_buf(buf, count, in_count, 'O');
rfcomm_append_buf(buf, count, in_count, 'K');
rfcomm_append_buf(buf, count, in_count, c);
continue;
}
if ((res = rfcomm_read_and_expect_char(rsock, &c, '\n')) != 1) {
if (res != -2) {
break;
}
rfcomm_append_buf(buf, count, in_count, '\r');
rfcomm_append_buf(buf, count, in_count, '\n');
rfcomm_append_buf(buf, count, in_count, 'O');
rfcomm_append_buf(buf, count, in_count, 'K');
rfcomm_append_buf(buf, count, in_count, '\r');
rfcomm_append_buf(buf, count, in_count, c);
continue;
}
/* we have successfully parsed a '\r\nOK\r\n' string */
return 1;
}
return res;
}
/*!
* \brief Read the remainder of a +CMGR message.
* \note the entire parsed string is \verbatim '+CMGR: ...\r\n...\r\n...\r\n...\r\nOK\r\n' \endverbatim
*/
static int rfcomm_read_cmgr(int rsock, char **buf, size_t count, size_t *in_count)
{
int res;
/* append the \r\n that was stripped by the calling function */
rfcomm_append_buf(buf, count, in_count, '\r');
rfcomm_append_buf(buf, count, in_count, '\n');
if ((res = rfcomm_read_until_ok(rsock, buf, count, in_count)) != 1) {
ast_log(LOG_ERROR, "error reading +CMGR message on rfcomm socket\n");
}
return res;
}
/*!
* \brief Read and AT result code.
* \note the entire parsed string is \verbatim '\r\n<result code>\r\n' \endverbatim
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
*/
static int rfcomm_read_result(int rsock, char **buf, size_t count, size_t *in_count)
{
int res;
char c;
if ((res = rfcomm_read_and_expect_char(rsock, &c, '\n')) < 1) {
goto e_return;
}
if ((res = rfcomm_read_and_append_char(rsock, buf, count, in_count, &c, '>')) == 1) {
return rfcomm_read_sms_prompt(rsock, buf, count, in_count);
} else if (res != -2) {
goto e_return;
}
rfcomm_append_buf(buf, count, in_count, c);
res = rfcomm_read_until_crlf(rsock, buf, count, in_count);
if (res != 1)
return res;
/* check for CMGR, which contains an embedded \r\n pairs terminated by
* an \r\nOK\r\n message */
if (*in_count >= 5 && !strncmp(*buf - *in_count, "+CMGR", 5)) {
return rfcomm_read_cmgr(rsock, buf, count, in_count);
}
return 1;
e_return:
ast_log(LOG_ERROR, "error parsing AT result on rfcomm socket\n");
return res;
}
/*!
* \brief Read the remainder of an AT command.
* \note the entire parsed string is \verbatim '<at command>\r' \endverbatim
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
*/
static int rfcomm_read_command(int rsock, char **buf, size_t count, size_t *in_count)
{
int res;
char c;
while ((res = read(rsock, &c, 1)) == 1) {
rfcomm_read_debug(c);
/* stop when we get to '\r' */
if (c == '\r')
break;
rfcomm_append_buf(buf, count, in_count, c);
}
return res;
}
/*!
* \brief Read one Hayes AT message from an rfcomm socket.
* \param rsock the rfcomm socket to read from
* \param buf the buffer to store the result in
* \param count the size of the buffer or the maximum number of characters to read
*
* Here we need to read complete Hayes AT messages. The AT message formats we
* support are listed below.
*
* \verbatim
* \r\n<result code>\r\n
* <at command>\r
* \endverbatim
*
* These formats correspond to AT result codes, AT commands, and the AT SMS
* prompt respectively. When messages are read the leading and trailing \verbatim '\r' \endverbatim
* and \verbatim '\n' \endverbatim characters are discarded. If the given buffer is not large enough
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
* to hold the response, what does not fit in the buffer will be dropped.
*
* \note The rfcomm connection to the device is asynchronous, so there is no
* guarantee that responses will be returned in a single read() call. We handle
* this by blocking until we can read an entire response.
*
* \retval 0 end of file
* \retval -1 read error
* \retval -2 parse error
* \retval other the number of characters added to buf
*/
static ssize_t rfcomm_read(int rsock, char *buf, size_t count)
{
ssize_t res;
size_t in_count = 0;
char c;
if ((res = rfcomm_read_and_expect_char(rsock, &c, '\r')) == 1) {
res = rfcomm_read_result(rsock, &buf, count, &in_count);
} else if (res == -2) {
rfcomm_append_buf(&buf, count, &in_count, c);
res = rfcomm_read_command(rsock, &buf, count, &in_count);
}
if (res < 1)
return res;
else
return in_count;
}
/*
sco helpers and callbacks
*/
static int sco_connect(bdaddr_t src, bdaddr_t dst)
{
struct sockaddr_sco addr;
int s;
if ((s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) {
ast_debug(1, "socket() failed (%d).\n", errno);
return -1;
}
/* XXX this does not work with the do_sco_listen() thread (which also bind()s
* to this address). Also I am not sure if it is necessary. */
#if 0
memset(&addr, 0, sizeof(addr));
addr.sco_family = AF_BLUETOOTH;
bacpy(&addr.sco_bdaddr, &src);
if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
ast_debug(1, "bind() failed (%d).\n", errno);
close(s);
return -1;
}
#endif
memset(&addr, 0, sizeof(addr));
addr.sco_family = AF_BLUETOOTH;
bacpy(&addr.sco_bdaddr, &dst);
if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
ast_debug(1, "sco connect() failed (%d).\n", errno);
close(s);
return -1;
}
return s;
}
static int sco_write(int s, char *buf, int len)
{
int r;
if (s == -1) {
ast_debug(3, "sco_write() not ready\n");
return 0;
}
ast_debug(3, "sco_write()\n");
r = write(s, buf, len);
if (r == -1) {
ast_debug(3, "sco write error %d\n", errno);
return 0;
}
return 1;
}
/*!
* \brief Accept SCO connections.
* This function is an ast_io callback function used to accept incoming sco
* audio connections.
*/
static int sco_accept(int *id, int fd, short events, void *data)
{
struct adapter_pvt *adapter = (struct adapter_pvt *) data;
struct sockaddr_sco addr;
socklen_t addrlen;
struct mbl_pvt *pvt;
socklen_t len;
char saddr[18];
struct sco_options so;
int sock;
addrlen = sizeof(struct sockaddr_sco);
if ((sock = accept(fd, (struct sockaddr *)&addr, &addrlen)) == -1) {
ast_log(LOG_ERROR, "error accepting audio connection on adapter %s\n", adapter->id);
return 0;
}
len = sizeof(so);
getsockopt(sock, SOL_SCO, SCO_OPTIONS, &so, &len);
ba2str(&addr.sco_bdaddr, saddr);
ast_debug(1, "Incoming Audio Connection from device %s MTU is %d\n", saddr, so.mtu);
/* figure out which device this sco connection belongs to */
pvt = NULL;
AST_RWLIST_RDLOCK(&devices);
AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
if (!bacmp(&pvt->addr, &addr.sco_bdaddr))
break;
}
AST_RWLIST_UNLOCK(&devices);
if (!pvt) {
ast_log(LOG_WARNING, "could not find device for incoming audio connection\n");
close(sock);
return 1;
}
ast_mutex_lock(&pvt->lock);
if (pvt->sco_socket != -1) {
close(pvt->sco_socket);
pvt->sco_socket = -1;
}
pvt->sco_socket = sock;
if (pvt->owner) {
ast_channel_set_fd(pvt->owner, 0, sock);
} else {
ast_debug(1, "incoming audio connection for pvt without owner\n");
}
ast_mutex_unlock(&pvt->lock);
return 1;
}
/*!
* \brief Bind an SCO listener socket for the given adapter.
* \param adapter an adapter_pvt
* \return -1 on error, non zero on success
*/
static int sco_bind(struct adapter_pvt *adapter)
{
struct sockaddr_sco addr;
int opt = 1;
if ((adapter->sco_socket = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) {
ast_log(LOG_ERROR, "Unable to create sco listener socket for adapter %s.\n", adapter->id);
goto e_return;
}
memset(&addr, 0, sizeof(addr));
addr.sco_family = AF_BLUETOOTH;
bacpy(&addr.sco_bdaddr, &adapter->addr);
if (bind(adapter->sco_socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
ast_log(LOG_ERROR, "Unable to bind sco listener socket. (%d)\n", errno);
goto e_close_socket;
}
if (setsockopt(adapter->sco_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) {
ast_log(LOG_ERROR, "Unable to setsockopt sco listener socket.\n");
goto e_close_socket;
}
if (listen(adapter->sco_socket, 5) < 0) {
ast_log(LOG_ERROR, "Unable to listen sco listener socket.\n");
goto e_close_socket;
}
return adapter->sco_socket;
e_close_socket:
close(adapter->sco_socket);
adapter->sco_socket = -1;
e_return:
return -1;
}
/*
* Hayes AT command helpers.
*/
/*!
* \brief Match the given buffer with the given prefix.
* \param buf the buffer to match