--- /dev/null
+/* $OpenBSD: hidcc.c,v 1.1 2022/11/11 06:46:48 anton Exp $ */
+
+/*
+ * Copyright (c) 2022 Anton Lindqvist <anton@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+
+#include <dev/hid/hidccvar.h>
+#include <dev/hid/hid.h>
+
+#include <dev/wscons/wsconsio.h>
+#include <dev/wscons/wskbdvar.h>
+#include <dev/wscons/wsksymdef.h>
+#include <dev/wscons/wsksymvar.h>
+
+#define DEVNAME(sc) ((sc)->sc_dev->dv_xname)
+
+/* #define HIDCC_DEBUG */
+#ifdef HIDCC_DEBUG
+#define DPRINTF(x...) do { if (hidcc_debug) printf(x); } while (0)
+struct hidcc;
+void hidcc_dump(struct hidcc *, const char *, uint8_t *, u_int);
+int hidcc_debug = 1;
+#else
+#define DPRINTF(x...)
+#define hidcc_dump(sc, prefix, data, len)
+#endif
+
+struct hidcc {
+ struct device *sc_dev;
+ struct device *sc_wskbddev;
+
+ /* Key mappings used in translating mode. */
+ keysym_t *sc_map;
+ u_int sc_maplen;
+ u_int sc_mapsiz;
+
+ /* Key mappings used in raw mode. */
+ const struct hidcc_keysym **sc_raw;
+ u_int sc_rawsiz;
+
+ u_int sc_nusages;
+ int sc_isarray;
+ int sc_mode;
+
+ /*
+ * Slice of the interrupt buffer which represents a pressed key.
+ * See section 8 (Report Protocol) of the HID specification v1.11.
+ */
+ struct {
+ uint8_t *i_buf;
+ uint32_t i_bufsiz;
+ uint32_t i_off; /* offset in bits */
+ uint32_t i_len; /* length in bits */
+ } sc_input;
+
+ struct {
+ uint32_t v_inc; /* volume increment bit offset */
+ uint32_t v_dec; /* volume decrement bit offset */
+ uint32_t v_off; /* offset in bits */
+ uint32_t v_len; /* length in bits */
+ } sc_volume;
+
+ /* Last pressed key. */
+ union {
+ int sc_last_translate;
+ u_char sc_last_raw;
+ };
+
+ /*
+ * Only the first element is populated whereas the second remains zeroed
+ * out since such trailing sentinel is required by wskbd_load_keymap().
+ */
+ struct wscons_keydesc sc_keydesc[2];
+ struct wskbd_mapdata sc_keymap;
+
+ int (*sc_enable)(void *, int);
+ void *sc_arg;
+};
+
+struct hidcc_keysym {
+#ifdef HIDCC_DEBUG
+ const char *ks_name;
+#endif
+ int32_t ks_usage;
+ keysym_t ks_key;
+ u_char ks_raw;
+};
+
+/*
+ * Mapping of HID Consumer Control usages to key symbols based on the HID Usage
+ * Tables 1.21 specification. The same usages can also be found at
+ * /usr/share/misc/usb_hid_usages.
+ * The raw scan codes are taken from X11, see the media_nav_acpi_common symbols
+ * in dist/xkeyboard-config/symbols/inet.
+ * Then use dist/xkeyboard-config/keycodes/xfree86 to resolve keys to the
+ * corresponding raw scan code.
+ */
+static const struct hidcc_keysym hidcc_keysyms[] = {
+#ifdef HIDCC_DEBUG
+#define Y(usage, name, key, raw) { name, usage, key, raw },
+#else
+#define Y(usage, name, key, raw) { usage, key, raw },
+#endif
+#define N(usage, name, key, raw)
+ /* 0x0000 Unassigned */
+ N(0x0001, "Consumer Control", 0, 0)
+ N(0x0002, "Numeric Key Pad", 0, 0)
+ N(0x0003, "Programmable Buttons", 0, 0)
+ N(0x0004, "Microphone", 0, 0)
+ N(0x0005, "Headphone", 0, 0)
+ N(0x0006, "Graphic Equalizer", 0, 0)
+ /* 0x0007-0x001F Reserved */
+ N(0x0020, "+10", 0, 0)
+ N(0x0021, "+100", 0, 0)
+ N(0x0022, "AM/PM", 0, 0)
+ /* 0x0023-0x002F Reserved */
+ Y(0x0030, "Power", 0, 222 /* I5E = XF86PowerOff */)
+ N(0x0031, "Reset", 0, 0)
+ Y(0x0032, "Sleep", 0, 150 /* I16 = XF86Sleep */)
+ N(0x0033, "Sleep After", 0, 0)
+ N(0x0034, "Sleep Mode", 0, 0)
+ N(0x0035, "Illumination", 0, 0)
+ N(0x0036, "Function Buttons", 0, 0)
+ /* 0x0037-0x003F Reserved */
+ N(0x0040, "Menu", 0, 0)
+ N(0x0041, "Menu Pick", 0, 0)
+ N(0x0042, "Menu Up", 0, 0)
+ N(0x0043, "Menu Down", 0, 0)
+ N(0x0044, "Menu Left", 0, 0)
+ N(0x0045, "Menu Right", 0, 0)
+ N(0x0046, "Menu Escape", 0, 0)
+ N(0x0047, "Menu Value Increase", 0, 0)
+ N(0x0048, "Menu Value Decrease", 0, 0)
+ /* 0x0049-0x005F Reserved */
+ N(0x0060, "Data On Screen", 0, 0)
+ N(0x0061, "Closed Caption", 0, 0)
+ N(0x0062, "Closed Caption Select", 0, 0)
+ N(0x0063, "VCR/TV", 0, 0)
+ N(0x0064, "Broadcast Mode", 0, 0)
+ N(0x0065, "Snapshot", 0, 0)
+ N(0x0066, "Still", 0, 0)
+ N(0x0067, "Picture-in-Picture Toggle", 0, 0)
+ N(0x0068, "Picture-in-Picture Swap", 0, 0)
+ N(0x0069, "Red Menu Button", 0, 0)
+ N(0x006A, "Green Menu Button", 0, 0)
+ N(0x006B, "Blue Menu Button", 0, 0)
+ N(0x006C, "Yellow Menu Button", 0, 0)
+ N(0x006D, "Aspect", 0, 0)
+ N(0x006E, "3D Mode Select", 0, 0)
+ N(0x006F, "Display Brightness Increment", 0, 0)
+ N(0x0070, "Display Brightness Decrement", 0, 0)
+ N(0x0071, "Display Brightness", 0, 0)
+ N(0x0072, "Display Backlight Toggle", 0, 0)
+ N(0x0073, "Display Set Brightness to Minimum", 0, 0)
+ N(0x0074, "Display Set Brightness to Maximum", 0, 0)
+ N(0x0075, "Display Set Auto Brightness", 0, 0)
+ N(0x0076, "Camera Access Enabled", 0, 0)
+ N(0x0077, "Camera Access Disabled", 0, 0)
+ N(0x0078, "Camera Access Toggle", 0, 0)
+ N(0x0079, "Keyboard Brightness Increment", 0, 0)
+ N(0x007A, "Keyboard Brightness Decrement", 0, 0)
+ N(0x007B, "Keyboard Backlight Set Level", 0, 0)
+ N(0x007C, "Keyboard Backlight OOC", 0, 0)
+ N(0x007D, "Keyboard Backlight Set Minimum", 0, 0)
+ N(0x007E, "Keyboard Backlight Set Maximum", 0, 0)
+ N(0x007F, "Keyboard Backlight Auto", 0, 0)
+ N(0x0080, "Selection", 0, 0)
+ N(0x0081, "Assign Selection", 0, 0)
+ N(0x0082, "Mode Step", 0, 0)
+ N(0x0083, "Recall Last", 0, 0)
+ N(0x0084, "Enter Channel", 0, 0)
+ N(0x0085, "Order Movie", 0, 0)
+ N(0x0086, "Channel", 0, 0)
+ N(0x0087, "Media Selection", 0, 0)
+ N(0x0088, "Media Select Computer", 0, 0)
+ N(0x0089, "Media Select TV", 0, 0)
+ N(0x008A, "Media Select WWW", 0, 0)
+ N(0x008B, "Media Select DVD", 0, 0)
+ N(0x008C, "Media Select Telephone", 0, 0)
+ N(0x008D, "Media Select Program Guide", 0, 0)
+ N(0x008E, "Media Select Video Phone", 0, 0)
+ N(0x008F, "Media Select Games", 0, 0)
+ N(0x0090, "Media Select Messages", 0, 0)
+ N(0x0091, "Media Select CD", 0, 0)
+ N(0x0092, "Media Select VCR", 0, 0)
+ N(0x0093, "Media Select Tuner", 0, 0)
+ N(0x0094, "Quit", 0, 0)
+ N(0x0095, "Help", 0, 0)
+ N(0x0096, "Media Select Tape", 0, 0)
+ N(0x0097, "Media Select Cable", 0, 0)
+ N(0x0098, "Media Select Satellite", 0, 0)
+ N(0x0099, "Media Select Security", 0, 0)
+ N(0x009A, "Media Select Home", 0, 0)
+ N(0x009B, "Media Select Call", 0, 0)
+ N(0x009C, "Channel Increment", 0, 0)
+ N(0x009D, "Channel Decrement", 0, 0)
+ N(0x009E, "Media Select SAP", 0, 0)
+ /* 0x009F-0x009F Reserved */
+ N(0x00A0, "VCR Plus", 0, 0)
+ N(0x00A1, "Once", 0, 0)
+ N(0x00A2, "Daily", 0, 0)
+ N(0x00A3, "Weekly", 0, 0)
+ N(0x00A4, "Monthly", 0, 0)
+ /* 0x00A5-0x00AF Reserved */
+ N(0x00B0, "Play", 0, 0)
+ N(0x00B1, "Pause", 0, 0)
+ N(0x00B2, "Record", 0, 0)
+ N(0x00B3, "Fast Forward", 0, 0)
+ N(0x00B4, "Rewind", 0, 0)
+ Y(0x00B5, "Scan Next Track", 0, 153 /* I19 = XF86AudioNext */)
+ Y(0x00B6, "Scan Previous Track", 0, 144 /* I10 = XF86AudioPrev */)
+ Y(0x00B7, "Stop", 0, 164 /* I24 = XF86AudioStop */)
+ Y(0x00B8, "Eject", 0, 170 /* K5A = XF86Eject */)
+ N(0x00B9, "Random Play", 0, 0)
+ N(0x00BA, "Select Disc", 0, 0)
+ N(0x00BB, "Enter Disc", 0, 0)
+ N(0x00BC, "Repeat", 0, 0)
+ N(0x00BD, "Tracking", 0, 0)
+ N(0x00BE, "Track Normal", 0, 0)
+ N(0x00BF, "Slow Tracking", 0, 0)
+ N(0x00C0, "Frame Forward", 0, 0)
+ N(0x00C1, "Frame Back", 0, 0)
+ N(0x00C2, "Mark", 0, 0)
+ N(0x00C3, "Clear Mark", 0, 0)
+ N(0x00C4, "Repeat From Mark", 0, 0)
+ N(0x00C5, "Return To Mark", 0, 0)
+ N(0x00C6, "Search Mark Forward", 0, 0)
+ N(0x00C7, "Search Mark Backwards", 0, 0)
+ N(0x00C8, "Counter Reset", 0, 0)
+ N(0x00C9, "Show Counter", 0, 0)
+ N(0x00CA, "Tracking Increment", 0, 0)
+ N(0x00CB, "Tracking Decrement", 0, 0)
+ N(0x00CC, "Stop/Eject", 0, 0)
+ Y(0x00CD, "Play/Pause", 0, 162 /* I22 = XF86AudioPlay */)
+ N(0x00CE, "Play/Skip", 0, 0)
+ N(0x00CF, "Voice Command", 0, 0)
+ N(0x00D0, "Invoke Capture Interface", 0, 0)
+ N(0x00D1, "Start or Stop Game Recording", 0, 0)
+ N(0x00D2, "Historical Game Capture", 0, 0)
+ N(0x00D3, "Capture Game Screenshot", 0, 0)
+ N(0x00D4, "Show or Hide Recording Indicator", 0, 0)
+ N(0x00D5, "Start or Stop Microphone Capture", 0, 0)
+ N(0x00D6, "Start or Stop Camera Capture", 0, 0)
+ N(0x00D7, "Start or Stop Game Broadcast", 0, 0)
+ /* 0x00D8-0x00DF Reserved */
+ N(0x00E0, "Volume", 0, 0)
+ N(0x00E1, "Balance", 0, 0)
+ Y(0x00E2, "Mute", KS_AudioMute, 160 /* I20 = XF86AudioMute */)
+ N(0x00E3, "Bass", 0, 0)
+ N(0x00E4, "Treble", 0, 0)
+ N(0x00E5, "Bass Boost", 0, 0)
+ N(0x00E6, "Surround Mode", 0, 0)
+ N(0x00E7, "Loudness", 0, 0)
+ N(0x00E8, "MPX", 0, 0)
+ Y(0x00E9, "Volume Increment", KS_AudioRaise, 176 /* I30 = XF86AudioRaiseVolume */)
+ Y(0x00EA, "Volume Decrement", KS_AudioLower, 174 /* I2E = XF86AudioLowerVolume */)
+ /* 0x00EB-0x00EF Reserved */
+ N(0x00F0, "Speed Select", 0, 0)
+ N(0x00F1, "Playback Speed", 0, 0)
+ N(0x00F2, "Standard Play", 0, 0)
+ N(0x00F3, "Long Play", 0, 0)
+ N(0x00F4, "Extended Play", 0, 0)
+ N(0x00F5, "Slow", 0, 0)
+ /* 0x00F6-0x00FF Reserved */
+ N(0x0100, "Fan Enable", 0, 0)
+ N(0x0101, "Fan Speed", 0, 0)
+ N(0x0102, "Light Enable", 0, 0)
+ N(0x0103, "Light Illumination Level", 0, 0)
+ N(0x0104, "Climate Control Enable", 0, 0)
+ N(0x0105, "Room Temperature", 0, 0)
+ N(0x0106, "Security Enable", 0, 0)
+ N(0x0107, "Fire Alarm", 0, 0)
+ N(0x0108, "Police Alarm", 0, 0)
+ N(0x0109, "Proximity", 0, 0)
+ N(0x010A, "Motion", 0, 0)
+ N(0x010B, "Duress Alarm", 0, 0)
+ N(0x010C, "Holdup Alarm", 0, 0)
+ N(0x010D, "Medical Alarm", 0, 0)
+ /* 0x010E-0x014F Reserved */
+ N(0x0150, "Balance Right", 0, 0)
+ N(0x0151, "Balance Left", 0, 0)
+ N(0x0152, "Bass Increment", 0, 0)
+ N(0x0153, "Bass Decrement", 0, 0)
+ N(0x0154, "Treble Increment", 0, 0)
+ N(0x0155, "Treble Decrement", 0, 0)
+ /* 0x0156-0x015F Reserved */
+ N(0x0160, "Speaker System", 0, 0)
+ N(0x0161, "Channel Left", 0, 0)
+ N(0x0162, "Channel Right", 0, 0)
+ N(0x0163, "Channel Center", 0, 0)
+ N(0x0164, "Channel Front", 0, 0)
+ N(0x0165, "Channel Center Front", 0, 0)
+ N(0x0166, "Channel Side", 0, 0)
+ N(0x0167, "Channel Surround", 0, 0)
+ N(0x0168, "Channel Low Frequency Enhancement", 0, 0)
+ N(0x0169, "Channel Top", 0, 0)
+ N(0x016A, "Channel Unknown", 0, 0)
+ /* 0x016B-0x016F Reserved */
+ N(0x0170, "Sub-channel", 0, 0)
+ N(0x0171, "Sub-channel Increment", 0, 0)
+ N(0x0172, "Sub-channel Decrement", 0, 0)
+ N(0x0173, "Alternate Audio Increment", 0, 0)
+ N(0x0174, "Alternate Audio Decrement", 0, 0)
+ /* 0x0175-0x017F Reserved */
+ N(0x0180, "Application Launch Buttons", 0, 0)
+ N(0x0181, "AL Launch Button Configuration Tool", 0, 0)
+ N(0x0182, "AL Programmable Button Configuration", 0, 0)
+ N(0x0183, "AL Consumer Control Configuration", 0, 0)
+ N(0x0184, "AL Word Processor", 0, 0)
+ N(0x0185, "AL Text Editor", 0, 0)
+ N(0x0186, "AL Spreadsheet", 0, 0)
+ N(0x0187, "AL Graphics Editor", 0, 0)
+ N(0x0188, "AL Presentation App", 0, 0)
+ N(0x0189, "AL Database App", 0, 0)
+ Y(0x018A, "AL Email Reader", 0, 235 /* I6C = XF86Mail */)
+ N(0x018B, "AL Newsreader", 0, 0)
+ N(0x018C, "AL Voicemail", 0, 0)
+ N(0x018D, "AL Contacts/Address Book", 0, 0)
+ N(0x018E, "AL Calendar/Schedule", 0, 0)
+ N(0x018F, "AL Task/Project Manager", 0, 0)
+ N(0x0190, "AL Log/Journal/Timecard", 0, 0)
+ N(0x0191, "AL Checkbook/Finance", 0, 0)
+ Y(0x0192, "AL Calculator", 0, 161 /* I21 = XF86Calculator */)
+ N(0x0193, "AL A/V Capture/Playback", 0, 0)
+ N(0x0194, "AL Local Machine Browser", 0, 0)
+ N(0x0195, "AL LAN/WAN Browser", 0, 0)
+ Y(0x0196, "AL Internet Browser", 0, 178 /* I32 = XF86WWW */)
+ N(0x0197, "AL Remote Networking/ISP Connect", 0, 0)
+ N(0x0198, "AL Network Conference", 0, 0)
+ N(0x0199, "AL Network Chat", 0, 0)
+ N(0x019A, "AL Telephony/Dialer", 0, 0)
+ N(0x019B, "AL Logon", 0, 0)
+ N(0x019C, "AL Logoff", 0, 0)
+ N(0x019D, "AL Logon/Logoff", 0, 0)
+ N(0x019E, "AL Terminal Lock/Screensaver", 0, 0)
+ N(0x019F, "AL Control Panel", 0, 0)
+ N(0x01A0, "AL Command Line Processor/Run", 0, 0)
+ N(0x01A1, "AL Process/Task Manager", 0, 0)
+ N(0x01A2, "AL Select Task/Application", 0, 0)
+ N(0x01A3, "AL Next Task/Application", 0, 0)
+ N(0x01A4, "AL Previous Task/Application", 0, 0)
+ N(0x01A5, "AL Preemptive Halt Task/Application", 0, 0)
+ N(0x01A6, "AL Integrated Help Center", 0, 0)
+ N(0x01A7, "AL My Documents", 0, 0)
+ N(0x01A8, "AL Thesaurus", 0, 0)
+ N(0x01A9, "AL Dictionary", 0, 0)
+ N(0x01AA, "AL Desktop", 0, 0)
+ N(0x01AB, "AC Spell", 0, 0)
+ N(0x01AC, "AL Grammar Check", 0, 0)
+ N(0x01AD, "AL Wireless Status", 0, 0)
+ N(0x01AE, "AL Keyboard Layout", 0, 0)
+ N(0x01AF, "AL Virus Protection", 0, 0)
+ N(0x01B0, "AL Encryption", 0, 0)
+ N(0x01B1, "AL Screen Saver", 0, 0)
+ N(0x01B2, "AL Alarms", 0, 0)
+ N(0x01B3, "AL Clock", 0, 0)
+ N(0x01B4, "AL File Browser", 0, 0)
+ N(0x01B5, "AL Power Status", 0, 0)
+ N(0x01B6, "AL My Pictures", 0, 0)
+ N(0x01B7, "AL My Music", 0, 0)
+ N(0x01B8, "AL Movie Browser", 0, 0)
+ N(0x01B9, "AL Digital Rights Manager", 0, 0)
+ N(0x01BA, "AL Digital Wallet", 0, 0)
+ /* 0x01BB-0x01BB Reserved */
+ N(0x01BC, "AL Instant Messaging", 0, 0)
+ N(0x01BD, "AL OEM Feature/Tips/Tutorial Browser", 0, 0)
+ N(0x01BE, "AL OEM Help", 0, 0)
+ N(0x01BF, "AL Online Community", 0, 0)
+ N(0x01C0, "AL Entertainment Content Browser", 0, 0)
+ N(0x01C1, "AL Online Shopping Browser", 0, 0)
+ N(0x01C2, "AL SmartCard Information/Help", 0, 0)
+ N(0x01C3, "AL Market Monitor/Finance Browser", 0, 0)
+ N(0x01C4, "AL Customized Corporate News Browser", 0, 0)
+ N(0x01C5, "AL Online Activity Browser", 0, 0)
+ Y(0x01C6, "AL Research/Search Browser", 0, 229 /* I65 = XF86Search */)
+ N(0x01C7, "AL Audio Player", 0, 0)
+ N(0x01C8, "AL Message Status", 0, 0)
+ N(0x01C9, "AL Contact Sync", 0, 0)
+ N(0x01CA, "AL Navigation", 0, 0)
+ N(0x01CB, "AL Context-aware Desktop Assistant", 0, 0)
+ /* 0x01CC-0x01FF Reserved */
+ N(0x0200, "Generic GUI Application Controls", 0, 0)
+ N(0x0201, "AC New", 0, 0)
+ N(0x0202, "AC Open", 0, 0)
+ N(0x0203, "AC Close", 0, 0)
+ N(0x0204, "AC Exit", 0, 0)
+ N(0x0205, "AC Maximize", 0, 0)
+ N(0x0206, "AC Minimize", 0, 0)
+ N(0x0207, "AC Save", 0, 0)
+ N(0x0208, "AC Print", 0, 0)
+ N(0x0209, "AC Properties", 0, 0)
+ /* 0x020A-0x0219 Reserved */
+ N(0x021A, "AC Undo", 0, 0)
+ N(0x021B, "AC Copy", 0, 0)
+ N(0x021C, "AC Cut", 0, 0)
+ N(0x021D, "AC Paste", 0, 0)
+ N(0x021E, "AC Select All", 0, 0)
+ N(0x021F, "AC Find", 0, 0)
+ N(0x0220, "AC Find and Replace", 0, 0)
+ N(0x0221, "AC Search", 0, 0)
+ N(0x0222, "AC Go To", 0, 0)
+ N(0x0223, "AC Home", 0, 0)
+ Y(0x0224, "AC Back", 0, 234 /* I6A = XF86Back */)
+ Y(0x0225, "AC Forward", 0, 233 /* I69 = XF86Forward */)
+ Y(0x0226, "AC Stop", 0, 232 /* I68 = XF86Stop */)
+ Y(0x0227, "AC Refresh", 0, 231 /* I67 = XF86Reload */)
+ N(0x0228, "AC Previous Link", 0, 0)
+ N(0x0229, "AC Next Link", 0, 0)
+ N(0x022A, "AC Bookmarks", 0, 0)
+ N(0x022B, "AC History", 0, 0)
+ N(0x022C, "AC Subscriptions", 0, 0)
+ N(0x022D, "AC Zoom In", 0, 0)
+ N(0x022E, "AC Zoom Out", 0, 0)
+ N(0x022F, "AC Zoom", 0, 0)
+ N(0x0230, "AC Full Screen View", 0, 0)
+ N(0x0231, "AC Normal View", 0, 0)
+ N(0x0232, "AC View Toggle", 0, 0)
+ N(0x0233, "AC Scroll Up", 0, 0)
+ N(0x0234, "AC Scroll Down", 0, 0)
+ N(0x0235, "AC Scroll", 0, 0)
+ N(0x0236, "AC Pan Left", 0, 0)
+ N(0x0237, "AC Pan Right", 0, 0)
+ N(0x0238, "AC Pan", 0, 0)
+ N(0x0239, "AC New Window", 0, 0)
+ N(0x023A, "AC Tile Horizontally", 0, 0)
+ N(0x023B, "AC Tile Vertically", 0, 0)
+ N(0x023C, "AC Format", 0, 0)
+ N(0x023D, "AC Edit", 0, 0)
+ N(0x023E, "AC Bold", 0, 0)
+ N(0x023F, "AC Italics", 0, 0)
+ N(0x0240, "AC Underline", 0, 0)
+ N(0x0241, "AC Strikethrough", 0, 0)
+ N(0x0242, "AC Subscript", 0, 0)
+ N(0x0243, "AC Superscript", 0, 0)
+ N(0x0244, "AC All Caps", 0, 0)
+ N(0x0245, "AC Rotate", 0, 0)
+ N(0x0246, "AC Resize", 0, 0)
+ N(0x0247, "AC Flip Horizontal", 0, 0)
+ N(0x0248, "AC Flip Vertical", 0, 0)
+ N(0x0249, "AC Mirror Horizontal", 0, 0)
+ N(0x024A, "AC Mirror Vertical", 0, 0)
+ N(0x024B, "AC Font Select", 0, 0)
+ N(0x024C, "AC Font Color", 0, 0)
+ N(0x024D, "AC Font Size", 0, 0)
+ N(0x024E, "AC Justify Left", 0, 0)
+ N(0x024F, "AC Justify Center H", 0, 0)
+ N(0x0250, "AC Justify Right", 0, 0)
+ N(0x0251, "AC Justify Block H", 0, 0)
+ N(0x0252, "AC Justify Top", 0, 0)
+ N(0x0253, "AC Justify Center V", 0, 0)
+ N(0x0254, "AC Justify Bottom", 0, 0)
+ N(0x0255, "AC Justify Block V", 0, 0)
+ N(0x0256, "AC Justify Decrease", 0, 0)
+ N(0x0257, "AC Justify Increase", 0, 0)
+ N(0x0258, "AC Numbered List", 0, 0)
+ N(0x0259, "AC Restart Numbering", 0, 0)
+ N(0x025A, "AC Bulleted List", 0, 0)
+ N(0x025B, "AC Promote", 0, 0)
+ N(0x025C, "AC Demote", 0, 0)
+ N(0x025D, "AC Yes", 0, 0)
+ N(0x025E, "AC No", 0, 0)
+ N(0x025F, "AC Cancel", 0, 0)
+ N(0x0260, "AC Catalog", 0, 0)
+ N(0x0261, "AC Buy/Checkout", 0, 0)
+ N(0x0262, "AC Add to Cart", 0, 0)
+ N(0x0263, "AC Expand", 0, 0)
+ N(0x0264, "AC Expand All", 0, 0)
+ N(0x0265, "AC Collapse", 0, 0)
+ N(0x0266, "AC Collapse All", 0, 0)
+ N(0x0267, "AC Print Preview", 0, 0)
+ N(0x0268, "AC Paste Special", 0, 0)
+ N(0x0269, "AC Insert Mode", 0, 0)
+ N(0x026A, "AC Delete", 0, 0)
+ N(0x026B, "AC Lock", 0, 0)
+ N(0x026C, "AC Unlock", 0, 0)
+ N(0x026D, "AC Protect", 0, 0)
+ N(0x026E, "AC Unprotect", 0, 0)
+ N(0x026F, "AC Attach Comment", 0, 0)
+ N(0x0270, "AC Delete Comment", 0, 0)
+ N(0x0271, "AC View Comment", 0, 0)
+ N(0x0272, "AC Select Word", 0, 0)
+ N(0x0273, "AC Select Sentence", 0, 0)
+ N(0x0274, "AC Select Paragraph", 0, 0)
+ N(0x0275, "AC Select Column", 0, 0)
+ N(0x0276, "AC Select Row", 0, 0)
+ N(0x0277, "AC Select Table", 0, 0)
+ N(0x0278, "AC Select Object", 0, 0)
+ N(0x0279, "AC Redo/Repeat", 0, 0)
+ N(0x027A, "AC Sort", 0, 0)
+ N(0x027B, "AC Sort Ascending", 0, 0)
+ N(0x027C, "AC Sort Descending", 0, 0)
+ N(0x027D, "AC Filter", 0, 0)
+ N(0x027E, "AC Set Clock", 0, 0)
+ N(0x027F, "AC View Clock", 0, 0)
+ N(0x0280, "AC Select Time Zone", 0, 0)
+ N(0x0281, "AC Edit Time Zones", 0, 0)
+ N(0x0282, "AC Set Alarm", 0, 0)
+ N(0x0283, "AC Clear Alarm", 0, 0)
+ N(0x0284, "AC Snooze Alarm", 0, 0)
+ N(0x0285, "AC Reset Alarm", 0, 0)
+ N(0x0286, "AC Synchronize", 0, 0)
+ N(0x0287, "AC Send/Receive", 0, 0)
+ N(0x0288, "AC Send To", 0, 0)
+ N(0x0289, "AC Reply", 0, 0)
+ N(0x028A, "AC Reply All", 0, 0)
+ N(0x028B, "AC Forward Message", 0, 0)
+ N(0x028C, "AC Send", 0, 0)
+ N(0x028D, "AC Attach File", 0, 0)
+ N(0x028E, "AC Upload", 0, 0)
+ N(0x028F, "AC Download (Save Target As)", 0, 0)
+ N(0x0290, "AC Set Borders", 0, 0)
+ N(0x0291, "AC Insert Row", 0, 0)
+ N(0x0292, "AC Insert Column", 0, 0)
+ N(0x0293, "AC Insert File", 0, 0)
+ N(0x0294, "AC Insert Picture", 0, 0)
+ N(0x0295, "AC Insert Object", 0, 0)
+ N(0x0296, "AC Insert Symbol", 0, 0)
+ N(0x0297, "AC Save and Close", 0, 0)
+ N(0x0298, "AC Rename", 0, 0)
+ N(0x0299, "AC Merge", 0, 0)
+ N(0x029A, "AC Split", 0, 0)
+ N(0x029B, "AC Distribute Horizontally", 0, 0)
+ N(0x029C, "AC Distribute Vertically", 0, 0)
+ N(0x029D, "AC Next Keyboard Layout Select", 0, 0)
+ N(0x029E, "AC Navigation Guidance", 0, 0)
+ N(0x029F, "AC Desktop Show All Windows", 0, 0)
+ N(0x02A0, "AC Soft Key Left", 0, 0)
+ N(0x02A1, "AC Soft Key Right", 0, 0)
+ N(0x02A2, "AC Desktop Show All Applications", 0, 0)
+ /* 0x02A3-0x02AF Reserved */
+ N(0x02B0, "AC Idle Keep Alive", 0, 0)
+ /* 0x02B1-0x02BF Reserved */
+ N(0x02C0, "Extended Keyboard Attributes Collection", 0, 0)
+ N(0x02C1, "Keyboard Form Factor", 0, 0)
+ N(0x02C2, "Keyboard Key Type", 0, 0)
+ N(0x02C3, "Keyboard Physical Layout", 0, 0)
+ N(0x02C4, "Vendor-Specific Keyboard Physical Layout", 0, 0)
+ N(0x02C5, "Keyboard IETF Language Tag Index", 0, 0)
+ N(0x02C6, "Implemented Keyboard Input Assist Controls", 0, 0)
+ N(0x02C7, "Keyboard Input Assist Previous", 0, 0)
+ N(0x02C8, "Keyboard Input Assist Next", 0, 0)
+ N(0x02C9, "Keyboard Input Assist Previous Group", 0, 0)
+ N(0x02CA, "Keyboard Input Assist Next Group", 0, 0)
+ N(0x02CB, "Keyboard Input Assist Accept", 0, 0)
+ N(0x02CC, "Keyboard Input Assist Cancel", 0, 0)
+ /* 0x02CD-0x02CF Reserved */
+ N(0x02D0, "Privacy Screen Toggle", 0, 0)
+ N(0x02D1, "Privacy Screen Level Decrement", 0, 0)
+ N(0x02D2, "Privacy Screen Level Increment", 0, 0)
+ N(0x02D3, "Privacy Screen Level Minimum", 0, 0)
+ N(0x02D4, "Privacy Screen Level Maximum", 0, 0)
+ /* 0x02D5-0x04FF Reserved */
+ N(0x0500, "Contact Edited", 0, 0)
+ N(0x0501, "Contact Added", 0, 0)
+ N(0x0502, "Contact Record Active", 0, 0)
+ N(0x0503, "Contact Index", 0, 0)
+ N(0x0504, "Contact Nickname", 0, 0)
+ N(0x0505, "Contact First Name", 0, 0)
+ N(0x0506, "Contact Last Name", 0, 0)
+ N(0x0507, "Contact Full Name", 0, 0)
+ N(0x0508, "Contact Phone Number Personal", 0, 0)
+ N(0x0509, "Contact Phone Number Business", 0, 0)
+ N(0x050A, "Contact Phone Number Mobile", 0, 0)
+ N(0x050B, "Contact Phone Number Pager", 0, 0)
+ N(0x050C, "Contact Phone Number Fax", 0, 0)
+ N(0x050D, "Contact Phone Number Other", 0, 0)
+ N(0x050E, "Contact Email Personal", 0, 0)
+ N(0x050F, "Contact Email Business", 0, 0)
+ N(0x0510, "Contact Email Other", 0, 0)
+ N(0x0511, "Contact Email Main", 0, 0)
+ N(0x0512, "Contact Speed Dial Number", 0, 0)
+ N(0x0513, "Contact Status Flag", 0, 0)
+ N(0x0514, "Contact Misc.", 0, 0)
+ /* 0x0515-0xFFFF Reserved */
+#undef Y
+#undef N
+};
+
+void hidcc_attach_wskbd(struct hidcc *);
+int hidcc_enable(void *, int);
+void hidcc_set_leds(void *, int);
+int hidcc_ioctl(void *, u_long, caddr_t, int, struct proc *);
+
+int hidcc_parse(struct hidcc *, void *, int, int, int);
+int hidcc_parse_array(struct hidcc *, const struct hid_item *);
+int hidcc_is_array(const struct hid_item *);
+int hidcc_add_key(struct hidcc *, int32_t, u_int);
+int hidcc_add_key_volume(struct hidcc *, const struct hid_item *, uint32_t,
+ u_int);
+int hidcc_bit_to_sym(struct hidcc *, u_int, const struct hidcc_keysym **);
+int hidcc_usage_to_sym(int32_t, const struct hidcc_keysym **);
+int hidcc_bits_to_int(uint8_t *, u_int, int32_t *);
+int hidcc_bits_to_volume(struct hidcc *, uint8_t *, int, u_int *);
+int hidcc_intr_slice(struct hidcc *, uint8_t *, uint8_t *, int *);
+void hidcc_input(struct hidcc *, u_int, int);
+void hidcc_rawinput(struct hidcc *, u_char, int);
+int hidcc_setbits(struct hidcc *, uint8_t *, int, u_int *);
+
+/*
+ * Returns non-zero if the given report ID has at least one Consumer Control
+ * usage.
+ */
+int
+hidcc_match(void *desc, int descsiz, uint8_t repid)
+{
+ struct hid_item hi;
+ struct hid_data *hd;
+ int32_t maxusage = 0;
+
+ hd = hid_start_parse(desc, descsiz, hid_input);
+ while (hid_get_item(hd, &hi)) {
+ if (hi.report_ID == repid &&
+ hi.kind == hid_input &&
+ HID_GET_USAGE_PAGE(hi.usage) == HUP_CONSUMER) {
+ if (HID_GET_USAGE(hi.usage_maximum) > maxusage)
+ maxusage = HID_GET_USAGE(hi.usage_maximum);
+ else if (HID_GET_USAGE(hi.usage) > maxusage)
+ maxusage = HID_GET_USAGE(hi.usage);
+ }
+ }
+ hid_end_parse(hd);
+ return maxusage > 0;
+}
+
+struct hidcc *
+hidcc_attach(const struct hidcc_attach_arg *hca)
+{
+ struct hidcc *sc;
+ int error;
+
+ sc = malloc(sizeof(*sc), M_USBDEV, M_WAITOK | M_ZERO);
+ sc->sc_dev = hca->device;
+ sc->sc_mode = WSKBD_TRANSLATED;
+ sc->sc_last_translate = -1;
+ sc->sc_enable = hca->enable;
+ sc->sc_arg = hca->arg;
+
+ error = hidcc_parse(sc, hca->desc, hca->descsiz, hca->repid,
+ hca->isize);
+ if (error) {
+ printf(": hid error %d\n", error);
+ free(sc, M_USBDEV, sizeof(*sc));
+ return NULL;
+ }
+
+ printf(": %d usage%s, %d key%s, %s\n",
+ sc->sc_nusages, sc->sc_nusages == 1 ? "" : "s",
+ sc->sc_maplen / 2, sc->sc_maplen / 2 == 1 ? "" : "s",
+ sc->sc_isarray ? "array" : "enum");
+
+ /* Cannot load an empty map. */
+ if (sc->sc_maplen > 0)
+ hidcc_attach_wskbd(sc);
+
+ return sc;
+}
+
+int
+hidcc_detach(struct hidcc *sc, int flags)
+{
+ int error = 0;
+
+ if (sc->sc_wskbddev != NULL)
+ error = config_detach(sc->sc_wskbddev, flags);
+ free(sc->sc_input.i_buf, M_USBDEV, sc->sc_input.i_bufsiz);
+ free(sc->sc_map, M_USBDEV, sc->sc_mapsiz * sizeof(*sc->sc_map));
+ free(sc->sc_raw, M_USBDEV, sc->sc_rawsiz * sizeof(*sc->sc_raw));
+ free(sc, M_USBDEV, sizeof(*sc));
+ return error;
+}
+
+void
+hidcc_intr(struct hidcc *sc, void *data, u_int len)
+{
+ const struct hidcc_keysym *ks;
+ uint8_t *buf = sc->sc_input.i_buf;
+ int raw = sc->sc_mode == WSKBD_RAW;
+ int error;
+ u_int bit = 0;
+
+ hidcc_dump(sc, __func__, data, len);
+
+ if (len > sc->sc_input.i_bufsiz) {
+ DPRINTF("%s: too much data: len %d, bufsiz %d\n", DEVNAME(sc),
+ len, sc->sc_input.i_bufsiz);
+ return;
+ }
+
+ error = hidcc_intr_slice(sc, data, buf, &len);
+ if (error) {
+ DPRINTF("%s: slice failure: error %d\n", DEVNAME(sc), error);
+ return;
+ }
+
+ /* Dump the buffer again after slicing. */
+ hidcc_dump(sc, __func__, buf, len);
+
+ if (hidcc_setbits(sc, buf, len, &bit)) {
+ /* All zeroes, assume key up event. */
+ if (raw) {
+ if (sc->sc_last_raw != 0) {
+ hidcc_rawinput(sc, sc->sc_last_raw, 1);
+ sc->sc_last_raw = 0;
+ }
+ } else {
+ if (sc->sc_last_translate != -1) {
+ hidcc_input(sc, sc->sc_last_translate, 1);
+ sc->sc_last_translate = -1;
+ }
+ }
+ return;
+ } else if (sc->sc_isarray) {
+ int32_t usage;
+
+ if (hidcc_bits_to_int(buf, len, &usage) ||
+ hidcc_usage_to_sym(usage, &ks))
+ goto unknown;
+ bit = ks->ks_usage;
+ } else if (raw) {
+ if (hidcc_bit_to_sym(sc, bit, &ks))
+ goto unknown;
+ }
+
+ if (raw) {
+ hidcc_rawinput(sc, ks->ks_raw, 0);
+ sc->sc_last_raw = ks->ks_raw;
+ /*
+ * Feed both raw and translating input for keys that have both
+ * defined. This is only the case for volume related keys.
+ */
+ if (ks->ks_key == 0)
+ return;
+ }
+
+ hidcc_input(sc, bit, 0);
+ if (!raw)
+ sc->sc_last_translate = bit;
+ return;
+
+unknown:
+ DPRINTF("%s: unknown key: bit %d\n", DEVNAME(sc), bit);
+}
+
+void
+hidcc_attach_wskbd(struct hidcc *sc)
+{
+ static const struct wskbd_accessops accessops = {
+ .enable = hidcc_enable,
+ .set_leds = hidcc_set_leds,
+ .ioctl = hidcc_ioctl,
+ };
+ struct wskbddev_attach_args a = {
+ .console = 0,
+ .keymap = &sc->sc_keymap,
+ .accessops = &accessops,
+ .accesscookie = sc,
+ .audiocookie = NULL, /* XXX audio_cookie */
+ };
+
+ sc->sc_keydesc[0].name = KB_US;
+ sc->sc_keydesc[0].base = 0;
+ sc->sc_keydesc[0].map_size = sc->sc_maplen;
+ sc->sc_keydesc[0].map = sc->sc_map;
+ sc->sc_keymap.keydesc = sc->sc_keydesc;
+ sc->sc_keymap.layout = KB_US | KB_NOENCODING;
+ sc->sc_wskbddev = config_found(sc->sc_dev, &a, wskbddevprint);
+}
+
+int
+hidcc_enable(void *v, int on)
+{
+ struct hidcc *sc = (struct hidcc *)v;
+
+ return sc->sc_enable(sc->sc_arg, on);
+}
+
+void
+hidcc_set_leds(void *v, int leds)
+{
+}
+
+int
+hidcc_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
+{
+ switch (cmd) {
+ /* wsconsctl(8) stub */
+ case WSKBDIO_GTYPE:
+ *(int *)data = WSKBD_TYPE_USB;
+ return 0;
+
+ /* wsconsctl(8) stub */
+ case WSKBDIO_GETLEDS:
+ *(int *)data = 0;
+ return 0;
+
+#ifdef WSDISPLAY_COMPAT_RAWKBD
+ case WSKBDIO_SETMODE: {
+ struct hidcc *sc = (struct hidcc *)v;
+
+ sc->sc_mode = *(int *)data;
+ return 0;
+ }
+#endif
+ }
+
+ return -1;
+}
+
+/*
+ * Parse the HID report and construct a mapping between the bits in the input
+ * report and the corresponding pressed key.
+ */
+int
+hidcc_parse(struct hidcc *sc, void *desc, int descsiz, int repid, int isize)
+{
+ enum { OFFSET, LENGTH } istate = OFFSET;
+ struct hid_item hi;
+ struct hid_data *hd;
+ u_int bit = 0;
+ int error = 0;
+ int nsyms = nitems(hidcc_keysyms);
+ int nbits;
+
+ /*
+ * The size of the input report is expressed in bytes where each bit in
+ * turn represents a pressed key. It's likely that the number of keys is
+ * less than this generous estimate.
+ */
+ nbits = isize * 8;
+ if (nbits == 0)
+ return ENXIO;
+
+ /* Allocate buffer used to slice interrupt data. */
+ sc->sc_input.i_bufsiz = isize;
+ sc->sc_input.i_buf = malloc(sc->sc_input.i_bufsiz, M_USBDEV, M_WAITOK);
+
+ /*
+ * Create mapping between each input bit and the corresponding usage,
+ * used in translating mode. Two entries are needed per bit in order
+ * construct a mapping. Note that at most all known usages as given by
+ * hidcc_keysyms can be inserted into this map.
+ */
+ sc->sc_mapsiz = nsyms * 2;
+ sc->sc_map = mallocarray(nsyms, 2 * sizeof(*sc->sc_map), M_USBDEV,
+ M_WAITOK | M_ZERO);
+
+ /*
+ * Create mapping between each input bit and the corresponding usage,
+ * used in raw mode.
+ */
+ sc->sc_rawsiz = nbits;
+ sc->sc_raw = mallocarray(nbits, sizeof(*sc->sc_raw), M_USBDEV,
+ M_WAITOK | M_ZERO);
+
+ hd = hid_start_parse(desc, descsiz, hid_input);
+ while (hid_get_item(hd, &hi)) {
+ uint32_t off;
+ int32_t usage;
+
+ if (hi.report_ID != repid || hi.kind != hid_input)
+ continue;
+
+ if (HID_GET_USAGE_PAGE(hi.usage) != HUP_CONSUMER) {
+ uint32_t len = hi.loc.size * hi.loc.count;
+
+ switch (istate) {
+ case OFFSET:
+ sc->sc_input.i_off = hi.loc.pos + len;
+ break;
+ case LENGTH:
+ /* Constant padding. */
+ if (hi.flags & HIO_CONST)
+ sc->sc_input.i_len += len;
+ break;
+ }
+ continue;
+ }
+
+ /* Signal that the input offset is reached. */
+ istate = LENGTH;
+ off = sc->sc_input.i_len;
+ sc->sc_input.i_len += hi.loc.size * hi.loc.count;
+
+ /*
+ * The usages could be expressed as an array instead of
+ * enumerating all supported ones.
+ */
+ if (hidcc_is_array(&hi)) {
+ error = hidcc_parse_array(sc, &hi);
+ break;
+ }
+
+ usage = HID_GET_USAGE(hi.usage);
+ if (usage == HUC_VOLUME)
+ error = hidcc_add_key_volume(sc, &hi, off, bit);
+ else
+ error = hidcc_add_key(sc, usage, bit);
+ if (error)
+ break;
+ sc->sc_nusages++;
+ bit += hi.loc.size * hi.loc.count;
+ }
+ hid_end_parse(hd);
+
+ DPRINTF("%s: input: off %d, len %d\n", DEVNAME(sc),
+ sc->sc_input.i_off, sc->sc_input.i_len);
+
+ return error;
+}
+
+int
+hidcc_parse_array(struct hidcc *sc, const struct hid_item *hi)
+{
+ int32_t max, min, usage;
+
+ min = HID_GET_USAGE(hi->usage_minimum);
+ max = HID_GET_USAGE(hi->usage_maximum);
+
+ sc->sc_nusages = (max - min) + 1;
+ sc->sc_isarray = 1;
+
+ for (usage = min; usage <= max; usage++) {
+ int error;
+
+ error = hidcc_add_key(sc, usage, 0);
+ if (error)
+ return error;
+ }
+
+ return 0;
+}
+
+int
+hidcc_is_array(const struct hid_item *hi)
+{
+ int32_t max, min;
+
+ min = HID_GET_USAGE(hi->usage_minimum);
+ max = HID_GET_USAGE(hi->usage_maximum);
+ return min >= 0 && max > 0 && min < max;
+}
+
+int
+hidcc_add_key(struct hidcc *sc, int32_t usage, u_int bit)
+{
+ const struct hidcc_keysym *ks;
+
+ if (hidcc_usage_to_sym(usage, &ks))
+ return 0;
+
+ if (sc->sc_maplen + 2 > sc->sc_mapsiz)
+ return ENOMEM;
+ sc->sc_map[sc->sc_maplen++] = KS_KEYCODE(sc->sc_isarray ? usage : bit);
+ sc->sc_map[sc->sc_maplen++] = ks->ks_key;
+
+ if (!sc->sc_isarray) {
+ if (bit >= sc->sc_rawsiz)
+ return ENOMEM;
+ sc->sc_raw[bit] = ks;
+ }
+
+ DPRINTF("%s: bit %d, usage \"%s\"\n", DEVNAME(sc),
+ bit, ks->ks_name);
+ return 0;
+}
+
+/*
+ * Add key mappings for the volume usage which differs compared to the volume
+ * increment/decrement usages in which each volume change direction is
+ * represented using a distinct usage. The volume usage instead uses bits of the
+ * interrupt buffer to represent the wanted volume. The same bits should be
+ * within the bounds given by the logical min/max associated with the HID item.
+ */
+int
+hidcc_add_key_volume(struct hidcc *sc, const struct hid_item *hi,
+ uint32_t off, u_int bit)
+{
+ uint32_t len;
+ int error;
+
+ /*
+ * Since the volume usage is internally represented using two key
+ * mappings, make sure enough bits are available to avoid any ambiguity.
+ */
+ len = hi->loc.size * hi->loc.count;
+ if (len <= 1)
+ return 1;
+
+ sc->sc_volume.v_inc = bit;
+ sc->sc_volume.v_dec = bit + 1;
+ sc->sc_volume.v_off = off;
+ sc->sc_volume.v_len = len;
+
+ DPRINTF("%s: inc %d, dec %d, off %d, len %d, min %d, max %d\n",
+ DEVNAME(sc), sc->sc_volume.v_inc, sc->sc_volume.v_dec,
+ sc->sc_volume.v_off, sc->sc_volume.v_len,
+ hi->logical_minimum, hi->logical_maximum);
+
+ error = hidcc_add_key(sc, HUC_VOL_INC, sc->sc_volume.v_inc);
+ if (error)
+ return error;
+ error = hidcc_add_key(sc, HUC_VOL_DEC, sc->sc_volume.v_dec);
+ if (error)
+ return error;
+ return 0;
+}
+
+int
+hidcc_bit_to_sym(struct hidcc *sc, u_int bit, const struct hidcc_keysym **ks)
+{
+ if (bit >= sc->sc_rawsiz || sc->sc_raw[bit] == NULL)
+ return 1;
+ *ks = sc->sc_raw[bit];
+ return 0;
+}
+
+int
+hidcc_usage_to_sym(int32_t usage, const struct hidcc_keysym **ks)
+{
+ int len = nitems(hidcc_keysyms);
+ int i;
+
+ for (i = 0; i < len; i++) {
+ if (hidcc_keysyms[i].ks_usage == usage) {
+ *ks = &hidcc_keysyms[i];
+ return 0;
+ }
+ }
+ return 1;
+}
+
+int
+hidcc_bits_to_int(uint8_t *buf, u_int buflen, int32_t *usage)
+{
+ int32_t x = 0;
+ int i;
+
+ if (buflen == 0 || buflen > sizeof(*usage))
+ return 1;
+
+ for (i = buflen - 1; i >= 0; i--) {
+ x |= buf[i];
+ if (i > 0)
+ x <<= 8;
+ }
+ *usage = x;
+ return 0;
+}
+
+int
+hidcc_bits_to_volume(struct hidcc *sc, uint8_t *buf, int buflen, u_int *bit)
+{
+ uint32_t vlen = sc->sc_volume.v_len;
+ uint32_t voff = sc->sc_volume.v_off;
+ int32_t vol;
+ int sign;
+
+ if (vlen == 0)
+ return 1;
+ if (hidcc_bits_to_int(buf, buflen, &vol))
+ return 1;
+ vol = (vol >> voff) & ((1 << vlen) - 1);
+ if (vol == 0)
+ return 1;
+
+ /*
+ * Interpret the volume as a relative change by only looking at the sign
+ * in order to determine the change direction.
+ */
+ sign = vol & (1 << (vlen - 1)) ? -1 : 1;
+ if (sign < 0)
+ vol = (1 << vlen) - vol;
+ vol *= sign;
+ if (vol > 0)
+ *bit = sc->sc_volume.v_inc;
+ else
+ *bit = sc->sc_volume.v_dec;
+ return 0;
+}
+
+int
+hidcc_intr_slice(struct hidcc *sc, uint8_t *src, uint8_t *dst, int *len)
+{
+ int ilen = sc->sc_input.i_len;
+ int ioff = sc->sc_input.i_off;
+ int di, si;
+ int maxlen = *len;
+
+ if (maxlen == 0)
+ return 1;
+
+ memset(dst, 0, maxlen);
+ si = ioff;
+ di = 0;
+ for (; ilen > 0; ilen--) {
+ int db, sb;
+
+ sb = si / 8;
+ db = di / 8;
+ if (sb >= maxlen || db >= maxlen)
+ return 1;
+
+ if (src[sb] & (1 << (si % 8)))
+ dst[db] |= 1 << (di % 8);
+ si++;
+ di++;
+ }
+
+ *len = (sc->sc_input.i_len + 7) / 8;
+ return 0;
+}
+
+void
+hidcc_input(struct hidcc *sc, u_int bit, int release)
+{
+ int s;
+
+ s = spltty();
+ wskbd_input(sc->sc_wskbddev,
+ release ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN, bit);
+ splx(s);
+}
+
+void
+hidcc_rawinput(struct hidcc *sc, u_char c, int release)
+{
+#ifdef WSDISPLAY_COMPAT_RAWKBD
+ u_char buf[2];
+ int len = 0;
+ int s;
+
+ if (c & 0x80)
+ buf[len++] = 0xe0;
+ buf[len++] = c & 0x7f;
+ if (release)
+ buf[len - 1] |= 0x80;
+
+ s = spltty();
+ wskbd_rawinput(sc->sc_wskbddev, buf, len);
+ splx(s);
+#endif
+}
+
+int
+hidcc_setbits(struct hidcc *sc, uint8_t *data, int len, u_int *bit)
+{
+ int i, j;
+
+ if (hidcc_bits_to_volume(sc, data, len, bit) == 0)
+ return 0;
+
+ for (i = 0; i < len; i++) {
+ if (data[i] == 0)
+ continue;
+
+ for (j = 0; j < 8; j++) {
+ if (data[i] & (1 << j)) {
+ *bit = (i * 8) + j;
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+#ifdef HIDCC_DEBUG
+
+void
+hidcc_dump(struct hidcc *sc, const char *prefix, uint8_t *data, u_int len)
+{
+ u_int i;
+
+ if (hidcc_debug == 0)
+ return;
+
+ printf("%s: %s:", DEVNAME(sc), prefix);
+ for (i = 0; i < len; i++)
+ printf(" %02x", data[i]);
+ printf("\n");
+}
+
+#endif