send something TO a hid device

Stefan `Sec` Zehl sec at 42.org
Wed May 16 21:03:51 UTC 2007


Hi,

On Mon, May 14, 2007 at 10:41 -0700, Maksim Yevmenkin wrote:
> again, you do not really need a device. socket will do just fine.
> local client(s) can connect to the bthidd and identify which hid
> device it (they) will be taking to, i.e. tell hid device's bd_addr.
> then client(s) simply sends hid reports to the bthidd via socket and
> it will relay them the hid device via bluetooth.

I have a first (rudimentary) version. The patch is below, any comments
would be appreciated.

It addas a new commandline option "-b" which makes bthidd listen on port
12345.  I think, either the port number to listen on should be
configurable, or it should perhaps better listen on a unix domain socket
"/tmp/.bthidd-socket" or somesuch.

It can currently only cope with one backchannel connection at a time.
This could be changed, if anyone thinks keeping it that way would be too
limiting.

There is only one interesting command implemented:
"raw BD_ADDR hexbytes" which simply sends the hexbytes to that device.

I'm currently thinking about sending the device answer back over the tcp
channel. At least the media pad has query functions, too.

CU,
    Sec
-- 
"Good people do not need laws to tell them to act responsibly, while
bad people will find a way around the laws." - Plato(?) (427-347 B.C.) 
-------------- next part --------------
--- bthidd.c.org	Mon Oct  9 23:00:17 2006
+++ bthidd.c	Wed May 16 02:45:30 2007
@@ -66,14 +66,15 @@
 	struct sigaction	 sa;
 	char const		*pid_file = BTHIDD_PIDFILE;
 	char			*ep;
-	int32_t			 opt, detach, tval;
+	int32_t			 opt, detach, tval, do_backchannel;
 
 	memset(&srv, 0, sizeof(srv));
 	memset(&srv.bdaddr, 0, sizeof(srv.bdaddr));
 	detach = 1;
 	tval = 10; /* sec */
+	do_backchannel=0;
 
-	while ((opt = getopt(argc, argv, "a:c:dH:hp:t:")) != -1) {
+	while ((opt = getopt(argc, argv, "a:bc:dH:hp:t:")) != -1) {
 		switch (opt) {
 		case 'a': /* BDADDR */
 			if (!bt_aton(optarg, &srv.bdaddr)) {
@@ -85,6 +86,9 @@
 				memcpy(&srv.bdaddr, he->h_addr, sizeof(srv.bdaddr));
 			}
 			break;
+		case 'b': /* enable backchannel */
+			do_backchannel=1;
+			break;
 			
 		case 'c': /* config file */
 			config_file = optarg;
@@ -153,6 +157,9 @@
 
 	if (read_config_file() < 0 || read_hids_file() < 0 ||
 	    server_init(&srv) < 0 || write_pid_file(pid_file) < 0)
+		exit(1);
+
+	if (do_backchannel && backchannel_init(&srv) < 0)
 		exit(1);
 
 	for (done = 0; !done; ) {
--- bthidd.h.org	Mon Oct  9 23:00:17 2006
+++ bthidd.h	Tue May 15 23:29:54 2007
@@ -45,6 +45,8 @@
 	int32_t				 cons;	 /* /dev/consolectl */
 	int32_t				 ctrl;   /* control channel (listen) */
 	int32_t				 intr;   /* intr. channel (listen) */
+	int32_t				 back;   /* backchannel (listen) */
+	int32_t				 backconn;   /* backchannel (connected) */
 	int32_t				 maxfd;	 /* max fd in sets */
 	fd_set				 rfdset; /* read descriptor set */
 	fd_set				 wfdset; /* write descriptor set */
@@ -78,6 +80,8 @@
 void		server_shutdown  (bthid_server_p srv);
 int32_t		server_do        (bthid_server_p srv);
 
+int32_t		backchannel_init (bthid_server_p srv);
+
 int32_t		client_rescan    (bthid_server_p srv);
 int32_t		client_connect   (bthid_server_p srv, int fd);
 
@@ -85,6 +89,7 @@
 bthid_session_p	session_by_bdaddr(bthid_server_p srv, bdaddr_p bdaddr);
 bthid_session_p	session_by_fd    (bthid_server_p srv, int32_t fd);
 void		session_close    (bthid_session_p s);
+void		backchannel_close(bthid_server_p srv);
 
 int32_t		hid_control      (bthid_session_p s, uint8_t *data, int32_t len);
 int32_t		hid_interrupt    (bthid_session_p s, uint8_t *data, int32_t len);
--- server.c.org	Mon Oct  9 23:00:17 2006
+++ server.c	Wed May 16 02:48:36 2007
@@ -46,6 +46,8 @@
 #include "bthid_config.h"
 #include "bthidd.h"
 #include "kbd.h"
+#include <netinet/in.h>
+#include <arpa/inet.h>
 
 #undef	max
 #define	max(x, y)	(((x) > (y))? (x) : (y))
@@ -53,6 +55,9 @@
 static int32_t	server_accept (bthid_server_p srv, int32_t fd);
 static int32_t	server_process(bthid_server_p srv, int32_t fd);
 
+static int32_t	backchannel_accept (bthid_server_p srv, int32_t fd);
+static int32_t	backchannel_process(bthid_server_p srv, int32_t fd);
+
 /*
  * Initialize server
  */
@@ -164,6 +169,45 @@
 }
 
 /*
+ * Create backchannel socket
+ */
+
+int32_t
+backchannel_init(bthid_server_p srv)
+{
+	struct sockaddr_in sin;
+	bzero(&sin, sizeof (struct sockaddr_in));
+	int x=1;
+
+	sin.sin_family = AF_INET;
+	sin.sin_port = htons(12345);
+//	inet_aton("194.77.85.2",&sin.sin_addr);
+
+	srv->back = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+	if(srv->back<0){
+		syslog(LOG_ERR, "Could not create backchannel socket. " \
+			"%s (%d)", strerror(errno), errno);
+		return (-1);
+	};
+	if (bind(srv->back, (struct sockaddr*)&sin, sizeof (struct sockaddr)) < 0){
+		syslog(LOG_ERR, "Could not bind backchannel socket. " \
+			"%s (%d)", strerror(errno), errno);
+		return (-1);
+	};
+	setsockopt(srv->back,SOL_SOCKET,SO_REUSEADDR,&x,sizeof(x));
+	if(listen(srv->back,1)<0){
+		syslog(LOG_ERR, "Could not listen on backchannel socket. " \
+			"%s (%d)", strerror(errno), errno);
+		return (-1);
+	};
+
+	FD_SET(srv->back, &srv->rfdset);
+	srv->maxfd = max(srv->back, srv->maxfd);
+
+	return(0);
+}
+
+/*
  * Do one server iteration
  */
 
@@ -201,6 +245,10 @@
 
 			if (fd == srv->ctrl || fd == srv->intr)
 				server_accept(srv, fd);
+			else if (fd == srv->back)
+				backchannel_accept(srv, fd);
+			else if (fd == srv->backconn)
+				backchannel_process(srv, fd);
 			else
 				server_process(srv, fd);
 		} else if (FD_ISSET(fd, &wfdset)) {
@@ -290,6 +338,44 @@
 }
 
 /*
+ * Accept new backchannel connection 
+ */
+
+static int32_t
+backchannel_accept(bthid_server_p srv, int32_t fd)
+{
+	int32_t			new_fd;
+	struct sockaddr_in addr;
+	socklen_t		len;
+	const char *data="ERR: Only one connection at a time.\n";
+
+	len = sizeof(addr);
+	if ((new_fd = accept(fd, (struct sockaddr*)&addr, &len)) < 0) {
+		syslog(LOG_ERR, "Could not accept backchannel connection. %s (%d)",
+			strerror(errno), errno);
+		return (-1);
+	}
+
+	if(srv->backconn){ 
+		syslog(LOG_NOTICE, "Refused second backchannel connection.");
+		write(new_fd,data,strlen(data));
+		close(new_fd);
+		return(-1);
+	};
+
+	FD_SET(new_fd, &srv->rfdset);
+	if (new_fd > srv->maxfd)
+		srv->maxfd = new_fd;
+
+	srv->backconn=new_fd;
+
+	syslog(LOG_NOTICE, "Accepted backchannel connection from %s",
+		inet_ntoa(addr.sin_addr));
+
+	return(0);
+};
+
+/*
  * Process data on the connection
  */
 
@@ -345,5 +431,134 @@
 	(*cb)(s, (uint8_t *) &data, len);
 
 	return (0);
+}
+
+/*
+ * Process command on the backchannel connection
+ */
+
+static int32_t
+backchannel_process(bthid_server_p srv, int32_t fd)
+{
+	uint8_t		data[1024];
+	int32_t		len, to_read;
+	char 		*p, *word;
+	const char	*out;
+	bdaddr_t	bdaddr;
+
+	to_read = sizeof(data);
+
+	do {
+		len = read(fd, &data, to_read);
+	} while (len < 0 && errno == EINTR);
+
+	if (len < 0) {
+		syslog(LOG_ERR, "Could not read data from backchannel. %s (%d)",
+			strerror(errno), errno);
+		backchannel_close(srv);
+		return (0);
+	}
+
+	if (len == 0) {
+		syslog(LOG_NOTICE, "Backchannel has closed connection");
+		backchannel_close(srv);
+		return (0);
+	}
+
+	data[len]=0;
+	p=data;
+#define OPTION(x) ( (!strncasecmp(p, x, strlen(x))) && (p += strlen(x)) )
+	out=NULL;
+	if( OPTION("raw ")){
+		bthid_session_p		s;
+#define MAXOUTLEN 22
+		uint8_t outb[MAXOUTLEN];
+		uint8_t val=0,idx=0;
+		char 	shift=0;
+
+		word=p;
+		for(word=p;*p && *p!=' ';p++);
+		if(!*p){
+			out="ERR: Missing parameter.\n";
+			goto err;
+		};
+		*p=0;
+		if (!bt_aton(word, &bdaddr)) {
+			struct hostent  *he;
+
+			if ((he = bt_gethostbyname(word)) == NULL){
+				out="ERR: Can't parse BD_ADDR.\n";
+				goto err;
+			};
+			memcpy(&bdaddr, he->h_addr, sizeof(bdaddr));
+		}
+		s=session_by_bdaddr(srv,&bdaddr);
+		if(s==NULL || s->intr == 0){
+			out="ERR: Device not connected\n";
+			goto err;
+		};
+		for(p++;*p;p++){
+			shift=4-shift;
+			if(*p>='0' && *p <= '9')
+				val=val|((*p-'0')<<shift);
+			else if(*p>='a' && *p <= 'f')
+				val=val|((*p-'a'+10)<<shift);
+			else if(*p>='A' && *p <= 'F')
+				val=val|((*p-'A'+10)<<shift);
+			else if(*p=='\r' || *p =='\n' || *p == ' '){
+				shift=4-shift;
+				continue;
+			}else{
+				out="ERR: Can't parse hexstring.\n";
+				goto err;
+			};
+			if(shift==0){
+				outb[idx++]=val;
+				val=0;
+				if(idx==MAXOUTLEN){
+					out="ERR: Hexstring too long.\n";
+					goto err;
+				};
+			};
+		};
+		if(shift!=0){
+			out="ERR: odd number of hex chars.\n";
+			goto err;
+		};
+		if(idx==0){
+			out="ERR: Missing parameter.\n";
+			goto err;
+		};
+		outb[idx]=0;
+
+		printf("out: ");
+		for (val=0;val<idx;val++){
+			printf("%02x ",outb[val]);
+		};
+		printf("(%d)\n",idx);
+
+		if(write(s->intr,outb,idx)<0){
+			syslog(LOG_ERR, "Backchannel write failed: " \
+					"%s (%d).", strerror(errno), errno);
+			out="ERR: Write failed.\n";
+			goto err;
+		}
+		out="OK: Sent.\n";
+
+	}else if(OPTION("quit")){
+		syslog(LOG_NOTICE, "closing backchannel connection");
+		backchannel_close(srv);
+		return (0);
+	}else if(OPTION("help")){
+		out="ERR: Commands:\n"
+			"ERR: - raw BD_ADDR hexstring.\n"
+			"ERR: - help\n";
+			"ERR: - quit\n";
+	}else{
+		out="ERR: Unknown command. Try help.\n";
+	};
+err:
+	if(out)
+		write(srv->backconn,out,strlen(out));
 }
 
--- session.c.org	Mon Oct  9 23:00:17 2006
+++ session.c	Tue May 15 23:17:53 2007
@@ -182,3 +182,15 @@
 	free(s);
 }
 
+/*
+ * Close backchannel
+ */
+
+void
+backchannel_close(bthid_server_p srv)
+{
+		FD_CLR(srv->backconn, &srv->rfdset);
+		FD_CLR(srv->backconn, &srv->wfdset);
+		close(srv->backconn);
+		srv->backconn=0;
+}


More information about the freebsd-bluetooth mailing list