summaryrefslogtreecommitdiffstats
path: root/input
diff options
context:
space:
mode:
authorBastien Nocera <hadess@hadess.net>2008-10-11 00:55:02 +0100
committerMarcel Holtmann <marcel@holtmann.org>2008-10-16 01:43:01 +0200
commitd2816fa090ca6c6ac718cb99ed6d09801d0e0875 (patch)
tree9e9f07ccb3e234400332ab57d0843ce0154f5452 /input
parent4141c0a31b9a79daea0f0d47e8018bcf168978ec (diff)
Add sixpair tool
Sixpair is an unfinished helper to automatically pair a plugged in Playstation 3 Sixaxis Bluetooth joypad.
Diffstat (limited to 'input')
-rw-r--r--input/sixpair.c393
1 files changed, 393 insertions, 0 deletions
diff --git a/input/sixpair.c b/input/sixpair.c
new file mode 100644
index 00000000..818d2831
--- /dev/null
+++ b/input/sixpair.c
@@ -0,0 +1,393 @@
+/* To compile
+ * gcc -g -Wall -I../src -I../lib/ -I../include -DSTORAGEDIR=\"/var/lib/bluetooth\" -o sixpair sixpair.c ../src/storage.c ../common/libhelper.a -I../common `pkg-config --libs --cflags glib-2.0 libusb` -lbluetooth
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <inttypes.h>
+
+#include <sdp.h>
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hidp.h>
+#include <glib.h>
+#include <usb.h>
+
+#include "storage.h"
+
+/* Vendor and product ID for the Sixaxis PS3 controller */
+#define VENDOR 0x054c
+#define PRODUCT 0x0268
+
+#define USB_DIR_IN 0x80
+#define USB_DIR_OUT 0
+
+gboolean option_get_master = TRUE;
+char *option_master= NULL;
+gboolean option_store_info = TRUE;
+const char *option_device = NULL;
+gboolean option_quiet = FALSE;
+
+const GOptionEntry options[] = {
+ { "get-master", '\0', 0, G_OPTION_ARG_NONE, &option_get_master, "Get currently set master address", NULL },
+ { "set-master", '\0', 0, G_OPTION_ARG_STRING, &option_master, "Set master address (\"auto\" for automatic)", NULL },
+ { "store-info", '\0', 0, G_OPTION_ARG_NONE, &option_store_info, "Store the HID info into the input database", NULL },
+ { "device", '\0', 0, G_OPTION_ARG_STRING, &option_device, "Only handle one device (default, all supported", NULL },
+ { "quiet", 'q', 0, G_OPTION_ARG_NONE, &option_quiet, "Quieten the output", NULL },
+ { NULL }
+};
+
+static gboolean
+show_master (usb_dev_handle *devh, int itfnum)
+{
+ unsigned char msg[8];
+ int res;
+
+ res = usb_control_msg (devh,
+ USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0x01, 0x03f5, itfnum,
+ (void*) msg, sizeof(msg),
+ 5000);
+
+ if (res < 0) {
+ g_warning ("Getting the master Bluetooth address failed");
+ return FALSE;
+ }
+ g_print ("Current Bluetooth master: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ msg[2], msg[3], msg[4], msg[5], msg[6], msg[7]);
+
+ return TRUE;
+}
+
+static char *
+get_bdaddr (usb_dev_handle *devh, int itfnum)
+{
+ unsigned char msg[17];
+ char *address;
+ int res;
+
+ res = usb_control_msg (devh,
+ USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0x01, 0x03f2, itfnum,
+ (void*) msg, sizeof(msg),
+ 5000);
+
+ if (res < 0) {
+ g_warning ("Getting the device Bluetooth address failed");
+ return NULL;
+ }
+
+ address = g_strdup_printf ("%02x:%02x:%02x:%02x:%02x:%02x",
+ msg[4], msg[5], msg[6], msg[7], msg[8], msg[9]);
+
+ if (option_quiet == FALSE) {
+ g_print ("Device Bluetooth address: %s\n", address);
+ }
+
+ return address;
+}
+
+static gboolean
+set_master_bdaddr (usb_dev_handle *devh, int itfnum, char *host)
+{
+ unsigned char msg[8];
+ int mac[6];
+ int res;
+
+ if (sscanf(host, "%x:%x:%x:%x:%x:%x",
+ &mac[0],&mac[1],&mac[2],&mac[3],&mac[4],&mac[5]) != 6) {
+ return FALSE;
+ }
+
+ msg[0] = 0x01;
+ msg[1] = 0x00;
+ msg[2] = mac[0];
+ msg[3] = mac[1];
+ msg[4] = mac[2];
+ msg[5] = mac[3];
+ msg[6] = mac[4];
+ msg[7] = mac[5];
+
+ res = usb_control_msg (devh,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0x09, 0x03f5, itfnum,
+ (void*) msg, sizeof(msg),
+ 5000);
+
+ if (res < 0) {
+ g_warning ("Setting the master Bluetooth address failed");
+ return FALSE;
+ }
+
+ return TRUE;
+
+}
+
+static char *
+get_host_bdaddr (void)
+{
+ FILE *f;
+ int mac[6];
+
+ //FIXME use dbus to get the default adapter
+
+ f = popen("hcitool dev", "r");
+
+ if (f == NULL) {
+ //FIXME
+ return NULL;
+ }
+ if (fscanf(f, "%*s\n%*s %x:%x:%x:%x:%x:%x",
+ &mac[0],&mac[1],&mac[2],&mac[3],&mac[4],&mac[5]) != 6) {
+ //FIXME
+ return NULL;
+ }
+
+ return g_strdup_printf ("%x:%x:%x:%x:%x:%x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+}
+
+static int
+get_record_info (struct usb_interface_descriptor *alt, unsigned int *_len, unsigned int *_country, uint16_t *_version)
+{
+ unsigned char *buf;
+ unsigned int size, len, country;
+ uint16_t version;
+ int l;
+
+ len = 0;
+ country = 0;
+ version = 0;
+
+ if (!alt->extralen)
+ return 0;
+
+ size = alt->extralen;
+ buf = alt->extra;
+ while (size >= 2 * sizeof(u_int8_t)) {
+ if (buf[0] < 2 || buf[1] != USB_DT_HID)
+ continue;
+
+ //FIXME that should be "21"
+ //g_message ("country: %u", buf[4]);
+ //country = buf[4];
+ //country = 0x21;
+ country = 0;
+ version = (buf[3] << 8) + buf[2];
+
+ for (l = 0; l < buf[5]; l++) {
+ /* we are just interested in report descriptors*/
+ if (buf[6+3*l] != USB_DT_REPORT)
+ continue;
+ len = buf[7+3*l] | (buf[8+3*l] << 8);
+ }
+ size -= buf[0];
+ buf += buf[0];
+ }
+
+ if (len == 0)
+ return -1;
+ *_len = len;
+ *_country = country;
+ *_version = version;
+
+ return 0;
+}
+
+static void
+fill_req_from_usb (struct usb_device *dev, struct hidp_connadd_req *req, void *data, unsigned int len, unsigned int country, uint16_t version)
+{
+ req->vendor = dev->descriptor.idVendor;
+ req->product = dev->descriptor.idProduct;
+ req->version = version;
+ /* req->subclass already set */
+ req->country = country;
+ /* Default value */
+ req->parser = 0x0100;
+ /* What are we expecting here? No idea, but we don't seem to need it */
+ req->flags = 0;
+
+ req->rd_size = len;
+ req->rd_data = data;
+}
+
+static void
+store_info (const char *host, const char *device, struct hidp_connadd_req *req)
+{
+ bdaddr_t dest, src;
+
+ if (str2ba (host, &src) < 0) {
+ //FIXME
+ return;
+ }
+ if (str2ba (device, &dest) < 0) {
+ //FIXME
+ return;
+ }
+
+#if 0
+ if (store_device_info (&src, &dest, req) < 0)
+#endif
+ g_message ("store_device_info failed");
+}
+
+static int
+handle_device (struct usb_device *dev, struct usb_config_descriptor *cfg, int itfnum, struct usb_interface_descriptor *alt)
+{
+ usb_dev_handle *devh;
+ int res, retval;
+
+ retval = -1;
+
+ devh = usb_open (dev);
+ if (devh == NULL) {
+ g_warning ("Can't open device");
+ goto bail;
+ }
+ usb_detach_kernel_driver_np (devh, itfnum);
+
+ res = usb_claim_interface (devh, itfnum);
+ if (res < 0) {
+ g_warning ("Can't claim interface %d", itfnum);
+ goto bail;
+ }
+
+ if (option_get_master != FALSE) {
+ if (show_master (devh, itfnum) == FALSE)
+ goto bail;
+ retval = 0;
+ }
+
+ if (option_master != NULL) {
+ if (strcmp (option_master, "auto") == 0) {
+ g_free (option_master);
+ option_master = get_host_bdaddr ();
+ if (option_master == NULL) {
+ g_warning ("Can't get bdaddr from default device");
+ retval = -1;
+ goto bail;
+ }
+ }
+ } else {
+ option_master = get_host_bdaddr ();
+ if (option_master == NULL) {
+ g_warning ("Can't get bdaddr from default device");
+ retval = -1;
+ goto bail;
+ }
+ }
+
+ if (option_store_info != FALSE) {
+ unsigned char data[8192];
+ struct hidp_connadd_req req;
+ unsigned int len, country;
+ int n;
+ uint16_t version;
+ char *device;
+
+ device = get_bdaddr (devh, itfnum);
+ if (device == NULL) {
+ retval = -1;
+ goto bail;
+ }
+
+ if (get_record_info (alt, &len, &country, &version) < 0) {
+ g_warning ("Can't get record info");
+ retval = -1;
+ goto bail;
+ }
+
+ if ((n = usb_control_msg(devh,
+ USB_ENDPOINT_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE,
+ USB_REQ_GET_DESCRIPTOR,
+ (USB_DT_REPORT << 8),
+ itfnum, (void *) &data, len, 5000)) < 0) {
+ g_warning ("Can't get report descriptor (length: %d, interface: %d)", len, itfnum);
+ retval = -1;
+ goto bail;
+ }
+
+ req.subclass = alt->bInterfaceSubClass;
+ fill_req_from_usb (dev, &req, data, len, country, version);
+
+ store_info (option_master, device, &req);
+
+ if (set_master_bdaddr (devh, itfnum, option_master) == FALSE) {
+ retval = -1;
+ goto bail;
+ }
+
+ //FIXME finally, set device as trusted
+ }
+
+bail:
+ if (devh != NULL)
+ usb_close (devh);
+
+ return retval;
+}
+
+int main (int argc, char **argv)
+{
+ GOptionContext *context;
+ GError *error = NULL;
+ struct usb_bus *busses, *bus;
+
+ context = g_option_context_new ("- Manage Sixaxis PS3 controllers");
+ g_option_context_add_main_entries (context, options, NULL);
+ if (g_option_context_parse (context, &argc, &argv, &error) == FALSE) {
+ g_warning ("Couldn't parse command-line options: %s", error->message);
+ return 1;
+ }
+
+ /* Check that the passed bdaddr is correct */
+ if (option_master != NULL && strcmp (option_master, "auto") != 0) {
+ //FIXME check bdaddr
+ }
+
+ /* Find device(s) */
+ usb_init ();
+ if (usb_find_busses () < 0) {
+ g_warning ("usb_find_busses failed");
+ return 1;
+ }
+ if (usb_find_devices () < 0) {
+ g_warning ("usb_find_devices failed");
+ return 1;
+ }
+
+ busses = usb_get_busses();
+ if (busses == NULL) {
+ g_warning ("usb_get_busses failed");
+ return 1;
+ }
+
+ for (bus = busses; bus; bus = bus->next) {
+ struct usb_device *dev;
+
+ for (dev = bus->devices; dev; dev = dev->next) {
+ struct usb_config_descriptor *cfg;
+
+ /* Here we check for the supported devices */
+ if (dev->descriptor.idVendor != VENDOR || dev->descriptor.idProduct != PRODUCT)
+ continue;
+
+ /* Look for the interface number that interests us */
+ for (cfg = dev->config; cfg < dev->config + dev->descriptor.bNumConfigurations; ++cfg) {
+ int itfnum;
+
+ for (itfnum = 0; itfnum < cfg->bNumInterfaces; ++itfnum) {
+ struct usb_interface *itf = &cfg->interface[itfnum];
+ struct usb_interface_descriptor *alt;
+
+ for (alt = itf->altsetting; alt < itf->altsetting + itf->num_altsetting; ++alt) {
+ if (alt->bInterfaceClass == 3) {
+ handle_device (dev, cfg, itfnum, alt);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+