/* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2008-2009 Texas Instruments, Inc. * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com> * Copyright (C) 2002-2008 Marcel Holtmann <marcel@holtmann.org> * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include <config.h> #endif #include <stdio.h> #include <errno.h> #include <ctype.h> #include <unistd.h> #include <stdlib.h> #include <getopt.h> #include <syslog.h> #include <signal.h> #include <sys/time.h> #include <sys/socket.h> #include <bluetooth/bluetooth.h> #include <bluetooth/hci.h> #include <bluetooth/hci_lib.h> #include <bluetooth/sco.h> /* Static Local variables */ /* BD Address of the BT head set */ static bdaddr_t bdaddr; /* Buffer to receive data feom headset */ static unsigned char *buffer; /* Default data size */ static long data_size = 672; /* Handling termination of process through signals */ static volatile int terminate = 0; /* Static functions declerations */ static void send_hciCmd(int dev_id, int command_length, char **command); /** Function to handle signal terminations */ static void sig_term(int sig) { terminate = 1; } /** do_connect Function * This function Creates the SCO connection to the BT headset * * Parameters : * @ svr : BD address of headset * Returns SCO socket id on success * suitable error code */ static int do_connect(char *svr) { struct sockaddr_sco addr; struct sco_conninfo conn; socklen_t optlen; int sk; /* Create socket */ sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO); if (sk < 0) { syslog(LOG_ERR, "Can't create socket: %s (%d)", strerror(errno), errno); return -1; } /* Bind to local address */ memset(&addr, 0, sizeof(addr)); addr.sco_family = AF_BLUETOOTH; bacpy(&addr.sco_bdaddr, &bdaddr); if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { syslog(LOG_ERR, "Can't bind socket: %s (%d)", strerror(errno), errno); goto error; } /* Connect to remote device */ memset(&addr, 0, sizeof(addr)); addr.sco_family = AF_BLUETOOTH; str2ba(svr, &addr.sco_bdaddr); if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { syslog(LOG_ERR, "Can't connect: %s (%d)", strerror(errno), errno); goto error; } /* Get connection information */ memset(&conn, 0, sizeof(conn)); optlen = sizeof(conn); if (getsockopt(sk, SOL_SCO, SCO_CONNINFO, &conn, &optlen) < 0) { syslog(LOG_ERR, "Can't get SCO connection information: %s (%d)", strerror(errno), errno); goto error; } syslog(LOG_INFO, "Connected [handle %d, class 0x%02x%02x%02x]", conn.hci_handle, conn.dev_class[2], conn.dev_class[1], conn.dev_class[0]); return sk; error: close(sk); return -1; } /** dump_mode Function * This function waits till disconnection is intiated from headset or from * the application. * * Parameters : * @ sk : SCO socket id * Returns VOID */ static void dump_mode(int sk) { int len; /* Wait till disconnect is issued from the headset OR * IF the application is killed using signals */ while ( ((len = read(sk, buffer, data_size)) > 0) && (!terminate) ) syslog(LOG_INFO, "Recevied %d bytes", len); } /** send_hciCmd Function * This function takes the hci commands for the BT chip configurations, creates * a hci channel to send the commadns through UART to configure BT chip * * Parameters : * @ dev_id : HCI device ID * @ command_length : Number of arguments of the command * @ command : Pointer to command list * Returns 0 upon success * , different error messages depending upon the error. */ static void send_hciCmd(int dev_id, int command_length, char **command) { unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr = buf; struct hci_filter flt; hci_event_hdr *hdr; int i, opt, len, dd; uint16_t ocf; uint8_t ogf; if (dev_id < 0) dev_id = hci_get_route(NULL); errno = 0; ogf = strtol(command[0], NULL, 16); ocf = strtol(command[1], NULL, 16); for (i = 2, len = 0; i < command_length && len < sizeof(buf); i++, len++) *ptr++ = (uint8_t) strtol(command[i], NULL, 16); dd = hci_open_dev(dev_id); if (dd < 0) { perror("Device open failed"); return; } /* Setup filter */ hci_filter_clear(&flt); hci_filter_set_ptype(HCI_EVENT_PKT, &flt); hci_filter_all_events(&flt); if (setsockopt(dd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) { perror("HCI filter setup failed"); return; } /* Send the BT chip configuration commands */ if (hci_send_cmd(dd, ogf, ocf, len, buf) < 0) { perror("Send failed"); return; } /* Wait for the command completion event */ len = read(dd, buf, sizeof(buf)); if (len < 0) { perror("Read failed"); return; } hdr = (void *)(buf + 1); ptr = buf + (1 + HCI_EVENT_HDR_SIZE); len -= (1 + HCI_EVENT_HDR_SIZE); hci_close_dev(dd); } /** USAGE Function * This function displays the usage of the bt_sco_app application. * * Parameters : * @ VOID * Returns VOID */ static void usage(void) { printf("bt_scoapp\n" "Usage:\n"); printf("\tbt_scoapp [bd_addr]\n"); } /** Main Function * The main function takes the command line BD adress of headset as inputs , * Calls the hci send configuration function and then creates a SCO connection. * * Parameters : * @ argc : Number of arguments on the command line * @ argv : Pointer to argument list - BD addr is the only valid argument. * Returns 0 upon success * , different error messages depending upon the error. */ int main(int argc ,char *argv[]) { struct sigaction sa; int opt, i = 0, sk; /* BT PCM configurations commands */ char *command[] = { "0x3f", "0x106", /* OCF and OGF */ "0x80", "0x00", /* Bit clock - 128KHz*/ "0x00", /* BT chip as Master*/ "0x40", "0x1f", "0x00", "0x00", /* Sampling rate - 8KHz*/ "0x01", "0x00", /* 50% Duty cycle*/ "0x00", /* Frame sync at falling edge*/ "0x00", /* FS Active high polarity*/ "0x00", /* FS direction - [Reserved]*/ "0x10", "0x00", /* CH1 16 -bit OUT size*/ "0x01", "0x00", /* One Clock delay */ "0x00", /* Data driven at rising edge*/ "0x10", "0x00", /* CH1 16 -bit IN size */ "0x01", "0x00", /* CH1 DAta IN One Clock delay*/ "0x00", /* Data driven at sampling edge*/ "0x00", /* Reserved bit*/ "0x10", "0x00", /* CH2 16 -bit OUT size*/ "0x11", "0x00", /* CH2 data OUT off set*/ "0x00", /* Data driven at rising edge*/ "0x10", "0x00", /* CH2 16 -bit IN size*/ "0x11", "0x00", /* CH2 data IN off set*/ "0x00", /* Data Sampled at rising edge*/ "0x00" /* Reserved bit*/ }; int command_length = 36; /* Length of the BT configuration commands */ /* Check if the number of arguemts mentioned is 2 */ if (argc != 2) { printf("\n Wrong input - No BD headset address specified"); usage(); exit(1); } memset(&sa, 0, sizeof(sa)); sa.sa_flags = SA_NOCLDSTOP; sa.sa_handler = sig_term; sigaction(SIGTERM, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigaction(SIGCHLD, &sa, NULL); sigaction(SIGPIPE, &sa, NULL); openlog("bt_sco_app", LOG_PERROR | LOG_PID, LOG_LOCAL0); /* Allocate memory for the buffer */ if (!(buffer = malloc(data_size))) { perror("Can't allocate data buffer"); exit(1); } /* send the BT configuration commands */ send_hciCmd(-1,command_length,command); sleep(2); /* wait for some time while BT chip is configured */ sk = do_connect(argv[1]); if (sk < 0) exit(1); dump_mode(sk); syslog(LOG_INFO, "Exit"); closelog(); return 0; }