1 /* $OpenBSD: umstc.c,v 1.3 2020/09/14 15:21:08 deraadt 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 }; 74 75 int 76 umstc_match(struct device *parent, void *match, void *aux) 77 { 78 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 79 int size; 80 void *desc; 81 82 if (!usb_lookup(umstc_devs, uha->uaa->vendor, uha->uaa->product)) 83 return UMATCH_NONE; 84 85 uhidev_get_report_desc(uha->parent, &desc, &size); 86 87 if (hid_is_collection(desc, size, uha->reportid, 88 HID_USAGE2(HUP_CONSUMER, HUC_CONTROL))) 89 return UMATCH_IFACECLASS; 90 91 return UMATCH_NONE; 92 } 93 94 void 95 umstc_attach(struct device *parent, struct device *self, void *aux) 96 { 97 struct umstc_softc *sc = (struct umstc_softc *)self; 98 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 99 struct usb_attach_arg *uaa = uha->uaa; 100 int size, repid; 101 void *desc; 102 103 sc->sc_hdev.sc_intr = umstc_intr; 104 sc->sc_hdev.sc_parent = uha->parent; 105 sc->sc_hdev.sc_udev = uaa->device; 106 sc->sc_hdev.sc_report_id = uha->reportid; 107 108 usbd_set_idle(uha->parent->sc_udev, uha->parent->sc_ifaceno, 0, 0); 109 110 uhidev_get_report_desc(uha->parent, &desc, &size); 111 repid = uha->reportid; 112 sc->sc_hdev.sc_isize = hid_report_size(desc, size, hid_input, repid); 113 sc->sc_hdev.sc_osize = hid_report_size(desc, size, hid_output, repid); 114 sc->sc_hdev.sc_fsize = hid_report_size(desc, size, hid_feature, repid); 115 116 uhidev_open(&sc->sc_hdev); 117 118 task_set(&sc->sc_brightness_task, umstc_brightness_task, sc); 119 120 printf("\n"); 121 } 122 123 int 124 umstc_detach(struct device *self, int flags) 125 { 126 struct umstc_softc *sc = (struct umstc_softc *)self; 127 128 task_del(systq, &sc->sc_brightness_task); 129 130 uhidev_close(&sc->sc_hdev); 131 132 return 0; 133 } 134 135 void 136 umstc_intr(struct uhidev *addr, void *buf, u_int len) 137 { 138 struct umstc_softc *sc = (struct umstc_softc *)addr; 139 int i; 140 141 if (!len) 142 return; 143 144 switch (((unsigned char *)buf)[0]) { 145 case HUC_PLAY_PAUSE: 146 /* 147 * It would be nice to pass this through to userland but we'd 148 * need to attach a wskbd 149 */ 150 break; 151 case HUC_MUTE: 152 #if NAUDIO > 0 153 wskbd_set_mixervolume(0, 1); 154 #endif 155 break; 156 case HUC_VOL_INC: 157 #if NAUDIO > 0 158 wskbd_set_mixervolume(1, 1); 159 #endif 160 break; 161 case HUC_VOL_DEC: 162 #if NAUDIO > 0 163 wskbd_set_mixervolume(-1, 1); 164 #endif 165 break; 166 case 0x70: /* brightness down */ 167 #if NWSDISPLAY > 0 168 atomic_sub_int(&sc->sc_brightness_steps, 1); 169 task_add(systq, &sc->sc_brightness_task); 170 #endif 171 break; 172 case 0x6f: /* brightness up */ 173 #if NWSDISPLAY > 0 174 atomic_add_int(&sc->sc_brightness_steps, 1); 175 task_add(systq, &sc->sc_brightness_task); 176 #endif 177 break; 178 case 0: 179 break; 180 default: 181 printf("%s: unhandled key ", sc->sc_hdev.sc_dev.dv_xname); 182 for (i = 0; i < len; i++) 183 printf(" 0x%02x", ((unsigned char *)buf)[i]); 184 printf("\n"); 185 } 186 } 187 188 void 189 umstc_brightness_task(void *arg) 190 { 191 struct umstc_softc *sc = arg; 192 int steps = atomic_swap_uint(&sc->sc_brightness_steps, 0); 193 int dir = 1; 194 195 if (steps < 0) { 196 steps = -steps; 197 dir = -1; 198 } 199 200 while (steps--) 201 wsdisplay_brightness_step(NULL, dir); 202 } 203