diff options
| author | Julian Elischer <julian@FreeBSD.org> | 2002-11-20 23:01:59 +0000 |
|---|---|---|
| committer | Julian Elischer <julian@FreeBSD.org> | 2002-11-20 23:01:59 +0000 |
| commit | 878ed22696d5402cabd6f90932cc970ab912b7b0 (patch) | |
| tree | c38e3c48c390af16003a1f451848e93558978422 /usr.sbin | |
| parent | 2699228f1effe6a9d4c53e0b065a8f37097ebf8d (diff) | |
Notes
Diffstat (limited to 'usr.sbin')
27 files changed, 6743 insertions, 0 deletions
diff --git a/usr.sbin/bluetooth/Makefile b/usr.sbin/bluetooth/Makefile new file mode 100644 index 0000000000000..821a0035cb449 --- /dev/null +++ b/usr.sbin/bluetooth/Makefile @@ -0,0 +1,12 @@ +# $Id$ +# $FreeBSD$ + +SUBDIR= \ + bt3cfw \ + hccontrol \ + hcseriald \ + l2control \ + l2ping + +.include <bsd.subdir.mk> + diff --git a/usr.sbin/bluetooth/bt3cfw/Makefile b/usr.sbin/bluetooth/bt3cfw/Makefile new file mode 100644 index 0000000000000..05ae1ab721b40 --- /dev/null +++ b/usr.sbin/bluetooth/bt3cfw/Makefile @@ -0,0 +1,15 @@ +# $Id: Makefile,v 1.1.1.1 2002/11/12 00:39:18 max Exp $ +# $FreeBSD$ + +DESTDIR= /usr/sbin/ +MANDIR= ../share/man/man +PROG= bt3cfw +MAN8= bt3cfw.8 +WARNS?= 2 +CFLAGS+= -g -I../../../sys/netgraph/bluetooth/include +SRCS= bt3cfw.c + +DPADD= ${LIBNETGRAPH} +LDADD= -lnetgraph + +.include <bsd.prog.mk> diff --git a/usr.sbin/bluetooth/bt3cfw/bt3cfw.8 b/usr.sbin/bluetooth/bt3cfw/bt3cfw.8 new file mode 100644 index 0000000000000..e85cb16dd2f75 --- /dev/null +++ b/usr.sbin/bluetooth/bt3cfw/bt3cfw.8 @@ -0,0 +1,69 @@ +.\" bt3cfw.1 +.\" +.\" Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com> +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $Id: bt3cfw.8,v 1.1.1.1 2002/11/12 00:39:18 max Exp $ +.\" $FreeBSD$ +.Dd November 11, 2002 +.Dt BT3CFW 8 +.Os +.Sh NAME +.Nm BT3CFW +.Nd Firmware download utility for 3Com Bluetooth PC card driver +.Sh SYNOPSIS +.Nm +.Op Fl n Ar Netgraph node name +.Op Fl f Ar Firmware file name +.Sh DESCRIPTION +The +.Nm +utility connects to the specified Netgraph driver node of type +.Dv BTCCC +and downloads specified firmware file. +.Pp +Due to copyright issues I will no longer provide firmware with the card +driver. The firmware can be obtained from the Windows driver package that +can be downloaded from the 3COM web site at no charge. The firmware name +is BT3CPCC.BIN. I'm using original firmware that came with the card on CD-ROM. +.Bd -literal -offset indent +MD5 (BT3CPCC.BIN) = 36170fda56ea9fdbf1702c966f8a97f1 +.Ed +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl n Ar Netgraph node name +Connect to the specified Netgraph driver node of type +.Dv BTCCC . +.It Fl f Ar Firmware file name +Specify firmware file name for download. +.El +.Sh BUGS +Please report if found. +.Sh DIAGNOSTICS +.Ex -std +.Sh SEE ALSO +.Xr ng_bt3c 4 +.Sh AUTHORS +.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com diff --git a/usr.sbin/bluetooth/bt3cfw/bt3cfw.c b/usr.sbin/bluetooth/bt3cfw/bt3cfw.c new file mode 100644 index 0000000000000..09252acf87537 --- /dev/null +++ b/usr.sbin/bluetooth/bt3cfw/bt3cfw.c @@ -0,0 +1,227 @@ +/* + * bt3cfw.c + * + * Copyright (c) 2001 Maksim Yevmenkin <m_evmenkin@yahoo.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: bt3cfw.c,v 1.1.1.1 2002/11/12 00:39:18 max Exp $ + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <errno.h> +#include <netgraph.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> +#include "ng_bt3c.h" + +#define BT3CFW_IDENT "bt3cfw" +#define BT3CFW_MAX_FIRMWARE_SIZE 0xffff + +/* Convert hex ASCII to int4 */ +static int +hexa2int4(const char *a) +{ + if ('0' <= *a && *a <= '9') + return (*a - '0'); + + if ('A' <= *a && *a <= 'F') + return (*a - 'A' + 0xa); + + if ('a' <= *a && *a <= 'f') + return (*a - 'a' + 0xa); + + syslog(LOG_ERR, "Invalid hex character: '%c' (%#x)", *a, *a); + exit(255); +} + +/* Convert hex ASCII to int8 */ +static int +hexa2int8(const char *a) +{ + return ((hexa2int4(a) << 4) | hexa2int4(a + 1)); +} + +/* Convert hex ASCII to int16 */ +static int +hexa2int16(const char *a) +{ + return ((hexa2int8(a) << 8) | hexa2int8(a + 2)); +} + +/* Convert hex ASCII to int32 */ +static int +hexa2int32(const char *a) +{ + return ((hexa2int16(a) << 16) | hexa2int16(a + 4)); +} + +/* Display usage() and exit */ +static void +usage(void) +{ + syslog(LOG_ERR, "Usage: %s -f FirmwareFile -n NodeName", BT3CFW_IDENT); + exit(255); +} + +/* Main */ +int +main(int argc, char *argv[]) +{ + FILE *firmware_file = NULL; + char buffer[80], path[NG_PATHLEN + 1], + *firmware_filename = NULL; + u_int8_t *firmware = NULL; + int firmware_size, opt, cs, ds; + + memset(path, 0, sizeof(path)); + openlog(BT3CFW_IDENT, LOG_NDELAY|LOG_PID|LOG_PERROR, LOG_USER); + + while ((opt = getopt(argc, argv, "f:hn:")) != -1) { + switch (opt) { + case 'f': + firmware_filename = optarg; + break; + + case 'n': + snprintf(path, sizeof(path), "%s:", optarg); + break; + + case 'h': + default: + usage(); + /* NOT REACHED */ + } + } + + if (firmware_filename == NULL || path[0] == 0) + usage(); + /* NOT REACHED */ + + firmware = (u_int8_t *) calloc(BT3CFW_MAX_FIRMWARE_SIZE, + sizeof(u_int8_t)); + if (firmware == NULL) { + syslog(LOG_ERR, "Could not allocate firmware buffer"); + exit(255); + } + + if ((firmware_file = fopen(firmware_filename, "r")) == NULL) { + syslog(LOG_ERR, "Could not open BT3C firmware file %s. %s (%d)", + firmware_filename, strerror(errno), errno); + exit(255); + } + + firmware_size = 0; + + while (fgets(buffer, sizeof(buffer), firmware_file)) { + int i, size, address, cs, fcs; + + size = hexa2int8(buffer + 2); + address = hexa2int32(buffer + 4); + fcs = hexa2int8(buffer + 2 + size * 2); + + if (buffer[1] == '3') { + ng_bt3c_firmware_block_ep *block = NULL; + u_int16_t *data = NULL; + + block = (ng_bt3c_firmware_block_ep *) + (firmware + firmware_size); + + firmware_size += sizeof(*block); + if (firmware_size >= BT3CFW_MAX_FIRMWARE_SIZE) { + syslog(LOG_ERR, "Could not add new firmware " \ + "block. Firmware file %s is " \ + "too big, firmware_size=%d", + firmware_filename, + firmware_size); + exit(255); + } + + block->block_address = address; + block->block_size = (size - 4) / 2; + block->block_alignment = (block->block_size * 2) % 3; + if (block->block_alignment != 0) + block->block_alignment = 3 - block->block_alignment; + + firmware_size += (block->block_size * 2); + firmware_size += block->block_alignment; + if (firmware_size >= BT3CFW_MAX_FIRMWARE_SIZE) { + syslog(LOG_ERR, "Could not add new firmware " \ + "data. Firmware file %s is " \ + "too big, firmware_size=%d", + firmware_filename, + firmware_size); + exit(255); + } + + /* First part of the cheksum: size and address */ + cs = 0; + for (i = 0; i < 5; i++) + cs += hexa2int8(buffer + 2 + i * 2); + + /* Data + second part of the cheksum: data */ + data = (u_int16_t *)(block + 1); + for (i = 0; i < block->block_size; i++) { + data[i] = hexa2int16(buffer + (i * 4) + 12); + cs += (((data[i] & 0xff00) >> 8) & 0xff); + cs += (data[i] & 0x00ff); + } + } else + for (cs = 0, i = 0; i < size; i++) + cs += hexa2int8(buffer + 2 + i * 2); + + if (((cs + fcs) & 0xff) != 0xff) { + syslog(LOG_ERR, "Invalid firmware file %s. Checksum " \ + "error, cs=%#x, fcs=%#x, checksum=%#x", + firmware_filename, (cs & 0xff), fcs, + ((cs + fcs) & 0xff)); + exit(255); + } + } + + /* Send firmware to the card */ + if (NgMkSockNode(NULL, &cs, &ds) < 0) { + syslog(LOG_ERR, "Could not create Netgraph socket. %s (%d)", + strerror(errno), errno); + exit(255); + } + + if (NgSendMsg(cs, path, NGM_BT3C_COOKIE, + NGM_BT3C_NODE_DOWNLOAD_FIRMWARE, + (void const *) firmware, firmware_size) < 0) { + syslog(LOG_ERR, "Could not send Netgraph message. %s (%d)", + strerror(errno), errno); + exit(255); + } + + free(firmware); + firmware = NULL; + + return (0); +} + diff --git a/usr.sbin/bluetooth/hccontrol/Makefile b/usr.sbin/bluetooth/hccontrol/Makefile new file mode 100644 index 0000000000000..c816c3c6d1e07 --- /dev/null +++ b/usr.sbin/bluetooth/hccontrol/Makefile @@ -0,0 +1,14 @@ +# $Id: Makefile,v 1.6 2002/09/06 18:52:41 max Exp $ +# $FreeBSD$ + +DESTDIR= /usr/sbin/ +MANDIR= ../share/man/man +PROG= hccontrol +MAN8= hccontrol.8 +WARNS?= 2 +CFLAGS+= -g -I../../../sys/netgraph/bluetooth/include +SRCS= send_recv.c link_policy.c link_control.c \ + host_controller_baseband.c info.c status.c node.c hccontrol.c \ + util.c + +.include <bsd.prog.mk> diff --git a/usr.sbin/bluetooth/hccontrol/hccontrol.8 b/usr.sbin/bluetooth/hccontrol/hccontrol.8 new file mode 100644 index 0000000000000..555cf32ac6be3 --- /dev/null +++ b/usr.sbin/bluetooth/hccontrol/hccontrol.8 @@ -0,0 +1,163 @@ +.\" hccontrol.8 +.\" +.\" Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com> +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $Id: hccontrol.8,v 1.8 2002/11/12 22:33:17 max Exp $ +.\" $FreeBSD$ +.Dd June 14, 2002 +.Dt HCCONTROL 8 +.Os +.Sh NAME +.Nm hccontrol +.Nd HCI configuration utility +.Sh SYNOPSIS +.Nm +.Op Fl n Ar HCI node name +.Op Ar command +.Op Ar parameters ... +.Sh DESCRIPTION +The +.Nm +utility connects to the specified Netgraph node of type +.Em HCI +and attempts to send specified command to the HCI Netgraph node or to the +associated Bluetooth device. +.Nm +will print results to the standard output and error messages to +the standard error. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl n Ar HCI node name +Connect to the specified HCI Netgraph node. +.It command +One of the supported commands (see below). Special command +.Dq help +can be used to obtain the list of all supported commands. To get more +information about specific command use +.Dq help command . +.It parameters +One or more optional space separated command parameters. +.El +.Sh COMMANDS +The currently supported HCI commands in +.Nm +are: +.Pp +.Bd -literal -offset indent -compact +Inquiry +Create_Connection +Disconnect +Add_SCO_Connection +Change_Connection_Packet_Type +Remote_Name_Request +Read_Remote_Supported_Features +Read_Remote_Version_Information +Read_Clock_Offset +Role_Discovery +Switch_Role +Read_Link_Policy_Settings +Write_Link_Policy_Settings +Reset +Read_Pin_Type +Write_Pin_Type +Read_Stored_Link_Key +Write_Stored_Link_Key +Delete_Stored_Link_Key +Change_Local_Name +Read_Local_Name +Read_Connection_Accept_Timeout +Write_Connection_Accept_Timeout +Read_Page_Timeout +Write_Page_Timeout +Read_Scan_Enable +Write_Scan_Enable +Read_Page_Scan_Activity +Write_Page_Scan_Activity +Read_Inquiry_Scan_Activity +Write_Inquiry_Scan_Activity +Read_Authentication_Enable +Write_Authentication_Enable +Read_Encryption_Mode +Write_Encryption_Mode +Read_Class_Of_Device +Write_Class_Of_Device +Read_Voice_Settings +Write_Voice_Settings +Read_Number_Broadcast_Retransmissions +Write_Number_Broadcast_Retransmissions +Read_Hold_Mode_Activity +Write_Hold_Mode_Activity +Read_SCO_Flow_Control_Enable +Write_SCO_Flow_Control_Enable +Read_Link_Supervision_Timeout +Write_Link_Supervision_Timeout +Read_Local_Version_Information +Read_Local_Supported_Features +Read_Buffer_Size +Read_Country_Code +Read_BD_ADDR +Read_Failed_Contact_Counter +Reset_Failed_Contact_Counter +Get_Link_Quality +Read_RSSI +.Ed +.Pp +The currently supported node commands in +.Nm +are: +.Pp +.Bd -literal -offset indent -compact +Read_Node_State +Initialize +Read_Debug_Level +Write_Debug_Level +Read_Command_Timeout +Write_Command_Timeout +Read_Node_Buffer_Size +Read_Node_BD_ADDR +Read_Node_Features +Read_Node_Stat +Reset_Node_Stat +Flush_Neighbor_Cache +Read_Neighbor_Cache +Read_Connection_List +Read_Node_Link_Policy_Settings_Mask +Write_Node_Link_Policy_Settings_Mask +Read_Node_Packet_Mask +Write_Node_Packet_Mask +.Ed +.Pp +.Sh BUGS +Most likely. Please report if found. +.Sh DIAGNOSTICS +.Ex -std +.Sh SEE ALSO +.Xr netgraph 3 , +.Xr netgraph 4 , +.Xr ng_hci 4 , +.Xr hcseriald 8 +.Sh AUTHORS +.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com diff --git a/usr.sbin/bluetooth/hccontrol/hccontrol.c b/usr.sbin/bluetooth/hccontrol/hccontrol.c new file mode 100644 index 0000000000000..12b70eb07d8ec --- /dev/null +++ b/usr.sbin/bluetooth/hccontrol/hccontrol.c @@ -0,0 +1,274 @@ +/* + * hccontrol.c + * + * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: hccontrol.c,v 1.11 2002/09/12 18:19:43 max Exp $ + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <assert.h> +#include <bitstring.h> +#include <err.h> +#include <errno.h> +#include <ng_hci.h> +#include <ng_l2cap.h> +#include <ng_btsocket.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "hccontrol.h" + +/* Prototypes */ +static int do_hci_command (char const *, int, char **); +static struct hci_command * find_hci_command (char const *, struct hci_command *); +static void print_hci_command (struct hci_command *); +static void usage (void); + +/* Globals */ +int verbose = 0; +int timeout; + +/* Main */ +int +main(int argc, char *argv[]) +{ + char *node = NULL; + int n; + + /* Process command line arguments */ + while ((n = getopt(argc, argv, "n:v")) != -1) { + switch (n) { + case 'n': + node = optarg; + break; + + case 'v': + verbose = 1; + break; + + case '?': + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + if (*argv == NULL) + usage(); + + n = do_hci_command(node, argc, argv); + + return (n); +} /* main */ + +/* Create socket and bind it */ +static int +socket_open(char const *node) +{ + struct sockaddr_hci addr; + struct ng_btsocket_hci_raw_filter filter; + int s, mib[4]; + size_t size; + + if (node == NULL) + usage(); + + s = socket(PF_BLUETOOTH, SOCK_RAW, BLUETOOTH_PROTO_HCI); + if (s < 0) + err(1, "Could not create socket"); + + memset(&addr, 0, sizeof(addr)); + addr.hci_len = sizeof(addr); + addr.hci_family = AF_BLUETOOTH; + strncpy(addr.hci_node, node, sizeof(addr.hci_node)); + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) + err(2, "Could not bind socket, node=%s", node); + + if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) + err(3, "Could not connect socket, node=%s", node); + + memset(&filter, 0, sizeof(filter)); + bit_set(filter.event_mask, NG_HCI_EVENT_COMMAND_COMPL - 1); + bit_set(filter.event_mask, NG_HCI_EVENT_COMMAND_STATUS - 1); + bit_set(filter.event_mask, NG_HCI_EVENT_INQUIRY_COMPL - 1); + bit_set(filter.event_mask, NG_HCI_EVENT_INQUIRY_RESULT - 1); + bit_set(filter.event_mask, NG_HCI_EVENT_CON_COMPL - 1); + bit_set(filter.event_mask, NG_HCI_EVENT_DISCON_COMPL - 1); + bit_set(filter.event_mask, NG_HCI_EVENT_REMOTE_NAME_REQ_COMPL - 1); + bit_set(filter.event_mask, NG_HCI_EVENT_READ_REMOTE_FEATURES_COMPL - 1); + bit_set(filter.event_mask, NG_HCI_EVENT_READ_REMOTE_VER_INFO_COMPL - 1); + bit_set(filter.event_mask, NG_HCI_EVENT_RETURN_LINK_KEYS - 1); + bit_set(filter.event_mask, NG_HCI_EVENT_READ_CLOCK_OFFSET_COMPL - 1); + bit_set(filter.event_mask, NG_HCI_EVENT_CON_PKT_TYPE_CHANGED - 1); + bit_set(filter.event_mask, NG_HCI_EVENT_ROLE_CHANGE - 1); + + if (setsockopt(s, SOL_HCI_RAW, SO_HCI_RAW_FILTER, + (void * const) &filter, sizeof(filter)) < 0) + err(4, "Could not setsockopt()"); + + size = (sizeof(mib)/sizeof(mib[0])); + if (sysctlnametomib("net.bluetooth.hci.command_timeout",mib,&size) < 0) + err(5, "Could not sysctlnametomib()"); + + if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), + (void *) &timeout, &size, NULL, 0) < 0) + err(6, "Could not sysctl()"); + + timeout ++; + + return (s); +} /* socket_open */ + +/* Execute commands */ +static int +do_hci_command(char const *node, int argc, char **argv) +{ + char *cmd = argv[0]; + struct hci_command *c = NULL; + int s, e, help; + + help = 0; + if (strcasecmp(cmd, "help") == 0) { + argc --; + argv ++; + + if (argc <= 0) { + fprintf(stdout, "Supported commands:\n"); + print_hci_command(link_control_commands); + print_hci_command(link_policy_commands); + print_hci_command(host_controller_baseband_commands); + print_hci_command(info_commands); + print_hci_command(status_commands); + print_hci_command(node_commands); + fprintf(stdout, "\nFor more information use " \ + "'help command'\n"); + + return (OK); + } + + help = 1; + cmd = argv[0]; + } + + c = find_hci_command(cmd, link_control_commands); + if (c != NULL) + goto execute; + + c = find_hci_command(cmd, link_policy_commands); + if (c != NULL) + goto execute; + + c = find_hci_command(cmd, host_controller_baseband_commands); + if (c != NULL) + goto execute; + + c = find_hci_command(cmd, info_commands); + if (c != NULL) + goto execute; + + c = find_hci_command(cmd, status_commands); + if (c != NULL) + goto execute; + + c = find_hci_command(cmd, node_commands); + if (c == NULL) { + fprintf(stdout, "Unknown command: \"%s\"\n", cmd); + return (ERROR); + } +execute: + if (!help) { + s = socket_open(node); + e = (c->handler)(s, -- argc, ++ argv); + close(s); + } else + e = USAGE; + + switch (e) { + case OK: + case FAILED: + break; + + case ERROR: + fprintf(stdout, "Could not execute command \"%s\". %s\n", + cmd, strerror(errno)); + break; + + case USAGE: + fprintf(stdout, "Usage: %s\n%s\n", c->command, c->description); + break; + + default: assert(0); break; + } + + + return (e); +} /* do_hci_command */ + +/* Try to find command in specified category */ +static struct hci_command * +find_hci_command(char const *command, struct hci_command *category) +{ + struct hci_command *c = NULL; + + for (c = category; c->command != NULL; c++) { + char *c_end = strchr(c->command, ' '); + + if (c_end != NULL) { + int len = c_end - c->command; + + if (strncasecmp(command, c->command, len) == 0) + return (c); + } else if (strcasecmp(command, c->command) == 0) + return (c); + } + + return (NULL); +} /* find_hci_command */ + +/* Try to find command in specified category */ +static void +print_hci_command(struct hci_command *category) +{ + struct hci_command *c = NULL; + + for (c = category; c->command != NULL; c++) + fprintf(stdout, "\t%s\n", c->command); +} /* print_hci_command */ + +/* Usage */ +static void +usage(void) +{ + fprintf(stdout, "Usage: hccontrol -n HCI_node_name cmd [p1] [..]]\n"); + exit(255); +} /* usage */ + diff --git a/usr.sbin/bluetooth/hccontrol/hccontrol.h b/usr.sbin/bluetooth/hccontrol/hccontrol.h new file mode 100644 index 0000000000000..8bcf7647397d6 --- /dev/null +++ b/usr.sbin/bluetooth/hccontrol/hccontrol.h @@ -0,0 +1,75 @@ +/* + * hccontrol.h + * + * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: hccontrol.h,v 1.8 2002/09/12 18:19:43 max Exp $ + * $FreeBSD$ + */ + +#ifndef _HCCONTROL_H_ +#define _HCCONTROL_H_ + +#define OK 0 /* everything was OK */ +#define ERROR 1 /* could not execute command */ +#define FAILED 2 /* error was reported */ +#define USAGE 3 /* invalid parameters */ + +struct hci_command { + char const *command; + char const *description; + int (*handler)(int, int, char **); +}; + +extern int timeout; +extern int verbose; +extern struct hci_command link_control_commands[]; +extern struct hci_command link_policy_commands[]; +extern struct hci_command host_controller_baseband_commands[]; +extern struct hci_command info_commands[]; +extern struct hci_command status_commands[]; +extern struct hci_command node_commands[]; + +int hci_request (int, int, char const *, int, char *, int *); +int hci_simple_request (int, int, char *, int *); +int hci_send (int, char const *, int); +int hci_recv (int, char *, int *); + +char const * const hci_link2str (int); +char const * const hci_pin2str (int); +char const * const hci_scan2str (int); +char const * const hci_encrypt2str (int, int); +char const * const hci_coding2str (int); +char const * const hci_vdata2str (int); +char const * const hci_hmode2str (int, char *, int); +char const * const hci_ver2str (int); +char const * const hci_manufacturer2str(int); +char const * const hci_features2str (u_int8_t *, char *, int); +char const * const hci_cc2str (int); +char const * const hci_con_state2str (int); +char const * const hci_status2str (int); + +#endif /* _HCCONTROL_H_ */ + diff --git a/usr.sbin/bluetooth/hccontrol/host_controller_baseband.c b/usr.sbin/bluetooth/hccontrol/host_controller_baseband.c new file mode 100644 index 0000000000000..3aa1571693828 --- /dev/null +++ b/usr.sbin/bluetooth/hccontrol/host_controller_baseband.c @@ -0,0 +1,1713 @@ +/* + * host_controller_baseband.c + * + * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: host_controller_baseband.c,v 1.12 2002/11/19 18:34:06 max Exp $ + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/endian.h> +#include <errno.h> +#include <ng_hci.h> +#include <stdio.h> +#include <string.h> +#include "hccontrol.h" + +/* Convert hex ASCII to int4 */ +static int +hci_hexa2int4(const char *a) +{ + if ('0' <= *a && *a <= '9') + return (*a - '0'); + + if ('A' <= *a && *a <= 'F') + return (*a - 'A' + 0xa); + + if ('a' <= *a && *a <= 'f') + return (*a - 'a' + 0xa); + + return (-1); +} + +/* Convert hex ASCII to int8 */ +static int +hci_hexa2int8(const char *a) +{ + int hi = hci_hexa2int4(a); + int lo = hci_hexa2int4(a + 1); + + if (hi < 0 || lo < 0) + return (-1); + + return ((hi << 4) | lo); +} + +/* Convert ascii hex string to the u_int8_t[] */ +static int +hci_hexstring2array(char const *s, u_int8_t *a, int asize) +{ + int i, l, b; + + l = strlen(s) / 2; + if (l > asize) + l = asize; + + for (i = 0; i < l; i++) { + b = hci_hexa2int8(s + i * 2); + if (b < 0) + return (-1); + + a[i] = (b & 0xff); + } + + return (0); +} + +/* Send RESET to the unit */ +static int +hci_reset(int s, int argc, char **argv) +{ + ng_hci_status_rp rp; + int n; + + n = sizeof(rp); + if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_RESET), (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + return (OK); +} /* hci_reset */ + +/* Send Read_PIN_Type command to the unit */ +static int +hci_read_pin_type(int s, int argc, char **argv) +{ + ng_hci_read_pin_type_rp rp; + int n; + + n = sizeof(rp); + if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_READ_PIN_TYPE), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + fprintf(stdout, "PIN type: %s [%#02x]\n", + hci_pin2str(rp.pin_type), rp.pin_type); + + return (OK); +} /* hci_read_pin_type */ + +/* Send Write_PIN_Type command to the unit */ +static int +hci_write_pin_type(int s, int argc, char **argv) +{ + ng_hci_write_pin_type_cp cp; + ng_hci_write_pin_type_rp rp; + int n; + + /* parse command parameters */ + switch (argc) { + case 1: + if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 1) + return (USAGE); + + cp.pin_type = (u_int8_t) n; + break; + + default: + return (USAGE); + } + + /* send command */ + n = sizeof(rp); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_WRITE_PIN_TYPE), + (char const *) &cp, sizeof(cp), + (char *) &rp , &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + return (OK); +} /* hci_write_pin_type */ + +/* Send Read_Stored_Link_Key command to the unit */ +static int +hci_read_stored_link_key(int s, int argc, char **argv) +{ + struct { + ng_hci_cmd_pkt_t hdr; + ng_hci_read_stored_link_key_cp cp; + } __attribute__ ((packed)) cmd; + + struct { + ng_hci_event_pkt_t hdr; + union { + ng_hci_command_compl_ep cc; + ng_hci_return_link_keys_ep key; + u_int8_t b[NG_HCI_EVENT_PKT_SIZE]; + } ep; + } __attribute__ ((packed)) event; + + int n,a0,a1,a2,a3,a4,a5; + + /* Send command */ + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.type = NG_HCI_CMD_PKT; + cmd.hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_READ_STORED_LINK_KEY)); + cmd.hdr.length = sizeof(cmd.cp); + + switch (argc) { + case 1: + /* parse BD_ADDR */ + if (sscanf(argv[0], "%x:%x:%x:%x:%x:%x", &a5, &a4, &a3, &a2, + &a1, &a0) != 6) + return (USAGE); + + cmd.cp.bdaddr.b[0] = (a0 & 0xff); + cmd.cp.bdaddr.b[1] = (a1 & 0xff); + cmd.cp.bdaddr.b[2] = (a2 & 0xff); + cmd.cp.bdaddr.b[3] = (a3 & 0xff); + cmd.cp.bdaddr.b[4] = (a4 & 0xff); + cmd.cp.bdaddr.b[5] = (a5 & 0xff); + break; + + default: + cmd.cp.read_all = 1; + break; + } + + if (hci_send(s, (char const *) &cmd, sizeof(cmd)) != OK) + return (ERROR); + + /* Receive events */ +again: + memset(&event, 0, sizeof(event)); + n = sizeof(event); + if (hci_recv(s, (char *) &event, &n) != OK) + return (ERROR); + + if (n <= sizeof(event.hdr)) { + errno = EMSGSIZE; + return (ERROR); + } + + if (event.hdr.type != NG_HCI_EVENT_PKT) { + errno = EIO; + return (ERROR); + } + + /* Parse event */ + switch (event.hdr.event) { + case NG_HCI_EVENT_COMMAND_COMPL: { + ng_hci_read_stored_link_key_rp *rp = NULL; + + if (event.ep.cc.opcode == 0x0000 || + event.ep.cc.opcode != cmd.hdr.opcode) + goto again; + + rp = (ng_hci_read_stored_link_key_rp *)(event.ep.b + + sizeof(event.ep.cc)); + + fprintf(stdout, "Complete: Status: %s [%#x]\n", + hci_status2str(rp->status), rp->status); + fprintf(stdout, "Maximum Number of keys: %d\n", + le16toh(rp->max_num_keys)); + fprintf(stdout, "Number of keys read: %d\n", + le16toh(rp->num_keys_read)); + } break; + + case NG_HCI_EVENT_RETURN_LINK_KEYS: { + struct _key { + bdaddr_t bdaddr; + u_int8_t key[NG_HCI_KEY_SIZE]; + } __attribute__ ((packed)) *k = NULL; + + fprintf(stdout, "Event: Number of keys: %d\n", + event.ep.key.num_keys); + + k = (struct _key *)(event.ep.b + sizeof(event.ep.key)); + for (n = 0; n < event.ep.key.num_keys; n++) { + fprintf(stdout, "\t%d: %02x:%02x:%02x:%02x:%02x:%02x ", + n + 1, + k->bdaddr.b[5], k->bdaddr.b[4], k->bdaddr.b[3], + k->bdaddr.b[2], k->bdaddr.b[1], k->bdaddr.b[0]); + + for (a0 = 0; a0 < sizeof(k->key); a0++) + fprintf(stdout, "%02x", k->key[a0]); + fprintf(stdout, "\n"); + + k ++; + } + + goto again; + + } break; + + default: + goto again; + } + + return (OK); +} /* hci_read_store_link_key */ + +/* Send Write_Stored_Link_Key command to the unit */ +static int +hci_write_stored_link_key(int s, int argc, char **argv) +{ + struct { + ng_hci_write_stored_link_key_cp p; + bdaddr_t bdaddr; + u_int8_t key[NG_HCI_KEY_SIZE]; + } cp; + ng_hci_write_stored_link_key_rp rp; + int32_t n, a0, a1, a2, a3, a4, a5; + + memset(&cp, 0, sizeof(cp)); + + switch (argc) { + case 2: + cp.p.num_keys_write = 1; + + /* parse BD_ADDR */ + if (sscanf(argv[0], "%x:%x:%x:%x:%x:%x", + &a5, &a4, &a3, &a2, &a1, &a0) != 6) + return (USAGE); + + cp.bdaddr.b[0] = (a0 & 0xff); + cp.bdaddr.b[1] = (a1 & 0xff); + cp.bdaddr.b[2] = (a2 & 0xff); + cp.bdaddr.b[3] = (a3 & 0xff); + cp.bdaddr.b[4] = (a4 & 0xff); + cp.bdaddr.b[5] = (a5 & 0xff); + + /* parse key */ + if (hci_hexstring2array(argv[1], cp.key, sizeof(cp.key)) < 0) + return (USAGE); + break; + + default: + return (USAGE); + } + + /* send command */ + n = sizeof(rp); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_WRITE_STORED_LINK_KEY), + (char const *) &cp, sizeof(cp), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + fprintf(stdout, "Number of keys written: %d\n", rp.num_keys_written); + + return (OK); +} /* hci_write_stored_link_key */ + + +/* Send Delete_Stored_Link_Key command to the unit */ +static int +hci_delete_stored_link_key(int s, int argc, char **argv) +{ + ng_hci_delete_stored_link_key_cp cp; + ng_hci_delete_stored_link_key_rp rp; + int32_t n, a0, a1, a2, a3, a4, a5; + + memset(&cp, 0, sizeof(cp)); + + switch (argc) { + case 1: + /* parse BD_ADDR */ + if (sscanf(argv[0], "%x:%x:%x:%x:%x:%x", + &a5, &a4, &a3, &a2, &a1, &a0) != 6) + return (USAGE); + + cp.bdaddr.b[0] = (a0 & 0xff); + cp.bdaddr.b[1] = (a1 & 0xff); + cp.bdaddr.b[2] = (a2 & 0xff); + cp.bdaddr.b[3] = (a3 & 0xff); + cp.bdaddr.b[4] = (a4 & 0xff); + cp.bdaddr.b[5] = (a5 & 0xff); + break; + + default: + cp.delete_all = 1; + break; + } + + /* send command */ + n = sizeof(cp); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_DELETE_STORED_LINK_KEY), + (char const *) &cp, sizeof(cp), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + fprintf(stdout, "Number of keys deleted: %d\n", rp.num_keys_deleted); + + return (OK); +} /* hci_delete_stored_link_key */ + +/* Send Change_Local_Name command to the unit */ +static int +hci_change_local_name(int s, int argc, char **argv) +{ + ng_hci_change_local_name_cp cp; + ng_hci_change_local_name_rp rp; + int n; + + /* parse command parameters */ + switch (argc) { + case 1: + snprintf(cp.name, sizeof(cp.name), "%s", argv[0]); + break; + + default: + return (USAGE); + } + + /* send command */ + n = sizeof(rp); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_CHANGE_LOCAL_NAME), + (char const *) &cp, sizeof(cp), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + return (OK); +} /* hci_change_local_name */ + +/* Send Read_Local_Name command to the unit */ +static int +hci_read_local_name(int s, int argc, char **argv) +{ + ng_hci_read_local_name_rp rp; + int n; + + n = sizeof(rp); + if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_READ_LOCAL_NAME), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + fprintf(stdout, "Local name: %s\n", rp.name); + + return (OK); +} /* hci_read_local_name */ + +/* Send Read_Connection_Accept_Timeout to the unit */ +static int +hci_read_connection_accept_timeout(int s, int argc, char **argv) +{ + ng_hci_read_con_accept_timo_rp rp; + int n; + + n = sizeof(rp); + if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_READ_CON_ACCEPT_TIMO), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + rp.timeout = le16toh(rp.timeout); + fprintf(stdout, "Connection accept timeout: %.2f msec [%d slots]\n", + rp.timeout * 0.625, rp.timeout); + + return (OK); +} /* hci_read_connection_accept_timeout */ + +/* Send Write_Connection_Accept_Timeout to the unit */ +static int +hci_write_connection_accept_timeout(int s, int argc, char **argv) +{ + ng_hci_write_con_accept_timo_cp cp; + ng_hci_write_con_accept_timo_rp rp; + int n; + + /* parse command parameters */ + switch (argc) { + case 1: + if (sscanf(argv[0], "%d", &n) != 1 || n < 1 || n > 0xb540) + return (USAGE); + + cp.timeout = (u_int16_t) n; + cp.timeout = htole16(cp.timeout); + break; + + default: + return (USAGE); + } + + /* send command */ + n = sizeof(rp); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_WRITE_CON_ACCEPT_TIMO), + (char const *) &cp, sizeof(cp), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + return (OK); +} /* hci_write_connection_accept_timeout */ + +/* Send Read_Page_Timeout command to the unit */ +static int +hci_read_page_timeout(int s, int argc, char **argv) +{ + ng_hci_read_page_timo_rp rp; + int n; + + n = sizeof(rp); + if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_READ_PAGE_TIMO), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + rp.timeout = le16toh(rp.timeout); + fprintf(stdout, "Page timeout: %.2f msec [%d slots]\n", + rp.timeout * 0.625, rp.timeout); + + return (OK); +} /* hci_read_page_timeoout */ + +/* Send Write_Page_Timeout command to the unit */ +static int +hci_write_page_timeout(int s, int argc, char **argv) +{ + ng_hci_write_page_timo_cp cp; + ng_hci_write_page_timo_rp rp; + int n; + + /* parse command parameters */ + switch (argc) { + case 1: + if (sscanf(argv[0], "%d", &n) != 1 || n < 1 || n > 0xffff) + return (USAGE); + + cp.timeout = (u_int16_t) n; + cp.timeout = htole16(cp.timeout); + break; + + default: + return (USAGE); + } + + /* send command */ + n = sizeof(rp); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_WRITE_PAGE_TIMO), + (char const *) &cp, sizeof(cp), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + return (OK); +} /* hci_write_page_timeout */ + +/* Send Read_Scan_Enable command to the unit */ +static int +hci_read_scan_enable(int s, int argc, char **argv) +{ + ng_hci_read_scan_enable_rp rp; + int n; + + n = sizeof(rp); + if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_READ_SCAN_ENABLE), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + fprintf(stdout, "Scan enable: %s [%#02x]\n", + hci_scan2str(rp.scan_enable), rp.scan_enable); + + return (OK); +} /* hci_read_scan_enable */ + +/* Send Write_Scan_Enable command to the unit */ +static int +hci_write_scan_enable(int s, int argc, char **argv) +{ + ng_hci_write_scan_enable_cp cp; + ng_hci_write_scan_enable_rp rp; + int n; + + /* parse command parameters */ + switch (argc) { + case 1: + if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 3) + return (USAGE); + + cp.scan_enable = (u_int8_t) n; + break; + + default: + return (USAGE); + } + + n = sizeof(rp); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_WRITE_SCAN_ENABLE), + (char const *) &cp, sizeof(cp), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + return (OK); +} /* hci_write_scan_enable */ + +/* Send Read_Page_Scan_Activity command to the unit */ +static int +hci_read_page_scan_activity(int s, int argc, char **argv) +{ + ng_hci_read_page_scan_activity_rp rp; + int n; + + n = sizeof(rp); + if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_READ_PAGE_SCAN_ACTIVITY), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + rp.page_scan_interval = le16toh(rp.page_scan_interval); + rp.page_scan_window = le16toh(rp.page_scan_window); + + fprintf(stdout, "Page Scan Interval: %.2f msec [%d slots]\n", + rp.page_scan_interval * 0.625, rp.page_scan_interval); + fprintf(stdout, "Page Scan Window: %.2f msec [%d slots]\n", + rp.page_scan_window * 0.625, rp.page_scan_window); + + return (OK); +} /* hci_read_page_scan_activity */ + +/* Send Write_Page_Scan_Activity command to the unit */ +static int +hci_write_page_scan_activity(int s, int argc, char **argv) +{ + ng_hci_write_page_scan_activity_cp cp; + ng_hci_write_page_scan_activity_rp rp; + int n; + + /* parse command parameters */ + switch (argc) { + case 2: + /* page scan interval */ + if (sscanf(argv[0], "%d", &n) != 1 || n < 0x12 || n > 0x1000) + return (USAGE); + + cp.page_scan_interval = (u_int16_t) n; + + /* page scan window */ + if (sscanf(argv[0], "%d", &n) != 1 || n < 0x12 || n > 0x1000) + return (USAGE); + + cp.page_scan_window = (u_int16_t) n; + + if (cp.page_scan_window > cp.page_scan_interval) + return (USAGE); + + cp.page_scan_interval = htole16(cp.page_scan_interval); + cp.page_scan_window = htole16(cp.page_scan_window); + break; + + default: + return (USAGE); + } + + /* send command */ + n = sizeof(rp); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_WRITE_PAGE_SCAN_ACTIVITY), + (char const *) &cp, sizeof(cp), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + return (OK); +} /* hci_write_page_scan_activity */ + +/* Send Read_Inquiry_Scan_Activity command to the unit */ +static int +hci_read_inquiry_scan_activity(int s, int argc, char **argv) +{ + ng_hci_read_inquiry_scan_activity_rp rp; + int n; + + n = sizeof(rp); + if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_READ_INQUIRY_SCAN_ACTIVITY), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + rp.inquiry_scan_interval = le16toh(rp.inquiry_scan_interval); + rp.inquiry_scan_window = le16toh(rp.inquiry_scan_window); + + fprintf(stdout, "Inquiry Scan Interval: %.2f msec [%d slots]\n", + rp.inquiry_scan_interval * 0.625, rp.inquiry_scan_interval); + fprintf(stdout, "Inquiry Scan Window: %.2f msec [%d slots]\n", + rp.inquiry_scan_window * 0.625, rp.inquiry_scan_interval); + + return (OK); +} /* hci_read_inquiry_scan_activity */ + +/* Send Write_Inquiry_Scan_Activity command to the unit */ +static int +hci_write_inquiry_scan_activity(int s, int argc, char **argv) +{ + ng_hci_write_inquiry_scan_activity_cp cp; + ng_hci_write_inquiry_scan_activity_rp rp; + int n; + + /* parse command parameters */ + switch (argc) { + case 2: + /* inquiry scan interval */ + if (sscanf(argv[0], "%d", &n) != 1 || n < 0x12 || n > 0x1000) + return (USAGE); + + cp.inquiry_scan_interval = (u_int16_t) n; + + /* inquiry scan window */ + if (sscanf(argv[0], "%d", &n) != 1 || n < 0x12 || n > 0x1000) + return (USAGE); + + cp.inquiry_scan_window = (u_int16_t) n; + + if (cp.inquiry_scan_window > cp.inquiry_scan_interval) + return (USAGE); + + cp.inquiry_scan_interval = + htole16(cp.inquiry_scan_interval); + cp.inquiry_scan_window = htole16(cp.inquiry_scan_window); + break; + + default: + return (USAGE); + } + + /* send command */ + n = sizeof(rp); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_WRITE_INQUIRY_SCAN_ACTIVITY), + (char const *) &cp, sizeof(cp), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + return (OK); +} /* hci_write_inquiry_scan_activity */ + +/* Send Read_Authentication_Enable command to the unit */ +static int +hci_read_authentication_enable(int s, int argc, char **argv) +{ + ng_hci_read_auth_enable_rp rp; + int n; + + n = sizeof(rp); + if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_READ_AUTH_ENABLE), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + fprintf(stdout, "Authentication Enable: %s [%d]\n", + rp.auth_enable? "Enabled" : "Disabled", rp.auth_enable); + + return (OK); +} /* hci_read_authentication_enable */ + +/* Send Write_Authentication_Enable command to the unit */ +static int +hci_write_authentication_enable(int s, int argc, char **argv) +{ + ng_hci_write_auth_enable_cp cp; + ng_hci_write_auth_enable_rp rp; + int n; + + /* parse command parameters */ + switch (argc) { + case 1: + if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 1) + return (USAGE); + + cp.auth_enable = (u_int8_t) n; + break; + + default: + return (USAGE); + } + + /* send command */ + n = sizeof(rp); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_WRITE_AUTH_ENABLE), + (char const *) &cp, sizeof(cp), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + return (OK); +} /* hci_write_authentication_enable */ + +/* Send Read_Encryption_Mode command to the unit */ +static int +hci_read_encryption_mode(int s, int argc, char **argv) +{ + ng_hci_read_encryption_mode_rp rp; + int n; + + n = sizeof(rp); + if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_READ_ENCRYPTION_MODE), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + fprintf(stdout, "Encryption mode: %s [%#02x]\n", + hci_encrypt2str(rp.encryption_mode, 0), rp.encryption_mode); + + return (OK); +} /* hci_read_encryption_mode */ + +/* Send Write_Encryption_Mode command to the unit */ +static int +hci_write_encryption_mode(int s, int argc, char **argv) +{ + ng_hci_write_encryption_mode_cp cp; + ng_hci_write_encryption_mode_rp rp; + int n; + + /* parse command parameters */ + switch (argc) { + case 1: + if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 2) + return (USAGE); + + cp.encryption_mode = (u_int8_t) n; + break; + + default: + return (USAGE); + } + + /* send command */ + n = sizeof(rp); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_WRITE_ENCRYPTION_MODE), + (char const *) &cp, sizeof(cp), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + return (OK); +} /* hci_write_encryption_mode */ + +/* Send Read_Class_Of_Device command to the unit */ +static int +hci_read_class_of_device(int s, int argc, char **argv) +{ + ng_hci_read_unit_class_rp rp; + int n; + + n = sizeof(rp); + if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_READ_UNIT_CLASS), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + fprintf(stdout, "Class: %02x:%02x:%02x\n", + rp.uclass[2], rp.uclass[1], rp.uclass[0]); + + return (0); +} /* hci_read_class_of_device */ + +/* Send Write_Class_Of_Device command to the unit */ +static int +hci_write_class_of_device(int s, int argc, char **argv) +{ + ng_hci_write_unit_class_cp cp; + ng_hci_write_unit_class_rp rp; + int n0, n1, n2; + + /* parse command parameters */ + switch (argc) { + case 1: + if (sscanf(argv[0], "%x:%x:%x", &n2, &n1, &n0) != 3) + return (USAGE); + + cp.uclass[0] = (n0 & 0xff); + cp.uclass[1] = (n1 & 0xff); + cp.uclass[2] = (n2 & 0xff); + break; + + default: + return (USAGE); + } + + /* send command */ + n0 = sizeof(rp); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_WRITE_UNIT_CLASS), + (char const *) &cp, sizeof(cp), + (char *) &rp, &n0) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + return (OK); +} /* hci_write_class_of_device */ + +/* Send Read_Voice_Settings command to the unit */ +static int +hci_read_voice_settings(int s, int argc, char **argv) +{ + ng_hci_read_voice_settings_rp rp; + int n, + input_coding, + input_data_format, + input_sample_size; + + n = sizeof(rp); + if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_READ_VOICE_SETTINGS), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + rp.settings = le16toh(rp.settings); + + input_coding = (rp.settings & 0x0300) >> 8; + input_data_format = (rp.settings & 0x00c0) >> 6; + input_sample_size = (rp.settings & 0x0020) >> 5; + + fprintf(stdout, "Voice settings: %#04x\n", rp.settings); + fprintf(stdout, "Input coding: %s [%d]\n", + hci_coding2str(input_coding), input_coding); + fprintf(stdout, "Input data format: %s [%d]\n", + hci_vdata2str(input_data_format), input_data_format); + + if (input_coding == 0x00) /* Only for Linear PCM */ + fprintf(stdout, "Input sample size: %d bit [%d]\n", + input_sample_size? 16 : 8, input_sample_size); + + return (OK); +} /* hci_read_voice_settings */ + +/* Send Write_Voice_Settings command to the unit */ +static int +hci_write_voice_settings(int s, int argc, char **argv) +{ + ng_hci_write_voice_settings_cp cp; + ng_hci_write_voice_settings_rp rp; + int n; + + /* parse command parameters */ + switch (argc) { + case 1: + if (sscanf(argv[0], "%x", &n) != 1) + return (USAGE); + + cp.settings = (u_int16_t) n; + cp.settings = htole16(cp.settings); + break; + + default: + return (USAGE); + } + + /* send command */ + n = sizeof(rp); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_WRITE_VOICE_SETTINGS), + (char const *) &cp, sizeof(cp), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + return (OK); +} /* hci_write_voice_settings */ + +/* Send Read_Number_Broadcast_Restransmissions */ +static int +hci_read_number_broadcast_retransmissions(int s, int argc, char **argv) +{ + ng_hci_read_num_broadcast_retrans_rp rp; + int n; + + n = sizeof(rp); + if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_READ_NUM_BROADCAST_RETRANS), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + fprintf(stdout, "Number of broadcast retransmissions: %d\n", + rp.counter); + + return (OK); +} /* hci_read_number_broadcast_retransmissions */ + +/* Send Write_Number_Broadcast_Restransmissions */ +static int +hci_write_number_broadcast_retransmissions(int s, int argc, char **argv) +{ + ng_hci_write_num_broadcast_retrans_cp cp; + ng_hci_write_num_broadcast_retrans_rp rp; + int n; + + /* parse command parameters */ + switch (argc) { + case 1: + if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 0xff) + return (USAGE); + + cp.counter = (u_int8_t) n; + break; + + default: + return (USAGE); + } + + /* send command */ + n = sizeof(rp); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_WRITE_NUM_BROADCAST_RETRANS), + (char const *) &cp, sizeof(cp), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + return (OK); +} /* hci_write_number_broadcast_retransmissions */ + +/* Send Read_Hold_Mode_Activity command to the unit */ +static int +hci_read_hold_mode_activity(int s, int argc, char **argv) +{ + ng_hci_read_hold_mode_activity_rp rp; + int n; + char buffer[1024]; + + n = sizeof(rp); + if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_READ_HOLD_MODE_ACTIVITY), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + fprintf(stdout, "Hold Mode Activities: %#02x\n", rp.hold_mode_activity); + if (rp.hold_mode_activity == 0) + fprintf(stdout, "Maintain current Power State"); + else + fprintf(stdout, "%s", hci_hmode2str(rp.hold_mode_activity, + buffer, sizeof(buffer))); + + fprintf(stdout, "\n"); + + return (OK); +} /* hci_read_hold_mode_activity */ + +/* Send Write_Hold_Mode_Activity command to the unit */ +static int +hci_write_hold_mode_activity(int s, int argc, char **argv) +{ + ng_hci_write_hold_mode_activity_cp cp; + ng_hci_write_hold_mode_activity_rp rp; + int n; + + /* parse command parameters */ + switch (argc) { + case 1: + if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 4) + return (USAGE); + + cp.hold_mode_activity = (u_int8_t) n; + break; + + default: + return (USAGE); + } + + /* send command */ + n = sizeof(rp); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_WRITE_HOLD_MODE_ACTIVITY), + (char const *) &cp, sizeof(cp), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + return (OK); +} /* hci_write_hold_mode_activity */ + +/* Send Read_SCO_Flow_Control_Enable command to the unit */ +static int +hci_read_sco_flow_control_enable(int s, int argc, char **argv) +{ + ng_hci_read_sco_flow_control_rp rp; + int n; + + n = sizeof(rp); + if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_READ_SCO_FLOW_CONTROL), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + fprintf(stdout, "SCO flow control %s [%d]\n", + rp.flow_control? "enabled" : "disabled", rp.flow_control); + + return (OK); +} /* hci_read_sco_flow_control_enable */ + +/* Send Write_SCO_Flow_Control_Enable command to the unit */ +static int +hci_write_sco_flow_control_enable(int s, int argc, char **argv) +{ + ng_hci_write_sco_flow_control_cp cp; + ng_hci_write_sco_flow_control_rp rp; + int n; + + /* parse command parameters */ + switch (argc) { + case 1: + if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 1) + return (USAGE); + + cp.flow_control = (u_int8_t) n; + break; + + default: + return (USAGE); + } + + /* send command */ + n = sizeof(rp); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_WRITE_SCO_FLOW_CONTROL), + (char const *) &cp, sizeof(cp), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + return (OK); +} /* hci_write_sco_flow_control_enable */ + +/* Send Read_Link_Supervision_Timeout command to the unit */ +static int +hci_read_link_supervision_timeout(int s, int argc, char **argv) +{ + ng_hci_read_link_supervision_timo_cp cp; + ng_hci_read_link_supervision_timo_rp rp; + int n; + + switch (argc) { + case 1: + /* connection handle */ + if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff) + return (USAGE); + + cp.con_handle = (u_int16_t) (n & 0x0fff); + cp.con_handle = htole16(cp.con_handle); + break; + + default: + return (USAGE); + } + + /* send command */ + n = sizeof(rp); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_READ_LINK_SUPERVISION_TIMO), + (char const *) &cp, sizeof(cp), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + rp.timeout = le16toh(rp.timeout); + + fprintf(stdout, "Connection handle: %d\n", le16toh(rp.con_handle)); + fprintf(stdout, "Link supervision timeout: %.2f msec [%d slots]\n", + rp.timeout * 0.625, rp.timeout); + + return (OK); +} /* hci_read_link_supervision_timeout */ + +/* Send Write_Link_Supervision_Timeout command to the unit */ +static int +hci_write_link_supervision_timeout(int s, int argc, char **argv) +{ + ng_hci_write_link_supervision_timo_cp cp; + ng_hci_write_link_supervision_timo_rp rp; + int n; + + switch (argc) { + case 2: + /* connection handle */ + if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff) + return (USAGE); + + cp.con_handle = (u_int16_t) (n & 0x0fff); + cp.con_handle = htole16(cp.con_handle); + + /* link supervision timeout */ + if (sscanf(argv[1], "%d", &n) != 1 || n < 0 || n > 0xeff) + return (USAGE); + + cp.timeout = (u_int16_t) (n & 0x0fff); + cp.timeout = htole16(cp.timeout); + break; + + default: + return (USAGE); + } + + /* send command */ + n = sizeof(rp); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_WRITE_LINK_SUPERVISION_TIMO), + (char const *) &cp, sizeof(cp), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + return (OK); +} /* hci_write_link_supervision_timeout */ + +struct hci_command host_controller_baseband_commands[] = { +{ +"reset", +"\nThe Reset command will reset the Host Controller and the Link Manager.\n" \ +"After the reset is completed, the current operational state will be lost,\n" \ +"the Bluetooth unit will enter standby mode and the Host Controller will\n" \ +"automatically revert to the default values for the parameters for which\n" \ +"default values are defined in the specification.", +&hci_reset +}, +{ +"read_pin_type", +"\nThe Read_PIN_Type command is used for the Host to read whether the Link\n" \ +"Manager assumes that the Host supports variable PIN codes only a fixed PIN\n" \ +"code.", +&hci_read_pin_type +}, +{ +"write_pin_type <pin_type>", +"\nThe Write_PIN_Type command is used for the Host to write to the Host\n" \ +"Controller whether the Host supports variable PIN codes or only a fixed PIN\n"\ +"code.\n\n" \ +"\t<pin_type> - dd; 0 - Variable; 1 - Fixed", +&hci_write_pin_type +}, +{ +"read_stored_link_key [<bdaddr>]", +"\nThe Read_Stored_Link_Key command provides the ability to read one or\n" \ +"more link keys stored in the Bluetooth Host Controller. The Bluetooth Host\n" \ +"Controller can store a limited number of link keys for other Bluetooth\n" \ +"devices.\n\n" \ +"\t<bdaddr> - xx:xx:xx:xx:xx:xx BD_ADDR", +&hci_read_stored_link_key +}, +{ +"write_stored_link_key <bdaddr> <key>", +"\nThe Write_Stored_Link_Key command provides the ability to write one\n" \ +"or more link keys to be stored in the Bluetooth Host Controller. The\n" \ +"Bluetooth Host Controller can store a limited number of link keys for other\n"\ +"Bluetooth devices. If no additional space is available in the Bluetooth\n"\ +"Host Controller then no additional link keys will be stored.\n\n" \ +"\t<bdaddr> - xx:xx:xx:xx:xx:xx BD_ADDR\n" \ +"\t<key> - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx up to 16 bytes link key", +&hci_write_stored_link_key +}, +{ +"delete_stored_link_key [<bdaddr>]", +"\nThe Delete_Stored_Link_Key command provides the ability to remove one\n" \ +"or more of the link keys stored in the Bluetooth Host Controller. The\n" \ +"Bluetooth Host Controller can store a limited number of link keys for other\n"\ +"Bluetooth devices.\n\n" \ +"\t<bdaddr> - xx:xx:xx:xx:xx:xx BD_ADDR", +&hci_delete_stored_link_key +}, +{ +"change_local_name <name>", +"\nThe Change_Local_Name command provides the ability to modify the user\n" \ +"friendly name for the Bluetooth unit.\n\n" \ +"\t<name> - string", +&hci_change_local_name +}, +{ +"read_local_name", +"\nThe Read_Local_Name command provides the ability to read the\n" \ +"stored user-friendly name for the Bluetooth unit.", +&hci_read_local_name +}, +{ +"read_connection_accept_timeout", +"\nThis command will read the value for the Connection_Accept_Timeout\n" \ +"configuration parameter. The Connection_Accept_Timeout configuration\n" \ +"parameter allows the Bluetooth hardware to automatically deny a\n" \ +"connection request after a specified time period has occurred and\n" \ +"the new connection is not accepted. Connection Accept Timeout\n" \ +"measured in Number of Baseband slots.", +&hci_read_connection_accept_timeout +}, +{ +"write_connection_accept_timeout <timeout>", +"\nThis command will write the value for the Connection_Accept_Timeout\n" \ +"configuration parameter.\n\n" \ +"\t<timeout> - dddd; measured in number of baseband slots.", +&hci_write_connection_accept_timeout +}, +{ +"read_page_timeout", +"\nThis command will read the value for the Page_Timeout configuration\n" \ +"parameter. The Page_Timeout configuration parameter defines the\n" \ +"maximum time the local Link Manager will wait for a baseband page\n" \ +"response from the remote unit at a locally initiated connection\n" \ +"attempt. Page Timeout measured in Number of Baseband slots.", +&hci_read_page_timeout +}, +{ +"write_page_timeout <timeout>", +"\nThis command will write the value for the Page_Timeout configuration\n" \ +"parameter.\n\n" \ +"\t<timeout> - dddd; measured in number of baseband slots.", +&hci_write_page_timeout +}, +{ +"read_scan_enable", +"\nThis command will read the value for the Scan_Enable parameter. The\n" \ +"Scan_Enable parameter controls whether or not the Bluetooth uint\n" \ +"will periodically scan for page attempts and/or inquiry requests\n" \ +"from other Bluetooth unit.\n\n" \ +"\t0x00 - No Scans enabled.\n" \ +"\t0x01 - Inquiry Scan enabled. Page Scan disabled.\n" \ +"\t0x02 - Inquiry Scan disabled. Page Scan enabled.\n" \ +"\t0x03 - Inquiry Scan enabled. Page Scan enabled.", +&hci_read_scan_enable +}, +{ +"write_scan_enable <scan_enable>", +"\nThis command will write the value for the Scan_Enable parameter.\n" \ +"The Scan_Enable parameter controls whether or not the Bluetooth\n" \ +"unit will periodically scan for page attempts and/or inquiry\n" \ +"requests from other Bluetooth unit.\n\n" \ +"\t<scan_enable> - dd;\n" \ +"\t0 - No Scans enabled.\n" \ +"\t1 - Inquiry Scan enabled. Page Scan disabled.\n" \ +"\t2 - Inquiry Scan disabled. Page Scan enabled.\n" \ +"\t3 - Inquiry Scan enabled. Page Scan enabled.", +&hci_write_scan_enable +}, +{ +"read_page_scan_activity", +"\nThis command will read the value for Page_Scan_Activity configuration\n" \ +"parameters. The Page_Scan_Interval configuration parameter defines the\n" \ +"amount of time between consecutive page scans. This time interval is \n" \ +"defined from when the Host Controller started its last page scan until\n" \ +"it begins the next page scan. The Page_Scan_Window configuration parameter\n" \ +"defines the amount of time for the duration of the page scan. The\n" \ +"Page_Scan_Window can only be less than or equal to the Page_Scan_Interval.", +&hci_read_page_scan_activity +}, +{ +"write_page_scan_activity interval(dddd) window(dddd)", +"\nThis command will write the value for Page_Scan_Activity configuration\n" \ +"parameter. The Page_Scan_Interval configuration parameter defines the\n" \ +"amount of time between consecutive page scans. This is defined as the time\n" \ +"interval from when the Host Controller started its last page scan until it\n" \ +"begins the next page scan. The Page_Scan_Window configuration parameter\n" \ +"defines the amount of time for the duration of the page scan. \n" \ +"The Page_Scan_Window can only be less than or equal to the Page_Scan_Interval.\n\n" \ +"\t<interval> - Range: 0x0012 -– 0x100, Time = N * 0.625 msec\n" \ +"\t<window> - Range: 0x0012 -– 0x100, Time = N * 0.625 msen", +&hci_write_page_scan_activity +}, +{ +"read_inquiry_scan_activity", +"\nThis command will read the value for Inquiry_Scan_Activity configuration\n" \ +"parameter. The Inquiry_Scan_Interval configuration parameter defines the\n" \ +"amount of time between consecutive inquiry scans. This is defined as the\n" \ +"time interval from when the Host Controller started its last inquiry scan\n" \ +"until it begins the next inquiry scan.", +&hci_read_inquiry_scan_activity +}, +{ +"write_inquiry_scan_activity interval(dddd) window(dddd)", +"\nThis command will write the value for Inquiry_Scan_Activity configuration\n"\ +"parameter. The Inquiry_Scan_Interval configuration parameter defines the\n" \ +"amount of time between consecutive inquiry scans. This is defined as the\n" \ +"time interval from when the Host Controller started its last inquiry scan\n" \ +"until it begins the next inquiry scan. The Inquiry_Scan_Window configuration\n" \ +"parameter defines the amount of time for the duration of the inquiry scan.\n" \ +"The Inquiry_Scan_Window can only be less than or equal to the Inquiry_Scan_Interval.\n\n" \ +"\t<interval> - Range: 0x0012 -– 0x100, Time = N * 0.625 msec\n" \ +"\t<window> - Range: 0x0012 -– 0x100, Time = N * 0.625 msen", +&hci_write_inquiry_scan_activity +}, +{ +"read_authentication_enable", +"\nThis command will read the value for the Authentication_Enable parameter.\n"\ +"The Authentication_Enable parameter controls if the local unit requires\n"\ +"to authenticate the remote unit at connection setup (between the\n" \ +"Create_Connection command or acceptance of an incoming ACL connection\n"\ +"and the corresponding Connection Complete event). At connection setup, only\n"\ +"the unit(s) with the Authentication_Enable parameter enabled will try to\n"\ +"authenticate the other unit.", +&hci_read_authentication_enable +}, +{ +"write_authentication_enable enable(0|1)", +"\nThis command will write the value for the Authentication_Enable parameter.\n"\ +"The Authentication_Enable parameter controls if the local unit requires to\n"\ +"authenticate the remote unit at connection setup (between the\n" \ +"Create_Connection command or acceptance of an incoming ACL connection\n" \ +"and the corresponding Connection Complete event). At connection setup, only\n"\ +"the unit(s) with the Authentication_Enable parameter enabled will try to\n"\ +"authenticate the other unit.", +&hci_write_authentication_enable +}, +{ +"read_encryption_mode", +"\nThis command will read the value for the Encryption_Mode parameter. The\n" \ +"Encryption_Mode parameter controls if the local unit requires encryption\n" \ +"to the remote unit at connection setup (between the Create_Connection\n" \ +"command or acceptance of an incoming ACL connection and the corresponding\n" \ +"Connection Complete event). At connection setup, only the unit(s) with\n" \ +"the Authentication_Enable parameter enabled and Encryption_Mode parameter\n" \ +"enabled will try to encrypt the connection to the other unit.\n\n" \ +"\t<encryption_mode>:\n" \ +"\t0x00 - Encryption disabled.\n" \ +"\t0x01 - Encryption only for point-to-point packets.\n" \ +"\t0x02 - Encryption for both point-to-point and broadcast packets.", +&hci_read_encryption_mode +}, +{ +"write_encryption_mode mode(0|1|2)", +"\tThis command will write the value for the Encryption_Mode parameter.\n" \ +"The Encryption_Mode parameter controls if the local unit requires\n" \ +"encryption to the remote unit at connection setup (between the\n" \ +"Create_Connection command or acceptance of an incoming ACL connection\n" \ +"and the corresponding Connection Complete event). At connection setup,\n" \ +"only the unit(s) with the Authentication_Enable parameter enabled and\n" \ +"Encryption_Mode parameter enabled will try to encrypt the connection to\n" \ +"the other unit.\n\n" \ +"\t<encryption_mode> (dd)\n" \ +"\t0 - Encryption disabled.\n" \ +"\t1 - Encryption only for point-to-point packets.\n" \ +"\t2 - Encryption for both point-to-point and broadcast packets.", +&hci_write_encryption_mode +}, +{ +"read_class_of_device", +"\nThis command will read the value for the Class_of_Device parameter.\n" \ +"The Class_of_Device parameter is used to indicate the capabilities of\n" \ +"the local unit to other units.", +&hci_read_class_of_device +}, +{ +"write_class_of_device class(xx:xx:xx)", +"\nThis command will write the value for the Class_of_Device parameter.\n" \ +"The Class_of_Device parameter is used to indicate the capabilities of \n" \ +"the local unit to other units.\n\n" \ +"\t<class> (xx:xx:xx) - class of device", +&hci_write_class_of_device +}, +{ +"read_voice_settings", +"\nThis command will read the values for the Voice_Setting parameter.\n" \ +"The Voice_Setting parameter controls all the various settings for voice\n" \ +"connections. These settings apply to all voice connections, and cannot be\n" \ +"set for individual voice connections. The Voice_Setting parameter controls\n" \ +"the configuration for voice connections: Input Coding, Air coding format,\n" \ +"input data format, Input sample size, and linear PCM parameter.", +&hci_read_voice_settings +}, +{ +"write_voice_settings settings(xxxx)", +"\nThis command will write the values for the Voice_Setting parameter.\n" \ +"The Voice_Setting parameter controls all the various settings for voice\n" \ +"connections. These settings apply to all voice connections, and cannot be\n" \ +"set for individual voice connections. The Voice_Setting parameter controls\n" \ +"the configuration for voice connections: Input Coding, Air coding format,\n" \ +"input data format, Input sample size, and linear PCM parameter.\n\n" \ +"\t<voice_settings> (xxxx) - voice settings", +&hci_write_voice_settings +}, +{ +"read_number_broadcast_retransmissions", +"\nThis command will read the unit's parameter value for the Number of\n" \ +"Broadcast Retransmissions. Broadcast packets are not acknowledged and are\n" \ +"unreliable.", +&hci_read_number_broadcast_retransmissions +}, +{ +"write_number_broadcast_retransmissions count(dd)", +"\nThis command will write the unit's parameter value for the Number of\n" \ +"Broadcast Retransmissions. Broadcast packets are not acknowledged and are\n" \ +"unreliable.\n\n" \ +"\t<count> (dd) - number of broadcast retransimissions", +&hci_write_number_broadcast_retransmissions +}, +{ +"read_hold_mode_activity", +"\nThis command will read the value for the Hold_Mode_Activity parameter.\n" \ +"The Hold_Mode_Activity value is used to determine what activities should\n" \ +"be suspended when the unit is in hold mode.", +&hci_read_hold_mode_activity +}, +{ +"write_hold_mode_activity settings(0|1|2|4)", +"\nThis command will write the value for the Hold_Mode_Activity parameter.\n" \ +"The Hold_Mode_Activity value is used to determine what activities should\n" \ +"be suspended when the unit is in hold mode.\n\n" \ +"\t<settings> (dd) - bit mask:\n" \ +"\t0 - Maintain current Power State. Default\n" \ +"\t1 - Suspend Page Scan.\n" \ +"\t2 - Suspend Inquiry Scan.\n" \ +"\t4 - Suspend Periodic Inquiries.", +&hci_write_hold_mode_activity +}, +{ +"read_sco_flow_control_enable", +"\nThe Read_SCO_Flow_Control_Enable command provides the ability to read\n" \ +"the SCO_Flow_Control_Enable setting. By using this setting, the Host can\n" \ +"decide if the Host Controller will send Number Of Completed Packets events\n" \ +"for SCO Connection Handles. This setting allows the Host to enable and\n" \ +"disable SCO flow control.", +&hci_read_sco_flow_control_enable +}, +{ +"write_sco_flow_control_enable enable(0|1)", +"\nThe Write_SCO_Flow_Control_Enable command provides the ability to write\n" \ +"the SCO_Flow_Control_Enable setting. By using this setting, the Host can\n" \ +"decide if the Host Controller will send Number Of Completed Packets events\n" \ +"for SCO Connection Handles. This setting allows the Host to enable and\n" \ +"disable SCO flow control. The SCO_Flow_Control_Enable setting can only be\n" \ +"changed if no connections exist.", +&hci_write_sco_flow_control_enable +}, +{ +"read_link_supervision_timeout <connection_handle>", +"\nThis command will read the value for the Link_Supervision_Timeout\n" \ +"parameter for the device. The Link_Supervision_Timeout parameter is used\n" \ +"by the master or slave Bluetooth device to monitor link loss. If, for any\n" \ +"reason, no Baseband packets are received from that Connection Handle for a\n" \ +"duration longer than the Link_Supervision_Timeout, the connection is\n" +"disconnected.\n\n" \ +"\t<connection_handle> - dddd; connection handle\n", +&hci_read_link_supervision_timeout +}, +{ +"write_link_supervision_timeout <connection_handle> <timeout>", +"\nThis command will write the value for the Link_Supervision_Timeout\n" \ +"parameter for the device. The Link_Supervision_Timeout parameter is used\n" \ +"by the master or slave Bluetooth device to monitor link loss. If, for any\n" \ +"reason, no Baseband packets are received from that connection handle for a\n" \ +"duration longer than the Link_Supervision_Timeout, the connection is\n" \ +"disconnected.\n\n" \ +"\t<connection_handle> - dddd; connection handle\n" \ +"\t<timeout> - dddd; timeout measured in number of baseband slots\n", +&hci_write_link_supervision_timeout +}, +{ NULL, } +}; + diff --git a/usr.sbin/bluetooth/hccontrol/info.c b/usr.sbin/bluetooth/hccontrol/info.c new file mode 100644 index 0000000000000..447a4930b012f --- /dev/null +++ b/usr.sbin/bluetooth/hccontrol/info.c @@ -0,0 +1,219 @@ +/* + * info.c + * + * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: info.c,v 1.7 2002/09/06 18:52:41 max Exp $ + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/endian.h> +#include <errno.h> +#include <ng_hci.h> +#include <stdio.h> +#include <string.h> +#include "hccontrol.h" + +/* Send Read_Local_Version_Information command to the unit */ +static int +hci_read_local_version_information(int s, int argc, char **argv) +{ + ng_hci_read_local_ver_rp rp; + int n; + + n = sizeof(rp); + if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_INFO, + NG_HCI_OCF_READ_LOCAL_VER), (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + rp.manufacturer = le16toh(rp.manufacturer); + + fprintf(stdout, "HCI version: %s [%#02x]\n", + hci_ver2str(rp.hci_version), rp.hci_version); + fprintf(stdout, "HCI revision: %#04x\n", + le16toh(rp.hci_revision)); + fprintf(stdout, "LMP version: %#02x\n", rp.lmp_version); + fprintf(stdout, "LMP sub-version: %#04x\n", + le16toh(rp.lmp_subversion)); + fprintf(stdout, "Manufacturer: %s [%#04x]\n", + hci_manufacturer2str(rp.manufacturer), rp.manufacturer); + + return (OK); +} /* hci_read_local_version_information */ + +/* Send Read_Local_Supported_Features command to the unit */ +static int +hci_read_local_supported_features(int s, int argc, char **argv) +{ + ng_hci_read_local_features_rp rp; + int n; + char buffer[1024]; + + n = sizeof(rp); + if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_INFO, + NG_HCI_OCF_READ_LOCAL_FEATURES), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + fprintf(stdout, "Features: "); + for (n = 0; n < sizeof(rp.features); n++) + fprintf(stdout, "%#02x ", rp.features[n]); + fprintf(stdout, "\n%s\n", hci_features2str(rp.features, + buffer, sizeof(buffer))); + + return (OK); +} /* hci_read_local_supported_features */ + +/* Sent Read_Buffer_Size command to the unit */ +static int +hci_read_buffer_size(int s, int argc, char **argv) +{ + ng_hci_read_buffer_size_rp rp; + int n; + + n = sizeof(rp); + if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_INFO, + NG_HCI_OCF_READ_BUFFER_SIZE), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + fprintf(stdout, "Max. ACL packet size: %d bytes\n", + le16toh(rp.max_acl_size)); + fprintf(stdout, "Number of ACL packets: %d\n", + le16toh(rp.num_acl_pkt)); + fprintf(stdout, "Max. SCO packet size: %d bytes\n", + rp.max_sco_size); + fprintf(stdout, "Number of SCO packets: %d\n", + le16toh(rp.num_sco_pkt)); + + return (OK); +} /* hci_read_buffer_size */ + +/* Send Read_Country_Code command to the unit */ +static int +hci_read_country_code(int s, int argc, char **argv) +{ + ng_hci_read_country_code_rp rp; + int n; + + n = sizeof(rp); + if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_INFO, + NG_HCI_OCF_READ_COUNTRY_CODE), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + fprintf(stdout, "Country code: %s [%#02x]\n", + hci_cc2str(rp.country_code), rp.country_code); + + return (OK); +} /* hci_read_country_code */ + +/* Send Read_BD_ADDR command to the unit */ +static int +hci_read_bd_addr(int s, int argc, char **argv) +{ + ng_hci_read_bdaddr_rp rp; + int n; + + n = sizeof(rp); + if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_INFO, + NG_HCI_OCF_READ_BDADDR), (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + fprintf(stdout, "BD_ADDR: %02x:%02x:%02x:%02x:%02x:%02x\n", + rp.bdaddr.b[5], rp.bdaddr.b[4], rp.bdaddr.b[3], + rp.bdaddr.b[2], rp.bdaddr.b[1], rp.bdaddr.b[0]); + + return (OK); +} /* hci_read_bd_addr */ + +struct hci_command info_commands[] = { +{ +"read_local_version_information", +"\nThis command will read the values for the version information for the\n" \ +"local Bluetooth unit.", +&hci_read_local_version_information +}, +{ +"read_local_supported_features", +"\nThis command requests a list of the supported features for the local\n" \ +"unit. This command will return a list of the LMP features.", +&hci_read_local_supported_features +}, +{ +"read_buffer_size", +"\nThe Read_Buffer_Size command is used to read the maximum size of the\n" \ +"data portion of HCI ACL and SCO Data Packets sent from the Host to the\n" \ +"Host Controller.", +&hci_read_buffer_size +}, +{ +"read_country_code", +"\nThis command will read the value for the Country_Code return parameter.\n" \ +"The Country_Code defines which range of frequency band of the ISM 2.4 GHz\n" \ +"band will be used by the unit.", +&hci_read_country_code +}, +{ +"read_bd_addr", +"\nThis command will read the value for the BD_ADDR parameter. The BD_ADDR\n" \ +"is a 48-bit unique identifier for a Bluetooth unit.", +&hci_read_bd_addr +}, +{ +NULL, +}}; + diff --git a/usr.sbin/bluetooth/hccontrol/link_control.c b/usr.sbin/bluetooth/hccontrol/link_control.c new file mode 100644 index 0000000000000..68bf35f2a6dd2 --- /dev/null +++ b/usr.sbin/bluetooth/hccontrol/link_control.c @@ -0,0 +1,973 @@ +/* + * link_control.c + * + * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: link_control.c,v 1.12 2002/09/17 16:36:46 max Exp $ + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/endian.h> +#include <errno.h> +#include <ng_hci.h> +#include <stdio.h> +#include <string.h> +#include "hccontrol.h" + +static void hci_inquiry_response (int n, u_int8_t **b); + +/* Send Inquiry command to the unit */ +static int +hci_inquiry(int s, int argc, char **argv) +{ + int n0, n1, n2, timo; + u_int8_t b[512]; + ng_hci_inquiry_cp cp; + ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b; + + /* set defaults */ + cp.lap[2] = 0x9e; + cp.lap[1] = 0x8b; + cp.lap[0] = 0x33; + cp.inquiry_length = 5; + cp.num_responses = 8; + + /* parse command parameters */ + switch (argc) { + case 3: + /* LAP */ + if (sscanf(argv[0], "%x:%x:%x", &n2, &n1, &n0) != 3) + return (USAGE); + + cp.lap[0] = (n0 & 0xff); + cp.lap[1] = (n1 & 0xff); + cp.lap[2] = (n2 & 0xff); + + /* inquiry length (N * 1.28) sec, range 0x01 - 0x30 */ + case 2: + if (sscanf(argv[1], "%d", &n0) != 1 || n0 < 0x1 || n0 > 0x30) + return (USAGE); + + cp.inquiry_length = (n0 & 0xff); + + /* number of responses, range 0x00 - 0xff */ + case 1: + if (sscanf(argv[2], "%d", &n0) != 1 || n0 > 0xff) + return (USAGE); + + cp.num_responses = (n0 & 0xff); + + /* use defaults */ + case 0: + break; + + default: + return (USAGE); + } + + /* send request and expect status back */ + n0 = sizeof(b); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, + NG_HCI_OCF_INQUIRY), (char const *) &cp, sizeof(cp), + b, &n0) == ERROR) + return (ERROR); + + if (*b != 0x00) + return (FAILED); + + timo = timeout; + timeout = cp.inquiry_length * 1.28 + 1; + +wait_for_more: + /* wait for inquiry events */ + n0 = sizeof(b); + if (hci_recv(s, b, &n0) == ERROR) { + timeout = timo; + return (ERROR); + } + + if (n0 < sizeof(*e)) { + timeout = timo; + errno = EIO; + return (ERROR); + } + + switch (e->event) { + case NG_HCI_EVENT_INQUIRY_RESULT: { + ng_hci_inquiry_result_ep *ir = + (ng_hci_inquiry_result_ep *)(e + 1); + u_int8_t *r = (u_int8_t *)(ir + 1); + + fprintf(stdout, "Inquiry result, num_responses=%d\n", + ir->num_responses); + + for (n0 = 0; n0 < ir->num_responses; n0++) + hci_inquiry_response(n0, &r); + + goto wait_for_more; + } + + case NG_HCI_EVENT_INQUIRY_COMPL: + fprintf(stdout, "Inquiry complete. Status: %s [%#02x]\n", + hci_status2str(*(b + sizeof(*e))), *(b + sizeof(*e))); + break; + + default: + goto wait_for_more; + } + + timeout = timo; + + return (OK); +} /* hci_inquiry */ + +/* Print Inquiry_Result event */ +static void +hci_inquiry_response(int n, u_int8_t **b) +{ + struct inquiry_response { + bdaddr_t bdaddr; + u_int8_t page_scan_rep_mode; + u_int8_t page_scan_period_mode; + u_int8_t page_scan_mode; + u_int8_t class[NG_HCI_CLASS_SIZE]; + u_int16_t clock_offset; + } *ir = (struct inquiry_response *)(*b); + + fprintf(stdout, "Inquiry result #%d\n", n); + fprintf(stdout, "\tBD_ADDR: %02x:%02x:%02x:%02x:%02x:%02x\n", + ir->bdaddr.b[5], ir->bdaddr.b[4], ir->bdaddr.b[3], + ir->bdaddr.b[2], ir->bdaddr.b[1], ir->bdaddr.b[0]); + fprintf(stdout, "\tPage Scan Rep. Mode: %#02x\n", + ir->page_scan_rep_mode); + fprintf(stdout, "\tPage Scan Period Mode: %#02x\n", + ir->page_scan_period_mode); + fprintf(stdout, "\tPage Scan Mode: %#02x\n", + ir->page_scan_mode); + fprintf(stdout, "\tClass: %02x:%02x:%02x\n", + ir->class[2], ir->class[1], ir->class[0]); + fprintf(stdout, "\tClock offset: %#04x\n", + le16toh(ir->clock_offset)); + + *b += sizeof(*ir); +} /* hci_inquiry_response */ + +/* Send Create_Connection command to the unit */ +static int +hci_create_connection(int s, int argc, char **argv) +{ + int n0, n1, n2, n3, n4, n5; + char b[512]; + ng_hci_create_con_cp cp; + ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b; + + /* Set defaults */ + memset(&cp, 0, sizeof(cp)); + cp.pkt_type = htole16( NG_HCI_PKT_DM1 | NG_HCI_PKT_DH1 | + NG_HCI_PKT_DM3 | NG_HCI_PKT_DH3 | + NG_HCI_PKT_DM5); + cp.page_scan_rep_mode = NG_HCI_SCAN_REP_MODE0; + cp.page_scan_mode = NG_HCI_MANDATORY_PAGE_SCAN_MODE; + cp.clock_offset = 0; + cp.accept_role_switch = 1; + + /* parse command parameters */ + switch (argc) { + case 6: + /* accept role switch */ + if (sscanf(argv[2], "%d", &n0) != 1) + return (USAGE); + + cp.accept_role_switch = n0 ? 1 : 0; + + case 5: + /* clock offset */ + if (sscanf(argv[2], "%d", &n0) != 1) + return (USAGE); + + cp.clock_offset = (n0 & 0xffff); + cp.clock_offset = htole16(cp.clock_offset); + + case 4: + /* page scan mode */ + if (sscanf(argv[2], "%d", &n0) != 1 || n0 < 0 || n0 > 3) + return (USAGE); + + cp.page_scan_mode = (n0 & 0xff); + + case 3: + /* page scan rep mode */ + if (sscanf(argv[2], "%d", &n0) != 1 || n0 < 0 || n0 > 2) + return (USAGE); + + cp.page_scan_rep_mode = (n0 & 0xff); + + case 2: + /* packet type */ + if (sscanf(argv[1], "%x", &n0) != 1) + return (USAGE); + + n0 &= ( NG_HCI_PKT_DM1 | NG_HCI_PKT_DH1 | + NG_HCI_PKT_DM3 | NG_HCI_PKT_DH3 | + NG_HCI_PKT_DM5); + if (n0 == 0) + return (USAGE); + + cp.pkt_type = (n0 & 0xffff); + cp.pkt_type = htole16(cp.pkt_type); + + case 1: + /* BD_ADDR */ + if (sscanf(argv[0], "%x:%x:%x:%x:%x:%x", + &n5, &n4, &n3, &n2, &n1, &n0) != 6) + return (USAGE); + + cp.bdaddr.b[0] = (n0 & 0xff); + cp.bdaddr.b[1] = (n1 & 0xff); + cp.bdaddr.b[2] = (n2 & 0xff); + cp.bdaddr.b[3] = (n3 & 0xff); + cp.bdaddr.b[4] = (n4 & 0xff); + cp.bdaddr.b[5] = (n5 & 0xff); + break; + + default: + return (USAGE); + } + + /* send request and expect status response */ + n0 = sizeof(b); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, + NG_HCI_OCF_CREATE_CON), + (char const *) &cp, sizeof(cp), b, &n0) == ERROR) + return (ERROR); + + if (*b != 0x00) + return (FAILED); + + /* wait for event */ +again: + n0 = sizeof(b); + if (hci_recv(s, b, &n0) == ERROR) + return (ERROR); + if (n0 < sizeof(*e)) { + errno = EIO; + return (ERROR); + } + + if (e->event == NG_HCI_EVENT_CON_COMPL) { + ng_hci_con_compl_ep *ep = (ng_hci_con_compl_ep *)(e + 1); + + if (ep->status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(ep->status), ep->status); + return (FAILED); + } + + fprintf(stdout, "BD_ADDR: %02x:%02x:%02x:%02x:%02x:%02x\n", + ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3], + ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]); + fprintf(stdout, "Connection handle: %d\n", + le16toh(ep->con_handle)); + fprintf(stdout, "Encryption mode: %s [%d]\n", + hci_encrypt2str(ep->encryption_mode, 0), + ep->encryption_mode); + } else + goto again; + + return (OK); +} /* hci_create_connection */ + +/* Send Disconnect command to the unit */ +static int +hci_disconnect(int s, int argc, char **argv) +{ + int n; + char b[512]; + ng_hci_discon_cp cp; + ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b; + + /* Set defaults */ + memset(&cp, 0, sizeof(cp)); + cp.reason = 0x13; + + /* parse command parameters */ + switch (argc) { + case 2: + /* reason */ + if (sscanf(argv[1], "%d", &n) != 1 || n <= 0x00 || n > 0xff) + return (USAGE); + + cp.reason = (u_int8_t) (n & 0xff); + + case 1: + /* connection handle */ + if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff) + return (USAGE); + + cp.con_handle = (u_int16_t) (n & 0x0fff); + cp.con_handle = htole16(cp.con_handle); + break; + + default: + return (USAGE); + } + + /* send request and expect status response */ + n = sizeof(b); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, + NG_HCI_OCF_DISCON), + (char const *) &cp, sizeof(cp), b, &n) == ERROR) + return (ERROR); + + if (*b != 0x00) + return (FAILED); + + /* wait for event */ +again: + n = sizeof(b); + if (hci_recv(s, b, &n) == ERROR) + return (ERROR); + if (n < sizeof(*e)) { + errno = EIO; + return (ERROR); + } + + if (e->event == NG_HCI_EVENT_DISCON_COMPL) { + ng_hci_discon_compl_ep *ep = (ng_hci_discon_compl_ep *)(e + 1); + + if (ep->status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(ep->status), ep->status); + return (FAILED); + } + + fprintf(stdout, "Connection handle: %d\n", + le16toh(ep->con_handle)); + fprintf(stdout, "Reason: %s [%#02x]\n", + hci_status2str(ep->reason), ep->reason); + } else + goto again; + + return (OK); +} /* hci_diconnect */ + +/* Send Add_SCO_Connection command to the unit */ +static int +hci_add_sco_connection(int s, int argc, char **argv) +{ + int n; + char b[512]; + ng_hci_add_sco_con_cp cp; + ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b; + + /* Set defaults */ + memset(&cp, 0, sizeof(cp)); + cp.pkt_type = htole16(NG_HCI_PKT_HV1 | NG_HCI_PKT_HV2 | NG_HCI_PKT_HV3); + + /* parse command parameters */ + switch (argc) { + case 2: + /* packet type */ + if (sscanf(argv[0], "%x", &n) != 1) + return (USAGE); + + n &= (NG_HCI_PKT_HV1 | NG_HCI_PKT_HV2 | NG_HCI_PKT_HV3); + if (n == 0) + return (USAGE); + + cp.pkt_type = (u_int16_t) (n & 0x0fff); + cp.pkt_type = htole16(cp.pkt_type); + + case 1: + /* acl connection handle */ + if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff) + return (USAGE); + + cp.con_handle = (u_int16_t) (n & 0x0fff); + cp.con_handle = htole16(cp.con_handle); + break; + + default: + return (USAGE); + } + + /* send request and expect status response */ + n = sizeof(b); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, + NG_HCI_OCF_ADD_SCO_CON), + (char const *) &cp, sizeof(cp), b, &n) == ERROR) + return (ERROR); + + if (*b != 0x00) + return (FAILED); + + /* wait for event */ +again: + n = sizeof(b); + if (hci_recv(s, b, &n) == ERROR) + return (ERROR); + if (n < sizeof(*e)) { + errno = EIO; + return (ERROR); + } + + if (e->event == NG_HCI_EVENT_CON_COMPL) { + ng_hci_con_compl_ep *ep = (ng_hci_con_compl_ep *)(e + 1); + + if (ep->status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(ep->status), ep->status); + return (FAILED); + } + + fprintf(stdout, "BD_ADDR: %02x:%02x:%02x:%02x:%02x:%02x\n", + ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3], + ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]); + fprintf(stdout, "Connection handle: %d\n", + le16toh(ep->con_handle)); + fprintf(stdout, "Encryption mode: %s [%d]\n", + hci_encrypt2str(ep->encryption_mode, 0), + ep->encryption_mode); + } else + goto again; + + return (OK); +} /* Add_SCO_Connection */ + +/* Send Change_Connection_Packet_Type command to the unit */ +static int +hci_change_connection_packet_type(int s, int argc, char **argv) +{ + int n; + char b[512]; + ng_hci_change_con_pkt_type_cp cp; + ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b; + + switch (argc) { + case 2: + /* connection handle */ + if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff) + return (USAGE); + + cp.con_handle = (u_int16_t) (n & 0x0fff); + cp.con_handle = htole16(cp.con_handle); + + /* packet type */ + if (sscanf(argv[1], "%x", &n) != 1) + return (USAGE); + + cp.pkt_type = (u_int16_t) (n & 0xffff); + cp.pkt_type = htole16(cp.pkt_type); + break; + + default: + return (USAGE); + } + + /* send request and expect status response */ + n = sizeof(b); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, + NG_HCI_OCF_CHANGE_CON_PKT_TYPE), + (char const *) &cp, sizeof(cp), b, &n) == ERROR) + return (ERROR); + + if (*b != 0x00) + return (FAILED); + + /* wait for event */ +again: + n = sizeof(b); + if (hci_recv(s, b, &n) == ERROR) + return (ERROR); + if (n < sizeof(*e)) { + errno = EIO; + return (ERROR); + } + + if (e->event == NG_HCI_EVENT_CON_PKT_TYPE_CHANGED) { + ng_hci_con_pkt_type_changed_ep *ep = + (ng_hci_con_pkt_type_changed_ep *)(e + 1); + + if (ep->status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(ep->status), ep->status); + return (FAILED); + } + + fprintf(stdout, "Connection handle: %d\n", + le16toh(ep->con_handle)); + fprintf(stdout, "Packet type: %#04x\n", + le16toh(ep->pkt_type)); + } else + goto again; + + return (OK); +} /* hci_change_connection_packet_type */ + +/* Send Remote_Name_Request command to the unit */ +static int +hci_remote_name_request(int s, int argc, char **argv) +{ + int n0, n1, n2, n3, n4, n5; + char b[512]; + ng_hci_remote_name_req_cp cp; + ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b; + + /* parse command parameters */ + switch (argc) { + case 4: + /* BD_ADDR */ + if (sscanf(argv[0], "%x:%x:%x:%x:%x:%x", + &n5, &n4, &n3, &n2, &n1, &n0) != 6) + return (USAGE); + + cp.bdaddr.b[0] = (n0 & 0xff); + cp.bdaddr.b[1] = (n1 & 0xff); + cp.bdaddr.b[2] = (n2 & 0xff); + cp.bdaddr.b[3] = (n3 & 0xff); + cp.bdaddr.b[4] = (n4 & 0xff); + cp.bdaddr.b[5] = (n5 & 0xff); + + /* page_scan_rep_mode */ + if (sscanf(argv[1], "%d", &n0) != 1 || n0 < 0x00 || n0 > 0x02) + return (USAGE); + + cp.page_scan_rep_mode = (n0 & 0xff); + + /* page_scan_mode */ + if (sscanf(argv[2], "%d", &n0) != 1 || n0 < 0x00 || n0 > 0x03) + return (USAGE); + + cp.page_scan_mode = (n0 & 0xff); + + /* clock_offset */ + if (sscanf(argv[3], "%x", &n0) != 1) + return (USAGE); + + cp.clock_offset = (n0 & 0xffff); + cp.clock_offset = htole16(cp.clock_offset); + break; + + default: + return (USAGE); + } + + /* send request and expect status response */ + n0 = sizeof(b); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, + NG_HCI_OCF_REMOTE_NAME_REQ), + (char const *) &cp, sizeof(cp), b, &n0) == ERROR) + return (ERROR); + + if (*b != 0x00) + return (FAILED); + + /* wait for event */ +again: + n0 = sizeof(b); + if (hci_recv(s, b, &n0) == ERROR) + return (ERROR); + if (n0 < sizeof(*e)) { + errno = EIO; + return (ERROR); + } + + if (e->event == NG_HCI_EVENT_REMOTE_NAME_REQ_COMPL) { + ng_hci_remote_name_req_compl_ep *ep = + (ng_hci_remote_name_req_compl_ep *)(e + 1); + + if (ep->status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(ep->status), ep->status); + return (FAILED); + } + + fprintf(stdout, "BD_ADDR: %02x:%02x:%02x:%02x:%02x:%02x\n", + ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3], + ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]); + fprintf(stdout, "Name: %s\n", ep->name); + } else + goto again; + + return (OK); +} /* hci_remote_name_request */ + +/* Send Read_Remote_Supported_Features command to the unit */ +static int +hci_read_remote_supported_features(int s, int argc, char **argv) +{ + int n; + char b[512]; + ng_hci_read_remote_features_cp cp; + ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b; + char buffer[1024]; + + /* parse command parameters */ + switch (argc) { + case 1: + /* connecton handle */ + if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 0x0eff) + return (USAGE); + + cp.con_handle = (n & 0x0fff); + cp.con_handle = htole16(cp.con_handle); + break; + + default: + return (USAGE); + } + + /* send request and expect status response */ + n = sizeof(b); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, + NG_HCI_OCF_READ_REMOTE_FEATURES), + (char const *) &cp, sizeof(cp), b, &n) == ERROR) + return (ERROR); + + if (*b != 0x00) + return (FAILED); + + /* wait for event */ +again: + n = sizeof(b); + if (hci_recv(s, b, &n) == ERROR) + return (ERROR); + + if (n < sizeof(*e)) { + errno = EIO; + return (ERROR); + } + + if (e->event == NG_HCI_EVENT_READ_REMOTE_FEATURES_COMPL) { + ng_hci_read_remote_features_compl_ep *ep = + (ng_hci_read_remote_features_compl_ep *)(e + 1); + + if (ep->status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(ep->status), ep->status); + return (FAILED); + } + + fprintf(stdout, "Connection handle: %d\n", + le16toh(ep->con_handle)); + fprintf(stdout, "Features: "); + for (n = 0; n < sizeof(ep->features); n++) + fprintf(stdout, "%#02x ", ep->features[n]); + fprintf(stdout, "\n%s\n", hci_features2str(ep->features, + buffer, sizeof(buffer))); + } else + goto again; + + return (OK); +} /* hci_read_remote_supported_features */ + +/* Send Read_Remote_Version_Information command to the unit */ +static int +hci_read_remote_version_information(int s, int argc, char **argv) +{ + int n; + char b[512]; + ng_hci_read_remote_ver_info_cp cp; + ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b; + + /* parse command parameters */ + switch (argc) { + case 1: + /* connecton handle */ + if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 0x0eff) + return (USAGE); + + cp.con_handle = (n & 0x0fff); + cp.con_handle = htole16(cp.con_handle); + break; + + default: + return (USAGE); + } + + /* send request and expect status response */ + n = sizeof(b); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, + NG_HCI_OCF_READ_REMOTE_VER_INFO), + (char const *) &cp, sizeof(cp), b, &n) == ERROR) + return (ERROR); + + if (*b != 0x00) + return (FAILED); + + /* wait for event */ +again: + n = sizeof(b); + if (hci_recv(s, b, &n) == ERROR) + return (ERROR); + + if (n < sizeof(*e)) { + errno = EIO; + return (ERROR); + } + + if (e->event == NG_HCI_EVENT_READ_REMOTE_VER_INFO_COMPL) { + ng_hci_read_remote_ver_info_compl_ep *ep = + (ng_hci_read_remote_ver_info_compl_ep *)(e + 1); + + if (ep->status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(ep->status), ep->status); + return (FAILED); + } + + ep->manufacturer = le16toh(ep->manufacturer); + + fprintf(stdout, "Connection handle: %d\n", + le16toh(ep->con_handle)); + fprintf(stdout, "LMP version: %#02x\n", ep->lmp_version); + fprintf(stdout, "LMP sub-version: %#04x\n", + le16toh(ep->lmp_subversion)); + fprintf(stdout, "Manufacturer: %s [%#04x]\n", + hci_manufacturer2str(ep->manufacturer), + ep->manufacturer); + } else + goto again; + + return (OK); +} /* hci_read_remote_version_information */ + +/* Send Read_Clock_Offset command to the unit */ +static int +hci_read_clock_offset(int s, int argc, char **argv) +{ + int n; + char b[512]; + ng_hci_read_clock_offset_cp cp; + ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b; + + /* parse command parameters */ + switch (argc) { + case 1: + /* connecton handle */ + if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 0x0eff) + return (USAGE); + + cp.con_handle = (n & 0x0fff); + cp.con_handle = htole16(cp.con_handle); + break; + + default: + return (USAGE); + } + + /* send request and expect status response */ + n = sizeof(b); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, + NG_HCI_OCF_READ_CLOCK_OFFSET), + (char const *) &cp, sizeof(cp), b, &n) == ERROR) + return (ERROR); + + if (*b != 0x00) + return (FAILED); + + /* wait for event */ +again: + n = sizeof(b); + if (hci_recv(s, b, &n) == ERROR) + return (ERROR); + + if (n < sizeof(*e)) { + errno = EIO; + return (ERROR); + } + + if (e->event == NG_HCI_EVENT_READ_CLOCK_OFFSET_COMPL) { + ng_hci_read_clock_offset_compl_ep *ep = + (ng_hci_read_clock_offset_compl_ep *)(e + 1); + + if (ep->status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(ep->status), ep->status); + return (FAILED); + } + + fprintf(stdout, "Connection handle: %d\n", + le16toh(ep->con_handle)); + fprintf(stdout, "Clock offset: %#04x\n", + le16toh(ep->clock_offset)); + } else + goto again; + + return (OK); +} /* hci_read_clock_offset */ + +struct hci_command link_control_commands[] = { +{ +"inquiry <LAP> <inquiry_length> <num_reponses>", +"\nThis command will cause the Bluetooth unit to enter Inquiry Mode.\n" \ +"Inquiry Mode is used to discover other nearby Bluetooth units. The LAP\n" \ +"input parameter contains the LAP from which the inquiry access code shall\n" \ +"be derived when the inquiry procedure is made. The Inquiry_Length parameter\n"\ +"specifies the total duration of the Inquiry Mode and, when this time\n" \ +"expires, Inquiry will be halted. The Num_Responses parameter specifies the\n" \ +"number of responses that can be received before the Inquiry is halted.\n\n" \ +"\t<LAP> - xx:xx:xx; 9e:8b:33 (GIAC), 93:8b:00 (LDIAC)\n" \ +"\t<inquiry_length> - dd; total length == dd * 1.28 sec\n" \ +"\t<num_responses> - dd", +&hci_inquiry +}, +{ +"create_connection <BD_ADDR> <pkt> <rep_mode> <ps_mode> <clck_off> <role_sw>", +"" \ +"\t<BD_ADDR> - remote unit address\n\n" \ +"\t<pkt> - xxxx; packet type\n" \ +"" \ +"\t\tACL packets\n" \ +"\t\t-----------\n" \ +"\t\t0x0008 DM1\n" \ +"\t\t0x0010 DH1\n" \ +"\t\t0x0400 DM3\n" \ +"\t\t0x0800 DH3\n" \ +"\t\t0x4000 DM5\n" \ +"\t\t0x8000 DH5\n\n" \ +"" \ +"\trep_mode - d; page scan repetition mode\n" \ +"" \ +"\t\tPage scan repetition modes\n" \ +"\t\t--------------------------\n" \ +"\t\t0 Page scan repetition mode 0\n" \ +"\t\t1 Page scan repetition mode 1\n" \ +"\t\t2 Page scan repetition mode 2\n" \ +"\n" \ +"\tps_mode - d; Page scan mode\n" \ +"" \ +"\t\tPage scan modes\n" \ +"\t\t---------------\n" \ +"\t\t0 Mandatory page scan mode\n" \ +"\t\t1 Optional page scan mode1\n" \ +"\t\t2 Optional page scan mode2\n" \ +"\t\t3 Optional page scan mode3\n" \ +"\n" \ +"\tclck_off - dddd; clock offset. Use 0 if unknown\n\n" \ +"\trole_sw - d; allow (1) or deny role switch\n", +&hci_create_connection +}, +{ +"disconnect <connection_handle> <reason>", +"\nThe Disconnection command is used to terminate an existing connection.\n" \ +"The connection handle command parameter indicates which connection is to\n" \ +"be disconnected. The Reason command parameter indicates the reason for\n" \ +"ending the connection.\n\n" \ +"\t<connection_handle> - dddd; connection handle\n" \ +"\t<reason> - dd; reason; usually 19 (0x13) - user ended;\n" \ +"\t also 0x05, 0x13-0x15, 0x1A, 0x29", +&hci_disconnect +}, +{ +"add_sco_connection <acl connection handle> <packet type>", +"This command will cause the link manager to create a SCO connection using\n" \ +"the ACL connection specified by the connection handle command parameter.\n" \ +"The Link Manager will determine how the new connection is established. This\n"\ +"connection is determined by the current state of the device, its piconet,\n" \ +"and the state of the device to be connected. The packet type command parameter\n" \ +"specifies which packet types the Link Manager should use for the connection.\n"\ +"The Link Manager must only use the packet type(s) specified by the packet\n" \ +"type command parameter for sending HCI SCO data packets. Multiple packet\n" \ +"types may be specified for the packet type command parameter by performing\n" \ +"a bitwise OR operation of the different packet types. Note: An SCO connection\n" \ +"can only be created when an ACL connection already exists and when it is\n" \ +"not put in park mode.\n\n" \ +"\t<connection_handle> - dddd; ACL connection handle\n" \ +"\t<packet_type> - xxxx; packet type\n" \ +"" \ +"\t\tSCO packets\n" \ +"\t\t-----------\n" \ +"\t\t0x0020 HV1\n" \ +"\t\t0x0040 HV2\n" \ +"\t\t0x0080 HV3\n", +&hci_add_sco_connection +}, +{ +"change_connection_packet_type <connection_hande> <packet_type>", +"The Change_Connection_Packet_Type command is used to change which packet\n" \ +"types can be used for a connection that is currently established. This\n" \ +"allows current connections to be dynamically modified to support different\n" \ +"types of user data. The Packet_Type command parameter specifies which\n" \ +"packet types the Link Manager can use for the connection. Multiple packet\n" \ +"types may be specified for the Packet_Type command parameter by bitwise OR\n" \ +"operation of the different packet types.\n\n" \ +"\t<connection_handle> - dddd; connection handle\n" \ +"\t<packet_type> - xxxx; packet type mask\n" \ +"" \ +"\t\tACL packets\n" \ +"\t\t-----------\n" \ +"\t\t0x0008 DM1\n" \ +"\t\t0x0010 DH1\n" \ +"\t\t0x0400 DM3\n" \ +"\t\t0x0800 DH3\n" \ +"\t\t0x4000 DM5\n" \ +"\t\t0x8000 DH5\n\n" \ +"" \ +"\t\tSCO packets\n" \ +"\t\t-----------\n" \ +"\t\t0x0020 HV1\n" \ +"\t\t0x0040 HV2\n" \ +"\t\t0x0080 HV3\n" \ +"", +&hci_change_connection_packet_type +}, +{ +"remote_name_request <bdaddr> <ps_rep_mode> <ps_mode> <clock_offset>", +"\nThe Remote_Name_Request command is used to obtain the user-friendly\n" \ +"name of another Bluetooth unit.\n\n" \ +"\t<bdaddr> - xx:xx:xx:xx:xx:xx remote unit BD_ADDR\n" \ +"\t<ps_rep_mode> - dd; page scan repetition mode [0-2]\n" \ +"\t<ps_mode> - dd; page scan mode [0-3]\n" \ +"\t<clock_offset> - xxxx; clock offset [0 - 0xffff]", +&hci_remote_name_request +}, +{ +"read_remote_supported_features <connection_handle>", +"\nThis command requests a list of the supported features for the remote\n" \ +"unit identified by the connection handle parameter. The connection handle\n" \ +"must be a connection handle for an ACL connection.\n\n" \ +"\t<connection_handle> - dddd; connection handle", +&hci_read_remote_supported_features +}, +{ +"read_remote_version_information <connection_handle>", +"\nThis command will obtain the values for the version information for the\n" \ +"remote Bluetooth unit identified by the connection handle parameter. The\n" \ +"connection handle must be a connection handle for an ACL connection.\n\n" \ +"\t<conneciton_handle> - dddd; connection handle", +&hci_read_remote_version_information +}, +{ +"read_clock_offset <connection_handle>", +"\nThis command allows the Host to read clock offset to remote unit.\n" \ +"\t<conneciton_handle> - dddd; connection handle", +&hci_read_clock_offset +}, +{ +NULL, +}}; + diff --git a/usr.sbin/bluetooth/hccontrol/link_policy.c b/usr.sbin/bluetooth/hccontrol/link_policy.c new file mode 100644 index 0000000000000..dec9259dc8ec8 --- /dev/null +++ b/usr.sbin/bluetooth/hccontrol/link_policy.c @@ -0,0 +1,310 @@ +/* + * link_policy.c + * + * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: link_policy.c,v 1.3 2002/09/17 16:33:44 max Exp $ + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/endian.h> +#include <errno.h> +#include <ng_hci.h> +#include <stdio.h> +#include "hccontrol.h" + +/* Send Role Discovery to the unit */ +static int +hci_role_discovery(int s, int argc, char **argv) +{ + ng_hci_role_discovery_cp cp; + ng_hci_role_discovery_rp rp; + int n; + + /* parse command parameters */ + switch (argc) { + case 1: + /* connection handle */ + if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff) + return (USAGE); + + cp.con_handle = (u_int16_t) (n & 0x0fff); + cp.con_handle = htole16(cp.con_handle); + break; + + default: + return (USAGE); + } + + /* send request */ + n = sizeof(rp); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY, + NG_HCI_OCF_ROLE_DISCOVERY), + (char const *) &cp, sizeof(cp), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + fprintf(stdout, "Connection handle: %d\n", le16toh(rp.con_handle)); + fprintf(stdout, "Role: %s [%#x]\n", + (rp.role == NG_HCI_ROLE_MASTER)? "Master" : "Slave", rp.role); + + return (OK); +} /* hci_role_discovery */ + +/* Send Swith Role to the unit */ +static int +hci_switch_role(int s, int argc, char **argv) +{ + int n0, n1, n2, n3, n4, n5; + char b[512]; + ng_hci_switch_role_cp cp; + ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b; + + /* parse command parameters */ + switch (argc) { + case 2: + /* bdaddr */ + if (sscanf(argv[0], "%x:%x:%x:%x:%x:%x", + &n5, &n4, &n3, &n2, &n1, &n0) != 6) + return (USAGE); + + cp.bdaddr.b[0] = n0 & 0xff; + cp.bdaddr.b[1] = n1 & 0xff; + cp.bdaddr.b[2] = n2 & 0xff; + cp.bdaddr.b[3] = n3 & 0xff; + cp.bdaddr.b[4] = n4 & 0xff; + cp.bdaddr.b[5] = n5 & 0xff; + + /* role */ + if (sscanf(argv[1], "%d", &n0) != 1) + return (USAGE); + + cp.role = n0? NG_HCI_ROLE_SLAVE : NG_HCI_ROLE_MASTER; + break; + + default: + return (USAGE); + } + + /* send request and expect status response */ + n0 = sizeof(b); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY, + NG_HCI_OCF_SWITCH_ROLE), + (char const *) &cp, sizeof(cp), b, &n0) == ERROR) + return (ERROR); + + if (*b != 0x00) + return (FAILED); + + /* wait for event */ +again: + n0 = sizeof(b); + if (hci_recv(s, b, &n0) == ERROR) + return (ERROR); + if (n0 < sizeof(*e)) { + errno = EIO; + return (ERROR); + } + + if (e->event == NG_HCI_EVENT_ROLE_CHANGE) { + ng_hci_role_change_ep *ep = (ng_hci_role_change_ep *)(e + 1); + + if (ep->status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(ep->status), ep->status); + return (FAILED); + } + + fprintf(stdout, "BD_ADDR: %02x:%02x:%02x:%02x:%02x:%02x\n", + ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3], + ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]); + fprintf(stdout, "Role: %s [%#x]\n", + (ep->role == NG_HCI_ROLE_MASTER)? "Master" : "Slave", + ep->role); + } else + goto again; + + return (OK); +} /* hci_switch_role */ + +/* Send Read_Link_Policy_Settings command to the unit */ +static int +hci_read_link_policy_settings(int s, int argc, char **argv) +{ + ng_hci_read_link_policy_settings_cp cp; + ng_hci_read_link_policy_settings_rp rp; + int n; + + /* parse command parameters */ + switch (argc) { + case 1: + /* connection handle */ + if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff) + return (USAGE); + + cp.con_handle = (u_int16_t) (n & 0x0fff); + cp.con_handle = htole16(cp.con_handle); + break; + + default: + return (USAGE); + } + + /* send request */ + n = sizeof(rp); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY, + NG_HCI_OCF_READ_LINK_POLICY_SETTINGS), + (char const *) &cp, sizeof(cp), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + fprintf(stdout, "Connection handle: %d\n", le16toh(rp.con_handle)); + fprintf(stdout, "Link policy settings: %#x\n", le16toh(rp.settings)); + + return (OK); +} /* hci_read_link_policy_settings */ + +/* Send Write_Link_Policy_Settings command to the unit */ +static int +hci_write_link_policy_settings(int s, int argc, char **argv) +{ + ng_hci_write_link_policy_settings_cp cp; + ng_hci_write_link_policy_settings_rp rp; + int n; + + /* parse command parameters */ + switch (argc) { + case 2: + /* connection handle */ + if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff) + return (USAGE); + + cp.con_handle = (u_int16_t) (n & 0x0fff); + cp.con_handle = htole16(cp.con_handle); + + /* link policy settings */ + if (sscanf(argv[1], "%x", &n) != 1) + return (USAGE); + + cp.settings = (u_int16_t) (n & 0x0ffff); + cp.settings = htole16(cp.settings); + break; + + default: + return (USAGE); + } + + /* send request */ + n = sizeof(rp); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY, + NG_HCI_OCF_WRITE_LINK_POLICY_SETTINGS), + (char const *) &cp, sizeof(cp), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + return (OK); +} /* hci_write_link_policy_settings */ + +struct hci_command link_policy_commands[] = { +{ +"role_discovery <conection_handle>", +"\nThe Role_Discovery command is used for a Bluetooth device to determine\n" \ +"which role the device is performing for a particular Connection Handle.\n" \ +"The connection handle must be a connection handle for an ACL connection.\n\n" \ +"\t<connection_handle> - dddd; connection handle", +&hci_role_discovery +}, +{ +"switch_role <bdaddr> <role>", +"\nThe Switch_Role command is used for a Bluetooth device to switch the\n" \ +"current role the device is performing for a particular connection with\n" \ +"another specified Bluetooth device. The BD_ADDR command parameter indicates\n"\ +"for which connection the role switch is to be performed. The Role indicates\n"\ +"the requested new role that the local device performs. Note: the BD_ADDR\n" \ +"command parameter must specify a Bluetooth device for which a connection\n" +"already exists.\n\n" \ +"\t<bdaddr> - xx:xx:xx:xx:xx:xx; device bdaddr\n" \ +"\t<role> - dd; role; 0 - Master, 1 - Slave", +&hci_switch_role +}, +{ +"read_link_policy_settings <connection_handle>", +"\nThis command will read the Link Policy setting for the specified connection\n"\ +"handle. The link policy settings parameter determines the behavior of the\n" \ +"local Link Manager when it receives a request from a remote device or it\n" \ +"determines itself to change the master-slave role or to enter the hold,\n" \ +"sniff, or park mode. The local Link Manager will automatically accept or\n" \ +"reject such a request from the remote device, and may even autonomously\n" \ +"request itself, depending on the value of the link policy settings parameter\n"\ +"for the corresponding connection handle. The connection handle must be a\n" \ +"connection handle for an ACL connection.\n\n" \ +"\t<connection_handle> - dddd; connection handle", +&hci_read_link_policy_settings +}, +{ +"write_link_policy_settings <connection_handle> <settings>", +"\nThis command will write the Link Policy setting for the specified connection\n"\ +"handle. The link policy settings parameter determines the behavior of the\n" \ +"local Link Manager when it receives a request from a remote device or it\n" \ +"determines itself to change the master-slave role or to enter the hold,\n" \ +"sniff, or park mode. The local Link Manager will automatically accept or\n" \ +"reject such a request from the remote device, and may even autonomously\n" \ +"request itself, depending on the value of the link policy settings parameter\n"\ +"for the corresponding connection handle. The connection handle must be a\n" \ +"connection handle for an ACL connection. Multiple Link Manager policies may\n"\ +"be specified for the link policy settings parameter by performing a bitwise\n"\ +"OR operation of the different activity types.\n\n" \ +"\t<connection_handle> - dddd; connection handle\n" \ +"\t<settings> - xxxx; settings\n" \ +"\t\t0x0000 - Disable All LM Modes (Default)\n" \ +"\t\t0x0001 - Enable Master Slave Switch\n" \ +"\t\t0x0002 - Enable Hold Mode\n" \ +"\t\t0x0004 - Enable Sniff Mode\n" \ +"\t\t0x0008 - Enable Park Mode\n", +&hci_write_link_policy_settings +}, +{ +NULL, +}}; + diff --git a/usr.sbin/bluetooth/hccontrol/node.c b/usr.sbin/bluetooth/hccontrol/node.c new file mode 100644 index 0000000000000..b7ff33ac07c49 --- /dev/null +++ b/usr.sbin/bluetooth/hccontrol/node.c @@ -0,0 +1,518 @@ +/* + * node.c + * + * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: node.c,v 1.8 2002/11/12 22:33:17 max Exp $ + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <bitstring.h> +#include <errno.h> +#include <ng_hci.h> +#include <ng_l2cap.h> +#include <ng_btsocket.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "hccontrol.h" + +/* Send Read_Node_State command to the node */ +static int +hci_read_node_state(int s, int argc, char **argv) +{ + struct ng_btsocket_hci_raw_node_state r; + + memset(&r, 0, sizeof(r)); + if (ioctl(s, SIOC_HCI_RAW_NODE_GET_STATE, &r, sizeof(r)) < 0) + return (ERROR); + + fprintf(stdout, "Node: %s\nState: %#x\n", r.hci_node, r.state); + + return (OK); +} /* hci_read_node_state */ + +/* Send Intitialize command to the node */ +static int +hci_node_initialize(int s, int argc, char **argv) +{ + struct ng_btsocket_hci_raw_node_init r; + + memset(&r, 0, sizeof(r)); + if (ioctl(s, SIOC_HCI_RAW_NODE_INIT, &r, sizeof(r)) < 0) + return (ERROR); + + return (OK); +} /* hci_node_initialize */ + +/* Send Read_Debug_Level command to the node */ +static int +hci_read_debug_level(int s, int argc, char **argv) +{ + struct ng_btsocket_hci_raw_node_debug r; + + memset(&r, 0, sizeof(r)); + if (ioctl(s, SIOC_HCI_RAW_NODE_GET_DEBUG, &r, sizeof(r)) < 0) + return (ERROR); + + fprintf(stdout, "Node: %s\nDebug level: %d\n", r.hci_node, r.debug); + + return (OK); +} /* hci_read_debug_level */ + +/* Send Write_Debug_Level command to the node */ +static int +hci_write_debug_level(int s, int argc, char **argv) +{ + struct ng_btsocket_hci_raw_node_debug r; + + memset(&r, 0, sizeof(r)); + switch (argc) { + case 1: + r.debug = atoi(argv[0]); + break; + + default: + return (USAGE); + } + + if (ioctl(s, SIOC_HCI_RAW_NODE_SET_DEBUG, &r, sizeof(r)) < 0) + return (ERROR); + + return (OK); +} /* hci_write_debug_level */ + +/* Send Read_Node_Buffer_Size command to the node */ +static int +hci_read_node_buffer_size(int s, int argc, char **argv) +{ + struct ng_btsocket_hci_raw_node_buffer r; + + memset(&r, 0, sizeof(r)); + if (ioctl(s, SIOC_HCI_RAW_NODE_GET_BUFFER, &r, sizeof(r)) < 0) + return (ERROR); + + fprintf(stdout, "Node: %s\n", + r.hci_node); + fprintf(stdout, "Number of free command buffers: %d\n", + r.buffer.cmd_free); + fprintf(stdout, "Max. ACL packet size: %d\n", + r.buffer.acl_size); + fprintf(stdout, "Numbef of free ACL buffers: %d\n", + r.buffer.acl_free); + fprintf(stdout, "Total number of ACL buffers: %d\n", + r.buffer.acl_pkts); + fprintf(stdout, "Max. SCO packet size: %d\n", + r.buffer.sco_size); + fprintf(stdout, "Numbef of free SCO buffers: %d\n", + r.buffer.sco_free); + fprintf(stdout, "Total number of SCO buffers: %d\n", + r.buffer.sco_pkts); + + return (OK); +} /* hci_read_node_buffer_size */ + +/* Send Read_Node_BD_ADDR command to the node */ +static int +hci_read_node_bd_addr(int s, int argc, char **argv) +{ + struct ng_btsocket_hci_raw_node_bdaddr r; + + memset(&r, 0, sizeof(r)); + if (ioctl(s, SIOC_HCI_RAW_NODE_GET_BDADDR, &r, sizeof(r)) < 0) + return (ERROR); + + fprintf(stdout, "Node: %s\n", r.hci_node); + fprintf(stdout, "BD_ADDR: %02x:%02x:%02x:%02x:%02x:%02x\n", + r.bdaddr.b[5], r.bdaddr.b[4], r.bdaddr.b[3], + r.bdaddr.b[2], r.bdaddr.b[1], r.bdaddr.b[0]); + + return (OK); +} /* hci_read_node_bd_addr */ + +/* Send Read_Node_Features command to the node */ +static int +hci_read_node_features(int s, int argc, char **argv) +{ + struct ng_btsocket_hci_raw_node_features r; + int n; + char buffer[1024]; + + memset(&r, 0, sizeof(r)); + if (ioctl(s, SIOC_HCI_RAW_NODE_GET_FEATURES, &r, sizeof(r)) < 0) + return (ERROR); + + fprintf(stdout, "Node: %s\nFeatures: ", r.hci_node); + for (n = 0; n < sizeof(r.features)/sizeof(r.features[0]); n++) + fprintf(stdout, "%#02x ", r.features[n]); + fprintf(stdout, "\n%s\n", hci_features2str(r.features, + buffer, sizeof(buffer))); + + return (OK); +} /* hci_read_node_features */ + +/* Send Read_Node_Stat command to the node */ +static int +hci_read_node_stat(int s, int argc, char **argv) +{ + struct ng_btsocket_hci_raw_node_stat r; + + memset(&r, 0, sizeof(r)); + if (ioctl(s, SIOC_HCI_RAW_NODE_GET_STAT, &r, sizeof(r)) < 0) + return (ERROR); + + fprintf(stdout, "Node: %s\n", r.hci_node); + fprintf(stdout, "Commands sent: %d\n", r.stat.cmd_sent); + fprintf(stdout, "Events received: %d\n", r.stat.evnt_recv); + fprintf(stdout, "ACL packets received: %d\n", r.stat.acl_recv); + fprintf(stdout, "ACL packets sent: %d\n", r.stat.acl_sent); + fprintf(stdout, "SCO packets received: %d\n", r.stat.sco_recv); + fprintf(stdout, "SCO packets sent: %d\n", r.stat.sco_sent); + fprintf(stdout, "Bytes received: %d\n", r.stat.bytes_recv); + fprintf(stdout, "Bytes sent: %d\n", r.stat.bytes_sent); + + return (OK); +} /* hci_read_node_stat */ + +/* Send Reset_Node_Stat command to the node */ +static int +hci_reset_node_stat(int s, int argc, char **argv) +{ + struct ng_btsocket_hci_raw_node_reset_stat r; + + memset(&r, 0, sizeof(r)); + if (ioctl(s, SIOC_HCI_RAW_NODE_RESET_STAT, &r, sizeof(r)) < 0) + return (ERROR); + + return (OK); +} /* hci_reset_node_stat */ + +/* Send Flush_Neighbor_Cache command to the node */ +static int +hci_flush_neighbor_cache(int s, int argc, char **argv) +{ + struct ng_btsocket_hci_raw_node_flush_neighbor_cache r; + + memset(&r, 0, sizeof(r)); + if (ioctl(s, SIOC_HCI_RAW_NODE_FLUSH_NEIGHBOR_CACHE, + &r, sizeof(r)) < 0) + return (ERROR); + + return (OK); +} /* hci_flush_neighbor_cache */ + +/* Send Read_Neighbor_Cache command to the node */ +static int +hci_read_neighbor_cache(int s, int argc, char **argv) +{ + struct ng_btsocket_hci_raw_node_neighbor_cache r; + int n, error = OK; + + memset(&r, 0, sizeof(r)); + r.num_entries = NG_HCI_MAX_NEIGHBOR_NUM; + r.entries = calloc(NG_HCI_MAX_NEIGHBOR_NUM, + sizeof(ng_hci_node_neighbor_cache_entry_ep)); + if (r.entries == NULL) { + errno = ENOMEM; + return (ERROR); + } + + if (ioctl(s, SIOC_HCI_RAW_NODE_GET_NEIGHBOR_CACHE, &r, + sizeof(r)) < 0) { + error = ERROR; + goto out; + } + + fprintf(stdout, "Neighbor cache for the node: %s\n", r.hci_node); + fprintf(stdout, +"BD_ADDR " \ +"Features " \ +"Clock offset " \ +"Page scan " \ +"Rep. scan\n"); + + for (n = 0; n < r.num_entries; n++) { + fprintf(stdout, +"%02x:%02x:%02x:%02x:%02x:%02x " \ +"%02x %02x %02x %02x %02x %02x %02x %02x " \ +"%#12x " \ +"%#9x " \ +"%#9x\n", + r.entries[n].bdaddr.b[5], r.entries[n].bdaddr.b[4], + r.entries[n].bdaddr.b[3], r.entries[n].bdaddr.b[2], + r.entries[n].bdaddr.b[1], r.entries[n].bdaddr.b[0], + r.entries[n].features[0], r.entries[n].features[1], + r.entries[n].features[2], r.entries[n].features[3], + r.entries[n].features[4], r.entries[n].features[5], + r.entries[n].features[6], r.entries[n].features[7], + r.entries[n].clock_offset, r.entries[n].page_scan_mode, + r.entries[n].page_scan_rep_mode); + } +out: + free(r.entries); + + return (error); +} /* hci_read_neightbor_cache */ + +/* Send Read_Connection_List command to the node */ +static int +hci_read_connection_list(int s, int argc, char **argv) +{ + struct ng_btsocket_hci_raw_con_list r; + int n, error = OK; + + memset(&r, 0, sizeof(r)); + r.num_connections = NG_HCI_MAX_CON_NUM; + r.connections = calloc(NG_HCI_MAX_CON_NUM, sizeof(ng_hci_node_con_ep)); + if (r.connections == NULL) { + errno = ENOMEM; + return (ERROR); + } + + if (ioctl(s, SIOC_HCI_RAW_NODE_GET_CON_LIST, &r, sizeof(r)) < 0) { + error = ERROR; + goto out; + } + + fprintf(stdout, "Connections list for the node: %s\n", r.hci_node); + fprintf(stdout, +"Remote BD_ADDR " \ +"Handle " \ +"Type " \ +"Mode " \ +"Role " \ +"Encrypt " \ +"Pending " \ +"Queue " \ +"State\n"); + + for (n = 0; n < r.num_connections; n++) { + fprintf(stdout, +"%02x:%02x:%02x:%02x:%02x:%02x " \ +"%6d " \ +"%4.4s " \ +"%4d " \ +"%4.4s " \ +"%7.7s " \ +"%7d " \ +"%5d " \ +"%s\n", + r.connections[n].bdaddr.b[5], + r.connections[n].bdaddr.b[4], + r.connections[n].bdaddr.b[3], + r.connections[n].bdaddr.b[2], + r.connections[n].bdaddr.b[1], + r.connections[n].bdaddr.b[0], + r.connections[n].con_handle, + (r.connections[n].link_type == NG_HCI_LINK_ACL)? + "ACL" : "SCO", + r.connections[n].mode, + (r.connections[n].role == NG_HCI_ROLE_MASTER)? + "MAST" : "SLAV", + hci_encrypt2str(r.connections[n].encryption_mode, 1), + r.connections[n].pending, + r.connections[n].queue_len, + hci_con_state2str(r.connections[n].state)); + } +out: + free(r.connections); + + return (error); +} /* hci_read_connection_list */ + +/* Send Read_Link_Policy_Settings_Mask command to the node */ +int +hci_read_link_policy_settings_mask(int s, int argc, char **argv) +{ + struct ng_btsocket_hci_raw_node_link_policy_mask r; + + memset(&r, 0, sizeof(r)); + if (ioctl(s, SIOC_HCI_RAW_NODE_GET_LINK_POLICY_MASK, &r, sizeof(r)) < 0) + return (ERROR); + + fprintf(stdout, "Node: %s\nLink Policy Settings mask: %#04x\n", + r.hci_node, r.policy_mask); + + return (OK); +} /* hci_read_link_policy_settings_mask */ + +/* Send Write_Link_Policy_Settings_Mask command to the node */ +int +hci_write_link_policy_settings_mask(int s, int argc, char **argv) +{ + struct ng_btsocket_hci_raw_node_link_policy_mask r; + int m; + + memset(&r, 0, sizeof(r)); + + switch (argc) { + case 1: + if (sscanf(argv[0], "%x", &m) != 1) + return (USAGE); + + r.policy_mask = (m & 0xffff); + break; + + default: + return (USAGE); + } + + if (ioctl(s, SIOC_HCI_RAW_NODE_SET_LINK_POLICY_MASK, &r, sizeof(r)) < 0) + return (ERROR); + + return (OK); +} /* hci_write_link_policy_settings_mask */ + +/* Send Read_Packet_Mask command to the node */ +int +hci_read_packet_mask(int s, int argc, char **argv) +{ + struct ng_btsocket_hci_raw_node_packet_mask r; + + memset(&r, 0, sizeof(r)); + if (ioctl(s, SIOC_HCI_RAW_NODE_GET_PACKET_MASK, &r, sizeof(r)) < 0) + return (ERROR); + + fprintf(stdout, "Node: %s\nPacket mask: %#04x\n", + r.hci_node, r.packet_mask); + + return (OK); +} /* hci_read_packet_mask */ + +/* Send Write_Packet_Mask command to the node */ +int +hci_write_packet_mask(int s, int argc, char **argv) +{ + struct ng_btsocket_hci_raw_node_packet_mask r; + int m; + + memset(&r, 0, sizeof(r)); + + switch (argc) { + case 1: + if (sscanf(argv[0], "%x", &m) != 1) + return (USAGE); + + r.packet_mask = (m & 0xffff); + break; + + default: + return (USAGE); + } + + if (ioctl(s, SIOC_HCI_RAW_NODE_SET_PACKET_MASK, &r, sizeof(r)) < 0) + return (ERROR); + + return (OK); +} /* hci_write_packet_mask */ + +struct hci_command node_commands[] = { +{ +"read_node_state", +"Get HCI node state", +&hci_read_node_state +}, +{ +"initialize", +"Initialize HCI node", +&hci_node_initialize +}, +{ +"read_debug_level", +"Read HCI node debug level", +&hci_read_debug_level +}, +{ +"write_debug_level <level>", +"Write HCI node debug level", +&hci_write_debug_level +}, +{ +"read_node_buffer_size", +"Read HCI node buffer information", +&hci_read_node_buffer_size +}, +{ +"read_node_bd_addr", +"Read HCI node BD_ADDR", +&hci_read_node_bd_addr +}, +{ +"read_node_features", +"Read HCI node features", +&hci_read_node_features +}, +{ +"read_node_stat", +"Read HCI node statistic information", +&hci_read_node_stat +}, +{ +"reset_node_stat", +"Reset HCI node statistic information", +&hci_reset_node_stat +}, +{ +"flush_neighbor_cache", +"Flush HCI node neighbor cache", +&hci_flush_neighbor_cache +}, +{ +"read_neighbor_cache", +"Read HCI node neighbor cache", +&hci_read_neighbor_cache +}, +{ +"read_connection_list", +"Read connection list", +&hci_read_connection_list +}, +{ +"read_node_link_policy_settings_mask", +"Read Link Policy Settinngs mask for the node", +&hci_read_link_policy_settings_mask +}, +{ +"write_node_link_policy_settings_mask <policy_mask>", +"Write Link Policy Settinngs mask for the node. Policy mask - xxxx", +&hci_write_link_policy_settings_mask +}, +{ +"read_node_packet_mask", +"Read Packet mask for the node", +&hci_read_packet_mask +}, +{ +"write_node_packet_mask <packet_mask>", +"Write Packet mask for the node. Packet mask - xxxx", +&hci_write_packet_mask +}, +{ +NULL, +}}; + diff --git a/usr.sbin/bluetooth/hccontrol/send_recv.c b/usr.sbin/bluetooth/hccontrol/send_recv.c new file mode 100644 index 0000000000000..aff6f9341c695 --- /dev/null +++ b/usr.sbin/bluetooth/hccontrol/send_recv.c @@ -0,0 +1,184 @@ +/* + * send_recv.c + * + * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: send_recv.c,v 1.4 2002/09/04 21:31:30 max Exp $ + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/endian.h> +#include <assert.h> +#include <errno.h> +#include <ng_hci.h> +#include <string.h> +#include <unistd.h> +#include "hccontrol.h" + +/* Send HCI request to the unit */ +int +hci_request(int s, int opcode, char const *cp, int cp_size, char *rp, int *rp_size) +{ + char buffer[512]; + int n; + ng_hci_cmd_pkt_t *c = (ng_hci_cmd_pkt_t *) buffer; + ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) buffer; + + assert(rp != NULL); + assert(*rp_size != NULL); + assert(*rp_size > 0); + + c->type = NG_HCI_CMD_PKT; + c->opcode = (u_int16_t) opcode; + c->opcode = htole16(c->opcode); + + if (cp != NULL) { + assert(0 < cp_size && cp_size <= NG_HCI_CMD_PKT_SIZE); + + c->length = (u_int8_t) cp_size; + memcpy(buffer + sizeof(*c), cp, cp_size); + } else + c->length = 0; + + if (hci_send(s, buffer, sizeof(*c) + cp_size) == ERROR) + return (ERROR); + +again: + n = sizeof(buffer); + if (hci_recv(s, buffer, &n) == ERROR) + return (ERROR); + + if (n < sizeof(*e)) { + errno = EMSGSIZE; + return (ERROR); + } + + if (e->type != NG_HCI_EVENT_PKT) { + errno = EIO; + return (ERROR); + } + + switch (e->event) { + case NG_HCI_EVENT_COMMAND_COMPL: { + ng_hci_command_compl_ep *cc = + (ng_hci_command_compl_ep *)(e + 1); + + cc->opcode = le16toh(cc->opcode); + + if (cc->opcode == 0x0000 || cc->opcode != opcode) + goto again; + + n -= (sizeof(*e) + sizeof(*cc)); + if (n < *rp_size) + *rp_size = n; + + memcpy(rp, buffer + sizeof(*e) + sizeof(*cc), *rp_size); + } break; + + case NG_HCI_EVENT_COMMAND_STATUS: { + ng_hci_command_status_ep *cs = + (ng_hci_command_status_ep *)(e + 1); + + cs->opcode = le16toh(cs->opcode); + + if (cs->opcode == 0x0000 || cs->opcode != opcode) + goto again; + + *rp_size = 1; + *rp = cs->status; + } break; + + default: + goto again; + } + + return (OK); +} /* hci_request */ + +/* Send simple HCI request - Just HCI command packet (no parameters) */ +int +hci_simple_request(int s, int opcode, char *rp, int *rp_size) +{ + return (hci_request(s, opcode, NULL, 0, rp, rp_size)); +} /* hci_simple_request */ + +/* Send HCI data to the unit */ +int +hci_send(int s, char const *buffer, int size) +{ + assert(buffer != NULL); + assert(size >= sizeof(ng_hci_cmd_pkt_t)); + assert(size <= sizeof(ng_hci_cmd_pkt_t) + NG_HCI_CMD_PKT_SIZE); + + if (send(s, buffer, size, 0) < 0) + return (ERROR); + + return (OK); +} /* hci_send */ + +/* Receive HCI data from the unit */ +int +hci_recv(int s, char *buffer, int *size) +{ + struct timeval tv; + fd_set rfd; + int n; + + assert(buffer != NULL); + assert(size != NULL); + assert(*size > sizeof(ng_hci_event_pkt_t)); + +again: + FD_ZERO(&rfd); + FD_SET(s, &rfd); + + tv.tv_sec = timeout; + tv.tv_usec = 0; + + n = select(s + 1, &rfd, NULL, NULL, &tv); + if (n <= 0) { + if (n < 0) { + if (errno == EINTR) + goto again; + } else + errno = ETIMEDOUT; + + return (ERROR); + } + + assert(FD_ISSET(s, &rfd)); + + n = recv(s, buffer, *size, 0); + if (n < 0) + return (ERROR); + + *size = n; + + return (OK); +} /* hci_recv */ + diff --git a/usr.sbin/bluetooth/hccontrol/status.c b/usr.sbin/bluetooth/hccontrol/status.c new file mode 100644 index 0000000000000..c857349132a11 --- /dev/null +++ b/usr.sbin/bluetooth/hccontrol/status.c @@ -0,0 +1,245 @@ +/* + * status.c + * + * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: status.c,v 1.2 2002/09/06 18:52:41 max Exp $ + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/endian.h> +#include <errno.h> +#include <ng_hci.h> +#include <stdio.h> +#include "hccontrol.h" + +/* Send Read_Failed_Contact_Counter command to the unit */ +static int +hci_read_failed_contact_counter(int s, int argc, char **argv) +{ + ng_hci_read_failed_contact_cntr_cp cp; + ng_hci_read_failed_contact_cntr_rp rp; + int n; + + switch (argc) { + case 1: + /* connection handle */ + if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff) + return (USAGE); + + cp.con_handle = (u_int16_t) (n & 0x0fff); + cp.con_handle = htole16(cp.con_handle); + break; + + default: + return (USAGE); + } + + /* send command */ + n = sizeof(rp); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_STATUS, + NG_HCI_OCF_READ_FAILED_CONTACT_CNTR), + (char const *) &cp, sizeof(cp), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + fprintf(stdout, "Connection handle: %d\n", le16toh(rp.con_handle)); + fprintf(stdout, "Failed contact counter: %d\n", le16toh(rp.counter)); + + return (OK); +} /* hci_read_failed_contact_counter */ + +/* Send Reset_Failed_Contact_Counter command to the unit */ +static int +hci_reset_failed_contact_counter(int s, int argc, char **argv) +{ + ng_hci_reset_failed_contact_cntr_cp cp; + ng_hci_reset_failed_contact_cntr_rp rp; + int n; + + switch (argc) { + case 1: + /* connection handle */ + if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff) + return (USAGE); + + cp.con_handle = (u_int16_t) (n & 0x0fff); + cp.con_handle = htole16(cp.con_handle); + break; + + default: + return (USAGE); + } + + /* send command */ + n = sizeof(rp); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_STATUS, + NG_HCI_OCF_RESET_FAILED_CONTACT_CNTR), + (char const *) &cp, sizeof(cp), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + return (OK); +} /* hci_reset_failed_contact_counter */ + +/* Sent Get_Link_Quality command to the unit */ +static int +hci_get_link_quality(int s, int argc, char **argv) +{ + ng_hci_get_link_quality_cp cp; + ng_hci_get_link_quality_rp rp; + int n; + + switch (argc) { + case 1: + /* connection handle */ + if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff) + return (USAGE); + + cp.con_handle = (u_int16_t) (n & 0x0fff); + cp.con_handle = htole16(cp.con_handle); + break; + + default: + return (USAGE); + } + + /* send command */ + n = sizeof(rp); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_STATUS, + NG_HCI_OCF_GET_LINK_QUALITY), + (char const *) &cp, sizeof(cp), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + fprintf(stdout, "Connection handle: %d\n", le16toh(rp.con_handle)); + fprintf(stdout, "Link quality: %d\n", le16toh(rp.quality)); + + return (OK); +} /* hci_get_link_quality */ + +/* Send Read_RSSI command to the unit */ +static int +hci_read_rssi(int s, int argc, char **argv) +{ + ng_hci_read_rssi_cp cp; + ng_hci_read_rssi_rp rp; + int n; + + switch (argc) { + case 1: + /* connection handle */ + if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff) + return (USAGE); + + cp.con_handle = (u_int16_t) (n & 0x0fff); + cp.con_handle = htole16(cp.con_handle); + break; + + default: + return (USAGE); + } + + /* send command */ + n = sizeof(rp); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_STATUS, + NG_HCI_OCF_READ_RSSI), + (char const *) &cp, sizeof(cp), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + fprintf(stdout, "Connection handle: %d\n", le16toh(rp.con_handle)); + fprintf(stdout, "RSSI: %d dB\n", (int) rp.rssi); + + return (OK); +} /* hci_read_rssi */ + +struct hci_command status_commands[] = { +{ +"read_failed_contact_counter <connection_handle>", +"\nThis command will read the value for the Failed_Contact_Counter\n" \ +"parameter for a particular ACL connection to another device.\n\n" \ +"\t<connection_handle> - dddd; ACL connection handle\n", +&hci_read_failed_contact_counter +}, +{ +"reset_failed_contact_counter <connection_handle>", +"\nThis command will reset the value for the Failed_Contact_Counter\n" \ +"parameter for a particular ACL connection to another device.\n\n" \ +"\t<connection_handle> - dddd; ACL connection handle\n", +&hci_reset_failed_contact_counter +}, +{ +"get_link_quality <connection_handle>", +"\nThis command will return the value for the Link_Quality for the\n" \ +"specified ACL connection handle. This command will return a Link_Quality\n" \ +"value from 0-255, which represents the quality of the link between two\n" \ +"Bluetooth devices. The higher the value, the better the link quality is.\n" \ +"Each Bluetooth module vendor will determine how to measure the link quality." \ +"\n\n" \ +"\t<connection_handle> - dddd; ACL connection handle\n", +&hci_get_link_quality +}, +{ +"read_rssi <connection_handle>", +"\nThis command will read the value for the difference between the\n" \ +"measured Received Signal Strength Indication (RSSI) and the limits of\n" \ +"the Golden Receive Power Range for a ACL connection handle to another\n" \ +"Bluetooth device. Any positive RSSI value returned by the Host Controller\n" \ +"indicates how many dB the RSSI is above the upper limit, any negative\n" \ +"value indicates how many dB the RSSI is below the lower limit. The value\n" \ +"zero indicates that the RSSI is inside the Golden Receive Power Range.\n\n" \ +"\t<connection_handle> - dddd; ACL connection handle\n", +&hci_read_rssi +}, +{ +NULL, +}}; + diff --git a/usr.sbin/bluetooth/hccontrol/util.c b/usr.sbin/bluetooth/hccontrol/util.c new file mode 100644 index 0000000000000..51868b4c7698e --- /dev/null +++ b/usr.sbin/bluetooth/hccontrol/util.c @@ -0,0 +1,350 @@ +/* + * util.c + * + * Copyright (c) 2001 Maksim Yevmenkin <m_evmenkin@yahoo.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: util.c,v 1.2 2002/09/12 18:19:43 max Exp $ + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <string.h> + +#define SIZE(x) (sizeof((x))/sizeof((x)[0])) + +char const * const +hci_link2str(int link_type) +{ + static char const * const t[] = { + /* NG_HCI_LINK_SCO */ "SCO", + /* NG_HCI_LINK_ACL */ "ACL" + }; + + return (link_type >= SIZE(t)? "?" : t[link_type]); +} /* hci_link2str */ + +char const * const +hci_pin2str(int type) +{ + static char const * const t[] = { + /* 0x00 */ "Variable PIN", + /* 0x01 */ "Fixed PIN" + }; + + return (type >= SIZE(t)? "?" : t[type]); +} /* hci_pin2str */ + +char const * const +hci_scan2str(int scan) +{ + static char const * const t[] = { + /* 0x00 */ "No Scan enabled", + /* 0x01 */ "Inquiry Scan enabled. Page Scan disabled", + /* 0x02 */ "Inquiry Scan disabled. Page Scan enabled", + /* 0x03 */ "Inquiry Scan enabled. Page Scan enabled" + }; + + return (scan >= SIZE(t)? "?" : t[scan]); +} /* hci_scan2str */ + +char const * const +hci_encrypt2str(int encrypt, int brief) +{ + static char const * const t[] = { + /* 0x00 */ "Disabled", + /* 0x01 */ "Only for point-to-point packets", + /* 0x02 */ "Both point-to-point and broadcast packets" + }; + + static char const * const t1[] = { + /* NG_HCI_ENCRYPTION_MODE_NONE */ "NONE", + /* NG_HCI_ENCRYPTION_MODE_P2P */ "P2P", + /* NG_HCI_ENCRYPTION_MODE_ALL */ "ALL", + }; + + if (brief) + return (encrypt >= SIZE(t1)? "?" : t1[encrypt]); + + return (encrypt >= SIZE(t)? "?" : t[encrypt]); +} /* hci_encrypt2str */ + +char const * const +hci_coding2str(int coding) +{ + static char const * const t[] = { + /* 0x00 */ "Linear", + /* 0x01 */ "u-law", + /* 0x02 */ "A-law", + /* 0x03 */ "Reserved" + }; + + return (coding >= SIZE(t)? "?" : t[coding]); +} /* hci_coding2str */ + +char const * const +hci_vdata2str(int data) +{ + static char const * const t[] = { + /* 0x00 */ "1's complement", + /* 0x01 */ "2's complement", + /* 0x02 */ "Sign-Magnitude", + /* 0x03 */ "Reserved" + }; + + return (data >= SIZE(t)? "?" : t[data]); +} /* hci_vdata2str */ + +char const * const +hci_hmode2str(int mode, char *buffer, int size) +{ + static char const * const t[] = { + /* 0x01 */ "Suspend Page Scan ", + /* 0x02 */ "Suspend Inquiry Scan ", + /* 0x04 */ "Suspend Periodic Inquiries " + }; + + if (buffer != NULL && size > 0) { + int n; + + memset(buffer, 0, size); + for (n = 0; n < SIZE(t); n++) { + int len = strlen(buffer); + + if (len >= size) + break; + if (mode & (1 << n)) + strncat(buffer, t[n], size - len); + } + } + + return (buffer); +} /* hci_hmode2str */ + +char const * const +hci_ver2str(int ver) +{ + static char const * const t[] = { + /* 0x00 */ "v1.0B", + /* 0x01 */ "v1.1" + }; + + return (ver >= SIZE(t)? "?" : t[ver]); +} /* hci_ver2str */ + +char const * const +hci_manufacturer2str(int m) +{ + static char const * const t[] = { + /* 0000 */ "Ericsson Mobile Comunications", + /* 0001 */ "Nokia Mobile Phones", + /* 0002 */ "Intel Corp.", + /* 0003 */ "IBM Corp.", + /* 0004 */ "Toshiba Corp.", + /* 0005 */ "3Com", + /* 0006 */ "Microsoft", + /* 0007 */ "Lucent", + /* 0008 */ "Motorola", + /* 0009 */ "Infineon Technologies AG", + /* 0010 */ "Cambridge Silicon Radio", + /* 0011 */ "Silicon Wave", + /* 0012 */ "Digianswer A/S", + /* 0013 */ "Texas Instruments Inc.", + /* 0014 */ "Parthus Technologies Inc.", + /* 0015 */ "Broadcom Corporation", + /* 0016 */ "Mitel Semiconductor", + /* 0017 */ "Widcomm, Inc.", + /* 0018 */ "Telencomm Inc.", + /* 0019 */ "Atmel Corporation", + /* 0020 */ "Mitsubishi Electric Corporation", + /* 0021 */ "RTX Telecom A/S", + /* 0022 */ "KC Technology Inc.", + /* 0023 */ "Newlogic", + /* 0024 */ "Transilica, Inc.", + /* 0025 */ "Rohde & Schwartz GmbH & Co. KG", + /* 0026 */ "TTPCom Limited", + /* 0027 */ "Signia Technologies, Inc.", + /* 0028 */ "Conexant Systems Inc.", + /* 0029 */ "Qualcomm", + /* 0030 */ "Inventel", + /* 0031 */ "AVM Berlin", + /* 0032 */ "BandSpeed, Inc.", + /* 0033 */ "Mansella Ltd", + /* 0034 */ "NEC Corporation", + /* 0035 */ "WavePlus Technology Co., Ltd.", + /* 0036 */ "Alcatel", + /* 0037 */ "Philips Semiconductors", + /* 0038 */ "C Technologies", + /* 0039 */ "Open Interface", + /* 0040 */ "R F Micro Devices", + /* 0041 */ "Hitachi Ltd", + /* 0042 */ "Symbol Technologies, Inc.", + /* 0043 */ "Tenovis", + /* 0044 */ "Macronix International Co. Ltd.", + /* 0045 */ "GCT Semiconductor", + /* 0046 */ "Norwood Systems", + /* 0047 */ "MewTel Technology Inc." + }; + + return (m >= SIZE(t)? "?" : t[m]); +} /* hci_manufacturer2str */ + +char const * const +hci_features2str(u_int8_t *features, char *buffer, int size) +{ + static char const * const t[][8] = { + { /* byte 0 */ + /* 0 */ "<3-Slot> ", + /* 1 */ "<5-Slot> ", + /* 2 */ "<Encryption> ", + /* 3 */ "<Slot offset> ", + /* 4 */ "<Timing accuracy> ", + /* 5 */ "<Switch> ", + /* 6 */ "<Hold mode> ", + /* 7 */ "<Sniff mode> " + }, + { /* byte 1 */ + /* 0 */ "<Park mode> ", + /* 1 */ "<RSSI> ", + /* 2 */ "<Channel quality> ", + /* 3 */ "<SCO link> ", + /* 4 */ "<HV2 packets> ", + /* 5 */ "<HV3 packets> ", + /* 6 */ "<u-law log> ", + /* 7 */ "<A-law log> " + }, + { /* byte 2 */ + /* 0 */ "<CVSD> ", + /* 1 */ "<Paging scheme> ", + /* 2 */ "<Power control> ", + /* 3 */ "<Transparent SCO data> ", + /* 4 */ "<Flow control lag (bit0)> ", + /* 5 */ "<Flow control lag (bit1)> ", + /* 6 */ "<Flow control lag (bit2)> ", + /* 7 */ "<Unknown2.7> " + }}; + + if (buffer != NULL && size > 0) { + int n, i, len0, len1; + + memset(buffer, 0, size); + len1 = 0; + + for (n = 0; n < SIZE(t); n++) { + for (i = 0; i < SIZE(t[n]); i++) { + len0 = strlen(buffer); + if (len0 >= size) + goto done; + + if (features[n] & (1 << i)) { + if (len1 + strlen(t[n][i]) > 60) { + len1 = 0; + buffer[len0 - 1] = '\n'; + } + + len1 += strlen(t[n][i]); + strncat(buffer, t[n][i], size - len0); + } + } + } + } +done: + return (buffer); +} /* hci_features2str */ + +char const * const +hci_cc2str(int cc) +{ + static char const * const t[] = { + /* 0x00 */ "North America, Europe, Japan", + /* 0x01 */ "France" + }; + + return (cc >= SIZE(t)? "?" : t[cc]); +} /* hci_cc2str */ + +char const * const +hci_con_state2str(int state) +{ + static char const * const t[] = { + /* NG_HCI_CON_CLOSED */ "CLOSED", + /* NG_HCI_CON_W4_LP_CON_RSP */ "W4_LP_CON_RSP", + /* NG_HCI_CON_W4_CONN_COMPLETE */ "W4_CONN_COMPLETE", + /* NG_HCI_CON_OPEN */ "OPEN" + }; + + return (state >= SIZE(t)? "UNKNOWN" : t[state]); +} /* hci_con_state2str */ + +char const * const +hci_status2str(int status) +{ + static char const * const t[] = { + /* 0x00 */ "No error", + /* 0x01 */ "Unknown HCI command", + /* 0x02 */ "No connection", + /* 0x03 */ "Hardware failure", + /* 0x04 */ "Page timeout", + /* 0x05 */ "Authentication failure", + /* 0x06 */ "Key missing", + /* 0x07 */ "Memory full", + /* 0x08 */ "Connection timeout", + /* 0x09 */ "Max number of connections", + /* 0x0a */ "Max number of SCO connections to a unit", + /* 0x0b */ "ACL connection already exists", + /* 0x0c */ "Command disallowed", + /* 0x0d */ "Host rejected due to limited resources", + /* 0x0e */ "Host rejected due to securiity reasons", + /* 0x0f */ "Host rejected due to remote unit is a personal unit", + /* 0x10 */ "Host timeout", + /* 0x11 */ "Unsupported feature or parameter value", + /* 0x12 */ "Invalid HCI command parameter", + /* 0x13 */ "Other end terminated connection: User ended connection", + /* 0x14 */ "Other end terminated connection: Low resources", + /* 0x15 */ "Other end terminated connection: About to power off", + /* 0x16 */ "Connection terminated by local host", + /* 0x17 */ "Repeated attempts", + /* 0x18 */ "Pairing not allowed", + /* 0x19 */ "Unknown LMP PDU", + /* 0x1a */ "Unsupported remote feature", + /* 0x1b */ "SCO offset rejected", + /* 0x1c */ "SCO interval rejected", + /* 0x1d */ "SCO air mode rejected", + /* 0x1e */ "Invalid LMP parameters", + /* 0x1f */ "Unspecified error", + /* 0x20 */ "Unsupported LMP parameter value", + /* 0x21 */ "Role change not allowed", + /* 0x22 */ "LMP response timeout", + /* 0x23 */ "LMP error transaction collision", + /* 0x24 */ "LMP PSU not allowed", + /* 0x25 */ "Encryption mode not acceptable", + /* 0x26 */ "Unit key used", + /* 0x27 */ "QoS is not supported", + /* 0x28 */ "Instant passed", + /* 0x29 */ "Paring with unit key not supported" + }; + + return (status >= SIZE(t)? "Unknown error" : t[status]); +} /* hci_status2str */ + diff --git a/usr.sbin/bluetooth/hcseriald/Makefile b/usr.sbin/bluetooth/hcseriald/Makefile new file mode 100644 index 0000000000000..4153cd6b9ef54 --- /dev/null +++ b/usr.sbin/bluetooth/hcseriald/Makefile @@ -0,0 +1,17 @@ +# $Id: Makefile,v 1.4 2002/09/04 21:29:58 max Exp $ +# $FreeBSD$ + +DESTDIR= /usr/sbin/ +MANDIR= ../share/man/man + +PROG= hcseriald +MAN8= hcseriald.8 + +WARNS?= 2 +CFLAGS+= -Wall -O2 -I../../../sys/netgraph/bluetooth/include +SRCS= hcseriald.c + +DPADD= ${LIBNETGRAPH} +LDADD= -lnetgraph + +.include <bsd.prog.mk> diff --git a/usr.sbin/bluetooth/hcseriald/hcseriald.8 b/usr.sbin/bluetooth/hcseriald/hcseriald.8 new file mode 100644 index 0000000000000..d4b641d8b8afc --- /dev/null +++ b/usr.sbin/bluetooth/hcseriald/hcseriald.8 @@ -0,0 +1,81 @@ +.\" hcseriald.8 +.\" +.\" Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com> +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $Id: hcseriald.8,v 1.3 2002/11/09 19:16:50 max Exp $ +.\" $FreeBSD$ +.Dd June 14, 2002 +.Dt HCSERIALD 8 +.Os +.Sh NAME +.Nm hcseriald +.Nd supervise serial Bluetooth devices +.Sh SYNOPSIS +.Nm +.Op Fl f Ar device +.Op Fl n Ar node name +.Op Fl s Ar speed +.Op Fl d +.Sh DESCRIPTION +The +.Nm +handles serial Bluetooth devices. It does one simple thing. It opens +specified serial device, sets device parameters and pushes +.Em H4 +line discipline. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl f Ar device +Callout device name. Example: +.Fl f +.Pa /dev/cuaa0 . +.It Fl n Ar node name +Set H4 Netgraph node name. Example: +.Fl n Ar sio0 . +.It Fl s Ar speed +Set serial device speed to +.Em speed . +Example: +.Fl s Ar 115200 . +.It Fl d +Do not disassociate from the controlling terminal, i.e. run in foreground. +.El +.Sh FILES +.Bl -tag -width /dev/consolectl -compact +.It Pa /var/run/hcserial.*.pid +process id of the currently running +.Nm +daemon. Where +.Dq * +is a H4 Netgraph node name. +.El +.Sh SEE ALSO +.Xr tty 4 , +.Xr ng_h4 4 , +.Xr ng_hci 4 , +.Xr hccontrol 8 +.Sh AUTHORS +.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com diff --git a/usr.sbin/bluetooth/hcseriald/hcseriald.c b/usr.sbin/bluetooth/hcseriald/hcseriald.c new file mode 100644 index 0000000000000..bb0e8190caafc --- /dev/null +++ b/usr.sbin/bluetooth/hcseriald/hcseriald.c @@ -0,0 +1,279 @@ +/* + * hcseriald.c + * + * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: hcseriald.c,v 1.4 2002/09/04 21:29:58 max Exp $ + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/ioctl.h> + +#include <netgraph/ng_message.h> +#include <netgraph.h> +#include <ng_h4.h> + +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <termios.h> +#include <unistd.h> + +/* Prototypes */ +static int open_device (char const *, speed_t, char const *); +static void sighandler (int); +static void usage (); + +static char const * const hcseriald = "hcseriald"; +static int done = 0; + +int +main(int argc, char *argv[]) +{ + char *device = NULL, *name = NULL; + speed_t speed = 115200; + int n, detach = 1; + char p[FILENAME_MAX]; + FILE *f = NULL; + struct sigaction sa; + + /* Process command line arguments */ + while ((n = getopt(argc, argv, "df:n:s:")) != -1) { + switch (n) { + case 'd': + detach = 0; + break; + + case 'f': + device = optarg; + break; + + case 'n': + name = optarg; + break; + + case 's': + speed = atoi(optarg); + if (speed < 0) + usage(argv[0]); + break; + + default: + usage(argv[0]); + break; + } + } + + if (device == NULL || name == NULL) + usage(argv[0]); + + openlog(hcseriald, LOG_PID | LOG_NDELAY, LOG_USER); + + /* Open device */ + n = open_device(device, speed, name); + + if (detach) { + pid_t pid = fork(); + + if (pid == (pid_t) -1) { + syslog(LOG_ERR, "Could not fork(). %s (%d)", + strerror(errno), errno); + exit(1); + } + + if (pid != 0) + exit(0); + + if (daemon(0, 0) < 0) { + syslog(LOG_ERR, "Could not daemon(0, 0). %s (%d)", + strerror(errno), errno); + exit(1); + } + } + + /* Write PID file */ + snprintf(p, sizeof(p), "/var/run/%s.%s.pid", hcseriald, name); + f = fopen(p, "w"); + if (f == NULL) { + syslog(LOG_ERR, "Could not fopen(%s). %s (%d)", + p, strerror(errno), errno); + exit(1); + } + fprintf(f, "%d", getpid()); + fclose(f); + + /* Install signal handler */ + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = sighandler; + + if (sigaction(SIGTERM, &sa, NULL) < 0) { + syslog(LOG_ERR, "Could not sigaction(SIGTERM). %s (%d)", + strerror(errno), errno); + exit(1); + } + + if (sigaction(SIGHUP, &sa, NULL) < 0) { + syslog(LOG_ERR, "Could not sigaction(SIGHUP). %s (%d)", + strerror(errno), errno); + exit(1); + } + + if (sigaction(SIGINT, &sa, NULL) < 0) { + syslog(LOG_ERR, "Could not sigaction(SIGINT). %s (%d)", + strerror(errno), errno); + exit(1); + } + + /* Keep running */ + while (!done) + select(0, NULL, NULL, NULL, NULL); + + /* Remove PID file and close device */ + unlink(p); + close(n); + closelog(); + + return (0); +} /* main */ + +/* Open terminal, set settings, push H4 line discipline and set node name */ +static int +open_device(char const *device, speed_t speed, char const *name) +{ + int fd, disc, cs, ds; + struct termios t; + struct nodeinfo ni; + struct ngm_name n; + char p[NG_NODELEN + 1]; + + /* Open terminal device and setup H4 line discipline */ + fd = open(device, O_RDWR|O_NOCTTY); + if (fd < 0) { + syslog(LOG_ERR, "Could not open(%s). %s (%d)", + device, strerror(errno), errno); + exit(1); + } + + tcflush(fd, TCIOFLUSH); + + if (tcgetattr(fd, &t) < 0) { + syslog(LOG_ERR, "Could not tcgetattr(%s). %s (%d)", + device, strerror(errno), errno); + exit(1); + } + + cfmakeraw(&t); + + t.c_cflag |= CLOCAL; /* clocal */ + t.c_cflag &= ~CSIZE; /* cs8 */ + t.c_cflag |= CS8; /* cs8 */ + t.c_cflag &= ~PARENB; /* -parenb */ + t.c_cflag &= ~CSTOPB; /* -cstopb */ + t.c_cflag |= CRTSCTS; /* crtscts */ + + if (tcsetattr(fd, TCSANOW, &t) < 0) { + syslog(LOG_ERR, "Could not tcsetattr(%s). %s (%d)", + device, strerror(errno), errno); + exit(1); + } + + tcflush(fd, TCIOFLUSH); + + if (cfsetspeed(&t, speed) < 0) { + syslog(LOG_ERR, "Could not cfsetspeed(%s). %s (%d)", + device, strerror(errno), errno); + exit(1); + } + + if (tcsetattr(fd, TCSANOW, &t) < 0) { + syslog(LOG_ERR, "Could not tcsetattr(%s). %s (%d)", + device, strerror(errno), errno); + exit(1); + } + + disc = H4DISC; + if (ioctl(fd, TIOCSETD, &disc) < 0) { + syslog(LOG_ERR, "Could not ioctl(%s, TIOCSETD, %d). %s (%d)", + device, disc, strerror(errno), errno); + exit(1); + } + + /* Get default name of the Netgraph node */ + memset(&ni, 0, sizeof(ni)); + if (ioctl(fd, NGIOCGINFO, &ni) < 0) { + syslog(LOG_ERR, "Could not ioctl(%d, NGIOGINFO). %s (%d)", + fd, strerror(errno), errno); + exit(1); + } + + /* Assign new name to the Netgraph node */ + snprintf(p, sizeof(p), "%s:", ni.name); + snprintf(n.name, sizeof(n.name), "%s", name); + + if (NgMkSockNode(NULL, &cs, &ds) < 0) { + syslog(LOG_ERR, "Could not NgMkSockNode(). %s (%d)", + strerror(errno), errno); + exit(1); + } + + if (NgSendMsg(cs, p, NGM_GENERIC_COOKIE, NGM_NAME, &n, sizeof(n)) < 0) { + syslog(LOG_ERR, "Could not NgSendMsg(%d, %s, NGM_NAME, %s). " \ + "%s (%d)", cs, p, n.name, strerror(errno), errno); + exit(1); + } + + close(cs); + close(ds); + + return (fd); +} /* open_device */ + +/* Signal handler */ +static void +sighandler(int s) +{ + done = 1; +} /* sighandler */ + +/* Usage */ +static void +usage(void) +{ + fprintf(stderr, "Usage: %s -f device -n node_name [-s speed -d]\n" \ + "Where:\n" \ + "\t-f device tty device name, ex. /dev/cuaa1\n" \ + "\t-n node_name set Netgraph node name to node_name\n" \ + "\t-s speed set tty speed, ex. 115200\n" \ + "\t-d run in foreground\n", + hcseriald); + exit(255); +} /* usage */ + diff --git a/usr.sbin/bluetooth/l2control/Makefile b/usr.sbin/bluetooth/l2control/Makefile new file mode 100644 index 0000000000000..b098874082e14 --- /dev/null +++ b/usr.sbin/bluetooth/l2control/Makefile @@ -0,0 +1,12 @@ +# $Id: Makefile,v 1.3 2002/09/04 21:30:40 max Exp $ +# $FreeBSD$ + +DESTDIR= /usr/sbin/ +MANDIR= ../share/man/man +PROG= l2control +MAN8= l2control.8 +WARNS?= 2 +CFLAGS+= -g -I../../../sys/netgraph/bluetooth/include +SRCS= l2cap.c l2control.c + +.include <bsd.prog.mk> diff --git a/usr.sbin/bluetooth/l2control/l2cap.c b/usr.sbin/bluetooth/l2control/l2cap.c new file mode 100644 index 0000000000000..a0f2d240d579c --- /dev/null +++ b/usr.sbin/bluetooth/l2control/l2cap.c @@ -0,0 +1,256 @@ +/* + * l2cap.c + * + * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: l2cap.c,v 1.6 2002/09/04 21:30:40 max Exp $ + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <bitstring.h> +#include <errno.h> +#include <ng_hci.h> +#include <ng_l2cap.h> +#include <ng_btsocket.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "l2control.h" + +#define SIZE(x) (sizeof((x))/sizeof((x)[0])) + +/* Send read_node_flags command to the node */ +static int +l2cap_read_node_flags(int s, int argc, char **argv) +{ + struct ng_btsocket_l2cap_raw_node_flags r; + + memset(&r, 0, sizeof(r)); + if (ioctl(s, SIOC_L2CAP_NODE_GET_FLAGS, &r, sizeof(r)) < 0) + return (ERROR); + + fprintf(stdout, "BD_ADDR: %x:%x:%x:%x:%x:%x\n", + r.src.b[5], r.src.b[4], r.src.b[3], + r.src.b[2], r.src.b[1], r.src.b[0]); + fprintf(stdout, "Connectionless traffic flags:\n"); + fprintf(stdout, "\tSDP: %s\n", + (r.flags & NG_L2CAP_CLT_SDP_DISABLED)? "disabled" : "enabled"); + fprintf(stdout, "\tRFCOMM: %s\n", + (r.flags & NG_L2CAP_CLT_RFCOMM_DISABLED)? "disabled":"enabled"); + fprintf(stdout, "\tTCP: %s\n", + (r.flags & NG_L2CAP_CLT_TCP_DISABLED)? "disabled" : "enabled"); + + return (OK); +} /* l2cap_read_node_flags */ + +/* Send read_debug_level command to the node */ +static int +l2cap_read_debug_level(int s, int argc, char **argv) +{ + struct ng_btsocket_l2cap_raw_node_debug r; + + memset(&r, 0, sizeof(r)); + if (ioctl(s, SIOC_L2CAP_NODE_GET_DEBUG, &r, sizeof(r)) < 0) + return (ERROR); + + fprintf(stdout, "BD_ADDR: %x:%x:%x:%x:%x:%x\n", + r.src.b[5], r.src.b[4], r.src.b[3], + r.src.b[2], r.src.b[1], r.src.b[0]); + fprintf(stdout, "Debug level: %d\n", r.debug); + + return (OK); +} /* l2cap_read_debug_level */ + +/* Send write_debug_level command to the node */ +static int +l2cap_write_debug_level(int s, int argc, char **argv) +{ + struct ng_btsocket_l2cap_raw_node_debug r; + + memset(&r, 0, sizeof(r)); + switch (argc) { + case 1: + r.debug = atoi(argv[0]); + break; + + default: + return (USAGE); + } + + if (ioctl(s, SIOC_L2CAP_NODE_SET_DEBUG, &r, sizeof(r)) < 0) + return (ERROR); + + return (OK); +} /* l2cap_write_debug_level */ + +/* Send read_connection_list command to the node */ +static int +l2cap_read_connection_list(int s, int argc, char **argv) +{ + static char const * const state[] = { + /* NG_L2CAP_CON_CLOSED */ "CLOSED", + /* NG_L2CAP_W4_LP_CON_CFM */ "W4_LP_CON_CFM", + /* NG_L2CAP_CON_OPEN */ "OPEN" + }; +#define con_state2str(x) ((x) >= SIZE(state)? "UNKNOWN" : state[(x)]) + + struct ng_btsocket_l2cap_raw_con_list r; + int n, error = OK; + + memset(&r, 0, sizeof(r)); + r.num_connections = NG_L2CAP_MAX_CON_NUM; + r.connections = calloc(NG_L2CAP_MAX_CON_NUM, + sizeof(ng_l2cap_node_con_ep)); + if (r.connections == NULL) { + errno = ENOMEM; + return (ERROR); + } + + if (ioctl(s, SIOC_L2CAP_NODE_GET_CON_LIST, &r, sizeof(r)) < 0) { + error = ERROR; + goto out; + } + + fprintf(stdout, "BD_ADDR: %x:%x:%x:%x:%x:%x\n", + r.src.b[5], r.src.b[4], r.src.b[3], + r.src.b[2], r.src.b[1], r.src.b[0]); + fprintf(stdout, "L2CAP connections:\n"); + fprintf(stdout, +"Remote BD_ADDR Handle Flags Pending State\n"); + for (n = 0; n < r.num_connections; n++) { + fprintf(stdout, + "%02x:%02x:%02x:%02x:%02x:%02x " \ + " %5d " \ + "%2.2s %2.2s " \ + "%7d " \ + "%s\n", + r.connections[n].remote.b[5], + r.connections[n].remote.b[4], + r.connections[n].remote.b[3], + r.connections[n].remote.b[2], + r.connections[n].remote.b[1], + r.connections[n].remote.b[0], + r.connections[n].con_handle, + ((r.connections[n].flags & NG_L2CAP_CON_TX)? "TX" : ""), + ((r.connections[n].flags & NG_L2CAP_CON_RX)? "RX" : ""), + r.connections[n].pending, + con_state2str(r.connections[n].state)); + } +out: + free(r.connections); + + return (error); +} /* l2cap_read_connection_list */ + +/* Send read_channel_list command to the node */ +static int +l2cap_read_channel_list(int s, int argc, char **argv) +{ + static char const * const state[] = { + /* NG_L2CAP_CLOSED */ "CLOSED", + /* NG_L2CAP_W4_L2CAP_CON_RSP */ "W4_L2CAP_CON_RSP", + /* NG_L2CAP_W4_L2CA_CON_RSP */ "W4_L2CA_CON_RSP", + /* NG_L2CAP_CONFIG */ "CONFIG", + /* NG_L2CAP_OPEN */ "OPEN", + /* NG_L2CAP_W4_L2CAP_DISCON_RSP */ "W4_L2CAP_DISCON_RSP", + /* NG_L2CAP_W4_L2CA_DISCON_RSP */ "W4_L2CA_DISCON_RSP" + }; +#define ch_state2str(x) ((x) >= SIZE(state)? "UNKNOWN" : state[(x)]) + + struct ng_btsocket_l2cap_raw_chan_list r; + int n, error = OK; + + memset(&r, 0, sizeof(r)); + r.num_channels = NG_L2CAP_MAX_CHAN_NUM; + r.channels = calloc(NG_L2CAP_MAX_CHAN_NUM, + sizeof(ng_l2cap_node_chan_ep)); + if (r.channels == NULL) { + errno = ENOMEM; + return (ERROR); + } + + if (ioctl(s, SIOC_L2CAP_NODE_GET_CHAN_LIST, &r, sizeof(r)) < 0) { + error = ERROR; + goto out; + } + + fprintf(stdout, "BD_ADDR: %x:%x:%x:%x:%x:%x\n", + r.src.b[5], r.src.b[4], r.src.b[3], + r.src.b[2], r.src.b[1], r.src.b[0]); + fprintf(stdout, "L2CAP channels:\n"); + fprintf(stdout, +"Remote BD_ADDR SCID/ DCID PSM IMTU/ OMTU State\n"); + for (n = 0; n < r.num_channels; n++) { + fprintf(stdout, + "%02x:%02x:%02x:%02x:%02x:%02x " \ + "%5d/%5d %5d " \ + "%5d/%5d " \ + "%s\n", + r.channels[n].remote.b[5], r.channels[n].remote.b[4], + r.channels[n].remote.b[3], r.channels[n].remote.b[2], + r.channels[n].remote.b[1], r.channels[n].remote.b[0], + r.channels[n].scid, r.channels[n].dcid, + r.channels[n].psm, r.channels[n].imtu, + r.channels[n].omtu, + ch_state2str(r.channels[n].state)); + } +out: + free(r.channels); + + return (error); +} /* l2cap_read_channel_list */ + +struct l2cap_command l2cap_commands[] = { +{ +"read_node_flags", +"Get L2CAP node flags", +&l2cap_read_node_flags +}, +{ +"read_debug_level", +"Get L2CAP node debug level", +&l2cap_read_debug_level +}, +{ +"write_debug_level <level>", +"Set L2CAP node debug level", +&l2cap_write_debug_level +}, +{ +"read_connection_list", +"Read list of the L2CAP connections", +&l2cap_read_connection_list +}, +{ +"read_channel_list", +"Read list of the L2CAP channels", +&l2cap_read_channel_list +}, +{ +NULL, +}}; + diff --git a/usr.sbin/bluetooth/l2control/l2control.8 b/usr.sbin/bluetooth/l2control/l2control.8 new file mode 100644 index 0000000000000..9efa2512369a6 --- /dev/null +++ b/usr.sbin/bluetooth/l2control/l2control.8 @@ -0,0 +1,84 @@ +.\" l2control.8 +.\" +.\" Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com> +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $Id: l2control.8,v 1.4 2002/11/09 19:18:16 max Exp $ +.\" $FreeBSD$ +.Dd June 14, 2002 +.Dt L2CONTROL 8 +.Os +.Sh NAME +.Nm l2control +.Nd L2CAP configuration utility +.Sh SYNOPSIS +.Nm +.Op Fl a Ar local BD_ADDR +.Op Ar command +.Op Ar parameters ... +.Sh DESCRIPTION +The +.Nm +utility connects to the local device with specified BD_ADDR and attempts +to send specified command. +.Nm +will print results to the standard output and error messages to +the standard error. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl a Ar local BD_ADDR +Connect to the local device with specified BD_ADDR. Example: +.Fl a Ar 00:01:02:03:04:05 . +.It command +One of the supported commands (see below). Special command +.Dq help +can be used to obtain the list of all supported commands. To get more +information about specific command use +.Dq help command . +.It parameters +One or more optional space separated command parameters. +.El +.Sh COMMANDS +The currently supported node commands in +.Nm +are: +.Pp +.Bd -literal -offset indent -compact +Read_Node_Flags +Read_Debug_Level +Write_Debug_Level +Read_Connection_List +Read_Channel_List +.Ed +.Pp +.Sh DIAGNOSTICS +.Ex -std +.Sh SEE ALSO +.Xr netgraph 3 , +.Xr netgraph 4 , +.Xr ng_l2cap 4 , +.Xr l2ping 8 +.Sh AUTHORS +.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com diff --git a/usr.sbin/bluetooth/l2control/l2control.c b/usr.sbin/bluetooth/l2control/l2control.c new file mode 100644 index 0000000000000..ef658265fb9fb --- /dev/null +++ b/usr.sbin/bluetooth/l2control/l2control.c @@ -0,0 +1,226 @@ +/* + * l2control.c + * + * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: l2control.c,v 1.5 2002/09/04 21:30:40 max Exp $ + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <bitstring.h> +#include <assert.h> +#include <err.h> +#include <errno.h> +#include <ng_hci.h> +#include <ng_l2cap.h> +#include <ng_btsocket.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "l2control.h" + +/* Prototypes */ +static int do_l2cap_command (bdaddr_p, int, char **); +static struct l2cap_command * find_l2cap_command (char const *, + struct l2cap_command *); +static void print_l2cap_command (struct l2cap_command *); +static void usage (void); + +/* Main */ +int +main(int argc, char *argv[]) +{ + int n; + bdaddr_t bdaddr; + + memset(&bdaddr, 0, sizeof(bdaddr)); + + /* Process command line arguments */ + while ((n = getopt(argc, argv, "a:")) != -1) { + switch (n) { + case 'a': { + int a0, a1, a2, a3, a4, a5; + + if (sscanf(optarg, "%x:%x:%x:%x:%x:%x", + &a5, &a4, &a3, &a2, &a1, &a0) != 6) { + usage(); + break; + } + + bdaddr.b[0] = (a0 & 0xff); + bdaddr.b[1] = (a1 & 0xff); + bdaddr.b[2] = (a2 & 0xff); + bdaddr.b[3] = (a3 & 0xff); + bdaddr.b[4] = (a4 & 0xff); + bdaddr.b[5] = (a5 & 0xff); + } break; + + default: + usage(); + break; + } + } + + argc -= optind; + argv += optind; + + if (*argv == NULL) + usage(); + + return (do_l2cap_command(&bdaddr, argc, argv)); +} /* main */ + +/* Execute commands */ +static int +do_l2cap_command(bdaddr_p bdaddr, int argc, char **argv) +{ + char *cmd = argv[0]; + struct l2cap_command *c = NULL; + struct sockaddr_l2cap sa; + int s, e, help; + + help = 0; + if (strcasecmp(cmd, "help") == 0) { + argc --; + argv ++; + + if (argc <= 0) { + fprintf(stdout, "Supported commands:\n"); + print_l2cap_command(l2cap_commands); + fprintf(stdout, "\nFor more information use " \ + "'help command'\n"); + + return (OK); + } + + help = 1; + cmd = argv[0]; + } + + c = find_l2cap_command(cmd, l2cap_commands); + if (c == NULL) { + fprintf(stdout, "Unknown command: \"%s\"\n", cmd); + return (ERROR); + } + + if (!help) { + if (memcmp(bdaddr, NG_HCI_BDADDR_ANY, sizeof(*bdaddr)) == 0) + usage(); + + memset(&sa, 0, sizeof(sa)); + sa.l2cap_len = sizeof(sa); + sa.l2cap_family = AF_BLUETOOTH; + memcpy(&sa.l2cap_bdaddr, bdaddr, sizeof(sa.l2cap_bdaddr)); + + s = socket(PF_BLUETOOTH, SOCK_RAW, BLUETOOTH_PROTO_L2CAP); + if (s < 0) + err(1, "Could not create socket"); + + if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) + err(2, +"Could not bind socket, bdaddr=%x:%x:%x:%x:%x:%x", + sa.l2cap_bdaddr.b[5], sa.l2cap_bdaddr.b[4], + sa.l2cap_bdaddr.b[3], sa.l2cap_bdaddr.b[2], + sa.l2cap_bdaddr.b[1], sa.l2cap_bdaddr.b[0]); + + if (connect(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) + err(2, +"Could not connect socket, bdaddr=%x:%x:%x:%x:%x:%x", + sa.l2cap_bdaddr.b[5], sa.l2cap_bdaddr.b[4], + sa.l2cap_bdaddr.b[3], sa.l2cap_bdaddr.b[2], + sa.l2cap_bdaddr.b[1], sa.l2cap_bdaddr.b[0]); + + e = 0x0ffff; + if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &e, sizeof(e)) < 0) + err(3, "Coult not setsockopt(RCVBUF, %d)", e); + + e = (c->handler)(s, -- argc, ++ argv); + + close(s); + } else + e = USAGE; + + switch (e) { + case OK: + case FAILED: + break; + + case ERROR: + fprintf(stdout, "Could not execute command \"%s\". %s\n", + cmd, strerror(errno)); + break; + + case USAGE: + fprintf(stdout, "Usage: %s\n%s\n", c->command, c->description); + break; + + default: assert(0); break; + } + + return (e); +} /* do_l2cap_command */ + +/* Try to find command in specified category */ +static struct l2cap_command * +find_l2cap_command(char const *command, struct l2cap_command *category) +{ + struct l2cap_command *c = NULL; + + for (c = category; c->command != NULL; c++) { + char *c_end = strchr(c->command, ' '); + + if (c_end != NULL) { + int len = c_end - c->command; + + if (strncasecmp(command, c->command, len) == 0) + return (c); + } else if (strcasecmp(command, c->command) == 0) + return (c); + } + + return (NULL); +} /* find_l2cap_command */ + +/* Try to find command in specified category */ +static void +print_l2cap_command(struct l2cap_command *category) +{ + struct l2cap_command *c = NULL; + + for (c = category; c->command != NULL; c++) + fprintf(stdout, "\t%s\n", c->command); +} /* print_l2cap_command */ + +/* Usage */ +static void +usage(void) +{ + fprintf(stdout, "Usage: l2control -a BD_ADDR cmd [p1] [..]]\n"); + exit(255); +} /* usage */ + diff --git a/usr.sbin/bluetooth/l2control/l2control.h b/usr.sbin/bluetooth/l2control/l2control.h new file mode 100644 index 0000000000000..0f6e5dca3508e --- /dev/null +++ b/usr.sbin/bluetooth/l2control/l2control.h @@ -0,0 +1,49 @@ +/* + * l2control.h + * + * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: l2control.h,v 1.2 2002/09/04 21:30:40 max Exp $ + * $FreeBSD$ + */ + +#ifndef _L2CONTROL_H_ +#define _L2CONTROL_H_ + +#define OK 0 /* everything was OK */ +#define ERROR 1 /* could not execute command */ +#define FAILED 2 /* error was reported */ +#define USAGE 3 /* invalid parameters */ + +struct l2cap_command { + char const *command; + char const *description; + int (*handler)(int, int, char **); +}; + +extern struct l2cap_command l2cap_commands[]; + +#endif /* _L2CONTROL_H_ */ + diff --git a/usr.sbin/bluetooth/l2ping/Makefile b/usr.sbin/bluetooth/l2ping/Makefile new file mode 100644 index 0000000000000..f2dad3196d1bd --- /dev/null +++ b/usr.sbin/bluetooth/l2ping/Makefile @@ -0,0 +1,12 @@ +# $Id: Makefile,v 1.4 2002/09/04 21:28:05 max Exp $ +# $FreeBSD$ + +DESTDIR= /usr/sbin/ +MANDIR= ../share/man/man +PROG= l2ping +MAN8= l2ping.8 +SRCS= l2ping.c +WARNS?= 2 +CFLAGS+= -g -I../../../sys/netgraph/bluetooth/include + +.include <bsd.prog.mk> diff --git a/usr.sbin/bluetooth/l2ping/l2ping.8 b/usr.sbin/bluetooth/l2ping/l2ping.8 new file mode 100644 index 0000000000000..aa79ad95ea5c3 --- /dev/null +++ b/usr.sbin/bluetooth/l2ping/l2ping.8 @@ -0,0 +1,87 @@ +.\" l2ping.8 +.\" +.\" Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com> +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $Id: l2ping.8,v 1.4 2002/11/09 19:20:09 max Exp $ +.\" $FreeBSD$ +.Dd June 14, 2002 +.Dt L2PING 8 +.Os +.Sh NAME +.Nm l2ping +.Nd send L2CAP ECHO_REQUEST to remote devices +.Sh SYNOPSIS +.Nm +.Op Fl a Ar remote BD_ADDR +.Op Fl S Ar source BD_ADDR +.Op Fl c Ar count +.Op Fl f +.Op Fl i Ar delay +.Op Fl s Ar size +.Sh DESCRIPTION +The +.Nm +uses L2CAP ECHO_REQUEST datagram to elicit a L2CAP ECHO_RESPONSE from a +remote device. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl a Ar remote BD_ADDR +Address of remote device to ping. Example: +.Fl a Ar 00:01:02:03:04:05 . +.It Fl S Ar source BD_ADDR +Send L2CAP ECHO_REQUEST from local device that has BD_ADDR. Example: +.Fl S Ar 00:05:04:03:02:01 . +.It Fl c Ar count +Number of packets to send. If this option is not specified, +.Nm +will operate until interrupted. +.It Fl f +.Dq Flood +ping, i.e. no delay between packets. +.It Fl i Ar wait +Wait +.Em wait +seconds between sending each packet. The default is to wait for one +second between each packet. This option is ignored if +.Fl f +has been specified. +.It Fl s Ar size +Specify the number of payload bytes to be sent. The default is 64. The +maximum size is 65531. Use this option with caution. Some implementations +may not like large sizes and may hang or even crash. +.El +.Sh BUGS +Could collect more statistic. Could check for duplicated, corrupted +and lost packets. +.Sh DIAGNOSTICS +.Ex -std +.Sh SEE ALSO +.Xr netgraph 3 , +.Xr netgraph 4 , +.Xr ng_l2cap 4 , +.Xr l2control 8 +.Sh AUTHORS +.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com diff --git a/usr.sbin/bluetooth/l2ping/l2ping.c b/usr.sbin/bluetooth/l2ping/l2ping.c new file mode 100644 index 0000000000000..a7ffd3771d567 --- /dev/null +++ b/usr.sbin/bluetooth/l2ping/l2ping.c @@ -0,0 +1,279 @@ +/* + * l2ping.c + * + * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: l2ping.c,v 1.9 2002/09/04 21:28:05 max Exp $ + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <assert.h> +#include <bitstring.h> +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <ng_hci.h> +#include <ng_l2cap.h> +#include <ng_btsocket.h> + +static void usage (void); +static void tv_sub (struct timeval *, struct timeval const *); +static double tv2msec (struct timeval const *); + +#undef min +#define min(x, y) (((x) > (y))? (y) : (x)) + +static char const pattern[] = "1234567890-"; +#define PATTERN_SIZE (sizeof(pattern) - 1) + +/* + * Main + */ + +int +main(int argc, char *argv[]) +{ + struct ng_btsocket_l2cap_raw_ping r; + int n, s, count, wait, flood, fail; + struct timeval a, b; + + /* Set defaults */ + memset(&r, 0, sizeof(r)); + r.echo_data = calloc(NG_L2CAP_MAX_ECHO_SIZE, sizeof(u_int8_t)); + if (r.echo_data == NULL) { + fprintf(stderr, "Failed to allocate echo data buffer"); + exit(1); + } + + r.echo_size = 64; /* bytes */ + count = -1; /* unlimited */ + wait = 1; /* sec */ + flood = 0; + + /* Parse command line arguments */ + while ((n = getopt(argc, argv, "a:c:fi:n:s:S:")) != -1) { + switch (n) { + case 'a': + case 'S': { + int a0, a1, a2, a3, a4, a5; + + if (sscanf(optarg, "%x:%x:%x:%x:%x:%x", + &a5, &a4, &a3, &a2, &a1, &a0) != 6) + usage(); + + if (n == 'a') { + /* destination bdaddr */ + r.echo_dst.b[0] = (a0 & 0xff); + r.echo_dst.b[1] = (a1 & 0xff); + r.echo_dst.b[2] = (a2 & 0xff); + r.echo_dst.b[3] = (a3 & 0xff); + r.echo_dst.b[4] = (a4 & 0xff); + r.echo_dst.b[5] = (a5 & 0xff); + } else { + /* source bdaddr */ + r.echo_src.b[0] = (a0 & 0xff); + r.echo_src.b[1] = (a1 & 0xff); + r.echo_src.b[2] = (a2 & 0xff); + r.echo_src.b[3] = (a3 & 0xff); + r.echo_src.b[4] = (a4 & 0xff); + r.echo_src.b[5] = (a5 & 0xff); + } + } break; + + case 'c': + count = atoi(optarg); + if (count <= 0) + usage(); + break; + + case 'f': + flood = 1; + break; + + case 'i': + wait = atoi(optarg); + if (wait <= 0) + usage(); + break; + + case 's': + r.echo_size = atoi(optarg); + if ((int) r.echo_size < sizeof(int)) + usage(); + + if (r.echo_size > NG_L2CAP_MAX_ECHO_SIZE) + r.echo_size = NG_L2CAP_MAX_ECHO_SIZE; + break; + + default: + usage(); + break; + } + } + + if (memcmp(&r.echo_dst, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0) + usage(); + + s = socket(PF_BLUETOOTH, SOCK_RAW, BLUETOOTH_PROTO_L2CAP); + if (s < 0) + err(2, "Could not create socket"); + + if (memcmp(&r.echo_src, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) != 0) { + struct sockaddr_l2cap sa; + + memset(&sa, 0, sizeof(sa)); + sa.l2cap_len = sizeof(sa); + sa.l2cap_family = AF_BLUETOOTH; + memcpy(&sa.l2cap_bdaddr, &r.echo_src, sizeof(bdaddr_t)); + + if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) + err(3, +"Could not bind socket, src bdaddr=%x:%x:%x:%x:%x:%x", + sa.l2cap_bdaddr.b[5], sa.l2cap_bdaddr.b[4], + sa.l2cap_bdaddr.b[3], sa.l2cap_bdaddr.b[2], + sa.l2cap_bdaddr.b[1], sa.l2cap_bdaddr.b[0]); + + if (connect(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) + err(4, +"Could not connect socket, src bdaddr=%x:%x:%x:%x:%x:%x", + sa.l2cap_bdaddr.b[5], sa.l2cap_bdaddr.b[4], + sa.l2cap_bdaddr.b[3], sa.l2cap_bdaddr.b[2], + sa.l2cap_bdaddr.b[1], sa.l2cap_bdaddr.b[0]); + } + + /* Fill pattern */ + for (n = 0; n < r.echo_size; ) { + int avail = min(r.echo_size - n, PATTERN_SIZE); + + memcpy(r.echo_data + n, pattern, avail); + n += avail; + } + + /* Start ping'ing */ + for (n = 0; count == -1 || count > 0; n ++) { + if (gettimeofday(&a, NULL) < 0) + err(5, "Could not gettimeofday(a)"); + + fail = 0; + *((int *)(r.echo_data)) = htonl(n); + if (ioctl(s, SIOC_L2CAP_L2CA_PING, &r, sizeof(r)) < 0) { + r.result = errno; + fail = 1; +/* + warn("Could not ping, dst bdaddr=%x:%x:%x:%x:%x:%x", + r.echo_dst.b[5], r.echo_dst.b[4], + r.echo_dst.b[3], r.echo_dst.b[2], + r.echo_dst.b[1], r.echo_dst.b[0]); +*/ + } + + if (gettimeofday(&b, NULL) < 0) + err(7, "Could not gettimeofday(b)"); + + tv_sub(&b, &a); + + fprintf(stdout, +"%d bytes from %x:%x:%x:%x:%x:%x seq_no=%d time=%.3f ms result=%#x %s\n", + r.echo_size, + r.echo_dst.b[5], r.echo_dst.b[4], + r.echo_dst.b[3], r.echo_dst.b[2], + r.echo_dst.b[1], r.echo_dst.b[0], + ntohl(*((int *)(r.echo_data))), + tv2msec(&b), r.result, + ((fail == 0)? "" : strerror(errno))); + + if (!flood) { + /* Wait */ + a.tv_sec = wait; + a.tv_usec = 0; + select(0, NULL, NULL, NULL, &a); + } + + if (count != -1) + count --; + } + + free(r.echo_data); + close(s); + + return (0); +} /* main */ + +/* + * a -= b, for timevals + */ + +static void +tv_sub(struct timeval *a, struct timeval const *b) +{ + if (a->tv_usec < b->tv_usec) { + a->tv_usec += 1000000; + a->tv_sec -= 1; + } + + a->tv_usec -= b->tv_usec; + a->tv_sec -= b->tv_sec; +} /* tv_sub */ + +/* + * convert tv to msec + */ + +static double +tv2msec(struct timeval const *tvp) +{ + return(((double)tvp->tv_usec)/1000.0 + ((double)tvp->tv_sec)*1000.0); +} /* tv2msec */ + +/* + * Usage + */ + +static void +usage(void) +{ + fprintf(stderr, "Usage: l2ping -a bd_addr " \ + "[-S bd_addr -c count -i wait -s size]\n"); + fprintf(stderr, "Where:\n"); + fprintf(stderr, "\t-S bd_addr - Source BD_ADDR\n"); + fprintf(stderr, "\t-a bd_addr - Remote BD_ADDR to ping\n"); + fprintf(stderr, "\t-c count - Number of packets to send\n"); + fprintf(stderr, "\t-f - No delay (soft of flood)\n"); + fprintf(stderr, "\t-i wait - Delay between packets (sec)\n"); + fprintf(stderr, "\t-s size - Packet size (bytes), " \ + "between %d and %d\n", sizeof(int), NG_L2CAP_MAX_ECHO_SIZE); + + exit(255); +} /* usage */ + |
