summaryrefslogtreecommitdiff
path: root/test/avtest.c
diff options
context:
space:
mode:
Diffstat (limited to 'test/avtest.c')
-rw-r--r--test/avtest.c869
1 files changed, 869 insertions, 0 deletions
diff --git a/test/avtest.c b/test/avtest.c
new file mode 100644
index 0000000..168326f
--- /dev/null
+++ b/test/avtest.c
@@ -0,0 +1,869 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2007-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2009-2010 Nokia Corporation
+ *
+ *
+ * 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 <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/sdp.h>
+
+#define AVDTP_PKT_TYPE_SINGLE 0x00
+#define AVDTP_PKT_TYPE_START 0x01
+#define AVDTP_PKT_TYPE_CONTINUE 0x02
+#define AVDTP_PKT_TYPE_END 0x03
+
+#define AVDTP_MSG_TYPE_COMMAND 0x00
+#define AVDTP_MSG_TYPE_GEN_REJECT 0x01
+#define AVDTP_MSG_TYPE_ACCEPT 0x02
+#define AVDTP_MSG_TYPE_REJECT 0x03
+
+#define AVDTP_DISCOVER 0x01
+#define AVDTP_GET_CAPABILITIES 0x02
+#define AVDTP_SET_CONFIGURATION 0x03
+#define AVDTP_GET_CONFIGURATION 0x04
+#define AVDTP_RECONFIGURE 0x05
+#define AVDTP_OPEN 0x06
+#define AVDTP_START 0x07
+#define AVDTP_CLOSE 0x08
+#define AVDTP_SUSPEND 0x09
+#define AVDTP_ABORT 0x0A
+
+#define AVDTP_SEP_TYPE_SOURCE 0x00
+#define AVDTP_SEP_TYPE_SINK 0x01
+
+#define AVDTP_MEDIA_TYPE_AUDIO 0x00
+#define AVDTP_MEDIA_TYPE_VIDEO 0x01
+#define AVDTP_MEDIA_TYPE_MULTIMEDIA 0x02
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avdtp_header {
+ uint8_t message_type:2;
+ uint8_t packet_type:2;
+ uint8_t transaction:4;
+ uint8_t signal_id:6;
+ uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct seid_info {
+ uint8_t rfa0:1;
+ uint8_t inuse:1;
+ uint8_t seid:6;
+ uint8_t rfa2:3;
+ uint8_t type:1;
+ uint8_t media_type:4;
+} __attribute__ ((packed));
+
+struct avdtp_start_header {
+ uint8_t message_type:2;
+ uint8_t packet_type:2;
+ uint8_t transaction:4;
+ uint8_t no_of_packets;
+ uint8_t signal_id:6;
+ uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct avdtp_continue_header {
+ uint8_t message_type:2;
+ uint8_t packet_type:2;
+ uint8_t transaction:4;
+} __attribute__ ((packed));
+
+struct avctp_header {
+ uint8_t ipid:1;
+ uint8_t cr:1;
+ uint8_t packet_type:2;
+ uint8_t transaction:4;
+ uint16_t pid;
+} __attribute__ ((packed));
+#define AVCTP_HEADER_LENGTH 3
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avdtp_header {
+ uint8_t transaction:4;
+ uint8_t packet_type:2;
+ uint8_t message_type:2;
+ uint8_t rfa0:2;
+ uint8_t signal_id:6;
+} __attribute__ ((packed));
+
+struct seid_info {
+ uint8_t seid:6;
+ uint8_t inuse:1;
+ uint8_t rfa0:1;
+ uint8_t media_type:4;
+ uint8_t type:1;
+ uint8_t rfa2:3;
+} __attribute__ ((packed));
+
+struct avdtp_start_header {
+ uint8_t transaction:4;
+ uint8_t packet_type:2;
+ uint8_t message_type:2;
+ uint8_t no_of_packets;
+ uint8_t rfa0:2;
+ uint8_t signal_id:6;
+} __attribute__ ((packed));
+
+struct avdtp_continue_header {
+ uint8_t transaction:4;
+ uint8_t packet_type:2;
+ uint8_t message_type:2;
+} __attribute__ ((packed));
+
+struct avctp_header {
+ uint8_t transaction:4;
+ uint8_t packet_type:2;
+ uint8_t cr:1;
+ uint8_t ipid:1;
+ uint16_t pid;
+} __attribute__ ((packed));
+#define AVCTP_HEADER_LENGTH 3
+
+#else
+#error "Unknown byte order"
+#endif
+
+#define AVCTP_COMMAND 0
+#define AVCTP_RESPONSE 1
+
+#define AVCTP_PACKET_SINGLE 0
+
+static const unsigned char media_transport[] = {
+ 0x01, /* Media transport category */
+ 0x00,
+ 0x07, /* Media codec category */
+ 0x06,
+ 0x00, /* Media type audio */
+ 0x00, /* Codec SBC */
+ 0x22, /* 44.1 kHz, stereo */
+ 0x15, /* 16 blocks, 8 subbands */
+ 0x02,
+ 0x33,
+};
+
+static int media_sock = -1;
+
+static void dump_avctp_header(struct avctp_header *hdr)
+{
+ printf("TL %d PT %d CR %d IPID %d PID 0x%04x\n", hdr->transaction,
+ hdr->packet_type, hdr->cr, hdr->ipid, ntohs(hdr->pid));
+}
+
+static void dump_avdtp_header(struct avdtp_header *hdr)
+{
+ printf("TL %d PT %d MT %d SI %d\n", hdr->transaction,
+ hdr->packet_type, hdr->message_type, hdr->signal_id);
+}
+
+static void dump_buffer(const unsigned char *buf, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ printf("%02x ", buf[i]);
+ printf("\n");
+}
+
+static void process_avdtp(int srv_sk, int sk, unsigned char reject,
+ int fragment)
+{
+ unsigned char buf[672];
+ ssize_t len;
+
+ while (1) {
+ struct avdtp_header *hdr = (void *) buf;
+
+ len = read(sk, buf, sizeof(buf));
+ if (len <= 0) {
+ perror("Read failed");
+ break;
+ }
+
+ dump_buffer(buf, len);
+ dump_avdtp_header(hdr);
+
+ if (hdr->packet_type != AVDTP_PKT_TYPE_SINGLE) {
+ fprintf(stderr, "Only single packets are supported\n");
+ break;
+ }
+
+ if (hdr->message_type != AVDTP_MSG_TYPE_COMMAND) {
+ fprintf(stderr, "Ignoring non-command messages\n");
+ continue;
+ }
+
+ switch (hdr->signal_id) {
+ case AVDTP_DISCOVER:
+ if (reject == AVDTP_DISCOVER) {
+ hdr->message_type = AVDTP_MSG_TYPE_REJECT;
+ buf[2] = 0x29; /* Unsupported configuration */
+ printf("Rejecting discover command\n");
+ len = write(sk, buf, 3);
+ } else {
+ struct seid_info *sei = (void *) (buf + 2);
+ hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+ buf[2] = 0x00;
+ buf[3] = 0x00;
+ sei->seid = 0x01;
+ sei->type = AVDTP_SEP_TYPE_SINK;
+ sei->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+ printf("Accepting discover command\n");
+ len = write(sk, buf, 4);
+ }
+ break;
+
+ case AVDTP_GET_CAPABILITIES:
+ if (reject == AVDTP_GET_CAPABILITIES) {
+ hdr->message_type = AVDTP_MSG_TYPE_REJECT;
+ buf[2] = 0x29; /* Unsupported configuration */
+ printf("Rejecting get capabilties command\n");
+ len = write(sk, buf, 3);
+ } else if (fragment) {
+ struct avdtp_start_header *start = (void *) buf;
+
+ printf("Sending fragmented reply to getcap\n");
+
+ hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+
+ /* Start packet */
+ hdr->packet_type = AVDTP_PKT_TYPE_START;
+ start->signal_id = AVDTP_GET_CAPABILITIES;
+ start->no_of_packets = 3;
+ memcpy(&buf[3], media_transport,
+ sizeof(media_transport));
+ len = write(sk, buf,
+ 3 + sizeof(media_transport));
+
+ /* Continue packet */
+ hdr->packet_type = AVDTP_PKT_TYPE_CONTINUE;
+ memcpy(&buf[1], media_transport,
+ sizeof(media_transport));
+ len = write(sk, buf,
+ 1 + sizeof(media_transport));
+
+ /* End packet */
+ hdr->packet_type = AVDTP_PKT_TYPE_END;
+ memcpy(&buf[1], media_transport,
+ sizeof(media_transport));
+ len = write(sk, buf,
+ 1 + sizeof(media_transport));
+ } else {
+ hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+ memcpy(&buf[2], media_transport,
+ sizeof(media_transport));
+ printf("Accepting get capabilities command\n");
+ len = write(sk, buf,
+ 2 + sizeof(media_transport));
+ }
+ break;
+
+ case AVDTP_SET_CONFIGURATION:
+ if (reject == AVDTP_SET_CONFIGURATION) {
+ hdr->message_type = AVDTP_MSG_TYPE_REJECT;
+ buf[2] = buf[4];
+ buf[3] = 0x13; /* SEP In Use */
+ printf("Rejecting set configuration command\n");
+ len = write(sk, buf, 4);
+ } else {
+ hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+ printf("Accepting set configuration command\n");
+ len = write(sk, buf, 2);
+ }
+ break;
+
+ case AVDTP_GET_CONFIGURATION:
+ if (reject == AVDTP_GET_CONFIGURATION) {
+ hdr->message_type = AVDTP_MSG_TYPE_REJECT;
+ buf[2] = 0x12; /* Bad ACP SEID */
+ printf("Rejecting get configuration command\n");
+ len = write(sk, buf, 3);
+ } else {
+ hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+ printf("Accepting get configuration command\n");
+ len = write(sk, buf, 2);
+ }
+ break;
+
+ case AVDTP_OPEN:
+ if (reject == AVDTP_OPEN) {
+ hdr->message_type = AVDTP_MSG_TYPE_REJECT;
+ buf[2] = 0x31; /* Bad State */
+ printf("Rejecting open command\n");
+ len = write(sk, buf, 3);
+ } else {
+ struct sockaddr_l2 addr;
+ socklen_t optlen;
+
+ hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+ printf("Accepting open command\n");
+ len = write(sk, buf, 2);
+
+ memset(&addr, 0, sizeof(addr));
+ optlen = sizeof(addr);
+
+ media_sock = accept(srv_sk,
+ (struct sockaddr *) &addr,
+ &optlen);
+ if (media_sock < 0) {
+ perror("Accept failed");
+ break;
+ }
+ }
+ break;
+
+ case AVDTP_START:
+ if (reject == AVDTP_ABORT)
+ printf("Ignoring start to cause abort");
+ else if (reject == AVDTP_START) {
+ hdr->message_type = AVDTP_MSG_TYPE_REJECT;
+ buf[3] = 0x31; /* Bad State */
+ printf("Rejecting start command\n");
+ len = write(sk, buf, 4);
+ } else {
+ hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+ printf("Accepting start command\n");
+ len = write(sk, buf, 2);
+ }
+ break;
+
+ case AVDTP_CLOSE:
+ if (reject == AVDTP_CLOSE) {
+ hdr->message_type = AVDTP_MSG_TYPE_REJECT;
+ buf[2] = 0x31; /* Bad State */
+ printf("Rejecting close command\n");
+ len = write(sk, buf, 3);
+ } else {
+ hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+ printf("Accepting close command\n");
+ len = write(sk, buf, 2);
+ if (media_sock >= 0) {
+ close(media_sock);
+ media_sock = -1;
+ }
+ }
+ break;
+
+ case AVDTP_SUSPEND:
+ if (reject == AVDTP_SUSPEND) {
+ hdr->message_type = AVDTP_MSG_TYPE_REJECT;
+ buf[3] = 0x31; /* Bad State */
+ printf("Rejecting suspend command\n");
+ len = write(sk, buf, 4);
+ } else {
+ hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+ printf("Accepting suspend command\n");
+ len = write(sk, buf, 2);
+ }
+ break;
+
+ case AVDTP_ABORT:
+ hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+ printf("Accepting abort command\n");
+ len = write(sk, buf, 2);
+ if (media_sock >= 0) {
+ close(media_sock);
+ media_sock = -1;
+ }
+ break;
+
+ default:
+ buf[1] = 0x00;
+ printf("Unknown command\n");
+ len = write(sk, buf, 2);
+ break;
+ }
+ }
+}
+
+static void process_avctp(int sk, int reject)
+{
+ unsigned char buf[672];
+ ssize_t len;
+
+ while (1) {
+ struct avctp_header *hdr = (void *) buf;
+
+ len = read(sk, buf, sizeof(buf));
+ if (len <= 0) {
+ perror("Read failed");
+ break;
+ }
+
+ dump_buffer(buf, len);
+
+ if (len >= AVCTP_HEADER_LENGTH)
+ dump_avctp_header(hdr);
+ }
+}
+
+static int set_minimum_mtu(int sk)
+{
+ struct l2cap_options l2o;
+ socklen_t optlen;
+
+ memset(&l2o, 0, sizeof(l2o));
+ optlen = sizeof(l2o);
+
+ if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &optlen) < 0) {
+ perror("getsockopt");
+ return -1;
+ }
+
+ l2o.imtu = 48;
+ l2o.omtu = 48;
+
+ if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, sizeof(l2o)) < 0) {
+ perror("setsockopt");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void do_listen(const bdaddr_t *src, unsigned char reject, int fragment)
+{
+ struct sockaddr_l2 addr;
+ socklen_t optlen;
+ int sk, nsk;
+
+ sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+ if (sk < 0) {
+ perror("Can't create socket");
+ return;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, src);
+ addr.l2_psm = htobs(25);
+
+ if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("Can't bind socket");
+ goto error;
+ }
+
+ if (fragment)
+ set_minimum_mtu(sk);
+
+ if (listen(sk, 10)) {
+ perror("Can't listen on the socket");
+ goto error;
+ }
+
+ while (1) {
+ memset(&addr, 0, sizeof(addr));
+ optlen = sizeof(addr);
+
+ nsk = accept(sk, (struct sockaddr *) &addr, &optlen);
+ if (nsk < 0) {
+ perror("Accept failed");
+ continue;
+ }
+
+ process_avdtp(sk, nsk, reject, fragment);
+
+ if (media_sock >= 0) {
+ close(media_sock);
+ media_sock = -1;
+ }
+
+ close(nsk);
+ }
+
+error:
+ close(sk);
+}
+
+static int do_connect(const bdaddr_t *src, const bdaddr_t *dst, int avctp,
+ int fragment)
+{
+ struct sockaddr_l2 addr;
+ int sk, err;
+
+ sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+ if (sk < 0) {
+ perror("Can't create socket");
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, src);
+
+ if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("Can't bind socket");
+ goto error;
+ }
+
+ if (fragment)
+ set_minimum_mtu(sk);
+
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, dst);
+ addr.l2_psm = htobs(avctp ? 23 : 25);
+
+ err = connect(sk, (struct sockaddr *) &addr, sizeof(addr));
+ if (err < 0) {
+ perror("Unable to connect");
+ goto error;
+ }
+
+ return sk;
+
+error:
+ close(sk);
+ return -1;
+}
+
+static void do_avdtp_send(int sk, const bdaddr_t *src, const bdaddr_t *dst,
+ unsigned char cmd, int invalid, int preconf)
+{
+ unsigned char buf[672];
+ struct avdtp_header *hdr = (void *) buf;
+ ssize_t len;
+
+ memset(buf, 0, sizeof(buf));
+
+ switch (cmd) {
+ case AVDTP_DISCOVER:
+ if (invalid)
+ hdr->message_type = 0x01;
+ else
+ hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+ hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+ hdr->signal_id = AVDTP_DISCOVER;
+ len = write(sk, buf, 2);
+ break;
+
+ case AVDTP_GET_CAPABILITIES:
+ hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+ hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+ hdr->signal_id = AVDTP_GET_CAPABILITIES;
+ buf[2] = 1 << 2; /* SEID 1 */
+ len = write(sk, buf, invalid ? 2 : 3);
+ break;
+
+ case AVDTP_SET_CONFIGURATION:
+ if (preconf)
+ do_avdtp_send(sk, src, dst, cmd, 0, 0);
+ hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+ hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+ hdr->signal_id = AVDTP_SET_CONFIGURATION;
+ buf[2] = 1 << 2; /* ACP SEID */
+ buf[3] = 1 << 2; /* INT SEID */
+ memcpy(&buf[4], media_transport, sizeof(media_transport));
+ if (invalid)
+ buf[5] = 0x01; /* LOSC != 0 */
+ len = write(sk, buf, 4 + sizeof(media_transport));
+ break;
+
+ case AVDTP_GET_CONFIGURATION:
+ if (preconf)
+ do_avdtp_send(sk, src, dst, AVDTP_SET_CONFIGURATION, 0, 0);
+ hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+ hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+ hdr->signal_id = AVDTP_GET_CONFIGURATION;
+ if (invalid)
+ buf[2] = 13 << 2; /* Invalid ACP SEID */
+ else
+ buf[2] = 1 << 2; /* Valid ACP SEID */
+ len = write(sk, buf, 3);
+ break;
+
+ case AVDTP_OPEN:
+ if (preconf)
+ do_avdtp_send(sk, src, dst, AVDTP_SET_CONFIGURATION, 0, 0);
+ hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+ hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+ hdr->signal_id = AVDTP_OPEN;
+ buf[2] = 1 << 2; /* ACP SEID */
+ len = write(sk, buf, 3);
+ break;
+
+ case AVDTP_START:
+ if (preconf)
+ do_avdtp_send(sk, src, dst, AVDTP_SET_CONFIGURATION, 0, 0);
+ if (!invalid)
+ do_avdtp_send(sk, src, dst, AVDTP_OPEN, 0, 0);
+ hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+ hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+ hdr->signal_id = AVDTP_START;
+ buf[2] = 1 << 2; /* ACP SEID */
+ len = write(sk, buf, 3);
+ break;
+
+ case AVDTP_CLOSE:
+ if (preconf) {
+ do_avdtp_send(sk, src, dst, AVDTP_SET_CONFIGURATION, 0, 0);
+ do_avdtp_send(sk, src, dst, AVDTP_OPEN, 0, 0);
+ }
+ hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+ hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+ hdr->signal_id = AVDTP_CLOSE;
+ if (invalid)
+ buf[2] = 13 << 2; /* Invalid ACP SEID */
+ else
+ buf[2] = 1 << 2; /* Valid ACP SEID */
+ len = write(sk, buf, 3);
+ break;
+
+ case AVDTP_SUSPEND:
+ if (invalid)
+ do_avdtp_send(sk, src, dst, AVDTP_OPEN, 0, preconf);
+ else
+ do_avdtp_send(sk, src, dst, AVDTP_START, 0, preconf);
+ hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+ hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+ hdr->signal_id = AVDTP_SUSPEND;
+ buf[2] = 1 << 2; /* ACP SEID */
+ len = write(sk, buf, 3);
+ break;
+
+ case AVDTP_ABORT:
+ do_avdtp_send(sk, src, dst, AVDTP_OPEN, 0, 1);
+ hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+ hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+ hdr->signal_id = AVDTP_ABORT;
+ buf[2] = 1 << 2; /* ACP SEID */
+ len = write(sk, buf, 3);
+ break;
+
+ default:
+ hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+ hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+ hdr->signal_id = cmd;
+ len = write(sk, buf, 2);
+ break;
+ }
+
+ do {
+ len = read(sk, buf, sizeof(buf));
+
+ dump_buffer(buf, len);
+ dump_avdtp_header(hdr);
+ } while (len < 2 || (hdr->message_type != AVDTP_MSG_TYPE_ACCEPT &&
+ hdr->message_type != AVDTP_MSG_TYPE_REJECT &&
+ hdr->message_type != AVDTP_MSG_TYPE_GEN_REJECT));
+
+ if (cmd == AVDTP_OPEN && len >= 2 &&
+ hdr->message_type == AVDTP_MSG_TYPE_ACCEPT)
+ media_sock = do_connect(src, dst, 0, 0);
+}
+
+static void do_avctp_send(int sk, int invalid)
+{
+ unsigned char buf[672];
+ struct avctp_header *hdr = (void *) buf;
+ unsigned char play_pressed[] = { 0x00, 0x48, 0x7c, 0x44, 0x00 };
+ ssize_t len;
+
+ memset(buf, 0, sizeof(buf));
+
+ hdr->packet_type = AVCTP_PACKET_SINGLE;
+ hdr->cr = AVCTP_COMMAND;
+ if (invalid)
+ hdr->pid = 0xffff;
+ else
+ hdr->pid = htons(AV_REMOTE_SVCLASS_ID);
+
+ memcpy(&buf[AVCTP_HEADER_LENGTH], play_pressed, sizeof(play_pressed));
+
+ len = write(sk, buf, AVCTP_HEADER_LENGTH + sizeof(play_pressed));
+
+ len = read(sk, buf, sizeof(buf));
+
+ dump_buffer(buf, len);
+ if (len >= AVCTP_HEADER_LENGTH)
+ dump_avctp_header(hdr);
+}
+
+static void usage()
+{
+ printf("avtest - Audio/Video testing ver %s\n", VERSION);
+ printf("Usage:\n"
+ "\tavtest [options] [remote address]\n");
+ printf("Options:\n"
+ "\t--device <hcidev> HCI device\n"
+ "\t--reject <command> Reject command\n"
+ "\t--send <command> Send command\n"
+ "\t--preconf Configure stream before actual command\n"
+ "\t--wait <N> Wait N seconds before exiting\n"
+ "\t--fragment Use minimum MTU and fragmented messages\n"
+ "\t--invalid <command> Send invalid command\n");
+}
+
+static struct option main_options[] = {
+ { "help", 0, 0, 'h' },
+ { "device", 1, 0, 'i' },
+ { "reject", 1, 0, 'r' },
+ { "send", 1, 0, 's' },
+ { "invalid", 1, 0, 'f' },
+ { "preconf", 0, 0, 'c' },
+ { "fragment", 0, 0, 'F' },
+ { "avctp", 0, 0, 'C' },
+ { "wait", 1, 0, 'w' },
+ { 0, 0, 0, 0 }
+};
+
+static unsigned char parse_cmd(const char *arg)
+{
+ if (!strncmp(arg, "discov", 6))
+ return AVDTP_DISCOVER;
+ else if (!strncmp(arg, "capa", 4))
+ return AVDTP_GET_CAPABILITIES;
+ else if (!strncmp(arg, "getcapa", 7))
+ return AVDTP_GET_CAPABILITIES;
+ else if (!strncmp(arg, "setconf", 7))
+ return AVDTP_SET_CONFIGURATION;
+ else if (!strncmp(arg, "getconf", 7))
+ return AVDTP_GET_CONFIGURATION;
+ else if (!strncmp(arg, "open", 4))
+ return AVDTP_OPEN;
+ else if (!strncmp(arg, "start", 5))
+ return AVDTP_START;
+ else if (!strncmp(arg, "close", 5))
+ return AVDTP_CLOSE;
+ else if (!strncmp(arg, "suspend", 7))
+ return AVDTP_SUSPEND;
+ else if (!strncmp(arg, "abort", 7))
+ return AVDTP_ABORT;
+ else
+ return atoi(arg);
+}
+
+enum {
+ MODE_NONE, MODE_REJECT, MODE_SEND,
+};
+
+int main(int argc, char *argv[])
+{
+ unsigned char cmd = 0x00;
+ bdaddr_t src, dst;
+ int opt, mode = MODE_NONE, sk, invalid = 0, preconf = 0, fragment = 0;
+ int avctp = 0, wait_before_exit = 0;
+
+ bacpy(&src, BDADDR_ANY);
+ bacpy(&dst, BDADDR_ANY);
+
+ while ((opt = getopt_long(argc, argv, "+i:r:s:f:hcFCw:",
+ main_options, NULL)) != EOF) {
+ switch (opt) {
+ case 'i':
+ if (!strncmp(optarg, "hci", 3))
+ hci_devba(atoi(optarg + 3), &src);
+ else
+ str2ba(optarg, &src);
+ break;
+
+ case 'r':
+ mode = MODE_REJECT;
+ cmd = parse_cmd(optarg);
+ break;
+
+ case 'f':
+ invalid = 1;
+ /* Intentionally missing break */
+
+ case 's':
+ mode = MODE_SEND;
+ cmd = parse_cmd(optarg);
+ break;
+
+ case 'c':
+ preconf = 1;
+ break;
+
+ case 'F':
+ fragment = 1;
+ break;
+
+ case 'C':
+ avctp = 1;
+ break;
+
+ case 'w':
+ wait_before_exit = atoi(optarg);
+ break;
+
+ case 'h':
+ default:
+ usage();
+ exit(0);
+ }
+ }
+
+ if (argv[optind])
+ str2ba(argv[optind], &dst);
+
+ if (avctp) {
+ avctp = mode;
+ mode = MODE_SEND;
+ }
+
+ switch (mode) {
+ case MODE_REJECT:
+ do_listen(&src, cmd, fragment);
+ break;
+ case MODE_SEND:
+ sk = do_connect(&src, &dst, avctp, fragment);
+ if (sk < 0)
+ exit(1);
+ if (avctp) {
+ if (avctp == MODE_SEND)
+ do_avctp_send(sk, invalid);
+ else
+ process_avctp(sk, cmd);
+ } else
+ do_avdtp_send(sk, &src, &dst, cmd, invalid, preconf);
+ if (wait_before_exit) {
+ printf("Waiting %d seconds before exiting\n", wait_before_exit);
+ sleep(wait_before_exit);
+ }
+ if (media_sock >= 0)
+ close(media_sock);
+ close(sk);
+ break;
+ default:
+ fprintf(stderr, "No operating mode specified!\n");
+ exit(1);
+ }
+
+ return 0;
+}