summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarcel Holtmann <marcel@holtmann.org>2004-03-31 21:32:22 +0000
committerMarcel Holtmann <marcel@holtmann.org>2004-03-31 21:32:22 +0000
commitdf713b4db4fb851b197d982d9e91cd85df50839d (patch)
treef23d3600d371e7a28a68975614b6ee651af8c095
parent32c5dbc2343b258dd9f6f59ab69cdf0b958b5e2c (diff)
Add the ciptool utility
-rw-r--r--tools/Makefile.am4
-rw-r--r--tools/ciptool.168
-rw-r--r--tools/ciptool.c479
3 files changed, 549 insertions, 2 deletions
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 4ddeb5c9..44b30990 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -6,11 +6,11 @@ mandir = $(prefix)/usr/share/man
sbin_PROGRAMS = hciattach hciconfig
-bin_PROGRAMS = hcitool l2ping sdptool
+bin_PROGRAMS = hcitool l2ping sdptool ciptool
hciconfig_SOURCES = hciconfig.c csr.h csr.c
-man_MANS = hciattach.8 hciconfig.8 hcitool.1 l2ping.1 sdptool.1
+man_MANS = hciattach.8 hciconfig.8 hcitool.1 l2ping.1 sdptool.1 ciptool.1
noinst_PROGRAMS = ppporc
diff --git a/tools/ciptool.1 b/tools/ciptool.1
new file mode 100644
index 00000000..982f414b
--- /dev/null
+++ b/tools/ciptool.1
@@ -0,0 +1,68 @@
+.\"
+.\" 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.TH CIPTOOL 1 "JUNE 6, 2003" "" ""
+
+.SH NAME
+ciptool \- Bluetooth Common ISDN Access Profile (CIP)
+.SH SYNOPSIS
+.BR "ciptool
+[
+.I options
+] <
+.I command
+>
+.SH DESCRIPTION
+.B ciptool
+is used to set up, maintain, and inspect the CIP configuration
+of the Bluetooth subsystem in the Linux kernel.
+.SH OPTIONS
+.TP
+.BI -h
+Gives a list of possible commands.
+.TP
+.BI -i " <hciX> | <bdaddr>"
+The command is applied to device
+.I
+hciX
+, which must be the name or the address of an installed Bluetooth
+device. If not specified, the command will be use the first
+available Bluetooth device.
+.SH COMMANDS
+.TP
+.BI show
+Display information about the connected devices.
+.TP
+.BI search
+Search for Bluetooth devices and connect to first one that
+offers CIP support.
+.TP
+.BI connect " <bdaddr> [psm]"
+Connect the local device to the remote Bluetooth device on the
+specified PSM number. If no PSM is specified, it will use the
+SDP to retrieve it from the remote device.
+.TP
+.BI release " [bdaddr]"
+Release a connection to the specific device. If no address is
+given and only one device is connected this will be released.
+.TP
+.BI loopback " <bdaddr> [psm]"
+Create a connection to the remote device for Bluetooth testing.
+This command will not provide a CAPI controller, because it is
+only for testing the CAPI Message Transport Protocol.
+.SH AUTHOR
+Written by Marcel Holtmann <marcel@holtmann.org>.
+.br
diff --git a/tools/ciptool.c b/tools/ciptool.c
new file mode 100644
index 00000000..31984985
--- /dev/null
+++ b/tools/ciptool.c
@@ -0,0 +1,479 @@
+/*
+ *
+ * Bluetooth Common ISDN Access Profile (CIP)
+ *
+ * Copyright (C) 2002-2003 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <malloc.h>
+#include <getopt.h>
+#include <signal.h>
+#include <sys/poll.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+#include <bluetooth/cmtp.h>
+
+
+static volatile sig_atomic_t __io_canceled = 0;
+
+static void sig_hup(int sig)
+{
+ return;
+}
+
+static void sig_term(int sig)
+{
+ __io_canceled = 1;
+}
+
+static char *cmtp_state[] = {
+ "unknown",
+ "connected",
+ "open",
+ "bound",
+ "listening",
+ "connecting",
+ "connecting",
+ "config",
+ "disconnecting",
+ "closed"
+};
+
+static char *cmtp_flagstostr(uint32_t flags)
+{
+ static char str[100] = "";
+
+ strcat(str, "[");
+
+ if (flags & (1 << CMTP_LOOPBACK))
+ strcat(str, "loopback");
+
+ strcat(str, "]");
+
+ return str;
+}
+
+static int get_psm(bdaddr_t *src, bdaddr_t *dst, unsigned short *psm)
+{
+ sdp_session_t *s;
+ sdp_list_t *srch, *attrs, *rsp;
+ uuid_t svclass;
+ uint16_t attr;
+ int err;
+
+ if (!(s = sdp_connect(src, dst, 0)))
+ return -1;
+
+ sdp_uuid16_create(&svclass, CIP_SVCLASS_ID);
+ srch = sdp_list_append(NULL, &svclass);
+
+ attr = SDP_ATTR_PROTO_DESC_LIST;
+ attrs = sdp_list_append(NULL, &attr);
+
+ err = sdp_service_search_attr_req(s, srch, SDP_ATTR_REQ_INDIVIDUAL, attrs, &rsp);
+
+ sdp_close(s);
+
+ if (err)
+ return 0;
+
+ for (; rsp; rsp = rsp->next) {
+ sdp_record_t *rec = (sdp_record_t *) rsp->data;
+ sdp_list_t *protos;
+
+ if (!sdp_get_access_protos(rec, &protos)) {
+ unsigned short p = sdp_get_proto_port(protos, L2CAP_UUID);
+ if (p > 0) {
+ *psm = p;
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int do_connect(int ctl, int dev_id, bdaddr_t *src, bdaddr_t *dst, unsigned short psm, uint32_t flags)
+{
+ struct cmtp_connadd_req req;
+ struct hci_dev_info di;
+ struct sockaddr_l2 addr;
+ struct l2cap_options opts;
+ int sk, size;
+
+ hci_devinfo(dev_id, &di);
+ if (!(di.link_policy & HCI_LP_RSWITCH)) {
+ printf("Local device is not accepting role switch\n");
+ }
+
+ if ((sk = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) < 0) {
+ perror("Can't create L2CAP socket");
+ exit(1);
+ }
+
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, src);
+ addr.l2_psm = 0;
+
+ if (bind(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ perror("Can't bind L2CAP socket");
+ close(sk);
+ exit(1);
+ }
+
+ size = sizeof(opts);
+ if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &size) < 0) {
+ perror("Can't get L2CAP options");
+ close(sk);
+ exit(1);
+ }
+
+ opts.imtu = CMTP_DEFAULT_MTU;
+ opts.omtu = CMTP_DEFAULT_MTU;
+ opts.flush_to = 0xffff;
+
+ if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)) < 0) {
+ perror("Can't set L2CAP options");
+ close(sk);
+ exit(1);
+ }
+
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, dst);
+ addr.l2_psm = psm;
+
+ if (connect(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ perror("Can't connect L2CAP socket");
+ close(sk);
+ exit(1);
+ }
+
+ req.sock = sk;
+ req.flags = flags;
+
+ if (ioctl(ctl, CMTPCONNADD, &req) < 0) {
+ perror("Can't create connection");
+ exit(1);
+ }
+
+ return sk;
+}
+
+static void cmd_show(int ctl, bdaddr_t *bdaddr, int argc, char **argv)
+{
+ struct cmtp_connlist_req req;
+ struct cmtp_conninfo ci[16];
+ char addr[18];
+ int i;
+
+ req.cnum = 16;
+ req.ci = ci;
+
+ if (ioctl(ctl, CMTPGETCONNLIST, &req) < 0) {
+ perror("Can't get connection list");
+ exit(1);
+ }
+
+ for (i = 0; i < req.cnum; i++) {
+ ba2str(&ci[i].bdaddr, addr);
+ printf("%d %s %s %s\n", ci[i].num, addr,
+ cmtp_state[ci[i].state],
+ ci[i].flags ? cmtp_flagstostr(ci[i].flags) : "");
+ }
+}
+
+static void cmd_search(int ctl, bdaddr_t *bdaddr, int argc, char **argv)
+{
+ inquiry_info *info = NULL;
+ bdaddr_t src, dst;
+ unsigned short psm;
+ int i, dev_id, num_rsp, length, flags;
+ char addr[18];
+ uint8_t class[3];
+
+ ba2str(bdaddr, addr);
+ dev_id = hci_devid(addr);
+ if (dev_id < 0) {
+ dev_id = hci_get_route(NULL);
+ hci_devba(dev_id, &src);
+ } else
+ bacpy(&src, bdaddr);
+
+ length = 8; /* ~10 seconds */
+ num_rsp = 0;
+ flags = 0;
+
+ printf("Searching ...\n");
+
+ num_rsp = hci_inquiry(dev_id, length, num_rsp, NULL, &info, flags);
+
+ for (i = 0; i < num_rsp; i++) {
+ memcpy(class, (info+i)->dev_class, 3);
+ if ((class[1] == 2) && ((class[0] / 4) == 5)) {
+ bacpy(&dst, &(info+i)->bdaddr);
+ ba2str(&dst, addr);
+
+ printf("\tChecking service for %s\n", addr);
+ if (!get_psm(&src, &dst, &psm))
+ continue;
+
+ free(info);
+
+ printf("\tConnecting to device %s\n", addr);
+ do_connect(ctl, dev_id, &src, &dst, psm, 0);
+ return;
+ }
+ }
+
+ free(info);
+ fprintf(stderr, "\tNo devices in range or visible\n");
+ exit(1);
+}
+
+static void cmd_create(int ctl, bdaddr_t *bdaddr, int argc, char **argv)
+{
+ bdaddr_t src, dst;
+ unsigned short psm;
+ int dev_id;
+ char addr[18];
+
+ if (argc < 2)
+ return;
+
+ str2ba(argv[1], &dst);
+
+ ba2str(bdaddr, addr);
+ dev_id = hci_devid(addr);
+ if (dev_id < 0) {
+ dev_id = hci_get_route(&dst);
+ hci_devba(dev_id, &src);
+ } else
+ bacpy(&src, bdaddr);
+
+ if (argc < 3) {
+ if (!get_psm(&src, &dst, &psm))
+ psm = htobs(4099);
+ } else
+ psm = htobs(atoi(argv[2]));
+
+ do_connect(ctl, dev_id, &src, &dst, psm, 0);
+}
+
+static void cmd_release(int ctl, bdaddr_t *bdaddr, int argc, char **argv)
+{
+ struct cmtp_conndel_req req;
+ struct cmtp_connlist_req cl;
+ struct cmtp_conninfo ci[16];
+
+ if (argc < 2) {
+ cl.cnum = 16;
+ cl.ci = ci;
+
+ if (ioctl(ctl, CMTPGETCONNLIST, &cl) < 0) {
+ perror("Can't get connection list");
+ exit(1);
+ }
+
+ if (cl.cnum == 0)
+ return;
+
+ if (cl.cnum != 1) {
+ fprintf(stderr, "You have to specifiy the device address.\n");
+ exit(1);
+ }
+
+ bacpy(&req.bdaddr, &ci[0].bdaddr);
+ } else
+ str2ba(argv[1], &req.bdaddr);
+
+ if (ioctl(ctl, CMTPCONNDEL, &req) < 0) {
+ perror("Can't release connection");
+ exit(1);
+ }
+}
+
+static void cmd_loopback(int ctl, bdaddr_t *bdaddr, int argc, char **argv)
+{
+ struct cmtp_conndel_req req;
+ struct sigaction sa;
+ struct pollfd p;
+ bdaddr_t src, dst;
+ unsigned short psm;
+ int dev_id, sk;
+ char addr[18];
+
+ if (argc < 2)
+ return;
+
+ str2ba(argv[1], &dst);
+
+ ba2str(bdaddr, addr);
+ dev_id = hci_devid(addr);
+ if (dev_id < 0) {
+ dev_id = hci_get_route(&dst);
+ hci_devba(dev_id, &src);
+ } else
+ bacpy(&src, bdaddr);
+
+ ba2str(&dst, addr);
+ printf("Connecting to %s in loopback mode\n", addr);
+
+ if (argc < 3) {
+ if (!get_psm(&src, &dst, &psm))
+ psm = htobs(4099);
+ } else
+ psm = htobs(atoi(argv[2]));
+
+ sk = do_connect(ctl, dev_id, &src, &dst, psm, (1 << CMTP_LOOPBACK));
+
+ printf("Press CTRL-C for hangup\n");
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_flags = SA_NOCLDSTOP;
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGCHLD, &sa, NULL);
+ sigaction(SIGPIPE, &sa, NULL);
+
+ sa.sa_handler = sig_term;
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+ sa.sa_handler = sig_hup;
+ sigaction(SIGHUP, &sa, NULL);
+
+ p.fd = sk;
+ p.events = POLLERR | POLLHUP;
+
+ while (!__io_canceled) {
+ p.revents = 0;
+ if (poll(&p, 1, 100))
+ break;
+ }
+
+ bacpy(&req.bdaddr, &dst);
+ ioctl(ctl, CMTPCONNDEL, &req);
+}
+
+
+static struct {
+ char *cmd;
+ char *alt;
+ void (*func)(int ctl, bdaddr_t *bdaddr, int argc, char **argv);
+ char *opt;
+ char *doc;
+} command[] = {
+ { "show", "list", cmd_show, 0, "Show remote connections" },
+ { "search", "scan", cmd_search, 0, "Search for a remote device" },
+ { "connect", "create", cmd_create, "<bdaddr>", "Connect a remote device" },
+ { "release", "disconnect", cmd_release, "[bdaddr]", "Disconnect the remote device" },
+ { "loopback", "test", cmd_loopback, "<bdaddr>", "Loopback test of a device" },
+ { NULL, NULL, NULL, 0, 0 }
+};
+
+static void usage(void)
+{
+ int i;
+
+ printf("ciptool - Bluetooth Common ISDN Access Profile (CIP)\n\n");
+
+ printf("Usage:\n"
+ "\tciptool [options] [command]\n"
+ "\n");
+
+ printf("Options:\n"
+ "\t-i [hciX|bdaddr] Local HCI device or BD Address\n"
+ "\t-h, --help Display help\n"
+ "\n");
+
+ printf("Commands:\n");
+ for (i = 0; command[i].cmd; i++)
+ printf("\t%-8s %-10s\t%s\n", command[i].cmd,
+ command[i].opt ? command[i].opt : " ",
+ command[i].doc);
+ printf("\n");
+}
+
+static struct option main_options[] = {
+ { "help", 0, 0, 'h' },
+ { "device", 1, 0, 'i' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ bdaddr_t bdaddr;
+ int i, opt, ctl;
+
+ bacpy(&bdaddr, BDADDR_ANY);
+
+ while ((opt = getopt_long(argc, argv, "+i:h", main_options, NULL)) != -1) {
+ switch(opt) {
+ case 'i':
+ if (!strncmp(optarg, "hci", 3))
+ hci_devba(atoi(optarg + 3), &bdaddr);
+ else
+ str2ba(optarg, &bdaddr);
+ break;
+ case 'h':
+ usage();
+ exit(0);
+ default:
+ exit(0);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ if (argc < 1) {
+ usage();
+ return 0;
+ }
+
+ if ((ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_CMTP)) < 0 ) {
+ perror("Can't open CMTP control socket");
+ exit(1);
+ }
+
+ for (i = 0; command[i].cmd; i++) {
+ if (strncmp(command[i].cmd, argv[0], 4) && strncmp(command[i].alt, argv[0], 4))
+ continue;
+ command[i].func(ctl, &bdaddr, argc, argv);
+ close(ctl);
+ exit(0);
+ }
+
+ usage();
+
+ close(ctl);
+
+ return 0;
+}