On Sat, 12 Oct 2013, Alexey Dokuchaev wrote:

> Today I got a chance to play with this glamorous rat from Apple, and was
> curious how it gets along with FreeBSD.  Well, it worked, but only as a
> pointer.  Even simplest features like vertical scrolling did not work.
> xev(1) reported of no events coming from when I touch the stupid mouse.
> It looks like they are not being proxied as virtual buttons clicks, but
> implemented somehow differently.  I've also found that in Linux, they
> kinda use a special driver to make it work [1].

yes, also I wrote one for NetBSD[1]

the mouse itself provides a HID profile but the descriptor does not
properly describe the actions of the mouse, and the extra reports are
enabled by setting a feature. it should work as a basic mouse with x & y
and two buttons though..

although the driver is long, mostly the only bits you need to care about
are the input routines and the btmagic_enable() function. You can probably
add this stuff to bthidd(8) I don't know how easy that would be

> Any clues how to investigate this issue?  I probably won't be able to
> make use of all fancy multi-touch features of the mouse, but I'd like to
> at least "export" some of the common gestures, like mouse wheel scroll,
> as a legacy button clicks (so they can be propagated up to the X.org).

I don't know why it doesn't work but I thought legacy button clicks were
built into the normal mouse (did you push? it is not just touch..)

middle button and scrolling will not work without a driver though, that is
all handled in software.. the mouse itself just reports up to five finger
positions, which are stored and compared for each report. after a
threshold then scrolling is activated.

> I guess I could study how we ourselves handle Synaptics touchpads, but
> given this mouse is bluetooth, I figured I better ask here first.

I have a tool i wrote to parse the input via a hci sniffer (attached for
interest) and another tool which I used to send something to the mouse (i
don't remember how that worked, if I had a special driver during
development)

regards,
iain

[1] http://cvsweb.netbsd.org/bsdweb.cgi/src/sys/dev/bluetooth/btmagic.c
#include <sys/param.h>

#include <bluetooth.h>
#include <err.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <usbhid.h>

#include <bthid.h>

/*
 * provide const locators because we don't have a descriptor.
 */

static const struct {
        hid_item_t button1;
        hid_item_t button2;
        hid_item_t padding;
        hid_item_t dX;
        hid_item_t dY;
} basic = {
        .button1 = { .pos = 0, .report_size = 1 },
        .button2 = { .pos = 1, .report_size = 1 },
        .padding = { .pos = 2, .report_size = 6 },
        .dX = { .pos =  8, .report_size = 16, .logical_minimum = -511 },
        .dY = { .pos = 24, .report_size = 16, .logical_minimum = -511 },
};

static const struct {
        /* 40-bit header */
        hid_item_t dXl;
        hid_item_t dYl;
        hid_item_t button1;
        hid_item_t button2;
        hid_item_t dXm;
        hid_item_t dYm;
        hid_item_t timestamp;

        /* 64-bit touch report */
        hid_item_t aW;
        hid_item_t aZ;
        hid_item_t major;
        hid_item_t minor;
        hid_item_t pressure;
        hid_item_t id;
        hid_item_t angle;
        hid_item_t unknown;
        hid_item_t phase;
} mouse = {
        /* header */
        .dXl = { .pos = 0, .report_size = 8 },
        .dYl = { .pos = 8, .report_size = 8 },
        .button1 = { .pos = 16, .report_size = 1 },
        .button2 = { .pos = 17, .report_size = 1 },
        .dXm = { .pos = 18, .report_size = 2, .logical_minimum = -2 },
        .dYm = { .pos = 20, .report_size = 2, .logical_minimum = -2 },
        .timestamp = { .pos = 22, .report_size = 18 },

        /* per touch */
        .aW = { .pos = 0, .report_size = 12, .logical_minimum = -1440 },
        .aZ = { .pos = 12, .report_size = 12, .logical_minimum = -2048 },
        .major = { .pos = 24, .report_size = 8 },
        .minor = { .pos = 32, .report_size = 8 },
        .pressure = { .pos = 40, .report_size = 6 },
        .id = { .pos = 46, .report_size = 4 },
        .angle = { .pos = 50, .report_size = 6 },
        .unknown = { .pos = 56, .report_size = 4 },
        .phase = { .pos = 60, .report_size = 4 },
};

static const struct {
        /* 32-bit header */
        hid_item_t clicks;
        hid_item_t unknown1;
        hid_item_t timestamp;

        /* 72-bit touch report */
        hid_item_t aX;
        hid_item_t aY;
        hid_item_t unknown2;
        hid_item_t major;
        hid_item_t minor;
        hid_item_t pressure;
        hid_item_t id;
        hid_item_t angle;
        hid_item_t unknown3;
        hid_item_t phase;
} track = {
        /* header */
        .clicks = { .pos = 0, .report_size = 8 },
        .unknown1 = { .pos = 8, .report_size = 6 },
        .timestamp = { .pos = 14, .report_size = 18 },

        /* per touch */
        .aX = { .pos = 0, .report_size = 13 },
        .aY = { .pos = 13, .report_size = 13 },
        .unknown2 = { .pos = 26, .report_size = 6 },
        .major = { .pos = 32, .report_size = 8 },
        .minor = { .pos = 40, .report_size = 8 },
        .pressure = { .pos = 48, .report_size = 6 },
        .id = { .pos = 54, .report_size = 8 },
        .angle = { .pos = 62, .report_size = 2 },
        .unknown3 = { .pos = 64, .report_size = 4 },
        .phase = { .pos = 68, .report_size = 4 },
};

static void read_file(char *);
static void read_device(char *);

static void usage(void);
static void dump(const char *, uint8_t *, size_t);
static void printb(const char *, uint8_t *, const hid_item_t *);

static int hci_acl(int, uint8_t *, size_t);
static int hci_event(int, uint8_t *, size_t);
static int l2cap_recv(uint8_t *, size_t);
static int hid_recv(uint8_t *, size_t);
static int basic_input(uint8_t *, size_t);
static int track_input(uint8_t *, size_t);
static int mouse_input(uint8_t *, size_t);

int
main(int ac, char *av[])
{
        char *device, *path;
        int ch;

        device = NULL;
        path = NULL;

        while ((ch = getopt(ac, av, "d:r:")) != -1) {
                switch (ch) {
                case 'd': /* local device */
                        device = optarg;
                        break;

                case 'r': /* read file */
                        path = optarg;
                        break;

                default:
                        usage();
                        /* NOT REACHED */
                }
        }

        if (device != NULL && path != NULL)
                usage();

        if (path != NULL)
                read_file(path);
        else
                read_device(device);

        return 0;
}

static void
read_file(char *path)
{
}

static void
read_device(char *device)
{
        struct bt_devfilter f;
        uint8_t buf[1024];
        ssize_t nr;
        int fd, handle;

        fd = bt_devopen(device, 0);
        if (fd == -1)
                err(EXIT_FAILURE, "bt_devopen");
        
        memset(&f, 0, sizeof(f));
        bt_devfilter_pkt_set(&f, HCI_ACL_DATA_PKT);
        bt_devfilter_pkt_set(&f, HCI_EVENT_PKT);
        bt_devfilter_evt_set(&f, HCI_EVENT_DISCON_COMPL);
        if (bt_devfilter(fd, &f, NULL) == -1)
                err(EXIT_FAILURE, "bt_devfilter");

        handle = 0;

        for (;;) {
                nr = bt_devrecv(fd, buf, sizeof(buf), -1);
                if (nr < 0)
                        err(EXIT_FAILURE, "bt_devrecv");

                if (nr == 0)
                        break;

                switch (buf[0]) {
                case HCI_ACL_DATA_PKT:
                        handle = hci_acl(handle, buf, (size_t)nr);
                        break;

                case HCI_EVENT_PKT:
                        handle = hci_event(handle, buf, (size_t)nr);
                        break;

                default:
                        break;
                }
        }

        close(fd);
}

static void
usage(void)
{

        fprintf(stderr, "Usage: %s { [-d device] | -r path }\n",
            getprogname());

        exit(0);
}

/*
 * dump buffer as hex bytes
 */
static void
dump(const char *str, uint8_t *buf, size_t len)
{

        printf("%s:", str);
        while (len-- > 0)
                printf(" 0x%02x", *buf++);

        printf("\n");
}

/*
 * print hid item as binary
 */
static void
printb(const char *str, uint8_t *buf, const hid_item_t *item)
{
        int num = item->report_size;
        unsigned int value = hid_get_data(buf, item);

        printf("%s", str);

        while (num-- > 0)
                printf("%c", (value & __BIT(num)) ? '1' : '0');
}

static int
hci_acl(int handle, uint8_t *buf, size_t len)
{
        static uint8_t xbuf[1024];
        static size_t xlen;
        int h;

        //dump("acl", buf, len);
        if (len < 5 || len != (size_t)(le16dec(buf + 3) + 5))
                return handle;

        h = le16dec(buf + 1);

        if (HCI_PB_FLAG(h) == HCI_PACKET_START)
                xlen = 0;
        memcpy(xbuf + xlen, buf + 5, len - 5);
        xlen += len - 5;
        if (xlen < le16dec(xbuf))
                return handle;  /* to be continued... */

        if (handle == 0) {
                handle = HCI_CON_HANDLE(h);
                printf("trying handle %d\n", handle);
        }
        if (handle == HCI_CON_HANDLE(h)) {
                if (l2cap_recv(xbuf, xlen) == 0) {
                        printf("abandon handle %d\n", handle);
                        handle = 0;
                }
        }
        return handle;
}

static int
hci_event(int handle, uint8_t *buf, size_t len)
{

        //dump("event", buf, len);
        if (len < 3 || len != (size_t)(buf[2] + 3))
                return handle;

        switch (buf[1]) {
        case HCI_EVENT_DISCON_COMPL:
                if (len != 7 || buf[3] != 0x00 || le16dec(buf + 4) != handle)
                        break;

                printf("handle %d disconnected (reason 0x%02x)\n", handle, 
buf[6]);
                handle = 0;
                break;

        default:
                break;
        }

        return handle;
}

static int
l2cap_recv(uint8_t *buf, size_t len)
{
        int dcid;

        //dump("l2cap", buf, len);
        if (len < 4 || len != (size_t)(le16dec(buf) + 4))
                return 0;

        dcid = le16dec(buf + 2);
        if (dcid < L2CAP_FIRST_CID)
                /* ignore signals */;
        else if (hid_recv(buf + 4, len - 4))
                return 1;

        return 0;
}

#define DATA(type, id)   ((((BTHID_DATA << 4) | BTHID_DATA_##type) << 8) | id)

static int
hid_recv(uint8_t *buf, size_t len)
{

        //dump("hid", buf, len);
        if (len < 1)
                return 0;

        switch (BTHID_TYPE(buf[0])) {
        case BTHID_HANDSHAKE:
                if (len != 1) return 0;
                printf("handshake: 0x%x\n", BTHID_HANDSHAKE_PARAM(buf[0]));
                break;

        case BTHID_DATA:
                if (len < 2) return 0;
                switch (be16dec(buf)) {
                case DATA(INPUT, 0x10): /* Basic report */
                        if (!basic_input(buf + 2, len - 2))
                                return 0;
                        break;

                case DATA(INPUT, 0x28): /* Track report */
                        if (!track_input(buf + 2, len - 2))
                                return 0;
                        break;

                case DATA(INPUT, 0x29): /* Mouse report */
                        if (!mouse_input(buf + 2, len - 2))
                                return 0;
                        break;

                case DATA(INPUT, 0x2a): /* Battery warning */
                        if (len != 3) return 0;
                        printf("battery %s\n", buf[2] > 1 ? "critical" : 
"warning");
                        break;

                case DATA(INPUT, 0x61): /* Surface detection */
                        if (len != 3) return 0;
                        printf("mouse %s\n", buf[2] > 0 ? "raised" : "lowered");
                        break;

                case DATA(FEATURE, 0x47): /* Battery status */
                        if (len != 2) return 0;
                        printf("battery %d%%\n", buf[2]);
                        break;

                case DATA(FEATURE, 0xd7): /* Touch reports */
                        if (len != 3) return 0;
                        printf("touch reports %sabled\n", buf[2] ? "en" : 
"dis");
                        break;

                case DATA(INPUT, 0x30):
                case DATA(INPUT, 0x60):
                case DATA(INPUT, 0xf7):
                        dump("input", buf, len);
                        break;

                case DATA(FEATURE, 0xf0):
                case DATA(FEATURE, 0xf1):
                        dump("feature", buf, len);
                        break;
                        
                default:
                        dump("data", buf, len);
                        return 0;
                }
                break;

        case BTHID_GET_REPORT:
                dump("get report", buf, len);
                break;

        case BTHID_SET_REPORT:
                dump("set report", buf, len);
                break;

        case BTHID_CONTROL:
        case BTHID_GET_PROTOCOL:
        case BTHID_SET_PROTOCOL:
        case BTHID_GET_IDLE:
        case BTHID_SET_IDLE:
        case BTHID_DATC:
        default:
                dump("hid", buf, len);
                return 0;
        }

        return 1;
}

static int
basic_input(uint8_t *buf, size_t len)
{
        
        if (len != 5)
                return 0;

        printf("basic dx%4d, dy%4d, mb %d%d",
            hid_get_data(buf, &basic.dX),
            hid_get_data(buf, &basic.dY),
            hid_get_data(buf, &basic.button1) ? 1 : 0,
            hid_get_data(buf, &basic.button2) ? 1 : 0);

        printf("\n");
        return 1;
}

static int
track_input(uint8_t *buf, size_t len)
{
        if (((len - 4) % 9) != 0)
                return 0;

        printf("track clicks%4d, ts 0x%05x",
            hid_get_data(buf, &track.clicks),
            hid_get_data(buf, &track.timestamp));
        printb(", ", buf, &track.unknown1);

        for (buf += 4, len -= 4; len > 0; len -= 9, buf += 9) {
                printf(" id %x { %d:%d(%d), %d.%d/%d",
                    hid_get_data(buf, &track.id),
                    hid_get_data(buf, &track.aX),
                    hid_get_data(buf, &track.aY),
                    hid_get_data(buf, &track.pressure),
                    hid_get_data(buf, &track.major),
                    hid_get_data(buf, &track.minor),
                    hid_get_data(buf, &track.angle));
                printb(", ", buf, &track.unknown2);
                printb(", ", buf, &track.unknown3);
                printb(", ", buf, &track.phase);
                printf("}");
        }

        printf("\n");
        return 1;
}

static int
mouse_input(uint8_t *buf, size_t len)
{

        if (((len - 5) % 8) != 0)
                return 0;

        printf("mouse dx%4d, dy%4d, mb %d%d, ts 0x%05x",
            (hid_get_data(buf, &mouse.dXm) << 8) | (hid_get_data(buf, 
&mouse.dXl) & 0xff),
            (hid_get_data(buf, &mouse.dYm) << 8) | (hid_get_data(buf, 
&mouse.dYl) & 0xff),
            hid_get_data(buf, &mouse.button1) ? 1 : 0,
            hid_get_data(buf, &mouse.button2) ? 1 : 0,
            hid_get_data(buf, &mouse.timestamp));

        for (buf += 5, len -= 5; len > 0; len -= 8, buf += 8) {
                printf(", id %x {%d:%d(%d), %d.%d/%d",
                    hid_get_data(buf, &mouse.id),
                    hid_get_data(buf, &mouse.aW),
                    hid_get_data(buf, &mouse.aZ),
                    hid_get_data(buf, &mouse.pressure),
                    hid_get_data(buf, &mouse.major),
                    hid_get_data(buf, &mouse.minor),
                    hid_get_data(buf, &mouse.angle));
                printb(", ", buf, &mouse.unknown);
                printb(", ", buf, &mouse.phase);
                printf("}");
        }

        printf("\n");
        return 1;
}
#include <sys/param.h>
#include <sys/ioctl.h>

#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <bthid.h>

static void usage(void);

const char *device = "/dev/wsmouse";

struct btmagic_io {
        size_t  len;
        uint8_t buf[256];
};

int
main(int ac, char *av[])
{
        struct btmagic_io b;
        int ch, fd;

        while ((ch = getopt(ac, av, "d:")) != -1) {
                switch (ch) {
                case 'd':
                        device = optarg;
                        break;

                default:
                        usage();
                        /* NOT REACHED */
                }
        }

        ac -= optind;
        av += optind;

        if (ac == 0)
                usage();

        memset(&b, 0, sizeof(b));
        while (ac-- > 0)
                b.buf[b.len++] = (uint8_t)strtoul(*av++, NULL, 0);

        fd = open(device, O_WRONLY, 0);
        if (fd == -1)
                fd = open(device, O_RDONLY, 0);
        if (fd == -1)
                err(EXIT_FAILURE, "%s", device);

        if (ioctl(fd, _IOW('b', 0xff, struct btmagic_io), &b) == -1)
                err(EXIT_FAILURE, "btmagic_send");

        close(fd);
        return 0;
}

static void
usage(void)
{
        fprintf(stderr, "Usage: %s [-d device] ...\n",
            getprogname());

        exit(0);
}
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-bluetooth
To unsubscribe, send any mail to "[email protected]"

Reply via email to