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: releng/10.2/usr.sbin/bluetooth/bthidd/hid.c 281567 2015-04-15 22:07:51Z rakuco $ 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/* 52128080Semax * Process data from control channel 53128080Semax */ 54128080Semax 55162128Semaxint32_t 56162128Semaxhid_control(bthid_session_p s, uint8_t *data, int32_t len) 57128080Semax{ 58128080Semax assert(s != NULL); 59128080Semax assert(data != NULL); 60128080Semax assert(len > 0); 61128080Semax 62128080Semax switch (data[0] >> 4) { 63128080Semax case 0: /* Handshake (response to command) */ 64128080Semax if (data[0] & 0xf) 65128080Semax syslog(LOG_ERR, "Got handshake message with error " \ 66128080Semax "response 0x%x from %s", 67128080Semax data[0], bt_ntoa(&s->bdaddr, NULL)); 68128080Semax break; 69128080Semax 70128080Semax case 1: /* HID Control */ 71128080Semax switch (data[0] & 0xf) { 72128080Semax case 0: /* NOP */ 73128080Semax break; 74128080Semax 75128080Semax case 1: /* Hard reset */ 76128080Semax case 2: /* Soft reset */ 77128080Semax syslog(LOG_WARNING, "Device %s requested %s reset", 78128080Semax bt_ntoa(&s->bdaddr, NULL), 79128080Semax ((data[0] & 0xf) == 1)? "hard" : "soft"); 80128080Semax break; 81128080Semax 82128080Semax case 3: /* Suspend */ 83128080Semax syslog(LOG_NOTICE, "Device %s requested Suspend", 84128080Semax bt_ntoa(&s->bdaddr, NULL)); 85128080Semax break; 86128080Semax 87128080Semax case 4: /* Exit suspend */ 88128080Semax syslog(LOG_NOTICE, "Device %s requested Exit Suspend", 89128080Semax bt_ntoa(&s->bdaddr, NULL)); 90128080Semax break; 91128080Semax 92128080Semax case 5: /* Virtual cable unplug */ 93128080Semax syslog(LOG_NOTICE, "Device %s unplugged virtual cable", 94128080Semax bt_ntoa(&s->bdaddr, NULL)); 95128080Semax session_close(s); 96128080Semax break; 97128080Semax 98128080Semax default: 99128080Semax syslog(LOG_WARNING, "Device %s sent unknown " \ 100128080Semax "HID_Control message 0x%x", 101128080Semax bt_ntoa(&s->bdaddr, NULL), data[0]); 102128080Semax break; 103128080Semax } 104128080Semax break; 105128080Semax 106128080Semax default: 107128080Semax syslog(LOG_WARNING, "Got unexpected message 0x%x on Control " \ 108128080Semax "channel from %s", data[0], bt_ntoa(&s->bdaddr, NULL)); 109128080Semax break; 110128080Semax } 111128080Semax 112128080Semax return (0); 113128080Semax} 114128080Semax 115128080Semax/* 116128080Semax * Process data from the interrupt channel 117128080Semax */ 118128080Semax 119162128Semaxint32_t 120162128Semaxhid_interrupt(bthid_session_p s, uint8_t *data, int32_t len) 121128080Semax{ 122162128Semax hid_device_p hid_device; 123128080Semax hid_data_t d; 124128080Semax hid_item_t h; 125162128Semax int32_t report_id, usage, page, val, 126128080Semax mouse_x, mouse_y, mouse_z, mouse_butt, 127207812Skaiw mevents, kevents, i; 128128080Semax 129128080Semax assert(s != NULL); 130137868Semax assert(s->srv != NULL); 131128080Semax assert(data != NULL); 132128080Semax 133128080Semax if (len < 3) { 134128080Semax syslog(LOG_ERR, "Got short message (%d bytes) on Interrupt " \ 135128080Semax "channel from %s", len, bt_ntoa(&s->bdaddr, NULL)); 136128080Semax return (-1); 137128080Semax } 138128080Semax 139162128Semax if (data[0] != 0xa1) { 140128080Semax syslog(LOG_ERR, "Got unexpected message 0x%x on " \ 141128080Semax "Interrupt channel from %s", 142128080Semax data[0], bt_ntoa(&s->bdaddr, NULL)); 143128080Semax return (-1); 144128080Semax } 145128080Semax 146128080Semax report_id = data[1]; 147207812Skaiw data ++; 148207812Skaiw len --; 149128080Semax 150128080Semax hid_device = get_hid_device(&s->bdaddr); 151128080Semax assert(hid_device != NULL); 152128080Semax 153137868Semax mouse_x = mouse_y = mouse_z = mouse_butt = mevents = kevents = 0; 154128080Semax 155128080Semax for (d = hid_start_parse(hid_device->desc, 1 << hid_input, -1); 156128080Semax hid_get_item(d, &h) > 0; ) { 157212335Semax if ((h.flags & HIO_CONST) || (h.report_ID != report_id) || 158212335Semax (h.kind != hid_input)) 159128080Semax continue; 160128080Semax 161128080Semax page = HID_PAGE(h.usage); 162128080Semax val = hid_get_data(data, &h); 163128080Semax 164281566Srakuco /* 165281566Srakuco * When the input field is an array and the usage is specified 166281566Srakuco * with a range instead of an ID, we have to derive the actual 167281566Srakuco * usage by using the item value as an index in the usage range 168281566Srakuco * list. 169281566Srakuco */ 170281566Srakuco if ((h.flags & HIO_VARIABLE)) { 171281566Srakuco usage = HID_USAGE(h.usage); 172281566Srakuco } else { 173281566Srakuco const uint32_t usage_offset = val - h.logical_minimum; 174281566Srakuco usage = HID_USAGE(h.usage_minimum + usage_offset); 175281566Srakuco } 176281566Srakuco 177128080Semax switch (page) { 178128080Semax case HUP_GENERIC_DESKTOP: 179128080Semax switch (usage) { 180128080Semax case HUG_X: 181128080Semax mouse_x = val; 182137868Semax mevents ++; 183128080Semax break; 184128080Semax 185128080Semax case HUG_Y: 186128080Semax mouse_y = val; 187137868Semax mevents ++; 188128080Semax break; 189128080Semax 190128080Semax case HUG_WHEEL: 191128080Semax mouse_z = -val; 192137868Semax mevents ++; 193128080Semax break; 194128080Semax 195128080Semax case HUG_SYSTEM_SLEEP: 196128080Semax if (val) 197128080Semax syslog(LOG_NOTICE, "Sleep button pressed"); 198128080Semax break; 199128080Semax } 200128080Semax break; 201128080Semax 202128080Semax case HUP_KEYBOARD: 203137868Semax kevents ++; 204137868Semax 205128080Semax if (h.flags & HIO_VARIABLE) { 206137868Semax if (val && usage < kbd_maxkey()) 207162128Semax bit_set(s->keys1, usage); 208128080Semax } else { 209137868Semax if (val && val < kbd_maxkey()) 210162128Semax bit_set(s->keys1, val); 211137868Semax 212207812Skaiw for (i = 1; i < h.report_count; i++) { 213207812Skaiw h.pos += h.report_size; 214128080Semax val = hid_get_data(data, &h); 215137868Semax if (val && val < kbd_maxkey()) 216162128Semax bit_set(s->keys1, val); 217128080Semax } 218128080Semax } 219128080Semax break; 220128080Semax 221128080Semax case HUP_BUTTON: 222162128Semax if (usage != 0) { 223162128Semax if (usage == 2) 224162128Semax usage = 3; 225162128Semax else if (usage == 3) 226162128Semax usage = 2; 227162128Semax 228162128Semax mouse_butt |= (val << (usage - 1)); 229162128Semax mevents ++; 230162128Semax } 231128080Semax break; 232128080Semax 233137868Semax case HUP_CONSUMER: 234137868Semax if (!val) 235137868Semax break; 236137868Semax 237137868Semax switch (usage) { 238221448Semax case HUC_AC_PAN: 239221448Semax /* Horizontal scroll */ 240221448Semax if (val < 0) 241221448Semax mouse_butt |= (1 << 5); 242221448Semax else 243221448Semax mouse_butt |= (1 << 6); 244221448Semax 245221448Semax mevents ++; 246221448Semax val = 0; 247221448Semax break; 248221448Semax 249137868Semax case 0xb5: /* Scan Next Track */ 250137868Semax val = 0x19; 251137868Semax break; 252137868Semax 253137868Semax case 0xb6: /* Scan Previous Track */ 254137868Semax val = 0x10; 255137868Semax break; 256137868Semax 257137868Semax case 0xb7: /* Stop */ 258137868Semax val = 0x24; 259137868Semax break; 260137868Semax 261137868Semax case 0xcd: /* Play/Pause */ 262137868Semax val = 0x22; 263137868Semax break; 264137868Semax 265137868Semax case 0xe2: /* Mute */ 266137868Semax val = 0x20; 267137868Semax break; 268137868Semax 269137868Semax case 0xe9: /* Volume Up */ 270137868Semax val = 0x30; 271137868Semax break; 272137868Semax 273137868Semax case 0xea: /* Volume Down */ 274137868Semax val = 0x2E; 275137868Semax break; 276137868Semax 277137868Semax case 0x183: /* Media Select */ 278137868Semax val = 0x6D; 279137868Semax break; 280137868Semax 281137868Semax case 0x018a: /* Mail */ 282137868Semax val = 0x6C; 283137868Semax break; 284137868Semax 285137868Semax case 0x192: /* Calculator */ 286137868Semax val = 0x21; 287137868Semax break; 288137868Semax 289137868Semax case 0x194: /* My Computer */ 290137868Semax val = 0x6B; 291137868Semax break; 292137868Semax 293137868Semax case 0x221: /* WWW Search */ 294137868Semax val = 0x65; 295137868Semax break; 296137868Semax 297137868Semax case 0x223: /* WWW Home */ 298137868Semax val = 0x32; 299137868Semax break; 300137868Semax 301137868Semax case 0x224: /* WWW Back */ 302137868Semax val = 0x6A; 303137868Semax break; 304137868Semax 305137868Semax case 0x225: /* WWW Forward */ 306137868Semax val = 0x69; 307137868Semax break; 308137868Semax 309137868Semax case 0x226: /* WWW Stop */ 310137868Semax val = 0x68; 311137868Semax break; 312137868Semax 313163185Smarkus case 0x227: /* WWW Refresh */ 314137868Semax val = 0x67; 315137868Semax break; 316137868Semax 317137868Semax case 0x22a: /* WWW Favorites */ 318137868Semax val = 0x66; 319137868Semax break; 320137868Semax 321137868Semax default: 322137868Semax val = 0; 323137868Semax break; 324137868Semax } 325137868Semax 326137868Semax /* XXX FIXME - UGLY HACK */ 327137868Semax if (val != 0) { 328162128Semax if (hid_device->keyboard) { 329162128Semax int32_t buf[4] = { 0xe0, val, 330162128Semax 0xe0, val|0x80 }; 331137868Semax 332162128Semax assert(s->vkbd != -1); 333162128Semax write(s->vkbd, buf, sizeof(buf)); 334162128Semax } else 335162128Semax syslog(LOG_ERR, "Keyboard events " \ 336162128Semax "received from non-keyboard " \ 337162128Semax "device %s. Please report", 338162128Semax bt_ntoa(&s->bdaddr, NULL)); 339137868Semax } 340137868Semax break; 341137868Semax 342128080Semax case HUP_MICROSOFT: 343128080Semax switch (usage) { 344128080Semax case 0xfe01: 345128080Semax if (!hid_device->battery_power) 346128080Semax break; 347128080Semax 348128080Semax switch (val) { 349128080Semax case 1: 350128080Semax syslog(LOG_INFO, "Battery is OK on %s", 351128080Semax bt_ntoa(&s->bdaddr, NULL)); 352128080Semax break; 353128080Semax 354128080Semax case 2: 355128080Semax syslog(LOG_NOTICE, "Low battery on %s", 356128080Semax bt_ntoa(&s->bdaddr, NULL)); 357128080Semax break; 358128080Semax 359128080Semax case 3: 360128080Semax syslog(LOG_WARNING, "Very low battery "\ 361128080Semax "on %s", 362128080Semax bt_ntoa(&s->bdaddr, NULL)); 363128080Semax break; 364128080Semax } 365128080Semax break; 366128080Semax } 367128080Semax break; 368128080Semax } 369128080Semax } 370128080Semax hid_end_parse(d); 371128080Semax 372162128Semax /* 373162128Semax * XXX FIXME Feed keyboard events into kernel. 374162128Semax * The code below works, bit host also needs to track 375162128Semax * and handle repeat. 376162128Semax * 377162128Semax * Key repeat currently works in X, but not in console. 378162128Semax */ 379137868Semax 380162128Semax if (kevents > 0) { 381162128Semax if (hid_device->keyboard) { 382162128Semax assert(s->vkbd != -1); 383162128Semax kbd_process_keys(s); 384162128Semax } else 385162128Semax syslog(LOG_ERR, "Keyboard events received from " \ 386162128Semax "non-keyboard device %s. Please report", 387162128Semax bt_ntoa(&s->bdaddr, NULL)); 388162128Semax } 389162128Semax 390128080Semax /* 391137868Semax * XXX FIXME Feed mouse events into kernel. 392137868Semax * The code block below works, but it is not good enough. 393137868Semax * Need to track double-clicks etc. 394162128Semax * 395162128Semax * Double click currently works in X, but not in console. 396128080Semax */ 397128080Semax 398137868Semax if (mevents > 0) { 399128080Semax struct mouse_info mi; 400128080Semax 401128080Semax mi.operation = MOUSE_ACTION; 402128080Semax mi.u.data.x = mouse_x; 403128080Semax mi.u.data.y = mouse_y; 404128080Semax mi.u.data.z = mouse_z; 405128080Semax mi.u.data.buttons = mouse_butt; 406128080Semax 407128080Semax if (ioctl(s->srv->cons, CONS_MOUSECTL, &mi) < 0) 408128080Semax syslog(LOG_ERR, "Could not process mouse events from " \ 409128080Semax "%s. %s (%d)", bt_ntoa(&s->bdaddr, NULL), 410128080Semax strerror(errno), errno); 411128080Semax } 412128080Semax 413128080Semax return (0); 414128080Semax} 415