diff options
author | Anton Lindqvist <anton@cvs.openbsd.org> | 2022-11-11 06:46:49 +0000 |
---|---|---|
committer | Anton Lindqvist <anton@cvs.openbsd.org> | 2022-11-11 06:46:49 +0000 |
commit | 5e1ac03b26ed38726b3194c617f52b4687a48e34 (patch) | |
tree | e7cd6001764a9a8cb9289ee8cbc6a68bdcc4e986 /sys/dev/hid | |
parent | bad320476abd1fd15c0738168c771e11cb60bc52 (diff) |
Extract the HID specific pieces from ucc(4) into hidcc. First steps
toward attaching hidcc over i2c.
ok matthieu@
Diffstat (limited to 'sys/dev/hid')
-rw-r--r-- | sys/dev/hid/files.hid | 6 | ||||
-rw-r--r-- | sys/dev/hid/hidcc.c | 1199 | ||||
-rw-r--r-- | sys/dev/hid/hidccvar.h | 37 |
3 files changed, 1241 insertions, 1 deletions
diff --git a/sys/dev/hid/files.hid b/sys/dev/hid/files.hid index 438f076ce5c..351bc5b0187 100644 --- a/sys/dev/hid/files.hid +++ b/sys/dev/hid/files.hid @@ -1,4 +1,4 @@ -# $OpenBSD: files.hid,v 1.2 2016/01/20 01:26:00 jcs Exp $ +# $OpenBSD: files.hid,v 1.3 2022/11/11 06:46:48 anton Exp $ # Human Interface Devices @@ -20,3 +20,7 @@ file dev/hid/hidms.c hidms # Multitouch define hidmt file dev/hid/hidmt.c hidmt + +# Consumer Control keyboards +define hidcc +file dev/hid/hidcc.c hidcc diff --git a/sys/dev/hid/hidcc.c b/sys/dev/hid/hidcc.c new file mode 100644 index 00000000000..eb27f0b260c --- /dev/null +++ b/sys/dev/hid/hidcc.c @@ -0,0 +1,1199 @@ +/* $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 diff --git a/sys/dev/hid/hidccvar.h b/sys/dev/hid/hidccvar.h new file mode 100644 index 00000000000..b724accc074 --- /dev/null +++ b/sys/dev/hid/hidccvar.h @@ -0,0 +1,37 @@ +/* $OpenBSD: hidccvar.h,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/types.h> + +struct hidcc_attach_arg { + struct device *device; + void *audio_cookie; + + void *desc; + int descsiz; + int repid; + int isize; + + int (*enable)(void *, int); + void *arg; +}; + +int hidcc_match(void *, int, uint8_t); +struct hidcc *hidcc_attach(const struct hidcc_attach_arg *); +int hidcc_detach(struct hidcc *, int); +void hidcc_intr(struct hidcc *, void *, u_int); |