hid.c revision 163185
1128080Semax/* 2128080Semax * hid.c 3162128Semax */ 4162128Semax 5162128Semax/*- 6162128Semax * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com> 7128080Semax * All rights reserved. 8128080Semax * 9128080Semax * Redistribution and use in source and binary forms, with or without 10128080Semax * modification, are permitted provided that the following conditions 11128080Semax * are met: 12128080Semax * 1. Redistributions of source code must retain the above copyright 13128080Semax * notice, this list of conditions and the following disclaimer. 14128080Semax * 2. Redistributions in binary form must reproduce the above copyright 15128080Semax * notice, this list of conditions and the following disclaimer in the 16128080Semax * documentation and/or other materials provided with the distribution. 17128080Semax * 18128080Semax * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19128080Semax * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20128080Semax * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21128080Semax * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22128080Semax * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23128080Semax * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24128080Semax * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25128080Semax * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26128080Semax * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27128080Semax * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28128080Semax * SUCH DAMAGE. 29128080Semax * 30162128Semax * $Id: hid.c,v 1.5 2006/09/07 21:06:53 max Exp $ 31128080Semax * $FreeBSD: head/usr.sbin/bluetooth/bthidd/hid.c 163185 2006-10-09 22:27:23Z markus $ 32128080Semax */ 33128080Semax 34128080Semax#include <sys/consio.h> 35128080Semax#include <sys/mouse.h> 36128080Semax#include <sys/queue.h> 37128080Semax#include <assert.h> 38128080Semax#include <bluetooth.h> 39128080Semax#include <dev/usb/usb.h> 40128080Semax#include <dev/usb/usbhid.h> 41162128Semax#include <errno.h> 42128080Semax#include <stdio.h> 43128080Semax#include <string.h> 44128080Semax#include <syslog.h> 45137868Semax#include <unistd.h> 46128080Semax#include <usbhid.h> 47162128Semax#include "bthid_config.h" 48128080Semax#include "bthidd.h" 49137868Semax#include "kbd.h" 50128080Semax 51128080Semax#undef min 52128080Semax#define min(x, y) (((x) < (y))? (x) : (y)) 53128080Semax 54137868Semax#undef ASIZE 55137868Semax#define ASIZE(a) (sizeof(a)/sizeof(a[0])) 56137868Semax 57128080Semax/* 58128080Semax * Process data from control channel 59128080Semax */ 60128080Semax 61162128Semaxint32_t 62162128Semaxhid_control(bthid_session_p s, uint8_t *data, int32_t len) 63128080Semax{ 64128080Semax assert(s != NULL); 65128080Semax assert(data != NULL); 66128080Semax assert(len > 0); 67128080Semax 68128080Semax switch (data[0] >> 4) { 69128080Semax case 0: /* Handshake (response to command) */ 70128080Semax if (data[0] & 0xf) 71128080Semax syslog(LOG_ERR, "Got handshake message with error " \ 72128080Semax "response 0x%x from %s", 73128080Semax data[0], bt_ntoa(&s->bdaddr, NULL)); 74128080Semax break; 75128080Semax 76128080Semax case 1: /* HID Control */ 77128080Semax switch (data[0] & 0xf) { 78128080Semax case 0: /* NOP */ 79128080Semax break; 80128080Semax 81128080Semax case 1: /* Hard reset */ 82128080Semax case 2: /* Soft reset */ 83128080Semax syslog(LOG_WARNING, "Device %s requested %s reset", 84128080Semax bt_ntoa(&s->bdaddr, NULL), 85128080Semax ((data[0] & 0xf) == 1)? "hard" : "soft"); 86128080Semax break; 87128080Semax 88128080Semax case 3: /* Suspend */ 89128080Semax syslog(LOG_NOTICE, "Device %s requested Suspend", 90128080Semax bt_ntoa(&s->bdaddr, NULL)); 91128080Semax break; 92128080Semax 93128080Semax case 4: /* Exit suspend */ 94128080Semax syslog(LOG_NOTICE, "Device %s requested Exit Suspend", 95128080Semax bt_ntoa(&s->bdaddr, NULL)); 96128080Semax break; 97128080Semax 98128080Semax case 5: /* Virtual cable unplug */ 99128080Semax syslog(LOG_NOTICE, "Device %s unplugged virtual cable", 100128080Semax bt_ntoa(&s->bdaddr, NULL)); 101128080Semax session_close(s); 102128080Semax break; 103128080Semax 104128080Semax default: 105128080Semax syslog(LOG_WARNING, "Device %s sent unknown " \ 106128080Semax "HID_Control message 0x%x", 107128080Semax bt_ntoa(&s->bdaddr, NULL), data[0]); 108128080Semax break; 109128080Semax } 110128080Semax break; 111128080Semax 112128080Semax default: 113128080Semax syslog(LOG_WARNING, "Got unexpected message 0x%x on Control " \ 114128080Semax "channel from %s", data[0], bt_ntoa(&s->bdaddr, NULL)); 115128080Semax break; 116128080Semax } 117128080Semax 118128080Semax return (0); 119128080Semax} 120128080Semax 121128080Semax/* 122128080Semax * Process data from the interrupt channel 123128080Semax */ 124128080Semax 125162128Semaxint32_t 126162128Semaxhid_interrupt(bthid_session_p s, uint8_t *data, int32_t len) 127128080Semax{ 128162128Semax hid_device_p hid_device; 129128080Semax hid_data_t d; 130128080Semax hid_item_t h; 131162128Semax int32_t report_id, usage, page, val, 132128080Semax mouse_x, mouse_y, mouse_z, mouse_butt, 133137868Semax mevents, kevents; 134128080Semax 135128080Semax assert(s != NULL); 136137868Semax assert(s->srv != NULL); 137128080Semax assert(data != NULL); 138128080Semax 139128080Semax if (len < 3) { 140128080Semax syslog(LOG_ERR, "Got short message (%d bytes) on Interrupt " \ 141128080Semax "channel from %s", len, bt_ntoa(&s->bdaddr, NULL)); 142128080Semax return (-1); 143128080Semax } 144128080Semax 145162128Semax if (data[0] != 0xa1) { 146128080Semax syslog(LOG_ERR, "Got unexpected message 0x%x on " \ 147128080Semax "Interrupt channel from %s", 148128080Semax data[0], bt_ntoa(&s->bdaddr, NULL)); 149128080Semax return (-1); 150128080Semax } 151128080Semax 152128080Semax report_id = data[1]; 153128080Semax data += 2; 154128080Semax len -= 2; 155128080Semax 156128080Semax hid_device = get_hid_device(&s->bdaddr); 157128080Semax assert(hid_device != NULL); 158128080Semax 159137868Semax mouse_x = mouse_y = mouse_z = mouse_butt = mevents = kevents = 0; 160128080Semax 161128080Semax for (d = hid_start_parse(hid_device->desc, 1 << hid_input, -1); 162128080Semax hid_get_item(d, &h) > 0; ) { 163128080Semax if ((h.flags & HIO_CONST) || (h.report_ID != report_id)) 164128080Semax continue; 165128080Semax 166128080Semax page = HID_PAGE(h.usage); 167128080Semax usage = HID_USAGE(h.usage); 168128080Semax val = hid_get_data(data, &h); 169128080Semax 170128080Semax switch (page) { 171128080Semax case HUP_GENERIC_DESKTOP: 172128080Semax switch (usage) { 173128080Semax case HUG_X: 174128080Semax mouse_x = val; 175137868Semax mevents ++; 176128080Semax break; 177128080Semax 178128080Semax case HUG_Y: 179128080Semax mouse_y = val; 180137868Semax mevents ++; 181128080Semax break; 182128080Semax 183128080Semax case HUG_WHEEL: 184128080Semax mouse_z = -val; 185137868Semax mevents ++; 186128080Semax break; 187128080Semax 188128080Semax case HUG_SYSTEM_SLEEP: 189128080Semax if (val) 190128080Semax syslog(LOG_NOTICE, "Sleep button pressed"); 191128080Semax break; 192128080Semax } 193128080Semax break; 194128080Semax 195128080Semax case HUP_KEYBOARD: 196137868Semax kevents ++; 197137868Semax 198128080Semax if (h.flags & HIO_VARIABLE) { 199137868Semax if (val && usage < kbd_maxkey()) 200162128Semax bit_set(s->keys1, usage); 201128080Semax } else { 202137868Semax if (val && val < kbd_maxkey()) 203162128Semax bit_set(s->keys1, val); 204137868Semax 205128080Semax data ++; 206128080Semax len --; 207128080Semax 208128080Semax len = min(len, h.report_size); 209128080Semax while (len > 0) { 210128080Semax val = hid_get_data(data, &h); 211137868Semax if (val && val < kbd_maxkey()) 212162128Semax bit_set(s->keys1, val); 213137868Semax 214128080Semax data ++; 215128080Semax len --; 216128080Semax } 217128080Semax } 218128080Semax break; 219128080Semax 220128080Semax case HUP_BUTTON: 221162128Semax if (usage != 0) { 222162128Semax if (usage == 2) 223162128Semax usage = 3; 224162128Semax else if (usage == 3) 225162128Semax usage = 2; 226162128Semax 227162128Semax mouse_butt |= (val << (usage - 1)); 228162128Semax mevents ++; 229162128Semax } 230128080Semax break; 231128080Semax 232137868Semax case HUP_CONSUMER: 233137868Semax if (!val) 234137868Semax break; 235137868Semax 236137868Semax switch (usage) { 237137868Semax case 0xb5: /* Scan Next Track */ 238137868Semax val = 0x19; 239137868Semax break; 240137868Semax 241137868Semax case 0xb6: /* Scan Previous Track */ 242137868Semax val = 0x10; 243137868Semax break; 244137868Semax 245137868Semax case 0xb7: /* Stop */ 246137868Semax val = 0x24; 247137868Semax break; 248137868Semax 249137868Semax case 0xcd: /* Play/Pause */ 250137868Semax val = 0x22; 251137868Semax break; 252137868Semax 253137868Semax case 0xe2: /* Mute */ 254137868Semax val = 0x20; 255137868Semax break; 256137868Semax 257137868Semax case 0xe9: /* Volume Up */ 258137868Semax val = 0x30; 259137868Semax break; 260137868Semax 261137868Semax case 0xea: /* Volume Down */ 262137868Semax val = 0x2E; 263137868Semax break; 264137868Semax 265137868Semax case 0x183: /* Media Select */ 266137868Semax val = 0x6D; 267137868Semax break; 268137868Semax 269137868Semax case 0x018a: /* Mail */ 270137868Semax val = 0x6C; 271137868Semax break; 272137868Semax 273137868Semax case 0x192: /* Calculator */ 274137868Semax val = 0x21; 275137868Semax break; 276137868Semax 277137868Semax case 0x194: /* My Computer */ 278137868Semax val = 0x6B; 279137868Semax break; 280137868Semax 281137868Semax case 0x221: /* WWW Search */ 282137868Semax val = 0x65; 283137868Semax break; 284137868Semax 285137868Semax case 0x223: /* WWW Home */ 286137868Semax val = 0x32; 287137868Semax break; 288137868Semax 289137868Semax case 0x224: /* WWW Back */ 290137868Semax val = 0x6A; 291137868Semax break; 292137868Semax 293137868Semax case 0x225: /* WWW Forward */ 294137868Semax val = 0x69; 295137868Semax break; 296137868Semax 297137868Semax case 0x226: /* WWW Stop */ 298137868Semax val = 0x68; 299137868Semax break; 300137868Semax 301163185Smarkus case 0x227: /* WWW Refresh */ 302137868Semax val = 0x67; 303137868Semax break; 304137868Semax 305137868Semax case 0x22a: /* WWW Favorites */ 306137868Semax val = 0x66; 307137868Semax break; 308137868Semax 309137868Semax default: 310137868Semax val = 0; 311137868Semax break; 312137868Semax } 313137868Semax 314137868Semax /* XXX FIXME - UGLY HACK */ 315137868Semax if (val != 0) { 316162128Semax if (hid_device->keyboard) { 317162128Semax int32_t buf[4] = { 0xe0, val, 318162128Semax 0xe0, val|0x80 }; 319137868Semax 320162128Semax assert(s->vkbd != -1); 321162128Semax write(s->vkbd, buf, sizeof(buf)); 322162128Semax } else 323162128Semax syslog(LOG_ERR, "Keyboard events " \ 324162128Semax "received from non-keyboard " \ 325162128Semax "device %s. Please report", 326162128Semax bt_ntoa(&s->bdaddr, NULL)); 327137868Semax } 328137868Semax break; 329137868Semax 330128080Semax case HUP_MICROSOFT: 331128080Semax switch (usage) { 332128080Semax case 0xfe01: 333128080Semax if (!hid_device->battery_power) 334128080Semax break; 335128080Semax 336128080Semax switch (val) { 337128080Semax case 1: 338128080Semax syslog(LOG_INFO, "Battery is OK on %s", 339128080Semax bt_ntoa(&s->bdaddr, NULL)); 340128080Semax break; 341128080Semax 342128080Semax case 2: 343128080Semax syslog(LOG_NOTICE, "Low battery on %s", 344128080Semax bt_ntoa(&s->bdaddr, NULL)); 345128080Semax break; 346128080Semax 347128080Semax case 3: 348128080Semax syslog(LOG_WARNING, "Very low battery "\ 349128080Semax "on %s", 350128080Semax bt_ntoa(&s->bdaddr, NULL)); 351128080Semax break; 352128080Semax } 353128080Semax break; 354128080Semax } 355128080Semax break; 356128080Semax } 357128080Semax } 358128080Semax hid_end_parse(d); 359128080Semax 360162128Semax /* 361162128Semax * XXX FIXME Feed keyboard events into kernel. 362162128Semax * The code below works, bit host also needs to track 363162128Semax * and handle repeat. 364162128Semax * 365162128Semax * Key repeat currently works in X, but not in console. 366162128Semax */ 367137868Semax 368162128Semax if (kevents > 0) { 369162128Semax if (hid_device->keyboard) { 370162128Semax assert(s->vkbd != -1); 371162128Semax kbd_process_keys(s); 372162128Semax } else 373162128Semax syslog(LOG_ERR, "Keyboard events received from " \ 374162128Semax "non-keyboard device %s. Please report", 375162128Semax bt_ntoa(&s->bdaddr, NULL)); 376162128Semax } 377162128Semax 378128080Semax /* 379137868Semax * XXX FIXME Feed mouse events into kernel. 380137868Semax * The code block below works, but it is not good enough. 381137868Semax * Need to track double-clicks etc. 382162128Semax * 383162128Semax * Double click currently works in X, but not in console. 384128080Semax */ 385128080Semax 386137868Semax if (mevents > 0) { 387128080Semax struct mouse_info mi; 388128080Semax 389128080Semax mi.operation = MOUSE_ACTION; 390128080Semax mi.u.data.x = mouse_x; 391128080Semax mi.u.data.y = mouse_y; 392128080Semax mi.u.data.z = mouse_z; 393128080Semax mi.u.data.buttons = mouse_butt; 394128080Semax 395128080Semax if (ioctl(s->srv->cons, CONS_MOUSECTL, &mi) < 0) 396128080Semax syslog(LOG_ERR, "Could not process mouse events from " \ 397128080Semax "%s. %s (%d)", bt_ntoa(&s->bdaddr, NULL), 398128080Semax strerror(errno), errno); 399128080Semax } 400128080Semax 401128080Semax return (0); 402128080Semax} 403