hid.c revision 137868
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 * 28137868Semax * $Id: hid.c,v 1.4 2004/11/17 21:59:42 max Exp $ 29128080Semax * $FreeBSD: head/usr.sbin/bluetooth/bthidd/hid.c 137868 2004-11-18 18:05:15Z 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> 43137868Semax#include <unistd.h> 44128080Semax#include <usbhid.h> 45128080Semax#include "bthidd.h" 46128080Semax#include "bthid_config.h" 47137868Semax#include "kbd.h" 48128080Semax 49128080Semax#undef min 50128080Semax#define min(x, y) (((x) < (y))? (x) : (y)) 51128080Semax 52137868Semax#undef ASIZE 53137868Semax#define ASIZE(a) (sizeof(a)/sizeof(a[0])) 54137868Semax 55128080Semax/* 56128080Semax * Process data from control channel 57128080Semax */ 58128080Semax 59128080Semaxint 60128080Semaxhid_control(bthid_session_p s, char *data, int len) 61128080Semax{ 62128080Semax assert(s != NULL); 63128080Semax assert(data != NULL); 64128080Semax assert(len > 0); 65128080Semax 66128080Semax switch (data[0] >> 4) { 67128080Semax case 0: /* Handshake (response to command) */ 68128080Semax if (data[0] & 0xf) 69128080Semax syslog(LOG_ERR, "Got handshake message with error " \ 70128080Semax "response 0x%x from %s", 71128080Semax data[0], bt_ntoa(&s->bdaddr, NULL)); 72128080Semax break; 73128080Semax 74128080Semax case 1: /* HID Control */ 75128080Semax switch (data[0] & 0xf) { 76128080Semax case 0: /* NOP */ 77128080Semax break; 78128080Semax 79128080Semax case 1: /* Hard reset */ 80128080Semax case 2: /* Soft reset */ 81128080Semax syslog(LOG_WARNING, "Device %s requested %s reset", 82128080Semax bt_ntoa(&s->bdaddr, NULL), 83128080Semax ((data[0] & 0xf) == 1)? "hard" : "soft"); 84128080Semax break; 85128080Semax 86128080Semax case 3: /* Suspend */ 87128080Semax syslog(LOG_NOTICE, "Device %s requested Suspend", 88128080Semax bt_ntoa(&s->bdaddr, NULL)); 89128080Semax break; 90128080Semax 91128080Semax case 4: /* Exit suspend */ 92128080Semax syslog(LOG_NOTICE, "Device %s requested Exit Suspend", 93128080Semax bt_ntoa(&s->bdaddr, NULL)); 94128080Semax break; 95128080Semax 96128080Semax case 5: /* Virtual cable unplug */ 97128080Semax syslog(LOG_NOTICE, "Device %s unplugged virtual cable", 98128080Semax bt_ntoa(&s->bdaddr, NULL)); 99128080Semax session_close(s); 100128080Semax break; 101128080Semax 102128080Semax default: 103128080Semax syslog(LOG_WARNING, "Device %s sent unknown " \ 104128080Semax "HID_Control message 0x%x", 105128080Semax bt_ntoa(&s->bdaddr, NULL), data[0]); 106128080Semax break; 107128080Semax } 108128080Semax break; 109128080Semax 110128080Semax default: 111128080Semax syslog(LOG_WARNING, "Got unexpected message 0x%x on Control " \ 112128080Semax "channel from %s", data[0], bt_ntoa(&s->bdaddr, NULL)); 113128080Semax break; 114128080Semax } 115128080Semax 116128080Semax return (0); 117128080Semax} 118128080Semax 119128080Semax/* 120128080Semax * Process data from the interrupt channel 121128080Semax */ 122128080Semax 123128080Semaxint 124128080Semaxhid_interrupt(bthid_session_p s, char *data, int len) 125128080Semax{ 126128080Semax hid_device_p hid_device = NULL; 127128080Semax hid_data_t d; 128128080Semax hid_item_t h; 129128080Semax int report_id, usage, page, val, 130128080Semax mouse_x, mouse_y, mouse_z, mouse_butt, 131137868Semax mevents, kevents; 132128080Semax 133128080Semax assert(s != NULL); 134137868Semax assert(s->srv != NULL); 135128080Semax assert(data != NULL); 136128080Semax 137128080Semax if (len < 3) { 138128080Semax syslog(LOG_ERR, "Got short message (%d bytes) on Interrupt " \ 139128080Semax "channel from %s", len, bt_ntoa(&s->bdaddr, NULL)); 140128080Semax return (-1); 141128080Semax } 142128080Semax 143128080Semax if ((unsigned char) data[0] != 0xa1) { 144128080Semax syslog(LOG_ERR, "Got unexpected message 0x%x on " \ 145128080Semax "Interrupt channel from %s", 146128080Semax data[0], bt_ntoa(&s->bdaddr, NULL)); 147128080Semax return (-1); 148128080Semax } 149128080Semax 150128080Semax report_id = data[1]; 151128080Semax data += 2; 152128080Semax len -= 2; 153128080Semax 154128080Semax hid_device = get_hid_device(&s->bdaddr); 155128080Semax assert(hid_device != NULL); 156128080Semax 157137868Semax mouse_x = mouse_y = mouse_z = mouse_butt = mevents = kevents = 0; 158128080Semax 159128080Semax for (d = hid_start_parse(hid_device->desc, 1 << hid_input, -1); 160128080Semax hid_get_item(d, &h) > 0; ) { 161128080Semax if ((h.flags & HIO_CONST) || (h.report_ID != report_id)) 162128080Semax continue; 163128080Semax 164128080Semax page = HID_PAGE(h.usage); 165128080Semax usage = HID_USAGE(h.usage); 166128080Semax val = hid_get_data(data, &h); 167128080Semax 168128080Semax switch (page) { 169128080Semax case HUP_GENERIC_DESKTOP: 170128080Semax switch (usage) { 171128080Semax case HUG_X: 172128080Semax mouse_x = val; 173137868Semax mevents ++; 174128080Semax break; 175128080Semax 176128080Semax case HUG_Y: 177128080Semax mouse_y = val; 178137868Semax mevents ++; 179128080Semax break; 180128080Semax 181128080Semax case HUG_WHEEL: 182128080Semax mouse_z = -val; 183137868Semax mevents ++; 184128080Semax break; 185128080Semax 186128080Semax case HUG_SYSTEM_SLEEP: 187128080Semax if (val) 188128080Semax syslog(LOG_NOTICE, "Sleep button pressed"); 189128080Semax break; 190128080Semax } 191128080Semax break; 192128080Semax 193128080Semax case HUP_KEYBOARD: 194137868Semax kevents ++; 195137868Semax 196128080Semax if (h.flags & HIO_VARIABLE) { 197137868Semax if (val && usage < kbd_maxkey()) 198137868Semax bit_set(s->srv->keys, usage); 199128080Semax } else { 200137868Semax if (val && val < kbd_maxkey()) 201137868Semax bit_set(s->srv->keys, val); 202137868Semax 203128080Semax data ++; 204128080Semax len --; 205128080Semax 206128080Semax len = min(len, h.report_size); 207128080Semax while (len > 0) { 208128080Semax val = hid_get_data(data, &h); 209137868Semax if (val && val < kbd_maxkey()) 210137868Semax bit_set(s->srv->keys, val); 211137868Semax 212128080Semax data ++; 213128080Semax len --; 214128080Semax } 215128080Semax } 216128080Semax break; 217128080Semax 218128080Semax case HUP_BUTTON: 219128080Semax mouse_butt |= (val << (usage - 1)); 220137868Semax mevents ++; 221128080Semax break; 222128080Semax 223137868Semax case HUP_CONSUMER: 224137868Semax if (!val) 225137868Semax break; 226137868Semax 227137868Semax switch (usage) { 228137868Semax case 0xb5: /* Scan Next Track */ 229137868Semax val = 0x19; 230137868Semax break; 231137868Semax 232137868Semax case 0xb6: /* Scan Previous Track */ 233137868Semax val = 0x10; 234137868Semax break; 235137868Semax 236137868Semax case 0xb7: /* Stop */ 237137868Semax val = 0x24; 238137868Semax break; 239137868Semax 240137868Semax case 0xcd: /* Play/Pause */ 241137868Semax val = 0x22; 242137868Semax break; 243137868Semax 244137868Semax case 0xe2: /* Mute */ 245137868Semax val = 0x20; 246137868Semax break; 247137868Semax 248137868Semax case 0xe9: /* Volume Up */ 249137868Semax val = 0x30; 250137868Semax break; 251137868Semax 252137868Semax case 0xea: /* Volume Down */ 253137868Semax val = 0x2E; 254137868Semax break; 255137868Semax 256137868Semax case 0x183: /* Media Select */ 257137868Semax val = 0x6D; 258137868Semax break; 259137868Semax 260137868Semax case 0x018a: /* Mail */ 261137868Semax val = 0x6C; 262137868Semax break; 263137868Semax 264137868Semax case 0x192: /* Calculator */ 265137868Semax val = 0x21; 266137868Semax break; 267137868Semax 268137868Semax case 0x194: /* My Computer */ 269137868Semax val = 0x6B; 270137868Semax break; 271137868Semax 272137868Semax case 0x221: /* WWW Search */ 273137868Semax val = 0x65; 274137868Semax break; 275137868Semax 276137868Semax case 0x223: /* WWW Home */ 277137868Semax val = 0x32; 278137868Semax break; 279137868Semax 280137868Semax case 0x224: /* WWW Back */ 281137868Semax val = 0x6A; 282137868Semax break; 283137868Semax 284137868Semax case 0x225: /* WWW Forward */ 285137868Semax val = 0x69; 286137868Semax break; 287137868Semax 288137868Semax case 0x226: /* WWW Stop */ 289137868Semax val = 0x68; 290137868Semax break; 291137868Semax 292137868Semax case 0227: /* WWW Refresh */ 293137868Semax val = 0x67; 294137868Semax break; 295137868Semax 296137868Semax case 0x22a: /* WWW Favorites */ 297137868Semax val = 0x66; 298137868Semax break; 299137868Semax 300137868Semax default: 301137868Semax val = 0; 302137868Semax break; 303137868Semax } 304137868Semax 305137868Semax /* XXX FIXME - UGLY HACK */ 306137868Semax if (val != 0) { 307137868Semax int buf[4] = { 0xe0, val, 0xe0, val|0x80 }; 308137868Semax 309137868Semax write(s->srv->vkbd, buf, sizeof(buf)); 310137868Semax } 311137868Semax break; 312137868Semax 313128080Semax case HUP_MICROSOFT: 314128080Semax switch (usage) { 315128080Semax case 0xfe01: 316128080Semax if (!hid_device->battery_power) 317128080Semax break; 318128080Semax 319128080Semax switch (val) { 320128080Semax case 1: 321128080Semax syslog(LOG_INFO, "Battery is OK on %s", 322128080Semax bt_ntoa(&s->bdaddr, NULL)); 323128080Semax break; 324128080Semax 325128080Semax case 2: 326128080Semax syslog(LOG_NOTICE, "Low battery on %s", 327128080Semax bt_ntoa(&s->bdaddr, NULL)); 328128080Semax break; 329128080Semax 330128080Semax case 3: 331128080Semax syslog(LOG_WARNING, "Very low battery "\ 332128080Semax "on %s", 333128080Semax bt_ntoa(&s->bdaddr, NULL)); 334128080Semax break; 335128080Semax } 336128080Semax break; 337128080Semax } 338128080Semax break; 339128080Semax } 340128080Semax } 341128080Semax hid_end_parse(d); 342128080Semax 343137868Semax /* Feed keyboard events into kernel */ 344137868Semax if (kevents > 0) 345137868Semax kbd_process_keys(s); 346137868Semax 347128080Semax /* 348137868Semax * XXX FIXME Feed mouse events into kernel. 349137868Semax * The code block below works, but it is not good enough. 350137868Semax * Need to track double-clicks etc. 351128080Semax */ 352128080Semax 353137868Semax if (mevents > 0) { 354128080Semax struct mouse_info mi; 355128080Semax 356128080Semax mi.operation = MOUSE_ACTION; 357128080Semax mi.u.data.x = mouse_x; 358128080Semax mi.u.data.y = mouse_y; 359128080Semax mi.u.data.z = mouse_z; 360128080Semax mi.u.data.buttons = mouse_butt; 361128080Semax 362128080Semax if (ioctl(s->srv->cons, CONS_MOUSECTL, &mi) < 0) 363128080Semax syslog(LOG_ERR, "Could not process mouse events from " \ 364128080Semax "%s. %s (%d)", bt_ntoa(&s->bdaddr, NULL), 365128080Semax strerror(errno), errno); 366128080Semax } 367128080Semax 368128080Semax return (0); 369128080Semax} 370128080Semax 371