/* $OpenBSD: umstc.c,v 1.7 2021/11/22 11:29:18 anton Exp $ */ /* * Copyright (c) 2020 joshua stein * * 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. */ /* * Microsoft Surface Type Cover driver to respond to F1-F7 keys, but also to * keep the USB HID pipes open or else the Type Cover will detach and reattach * each time one of these buttons is pressed. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "audio.h" #include "wsdisplay.h" struct umstc_softc { struct uhidev sc_hdev; struct task sc_brightness_task; int sc_brightness_steps; }; void umstc_intr(struct uhidev *addr, void *ibuf, u_int len); int umstc_match(struct device *, void *, void *); void umstc_attach(struct device *, struct device *, void *); int umstc_detach(struct device *, int flags); void umstc_brightness_task(void *); extern int wskbd_set_mixervolume(long, long); struct cfdriver umstc_cd = { NULL, "umstc", DV_DULL }; const struct cfattach umstc_ca = { sizeof(struct umstc_softc), umstc_match, umstc_attach, umstc_detach, }; static const struct usb_devno umstc_devs[] = { { USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_TYPECOVER }, { USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_TYPECOVER2 }, { USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_TYPECOVER3 }, }; int umstc_match(struct device *parent, void *match, void *aux) { struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; int size; void *desc; if (UHIDEV_CLAIM_MULTIPLE_REPORTID(uha)) return (UMATCH_NONE); if (!usb_lookup(umstc_devs, uha->uaa->vendor, uha->uaa->product)) return UMATCH_NONE; uhidev_get_report_desc(uha->parent, &desc, &size); if (hid_is_collection(desc, size, uha->reportid, HID_USAGE2(HUP_CONSUMER, HUC_CONTROL))) return UMATCH_IFACECLASS; return UMATCH_NONE; } void umstc_attach(struct device *parent, struct device *self, void *aux) { struct umstc_softc *sc = (struct umstc_softc *)self; struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; struct usb_attach_arg *uaa = uha->uaa; int size, repid; void *desc; sc->sc_hdev.sc_intr = umstc_intr; sc->sc_hdev.sc_parent = uha->parent; sc->sc_hdev.sc_udev = uaa->device; sc->sc_hdev.sc_report_id = uha->reportid; usbd_set_idle(uha->parent->sc_udev, uha->parent->sc_ifaceno, 0, 0); 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); uhidev_open(&sc->sc_hdev); task_set(&sc->sc_brightness_task, umstc_brightness_task, sc); printf("\n"); } int umstc_detach(struct device *self, int flags) { struct umstc_softc *sc = (struct umstc_softc *)self; task_del(systq, &sc->sc_brightness_task); uhidev_close(&sc->sc_hdev); return 0; } void umstc_intr(struct uhidev *addr, void *buf, u_int len) { struct umstc_softc *sc = (struct umstc_softc *)addr; int i; if (!len) return; switch (((unsigned char *)buf)[0]) { case HUC_PLAY_PAUSE: /* * It would be nice to pass this through to userland but we'd * need to attach a wskbd */ break; case HUC_MUTE: #if NAUDIO > 0 wskbd_set_mixervolume(0, 1); #endif break; case HUC_VOL_INC: #if NAUDIO > 0 wskbd_set_mixervolume(1, 1); #endif break; case HUC_VOL_DEC: #if NAUDIO > 0 wskbd_set_mixervolume(-1, 1); #endif break; case 0x70: /* brightness down */ #if NWSDISPLAY > 0 atomic_sub_int(&sc->sc_brightness_steps, 1); task_add(systq, &sc->sc_brightness_task); #endif break; case 0x6f: /* brightness up */ #if NWSDISPLAY > 0 atomic_add_int(&sc->sc_brightness_steps, 1); task_add(systq, &sc->sc_brightness_task); #endif break; case 0: break; default: printf("%s: unhandled key ", sc->sc_hdev.sc_dev.dv_xname); for (i = 0; i < len; i++) printf(" 0x%02x", ((unsigned char *)buf)[i]); printf("\n"); } } void umstc_brightness_task(void *arg) { struct umstc_softc *sc = arg; int steps = atomic_swap_uint(&sc->sc_brightness_steps, 0); int dir = 1; if (steps < 0) { steps = -steps; dir = -1; } while (steps--) wsdisplay_brightness_step(NULL, dir); }