1 /* $OpenBSD: umstc.c,v 1.7 2021/11/22 11:29:18 anton 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/kernel.h> 28 #include <sys/device.h> 29 #include <sys/atomic.h> 30 #include <sys/task.h> 31 32 #include <dev/usb/usb.h> 33 #include <dev/usb/usbhid.h> 34 #include <dev/usb/usbdi.h> 35 #include <dev/usb/usbdi_util.h> 36 #include <dev/usb/usbdevs.h> 37 #include <dev/usb/uhidev.h> 38 39 #include <dev/wscons/wsconsio.h> 40 #include <dev/wscons/wsdisplayvar.h> 41 42 #include "audio.h" 43 #include "wsdisplay.h" 44 45 struct umstc_softc { 46 struct uhidev sc_hdev; 47 struct task sc_brightness_task; 48 int sc_brightness_steps; 49 }; 50 51 void umstc_intr(struct uhidev *addr, void *ibuf, u_int len); 52 int umstc_match(struct device *, void *, void *); 53 void umstc_attach(struct device *, struct device *, void *); 54 int umstc_detach(struct device *, int flags); 55 void umstc_brightness_task(void *); 56 57 extern int wskbd_set_mixervolume(long, long); 58 59 struct cfdriver umstc_cd = { 60 NULL, "umstc", DV_DULL 61 }; 62 63 const struct cfattach umstc_ca = { 64 sizeof(struct umstc_softc), 65 umstc_match, 66 umstc_attach, 67 umstc_detach, 68 }; 69 70 static const struct usb_devno umstc_devs[] = { 71 { USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_TYPECOVER }, 72 { USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_TYPECOVER2 }, 73 { USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_TYPECOVER3 }, 74 }; 75 76 int 77 umstc_match(struct device *parent, void *match, void *aux) 78 { 79 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 80 int size; 81 void *desc; 82 83 if (UHIDEV_CLAIM_MULTIPLE_REPORTID(uha)) 84 return (UMATCH_NONE); 85 86 if (!usb_lookup(umstc_devs, uha->uaa->vendor, uha->uaa->product)) 87 return UMATCH_NONE; 88 89 uhidev_get_report_desc(uha->parent, &desc, &size); 90 91 if (hid_is_collection(desc, size, uha->reportid, 92 HID_USAGE2(HUP_CONSUMER, HUC_CONTROL))) 93 return UMATCH_IFACECLASS; 94 95 return UMATCH_NONE; 96 } 97 98 void 99 umstc_attach(struct device *parent, struct device *self, void *aux) 100 { 101 struct umstc_softc *sc = (struct umstc_softc *)self; 102 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 103 struct usb_attach_arg *uaa = uha->uaa; 104 int size, repid; 105 void *desc; 106 107 sc->sc_hdev.sc_intr = umstc_intr; 108 sc->sc_hdev.sc_parent = uha->parent; 109 sc->sc_hdev.sc_udev = uaa->device; 110 sc->sc_hdev.sc_report_id = uha->reportid; 111 112 usbd_set_idle(uha->parent->sc_udev, uha->parent->sc_ifaceno, 0, 0); 113 114 uhidev_get_report_desc(uha->parent, &desc, &size); 115 repid = uha->reportid; 116 sc->sc_hdev.sc_isize = hid_report_size(desc, size, hid_input, repid); 117 sc->sc_hdev.sc_osize = hid_report_size(desc, size, hid_output, repid); 118 sc->sc_hdev.sc_fsize = hid_report_size(desc, size, hid_feature, repid); 119 120 uhidev_open(&sc->sc_hdev); 121 122 task_set(&sc->sc_brightness_task, umstc_brightness_task, sc); 123 124 printf("\n"); 125 } 126 127 int 128 umstc_detach(struct device *self, int flags) 129 { 130 struct umstc_softc *sc = (struct umstc_softc *)self; 131 132 task_del(systq, &sc->sc_brightness_task); 133 134 uhidev_close(&sc->sc_hdev); 135 136 return 0; 137 } 138 139 void 140 umstc_intr(struct uhidev *addr, void *buf, u_int len) 141 { 142 struct umstc_softc *sc = (struct umstc_softc *)addr; 143 int i; 144 145 if (!len) 146 return; 147 148 switch (((unsigned char *)buf)[0]) { 149 case HUC_PLAY_PAUSE: 150 /* 151 * It would be nice to pass this through to userland but we'd 152 * need to attach a wskbd 153 */ 154 break; 155 case HUC_MUTE: 156 #if NAUDIO > 0 157 wskbd_set_mixervolume(0, 1); 158 #endif 159 break; 160 case HUC_VOL_INC: 161 #if NAUDIO > 0 162 wskbd_set_mixervolume(1, 1); 163 #endif 164 break; 165 case HUC_VOL_DEC: 166 #if NAUDIO > 0 167 wskbd_set_mixervolume(-1, 1); 168 #endif 169 break; 170 case 0x70: /* brightness down */ 171 #if NWSDISPLAY > 0 172 atomic_sub_int(&sc->sc_brightness_steps, 1); 173 task_add(systq, &sc->sc_brightness_task); 174 #endif 175 break; 176 case 0x6f: /* brightness up */ 177 #if NWSDISPLAY > 0 178 atomic_add_int(&sc->sc_brightness_steps, 1); 179 task_add(systq, &sc->sc_brightness_task); 180 #endif 181 break; 182 case 0: 183 break; 184 default: 185 printf("%s: unhandled key ", sc->sc_hdev.sc_dev.dv_xname); 186 for (i = 0; i < len; i++) 187 printf(" 0x%02x", ((unsigned char *)buf)[i]); 188 printf("\n"); 189 } 190 } 191 192 void 193 umstc_brightness_task(void *arg) 194 { 195 struct umstc_softc *sc = arg; 196 int steps = atomic_swap_uint(&sc->sc_brightness_steps, 0); 197 int dir = 1; 198 199 if (steps < 0) { 200 steps = -steps; 201 dir = -1; 202 } 203 204 while (steps--) 205 wsdisplay_brightness_step(NULL, dir); 206 } 207