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$ 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, 133207812Skaiw mevents, kevents, i; 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]; 153207812Skaiw data ++; 154207812Skaiw len --; 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; ) { 163212335Semax if ((h.flags & HIO_CONST) || (h.report_ID != report_id) || 164212335Semax (h.kind != hid_input)) 165128080Semax continue; 166128080Semax 167128080Semax page = HID_PAGE(h.usage); 168128080Semax usage = HID_USAGE(h.usage); 169128080Semax val = hid_get_data(data, &h); 170128080Semax 171128080Semax switch (page) { 172128080Semax case HUP_GENERIC_DESKTOP: 173128080Semax switch (usage) { 174128080Semax case HUG_X: 175128080Semax mouse_x = val; 176137868Semax mevents ++; 177128080Semax break; 178128080Semax 179128080Semax case HUG_Y: 180128080Semax mouse_y = val; 181137868Semax mevents ++; 182128080Semax break; 183128080Semax 184128080Semax case HUG_WHEEL: 185128080Semax mouse_z = -val; 186137868Semax mevents ++; 187128080Semax break; 188128080Semax 189128080Semax case HUG_SYSTEM_SLEEP: 190128080Semax if (val) 191128080Semax syslog(LOG_NOTICE, "Sleep button pressed"); 192128080Semax break; 193128080Semax } 194128080Semax break; 195128080Semax 196128080Semax case HUP_KEYBOARD: 197137868Semax kevents ++; 198137868Semax 199128080Semax if (h.flags & HIO_VARIABLE) { 200137868Semax if (val && usage < kbd_maxkey()) 201162128Semax bit_set(s->keys1, usage); 202128080Semax } else { 203137868Semax if (val && val < kbd_maxkey()) 204162128Semax bit_set(s->keys1, val); 205137868Semax 206207812Skaiw for (i = 1; i < h.report_count; i++) { 207207812Skaiw h.pos += h.report_size; 208128080Semax val = hid_get_data(data, &h); 209137868Semax if (val && val < kbd_maxkey()) 210162128Semax bit_set(s->keys1, val); 211128080Semax } 212128080Semax } 213128080Semax break; 214128080Semax 215128080Semax case HUP_BUTTON: 216162128Semax if (usage != 0) { 217162128Semax if (usage == 2) 218162128Semax usage = 3; 219162128Semax else if (usage == 3) 220162128Semax usage = 2; 221162128Semax 222162128Semax mouse_butt |= (val << (usage - 1)); 223162128Semax mevents ++; 224162128Semax } 225128080Semax break; 226128080Semax 227137868Semax case HUP_CONSUMER: 228137868Semax if (!val) 229137868Semax break; 230137868Semax 231137868Semax switch (usage) { 232221448Semax case HUC_AC_PAN: 233221448Semax /* Horizontal scroll */ 234221448Semax if (val < 0) 235221448Semax mouse_butt |= (1 << 5); 236221448Semax else 237221448Semax mouse_butt |= (1 << 6); 238221448Semax 239221448Semax mevents ++; 240221448Semax val = 0; 241221448Semax break; 242221448Semax 243137868Semax case 0xb5: /* Scan Next Track */ 244137868Semax val = 0x19; 245137868Semax break; 246137868Semax 247137868Semax case 0xb6: /* Scan Previous Track */ 248137868Semax val = 0x10; 249137868Semax break; 250137868Semax 251137868Semax case 0xb7: /* Stop */ 252137868Semax val = 0x24; 253137868Semax break; 254137868Semax 255137868Semax case 0xcd: /* Play/Pause */ 256137868Semax val = 0x22; 257137868Semax break; 258137868Semax 259137868Semax case 0xe2: /* Mute */ 260137868Semax val = 0x20; 261137868Semax break; 262137868Semax 263137868Semax case 0xe9: /* Volume Up */ 264137868Semax val = 0x30; 265137868Semax break; 266137868Semax 267137868Semax case 0xea: /* Volume Down */ 268137868Semax val = 0x2E; 269137868Semax break; 270137868Semax 271137868Semax case 0x183: /* Media Select */ 272137868Semax val = 0x6D; 273137868Semax break; 274137868Semax 275137868Semax case 0x018a: /* Mail */ 276137868Semax val = 0x6C; 277137868Semax break; 278137868Semax 279137868Semax case 0x192: /* Calculator */ 280137868Semax val = 0x21; 281137868Semax break; 282137868Semax 283137868Semax case 0x194: /* My Computer */ 284137868Semax val = 0x6B; 285137868Semax break; 286137868Semax 287137868Semax case 0x221: /* WWW Search */ 288137868Semax val = 0x65; 289137868Semax break; 290137868Semax 291137868Semax case 0x223: /* WWW Home */ 292137868Semax val = 0x32; 293137868Semax break; 294137868Semax 295137868Semax case 0x224: /* WWW Back */ 296137868Semax val = 0x6A; 297137868Semax break; 298137868Semax 299137868Semax case 0x225: /* WWW Forward */ 300137868Semax val = 0x69; 301137868Semax break; 302137868Semax 303137868Semax case 0x226: /* WWW Stop */ 304137868Semax val = 0x68; 305137868Semax break; 306137868Semax 307163185Smarkus case 0x227: /* WWW Refresh */ 308137868Semax val = 0x67; 309137868Semax break; 310137868Semax 311137868Semax case 0x22a: /* WWW Favorites */ 312137868Semax val = 0x66; 313137868Semax break; 314137868Semax 315137868Semax default: 316137868Semax val = 0; 317137868Semax break; 318137868Semax } 319137868Semax 320137868Semax /* XXX FIXME - UGLY HACK */ 321137868Semax if (val != 0) { 322162128Semax if (hid_device->keyboard) { 323162128Semax int32_t buf[4] = { 0xe0, val, 324162128Semax 0xe0, val|0x80 }; 325137868Semax 326162128Semax assert(s->vkbd != -1); 327162128Semax write(s->vkbd, buf, sizeof(buf)); 328162128Semax } else 329162128Semax syslog(LOG_ERR, "Keyboard events " \ 330162128Semax "received from non-keyboard " \ 331162128Semax "device %s. Please report", 332162128Semax bt_ntoa(&s->bdaddr, NULL)); 333137868Semax } 334137868Semax break; 335137868Semax 336128080Semax case HUP_MICROSOFT: 337128080Semax switch (usage) { 338128080Semax case 0xfe01: 339128080Semax if (!hid_device->battery_power) 340128080Semax break; 341128080Semax 342128080Semax switch (val) { 343128080Semax case 1: 344128080Semax syslog(LOG_INFO, "Battery is OK on %s", 345128080Semax bt_ntoa(&s->bdaddr, NULL)); 346128080Semax break; 347128080Semax 348128080Semax case 2: 349128080Semax syslog(LOG_NOTICE, "Low battery on %s", 350128080Semax bt_ntoa(&s->bdaddr, NULL)); 351128080Semax break; 352128080Semax 353128080Semax case 3: 354128080Semax syslog(LOG_WARNING, "Very low battery "\ 355128080Semax "on %s", 356128080Semax bt_ntoa(&s->bdaddr, NULL)); 357128080Semax break; 358128080Semax } 359128080Semax break; 360128080Semax } 361128080Semax break; 362128080Semax } 363128080Semax } 364128080Semax hid_end_parse(d); 365128080Semax 366162128Semax /* 367162128Semax * XXX FIXME Feed keyboard events into kernel. 368162128Semax * The code below works, bit host also needs to track 369162128Semax * and handle repeat. 370162128Semax * 371162128Semax * Key repeat currently works in X, but not in console. 372162128Semax */ 373137868Semax 374162128Semax if (kevents > 0) { 375162128Semax if (hid_device->keyboard) { 376162128Semax assert(s->vkbd != -1); 377162128Semax kbd_process_keys(s); 378162128Semax } else 379162128Semax syslog(LOG_ERR, "Keyboard events received from " \ 380162128Semax "non-keyboard device %s. Please report", 381162128Semax bt_ntoa(&s->bdaddr, NULL)); 382162128Semax } 383162128Semax 384128080Semax /* 385137868Semax * XXX FIXME Feed mouse events into kernel. 386137868Semax * The code block below works, but it is not good enough. 387137868Semax * Need to track double-clicks etc. 388162128Semax * 389162128Semax * Double click currently works in X, but not in console. 390128080Semax */ 391128080Semax 392137868Semax if (mevents > 0) { 393128080Semax struct mouse_info mi; 394128080Semax 395128080Semax mi.operation = MOUSE_ACTION; 396128080Semax mi.u.data.x = mouse_x; 397128080Semax mi.u.data.y = mouse_y; 398128080Semax mi.u.data.z = mouse_z; 399128080Semax mi.u.data.buttons = mouse_butt; 400128080Semax 401128080Semax if (ioctl(s->srv->cons, CONS_MOUSECTL, &mi) < 0) 402128080Semax syslog(LOG_ERR, "Could not process mouse events from " \ 403128080Semax "%s. %s (%d)", bt_ntoa(&s->bdaddr, NULL), 404128080Semax strerror(errno), errno); 405128080Semax } 406128080Semax 407128080Semax return (0); 408128080Semax} 409