xref: /openbsd-src/sys/dev/usb/umstc.c (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
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