Start committing Bluetooth HID (Human Interface Device) support.
Note: bthidd(8) is still not complete. Need to commit kernel support (a-la Linux /dev/input) to feed HID events into kernel. Also need to write bthidd(8) and bthidd.conf(5) man pages.
This commit is contained in:
parent
079a8a3e69
commit
6490c2ffab
15
usr.sbin/bluetooth/bthidcontrol/Makefile
Normal file
15
usr.sbin/bluetooth/bthidcontrol/Makefile
Normal file
@ -0,0 +1,15 @@
|
||||
# $Id: Makefile,v 1.2 2004/02/13 21:44:41 max Exp $
|
||||
# $FreeBSD$
|
||||
|
||||
.PATH: ${.CURDIR}/../bthidd
|
||||
|
||||
PROG= bthidcontrol
|
||||
MAN= bthidcontrol.8
|
||||
SRCS= bthidcontrol.c hid.c lexer.l parser.y sdp.c
|
||||
WARNS?= 1
|
||||
CFLAGS+= -DBTHIDCONTROL=1 -I${.CURDIR}/../bthidd
|
||||
|
||||
DPADD= ${LIBBLUETOOTH} ${LIBSDP} ${LIBUSBHID}
|
||||
LDADD= -lbluetooth -lsdp -lusbhid
|
||||
|
||||
.include <bsd.prog.mk>
|
99
usr.sbin/bluetooth/bthidcontrol/bthidcontrol.8
Normal file
99
usr.sbin/bluetooth/bthidcontrol/bthidcontrol.8
Normal file
@ -0,0 +1,99 @@
|
||||
.\" Copyright (c) 2004 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: bthidcontrol.8,v 1.1 2004/02/13 21:44:41 max Exp $
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd February 13, 2004
|
||||
.Dt BTHIDCONTROL 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm bthidcontrol
|
||||
.Nd Bluetooth HID control utility
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Fl h
|
||||
.Nm
|
||||
.Op Fl a Ar BD_ADDR
|
||||
.Op Fl c Ar file
|
||||
.Op Fl H Ar file
|
||||
.Ar command
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
utility can be used to query remote Bluetooth HID devices, dump HID descriptors
|
||||
in human readable form and perform simple manipulations on the Bluetooth HID
|
||||
daemon configuration files.
|
||||
.Pp
|
||||
The
|
||||
.Nm
|
||||
utility 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 BD_ADDR
|
||||
Specify BD_ADDR for the HID device.
|
||||
Example:
|
||||
.Fl a Li 00:01:02:03:04:05 .
|
||||
.It Fl c Ar file
|
||||
Specify path to the Bluetooth HID daemon configuration file.
|
||||
The default path is
|
||||
.Pa /etc/bluetooth/bthidd.conf .
|
||||
.It Fl H Ar file
|
||||
Specify path to the Bluetooth HID daemon known HIDs file.
|
||||
The default path is
|
||||
.Pa /var/db/bthidd.hids .
|
||||
.It Fl h
|
||||
Display usage message and exit.
|
||||
.It Ar command
|
||||
One of the supported commands (see below).
|
||||
Special command
|
||||
.Cm help
|
||||
can be used to obtain the list of all supported commands.
|
||||
To get more information about specific command use
|
||||
.Cm help Ar command .
|
||||
.El
|
||||
.Sh COMMANDS
|
||||
The currently supported node commands in
|
||||
.Nm
|
||||
are:
|
||||
.Pp
|
||||
.Bl -tag -offset indent -compact
|
||||
.It Cm Query
|
||||
.It Cm Dump
|
||||
.It Cm Known
|
||||
.It Cm Forget
|
||||
.El
|
||||
.Sh DIAGNOSTICS
|
||||
.Ex -std
|
||||
.Sh FILES
|
||||
.Bl -tag -width ".Pa /etc/bluetooth/bthidd.conf" -compact
|
||||
.It Pa /etc/bluetooth/bthidd.conf
|
||||
.It Pa /var/db/bthidd.hids
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr bthidd 8
|
||||
.Sh AUTHORS
|
||||
.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
|
208
usr.sbin/bluetooth/bthidcontrol/bthidcontrol.c
Normal file
208
usr.sbin/bluetooth/bthidcontrol/bthidcontrol.c
Normal file
@ -0,0 +1,208 @@
|
||||
/*
|
||||
* bthidcontrol.c
|
||||
*
|
||||
* Copyright (c) 2004 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: bthidcontrol.c,v 1.2 2004/02/13 21:44:41 max Exp $
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/queue.h>
|
||||
#include <assert.h>
|
||||
#include <bluetooth.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <usbhid.h>
|
||||
#include "bthid_config.h"
|
||||
#include "bthidcontrol.h"
|
||||
|
||||
static int do_bthid_command(bdaddr_p bdaddr, int argc, char **argv);
|
||||
static struct bthid_command * find_bthid_command(char const *command, struct bthid_command *category);
|
||||
static void print_bthid_command(struct bthid_command *category);
|
||||
static void usage(void);
|
||||
|
||||
int32_t hid_sdp_query(bdaddr_t const *local, bdaddr_t const *remote, int32_t *error);
|
||||
|
||||
/*
|
||||
* bthidcontrol
|
||||
*/
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
bdaddr_t bdaddr;
|
||||
int opt;
|
||||
|
||||
hid_init(NULL);
|
||||
memcpy(&bdaddr, NG_HCI_BDADDR_ANY, sizeof(bdaddr));
|
||||
|
||||
while ((opt = getopt(argc, argv, "a:c:H:h")) != -1) {
|
||||
switch (opt) {
|
||||
case 'a': /* bdaddr */
|
||||
if (!bt_aton(optarg, &bdaddr)) {
|
||||
struct hostent *he = NULL;
|
||||
|
||||
if ((he = bt_gethostbyname(optarg)) == NULL)
|
||||
errx(1, "%s: %s", optarg, hstrerror(h_errno));
|
||||
|
||||
memcpy(&bdaddr, he->h_addr, sizeof(bdaddr));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'c': /* config file */
|
||||
config_file = optarg;
|
||||
break;
|
||||
|
||||
case 'H': /* HIDs file */
|
||||
hids_file = optarg;
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
default:
|
||||
usage();
|
||||
/* NOT REACHED */
|
||||
}
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (*argv == NULL)
|
||||
usage();
|
||||
|
||||
return (do_bthid_command(&bdaddr, argc, argv));
|
||||
} /* main */
|
||||
|
||||
/* Execute commands */
|
||||
static int
|
||||
do_bthid_command(bdaddr_p bdaddr, int argc, char **argv)
|
||||
{
|
||||
char *cmd = argv[0];
|
||||
struct bthid_command *c = NULL;
|
||||
int e, help;
|
||||
|
||||
help = 0;
|
||||
if (strcasecmp(cmd, "help") == 0) {
|
||||
argc --;
|
||||
argv ++;
|
||||
|
||||
if (argc <= 0) {
|
||||
fprintf(stdout, "Supported commands:\n");
|
||||
print_bthid_command(sdp_commands);
|
||||
print_bthid_command(hid_commands);
|
||||
fprintf(stdout, "\nFor more information use " \
|
||||
"'help command'\n");
|
||||
|
||||
return (OK);
|
||||
}
|
||||
|
||||
help = 1;
|
||||
cmd = argv[0];
|
||||
}
|
||||
|
||||
c = find_bthid_command(cmd, sdp_commands);
|
||||
if (c == NULL)
|
||||
c = find_bthid_command(cmd, hid_commands);
|
||||
|
||||
if (c == NULL) {
|
||||
fprintf(stdout, "Unknown command: \"%s\"\n", cmd);
|
||||
return (ERROR);
|
||||
}
|
||||
|
||||
if (!help)
|
||||
e = (c->handler)(bdaddr, -- argc, ++ argv);
|
||||
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_bthid_command */
|
||||
|
||||
/* Try to find command in specified category */
|
||||
static struct bthid_command *
|
||||
find_bthid_command(char const *command, struct bthid_command *category)
|
||||
{
|
||||
struct bthid_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_bthid_command */
|
||||
|
||||
/* Print commands in specified category */
|
||||
static void
|
||||
print_bthid_command(struct bthid_command *category)
|
||||
{
|
||||
struct bthid_command *c = NULL;
|
||||
|
||||
for (c = category; c->command != NULL; c++)
|
||||
fprintf(stdout, "\t%s\n", c->command);
|
||||
} /* print_bthid_command */
|
||||
|
||||
/* Usage */
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage: bthidcontrol options command\n" \
|
||||
"Where options are:\n"
|
||||
" -a bdaddr specify bdaddr\n" \
|
||||
" -c file specify path to the bthidd config file\n" \
|
||||
" -H file specify path to the bthidd HIDs file\n" \
|
||||
" -h display usage and quit\n" \
|
||||
" command one of the supported commands\n");
|
||||
exit(255);
|
||||
} /* usage */
|
||||
|
50
usr.sbin/bluetooth/bthidcontrol/bthidcontrol.h
Normal file
50
usr.sbin/bluetooth/bthidcontrol/bthidcontrol.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* bthidcontrol.h
|
||||
*
|
||||
* Copyright (c) 2004 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: bthidcontrol.h,v 1.1 2004/02/12 23:25:51 max Exp $
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef __BTHIDCONTROL_H__
|
||||
#define __BTHIDCONTROL_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 bthid_command {
|
||||
char const *command;
|
||||
char const *description;
|
||||
int (*handler)(bdaddr_t *, int, char **);
|
||||
};
|
||||
|
||||
extern struct bthid_command hid_commands[];
|
||||
extern struct bthid_command sdp_commands[];
|
||||
|
||||
#endif /* __BTHIDCONTROL_H__ */
|
||||
|
209
usr.sbin/bluetooth/bthidcontrol/hid.c
Normal file
209
usr.sbin/bluetooth/bthidcontrol/hid.c
Normal file
@ -0,0 +1,209 @@
|
||||
/*
|
||||
* hid.c
|
||||
*
|
||||
* Copyright (c) 2004 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: hid.c,v 1.3 2004/02/17 22:14:57 max Exp $
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/queue.h>
|
||||
#include <bluetooth.h>
|
||||
#include <dev/usb/usb.h>
|
||||
#include <dev/usb/usbhid.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <usbhid.h>
|
||||
#include "bthid_config.h"
|
||||
#include "bthidcontrol.h"
|
||||
|
||||
static void hid_dump_descriptor (report_desc_t r);
|
||||
static void hid_dump_item (char const *label, struct hid_item *h);
|
||||
|
||||
static int
|
||||
hid_dump(bdaddr_t *bdaddr, int argc, char **argv)
|
||||
{
|
||||
struct hid_device *hd = NULL;
|
||||
int e = FAILED;
|
||||
|
||||
if (read_config_file() == 0) {
|
||||
if ((hd = get_hid_device(bdaddr)) != NULL) {
|
||||
hid_dump_descriptor(hd->desc);
|
||||
e = OK;
|
||||
}
|
||||
|
||||
clean_config();
|
||||
}
|
||||
|
||||
return (e);
|
||||
}
|
||||
|
||||
static int
|
||||
hid_forget(bdaddr_t *bdaddr, int argc, char **argv)
|
||||
{
|
||||
struct hid_device *hd = NULL;
|
||||
int e = FAILED;
|
||||
|
||||
if (read_config_file() == 0) {
|
||||
if (read_hids_file() == 0) {
|
||||
if ((hd = get_hid_device(bdaddr)) != NULL) {
|
||||
hd->new_device = 1;
|
||||
if (write_hids_file() == 0)
|
||||
e = OK;
|
||||
}
|
||||
}
|
||||
|
||||
clean_config();
|
||||
}
|
||||
|
||||
return (e);
|
||||
}
|
||||
|
||||
static int
|
||||
hid_known(bdaddr_t *bdaddr, int argc, char **argv)
|
||||
{
|
||||
struct hid_device *hd = NULL;
|
||||
struct hostent *he = NULL;
|
||||
int e = FAILED;
|
||||
|
||||
if (read_config_file() == 0) {
|
||||
if (read_hids_file() == 0) {
|
||||
e = OK;
|
||||
|
||||
for (hd = get_next_hid_device(hd);
|
||||
hd != NULL;
|
||||
hd = get_next_hid_device(hd)) {
|
||||
if (hd->new_device)
|
||||
continue;
|
||||
|
||||
he = bt_gethostbyaddr((char *) &hd->bdaddr,
|
||||
sizeof(hd->bdaddr),
|
||||
AF_BLUETOOTH);
|
||||
|
||||
fprintf(stdout,
|
||||
"%s %s\n", bt_ntoa(&hd->bdaddr, NULL),
|
||||
(he != NULL && he->h_name != NULL)?
|
||||
he->h_name : "");
|
||||
}
|
||||
}
|
||||
|
||||
clean_config();
|
||||
}
|
||||
|
||||
return (e);
|
||||
}
|
||||
|
||||
static void
|
||||
hid_dump_descriptor(report_desc_t r)
|
||||
{
|
||||
struct hid_data *d = NULL;
|
||||
struct hid_item h;
|
||||
|
||||
for (d = hid_start_parse(r, ~0, -1); hid_get_item(d, &h); ) {
|
||||
switch (h.kind) {
|
||||
case hid_collection:
|
||||
fprintf(stdout,
|
||||
"Collection page=%s usage=%s\n", hid_usage_page(HID_PAGE(h.usage)),
|
||||
hid_usage_in_page(h.usage));
|
||||
break;
|
||||
|
||||
case hid_endcollection:
|
||||
fprintf(stdout, "End collection\n");
|
||||
break;
|
||||
|
||||
case hid_input:
|
||||
hid_dump_item("Input ", &h);
|
||||
break;
|
||||
|
||||
case hid_output:
|
||||
hid_dump_item("Output ", &h);
|
||||
break;
|
||||
|
||||
case hid_feature:
|
||||
hid_dump_item("Feature", &h);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
hid_end_parse(d);
|
||||
}
|
||||
|
||||
static void
|
||||
hid_dump_item(char const *label, struct hid_item *h)
|
||||
{
|
||||
fprintf(stdout,
|
||||
"%s id=%u size=%u count=%u page=%s usage=%s%s%s%s%s%s%s%s%s%s",
|
||||
label, (uint8_t) h->report_ID, h->report_size, h->report_count,
|
||||
hid_usage_page(HID_PAGE(h->usage)),
|
||||
hid_usage_in_page(h->usage),
|
||||
h->flags & HIO_CONST ? " Const" : "",
|
||||
h->flags & HIO_VARIABLE ? " Variable" : "",
|
||||
h->flags & HIO_RELATIVE ? " Relative" : "",
|
||||
h->flags & HIO_WRAP ? " Wrap" : "",
|
||||
h->flags & HIO_NONLINEAR ? " NonLinear" : "",
|
||||
h->flags & HIO_NOPREF ? " NoPref" : "",
|
||||
h->flags & HIO_NULLSTATE ? " NullState" : "",
|
||||
h->flags & HIO_VOLATILE ? " Volatile" : "",
|
||||
h->flags & HIO_BUFBYTES ? " BufBytes" : "");
|
||||
|
||||
fprintf(stdout,
|
||||
", logical range %d..%d",
|
||||
h->logical_minimum, h->logical_maximum);
|
||||
|
||||
if (h->physical_minimum != h->physical_maximum)
|
||||
fprintf(stdout,
|
||||
", physical range %d..%d",
|
||||
h->physical_minimum, h->physical_maximum);
|
||||
|
||||
if (h->unit)
|
||||
fprintf(stdout,
|
||||
", unit=0x%02x exp=%d", h->unit, h->unit_exponent);
|
||||
|
||||
fprintf(stdout, "\n");
|
||||
}
|
||||
|
||||
struct bthid_command hid_commands[] = {
|
||||
{
|
||||
"Dump",
|
||||
"Dump HID descriptor for the specified device in human readable form. The\n" \
|
||||
"device must have an entry in the Bluetooth HID daemon configuration file.\n",
|
||||
hid_dump
|
||||
},
|
||||
{
|
||||
"Known",
|
||||
"List all known to the Bluetooth HID daemon devices.\n",
|
||||
hid_known
|
||||
},
|
||||
{
|
||||
"Forget",
|
||||
"Forget (mark as new) specified HID device. This command is useful when it\n" \
|
||||
"is required to remove device from the known HIDs file. This should be done\n" \
|
||||
"when reset button was pressed on the device or the battery was changed. The\n"\
|
||||
"Bluetooth HID daemon should be restarted.\n",
|
||||
hid_forget
|
||||
},
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
|
432
usr.sbin/bluetooth/bthidcontrol/sdp.c
Normal file
432
usr.sbin/bluetooth/bthidcontrol/sdp.c
Normal file
@ -0,0 +1,432 @@
|
||||
/*
|
||||
* sdp.c
|
||||
*
|
||||
* Copyright (c) 2004 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: sdp.c,v 1.3 2004/02/17 22:14:57 max Exp $
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/queue.h>
|
||||
#include <bluetooth.h>
|
||||
#include <dev/usb/usb.h>
|
||||
#include <dev/usb/usbhid.h>
|
||||
#include <errno.h>
|
||||
#include <sdp.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <usbhid.h>
|
||||
#include "bthid_config.h"
|
||||
#include "bthidcontrol.h"
|
||||
|
||||
static int32_t hid_sdp_query (bdaddr_t const *local, struct hid_device *hd, int32_t *error);
|
||||
static int32_t hid_sdp_parse_protocol_descriptor_list (sdp_attr_p a);
|
||||
static int32_t hid_sdp_parse_hid_descriptor (sdp_attr_p a);
|
||||
static int32_t hid_sdp_parse_boolean (sdp_attr_p a);
|
||||
|
||||
static uint16_t service = SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE;
|
||||
|
||||
static uint32_t attrs[] = {
|
||||
SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
|
||||
SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
|
||||
SDP_ATTR_RANGE (SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS,
|
||||
SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS),
|
||||
SDP_ATTR_RANGE( 0x0205, /* HIDReconnectInitiate */
|
||||
0x0206), /* HIDDesctiptorList */
|
||||
SDP_ATTR_RANGE( 0x020a, /* HIDBatteryPower */
|
||||
0x020a),
|
||||
SDP_ATTR_RANGE( 0x020d, /* HIDNormallyConnectable */
|
||||
0x020d)
|
||||
};
|
||||
#define nattrs (sizeof(attrs)/sizeof(attrs[0]))
|
||||
|
||||
static sdp_attr_t values[8];
|
||||
#define nvalues (sizeof(values)/sizeof(values[0]))
|
||||
|
||||
static uint8_t buffer[nvalues][512];
|
||||
|
||||
/*
|
||||
* Query remote device
|
||||
*/
|
||||
|
||||
#undef hid_sdp_query_exit
|
||||
#define hid_sdp_query_exit(e) { \
|
||||
if (error != NULL) \
|
||||
*error = (e); \
|
||||
if (ss != NULL) { \
|
||||
sdp_close(ss); \
|
||||
ss = NULL; \
|
||||
} \
|
||||
return (((e) == 0)? 0 : -1); \
|
||||
}
|
||||
|
||||
static int32_t
|
||||
hid_sdp_query(bdaddr_t const *local, struct hid_device *hd, int32_t *error)
|
||||
{
|
||||
void *ss = NULL;
|
||||
uint8_t *hid_descriptor = NULL;
|
||||
int32_t i, control_psm = -1, interrupt_psm = -1,
|
||||
reconnect_initiate = -1,
|
||||
normally_connectable = 0, battery_power = 0,
|
||||
hid_descriptor_length = -1;
|
||||
|
||||
if (local == NULL)
|
||||
local = NG_HCI_BDADDR_ANY;
|
||||
if (hd == NULL)
|
||||
hid_sdp_query_exit(EINVAL);
|
||||
|
||||
for (i = 0; i < nvalues; i ++) {
|
||||
values[i].flags = SDP_ATTR_INVALID;
|
||||
values[i].attr = 0;
|
||||
values[i].vlen = sizeof(buffer[i]);
|
||||
values[i].value = buffer[i];
|
||||
}
|
||||
|
||||
if ((ss = sdp_open(local, &hd->bdaddr)) == NULL)
|
||||
hid_sdp_query_exit(ENOMEM);
|
||||
if (sdp_error(ss) != 0)
|
||||
hid_sdp_query_exit(sdp_error(ss));
|
||||
if (sdp_search(ss, 1, &service, nattrs, attrs, nvalues, values) != 0)
|
||||
hid_sdp_query_exit(sdp_error(ss));
|
||||
|
||||
sdp_close(ss);
|
||||
ss = NULL;
|
||||
|
||||
for (i = 0; i < nvalues; i ++) {
|
||||
if (values[i].flags != SDP_ATTR_OK)
|
||||
continue;
|
||||
|
||||
switch (values[i].attr) {
|
||||
case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
|
||||
control_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
|
||||
break;
|
||||
|
||||
case SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS:
|
||||
interrupt_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
|
||||
break;
|
||||
|
||||
case 0x0205: /* HIDReconnectInitiate */
|
||||
reconnect_initiate = hid_sdp_parse_boolean(&values[i]);
|
||||
break;
|
||||
|
||||
case 0x0206: /* HIDDesctiptorList */
|
||||
if (hid_sdp_parse_hid_descriptor(&values[i]) == 0) {
|
||||
hid_descriptor = values[i].value;
|
||||
hid_descriptor_length = values[i].vlen;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x020a: /* HIDBatteryPower */
|
||||
battery_power = hid_sdp_parse_boolean(&values[i]);
|
||||
break;
|
||||
|
||||
case 0x020d: /* HIDNormallyConnectable */
|
||||
normally_connectable = hid_sdp_parse_boolean(&values[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (control_psm == -1 || interrupt_psm == -1 ||
|
||||
reconnect_initiate == -1 || normally_connectable == -1 ||
|
||||
hid_descriptor == NULL || hid_descriptor_length == -1)
|
||||
hid_sdp_query_exit(ENOATTR);
|
||||
|
||||
hd->control_psm = control_psm;
|
||||
hd->interrupt_psm = interrupt_psm;
|
||||
hd->reconnect_initiate = reconnect_initiate? 1 : 0;
|
||||
hd->battery_power = battery_power? 1 : 0;
|
||||
hd->normally_connectable = normally_connectable? 1 : 0;
|
||||
hd->desc = hid_use_report_desc(hid_descriptor, hid_descriptor_length);
|
||||
if (hd->desc == NULL)
|
||||
hid_sdp_query_exit(ENOMEM);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* seq len 2
|
||||
* seq len 2
|
||||
* uuid value 3
|
||||
* uint16 value 3
|
||||
* seq len 2
|
||||
* uuid value 3
|
||||
*/
|
||||
|
||||
static int32_t
|
||||
hid_sdp_parse_protocol_descriptor_list(sdp_attr_p a)
|
||||
{
|
||||
uint8_t *ptr = a->value;
|
||||
uint8_t *end = a->value + a->vlen;
|
||||
int32_t type, len, uuid, psm;
|
||||
|
||||
if (end - ptr < 15)
|
||||
return (-1);
|
||||
|
||||
if (a->attr == SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS) {
|
||||
SDP_GET8(type, ptr);
|
||||
switch (type) {
|
||||
case SDP_DATA_SEQ8:
|
||||
SDP_GET8(len, ptr);
|
||||
break;
|
||||
|
||||
case SDP_DATA_SEQ16:
|
||||
SDP_GET16(len, ptr);
|
||||
break;
|
||||
|
||||
case SDP_DATA_SEQ32:
|
||||
SDP_GET32(len, ptr);
|
||||
break;
|
||||
|
||||
default:
|
||||
return (-1);
|
||||
}
|
||||
if (ptr + len > end)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
SDP_GET8(type, ptr);
|
||||
switch (type) {
|
||||
case SDP_DATA_SEQ8:
|
||||
SDP_GET8(len, ptr);
|
||||
break;
|
||||
|
||||
case SDP_DATA_SEQ16:
|
||||
SDP_GET16(len, ptr);
|
||||
break;
|
||||
|
||||
case SDP_DATA_SEQ32:
|
||||
SDP_GET32(len, ptr);
|
||||
break;
|
||||
|
||||
default:
|
||||
return (-1);
|
||||
}
|
||||
if (ptr + len > end)
|
||||
return (-1);
|
||||
|
||||
/* Protocol */
|
||||
SDP_GET8(type, ptr);
|
||||
switch (type) {
|
||||
case SDP_DATA_SEQ8:
|
||||
SDP_GET8(len, ptr);
|
||||
break;
|
||||
|
||||
case SDP_DATA_SEQ16:
|
||||
SDP_GET16(len, ptr);
|
||||
break;
|
||||
|
||||
case SDP_DATA_SEQ32:
|
||||
SDP_GET32(len, ptr);
|
||||
break;
|
||||
|
||||
default:
|
||||
return (-1);
|
||||
}
|
||||
if (ptr + len > end)
|
||||
return (-1);
|
||||
|
||||
/* UUID */
|
||||
if (ptr + 3 > end)
|
||||
return (-1);
|
||||
SDP_GET8(type, ptr);
|
||||
switch (type) {
|
||||
case SDP_DATA_UUID16:
|
||||
SDP_GET16(uuid, ptr);
|
||||
if (uuid != SDP_UUID_PROTOCOL_L2CAP)
|
||||
return (-1);
|
||||
break;
|
||||
|
||||
case SDP_DATA_UUID32: /* XXX FIXME can we have 32-bit UUID */
|
||||
case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
|
||||
default:
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* PSM */
|
||||
if (ptr + 3 > end)
|
||||
return (-1);
|
||||
SDP_GET8(type, ptr);
|
||||
if (type != SDP_DATA_UINT16)
|
||||
return (-1);
|
||||
SDP_GET16(psm, ptr);
|
||||
|
||||
return (psm);
|
||||
}
|
||||
|
||||
/*
|
||||
* seq len 2
|
||||
* seq len 2
|
||||
* uint8 value8 2
|
||||
* str value 3
|
||||
*/
|
||||
|
||||
static int32_t
|
||||
hid_sdp_parse_hid_descriptor(sdp_attr_p a)
|
||||
{
|
||||
uint8_t *ptr = a->value;
|
||||
uint8_t *end = a->value + a->vlen;
|
||||
int32_t type, len, descriptor_type;
|
||||
|
||||
if (end - ptr < 9)
|
||||
return (-1);
|
||||
|
||||
SDP_GET8(type, ptr);
|
||||
switch (type) {
|
||||
case SDP_DATA_SEQ8:
|
||||
SDP_GET8(len, ptr);
|
||||
break;
|
||||
|
||||
case SDP_DATA_SEQ16:
|
||||
SDP_GET16(len, ptr);
|
||||
break;
|
||||
|
||||
case SDP_DATA_SEQ32:
|
||||
SDP_GET32(len, ptr);
|
||||
break;
|
||||
|
||||
default:
|
||||
return (-1);
|
||||
}
|
||||
if (ptr + len > end)
|
||||
return (-1);
|
||||
|
||||
while (ptr < end) {
|
||||
/* Descriptor */
|
||||
SDP_GET8(type, ptr);
|
||||
switch (type) {
|
||||
case SDP_DATA_SEQ8:
|
||||
if (ptr + 1 > end)
|
||||
return (-1);
|
||||
SDP_GET8(len, ptr);
|
||||
break;
|
||||
|
||||
case SDP_DATA_SEQ16:
|
||||
if (ptr + 2 > end)
|
||||
return (-1);
|
||||
SDP_GET16(len, ptr);
|
||||
break;
|
||||
|
||||
case SDP_DATA_SEQ32:
|
||||
if (ptr + 4 > end)
|
||||
return (-1);
|
||||
SDP_GET32(len, ptr);
|
||||
break;
|
||||
|
||||
default:
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Descripor type */
|
||||
if (ptr + 1 > end)
|
||||
return (-1);
|
||||
SDP_GET8(type, ptr);
|
||||
if (type != SDP_DATA_UINT8 || ptr + 1 > end)
|
||||
return (-1);
|
||||
SDP_GET8(descriptor_type, ptr);
|
||||
|
||||
/* Descriptor value */
|
||||
if (ptr + 1 > end)
|
||||
return (-1);
|
||||
SDP_GET8(type, ptr);
|
||||
switch (type) {
|
||||
case SDP_DATA_STR8:
|
||||
if (ptr + 1 > end)
|
||||
return (-1);
|
||||
SDP_GET8(len, ptr);
|
||||
break;
|
||||
|
||||
case SDP_DATA_STR16:
|
||||
if (ptr + 2 > end)
|
||||
return (-1);
|
||||
SDP_GET16(len, ptr);
|
||||
break;
|
||||
|
||||
case SDP_DATA_STR32:
|
||||
if (ptr + 4 > end)
|
||||
return (-1);
|
||||
SDP_GET32(len, ptr);
|
||||
break;
|
||||
|
||||
default:
|
||||
return (-1);
|
||||
}
|
||||
if (ptr + len > end)
|
||||
return (-1);
|
||||
|
||||
if (descriptor_type == UDESC_REPORT && len > 0) {
|
||||
a->value = ptr;
|
||||
a->vlen = len;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
ptr += len;
|
||||
}
|
||||
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* bool8 int8 */
|
||||
static int32_t
|
||||
hid_sdp_parse_boolean(sdp_attr_p a)
|
||||
{
|
||||
if (a->vlen != 2 || a->value[0] != SDP_DATA_BOOL)
|
||||
return (-1);
|
||||
|
||||
return (a->value[1]);
|
||||
}
|
||||
|
||||
/* Perform SDP query */
|
||||
static int32_t
|
||||
hid_query(bdaddr_t *bdaddr, int argc, char **argv)
|
||||
{
|
||||
struct hid_device hd;
|
||||
int e;
|
||||
|
||||
memcpy(&hd.bdaddr, bdaddr, sizeof(hd.bdaddr));
|
||||
if (hid_sdp_query(NULL, &hd, &e) < 0) {
|
||||
fprintf(stderr, "Could not perform SDP query on the " \
|
||||
"device %s. %s (%d)\n", bt_ntoa(bdaddr, NULL),
|
||||
strerror(e), e);
|
||||
return (FAILED);
|
||||
}
|
||||
|
||||
print_hid_device(&hd, stdout);
|
||||
|
||||
return (OK);
|
||||
}
|
||||
|
||||
struct bthid_command sdp_commands[] =
|
||||
{
|
||||
{
|
||||
"Query",
|
||||
"Perform SDP query to the specified device and print HID configuration entry\n"\
|
||||
"for the device. The configuration entry should be appended to the Bluetooth\n"\
|
||||
"HID daemon configuration file and the daemon should be restarted.\n",
|
||||
hid_query
|
||||
},
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
|
14
usr.sbin/bluetooth/bthidd/Makefile
Normal file
14
usr.sbin/bluetooth/bthidd/Makefile
Normal file
@ -0,0 +1,14 @@
|
||||
# $Id: Makefile,v 1.2 2004/02/13 21:46:20 max Exp $
|
||||
# $FreeBSD$
|
||||
|
||||
PROG= bthidd
|
||||
#MAN= bthidd.8 bthidd.conf.5
|
||||
NOMAN= 1
|
||||
SRCS= bthidd.c client.c hid.c lexer.l parser.y server.c session.c
|
||||
WARNS?= 1
|
||||
CFLAGS+= -I${.CURDIR}
|
||||
|
||||
DPADD= ${LIBBLUETOOTH} ${LIBSDP}
|
||||
LDADD= -lbluetooth -lusbhid
|
||||
|
||||
.include <bsd.prog.mk>
|
67
usr.sbin/bluetooth/bthidd/bthid_config.h
Normal file
67
usr.sbin/bluetooth/bthidd/bthid_config.h
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* bthid_config.h
|
||||
*
|
||||
* Copyright (c) 2004 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: bthid_config.h,v 1.3 2004/02/17 22:05:02 max Exp $
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _BTHID_CONFIG_H_
|
||||
#define _BTHID_CONFIG_H_ 1
|
||||
|
||||
#define BTHIDD_CONFFILE "/etc/bluetooth/bthidd.conf"
|
||||
#define BTHIDD_HIDSFILE "/var/db/bthidd.hids"
|
||||
|
||||
struct hid_device
|
||||
{
|
||||
bdaddr_t bdaddr; /* HID device BDADDR */
|
||||
uint16_t control_psm; /* control PSM */
|
||||
uint16_t interrupt_psm; /* interrupt PSM */
|
||||
unsigned new_device : 1;
|
||||
unsigned reconnect_initiate : 1;
|
||||
unsigned battery_power : 1;
|
||||
unsigned normally_connectable : 1;
|
||||
unsigned reserved : 12;
|
||||
report_desc_t desc; /* HID report descriptor */
|
||||
LIST_ENTRY(hid_device) next; /* link to the next */
|
||||
};
|
||||
typedef struct hid_device hid_device_t;
|
||||
typedef struct hid_device * hid_device_p;
|
||||
|
||||
extern char *config_file;
|
||||
extern char *hids_file;
|
||||
|
||||
int read_config_file (void);
|
||||
void clean_config (void);
|
||||
hid_device_p get_hid_device (bdaddr_p bdaddr);
|
||||
hid_device_p get_next_hid_device (hid_device_p d);
|
||||
void print_hid_device (hid_device_p hid_device, FILE *f);
|
||||
|
||||
int read_hids_file (void);
|
||||
int write_hids_file (void);
|
||||
|
||||
#endif /* ndef _BTHID_CONFIG_H_ */
|
||||
|
256
usr.sbin/bluetooth/bthidd/bthidd.c
Normal file
256
usr.sbin/bluetooth/bthidd/bthidd.c
Normal file
@ -0,0 +1,256 @@
|
||||
/*
|
||||
* bthidd.c
|
||||
*
|
||||
* Copyright (c) 2004 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: bthidd.c,v 1.4 2004/02/26 21:48:44 max Exp $
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <sys/queue.h>
|
||||
#include <assert.h>
|
||||
#include <bluetooth.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
#include <usbhid.h>
|
||||
#include "bthidd.h"
|
||||
#include "bthid_config.h"
|
||||
|
||||
static int write_pid_file (char const *file);
|
||||
static int remove_pid_file (char const *file);
|
||||
static int elapsed (int tval);
|
||||
static void sighandler (int s);
|
||||
static void usage (void);
|
||||
|
||||
/*
|
||||
* bthidd
|
||||
*/
|
||||
|
||||
static int done = 0; /* are we done? */
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct bthid_server srv;
|
||||
struct sigaction sa;
|
||||
char const *pid_file = BTHIDD_PIDFILE;
|
||||
int opt, detach, tval;
|
||||
|
||||
memcpy(&srv.bdaddr, NG_HCI_BDADDR_ANY, sizeof(srv.bdaddr));
|
||||
detach = 1;
|
||||
tval = 10; /* sec */
|
||||
|
||||
while ((opt = getopt(argc, argv, "a:c:dH:hp:t:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'a': /* BDADDR */
|
||||
if (!bt_aton(optarg, &srv.bdaddr)) {
|
||||
struct hostent *he = NULL;
|
||||
|
||||
if ((he = bt_gethostbyname(optarg)) == NULL)
|
||||
errx(1, "%s: %s", optarg, hstrerror(h_errno));
|
||||
|
||||
memcpy(&srv.bdaddr, he->h_addr, sizeof(srv.bdaddr));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'c': /* config file */
|
||||
config_file = optarg;
|
||||
break;
|
||||
|
||||
case 'd': /* do not detach */
|
||||
detach = 0;
|
||||
break;
|
||||
|
||||
case 'H': /* hids file */
|
||||
hids_file = optarg;
|
||||
break;
|
||||
|
||||
case 'p': /* pid file */
|
||||
pid_file = optarg;
|
||||
break;
|
||||
|
||||
case 't': { /* rescan interval */
|
||||
char *ep = NULL;
|
||||
|
||||
tval = strtol(optarg, &ep, 10);
|
||||
if (*ep != '\0' || tval <= 0)
|
||||
usage();
|
||||
} break;
|
||||
|
||||
case 'h':
|
||||
default:
|
||||
usage();
|
||||
/* NOT REACHED */
|
||||
}
|
||||
}
|
||||
|
||||
openlog(BTHIDD_IDENT, LOG_PID|LOG_PERROR|LOG_NDELAY, LOG_USER);
|
||||
|
||||
/* Become daemon if required */
|
||||
if (detach && daemon(0, 0) < 0) {
|
||||
syslog(LOG_CRIT, "Could not become daemon. %s (%d)",
|
||||
strerror(errno), errno);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Install signal handler */
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_handler = sighandler;
|
||||
|
||||
if (sigaction(SIGTERM, &sa, NULL) < 0 ||
|
||||
sigaction(SIGHUP, &sa, NULL) < 0 ||
|
||||
sigaction(SIGINT, &sa, NULL) < 0) {
|
||||
syslog(LOG_CRIT, "Could not install signal handlers. %s (%d)",
|
||||
strerror(errno), errno);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
sa.sa_handler = SIG_IGN;
|
||||
if (sigaction(SIGPIPE, &sa, NULL) < 0) {
|
||||
syslog(LOG_CRIT, "Could not install signal handlers. %s (%d)",
|
||||
strerror(errno), errno);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (read_config_file() < 0 || read_hids_file() < 0 ||
|
||||
server_init(&srv) < 0 || write_pid_file(pid_file) < 0)
|
||||
exit(1);
|
||||
|
||||
for (done = 0; !done; ) {
|
||||
if (elapsed(tval))
|
||||
client_rescan(&srv);
|
||||
|
||||
if (server_do(&srv) < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
server_shutdown(&srv);
|
||||
remove_pid_file(pid_file);
|
||||
clean_config();
|
||||
closelog();
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write pid file
|
||||
*/
|
||||
|
||||
static int
|
||||
write_pid_file(char const *file)
|
||||
{
|
||||
FILE *pid = NULL;
|
||||
|
||||
assert(file != NULL);
|
||||
|
||||
if ((pid = fopen(file, "w")) == NULL) {
|
||||
syslog(LOG_ERR, "Could not open file %s. %s (%d)",
|
||||
file, strerror(errno), errno);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
fprintf(pid, "%d", getpid());
|
||||
fclose(pid);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remote pid file
|
||||
*/
|
||||
|
||||
static int
|
||||
remove_pid_file(char const *file)
|
||||
{
|
||||
assert(file != NULL);
|
||||
|
||||
if (unlink(file) < 0) {
|
||||
syslog(LOG_ERR, "Could not unlink file %s. %s (%d)",
|
||||
file, strerror(errno), errno);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if desired time interval has elapsed
|
||||
*/
|
||||
|
||||
static int
|
||||
elapsed(int tval)
|
||||
{
|
||||
static struct timeval last = { 0, };
|
||||
struct timeval now;
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
|
||||
if (now.tv_sec - last.tv_sec >= tval) {
|
||||
last = now;
|
||||
return (1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Signal handler
|
||||
*/
|
||||
|
||||
static void
|
||||
sighandler(int s)
|
||||
{
|
||||
syslog(LOG_NOTICE, "Got signal %d, total number of signals %d",
|
||||
s, ++ done);
|
||||
}
|
||||
|
||||
/*
|
||||
* Display usage and exit
|
||||
*/
|
||||
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage: %s [options]\n" \
|
||||
"Where options are:\n" \
|
||||
" -a bdaddr specify BDADDR to listen on (default ANY)\n" \
|
||||
" -c file specify config file name\n" \
|
||||
" -d run in foreground\n" \
|
||||
" -H file specify known HIDs file name\n" \
|
||||
" -h display this message\n" \
|
||||
" -p file specify PID file name\n" \
|
||||
" -t tval client rescan interval (sec)\n" \
|
||||
"", BTHIDD_IDENT);
|
||||
exit(255);
|
||||
}
|
||||
|
72
usr.sbin/bluetooth/bthidd/bthidd.conf.sample
Normal file
72
usr.sbin/bluetooth/bthidd/bthidd.conf.sample
Normal file
@ -0,0 +1,72 @@
|
||||
# $FreeBSD$
|
||||
|
||||
device {
|
||||
bdaddr 00:50:f2:e5:68:84;
|
||||
control_psm 0x11;
|
||||
interrupt_psm 0x13;
|
||||
reconnect_initiate true;
|
||||
normally_connectable false;
|
||||
hid_descriptor {
|
||||
0x05 0x01 0x09 0x02 0xa1 0x01 0x85 0x02
|
||||
0x09 0x01 0xa1 0x00 0x05 0x09 0x19 0x01
|
||||
0x29 0x05 0x15 0x00 0x25 0x01 0x75 0x01
|
||||
0x95 0x05 0x81 0x02 0x75 0x03 0x95 0x01
|
||||
0x81 0x01 0x05 0x01 0x09 0x30 0x09 0x31
|
||||
0x09 0x38 0x15 0x81 0x25 0x7f 0x75 0x08
|
||||
0x95 0x03 0x81 0x06 0xc0 0xc0 0x05 0x0c
|
||||
0x09 0x01 0xa1 0x01 0x85 0x03 0x05 0x01
|
||||
0x09 0x02 0xa1 0x02 0x06 0x00 0xff 0x15
|
||||
0x00 0x25 0x03 0x95 0x01 0x75 0x02 0x0a
|
||||
0x01 0xfe 0x81 0x02 0x75 0x06 0x81 0x01
|
||||
0xc0 0xc0
|
||||
};
|
||||
}
|
||||
|
||||
device {
|
||||
bdaddr 00:50:f2:e3:fb:e1;
|
||||
control_psm 0x11;
|
||||
interrupt_psm 0x13;
|
||||
reconnect_initiate true;
|
||||
normally_connectable false;
|
||||
hid_descriptor {
|
||||
0x05 0x01 0x09 0x06 0xa1 0x01 0x85 0x01
|
||||
0x05 0x08 0x19 0x01 0x29 0x03 0x15 0x00
|
||||
0x25 0x01 0x75 0x01 0x95 0x03 0x91 0x02
|
||||
0x09 0x4b 0x95 0x01 0x91 0x02 0x95 0x04
|
||||
0x91 0x01 0x05 0x07 0x19 0xe0 0x29 0xe7
|
||||
0x95 0x08 0x81 0x02 0x75 0x08 0x95 0x01
|
||||
0x81 0x01 0x19 0x00 0x29 0x91 0x26 0xff
|
||||
0x00 0x95 0x06 0x81 0x00 0xc0 0x05 0x0c
|
||||
0x09 0x01 0xa1 0x01 0x85 0x02 0x05 0x0c
|
||||
0x15 0x00 0x25 0x01 0x75 0x01 0x95 0x1c
|
||||
0x09 0xe2 0x09 0xb7 0x09 0xcd 0x09 0xea
|
||||
0x09 0xe9 0x09 0xb6 0x09 0xb5 0x0a 0x83
|
||||
0x01 0x0a 0x1a 0x02 0x0a 0x79 0x02 0x0a
|
||||
0xab 0x01 0x0a 0x08 0x02 0x0a 0x02 0x02
|
||||
0x0a 0x03 0x02 0x0a 0x07 0x02 0x0a 0x01
|
||||
0x02 0x0a 0x92 0x01 0x0a 0x9c 0x01 0x09
|
||||
0x95 0x0a 0x23 0x02 0x0a 0x89 0x02 0x0a
|
||||
0x8b 0x02 0x0a 0x8c 0x02 0x0a 0x8a 0x01
|
||||
0x0a 0x99 0x01 0x0a 0xa7 0x01 0x0a 0xb6
|
||||
0x01 0x0a 0xb7 0x01 0x81 0x02 0x75 0x01
|
||||
0x95 0x04 0x81 0x01 0x06 0x00 0xff 0x0a
|
||||
0x02 0xff 0x26 0xff 0x00 0x95 0x01 0x75
|
||||
0x08 0x81 0x02 0xc0 0x05 0x01 0x09 0x80
|
||||
0xa1 0x01 0x85 0x03 0x19 0x81 0x29 0x83
|
||||
0x25 0x01 0x95 0x03 0x75 0x01 0x81 0x02
|
||||
0x95 0x05 0x81 0x01 0xc0 0x05 0x0c 0x09
|
||||
0x01 0xa1 0x01 0x85 0x04 0x05 0x01 0x09
|
||||
0x06 0xa1 0x02 0x06 0x00 0xff 0x15 0x00
|
||||
0x25 0x03 0x95 0x01 0x75 0x02 0x0a 0x01
|
||||
0xfe 0x81 0x02 0x75 0x06 0x81 0x01 0xc0
|
||||
0xc0 0x05 0x0c 0x09 0x01 0xa1 0x01 0x85
|
||||
0x05 0x05 0x01 0x09 0x06 0xa1 0x02 0x06
|
||||
0x00 0xff 0x25 0x01 0x75 0x01 0x95 0x02
|
||||
0x0a 0x03 0xfe 0x0a 0x04 0xfe 0x81 0x02
|
||||
0x95 0x06 0x81 0x01 0xc0 0xc0 0x05 0x0c
|
||||
0x09 0x01 0xa1 0x01 0x85 0xff 0x05 0x06
|
||||
0x95 0x01 0x75 0x02 0x19 0x24 0x29 0x26
|
||||
0x81 0x02 0x75 0x06 0x81 0x01 0xc0
|
||||
};
|
||||
}
|
||||
|
88
usr.sbin/bluetooth/bthidd/bthidd.h
Normal file
88
usr.sbin/bluetooth/bthidd/bthidd.h
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* bthidd.h
|
||||
*
|
||||
* Copyright (c) 2004 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: bthidd.h,v 1.4 2004/02/26 21:44:20 max Exp $
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _BTHIDD_H_
|
||||
#define _BTHIDD_H_ 1
|
||||
|
||||
#define BTHIDD_IDENT "bthidd"
|
||||
#define BTHIDD_PIDFILE "/var/run/" BTHIDD_IDENT ".pid"
|
||||
|
||||
struct bthid_session;
|
||||
|
||||
struct bthid_server
|
||||
{
|
||||
bdaddr_t bdaddr; /* local bdaddr */
|
||||
int cons; /* /dev/consolectl */
|
||||
int ctrl; /* control channel (listen) */
|
||||
int intr; /* interrupt channel (listen) */
|
||||
int maxfd; /* max fd in sets */
|
||||
fd_set rfdset; /* read descriptor set */
|
||||
fd_set wfdset; /* write descriptor set */
|
||||
LIST_HEAD(, bthid_session) sessions;
|
||||
};
|
||||
|
||||
typedef struct bthid_server bthid_server_t;
|
||||
typedef struct bthid_server * bthid_server_p;
|
||||
|
||||
struct bthid_session
|
||||
{
|
||||
bthid_server_p srv; /* pointer back to server */
|
||||
int ctrl; /* control channel */
|
||||
int intr; /* interrupt channel */
|
||||
bdaddr_t bdaddr; /* remote bdaddr */
|
||||
short state; /* session state */
|
||||
#define CLOSED 0
|
||||
#define W4CTRL 1
|
||||
#define W4INTR 2
|
||||
#define OPEN 3
|
||||
LIST_ENTRY(bthid_session) next; /* link to next */
|
||||
};
|
||||
|
||||
typedef struct bthid_session bthid_session_t;
|
||||
typedef struct bthid_session * bthid_session_p;
|
||||
|
||||
int server_init (bthid_server_p srv);
|
||||
void server_shutdown (bthid_server_p srv);
|
||||
int server_do (bthid_server_p srv);
|
||||
|
||||
int client_rescan (bthid_server_p srv);
|
||||
int client_connect (bthid_server_p srv, int fd);
|
||||
|
||||
bthid_session_p session_open (bthid_server_p srv, bdaddr_p bdaddr);
|
||||
bthid_session_p session_by_bdaddr(bthid_server_p srv, bdaddr_p bdaddr);
|
||||
bthid_session_p session_by_fd (bthid_server_p srv, int fd);
|
||||
void session_close (bthid_session_p s);
|
||||
|
||||
int hid_control (bthid_session_p s, char *data, int len);
|
||||
int hid_interrupt (bthid_session_p s, char *data, int len);
|
||||
|
||||
#endif /* ndef _BTHIDD_H_ */
|
||||
|
244
usr.sbin/bluetooth/bthidd/client.c
Normal file
244
usr.sbin/bluetooth/bthidd/client.c
Normal file
@ -0,0 +1,244 @@
|
||||
/*
|
||||
* client.c
|
||||
*
|
||||
* Copyright (c) 2004 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: client.c,v 1.6 2004/02/26 21:57:55 max Exp $
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/queue.h>
|
||||
#include <assert.h>
|
||||
#include <bluetooth.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
#include <usbhid.h>
|
||||
#include "bthidd.h"
|
||||
#include "bthid_config.h"
|
||||
|
||||
static int client_socket(bdaddr_p bdaddr, int psm);
|
||||
|
||||
/*
|
||||
* Get next config entry and create outbound connection (if required)
|
||||
*
|
||||
* XXX Do only one device at a time. At least one of my devices (3COM
|
||||
* Bluetooth PCCARD) rejects Create_Connection command if another
|
||||
* Create_Connection command is still pending. Weird...
|
||||
*/
|
||||
|
||||
static int connect_in_progress = 0;
|
||||
|
||||
int
|
||||
client_rescan(bthid_server_p srv)
|
||||
{
|
||||
static hid_device_p d = NULL;
|
||||
bthid_session_p s = NULL;
|
||||
|
||||
assert(srv != NULL);
|
||||
|
||||
if (connect_in_progress)
|
||||
return (0); /* another connect is still pending */
|
||||
|
||||
d = get_next_hid_device(d);
|
||||
if (d == NULL)
|
||||
return (0); /* XXX should not happen? empty config? */
|
||||
|
||||
if ((s = session_by_bdaddr(srv, &d->bdaddr)) != NULL)
|
||||
return (0); /* session already active */
|
||||
|
||||
if (!d->new_device) {
|
||||
if (d->reconnect_initiate)
|
||||
return (0); /* device will initiate reconnect */
|
||||
}
|
||||
|
||||
syslog(LOG_NOTICE, "Opening outbound session for %s " \
|
||||
"(new_device=%d, reconnect_initiate=%d)",
|
||||
bt_ntoa(&d->bdaddr, NULL), d->new_device, d->reconnect_initiate);
|
||||
|
||||
if ((s = session_open(srv, &d->bdaddr)) == NULL) {
|
||||
syslog(LOG_CRIT, "Could not open outbound session for %s. " \
|
||||
"Not enough memory", bt_ntoa(&d->bdaddr, NULL));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Open control channel */
|
||||
s->ctrl = client_socket(&s->bdaddr, d->control_psm);
|
||||
if (s->ctrl < 0) {
|
||||
syslog(LOG_ERR, "Could not open control channel to %s. %s (%d)",
|
||||
bt_ntoa(&s->bdaddr, NULL), strerror(errno), errno);
|
||||
session_close(s);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
s->state = W4CTRL;
|
||||
|
||||
FD_SET(s->ctrl, &srv->wfdset);
|
||||
if (s->ctrl > srv->maxfd)
|
||||
srv->maxfd = s->ctrl;
|
||||
|
||||
connect_in_progress = 1;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Process connect on the socket
|
||||
*/
|
||||
|
||||
int
|
||||
client_connect(bthid_server_p srv, int fd)
|
||||
{
|
||||
bthid_session_p s = NULL;
|
||||
hid_device_p d = NULL;
|
||||
int error, len;
|
||||
|
||||
assert(srv != NULL);
|
||||
assert(fd >= 0);
|
||||
|
||||
s = session_by_fd(srv, fd);
|
||||
assert(s != NULL);
|
||||
|
||||
d = get_hid_device(&s->bdaddr);
|
||||
assert(d != NULL);
|
||||
|
||||
error = 0;
|
||||
len = sizeof(error);
|
||||
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
|
||||
syslog(LOG_ERR, "Could not get socket error for %s. %s (%d)",
|
||||
bt_ntoa(&s->bdaddr, NULL), strerror(errno), errno);
|
||||
session_close(s);
|
||||
connect_in_progress = 0;
|
||||
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (error != 0) {
|
||||
syslog(LOG_ERR, "Could not connect to %s. %s (%d)",
|
||||
bt_ntoa(&s->bdaddr, NULL), strerror(error), error);
|
||||
session_close(s);
|
||||
connect_in_progress = 0;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
switch (s->state) {
|
||||
case W4CTRL: /* Control channel is open */
|
||||
assert(s->ctrl == fd);
|
||||
assert(s->intr == -1);
|
||||
|
||||
/* Open interrupt channel */
|
||||
s->intr = client_socket(&s->bdaddr, d->interrupt_psm);
|
||||
if (s->intr < 0) {
|
||||
syslog(LOG_ERR, "Could not open interrupt channel " \
|
||||
"to %s. %s (%d)", bt_ntoa(&s->bdaddr, NULL),
|
||||
strerror(errno), errno);
|
||||
session_close(s);
|
||||
connect_in_progress = 0;
|
||||
|
||||
return (-1);
|
||||
}
|
||||
|
||||
s->state = W4INTR;
|
||||
|
||||
FD_SET(s->intr, &srv->wfdset);
|
||||
if (s->intr > srv->maxfd)
|
||||
srv->maxfd = s->intr;
|
||||
|
||||
d->new_device = 0; /* reset new device flag */
|
||||
write_hids_file();
|
||||
break;
|
||||
|
||||
case W4INTR: /* Interrupt channel is open */
|
||||
assert(s->ctrl != -1);
|
||||
assert(s->intr == fd);
|
||||
|
||||
s->state = OPEN;
|
||||
connect_in_progress = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Move fd to from the write fd set into read fd set */
|
||||
FD_CLR(fd, &srv->wfdset);
|
||||
FD_SET(fd, &srv->rfdset);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create bound non-blocking socket and initiate connect
|
||||
*/
|
||||
|
||||
static int
|
||||
client_socket(bdaddr_p bdaddr, int psm)
|
||||
{
|
||||
struct sockaddr_l2cap l2addr;
|
||||
int s, m;
|
||||
|
||||
s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP);
|
||||
if (s < 0)
|
||||
return (-1);
|
||||
|
||||
m = fcntl(s, F_GETFL);
|
||||
if (m < 0) {
|
||||
close(s);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (fcntl(s, F_SETFL, (m|O_NONBLOCK)) < 0) {
|
||||
close(s);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
l2addr.l2cap_len = sizeof(l2addr);
|
||||
l2addr.l2cap_family = AF_BLUETOOTH;
|
||||
memcpy(&l2addr.l2cap_bdaddr, NG_HCI_BDADDR_ANY, sizeof(l2addr.l2cap_bdaddr));
|
||||
l2addr.l2cap_psm = 0;
|
||||
|
||||
if (bind(s, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0) {
|
||||
close(s);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
memcpy(&l2addr.l2cap_bdaddr, bdaddr, sizeof(l2addr.l2cap_bdaddr));
|
||||
l2addr.l2cap_psm = psm;
|
||||
|
||||
if (connect(s, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0 &&
|
||||
errno != EINPROGRESS) {
|
||||
close(s);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (s);
|
||||
}
|
||||
|
262
usr.sbin/bluetooth/bthidd/hid.c
Normal file
262
usr.sbin/bluetooth/bthidd/hid.c
Normal file
@ -0,0 +1,262 @@
|
||||
/*
|
||||
* hid.c
|
||||
*
|
||||
* Copyright (c) 2004 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: hid.c,v 1.3 2004/02/26 21:47:35 max Exp $
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/consio.h>
|
||||
#include <sys/mouse.h>
|
||||
#include <sys/queue.h>
|
||||
#include <assert.h>
|
||||
#include <bluetooth.h>
|
||||
#include <errno.h>
|
||||
#include <dev/usb/usb.h>
|
||||
#include <dev/usb/usbhid.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <usbhid.h>
|
||||
#include "bthidd.h"
|
||||
#include "bthid_config.h"
|
||||
|
||||
#undef min
|
||||
#define min(x, y) (((x) < (y))? (x) : (y))
|
||||
|
||||
/*
|
||||
* Process data from control channel
|
||||
*/
|
||||
|
||||
int
|
||||
hid_control(bthid_session_p s, char *data, int len)
|
||||
{
|
||||
assert(s != NULL);
|
||||
assert(data != NULL);
|
||||
assert(len > 0);
|
||||
|
||||
switch (data[0] >> 4) {
|
||||
case 0: /* Handshake (response to command) */
|
||||
if (data[0] & 0xf)
|
||||
syslog(LOG_ERR, "Got handshake message with error " \
|
||||
"response 0x%x from %s",
|
||||
data[0], bt_ntoa(&s->bdaddr, NULL));
|
||||
break;
|
||||
|
||||
case 1: /* HID Control */
|
||||
switch (data[0] & 0xf) {
|
||||
case 0: /* NOP */
|
||||
break;
|
||||
|
||||
case 1: /* Hard reset */
|
||||
case 2: /* Soft reset */
|
||||
syslog(LOG_WARNING, "Device %s requested %s reset",
|
||||
bt_ntoa(&s->bdaddr, NULL),
|
||||
((data[0] & 0xf) == 1)? "hard" : "soft");
|
||||
break;
|
||||
|
||||
case 3: /* Suspend */
|
||||
syslog(LOG_NOTICE, "Device %s requested Suspend",
|
||||
bt_ntoa(&s->bdaddr, NULL));
|
||||
break;
|
||||
|
||||
case 4: /* Exit suspend */
|
||||
syslog(LOG_NOTICE, "Device %s requested Exit Suspend",
|
||||
bt_ntoa(&s->bdaddr, NULL));
|
||||
break;
|
||||
|
||||
case 5: /* Virtual cable unplug */
|
||||
syslog(LOG_NOTICE, "Device %s unplugged virtual cable",
|
||||
bt_ntoa(&s->bdaddr, NULL));
|
||||
session_close(s);
|
||||
break;
|
||||
|
||||
default:
|
||||
syslog(LOG_WARNING, "Device %s sent unknown " \
|
||||
"HID_Control message 0x%x",
|
||||
bt_ntoa(&s->bdaddr, NULL), data[0]);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
syslog(LOG_WARNING, "Got unexpected message 0x%x on Control " \
|
||||
"channel from %s", data[0], bt_ntoa(&s->bdaddr, NULL));
|
||||
break;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Process data from the interrupt channel
|
||||
*/
|
||||
|
||||
int
|
||||
hid_interrupt(bthid_session_p s, char *data, int len)
|
||||
{
|
||||
hid_device_p hid_device = NULL;
|
||||
hid_data_t d;
|
||||
hid_item_t h;
|
||||
int report_id, usage, page, val,
|
||||
mouse_x, mouse_y, mouse_z, mouse_butt,
|
||||
nkeys, keys[32]; /* XXX how big keys[] should be? */
|
||||
|
||||
assert(s != NULL);
|
||||
assert(data != NULL);
|
||||
|
||||
if (len < 3) {
|
||||
syslog(LOG_ERR, "Got short message (%d bytes) on Interrupt " \
|
||||
"channel from %s", len, bt_ntoa(&s->bdaddr, NULL));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if ((unsigned char) data[0] != 0xa1) {
|
||||
syslog(LOG_ERR, "Got unexpected message 0x%x on " \
|
||||
"Interrupt channel from %s",
|
||||
data[0], bt_ntoa(&s->bdaddr, NULL));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
report_id = data[1];
|
||||
data += 2;
|
||||
len -= 2;
|
||||
|
||||
hid_device = get_hid_device(&s->bdaddr);
|
||||
assert(hid_device != NULL);
|
||||
|
||||
mouse_x = mouse_y = mouse_z = mouse_butt = nkeys = 0;
|
||||
|
||||
for (d = hid_start_parse(hid_device->desc, 1 << hid_input, -1);
|
||||
hid_get_item(d, &h) > 0; ) {
|
||||
if ((h.flags & HIO_CONST) || (h.report_ID != report_id))
|
||||
continue;
|
||||
|
||||
page = HID_PAGE(h.usage);
|
||||
usage = HID_USAGE(h.usage);
|
||||
val = hid_get_data(data, &h);
|
||||
|
||||
switch (page) {
|
||||
case HUP_GENERIC_DESKTOP:
|
||||
switch (usage) {
|
||||
case HUG_X:
|
||||
mouse_x = val;
|
||||
break;
|
||||
|
||||
case HUG_Y:
|
||||
mouse_y = val;
|
||||
break;
|
||||
|
||||
case HUG_WHEEL:
|
||||
mouse_z = -val;
|
||||
break;
|
||||
|
||||
case HUG_SYSTEM_SLEEP:
|
||||
if (val)
|
||||
syslog(LOG_NOTICE, "Sleep button pressed");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case HUP_KEYBOARD:
|
||||
if (h.flags & HIO_VARIABLE) {
|
||||
if (val && nkeys < sizeof(keys))
|
||||
keys[nkeys ++] = usage;
|
||||
} else {
|
||||
if (val && nkeys < sizeof(keys))
|
||||
keys[nkeys ++] = val;
|
||||
data ++;
|
||||
len --;
|
||||
|
||||
len = min(len, h.report_size);
|
||||
while (len > 0) {
|
||||
val = hid_get_data(data, &h);
|
||||
if (val && nkeys < sizeof(keys))
|
||||
keys[nkeys ++] = val;
|
||||
data ++;
|
||||
len --;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case HUP_BUTTON:
|
||||
mouse_butt |= (val << (usage - 1));
|
||||
break;
|
||||
|
||||
case HUP_MICROSOFT:
|
||||
switch (usage) {
|
||||
case 0xfe01:
|
||||
if (!hid_device->battery_power)
|
||||
break;
|
||||
|
||||
switch (val) {
|
||||
case 1:
|
||||
syslog(LOG_INFO, "Battery is OK on %s",
|
||||
bt_ntoa(&s->bdaddr, NULL));
|
||||
break;
|
||||
|
||||
case 2:
|
||||
syslog(LOG_NOTICE, "Low battery on %s",
|
||||
bt_ntoa(&s->bdaddr, NULL));
|
||||
break;
|
||||
|
||||
case 3:
|
||||
syslog(LOG_WARNING, "Very low battery "\
|
||||
"on %s",
|
||||
bt_ntoa(&s->bdaddr, NULL));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
hid_end_parse(d);
|
||||
|
||||
/*
|
||||
* XXX FIXME Feed mouse and keyboard events into kernel
|
||||
* The code block below works, but it is not
|
||||
* good enough
|
||||
*/
|
||||
|
||||
if (mouse_x != 0 || mouse_y != 0 || mouse_z != 0 || mouse_butt != 0) {
|
||||
struct mouse_info mi;
|
||||
|
||||
mi.operation = MOUSE_ACTION;
|
||||
mi.u.data.x = mouse_x;
|
||||
mi.u.data.y = mouse_y;
|
||||
mi.u.data.z = mouse_z;
|
||||
mi.u.data.buttons = mouse_butt;
|
||||
|
||||
if (ioctl(s->srv->cons, CONS_MOUSECTL, &mi) < 0)
|
||||
syslog(LOG_ERR, "Could not process mouse events from " \
|
||||
"%s. %s (%d)", bt_ntoa(&s->bdaddr, NULL),
|
||||
strerror(errno), errno);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
100
usr.sbin/bluetooth/bthidd/lexer.l
Normal file
100
usr.sbin/bluetooth/bthidd/lexer.l
Normal file
@ -0,0 +1,100 @@
|
||||
%{
|
||||
/*
|
||||
* lexer.l
|
||||
*
|
||||
* Copyright (c) 2004 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: lexer.l,v 1.2 2004/02/13 21:46:20 max Exp $
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <bluetooth.h>
|
||||
#include <stdlib.h>
|
||||
#include "parser.h"
|
||||
%}
|
||||
|
||||
%option yylineno noyywrap nounput
|
||||
|
||||
delim [ \t\n]
|
||||
ws {delim}+
|
||||
empty {delim}*
|
||||
comment \#.*
|
||||
|
||||
hexdigit [0-9a-fA-F]
|
||||
hexbyte {hexdigit}{hexdigit}?
|
||||
|
||||
device_word device
|
||||
bdaddr_word bdaddr
|
||||
control_psm_word control_psm
|
||||
interrupt_psm_word interrupt_psm
|
||||
reconnect_initiate_word reconnect_initiate
|
||||
battery_power_word battery_power
|
||||
normally_connectable_word normally_connectable
|
||||
hid_descriptor_word hid_descriptor
|
||||
true_word true
|
||||
false_word false
|
||||
|
||||
bdaddrstring {hexbyte}:{hexbyte}:{hexbyte}:{hexbyte}:{hexbyte}:{hexbyte}
|
||||
hexbytestring 0x{hexbyte}
|
||||
|
||||
%%
|
||||
|
||||
\; return (';');
|
||||
\: return (':');
|
||||
\{ return ('{');
|
||||
\} return ('}');
|
||||
|
||||
{ws} ;
|
||||
{empty} ;
|
||||
{comment} ;
|
||||
|
||||
{device_word} return (T_DEVICE);
|
||||
{bdaddr_word} return (T_BDADDR);
|
||||
{control_psm_word} return (T_CONTROL_PSM);
|
||||
{interrupt_psm_word} return (T_INTERRUPT_PSM);
|
||||
{reconnect_initiate_word} return (T_RECONNECT_INITIATE);
|
||||
{battery_power_word} return (T_BATTERY_POWER);
|
||||
{normally_connectable_word} return (T_NORMALLY_CONNECTABLE);
|
||||
{hid_descriptor_word} return (T_HID_DESCRIPTOR);
|
||||
{true_word} return (T_TRUE);
|
||||
{false_word} return (T_FALSE);
|
||||
|
||||
{bdaddrstring} {
|
||||
return (bt_aton(yytext, &yylval.bdaddr)?
|
||||
T_BDADDRSTRING : T_ERROR);
|
||||
}
|
||||
|
||||
{hexbytestring} {
|
||||
char *ep = NULL;
|
||||
|
||||
yylval.num = strtoul(yytext, &ep, 16);
|
||||
|
||||
return (*ep == '\0'? T_HEXBYTE : T_ERROR);
|
||||
}
|
||||
|
||||
. return (T_ERROR);
|
||||
|
||||
%%
|
||||
|
447
usr.sbin/bluetooth/bthidd/parser.y
Normal file
447
usr.sbin/bluetooth/bthidd/parser.y
Normal file
@ -0,0 +1,447 @@
|
||||
%{
|
||||
/*
|
||||
* parser.y
|
||||
*
|
||||
* Copyright (c) 2004 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: parser.y,v 1.3 2004/02/13 21:46:21 max Exp $
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/queue.h>
|
||||
#include <bluetooth.h>
|
||||
#include <errno.h>
|
||||
#include <libusbhid.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifndef BTHIDCONTROL
|
||||
#include <stdarg.h>
|
||||
#include <syslog.h>
|
||||
#define SYSLOG syslog
|
||||
#define LOGCRIT LOG_CRIT
|
||||
#define LOGERR LOG_ERR
|
||||
#define LOGWARNING LOG_WARNING
|
||||
#define EOL
|
||||
#else
|
||||
#define SYSLOG fprintf
|
||||
#define LOGCRIT stderr
|
||||
#define LOGERR stderr
|
||||
#define LOGWARNING stderr
|
||||
#define EOL "\n"
|
||||
#endif /* ndef BTHIDCONTROL */
|
||||
|
||||
#include "bthid_config.h"
|
||||
|
||||
int yyparse (void);
|
||||
int yylex (void);
|
||||
static int check_hid_device(hid_device_p hid_device);
|
||||
static void free_hid_device (hid_device_p hid_device);
|
||||
|
||||
extern int yylineno;
|
||||
char *config_file = BTHIDD_CONFFILE;
|
||||
char *hids_file = BTHIDD_HIDSFILE;
|
||||
|
||||
static char buffer[1024];
|
||||
static int hid_descriptor_size;
|
||||
static hid_device_t *hid_device = NULL;
|
||||
static LIST_HEAD(, hid_device) hid_devices;
|
||||
|
||||
%}
|
||||
|
||||
%union {
|
||||
bdaddr_t bdaddr;
|
||||
int num;
|
||||
}
|
||||
|
||||
%token <bdaddr> T_BDADDRSTRING
|
||||
%token <num> T_HEXBYTE
|
||||
%token T_DEVICE T_BDADDR T_CONTROL_PSM T_INTERRUPT_PSM T_RECONNECT_INITIATE
|
||||
%token T_BATTERY_POWER T_NORMALLY_CONNECTABLE T_HID_DESCRIPTOR
|
||||
%token T_TRUE T_FALSE T_ERROR
|
||||
|
||||
%%
|
||||
|
||||
config: line
|
||||
| config line
|
||||
;
|
||||
|
||||
line: T_DEVICE
|
||||
{
|
||||
hid_device = (hid_device_t *) calloc(1, sizeof(*hid_device));
|
||||
if (hid_device == NULL) {
|
||||
SYSLOG(LOGCRIT, "Could not allocate new " \
|
||||
"config entry" EOL);
|
||||
YYABORT;
|
||||
}
|
||||
|
||||
hid_device->new_device = 1;
|
||||
}
|
||||
'{' options '}'
|
||||
{
|
||||
if (check_hid_device(hid_device))
|
||||
LIST_INSERT_HEAD(&hid_devices,hid_device,next);
|
||||
else
|
||||
free_hid_device(hid_device);
|
||||
|
||||
hid_device = NULL;
|
||||
}
|
||||
;
|
||||
|
||||
options: option ';'
|
||||
| options option ';'
|
||||
;
|
||||
|
||||
option: bdaddr
|
||||
| control_psm
|
||||
| interrupt_psm
|
||||
| reconnect_initiate
|
||||
| battery_power
|
||||
| normally_connectable
|
||||
| hid_descriptor
|
||||
| parser_error
|
||||
;
|
||||
|
||||
bdaddr: T_BDADDR T_BDADDRSTRING
|
||||
{
|
||||
memcpy(&hid_device->bdaddr, &$2, sizeof(hid_device->bdaddr));
|
||||
}
|
||||
;
|
||||
|
||||
control_psm: T_CONTROL_PSM T_HEXBYTE
|
||||
{
|
||||
hid_device->control_psm = $2;
|
||||
}
|
||||
;
|
||||
|
||||
interrupt_psm: T_INTERRUPT_PSM T_HEXBYTE
|
||||
{
|
||||
hid_device->interrupt_psm = $2;
|
||||
}
|
||||
;
|
||||
|
||||
reconnect_initiate: T_RECONNECT_INITIATE T_TRUE
|
||||
{
|
||||
hid_device->reconnect_initiate = 1;
|
||||
}
|
||||
| T_RECONNECT_INITIATE T_FALSE
|
||||
{
|
||||
hid_device->reconnect_initiate = 0;
|
||||
}
|
||||
;
|
||||
|
||||
battery_power: T_BATTERY_POWER T_TRUE
|
||||
{
|
||||
hid_device->battery_power = 1;
|
||||
}
|
||||
| T_BATTERY_POWER T_FALSE
|
||||
{
|
||||
hid_device->battery_power = 0;
|
||||
}
|
||||
;
|
||||
|
||||
normally_connectable: T_NORMALLY_CONNECTABLE T_TRUE
|
||||
{
|
||||
hid_device->normally_connectable = 1;
|
||||
}
|
||||
| T_NORMALLY_CONNECTABLE T_FALSE
|
||||
{
|
||||
hid_device->normally_connectable = 0;
|
||||
}
|
||||
;
|
||||
|
||||
hid_descriptor: T_HID_DESCRIPTOR
|
||||
{
|
||||
hid_descriptor_size = 0;
|
||||
}
|
||||
'{' hid_descriptor_bytes '}'
|
||||
{
|
||||
if (hid_device->desc != NULL)
|
||||
hid_dispose_report_desc(hid_device->desc);
|
||||
|
||||
hid_device->desc = hid_use_report_desc(buffer, hid_descriptor_size);
|
||||
if (hid_device->desc == NULL) {
|
||||
SYSLOG(LOGCRIT, "Could not use HID descriptor" EOL);
|
||||
YYABORT;
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
hid_descriptor_bytes: hid_descriptor_byte
|
||||
| hid_descriptor_bytes hid_descriptor_byte
|
||||
;
|
||||
|
||||
hid_descriptor_byte: T_HEXBYTE
|
||||
{
|
||||
if (hid_descriptor_size >= sizeof(buffer)) {
|
||||
SYSLOG(LOGCRIT, "HID descriptor is too big" EOL);
|
||||
YYABORT;
|
||||
}
|
||||
|
||||
buffer[hid_descriptor_size ++] = $1;
|
||||
}
|
||||
;
|
||||
|
||||
parser_error: T_ERROR
|
||||
{
|
||||
YYABORT;
|
||||
}
|
||||
|
||||
%%
|
||||
|
||||
/* Display parser error message */
|
||||
void
|
||||
yyerror(char const *message)
|
||||
{
|
||||
SYSLOG(LOGERR, "%s in line %d" EOL, message, yylineno);
|
||||
}
|
||||
|
||||
/* Re-read config file */
|
||||
int
|
||||
read_config_file(void)
|
||||
{
|
||||
extern FILE *yyin;
|
||||
int e;
|
||||
|
||||
if (config_file == NULL) {
|
||||
SYSLOG(LOGERR, "Unknown config file name!" EOL);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if ((yyin = fopen(config_file, "r")) == NULL) {
|
||||
SYSLOG(LOGERR, "Could not open config file '%s'. %s (%d)" EOL,
|
||||
config_file, strerror(errno), errno);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
clean_config();
|
||||
if (yyparse() < 0) {
|
||||
SYSLOG(LOGERR, "Could not parse config file '%s'" EOL,
|
||||
config_file);
|
||||
e = -1;
|
||||
} else
|
||||
e = 0;
|
||||
|
||||
fclose(yyin);
|
||||
yyin = NULL;
|
||||
|
||||
return (e);
|
||||
}
|
||||
|
||||
/* Clean config */
|
||||
void
|
||||
clean_config(void)
|
||||
{
|
||||
while (!LIST_EMPTY(&hid_devices)) {
|
||||
hid_device_p hid_device = LIST_FIRST(&hid_devices);
|
||||
|
||||
LIST_REMOVE(hid_device, next);
|
||||
free_hid_device(hid_device);
|
||||
}
|
||||
}
|
||||
|
||||
/* Lookup config entry */
|
||||
hid_device_p
|
||||
get_hid_device(bdaddr_p bdaddr)
|
||||
{
|
||||
hid_device_p hid_device;
|
||||
|
||||
LIST_FOREACH(hid_device, &hid_devices, next)
|
||||
if (memcmp(&hid_device->bdaddr, bdaddr, sizeof(bdaddr_t)) == 0)
|
||||
break;
|
||||
|
||||
return (hid_device);
|
||||
}
|
||||
|
||||
/* Get next config entry */
|
||||
hid_device_p
|
||||
get_next_hid_device(hid_device_p d)
|
||||
{
|
||||
return ((d == NULL)? LIST_FIRST(&hid_devices) : LIST_NEXT(d, next));
|
||||
}
|
||||
|
||||
/* Print config entry */
|
||||
void
|
||||
print_hid_device(hid_device_p hid_device, FILE *f)
|
||||
{
|
||||
/* XXX FIXME hack! */
|
||||
struct report_desc {
|
||||
unsigned int size;
|
||||
unsigned char data[1];
|
||||
};
|
||||
/* XXX FIXME hack! */
|
||||
|
||||
struct report_desc *desc = (struct report_desc *) hid_device->desc;
|
||||
int i;
|
||||
|
||||
fprintf(f,
|
||||
"device {\n" \
|
||||
" bdaddr %s;\n" \
|
||||
" control_psm 0x%x;\n" \
|
||||
" interrupt_psm 0x%d;\n" \
|
||||
" reconnect_initiate %s;\n" \
|
||||
" battery_power %s;\n" \
|
||||
" normally_connectable %s;\n" \
|
||||
" hid_descriptor {",
|
||||
bt_ntoa(&hid_device->bdaddr, NULL),
|
||||
hid_device->control_psm, hid_device->interrupt_psm,
|
||||
hid_device->reconnect_initiate? "true" : "false",
|
||||
hid_device->battery_power? "true" : "false",
|
||||
hid_device->normally_connectable? "true" : "false");
|
||||
|
||||
for (i = 0; i < desc->size; i ++) {
|
||||
if ((i % 8) == 0)
|
||||
fprintf(stdout, "\n ");
|
||||
|
||||
fprintf(f, "0x%2.2x ", desc->data[i]);
|
||||
}
|
||||
|
||||
fprintf(stdout,
|
||||
"\n" \
|
||||
" };\n" \
|
||||
"}\n");
|
||||
}
|
||||
|
||||
/* Check config entry */
|
||||
static int
|
||||
check_hid_device(hid_device_p hid_device)
|
||||
{
|
||||
if (get_hid_device(&hid_device->bdaddr) != NULL) {
|
||||
SYSLOG(LOGERR, "Ignoring duplicated entry for bdaddr %s" EOL,
|
||||
bt_ntoa(&hid_device->bdaddr, NULL));
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (hid_device->control_psm == 0) {
|
||||
SYSLOG(LOGERR, "Ignoring entry with invalid control PSM" EOL);
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (hid_device->interrupt_psm == 0) {
|
||||
SYSLOG(LOGERR, "Ignoring entry with invalid interrupt PSM" EOL);
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (hid_device->desc == NULL) {
|
||||
SYSLOG(LOGERR, "Ignoring entry without HID descriptor" EOL);
|
||||
return (0);
|
||||
}
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Free config entry */
|
||||
static void
|
||||
free_hid_device(hid_device_p hid_device)
|
||||
{
|
||||
if (hid_device->desc != NULL)
|
||||
hid_dispose_report_desc(hid_device->desc);
|
||||
|
||||
memset(hid_device, 0, sizeof(*hid_device));
|
||||
free(hid_device);
|
||||
}
|
||||
|
||||
/* Re-read hids file */
|
||||
int
|
||||
read_hids_file(void)
|
||||
{
|
||||
FILE *f = NULL;
|
||||
hid_device_t *hid_device = NULL;
|
||||
char *line = NULL;
|
||||
bdaddr_t bdaddr;
|
||||
int lineno;
|
||||
|
||||
if (hids_file == NULL) {
|
||||
SYSLOG(LOGERR, "Unknown HIDs file name!" EOL);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if ((f = fopen(hids_file, "r")) == NULL) {
|
||||
if (errno == ENOENT)
|
||||
return (0);
|
||||
|
||||
SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL,
|
||||
hids_file, strerror(errno), errno);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
for (lineno = 1; fgets(buffer, sizeof(buffer), f) != NULL; lineno ++) {
|
||||
if ((line = strtok(buffer, "\r\n\t ")) == NULL)
|
||||
continue; /* ignore empty lines */
|
||||
|
||||
if (!bt_aton(line, &bdaddr)) {
|
||||
SYSLOG(LOGWARNING, "Ignoring unparseable BD_ADDR in " \
|
||||
"%s:%d" EOL, hids_file, lineno);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((hid_device = get_hid_device(&bdaddr)) != NULL)
|
||||
hid_device->new_device = 0;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Write hids file */
|
||||
int
|
||||
write_hids_file(void)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
FILE *f = NULL;
|
||||
hid_device_t *hid_device = NULL;
|
||||
|
||||
if (hids_file == NULL) {
|
||||
SYSLOG(LOGERR, "Unknown HIDs file name!" EOL);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
snprintf(path, sizeof(path), "%s.new", hids_file);
|
||||
|
||||
if ((f = fopen(path, "w")) == NULL) {
|
||||
SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL,
|
||||
path, strerror(errno), errno);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
LIST_FOREACH(hid_device, &hid_devices, next)
|
||||
if (!hid_device->new_device)
|
||||
fprintf(f, "%s\n", bt_ntoa(&hid_device->bdaddr, NULL));
|
||||
|
||||
fclose(f);
|
||||
|
||||
if (rename(path, hids_file) < 0) {
|
||||
SYSLOG(LOGERR, "Could not rename new HIDs file '%s' to '%s'. " \
|
||||
"%s (%d)" EOL, path, hids_file, strerror(errno), errno);
|
||||
unlink(path);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
316
usr.sbin/bluetooth/bthidd/server.c
Normal file
316
usr.sbin/bluetooth/bthidd/server.c
Normal file
@ -0,0 +1,316 @@
|
||||
/*
|
||||
* server.c
|
||||
*
|
||||
* Copyright (c) 2004 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: server.c,v 1.5 2004/02/26 21:43:36 max Exp $
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/queue.h>
|
||||
#include <assert.h>
|
||||
#include <bluetooth.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
#include <usbhid.h>
|
||||
#include "bthidd.h"
|
||||
#include "bthid_config.h"
|
||||
|
||||
#undef max
|
||||
#define max(x, y) (((x) > (y))? (x) : (y))
|
||||
|
||||
static int server_accept (bthid_server_p srv, int fd);
|
||||
static int server_process(bthid_server_p srv, int fd);
|
||||
|
||||
/*
|
||||
* Initialize server
|
||||
*/
|
||||
|
||||
int
|
||||
server_init(bthid_server_p srv)
|
||||
{
|
||||
struct sockaddr_l2cap l2addr;
|
||||
|
||||
assert(srv != NULL);
|
||||
|
||||
srv->ctrl = srv->intr = -1;
|
||||
FD_ZERO(&srv->rfdset);
|
||||
FD_ZERO(&srv->wfdset);
|
||||
LIST_INIT(&srv->sessions);
|
||||
|
||||
/* Open /dev/consolectl */
|
||||
srv->cons = open("/dev/consolectl", O_RDWR);
|
||||
if (srv->cons < 0) {
|
||||
syslog(LOG_ERR, "Could not open /dev/consolectl. %s (%d)",
|
||||
strerror(errno), errno);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Create control socket */
|
||||
srv->ctrl = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP);
|
||||
if (srv->ctrl < 0) {
|
||||
syslog(LOG_ERR, "Could not create control L2CAP socket. " \
|
||||
"%s (%d)", strerror(errno), errno);
|
||||
close(srv->cons);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
l2addr.l2cap_len = sizeof(l2addr);
|
||||
l2addr.l2cap_family = AF_BLUETOOTH;
|
||||
memcpy(&l2addr.l2cap_bdaddr, &srv->bdaddr, sizeof(l2addr.l2cap_bdaddr));
|
||||
l2addr.l2cap_psm = 0x11;
|
||||
|
||||
if (bind(srv->ctrl, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0) {
|
||||
syslog(LOG_ERR, "Could not bind control L2CAP socket. " \
|
||||
"%s (%d)", strerror(errno), errno);
|
||||
close(srv->cons);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (listen(srv->ctrl, 10) < 0) {
|
||||
syslog(LOG_ERR, "Could not listen on control L2CAP socket. " \
|
||||
"%s (%d)", strerror(errno), errno);
|
||||
close(srv->cons);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Create intrrupt socket */
|
||||
srv->intr = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP);
|
||||
if (srv->intr < 0) {
|
||||
syslog(LOG_ERR, "Could not create interrupt L2CAP socket. " \
|
||||
"%s (%d)", strerror(errno), errno);
|
||||
close(srv->ctrl);
|
||||
close(srv->cons);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
l2addr.l2cap_psm = 0x13;
|
||||
|
||||
if (bind(srv->intr, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0) {
|
||||
syslog(LOG_ERR, "Could not bind interrupt L2CAP socket. " \
|
||||
"%s (%d)", strerror(errno), errno);
|
||||
close(srv->ctrl);
|
||||
close(srv->cons);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (listen(srv->intr, 10) < 0) {
|
||||
syslog(LOG_ERR, "Could not listen on interrupt L2CAP socket. "\
|
||||
"%s (%d)", strerror(errno), errno);
|
||||
close(srv->ctrl);
|
||||
close(srv->cons);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
FD_SET(srv->ctrl, &srv->rfdset);
|
||||
FD_SET(srv->intr, &srv->rfdset);
|
||||
srv->maxfd = max(srv->ctrl, srv->intr);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Shutdown server
|
||||
*/
|
||||
|
||||
void
|
||||
server_shutdown(bthid_server_p srv)
|
||||
{
|
||||
assert(srv != NULL);
|
||||
|
||||
close(srv->cons);
|
||||
close(srv->ctrl);
|
||||
close(srv->intr);
|
||||
|
||||
while (!LIST_EMPTY(&srv->sessions))
|
||||
session_close(LIST_FIRST(&srv->sessions));
|
||||
|
||||
memset(srv, 0, sizeof(*srv));
|
||||
}
|
||||
|
||||
/*
|
||||
* Do one server iteration
|
||||
*/
|
||||
|
||||
int
|
||||
server_do(bthid_server_p srv)
|
||||
{
|
||||
struct timeval tv;
|
||||
fd_set rfdset, wfdset;
|
||||
int n, fd;
|
||||
|
||||
assert(srv != NULL);
|
||||
|
||||
tv.tv_sec = 1;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
/* Copy cached version of the fd sets and call select */
|
||||
memcpy(&rfdset, &srv->rfdset, sizeof(rfdset));
|
||||
memcpy(&wfdset, &srv->wfdset, sizeof(wfdset));
|
||||
|
||||
n = select(srv->maxfd + 1, &rfdset, &wfdset, NULL, &tv);
|
||||
if (n < 0) {
|
||||
if (errno == EINTR)
|
||||
return (0);
|
||||
|
||||
syslog(LOG_ERR, "Could not select(%d, %p, %p). %s (%d)",
|
||||
srv->maxfd + 1, &rfdset, &wfdset, strerror(errno), errno);
|
||||
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Process descriptors (if any) */
|
||||
for (fd = 0; fd < srv->maxfd + 1 && n > 0; fd ++) {
|
||||
if (FD_ISSET(fd, &rfdset)) {
|
||||
n --;
|
||||
|
||||
if (fd == srv->ctrl || fd == srv->intr)
|
||||
server_accept(srv, fd);
|
||||
else
|
||||
server_process(srv, fd);
|
||||
} else if (FD_ISSET(fd, &wfdset)) {
|
||||
n --;
|
||||
|
||||
client_connect(srv, fd);
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Accept new connection
|
||||
*/
|
||||
|
||||
static int
|
||||
server_accept(bthid_server_p srv, int fd)
|
||||
{
|
||||
bthid_session_p s = NULL;
|
||||
hid_device_p d = NULL;
|
||||
struct sockaddr_l2cap l2addr;
|
||||
int len, new_fd;
|
||||
|
||||
len = sizeof(l2addr);
|
||||
if ((new_fd = accept(fd, (struct sockaddr *) &l2addr, &len)) < 0) {
|
||||
syslog(LOG_ERR, "Could not accept %s connection. %s (%d)",
|
||||
(fd == srv->ctrl)? "control" : "interrupt",
|
||||
strerror(errno), errno);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Check if we have session for the device */
|
||||
if ((s = session_by_bdaddr(srv, &l2addr.l2cap_bdaddr)) == NULL) {
|
||||
/* Is device configured? */
|
||||
if ((d = get_hid_device(&l2addr.l2cap_bdaddr)) == NULL) {
|
||||
syslog(LOG_ERR, "Rejecting %s connection from %s. " \
|
||||
"Device not configured",
|
||||
(fd == srv->ctrl)? "control" : "interrupt",
|
||||
bt_ntoa(&l2addr.l2cap_bdaddr, NULL));
|
||||
close(new_fd);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
d->new_device = 0; /* reset new device flag */
|
||||
write_hids_file();
|
||||
|
||||
/* Create new inbound session */
|
||||
if ((s = session_open(srv, &l2addr.l2cap_bdaddr)) == NULL) {
|
||||
syslog(LOG_CRIT, "Could not open inbound session " \
|
||||
"for %s. Not enough memory",
|
||||
bt_ntoa(&l2addr.l2cap_bdaddr, NULL));
|
||||
close(new_fd);
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Update descriptors */
|
||||
if (fd == srv->ctrl) {
|
||||
assert(s->ctrl == -1);
|
||||
s->ctrl = new_fd;
|
||||
s->state = (s->intr == -1)? W4INTR : OPEN;
|
||||
} else {
|
||||
assert(s->intr == -1);
|
||||
s->intr = new_fd;
|
||||
s->state = (s->ctrl == -1)? W4CTRL : OPEN;
|
||||
}
|
||||
|
||||
FD_SET(new_fd, &srv->rfdset);
|
||||
if (new_fd > srv->maxfd)
|
||||
srv->maxfd = new_fd;
|
||||
|
||||
syslog(LOG_NOTICE, "Accepted %s connection from %s",
|
||||
(fd == srv->ctrl)? "control" : "interrupt",
|
||||
bt_ntoa(&l2addr.l2cap_bdaddr, NULL));
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Process data on the connection
|
||||
*/
|
||||
|
||||
static int
|
||||
server_process(bthid_server_p srv, int fd)
|
||||
{
|
||||
bthid_session_p s = session_by_fd(srv, fd);
|
||||
char data[1024];
|
||||
int len;
|
||||
|
||||
assert(s != NULL);
|
||||
|
||||
do {
|
||||
len = read(fd, data, sizeof(data));
|
||||
} while (len < 0 && errno == EINTR);
|
||||
|
||||
if (len < 0) {
|
||||
syslog(LOG_ERR, "Could not read data from %s (%s). %s (%d)",
|
||||
bt_ntoa(&s->bdaddr, NULL),
|
||||
(fd == s->ctrl)? "control" : "interrupt",
|
||||
strerror(errno), errno);
|
||||
session_close(s);
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
syslog(LOG_NOTICE, "Remote device %s has closed %s connection",
|
||||
bt_ntoa(&s->bdaddr, NULL),
|
||||
(fd == s->ctrl)? "control" : "interrupt");
|
||||
session_close(s);
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (fd == s->ctrl)
|
||||
hid_control(s, data, len);
|
||||
else
|
||||
hid_interrupt(s, data, len);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
136
usr.sbin/bluetooth/bthidd/session.c
Normal file
136
usr.sbin/bluetooth/bthidd/session.c
Normal file
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* session.c
|
||||
*
|
||||
* Copyright (c) 2004 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: session.c,v 1.1 2004/02/12 22:46:59 max Exp $
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/queue.h>
|
||||
#include <assert.h>
|
||||
#include <bluetooth.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "bthidd.h"
|
||||
|
||||
/*
|
||||
* Create new session
|
||||
*/
|
||||
|
||||
bthid_session_p
|
||||
session_open(bthid_server_p srv, bdaddr_p bdaddr)
|
||||
{
|
||||
bthid_session_p s = NULL;
|
||||
|
||||
assert(srv != NULL);
|
||||
assert(bdaddr != NULL);
|
||||
|
||||
if ((s = (bthid_session_p) malloc(sizeof(*s))) != NULL) {
|
||||
s->srv = srv;
|
||||
memcpy(&s->bdaddr, bdaddr, sizeof(s->bdaddr));
|
||||
s->ctrl = s->intr = -1;
|
||||
s->state = CLOSED;
|
||||
|
||||
LIST_INSERT_HEAD(&srv->sessions, s, next);
|
||||
}
|
||||
|
||||
return (s);
|
||||
}
|
||||
|
||||
/*
|
||||
* Lookup session by bdaddr
|
||||
*/
|
||||
|
||||
bthid_session_p
|
||||
session_by_bdaddr(bthid_server_p srv, bdaddr_p bdaddr)
|
||||
{
|
||||
bthid_session_p s = NULL;
|
||||
|
||||
assert(srv != NULL);
|
||||
assert(bdaddr != NULL);
|
||||
|
||||
LIST_FOREACH(s, &srv->sessions, next)
|
||||
if (memcmp(&s->bdaddr, bdaddr, sizeof(s->bdaddr)) == 0)
|
||||
break;
|
||||
|
||||
return (s);
|
||||
}
|
||||
|
||||
/*
|
||||
* Lookup session by fd
|
||||
*/
|
||||
|
||||
bthid_session_p
|
||||
session_by_fd(bthid_server_p srv, int fd)
|
||||
{
|
||||
bthid_session_p s = NULL;
|
||||
|
||||
assert(srv != NULL);
|
||||
assert(fd >= 0);
|
||||
|
||||
LIST_FOREACH(s, &srv->sessions, next)
|
||||
if (s->ctrl == fd || s->intr == fd)
|
||||
break;
|
||||
|
||||
return (s);
|
||||
}
|
||||
|
||||
/*
|
||||
* Close session
|
||||
*/
|
||||
|
||||
void
|
||||
session_close(bthid_session_p s)
|
||||
{
|
||||
assert(s != NULL);
|
||||
assert(s->srv != NULL);
|
||||
|
||||
LIST_REMOVE(s, next);
|
||||
|
||||
if (s->intr != -1) {
|
||||
FD_CLR(s->intr, &s->srv->rfdset);
|
||||
FD_CLR(s->intr, &s->srv->wfdset);
|
||||
close(s->intr);
|
||||
|
||||
if (s->srv->maxfd == s->intr)
|
||||
s->srv->maxfd --;
|
||||
}
|
||||
|
||||
if (s->ctrl != -1) {
|
||||
FD_CLR(s->ctrl, &s->srv->rfdset);
|
||||
FD_CLR(s->ctrl, &s->srv->wfdset);
|
||||
close(s->ctrl);
|
||||
|
||||
if (s->srv->maxfd == s->ctrl)
|
||||
s->srv->maxfd --;
|
||||
}
|
||||
|
||||
memset(s, 0, sizeof(*s));
|
||||
free(s);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user