hid.c revision 128080
1128080Semax/* 2128080Semax * hid.c 3128080Semax * 4128080Semax * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com> 5128080Semax * All rights reserved. 6128080Semax * 7128080Semax * Redistribution and use in source and binary forms, with or without 8128080Semax * modification, are permitted provided that the following conditions 9128080Semax * are met: 10128080Semax * 1. Redistributions of source code must retain the above copyright 11128080Semax * notice, this list of conditions and the following disclaimer. 12128080Semax * 2. Redistributions in binary form must reproduce the above copyright 13128080Semax * notice, this list of conditions and the following disclaimer in the 14128080Semax * documentation and/or other materials provided with the distribution. 15128080Semax * 16128080Semax * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17128080Semax * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18128080Semax * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19128080Semax * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20128080Semax * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21128080Semax * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22128080Semax * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23128080Semax * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24128080Semax * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25128080Semax * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26128080Semax * SUCH DAMAGE. 27128080Semax * 28128080Semax * $Id: hid.c,v 1.3 2004/02/26 21:47:35 max Exp $ 29128080Semax * $FreeBSD: head/usr.sbin/bluetooth/bthidd/hid.c 128080 2004-04-10 00:18:00Z emax $ 30128080Semax */ 31128080Semax 32128080Semax#include <sys/consio.h> 33128080Semax#include <sys/mouse.h> 34128080Semax#include <sys/queue.h> 35128080Semax#include <assert.h> 36128080Semax#include <bluetooth.h> 37128080Semax#include <errno.h> 38128080Semax#include <dev/usb/usb.h> 39128080Semax#include <dev/usb/usbhid.h> 40128080Semax#include <stdio.h> 41128080Semax#include <string.h> 42128080Semax#include <syslog.h> 43128080Semax#include <usbhid.h> 44128080Semax#include "bthidd.h" 45128080Semax#include "bthid_config.h" 46128080Semax 47128080Semax#undef min 48128080Semax#define min(x, y) (((x) < (y))? (x) : (y)) 49128080Semax 50128080Semax/* 51128080Semax * Process data from control channel 52128080Semax */ 53128080Semax 54128080Semaxint 55128080Semaxhid_control(bthid_session_p s, char *data, int len) 56128080Semax{ 57128080Semax assert(s != NULL); 58128080Semax assert(data != NULL); 59128080Semax assert(len > 0); 60128080Semax 61128080Semax switch (data[0] >> 4) { 62128080Semax case 0: /* Handshake (response to command) */ 63128080Semax if (data[0] & 0xf) 64128080Semax syslog(LOG_ERR, "Got handshake message with error " \ 65128080Semax "response 0x%x from %s", 66128080Semax data[0], bt_ntoa(&s->bdaddr, NULL)); 67128080Semax break; 68128080Semax 69128080Semax case 1: /* HID Control */ 70128080Semax switch (data[0] & 0xf) { 71128080Semax case 0: /* NOP */ 72128080Semax break; 73128080Semax 74128080Semax case 1: /* Hard reset */ 75128080Semax case 2: /* Soft reset */ 76128080Semax syslog(LOG_WARNING, "Device %s requested %s reset", 77128080Semax bt_ntoa(&s->bdaddr, NULL), 78128080Semax ((data[0] & 0xf) == 1)? "hard" : "soft"); 79128080Semax break; 80128080Semax 81128080Semax case 3: /* Suspend */ 82128080Semax syslog(LOG_NOTICE, "Device %s requested Suspend", 83128080Semax bt_ntoa(&s->bdaddr, NULL)); 84128080Semax break; 85128080Semax 86128080Semax case 4: /* Exit suspend */ 87128080Semax syslog(LOG_NOTICE, "Device %s requested Exit Suspend", 88128080Semax bt_ntoa(&s->bdaddr, NULL)); 89128080Semax break; 90128080Semax 91128080Semax case 5: /* Virtual cable unplug */ 92128080Semax syslog(LOG_NOTICE, "Device %s unplugged virtual cable", 93128080Semax bt_ntoa(&s->bdaddr, NULL)); 94128080Semax session_close(s); 95128080Semax break; 96128080Semax 97128080Semax default: 98128080Semax syslog(LOG_WARNING, "Device %s sent unknown " \ 99128080Semax "HID_Control message 0x%x", 100128080Semax bt_ntoa(&s->bdaddr, NULL), data[0]); 101128080Semax break; 102128080Semax } 103128080Semax break; 104128080Semax 105128080Semax default: 106128080Semax syslog(LOG_WARNING, "Got unexpected message 0x%x on Control " \ 107128080Semax "channel from %s", data[0], bt_ntoa(&s->bdaddr, NULL)); 108128080Semax break; 109128080Semax } 110128080Semax 111128080Semax return (0); 112128080Semax} 113128080Semax 114128080Semax/* 115128080Semax * Process data from the interrupt channel 116128080Semax */ 117128080Semax 118128080Semaxint 119128080Semaxhid_interrupt(bthid_session_p s, char *data, int len) 120128080Semax{ 121128080Semax hid_device_p hid_device = NULL; 122128080Semax hid_data_t d; 123128080Semax hid_item_t h; 124128080Semax int report_id, usage, page, val, 125128080Semax mouse_x, mouse_y, mouse_z, mouse_butt, 126128080Semax nkeys, keys[32]; /* XXX how big keys[] should be? */ 127128080Semax 128128080Semax assert(s != NULL); 129128080Semax assert(data != NULL); 130128080Semax 131128080Semax if (len < 3) { 132128080Semax syslog(LOG_ERR, "Got short message (%d bytes) on Interrupt " \ 133128080Semax "channel from %s", len, bt_ntoa(&s->bdaddr, NULL)); 134128080Semax return (-1); 135128080Semax } 136128080Semax 137128080Semax if ((unsigned char) data[0] != 0xa1) { 138128080Semax syslog(LOG_ERR, "Got unexpected message 0x%x on " \ 139128080Semax "Interrupt channel from %s", 140128080Semax data[0], bt_ntoa(&s->bdaddr, NULL)); 141128080Semax return (-1); 142128080Semax } 143128080Semax 144128080Semax report_id = data[1]; 145128080Semax data += 2; 146128080Semax len -= 2; 147128080Semax 148128080Semax hid_device = get_hid_device(&s->bdaddr); 149128080Semax assert(hid_device != NULL); 150128080Semax 151128080Semax mouse_x = mouse_y = mouse_z = mouse_butt = nkeys = 0; 152128080Semax 153128080Semax for (d = hid_start_parse(hid_device->desc, 1 << hid_input, -1); 154128080Semax hid_get_item(d, &h) > 0; ) { 155128080Semax if ((h.flags & HIO_CONST) || (h.report_ID != report_id)) 156128080Semax continue; 157128080Semax 158128080Semax page = HID_PAGE(h.usage); 159128080Semax usage = HID_USAGE(h.usage); 160128080Semax val = hid_get_data(data, &h); 161128080Semax 162128080Semax switch (page) { 163128080Semax case HUP_GENERIC_DESKTOP: 164128080Semax switch (usage) { 165128080Semax case HUG_X: 166128080Semax mouse_x = val; 167128080Semax break; 168128080Semax 169128080Semax case HUG_Y: 170128080Semax mouse_y = val; 171128080Semax break; 172128080Semax 173128080Semax case HUG_WHEEL: 174128080Semax mouse_z = -val; 175128080Semax break; 176128080Semax 177128080Semax case HUG_SYSTEM_SLEEP: 178128080Semax if (val) 179128080Semax syslog(LOG_NOTICE, "Sleep button pressed"); 180128080Semax break; 181128080Semax } 182128080Semax break; 183128080Semax 184128080Semax case HUP_KEYBOARD: 185128080Semax if (h.flags & HIO_VARIABLE) { 186128080Semax if (val && nkeys < sizeof(keys)) 187128080Semax keys[nkeys ++] = usage; 188128080Semax } else { 189128080Semax if (val && nkeys < sizeof(keys)) 190128080Semax keys[nkeys ++] = val; 191128080Semax data ++; 192128080Semax len --; 193128080Semax 194128080Semax len = min(len, h.report_size); 195128080Semax while (len > 0) { 196128080Semax val = hid_get_data(data, &h); 197128080Semax if (val && nkeys < sizeof(keys)) 198128080Semax keys[nkeys ++] = val; 199128080Semax data ++; 200128080Semax len --; 201128080Semax } 202128080Semax } 203128080Semax break; 204128080Semax 205128080Semax case HUP_BUTTON: 206128080Semax mouse_butt |= (val << (usage - 1)); 207128080Semax break; 208128080Semax 209128080Semax case HUP_MICROSOFT: 210128080Semax switch (usage) { 211128080Semax case 0xfe01: 212128080Semax if (!hid_device->battery_power) 213128080Semax break; 214128080Semax 215128080Semax switch (val) { 216128080Semax case 1: 217128080Semax syslog(LOG_INFO, "Battery is OK on %s", 218128080Semax bt_ntoa(&s->bdaddr, NULL)); 219128080Semax break; 220128080Semax 221128080Semax case 2: 222128080Semax syslog(LOG_NOTICE, "Low battery on %s", 223128080Semax bt_ntoa(&s->bdaddr, NULL)); 224128080Semax break; 225128080Semax 226128080Semax case 3: 227128080Semax syslog(LOG_WARNING, "Very low battery "\ 228128080Semax "on %s", 229128080Semax bt_ntoa(&s->bdaddr, NULL)); 230128080Semax break; 231128080Semax } 232128080Semax break; 233128080Semax } 234128080Semax break; 235128080Semax } 236128080Semax } 237128080Semax hid_end_parse(d); 238128080Semax 239128080Semax /* 240128080Semax * XXX FIXME Feed mouse and keyboard events into kernel 241128080Semax * The code block below works, but it is not 242128080Semax * good enough 243128080Semax */ 244128080Semax 245128080Semax if (mouse_x != 0 || mouse_y != 0 || mouse_z != 0 || mouse_butt != 0) { 246128080Semax struct mouse_info mi; 247128080Semax 248128080Semax mi.operation = MOUSE_ACTION; 249128080Semax mi.u.data.x = mouse_x; 250128080Semax mi.u.data.y = mouse_y; 251128080Semax mi.u.data.z = mouse_z; 252128080Semax mi.u.data.buttons = mouse_butt; 253128080Semax 254128080Semax if (ioctl(s->srv->cons, CONS_MOUSECTL, &mi) < 0) 255128080Semax syslog(LOG_ERR, "Could not process mouse events from " \ 256128080Semax "%s. %s (%d)", bt_ntoa(&s->bdaddr, NULL), 257128080Semax strerror(errno), errno); 258128080Semax } 259128080Semax 260128080Semax return (0); 261128080Semax} 262128080Semax 263