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