diff options
| author | Marcel Holtmann <marcel@holtmann.org> | 2005-03-27 19:19:52 +0000 | 
|---|---|---|
| committer | Marcel Holtmann <marcel@holtmann.org> | 2005-03-27 19:19:52 +0000 | 
| commit | 8e8b4632526b90c9a8a86d1829ee5b71033d3fbb (patch) | |
| tree | 8dc861ff329f9194c138b993615dc78b1a509e9a | |
| parent | f5434ca84a250bbe6a7fe7baffdc53fb79d482f9 (diff) | |
Add new search command and OUI support
| -rw-r--r-- | tools/hcitool.c | 314 | ||||
| -rw-r--r-- | tools/oui.c | 100 | ||||
| -rw-r--r-- | tools/oui.h | 30 | 
3 files changed, 440 insertions, 4 deletions
| diff --git a/tools/hcitool.c b/tools/hcitool.c index 27ac78b7..9c4dc468 100644 --- a/tools/hcitool.c +++ b/tools/hcitool.c @@ -44,6 +44,8 @@  #include <bluetooth/hci.h>  #include <bluetooth/hci_lib.h> +#include "oui.h" +  #define for_each_opt(opt, long, short) while ((opt=getopt_long(argc, argv, short ? short:"+", long, NULL)) != -1)  static void usage(void); @@ -224,8 +226,8 @@ static void cmd_inq(int dev_id, int argc, char **argv)  		ba2str(&(info+i)->bdaddr, addr);  		printf("\t%s\tclock offset: 0x%4.4x\tclass: 0x%2.2x%2.2x%2.2x\n",  			addr, btohs((info+i)->clock_offset), -			(info+i)->dev_class[2],  -			(info+i)->dev_class[1],  +			(info+i)->dev_class[2], +			(info+i)->dev_class[1],  			(info+i)->dev_class[0]);  	}  	free(info); @@ -250,7 +252,7 @@ static void cmd_scan(int dev_id, int argc, char **argv)  	inquiry_info *info = NULL;  	int num_rsp, length, flags;  	char addr[18]; -	char name[248]; +	char name[249];  	int i, opt, dd;  	length  = 8;	/* ~10 seconds */ @@ -383,7 +385,7 @@ static void cmd_info(int dev_id, int argc, char **argv)  {  	bdaddr_t bdaddr;  	uint16_t handle; -	char name[248]; +	char name[249], oui[9], *comp;  	unsigned char features[8];  	struct hci_version version;  	struct hci_dev_info di; @@ -454,6 +456,13 @@ static void cmd_info(int dev_id, int argc, char **argv)  	printf("\tBD Address:  %s\n", argv[0]); +	ba2oui(&bdaddr, oui); +	comp = ouitocomp(oui); +	if (comp) { +		printf("\tOUI Company: %s (%s)\n", comp, oui); +		free(comp); +	} +  	if (hci_read_remote_name(dd, &bdaddr, sizeof(name), name, 25000) == 0)  		printf("\tDevice Name: %s\n", name); @@ -482,6 +491,302 @@ static void cmd_info(int dev_id, int argc, char **argv)  	close(dd);  } +/* Find remote devices */ + +static struct option find_options[] = { +	{ "help",	0, 0, 'h' }, +	{ "length",	1, 0, 'l' }, +	{ "numrsp",	1, 0, 'n' }, +	{ "flush",	0, 0, 'f' }, +	{ 0, 0, 0, 0 } +}; + +static char *find_help = +	"Usage:\n" +	"\tfind [--length=N] [--numrsp=N] [--flush]\n"; + +static char *get_minor_device_name(int major, int minor) +{ +	switch (major) { +	case 0:	/* misc */ +		return ""; +	case 1:	/* computer */ +		switch(minor) { +		case 0: +			return "Uncategorized"; +		case 1: +			return "Desktop workstation"; +		case 2: +			return "Server"; +		case 3: +			return "Laptop"; +		case 4: +			return "Handheld"; +		case 5: +			return "Palm"; +		case 6: +			return "Wearable"; +		} +		break; +	case 2:	/* phone */ +		switch(minor) { +		case 0: +			return "Uncategorized"; +		case 1: +			return "Cellular"; +		case 2: +			return "Cordless"; +		case 3: +			return "Smart phone"; +		case 4: +			return "Wired modem or voice gateway"; +		case 5: +			return "Common ISDN Access"; +		case 6: +			return "Sim Card Reader"; +		} +		break; +	case 3:	/* lan access */ +		if (minor == 0) +			return "Uncategorized"; +		switch(minor / 8) { +		case 0: +			return "Fully available"; +		case 1: +			return "1-17% utilized"; +		case 2: +			return "17-33% utilized"; +		case 3: +			return "33-50% utilized"; +		case 4: +			return "50-67% utilized"; +		case 5: +			return "67-83% utilized"; +		case 6: +			return "83-99% utilized"; +		case 7: +			return "No service available"; +		} +		break; +	case 4:	/* audio/video */ +		switch(minor) { +		case 0: +			return "Uncategorized"; +		case 1: +			return "Device conforms to the Headset profile"; +		case 2: +			return "Hands-free"; +			/* 3 is reserved */ +		case 4: +			return "Microphone"; +		case 5: +			return "Loudspeaker"; +		case 6: +			return "Headphones"; +		case 7: +			return "Portable Audio"; +		case 8: +			return "Car Audio"; +		case 9: +			return "Set-top box"; +		case 10: +			return "HiFi Audio Device"; +		case 11: +			return "VCR"; +		case 12: +			return "Video Camera"; +		case 13: +			return "Camcorder"; +		case 14: +			return "Video Monitor"; +		case 15: +			return "Video Display and Loudspeaker"; +		case 16: +			return "Video Conferencing"; +			/* 17 is reserved */ +		case 18: +			return "Gaming/Toy"; +		} +		break; +	case 5:	/* peripheral */ +		switch(minor) { +		case 16: +			return "Keyboard"; +		case 32: +			return "Pointing device"; +		case 48: +			return "Combo keyboard/pointing device"; +		} +		break; +	case 6:	/* imaging */ +		if (minor & 4) +			return "Display"; +		if (minor & 8) +			return "Camera"; +		if (minor & 16) +			return "Scanner"; +		if (minor & 32) +			return "Printer"; +		break; +	case 63:	/* uncategorised */ +		return ""; +	} +	return "Unknown (reserved) minor device class"; +} + +static char *major_classes[] = { +	"Miscellaneous", "Computer", "Phone", "LAN Access", +	"Audio/Video", "Peripheral", "Imaging", "Uncategorized" +}; + +static void cmd_find(int dev_id, int argc, char **argv) +{ +	inquiry_info *info = NULL; +	int num_rsp, length, flags; +	uint8_t cls[3]; +	uint16_t handle; +	char addr[18], name[249], oui[9], *comp; +	unsigned char features[8]; +	struct hci_version version; +	struct hci_dev_info di; +	struct hci_conn_info_req *cr; +	int i, opt, dd, cc = 0; + +	length  = 8;	/* ~10 seconds */ +	num_rsp = 100; +	flags   = 0; + +	for_each_opt(opt, find_options, NULL) { +		switch (opt) { +		case 'l': +			length = atoi(optarg); +			break; + +		case 'n': +			num_rsp = atoi(optarg); +			break; + +		case 'f': +			flags |= IREQ_CACHE_FLUSH; +			break; + +		default: +			printf(find_help); +			return; +		} +	} + +	if (dev_id < 0) { +		dev_id = hci_get_route(NULL); +		if (dev_id < 0) { +			perror("Device is not available"); +			exit(1); +		} +	} + +	if (hci_devinfo(dev_id, &di) < 0) { +		perror("Can't get device info"); +		exit(1); +	} + +	printf("Searching ...\n"); +	num_rsp = hci_inquiry(dev_id, length, num_rsp, NULL, &info, flags); +	if (num_rsp < 0) { +		perror("Inquiry failed"); +		exit(1); +	} + +	dd = hci_open_dev(dev_id); +	if (dd < 0) { +		perror("HCI device open failed"); +		free(info); +		exit(1); +	} + +	printf("\n"); + +	for (i = 0; i < num_rsp; i++) { +		ba2str(&(info+i)->bdaddr, addr); +		printf("BD Address:\t%s [mode %d, clkoffset 0x%4.4x]\n", addr, +			(info+i)->pscan_rep_mode, btohs((info+i)->clock_offset)); + +		ba2oui(&(info+i)->bdaddr, oui); +		comp = ouitocomp(oui); +		if (comp) { +			printf("OUI company:\t%s (%s)\n", comp, oui); +			free(comp); +		} + +		cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info)); +		if (cr) { +			bacpy(&cr->bdaddr, &(info+i)->bdaddr); +			cr->type = ACL_LINK; +			if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) { +				if (hci_create_connection(dd, &(info+i)->bdaddr, +						htobs(di.pkt_type & ACL_PTYPE_MASK), +						(info+i)->clock_offset | 0x8000, +						0x01, &handle, 25000) < 0) +					handle = 0; +				else +					cc = 1; +			} else +				handle = htobs(cr->conn_info->handle); +		} else +			handle = 0; + +		memset(name, 0, sizeof(name)); +		if (hci_read_remote_name_with_clock_offset(dd, &(info+i)->bdaddr, +				(info+i)->pscan_rep_mode, +				(info+i)->clock_offset | 0x8000, +				sizeof(name), name, 100000) < 0) +			strcpy(name, "n/a"); +		printf("Device name:\t%s\n", name); + +		memcpy(cls, (info+i)->dev_class, 3); +		printf("Device class:\t"); +		if ((cls[1] & 0x1f) > sizeof(*major_classes)) +			printf("Invalid"); +		else +			printf("%s, %s", major_classes[cls[1] & 0x1f], +				get_minor_device_name(cls[1] & 0x1f, cls[0] >> 2)); +		printf(" (0x%2.2x%2.2x%2.2x)\n", cls[2], cls[1], cls[0]); + +		if (handle > 0) { +			if (hci_read_remote_version(dd, handle, &version, 20000) == 0) { +				printf("Manufacturer:\t%s (%d)\n", +					bt_compidtostr(version.manufacturer), +					version.manufacturer); +				printf("LMP version:\t%s (0x%x) [subver 0x%x]\n", +					lmp_vertostr(version.lmp_ver), +					version.lmp_ver, version.lmp_subver); +			} + +			if (hci_read_remote_features(dd, handle, features, 20000) == 0) { +				printf("LMP features:\t0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x" +					" 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n", +					features[0], features[1], +					features[2], features[3], +					features[4], features[5], +					features[6], features[7]); +				printf("%s\n", lmp_featurestostr(features, "\t\t", 63)); +			} + +			if (cc) { +				usleep(10000); +				hci_disconnect(dd, handle, HCI_OE_USER_ENDED_CONNECTION, 10000); +			} +		} + +		printf("\n"); + +		if (cr) +			free(cr); +	} + +	close(dd); +	free(info); +} +  /* Send arbitrary HCI commands */  static struct option cmd_options[] = { @@ -1637,6 +1942,7 @@ static struct {  	{ "scan",   cmd_scan,   "Scan for remote devices"              },  	{ "name",   cmd_name,   "Get name from remote device"          },  	{ "info",   cmd_info,   "Get information from remote device"   }, +	{ "find",   cmd_find,   "Search for remote devices"            },  	{ "cmd",    cmd_cmd,    "Submit arbitrary HCI commands"        },  	{ "con",    cmd_con,    "Display active connections"           },  	{ "cc",     cmd_cc,     "Create connection to remote device"   }, diff --git a/tools/oui.c b/tools/oui.c new file mode 100644 index 00000000..7dff358b --- /dev/null +++ b/tools/oui.c @@ -0,0 +1,100 @@ +/* + * + *  BlueZ - Bluetooth protocol stack for Linux + * + *  Copyright (C) 2004-2005  Marcel Holtmann <marcel@holtmann.org> + * + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License version 2 as + *  published by the Free Software Foundation; + * + *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + *  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + *  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY + *  CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES  + *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN  + *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF  + *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + *  ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,  + *  COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS  + *  SOFTWARE IS DISCLAIMED. + * + * + *  $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <errno.h> +#include <malloc.h> +#include <string.h> +#include <sys/stat.h> + +#include "oui.h" + +/* http://standards.ieee.org/regauth/oui/oui.txt */ + +#define OUIFILE "/usr/share/misc/oui.txt" + +#define AWKCMD "/usr/bin/awk" +#define TRCMD  "/usr/bin/tr" + +char *ouitocomp(const char *oui) +{ +	struct stat st; +	FILE *input; +	char cmd[512]; +	char *str; +	size_t len; + +	if (stat(OUIFILE, &st) < 0) +		return NULL; + +	if (stat(AWKCMD, &st) < 0) +		return NULL; + +	if (stat(TRCMD, &st) < 0) +		return NULL; + +	str = malloc(128); +	if (!str) +		return NULL; + +	memset(str, 0, 128); + +	snprintf(cmd, sizeof(cmd) - 1, "%s -F'\\t' '/^" +		"%s.*\\(hex\\).*/{ print $3 }' %s" +		" | %s -d '\\n\\r'", AWKCMD, oui, OUIFILE, TRCMD); + +	input = popen(cmd, "r"); +	if (!input) { +		free(str); +		return NULL; +	} + +	len = fread(str, 127, 1, input); +	pclose(input); + +	return str; +} + +int oui2comp(const char *oui, char *comp, size_t size) +{ +	char *tmp; + +	tmp = ouitocomp(oui); +	if (!tmp) +		return -1; + +	snprintf(comp, size, "%s", tmp); + +	free(tmp); + +	return 0; +} diff --git a/tools/oui.h b/tools/oui.h new file mode 100644 index 00000000..918a248b --- /dev/null +++ b/tools/oui.h @@ -0,0 +1,30 @@ +/* + * + *  BlueZ - Bluetooth protocol stack for Linux + * + *  Copyright (C) 2004-2005  Marcel Holtmann <marcel@holtmann.org> + * + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License version 2 as + *  published by the Free Software Foundation; + * + *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + *  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + *  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY + *  CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES  + *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN  + *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF  + *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + *  ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,  + *  COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS  + *  SOFTWARE IS DISCLAIMED. + * + * + *  $Id$ + */ + +char *ouitocomp(const char *oui); +int oui2comp(const char *oui, char *comp, size_t size); | 
