1/*	$OpenBSD: icc.c,v 1.2 2024/08/17 15:10:00 deraadt Exp $	*/
2
3/*
4 * Copyright (c) 2021 Anton Lindqvist <anton@openbsd.org>
5 * Copyright (c) 2022 Matthieu Herrb <matthieu@openbsd.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include <sys/param.h>
21#include <sys/systm.h>
22#include <sys/device.h>
23
24#include <dev/i2c/i2cvar.h>
25#include <dev/i2c/ihidev.h>
26
27#include <dev/hid/hid.h>
28#include <dev/hid/hidccvar.h>
29
30struct icc_softc {
31	struct ihidev	 sc_hdev;
32	struct hidcc	*sc_cc;
33	int		 sc_keydown;
34};
35
36int	icc_match(struct device *, void *, void *);
37void	icc_attach(struct device *, struct device *, void *);
38int	icc_detach(struct device *, int);
39void	icc_intr(struct ihidev *, void *, u_int);
40
41int	icc_enable(void *, int);
42
43struct cfdriver icc_cd = {
44	NULL, "icc", DV_DULL
45};
46
47const struct cfattach icc_ca = {
48	sizeof(struct icc_softc),
49	icc_match,
50	icc_attach,
51	icc_detach
52};
53
54int
55icc_match(struct device *parent, void *match, void *aux)
56{
57	struct ihidev_attach_arg *iha = aux;
58	void *desc;
59	int size;
60
61	if (iha->reportid == IHIDEV_CLAIM_MULTIPLEID)
62		return IMATCH_NONE;
63
64	ihidev_get_report_desc(iha->parent, &desc, &size);
65	if (hid_report_size(desc, size, hid_input, iha->reportid) == 0)
66		return IMATCH_NONE;
67
68	if (!hidcc_match(desc, size, iha->reportid))
69		return IMATCH_NONE;
70	return IMATCH_IFACECLASS;
71}
72
73void
74icc_attach(struct device *parent, struct device *self, void *aux)
75{
76	struct icc_softc *sc = (struct icc_softc *)self;
77	struct ihidev_attach_arg *iha = aux;
78	void *desc;
79	int repid, size;
80
81	sc->sc_hdev.sc_intr = icc_intr;
82	sc->sc_hdev.sc_parent = iha->parent;
83	sc->sc_hdev.sc_report_id = iha->reportid;
84
85	ihidev_get_report_desc(iha->parent, &desc, &size);
86	repid = iha->reportid;
87	sc->sc_hdev.sc_isize = hid_report_size(desc, size, hid_input, repid);
88	sc->sc_hdev.sc_osize = hid_report_size(desc, size, hid_output, repid);
89	sc->sc_hdev.sc_fsize = hid_report_size(desc, size, hid_feature, repid);
90
91	struct hidcc_attach_arg hca = {
92		.device		= &sc->sc_hdev.sc_idev,
93		.audio_cookie	= iha->iaa->ia_cookie,
94		.desc		= desc,
95		.descsiz	= size,
96		.repid		= repid,
97		.isize		= sc->sc_hdev.sc_isize,
98		.enable		= icc_enable,
99		.arg		= self,
100	};
101	sc->sc_cc = hidcc_attach(&hca);
102}
103
104int
105icc_detach(struct device *self, int flags)
106{
107	struct icc_softc *sc = (struct icc_softc *)self;
108	int error = 0;
109
110	ihidev_close(&sc->sc_hdev);
111	if (sc->sc_cc != NULL)
112		error = hidcc_detach(sc->sc_cc, flags);
113	return error;
114}
115
116void
117icc_intr(struct ihidev *addr, void *data, u_int len)
118{
119	struct icc_softc *sc = (struct icc_softc *)addr;
120
121	if (sc->sc_cc != NULL)
122		hidcc_intr(sc->sc_cc, data, len);
123}
124
125int
126icc_enable(void *v, int on)
127{
128	struct icc_softc *sc = v;
129	int error = 0;
130
131	if (on)
132		error = ihidev_open(&sc->sc_hdev);
133	else
134		ihidev_close(&sc->sc_hdev);
135	return error;
136}
137