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
umstc_match(struct device * parent,void * match,void * aux)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
umstc_attach(struct device * parent,struct device * self,void * aux)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
umstc_detach(struct device * self,int flags)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
umstc_intr(struct uhidev * addr,void * buf,u_int len)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
umstc_brightness_task(void * arg)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