1 /*        $NetBSD: nbpkbd.c,v 1.3 2021/08/07 16:18:53 thorpej Exp $ */
2 /*
3  * Copyright (c) 2011 KIYOHARA Takashi
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
19  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
23  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
24  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25  * POSSIBILITY OF SUCH DAMAGE.
26  */
27 #include <sys/cdefs.h>
28 __KERNEL_RCSID(0, "$NetBSD: nbpkbd.c,v 1.3 2021/08/07 16:18:53 thorpej Exp $");
29 
30 #include <sys/param.h>
31 #include <sys/callout.h>
32 #include <sys/device.h>
33 #include <sys/errno.h>
34 #include <sys/kernel.h>
35 
36 #include <machine/platid.h>
37 #include <machine/platid_mask.h>
38 
39 #include <arm/xscale/pxa2x0_i2c.h>
40 
41 #include <hpcarm/dev/nbppconvar.h>
42 
43 #include <dev/hpc/hpckbdvar.h>
44 
45 #include <dev/i2c/i2cvar.h>
46 
47 #include "locators.h"
48 
49 #ifdef NBPKBD_DEBUG
50 #define DPRINTF(x)  printf x
51 #else
52 #define DPRINTF(x)
53 #endif
54 
55 #define IS_KEY_DOWN(x)        (((x) & 0x80) == 0x00)
56 #define KEY_CODE(x) ((x) & 0x7f)
57 #define KEY_INITIAL_INTERVAL  (500 * hz / 1000)   /* 500ms */
58 #define KEY_REPEAT_INTERVAL   (200 * hz / 1000)   /* 200ms */
59 
60 struct nbpkbd_softc {
61           device_t sc_dev;
62           device_t sc_parent;
63           int sc_tag;
64 
65           struct hpckbd_ic_if sc_if;
66           struct hpckbd_if *sc_hpckbd;
67 
68           int sc_enabled;
69 
70           callout_t  sc_callout;
71           uint8_t sc_last_scancode;
72 };
73 
74 static int nbpkbd_match(device_t, cfdata_t, void *);
75 static void nbpkbd_attach(device_t, device_t, void *);
76 
77 static int nbpkbd_input_establish(void *, struct hpckbd_if *);
78 static int nbpkbd_input(void *, int, char *);
79 static int nbpkbd_poll(void *);
80 
81 static void nbpkbd_key_repeat(void *);
82 
83 CFATTACH_DECL_NEW(nbpkbd, sizeof(struct nbpkbd_softc),
84     nbpkbd_match, nbpkbd_attach, NULL, NULL);
85 
86 
87 /* ARGSUSED */
88 static int
nbpkbd_match(device_t parent,cfdata_t match,void * aux)89 nbpkbd_match(device_t parent, cfdata_t match, void *aux)
90 {
91           struct nbppcon_attach_args *pcon = aux;
92 
93           if (strcmp(pcon->aa_name, match->cf_name) ||
94               pcon->aa_tag == NBPPCONCF_TAG_DEFAULT)
95                     return 0;
96 
97           return 1;
98 }
99 
100 static void
nbpkbd_attach(device_t parent,device_t self,void * aux)101 nbpkbd_attach(device_t parent, device_t self, void *aux)
102 {
103           struct nbpkbd_softc *sc = device_private(self);
104           struct nbppcon_attach_args *pcon = aux;
105           struct hpckbd_attach_args haa;
106 
107           aprint_normal(": NETBOOK PRO Keyboard\n");
108           aprint_naive("\n");
109 
110           sc->sc_dev = self;
111           sc->sc_parent = parent;
112           sc->sc_tag = pcon->aa_tag;
113           sc->sc_if.hii_ctx = sc;
114           sc->sc_if.hii_establish = nbpkbd_input_establish;
115           sc->sc_if.hii_poll = nbpkbd_poll;
116 
117           hpckbd_cnattach(&sc->sc_if);
118 
119           callout_init(&sc->sc_callout, 0);
120           callout_setfunc(&sc->sc_callout, nbpkbd_key_repeat, sc);
121 
122           /* Attach hpckbd */
123           haa.haa_ic = &sc->sc_if;
124           config_found(self, &haa, hpckbd_print, CFARGS_NONE);
125 
126           if (nbppcon_regist_callback(parent, sc->sc_tag, nbpkbd_input, sc) ==
127               NULL)
128                     aprint_error_dev(self, "callback regist failed\n");
129 }
130 
131 
132 static int
nbpkbd_input_establish(void * arg,struct hpckbd_if * kbdif)133 nbpkbd_input_establish(void *arg, struct hpckbd_if *kbdif)
134 {
135           struct nbpkbd_softc *sc = arg;
136 
137           sc->sc_hpckbd = kbdif;
138           sc->sc_enabled = 1;
139 
140           return 0;
141 }
142 
143 static int
nbpkbd_input(void * arg,int buflen,char * buf)144 nbpkbd_input(void *arg, int buflen, char *buf)
145 {
146           struct nbpkbd_softc *sc = arg;
147 
148 #ifdef NBPKBD_DEBUG
149           {
150                     int i;
151 
152                     printf("%s:", __func__);
153                     for (i = 0; i < buflen; i++)
154                               printf(" %02x", buf[i]);
155                     printf("\n");
156           }
157 #endif
158 
159           if (buflen != 2) {
160                     aprint_error_dev(sc->sc_dev, "ugly length %d\n", buflen);
161                     return -1;
162           }
163 
164           hpckbd_input(sc->sc_hpckbd, IS_KEY_DOWN(buf[1]), KEY_CODE(buf[1]));
165 
166           sc->sc_last_scancode = buf[1];
167           if (IS_KEY_DOWN(buf[1]))
168                     callout_schedule(&sc->sc_callout, KEY_INITIAL_INTERVAL);
169           else
170                     callout_stop(&sc->sc_callout);
171 
172           delay(50);          /* XXXX */
173 
174           if (nbppcon_kbd_ready(sc->sc_parent) != 0)
175                     aprint_error_dev(sc->sc_dev,
176                         "%s: kbd-ready failed\n", __func__);
177 
178           return 0;
179 }
180 
181 static int
nbpkbd_poll(void * arg)182 nbpkbd_poll(void *arg)
183 {
184           struct nbpkbd_softc *sc = arg;
185           int rv, s;
186           char buf[2];
187 
188           if (!sc->sc_enabled)
189                     return 0;
190 
191           s = spltty();
192 
193           rv = nbppcon_poll(sc->sc_parent, sc->sc_tag, sizeof(buf), buf);
194 
195           DPRINTF(("%s: received %02x %02x\n",
196               device_xname(sc->sc_dev), buf[0], buf[1]));
197 
198           hpckbd_input(sc->sc_hpckbd, IS_KEY_DOWN(buf[1]), KEY_CODE(buf[1]));
199 
200           delay(50);          /* XXXX */
201 
202           rv = nbppcon_kbd_ready(sc->sc_parent);
203 
204           splx(s);
205 
206           if (rv != 0)
207                     aprint_error_dev(sc->sc_dev,
208                         "%s: kbd-ready failed\n", __func__);
209 
210           return 0;
211 }
212 
213 static void
nbpkbd_key_repeat(void * arg)214 nbpkbd_key_repeat(void *arg)
215 {
216           struct nbpkbd_softc *sc = arg;
217 
218           hpckbd_input(sc->sc_hpckbd, IS_KEY_DOWN(sc->sc_last_scancode),
219               KEY_CODE(sc->sc_last_scancode));
220 
221           if (IS_KEY_DOWN(sc->sc_last_scancode))
222                     callout_schedule(&sc->sc_callout, KEY_REPEAT_INTERVAL);
223 }
224