summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarcel Holtmann <marcel@holtmann.org>2002-12-21 15:40:33 +0000
committerMarcel Holtmann <marcel@holtmann.org>2002-12-21 15:40:33 +0000
commit7490b6c61bfa0b581ff7a15f6482787973cf7e3f (patch)
treeffdcc48949262bedd358df3534bc637715f162fc
parent34e63ae877d59207762da9f96e414a704c825393 (diff)
Add Headset Test utility
-rw-r--r--test/Makefile.am4
-rwxr-xr-xtest/hsmicro20
-rwxr-xr-xtest/hsplay22
-rw-r--r--test/hstest.c333
4 files changed, 378 insertions, 1 deletions
diff --git a/test/Makefile.am b/test/Makefile.am
index dc84f46f..ec6b7779 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -2,4 +2,6 @@
# $Id$
#
-noinst_PROGRAMS = l2test scotest rctest attest
+noinst_PROGRAMS = l2test scotest rctest attest hstest
+
+EXTRA_DIST = hsplay hsmicro
diff --git a/test/hsmicro b/test/hsmicro
new file mode 100755
index 00000000..8aec68f2
--- /dev/null
+++ b/test/hsmicro
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+SOX=`which sox`
+HSTEST=`which hstest`
+
+if [ -z "$HSTEST" ]
+then
+ HSTEST="./hstest"
+fi
+
+if [ -z "$1" ]
+then
+ echo -e "Usage:\n\thsmicro <bdaddr> [channel]"
+ exit
+fi
+
+BDADDR=$1
+CHANNEL=$2
+
+$HSTEST record - $BDADDR $CHANNEL | $SOX -t raw -r 8000 -c 1 -s -b - -t ossdsp -r 44100 -c 2 -s -w /dev/dsp polyphase vol 5.0 2> /dev/null
diff --git a/test/hsplay b/test/hsplay
new file mode 100755
index 00000000..8a676459
--- /dev/null
+++ b/test/hsplay
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+MPG123=`which mpg123`
+SOX=`which sox`
+HSTEST=`which hstest`
+
+if [ -z "$HSTEST" ]
+then
+ HSTEST="./hstest"
+fi
+
+if [ -z "$1" -o -z "$2" ]
+then
+ echo -e "Usage:\n\thsplay <file> <bdaddr> [channel]"
+ exit
+fi
+
+FILE=$1
+BDADDR=$2
+CHANNEL=$3
+
+$MPG123 -q -s "$FILE" | $SOX -t raw -r 44100 -c 2 -s -w - -t raw -r 8000 -c 1 -s -b - | $HSTEST play - $BDADDR $CHANNEL
diff --git a/test/hstest.c b/test/hstest.c
new file mode 100644
index 00000000..867eb20b
--- /dev/null
+++ b/test/hstest.c
@@ -0,0 +1,333 @@
+/*
+ *
+ * Bluetooth Headset Test utility
+ *
+ * Copyright (C) 2002 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 version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <termios.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/sco.h>
+#include <bluetooth/rfcomm.h>
+
+
+#ifndef OCF_READ_VOICE_SETTING
+#define OCF_READ_VOICE_SETTING 0x0025
+typedef struct {
+ uint8_t status;
+ uint16_t voice_setting;
+} __attribute__ ((packed)) read_voice_setting_rp;
+#define READ_VOICE_SETTING_RP_SIZE 3
+
+static int hci_read_voice_setting(int dd, uint16_t *vs, int to)
+{
+ read_voice_setting_rp rp;
+ struct hci_request rq;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_READ_VOICE_SETTING;
+ rq.rparam = &rp;
+ rq.rlen = READ_VOICE_SETTING_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ *vs = rp.voice_setting;
+ return 0;
+}
+#endif
+
+
+static volatile int terminate = 0;
+
+
+static void sig_term(int sig) {
+ terminate = 1;
+}
+
+
+static int rfcomm_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t channel)
+{
+ struct sockaddr_rc addr;
+ int s;
+
+ if ((s = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) {
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.rc_family = AF_BLUETOOTH;
+ bacpy(&addr.rc_bdaddr, src);
+ addr.rc_channel = 0;
+ if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ close(s);
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.rc_family = AF_BLUETOOTH;
+ bacpy(&addr.rc_bdaddr, dst);
+ addr.rc_channel = channel;
+ if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0 ){
+ close(s);
+ return -1;
+ }
+
+ return s;
+}
+
+
+static int sco_connect(bdaddr_t *src, bdaddr_t *dst, uint16_t *handle, uint16_t *mtu)
+{
+ struct sockaddr_sco addr;
+ struct sco_conninfo conn;
+ struct sco_options opts;
+ int s, size;
+
+ if ((s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) {
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sco_family = AF_BLUETOOTH;
+ bacpy(&addr.sco_bdaddr, src);
+ if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ close(s);
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sco_family = AF_BLUETOOTH;
+ bacpy(&addr.sco_bdaddr, dst);
+ if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0 ){
+ close(s);
+ return -1;
+ }
+
+ size = sizeof(conn);
+ if (getsockopt(s, SOL_SCO, SCO_CONNINFO, &conn, &size) < 0) {
+ close(s);
+ return -1;
+ }
+
+ size = sizeof(opts);
+ if (getsockopt(s, SOL_SCO, SCO_OPTIONS, &opts, &size) < 0) {
+ close(s);
+ return -1;
+ }
+
+ if (handle)
+ *handle = conn.hci_handle;
+
+ if (mtu)
+ *mtu = opts.mtu;
+
+ return s;
+}
+
+
+static void usage(void)
+{
+ printf("Usage:\n"
+ "\thstest play <file> <bdaddr> [channel]\n"
+ "\thstest record <file> <bdaddr> [channel]\n");
+}
+
+
+#define PLAY 1
+#define RECORD 2
+
+int main(int argc, char *argv[])
+{
+ struct sigaction sa;
+
+ fd_set rfds;
+ struct timeval timeout;
+ unsigned char buf[2048];
+ int maxfd, sel, rlen, wlen;
+
+ bdaddr_t local;
+ bdaddr_t bdaddr;
+ uint8_t channel;
+
+ char *filename;
+ mode_t filemode;
+ int mode = 0;
+ int dd, rd, sd, fd;
+ uint16_t sco_handle, sco_mtu, vs;
+
+
+ switch (argc) {
+ case 4:
+ str2ba(argv[3], &bdaddr);
+ channel = 6;
+ break;
+ case 5:
+ str2ba(argv[3], &bdaddr);
+ channel = atoi(argv[4]);
+ break;
+ default:
+ usage();
+ exit(-1);
+ }
+
+ if (strncmp(argv[1], "play", 4) == 0) {
+ mode = PLAY;
+ filemode = O_RDONLY;
+ } else if (strncmp(argv[1], "rec", 3) == 0) {
+ mode = RECORD;
+ filemode = O_WRONLY | O_CREAT | O_TRUNC;
+ } else {
+ usage();
+ exit(-1);
+ }
+
+ filename = argv[2];
+
+
+ hci_devba(0, &local);
+ dd = hci_open_dev(0);
+ hci_read_voice_setting(dd, &vs, 1000);
+ fprintf(stderr, "Voice setting: 0x%04x\n", vs);
+ close(dd);
+ if (vs != 0x0040) {
+ fprintf(stderr, "The voice setting must be 0x0040\n");
+ return -1;
+ }
+
+
+ if (strcmp(filename, "-") == 0) {
+ switch (mode) {
+ case PLAY:
+ fd = 0;
+ break;
+ case RECORD:
+ fd = 1;
+ break;
+ default:
+ return -1;
+ }
+ } else {
+ if ((fd = open(filename, filemode)) < 0) {
+ perror("Can't open input/output file");
+ return -1;
+ }
+ }
+
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_flags = SA_NOCLDSTOP;
+ sa.sa_handler = sig_term;
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGCHLD, &sa, NULL);
+ sigaction(SIGPIPE, &sa, NULL);
+
+
+ if ((rd = rfcomm_connect(&local, &bdaddr, channel)) < 0) {
+ perror("Can't connect RFCOMM channel");
+ return -1;
+ }
+
+ fprintf(stderr, "RFCOMM channel connected\n");
+
+
+ if ((sd = sco_connect(&local, &bdaddr, &sco_handle, &sco_mtu)) < 0) {
+ perror("Can't connect SCO audio channel");
+ close(rd);
+ return -1;
+ }
+
+ fprintf(stderr, "SCO audio channel connected (handle %d, mtu %d)\n", sco_handle, sco_mtu);
+
+
+ if (mode == RECORD)
+ write(rd, "RING\r\n", 6);
+
+ maxfd = (rd > sd) ? rd : sd;
+
+ while (!terminate) {
+
+ FD_ZERO(&rfds);
+ FD_SET(rd, &rfds);
+ FD_SET(sd, &rfds);
+
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 10000;
+
+ if ((sel = select(maxfd + 1, &rfds, NULL, NULL, &timeout)) > 0) {
+
+ if (FD_ISSET(rd, &rfds)) {
+ memset(buf, 0, sizeof(buf));
+ rlen = read(rd, buf, sizeof(buf));
+ if (rlen > 0) {
+ fprintf(stderr, "%s\n", buf);
+ wlen = write(rd, "OK\r\n", 4);
+ }
+ }
+
+ if (FD_ISSET(sd, &rfds)) {
+ memset(buf, 0, sizeof(buf));
+ rlen = read(sd, buf, sizeof(buf));
+ if (rlen > 0)
+ switch (mode) {
+ case PLAY:
+ rlen = read(fd, buf, rlen);
+ wlen = write(sd, buf, rlen);
+ break;
+ case RECORD:
+ wlen = write(fd, buf, rlen);
+ break;
+ default:
+ break;
+ }
+ }
+
+ }
+
+ }
+
+ close(sd);
+ sleep(5);
+ close(rd);
+
+ close(fd);
+
+ return 0;
+}