1 /* $OpenBSD: umstc.c,v 1.8 2024/05/23 03:21:09 jsg Exp $ */ 2 3 /* 4 * Copyright (c) 2020 joshua stein <jcs@jcs.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* 20 * Microsoft Surface Type Cover driver to respond to F1-F7 keys, but also to 21 * keep the USB HID pipes open or else the Type Cover will detach and reattach 22 * each time one of these buttons is pressed. 23 */ 24 25 #include <sys/param.h> 26 #include <sys/systm.h> 27 #include <sys/device.h> 28 #include <sys/atomic.h> 29 #include <sys/task.h> 30 31 #include <dev/usb/usb.h> 32 #include <dev/usb/usbhid.h> 33 #include <dev/usb/usbdi.h> 34 #include <dev/usb/usbdi_util.h> 35 #include <dev/usb/usbdevs.h> 36 #include <dev/usb/uhidev.h> 37 38 #include <dev/wscons/wsdisplayvar.h> 39 40 #include "audio.h" 41 #include "wsdisplay.h" 42 43 struct umstc_softc { 44 struct uhidev sc_hdev; 45 struct task sc_brightness_task; 46 int sc_brightness_steps; 47 }; 48 49 void umstc_intr(struct uhidev *addr, void *ibuf, u_int len); 50 int umstc_match(struct device *, void *, void *); 51 void umstc_attach(struct device *, struct device *, void *); 52 int umstc_detach(struct device *, int flags); 53 void umstc_brightness_task(void *); 54 55 extern int wskbd_set_mixervolume(long, long); 56 57 struct cfdriver umstc_cd = { 58 NULL, "umstc", DV_DULL 59 }; 60 61 const struct cfattach umstc_ca = { 62 sizeof(struct umstc_softc), 63 umstc_match, 64 umstc_attach, 65 umstc_detach, 66 }; 67 68 static const struct usb_devno umstc_devs[] = { 69 { USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_TYPECOVER }, 70 { USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_TYPECOVER2 }, 71 { USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_TYPECOVER3 }, 72 }; 73 74 int 75 umstc_match(struct device *parent, void *match, void *aux) 76 { 77 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 78 int size; 79 void *desc; 80 81 if (UHIDEV_CLAIM_MULTIPLE_REPORTID(uha)) 82 return (UMATCH_NONE); 83 84 if (!usb_lookup(umstc_devs, uha->uaa->vendor, uha->uaa->product)) 85 return UMATCH_NONE; 86 87 uhidev_get_report_desc(uha->parent, &desc, &size); 88 89 if (hid_is_collection(desc, size, uha->reportid, 90 HID_USAGE2(HUP_CONSUMER, HUC_CONTROL))) 91 return UMATCH_IFACECLASS; 92 93 return UMATCH_NONE; 94 } 95 96 void 97 umstc_attach(struct device *parent, struct device *self, void *aux) 98 { 99 struct umstc_softc *sc = (struct umstc_softc *)self; 100 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 101 struct usb_attach_arg *uaa = uha->uaa; 102 int size, repid; 103 void *desc; 104 105 sc->sc_hdev.sc_intr = umstc_intr; 106 sc->sc_hdev.sc_parent = uha->parent; 107 sc->sc_hdev.sc_udev = uaa->device; 108 sc->sc_hdev.sc_report_id = uha->reportid; 109 110 usbd_set_idle(uha->parent->sc_udev, uha->parent->sc_ifaceno, 0, 0); 111 112 uhidev_get_report_desc(uha->parent, &desc, &size); 113 repid = uha->reportid; 114 sc->sc_hdev.sc_isize = hid_report_size(desc, size, hid_input, repid); 115 sc->sc_hdev.sc_osize = hid_report_size(desc, size, hid_output, repid); 116 sc->sc_hdev.sc_fsize = hid_report_size(desc, size, hid_feature, repid); 117 118 uhidev_open(&sc->sc_hdev); 119 120 task_set(&sc->sc_brightness_task, umstc_brightness_task, sc); 121 122 printf("\n"); 123 } 124 125 int 126 umstc_detach(struct device *self, int flags) 127 { 128 struct umstc_softc *sc = (struct umstc_softc *)self; 129 130 task_del(systq, &sc->sc_brightness_task); 131 132 uhidev_close(&sc->sc_hdev); 133 134 return 0; 135 } 136 137 void 138 umstc_intr(struct uhidev *addr, void *buf, u_int len) 139 { 140 struct umstc_softc *sc = (struct umstc_softc *)addr; 141 int i; 142 143 if (!len) 144 return; 145 146 switch (((unsigned char *)buf)[0]) { 147 case HUC_PLAY_PAUSE: 148 /* 149 * It would be nice to pass this through to userland but we'd 150 * need to attach a wskbd 151 */ 152 break; 153 case HUC_MUTE: 154 #if NAUDIO > 0 155 wskbd_set_mixervolume(0, 1); 156 #endif 157 break; 158 case HUC_VOL_INC: 159 #if NAUDIO > 0 160 wskbd_set_mixervolume(1, 1); 161 #endif 162 break; 163 case HUC_VOL_DEC: 164 #if NAUDIO > 0 165 wskbd_set_mixervolume(-1, 1); 166 #endif 167 break; 168 case 0x70: /* brightness down */ 169 #if NWSDISPLAY > 0 170 atomic_sub_int(&sc->sc_brightness_steps, 1); 171 task_add(systq, &sc->sc_brightness_task); 172 #endif 173 break; 174 case 0x6f: /* brightness up */ 175 #if NWSDISPLAY > 0 176 atomic_add_int(&sc->sc_brightness_steps, 1); 177 task_add(systq, &sc->sc_brightness_task); 178 #endif 179 break; 180 case 0: 181 break; 182 default: 183 printf("%s: unhandled key ", sc->sc_hdev.sc_dev.dv_xname); 184 for (i = 0; i < len; i++) 185 printf(" 0x%02x", ((unsigned char *)buf)[i]); 186 printf("\n"); 187 } 188 } 189 190 void 191 umstc_brightness_task(void *arg) 192 { 193 struct umstc_softc *sc = arg; 194 int steps = atomic_swap_uint(&sc->sc_brightness_steps, 0); 195 int dir = 1; 196 197 if (steps < 0) { 198 steps = -steps; 199 dir = -1; 200 } 201 202 while (steps--) 203 wsdisplay_brightness_step(NULL, dir); 204 } 205