/* $OpenBSD: ucc.c,v 1.28 2021/11/17 06:21:23 anton Exp $ */ /* * Copyright (c) 2021 Anton Lindqvist * * 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 #include #include #include #include #include #include #include #include #include #include #include /* #define UCC_DEBUG */ #ifdef UCC_DEBUG #define DPRINTF(x...) do { if (ucc_debug) printf(x); } while (0) void ucc_dump(const char *, uint8_t *, u_int); int ucc_debug = 1; #else #define DPRINTF(x...) #define ucc_dump(prefix, data, len) #endif struct ucc_softc { struct uhidev sc_hdev; 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 ucc_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; }; struct ucc_keysym { #ifdef UCC_DEBUG const char *us_name; #endif int32_t us_usage; keysym_t us_key; u_char us_raw; }; int ucc_match(struct device *, void *, void *); void ucc_attach(struct device *, struct device *, void *); int ucc_detach(struct device *, int); void ucc_intr(struct uhidev *, void *, u_int); void ucc_attach_wskbd(struct ucc_softc *); int ucc_enable(void *, int); void ucc_set_leds(void *, int); int ucc_ioctl(void *, u_long, caddr_t, int, struct proc *); int ucc_hid_match(void *, int, uint8_t); int ucc_hid_parse(struct ucc_softc *, void *, int); int ucc_hid_parse_array(struct ucc_softc *, const struct hid_item *); int ucc_hid_is_array(const struct hid_item *); int ucc_add_key(struct ucc_softc *, int32_t, u_int); int ucc_add_key_volume(struct ucc_softc *, const struct hid_item *, uint32_t, u_int); int ucc_bit_to_sym(struct ucc_softc *, u_int, const struct ucc_keysym **); int ucc_usage_to_sym(int32_t, const struct ucc_keysym **); int ucc_bits_to_int(uint8_t *, u_int, int32_t *); int ucc_bits_to_volume(struct ucc_softc *, uint8_t *, int, u_int *); int ucc_intr_slice(struct ucc_softc *, uint8_t *, uint8_t *, int *); void ucc_input(struct ucc_softc *, u_int, int); void ucc_rawinput(struct ucc_softc *, u_char, int); int ucc_setbits(struct ucc_softc *, uint8_t *, int, u_int *); struct cfdriver ucc_cd = { NULL, "ucc", DV_DULL }; const struct cfattach ucc_ca = { sizeof(struct ucc_softc), ucc_match, ucc_attach, ucc_detach, }; /* * 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 ucc_keysym ucc_keysyms[] = { #ifdef UCC_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 Tast/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 }; int ucc_match(struct device *parent, void *match, void *aux) { struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; void *desc; int size; if (UHIDEV_CLAIM_MULTIPLE_REPORTID(uha)) return UMATCH_NONE; uhidev_get_report_desc(uha->parent, &desc, &size); if (hid_report_size(desc, size, hid_input, uha->reportid) == 0) return UMATCH_NONE; if (!ucc_hid_match(desc, size, uha->reportid)) return UMATCH_NONE; return UMATCH_IFACECLASS; } void ucc_attach(struct device *parent, struct device *self, void *aux) { struct ucc_softc *sc = (struct ucc_softc *)self; struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; void *desc; int error, repid, size; sc->sc_mode = WSKBD_TRANSLATED; sc->sc_last_translate = -1; sc->sc_hdev.sc_intr = ucc_intr; sc->sc_hdev.sc_parent = uha->parent; sc->sc_hdev.sc_udev = uha->uaa->device; sc->sc_hdev.sc_report_id = uha->reportid; uhidev_get_report_desc(uha->parent, &desc, &size); repid = uha->reportid; sc->sc_hdev.sc_isize = hid_report_size(desc, size, hid_input, repid); sc->sc_hdev.sc_osize = hid_report_size(desc, size, hid_output, repid); sc->sc_hdev.sc_fsize = hid_report_size(desc, size, hid_feature, repid); error = ucc_hid_parse(sc, desc, size); if (error) { printf(": hid error %d\n", error); return; } 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) ucc_attach_wskbd(sc); } int ucc_detach(struct device *self, int flags) { struct ucc_softc *sc = (struct ucc_softc *)self; int error = 0; if (sc->sc_wskbddev != NULL) error = config_detach(sc->sc_wskbddev, flags); uhidev_close(&sc->sc_hdev); 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)); return error; } void ucc_intr(struct uhidev *addr, void *data, u_int len) { struct ucc_softc *sc = (struct ucc_softc *)addr; const struct ucc_keysym *us; uint8_t *buf = sc->sc_input.i_buf; int raw = sc->sc_mode == WSKBD_RAW; int error; u_int bit = 0; ucc_dump(__func__, data, len); if (len > sc->sc_input.i_bufsiz) { DPRINTF("%s: too much data: len %d, bufsiz %d\n", __func__, len, sc->sc_input.i_bufsiz); return; } error = ucc_intr_slice(sc, data, buf, &len); if (error) { DPRINTF("%s: slice failure: error %d\n", __func__, error); return; } /* Dump the buffer again after slicing. */ ucc_dump(__func__, buf, len); if (ucc_setbits(sc, buf, len, &bit)) { /* All zeroes, assume key up event. */ if (raw) { if (sc->sc_last_raw != 0) { ucc_rawinput(sc, sc->sc_last_raw, 1); sc->sc_last_raw = 0; } } else { if (sc->sc_last_translate != -1) { ucc_input(sc, sc->sc_last_translate, 1); sc->sc_last_translate = -1; } } return; } else if (sc->sc_isarray) { int32_t usage; if (ucc_bits_to_int(buf, len, &usage) || ucc_usage_to_sym(usage, &us)) goto unknown; bit = us->us_usage; } else if (raw) { if (ucc_bit_to_sym(sc, bit, &us)) goto unknown; } if (raw) { ucc_rawinput(sc, us->us_raw, 0); sc->sc_last_raw = us->us_raw; /* * Feed both raw and translating input for keys that have both * defined. This is only the case for volume related keys. */ if (us->us_key == 0) return; } ucc_input(sc, bit, 0); if (!raw) sc->sc_last_translate = bit; return; unknown: DPRINTF("%s: unknown key: bit %d\n", __func__, bit); } void ucc_attach_wskbd(struct ucc_softc *sc) { static const struct wskbd_accessops accessops = { .enable = ucc_enable, .set_leds = ucc_set_leds, .ioctl = ucc_ioctl, }; struct wskbddev_attach_args a = { .console = 0, .keymap = &sc->sc_keymap, .accessops = &accessops, .accesscookie = sc, }; 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_hdev.sc_dev, &a, wskbddevprint); } int ucc_enable(void *v, int on) { struct ucc_softc *sc = (struct ucc_softc *)v; int error = 0; if (on) error = uhidev_open(&sc->sc_hdev); else uhidev_close(&sc->sc_hdev); return error; } void ucc_set_leds(void *v, int leds) { } int ucc_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 ucc_softc *sc = (struct ucc_softc *)v; sc->sc_mode = *(int *)data; return 0; } #endif } return -1; } /* * Returns non-zero if the given report ID has at least one Consumer Control * usage. */ int ucc_hid_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; } /* * Parse the HID report and construct a mapping between the bits in the input * report and the corresponding pressed key. */ int ucc_hid_parse(struct ucc_softc *sc, void *desc, int descsiz) { enum { OFFSET, LENGTH } istate = OFFSET; struct hid_item hi; struct hid_data *hd; u_int bit = 0; int error = 0; int nsyms = nitems(ucc_keysyms); int repid = sc->sc_hdev.sc_report_id; int isize; /* * 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. */ isize = sc->sc_hdev.sc_isize * 8; if (isize == 0) return ENXIO; /* Allocate buffer used to slice interrupt data. */ sc->sc_input.i_bufsiz = sc->sc_hdev.sc_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 * ucc_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 = isize; sc->sc_raw = mallocarray(isize, 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 (ucc_hid_is_array(&hi)) { error = ucc_hid_parse_array(sc, &hi); break; } usage = HID_GET_USAGE(hi.usage); if (usage == HUC_VOLUME) error = ucc_add_key_volume(sc, &hi, off, bit); else error = ucc_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", __func__, sc->sc_input.i_off, sc->sc_input.i_len); return error; } int ucc_hid_parse_array(struct ucc_softc *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 = ucc_add_key(sc, usage, 0); if (error) return error; } return 0; } int ucc_hid_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 ucc_add_key(struct ucc_softc *sc, int32_t usage, u_int bit) { const struct ucc_keysym *us; if (ucc_usage_to_sym(usage, &us)) 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++] = us->us_key; if (!sc->sc_isarray) { if (bit >= sc->sc_rawsiz) return ENOMEM; sc->sc_raw[bit] = us; } DPRINTF("%s: bit %d, usage \"%s\"\n", __func__, bit, us->us_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 ucc_add_key_volume(struct ucc_softc *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", __func__, 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 = ucc_add_key(sc, HUC_VOL_INC, sc->sc_volume.v_inc); if (error) return error; error = ucc_add_key(sc, HUC_VOL_DEC, sc->sc_volume.v_dec); if (error) return error; return 0; } int ucc_bit_to_sym(struct ucc_softc *sc, u_int bit, const struct ucc_keysym **us) { if (bit >= sc->sc_rawsiz || sc->sc_raw[bit] == NULL) return 1; *us = sc->sc_raw[bit]; return 0; } int ucc_usage_to_sym(int32_t usage, const struct ucc_keysym **us) { int len = nitems(ucc_keysyms); int i; for (i = 0; i < len; i++) { if (ucc_keysyms[i].us_usage == usage) { *us = &ucc_keysyms[i]; return 0; } } return 1; } int ucc_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 ucc_bits_to_volume(struct ucc_softc *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 (ucc_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 ucc_intr_slice(struct ucc_softc *sc, uint8_t *src, uint8_t *dst, int *len) { int ilen = sc->sc_input.i_len; int ioff = sc->sc_input.i_off; int maxlen = *len; int di, si; 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 ucc_input(struct ucc_softc *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 ucc_rawinput(struct ucc_softc *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 ucc_setbits(struct ucc_softc *sc, uint8_t *data, int len, u_int *bit) { int i, j; if (ucc_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 UCC_DEBUG void ucc_dump(const char *prefix, uint8_t *data, u_int len) { u_int i; if (ucc_debug == 0) return; printf("%s:", prefix); for (i = 0; i < len; i++) printf(" %02x", data[i]); printf("\n"); } #endif