1*1c60b9acSAndroid Build Coastguard Worker /*
2*1c60b9acSAndroid Build Coastguard Worker * Generic GPIO / irq buttons
3*1c60b9acSAndroid Build Coastguard Worker *
4*1c60b9acSAndroid Build Coastguard Worker * Copyright (C) 2019 - 2020 Andy Green <[email protected]>
5*1c60b9acSAndroid Build Coastguard Worker *
6*1c60b9acSAndroid Build Coastguard Worker * Permission is hereby granted, free of charge, to any person obtaining a copy
7*1c60b9acSAndroid Build Coastguard Worker * of this software and associated documentation files (the "Software"), to
8*1c60b9acSAndroid Build Coastguard Worker * deal in the Software without restriction, including without limitation the
9*1c60b9acSAndroid Build Coastguard Worker * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10*1c60b9acSAndroid Build Coastguard Worker * sell copies of the Software, and to permit persons to whom the Software is
11*1c60b9acSAndroid Build Coastguard Worker * furnished to do so, subject to the following conditions:
12*1c60b9acSAndroid Build Coastguard Worker *
13*1c60b9acSAndroid Build Coastguard Worker * The above copyright notice and this permission notice shall be included in
14*1c60b9acSAndroid Build Coastguard Worker * all copies or substantial portions of the Software.
15*1c60b9acSAndroid Build Coastguard Worker *
16*1c60b9acSAndroid Build Coastguard Worker * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17*1c60b9acSAndroid Build Coastguard Worker * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18*1c60b9acSAndroid Build Coastguard Worker * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19*1c60b9acSAndroid Build Coastguard Worker * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20*1c60b9acSAndroid Build Coastguard Worker * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21*1c60b9acSAndroid Build Coastguard Worker * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22*1c60b9acSAndroid Build Coastguard Worker * IN THE SOFTWARE.
23*1c60b9acSAndroid Build Coastguard Worker */
24*1c60b9acSAndroid Build Coastguard Worker #include "private-lib-core.h"
25*1c60b9acSAndroid Build Coastguard Worker
26*1c60b9acSAndroid Build Coastguard Worker typedef enum lws_button_classify_states {
27*1c60b9acSAndroid Build Coastguard Worker LBCS_IDLE, /* nothing happening */
28*1c60b9acSAndroid Build Coastguard Worker LBCS_MIN_DOWN_QUALIFY,
29*1c60b9acSAndroid Build Coastguard Worker
30*1c60b9acSAndroid Build Coastguard Worker LBCS_ASSESS_DOWN_HOLD,
31*1c60b9acSAndroid Build Coastguard Worker LBCS_UP_SETTLE1,
32*1c60b9acSAndroid Build Coastguard Worker LBCS_WAIT_DOUBLECLICK,
33*1c60b9acSAndroid Build Coastguard Worker LBCS_MIN_DOWN_QUALIFY2,
34*1c60b9acSAndroid Build Coastguard Worker
35*1c60b9acSAndroid Build Coastguard Worker LBCS_WAIT_UP,
36*1c60b9acSAndroid Build Coastguard Worker LBCS_UP_SETTLE2,
37*1c60b9acSAndroid Build Coastguard Worker } lws_button_classify_states_t;
38*1c60b9acSAndroid Build Coastguard Worker
39*1c60b9acSAndroid Build Coastguard Worker /*
40*1c60b9acSAndroid Build Coastguard Worker * This is the opaque, allocated, non-const, dynamic footprint of the
41*1c60b9acSAndroid Build Coastguard Worker * button controller
42*1c60b9acSAndroid Build Coastguard Worker */
43*1c60b9acSAndroid Build Coastguard Worker
44*1c60b9acSAndroid Build Coastguard Worker typedef struct lws_button_state {
45*1c60b9acSAndroid Build Coastguard Worker #if defined(LWS_PLAT_TIMER_TYPE)
46*1c60b9acSAndroid Build Coastguard Worker LWS_PLAT_TIMER_TYPE timer; /* bh timer */
47*1c60b9acSAndroid Build Coastguard Worker LWS_PLAT_TIMER_TYPE timer_mon; /* monitor timer */
48*1c60b9acSAndroid Build Coastguard Worker #endif
49*1c60b9acSAndroid Build Coastguard Worker const lws_button_controller_t *controller;
50*1c60b9acSAndroid Build Coastguard Worker struct lws_context *ctx;
51*1c60b9acSAndroid Build Coastguard Worker short mon_refcount;
52*1c60b9acSAndroid Build Coastguard Worker lws_button_idx_t enable_bitmap;
53*1c60b9acSAndroid Build Coastguard Worker lws_button_idx_t state_bitmap;
54*1c60b9acSAndroid Build Coastguard Worker
55*1c60b9acSAndroid Build Coastguard Worker uint16_t mon_timer_count;
56*1c60b9acSAndroid Build Coastguard Worker /* incremented each time the mon timer cb happens */
57*1c60b9acSAndroid Build Coastguard Worker
58*1c60b9acSAndroid Build Coastguard Worker /* lws_button_each_t per button overallocated after this */
59*1c60b9acSAndroid Build Coastguard Worker } lws_button_state_t;
60*1c60b9acSAndroid Build Coastguard Worker
61*1c60b9acSAndroid Build Coastguard Worker typedef struct lws_button_each {
62*1c60b9acSAndroid Build Coastguard Worker lws_button_state_t *bcs;
63*1c60b9acSAndroid Build Coastguard Worker uint16_t mon_timer_comp;
64*1c60b9acSAndroid Build Coastguard Worker uint16_t mon_timer_repeat;
65*1c60b9acSAndroid Build Coastguard Worker uint8_t state;
66*1c60b9acSAndroid Build Coastguard Worker /**^ lws_button_classify_states_t */
67*1c60b9acSAndroid Build Coastguard Worker uint8_t isr_pending;
68*1c60b9acSAndroid Build Coastguard Worker } lws_button_each_t;
69*1c60b9acSAndroid Build Coastguard Worker
70*1c60b9acSAndroid Build Coastguard Worker #if defined(LWS_PLAT_TIMER_START)
71*1c60b9acSAndroid Build Coastguard Worker static const lws_button_regime_t default_regime = {
72*1c60b9acSAndroid Build Coastguard Worker .ms_min_down = 20,
73*1c60b9acSAndroid Build Coastguard Worker .ms_min_down_longpress = 300,
74*1c60b9acSAndroid Build Coastguard Worker .ms_up_settle = 20,
75*1c60b9acSAndroid Build Coastguard Worker .ms_doubleclick_grace = 120,
76*1c60b9acSAndroid Build Coastguard Worker .flags = LWSBTNRGMFLAG_CLASSIFY_DOUBLECLICK
77*1c60b9acSAndroid Build Coastguard Worker };
78*1c60b9acSAndroid Build Coastguard Worker #endif
79*1c60b9acSAndroid Build Coastguard Worker
80*1c60b9acSAndroid Build Coastguard Worker
81*1c60b9acSAndroid Build Coastguard Worker /*
82*1c60b9acSAndroid Build Coastguard Worker * This is happening in interrupt context, we have to schedule a bottom half to
83*1c60b9acSAndroid Build Coastguard Worker * do the foreground lws_smd queueing, using, eg, a platform timer.
84*1c60b9acSAndroid Build Coastguard Worker *
85*1c60b9acSAndroid Build Coastguard Worker * All the buttons point here and use one timer per button controller. An
86*1c60b9acSAndroid Build Coastguard Worker * interrupt here means, "something happened to one or more buttons"
87*1c60b9acSAndroid Build Coastguard Worker */
88*1c60b9acSAndroid Build Coastguard Worker #if defined(LWS_PLAT_TIMER_START)
89*1c60b9acSAndroid Build Coastguard Worker void
lws_button_irq_cb_t(void * arg)90*1c60b9acSAndroid Build Coastguard Worker lws_button_irq_cb_t(void *arg)
91*1c60b9acSAndroid Build Coastguard Worker {
92*1c60b9acSAndroid Build Coastguard Worker lws_button_each_t *each = (lws_button_each_t *)arg;
93*1c60b9acSAndroid Build Coastguard Worker
94*1c60b9acSAndroid Build Coastguard Worker each->isr_pending = 1;
95*1c60b9acSAndroid Build Coastguard Worker LWS_PLAT_TIMER_START(each->bcs->timer);
96*1c60b9acSAndroid Build Coastguard Worker }
97*1c60b9acSAndroid Build Coastguard Worker #endif
98*1c60b9acSAndroid Build Coastguard Worker
99*1c60b9acSAndroid Build Coastguard Worker /*
100*1c60b9acSAndroid Build Coastguard Worker * This is the bottom-half scheduled via a timer set in the ISR. From here we
101*1c60b9acSAndroid Build Coastguard Worker * are allowed to hold mutexes etc. We are coming here because any button
102*1c60b9acSAndroid Build Coastguard Worker * interrupt arrived, we have to run another timer that tries to put whatever is
103*1c60b9acSAndroid Build Coastguard Worker * observed on any active button into context and either discard it or arrive at
104*1c60b9acSAndroid Build Coastguard Worker * a definitive event classification.
105*1c60b9acSAndroid Build Coastguard Worker */
106*1c60b9acSAndroid Build Coastguard Worker
107*1c60b9acSAndroid Build Coastguard Worker #if defined(LWS_PLAT_TIMER_CB)
LWS_PLAT_TIMER_CB(lws_button_bh,th)108*1c60b9acSAndroid Build Coastguard Worker static LWS_PLAT_TIMER_CB(lws_button_bh, th)
109*1c60b9acSAndroid Build Coastguard Worker {
110*1c60b9acSAndroid Build Coastguard Worker lws_button_state_t *bcs = LWS_PLAT_TIMER_CB_GET_OPAQUE(th);
111*1c60b9acSAndroid Build Coastguard Worker lws_button_each_t *each = (lws_button_each_t *)&bcs[1];
112*1c60b9acSAndroid Build Coastguard Worker const lws_button_controller_t *bc = bcs->controller;
113*1c60b9acSAndroid Build Coastguard Worker size_t n;
114*1c60b9acSAndroid Build Coastguard Worker
115*1c60b9acSAndroid Build Coastguard Worker /*
116*1c60b9acSAndroid Build Coastguard Worker * The ISR and bottom-half is shared by all the buttons. Each gpio
117*1c60b9acSAndroid Build Coastguard Worker * IRQ has an individual opaque ptr pointing to the corresponding
118*1c60b9acSAndroid Build Coastguard Worker * button's dynamic lws_button_each_t, the ISR marks the button's
119*1c60b9acSAndroid Build Coastguard Worker * each->isr_pending and schedules this bottom half.
120*1c60b9acSAndroid Build Coastguard Worker *
121*1c60b9acSAndroid Build Coastguard Worker * So now the bh timer has fired and something to do, we need to go
122*1c60b9acSAndroid Build Coastguard Worker * through all the buttons that have isr_pending set and service their
123*1c60b9acSAndroid Build Coastguard Worker * state. Intermediate states should start / bump the refcount on the
124*1c60b9acSAndroid Build Coastguard Worker * mon timer. That's refcounted so it only runs when a button down.
125*1c60b9acSAndroid Build Coastguard Worker */
126*1c60b9acSAndroid Build Coastguard Worker
127*1c60b9acSAndroid Build Coastguard Worker for (n = 0; n < bc->count_buttons; n++) {
128*1c60b9acSAndroid Build Coastguard Worker
129*1c60b9acSAndroid Build Coastguard Worker if (!each[n].isr_pending)
130*1c60b9acSAndroid Build Coastguard Worker continue;
131*1c60b9acSAndroid Build Coastguard Worker
132*1c60b9acSAndroid Build Coastguard Worker /*
133*1c60b9acSAndroid Build Coastguard Worker * Hide what we're about to do from the delicate eyes of the
134*1c60b9acSAndroid Build Coastguard Worker * IRQ controller...
135*1c60b9acSAndroid Build Coastguard Worker */
136*1c60b9acSAndroid Build Coastguard Worker
137*1c60b9acSAndroid Build Coastguard Worker bc->gpio_ops->irq_mode(bc->button_map[n].gpio,
138*1c60b9acSAndroid Build Coastguard Worker LWSGGPIO_IRQ_NONE, NULL, NULL);
139*1c60b9acSAndroid Build Coastguard Worker
140*1c60b9acSAndroid Build Coastguard Worker each[n].isr_pending = 0;
141*1c60b9acSAndroid Build Coastguard Worker
142*1c60b9acSAndroid Build Coastguard Worker /*
143*1c60b9acSAndroid Build Coastguard Worker * Force the network around the switch to the
144*1c60b9acSAndroid Build Coastguard Worker * active level briefly
145*1c60b9acSAndroid Build Coastguard Worker */
146*1c60b9acSAndroid Build Coastguard Worker
147*1c60b9acSAndroid Build Coastguard Worker bc->gpio_ops->set(bc->button_map[n].gpio,
148*1c60b9acSAndroid Build Coastguard Worker !!(bc->active_state_bitmap & (1 << n)));
149*1c60b9acSAndroid Build Coastguard Worker bc->gpio_ops->mode(bc->button_map[n].gpio, LWSGGPIO_FL_WRITE);
150*1c60b9acSAndroid Build Coastguard Worker
151*1c60b9acSAndroid Build Coastguard Worker if (each[n].state == LBCS_IDLE) {
152*1c60b9acSAndroid Build Coastguard Worker /*
153*1c60b9acSAndroid Build Coastguard Worker * If this is the first sign something happening on this
154*1c60b9acSAndroid Build Coastguard Worker * button, make sure the monitor timer is running to
155*1c60b9acSAndroid Build Coastguard Worker * classify its response over time
156*1c60b9acSAndroid Build Coastguard Worker */
157*1c60b9acSAndroid Build Coastguard Worker
158*1c60b9acSAndroid Build Coastguard Worker each[n].state = LBCS_MIN_DOWN_QUALIFY;
159*1c60b9acSAndroid Build Coastguard Worker each[n].mon_timer_comp = bcs->mon_timer_count;
160*1c60b9acSAndroid Build Coastguard Worker
161*1c60b9acSAndroid Build Coastguard Worker if (!bcs->mon_refcount++) {
162*1c60b9acSAndroid Build Coastguard Worker #if defined(LWS_PLAT_TIMER_START)
163*1c60b9acSAndroid Build Coastguard Worker LWS_PLAT_TIMER_START(bcs->timer_mon);
164*1c60b9acSAndroid Build Coastguard Worker #endif
165*1c60b9acSAndroid Build Coastguard Worker }
166*1c60b9acSAndroid Build Coastguard Worker }
167*1c60b9acSAndroid Build Coastguard Worker
168*1c60b9acSAndroid Build Coastguard Worker /*
169*1c60b9acSAndroid Build Coastguard Worker * Just for a us or two inbetween here, we're driving it to the
170*1c60b9acSAndroid Build Coastguard Worker * level we were informed by the interrupt it had enetered, to
171*1c60b9acSAndroid Build Coastguard Worker * force to charge on the actual and parasitic network around
172*1c60b9acSAndroid Build Coastguard Worker * the switch to a deterministic-ish state.
173*1c60b9acSAndroid Build Coastguard Worker *
174*1c60b9acSAndroid Build Coastguard Worker * If the switch remains in that state, well, it makes no
175*1c60b9acSAndroid Build Coastguard Worker * difference; if it was a pre-contact and the charge on the
176*1c60b9acSAndroid Build Coastguard Worker * network was left indeterminate, this will dispose it to act
177*1c60b9acSAndroid Build Coastguard Worker * consistently in the short term until the pullup / pulldown
178*1c60b9acSAndroid Build Coastguard Worker * has time to act on it or the switch comes and forces the
179*1c60b9acSAndroid Build Coastguard Worker * network charge state itself.
180*1c60b9acSAndroid Build Coastguard Worker */
181*1c60b9acSAndroid Build Coastguard Worker bc->gpio_ops->mode(bc->button_map[n].gpio, LWSGGPIO_FL_READ);
182*1c60b9acSAndroid Build Coastguard Worker
183*1c60b9acSAndroid Build Coastguard Worker /*
184*1c60b9acSAndroid Build Coastguard Worker * We could do a better job manipulating the irq mode according
185*1c60b9acSAndroid Build Coastguard Worker * to the switch state. But if an interrupt comes and we have
186*1c60b9acSAndroid Build Coastguard Worker * done that, we can't tell if it's from before or after the
187*1c60b9acSAndroid Build Coastguard Worker * mode change... ie, we don't know what the interrupt was
188*1c60b9acSAndroid Build Coastguard Worker * telling us. We can't trust the gpio state if we read it now
189*1c60b9acSAndroid Build Coastguard Worker * to be related to what the irq from some time before was
190*1c60b9acSAndroid Build Coastguard Worker * trying to tell us. So always set it back to the same mode
191*1c60b9acSAndroid Build Coastguard Worker * and accept the limitation.
192*1c60b9acSAndroid Build Coastguard Worker */
193*1c60b9acSAndroid Build Coastguard Worker
194*1c60b9acSAndroid Build Coastguard Worker bc->gpio_ops->irq_mode(bc->button_map[n].gpio,
195*1c60b9acSAndroid Build Coastguard Worker bc->active_state_bitmap & (1 << n) ?
196*1c60b9acSAndroid Build Coastguard Worker LWSGGPIO_IRQ_RISING :
197*1c60b9acSAndroid Build Coastguard Worker LWSGGPIO_IRQ_FALLING,
198*1c60b9acSAndroid Build Coastguard Worker lws_button_irq_cb_t, &each[n]);
199*1c60b9acSAndroid Build Coastguard Worker }
200*1c60b9acSAndroid Build Coastguard Worker }
201*1c60b9acSAndroid Build Coastguard Worker #endif
202*1c60b9acSAndroid Build Coastguard Worker
203*1c60b9acSAndroid Build Coastguard Worker #if defined(LWS_PLAT_TIMER_CB)
LWS_PLAT_TIMER_CB(lws_button_mon,th)204*1c60b9acSAndroid Build Coastguard Worker static LWS_PLAT_TIMER_CB(lws_button_mon, th)
205*1c60b9acSAndroid Build Coastguard Worker {
206*1c60b9acSAndroid Build Coastguard Worker lws_button_state_t *bcs = LWS_PLAT_TIMER_CB_GET_OPAQUE(th);
207*1c60b9acSAndroid Build Coastguard Worker lws_button_each_t *each = (lws_button_each_t *)&bcs[1];
208*1c60b9acSAndroid Build Coastguard Worker const lws_button_controller_t *bc = bcs->controller;
209*1c60b9acSAndroid Build Coastguard Worker const lws_button_regime_t *regime;
210*1c60b9acSAndroid Build Coastguard Worker const char *event_name;
211*1c60b9acSAndroid Build Coastguard Worker int comp_age_ms;
212*1c60b9acSAndroid Build Coastguard Worker char active;
213*1c60b9acSAndroid Build Coastguard Worker size_t n;
214*1c60b9acSAndroid Build Coastguard Worker
215*1c60b9acSAndroid Build Coastguard Worker bcs->mon_timer_count++;
216*1c60b9acSAndroid Build Coastguard Worker
217*1c60b9acSAndroid Build Coastguard Worker for (n = 0; n < bc->count_buttons; n++) {
218*1c60b9acSAndroid Build Coastguard Worker
219*1c60b9acSAndroid Build Coastguard Worker if (each->state == LBCS_IDLE) {
220*1c60b9acSAndroid Build Coastguard Worker each++;
221*1c60b9acSAndroid Build Coastguard Worker continue;
222*1c60b9acSAndroid Build Coastguard Worker }
223*1c60b9acSAndroid Build Coastguard Worker
224*1c60b9acSAndroid Build Coastguard Worker if (bc->button_map[n].regime)
225*1c60b9acSAndroid Build Coastguard Worker regime = bc->button_map[n].regime;
226*1c60b9acSAndroid Build Coastguard Worker else
227*1c60b9acSAndroid Build Coastguard Worker regime = &default_regime;
228*1c60b9acSAndroid Build Coastguard Worker
229*1c60b9acSAndroid Build Coastguard Worker comp_age_ms = (bcs->mon_timer_count - each->mon_timer_comp) *
230*1c60b9acSAndroid Build Coastguard Worker LWS_BUTTON_MON_TIMER_MS;
231*1c60b9acSAndroid Build Coastguard Worker
232*1c60b9acSAndroid Build Coastguard Worker active = bc->gpio_ops->read(bc->button_map[n].gpio) ^
233*1c60b9acSAndroid Build Coastguard Worker (!(bc->active_state_bitmap & (1 << n)));
234*1c60b9acSAndroid Build Coastguard Worker
235*1c60b9acSAndroid Build Coastguard Worker // lwsl_notice("%d\n", each->state);
236*1c60b9acSAndroid Build Coastguard Worker
237*1c60b9acSAndroid Build Coastguard Worker switch (each->state) {
238*1c60b9acSAndroid Build Coastguard Worker case LBCS_MIN_DOWN_QUALIFY:
239*1c60b9acSAndroid Build Coastguard Worker /*
240*1c60b9acSAndroid Build Coastguard Worker * We're trying to figure out if the initial down event
241*1c60b9acSAndroid Build Coastguard Worker * is a glitch, or if it meets the criteria for being
242*1c60b9acSAndroid Build Coastguard Worker * treated as the definitive start of some kind of click
243*1c60b9acSAndroid Build Coastguard Worker * action. To get past this, he has to be solidly down
244*1c60b9acSAndroid Build Coastguard Worker * for the time mentioned in the applied regime (at
245*1c60b9acSAndroid Build Coastguard Worker * least when we sample it).
246*1c60b9acSAndroid Build Coastguard Worker *
247*1c60b9acSAndroid Build Coastguard Worker * Significant bounce at the start will abort this try,
248*1c60b9acSAndroid Build Coastguard Worker * but if it's really down there will be a subsequent
249*1c60b9acSAndroid Build Coastguard Worker * solid down period... it will simply restart this flow
250*1c60b9acSAndroid Build Coastguard Worker * from a new interrupt and pass the filter then.
251*1c60b9acSAndroid Build Coastguard Worker *
252*1c60b9acSAndroid Build Coastguard Worker * The "brief drive on edge" strategy considerably
253*1c60b9acSAndroid Build Coastguard Worker * reduces inconsistencies here. But physical bounce
254*1c60b9acSAndroid Build Coastguard Worker * will continue to be observed.
255*1c60b9acSAndroid Build Coastguard Worker */
256*1c60b9acSAndroid Build Coastguard Worker
257*1c60b9acSAndroid Build Coastguard Worker if (!active) {
258*1c60b9acSAndroid Build Coastguard Worker /* We ignore stuff for a bit after discard */
259*1c60b9acSAndroid Build Coastguard Worker each->mon_timer_comp = bcs->mon_timer_count;
260*1c60b9acSAndroid Build Coastguard Worker each->state = LBCS_UP_SETTLE2;
261*1c60b9acSAndroid Build Coastguard Worker break;
262*1c60b9acSAndroid Build Coastguard Worker }
263*1c60b9acSAndroid Build Coastguard Worker
264*1c60b9acSAndroid Build Coastguard Worker if (comp_age_ms >= regime->ms_min_down) {
265*1c60b9acSAndroid Build Coastguard Worker
266*1c60b9acSAndroid Build Coastguard Worker /* We made it through the initial regime filter,
267*1c60b9acSAndroid Build Coastguard Worker * the next step is wait and see if this down
268*1c60b9acSAndroid Build Coastguard Worker * event evolves into a single/double click or
269*1c60b9acSAndroid Build Coastguard Worker * we can call it as a long-click
270*1c60b9acSAndroid Build Coastguard Worker */
271*1c60b9acSAndroid Build Coastguard Worker
272*1c60b9acSAndroid Build Coastguard Worker each->mon_timer_repeat = bcs->mon_timer_count;
273*1c60b9acSAndroid Build Coastguard Worker each->state = LBCS_ASSESS_DOWN_HOLD;
274*1c60b9acSAndroid Build Coastguard Worker event_name = "down";
275*1c60b9acSAndroid Build Coastguard Worker goto emit;
276*1c60b9acSAndroid Build Coastguard Worker }
277*1c60b9acSAndroid Build Coastguard Worker break;
278*1c60b9acSAndroid Build Coastguard Worker
279*1c60b9acSAndroid Build Coastguard Worker case LBCS_ASSESS_DOWN_HOLD:
280*1c60b9acSAndroid Build Coastguard Worker
281*1c60b9acSAndroid Build Coastguard Worker /*
282*1c60b9acSAndroid Build Coastguard Worker * How long is he going to hold it? If he holds it
283*1c60b9acSAndroid Build Coastguard Worker * past the long-click threshold, we can call it as a
284*1c60b9acSAndroid Build Coastguard Worker * long-click and do the up processing afterwards.
285*1c60b9acSAndroid Build Coastguard Worker */
286*1c60b9acSAndroid Build Coastguard Worker if (comp_age_ms >= regime->ms_min_down_longpress) {
287*1c60b9acSAndroid Build Coastguard Worker /* call it as a longclick */
288*1c60b9acSAndroid Build Coastguard Worker event_name = "longclick";
289*1c60b9acSAndroid Build Coastguard Worker each->state = LBCS_WAIT_UP;
290*1c60b9acSAndroid Build Coastguard Worker goto emit;
291*1c60b9acSAndroid Build Coastguard Worker }
292*1c60b9acSAndroid Build Coastguard Worker
293*1c60b9acSAndroid Build Coastguard Worker if (!active) {
294*1c60b9acSAndroid Build Coastguard Worker /*
295*1c60b9acSAndroid Build Coastguard Worker * He didn't hold it past the long-click
296*1c60b9acSAndroid Build Coastguard Worker * threshold... we could end up classifying it
297*1c60b9acSAndroid Build Coastguard Worker * as either a click or a double-click then.
298*1c60b9acSAndroid Build Coastguard Worker *
299*1c60b9acSAndroid Build Coastguard Worker * If double-clicks are not allowed to be
300*1c60b9acSAndroid Build Coastguard Worker * classified, then we can already classify it
301*1c60b9acSAndroid Build Coastguard Worker * as a single-click.
302*1c60b9acSAndroid Build Coastguard Worker */
303*1c60b9acSAndroid Build Coastguard Worker if (!(regime->flags &
304*1c60b9acSAndroid Build Coastguard Worker LWSBTNRGMFLAG_CLASSIFY_DOUBLECLICK))
305*1c60b9acSAndroid Build Coastguard Worker goto classify_single;
306*1c60b9acSAndroid Build Coastguard Worker
307*1c60b9acSAndroid Build Coastguard Worker /*
308*1c60b9acSAndroid Build Coastguard Worker * Just wait for the up settle time then start
309*1c60b9acSAndroid Build Coastguard Worker * looking for a second down.
310*1c60b9acSAndroid Build Coastguard Worker */
311*1c60b9acSAndroid Build Coastguard Worker each->mon_timer_comp = bcs->mon_timer_count;
312*1c60b9acSAndroid Build Coastguard Worker each->state = LBCS_UP_SETTLE1;
313*1c60b9acSAndroid Build Coastguard Worker event_name = "up";
314*1c60b9acSAndroid Build Coastguard Worker goto emit;
315*1c60b9acSAndroid Build Coastguard Worker }
316*1c60b9acSAndroid Build Coastguard Worker
317*1c60b9acSAndroid Build Coastguard Worker goto stilldown;
318*1c60b9acSAndroid Build Coastguard Worker
319*1c60b9acSAndroid Build Coastguard Worker case LBCS_UP_SETTLE1:
320*1c60b9acSAndroid Build Coastguard Worker if (comp_age_ms > regime->ms_up_settle)
321*1c60b9acSAndroid Build Coastguard Worker /*
322*1c60b9acSAndroid Build Coastguard Worker * Just block anything for the up settle time
323*1c60b9acSAndroid Build Coastguard Worker */
324*1c60b9acSAndroid Build Coastguard Worker each->state = LBCS_WAIT_DOUBLECLICK;
325*1c60b9acSAndroid Build Coastguard Worker break;
326*1c60b9acSAndroid Build Coastguard Worker
327*1c60b9acSAndroid Build Coastguard Worker case LBCS_WAIT_DOUBLECLICK:
328*1c60b9acSAndroid Build Coastguard Worker if (active) {
329*1c60b9acSAndroid Build Coastguard Worker /*
330*1c60b9acSAndroid Build Coastguard Worker * He has gone down again inside the regime's
331*1c60b9acSAndroid Build Coastguard Worker * doubleclick grace period... he's going down
332*1c60b9acSAndroid Build Coastguard Worker * the double-click path
333*1c60b9acSAndroid Build Coastguard Worker */
334*1c60b9acSAndroid Build Coastguard Worker each->mon_timer_comp = bcs->mon_timer_count;
335*1c60b9acSAndroid Build Coastguard Worker each->state = LBCS_MIN_DOWN_QUALIFY2;
336*1c60b9acSAndroid Build Coastguard Worker break;
337*1c60b9acSAndroid Build Coastguard Worker }
338*1c60b9acSAndroid Build Coastguard Worker
339*1c60b9acSAndroid Build Coastguard Worker if (comp_age_ms >= regime->ms_doubleclick_grace) {
340*1c60b9acSAndroid Build Coastguard Worker /*
341*1c60b9acSAndroid Build Coastguard Worker * The grace period expired, the second click
342*1c60b9acSAndroid Build Coastguard Worker * was either not forthcoming at all, or coming
343*1c60b9acSAndroid Build Coastguard Worker * quick enough to count: we classify it as a
344*1c60b9acSAndroid Build Coastguard Worker * single-click
345*1c60b9acSAndroid Build Coastguard Worker */
346*1c60b9acSAndroid Build Coastguard Worker
347*1c60b9acSAndroid Build Coastguard Worker goto classify_single;
348*1c60b9acSAndroid Build Coastguard Worker }
349*1c60b9acSAndroid Build Coastguard Worker break;
350*1c60b9acSAndroid Build Coastguard Worker
351*1c60b9acSAndroid Build Coastguard Worker case LBCS_MIN_DOWN_QUALIFY2:
352*1c60b9acSAndroid Build Coastguard Worker if (!active) {
353*1c60b9acSAndroid Build Coastguard Worker
354*1c60b9acSAndroid Build Coastguard Worker /*
355*1c60b9acSAndroid Build Coastguard Worker * He went up again too quickly, classify it
356*1c60b9acSAndroid Build Coastguard Worker * as a single-click. It could be bounce in
357*1c60b9acSAndroid Build Coastguard Worker * which case you might want to increase the
358*1c60b9acSAndroid Build Coastguard Worker * ms_up_settle in the regime
359*1c60b9acSAndroid Build Coastguard Worker */
360*1c60b9acSAndroid Build Coastguard Worker classify_single:
361*1c60b9acSAndroid Build Coastguard Worker event_name = "click";
362*1c60b9acSAndroid Build Coastguard Worker each->mon_timer_comp = bcs->mon_timer_count;
363*1c60b9acSAndroid Build Coastguard Worker each->state = LBCS_UP_SETTLE2;
364*1c60b9acSAndroid Build Coastguard Worker goto emit;
365*1c60b9acSAndroid Build Coastguard Worker }
366*1c60b9acSAndroid Build Coastguard Worker
367*1c60b9acSAndroid Build Coastguard Worker if (comp_age_ms == regime->ms_min_down) {
368*1c60b9acSAndroid Build Coastguard Worker event_name = "down";
369*1c60b9acSAndroid Build Coastguard Worker goto emit;
370*1c60b9acSAndroid Build Coastguard Worker }
371*1c60b9acSAndroid Build Coastguard Worker
372*1c60b9acSAndroid Build Coastguard Worker if (comp_age_ms > regime->ms_min_down) {
373*1c60b9acSAndroid Build Coastguard Worker /*
374*1c60b9acSAndroid Build Coastguard Worker * It's a double-click
375*1c60b9acSAndroid Build Coastguard Worker */
376*1c60b9acSAndroid Build Coastguard Worker event_name = "doubleclick";
377*1c60b9acSAndroid Build Coastguard Worker each->state = LBCS_WAIT_UP;
378*1c60b9acSAndroid Build Coastguard Worker goto emit;
379*1c60b9acSAndroid Build Coastguard Worker }
380*1c60b9acSAndroid Build Coastguard Worker break;
381*1c60b9acSAndroid Build Coastguard Worker
382*1c60b9acSAndroid Build Coastguard Worker case LBCS_WAIT_UP:
383*1c60b9acSAndroid Build Coastguard Worker if (!active) {
384*1c60b9acSAndroid Build Coastguard Worker /*
385*1c60b9acSAndroid Build Coastguard Worker * He has stopped pressing it
386*1c60b9acSAndroid Build Coastguard Worker */
387*1c60b9acSAndroid Build Coastguard Worker each->mon_timer_comp = bcs->mon_timer_count;
388*1c60b9acSAndroid Build Coastguard Worker each->state = LBCS_UP_SETTLE2;
389*1c60b9acSAndroid Build Coastguard Worker event_name = "up";
390*1c60b9acSAndroid Build Coastguard Worker goto emit;
391*1c60b9acSAndroid Build Coastguard Worker }
392*1c60b9acSAndroid Build Coastguard Worker stilldown:
393*1c60b9acSAndroid Build Coastguard Worker if (regime->ms_repeat_down &&
394*1c60b9acSAndroid Build Coastguard Worker (bcs->mon_timer_count - each->mon_timer_repeat) *
395*1c60b9acSAndroid Build Coastguard Worker LWS_BUTTON_MON_TIMER_MS > regime->ms_repeat_down) {
396*1c60b9acSAndroid Build Coastguard Worker each->mon_timer_repeat = bcs->mon_timer_count;
397*1c60b9acSAndroid Build Coastguard Worker event_name = "stilldown";
398*1c60b9acSAndroid Build Coastguard Worker goto emit;
399*1c60b9acSAndroid Build Coastguard Worker }
400*1c60b9acSAndroid Build Coastguard Worker break;
401*1c60b9acSAndroid Build Coastguard Worker
402*1c60b9acSAndroid Build Coastguard Worker case LBCS_UP_SETTLE2:
403*1c60b9acSAndroid Build Coastguard Worker if (comp_age_ms < regime->ms_up_settle)
404*1c60b9acSAndroid Build Coastguard Worker break;
405*1c60b9acSAndroid Build Coastguard Worker
406*1c60b9acSAndroid Build Coastguard Worker each->state = LBCS_IDLE;
407*1c60b9acSAndroid Build Coastguard Worker if (!(--bcs->mon_refcount)) {
408*1c60b9acSAndroid Build Coastguard Worker #if defined(LWS_PLAT_TIMER_STOP)
409*1c60b9acSAndroid Build Coastguard Worker LWS_PLAT_TIMER_STOP(bcs->timer_mon);
410*1c60b9acSAndroid Build Coastguard Worker #endif
411*1c60b9acSAndroid Build Coastguard Worker }
412*1c60b9acSAndroid Build Coastguard Worker }
413*1c60b9acSAndroid Build Coastguard Worker
414*1c60b9acSAndroid Build Coastguard Worker each++;
415*1c60b9acSAndroid Build Coastguard Worker continue;
416*1c60b9acSAndroid Build Coastguard Worker
417*1c60b9acSAndroid Build Coastguard Worker emit:
418*1c60b9acSAndroid Build Coastguard Worker lws_smd_msg_printf(bcs->ctx, LWSSMDCL_INTERACTION,
419*1c60b9acSAndroid Build Coastguard Worker "{\"type\":\"button\","
420*1c60b9acSAndroid Build Coastguard Worker "\"src\":\"%s/%s\",\"event\":\"%s\"}",
421*1c60b9acSAndroid Build Coastguard Worker bc->smd_bc_name,
422*1c60b9acSAndroid Build Coastguard Worker bc->button_map[n].smd_interaction_name,
423*1c60b9acSAndroid Build Coastguard Worker event_name);
424*1c60b9acSAndroid Build Coastguard Worker
425*1c60b9acSAndroid Build Coastguard Worker each++;
426*1c60b9acSAndroid Build Coastguard Worker }
427*1c60b9acSAndroid Build Coastguard Worker }
428*1c60b9acSAndroid Build Coastguard Worker #endif
429*1c60b9acSAndroid Build Coastguard Worker
430*1c60b9acSAndroid Build Coastguard Worker struct lws_button_state *
lws_button_controller_create(struct lws_context * ctx,const lws_button_controller_t * controller)431*1c60b9acSAndroid Build Coastguard Worker lws_button_controller_create(struct lws_context *ctx,
432*1c60b9acSAndroid Build Coastguard Worker const lws_button_controller_t *controller)
433*1c60b9acSAndroid Build Coastguard Worker {
434*1c60b9acSAndroid Build Coastguard Worker lws_button_state_t *bcs = lws_zalloc(sizeof(lws_button_state_t) +
435*1c60b9acSAndroid Build Coastguard Worker (controller->count_buttons * sizeof(lws_button_each_t)),
436*1c60b9acSAndroid Build Coastguard Worker __func__);
437*1c60b9acSAndroid Build Coastguard Worker lws_button_each_t *each = (lws_button_each_t *)&bcs[1];
438*1c60b9acSAndroid Build Coastguard Worker size_t n;
439*1c60b9acSAndroid Build Coastguard Worker
440*1c60b9acSAndroid Build Coastguard Worker if (!bcs)
441*1c60b9acSAndroid Build Coastguard Worker return NULL;
442*1c60b9acSAndroid Build Coastguard Worker
443*1c60b9acSAndroid Build Coastguard Worker bcs->controller = controller;
444*1c60b9acSAndroid Build Coastguard Worker bcs->ctx = ctx;
445*1c60b9acSAndroid Build Coastguard Worker
446*1c60b9acSAndroid Build Coastguard Worker for (n = 0; n < controller->count_buttons; n++)
447*1c60b9acSAndroid Build Coastguard Worker each[n].bcs = bcs;
448*1c60b9acSAndroid Build Coastguard Worker
449*1c60b9acSAndroid Build Coastguard Worker #if defined(LWS_PLAT_TIMER_CREATE)
450*1c60b9acSAndroid Build Coastguard Worker /* this only runs inbetween a gpio ISR and the bottom half */
451*1c60b9acSAndroid Build Coastguard Worker bcs->timer = LWS_PLAT_TIMER_CREATE("bcst",
452*1c60b9acSAndroid Build Coastguard Worker 1, 0, bcs, (TimerCallbackFunction_t)lws_button_bh);
453*1c60b9acSAndroid Build Coastguard Worker if (!bcs->timer)
454*1c60b9acSAndroid Build Coastguard Worker return NULL;
455*1c60b9acSAndroid Build Coastguard Worker
456*1c60b9acSAndroid Build Coastguard Worker /* this only runs when a button activity is being classified */
457*1c60b9acSAndroid Build Coastguard Worker bcs->timer_mon = LWS_PLAT_TIMER_CREATE("bcmon", LWS_BUTTON_MON_TIMER_MS,
458*1c60b9acSAndroid Build Coastguard Worker 1, bcs, (TimerCallbackFunction_t)
459*1c60b9acSAndroid Build Coastguard Worker lws_button_mon);
460*1c60b9acSAndroid Build Coastguard Worker if (!bcs->timer_mon)
461*1c60b9acSAndroid Build Coastguard Worker return NULL;
462*1c60b9acSAndroid Build Coastguard Worker #endif
463*1c60b9acSAndroid Build Coastguard Worker
464*1c60b9acSAndroid Build Coastguard Worker return bcs;
465*1c60b9acSAndroid Build Coastguard Worker }
466*1c60b9acSAndroid Build Coastguard Worker
467*1c60b9acSAndroid Build Coastguard Worker void
lws_button_controller_destroy(struct lws_button_state * bcs)468*1c60b9acSAndroid Build Coastguard Worker lws_button_controller_destroy(struct lws_button_state *bcs)
469*1c60b9acSAndroid Build Coastguard Worker {
470*1c60b9acSAndroid Build Coastguard Worker /* disable them all */
471*1c60b9acSAndroid Build Coastguard Worker lws_button_enable(bcs, 0, 0);
472*1c60b9acSAndroid Build Coastguard Worker
473*1c60b9acSAndroid Build Coastguard Worker #if defined(LWS_PLAT_TIMER_DELETE)
474*1c60b9acSAndroid Build Coastguard Worker LWS_PLAT_TIMER_DELETE(&bcs->timer);
475*1c60b9acSAndroid Build Coastguard Worker LWS_PLAT_TIMER_DELETE(&bcs->timer_mon);
476*1c60b9acSAndroid Build Coastguard Worker #endif
477*1c60b9acSAndroid Build Coastguard Worker
478*1c60b9acSAndroid Build Coastguard Worker lws_free(bcs);
479*1c60b9acSAndroid Build Coastguard Worker }
480*1c60b9acSAndroid Build Coastguard Worker
481*1c60b9acSAndroid Build Coastguard Worker lws_button_idx_t
lws_button_get_bit(struct lws_button_state * bcs,const char * name)482*1c60b9acSAndroid Build Coastguard Worker lws_button_get_bit(struct lws_button_state *bcs, const char *name)
483*1c60b9acSAndroid Build Coastguard Worker {
484*1c60b9acSAndroid Build Coastguard Worker const lws_button_controller_t *bc = bcs->controller;
485*1c60b9acSAndroid Build Coastguard Worker int n;
486*1c60b9acSAndroid Build Coastguard Worker
487*1c60b9acSAndroid Build Coastguard Worker for (n = 0; n < bc->count_buttons; n++)
488*1c60b9acSAndroid Build Coastguard Worker if (!strcmp(name, bc->button_map[n].smd_interaction_name))
489*1c60b9acSAndroid Build Coastguard Worker return 1 << n;
490*1c60b9acSAndroid Build Coastguard Worker
491*1c60b9acSAndroid Build Coastguard Worker return 0; /* not found */
492*1c60b9acSAndroid Build Coastguard Worker }
493*1c60b9acSAndroid Build Coastguard Worker
494*1c60b9acSAndroid Build Coastguard Worker void
lws_button_enable(lws_button_state_t * bcs,lws_button_idx_t _reset,lws_button_idx_t _set)495*1c60b9acSAndroid Build Coastguard Worker lws_button_enable(lws_button_state_t *bcs,
496*1c60b9acSAndroid Build Coastguard Worker lws_button_idx_t _reset, lws_button_idx_t _set)
497*1c60b9acSAndroid Build Coastguard Worker {
498*1c60b9acSAndroid Build Coastguard Worker lws_button_idx_t u = (bcs->enable_bitmap & (~_reset)) | _set;
499*1c60b9acSAndroid Build Coastguard Worker const lws_button_controller_t *bc = bcs->controller;
500*1c60b9acSAndroid Build Coastguard Worker #if defined(LWS_PLAT_TIMER_START)
501*1c60b9acSAndroid Build Coastguard Worker lws_button_each_t *each = (lws_button_each_t *)&bcs[1];
502*1c60b9acSAndroid Build Coastguard Worker #endif
503*1c60b9acSAndroid Build Coastguard Worker int n;
504*1c60b9acSAndroid Build Coastguard Worker
505*1c60b9acSAndroid Build Coastguard Worker for (n = 0; n < bcs->controller->count_buttons; n++) {
506*1c60b9acSAndroid Build Coastguard Worker if (!(bcs->enable_bitmap & (1 << n)) && (u & (1 << n))) {
507*1c60b9acSAndroid Build Coastguard Worker /* set as input with pullup or pulldown appropriately */
508*1c60b9acSAndroid Build Coastguard Worker bc->gpio_ops->mode(bc->button_map[n].gpio,
509*1c60b9acSAndroid Build Coastguard Worker LWSGGPIO_FL_READ |
510*1c60b9acSAndroid Build Coastguard Worker ((bc->active_state_bitmap & (1 << n)) ?
511*1c60b9acSAndroid Build Coastguard Worker LWSGGPIO_FL_PULLDOWN : LWSGGPIO_FL_PULLUP));
512*1c60b9acSAndroid Build Coastguard Worker #if defined(LWS_PLAT_TIMER_START)
513*1c60b9acSAndroid Build Coastguard Worker /*
514*1c60b9acSAndroid Build Coastguard Worker * This one is becoming enabled... the opaque for the
515*1c60b9acSAndroid Build Coastguard Worker * ISR is the indvidual lws_button_each_t, they all
516*1c60b9acSAndroid Build Coastguard Worker * point to the same ISR
517*1c60b9acSAndroid Build Coastguard Worker */
518*1c60b9acSAndroid Build Coastguard Worker bc->gpio_ops->irq_mode(bc->button_map[n].gpio,
519*1c60b9acSAndroid Build Coastguard Worker bc->active_state_bitmap & (1 << n) ?
520*1c60b9acSAndroid Build Coastguard Worker LWSGGPIO_IRQ_RISING :
521*1c60b9acSAndroid Build Coastguard Worker LWSGGPIO_IRQ_FALLING,
522*1c60b9acSAndroid Build Coastguard Worker lws_button_irq_cb_t, &each[n]);
523*1c60b9acSAndroid Build Coastguard Worker #endif
524*1c60b9acSAndroid Build Coastguard Worker }
525*1c60b9acSAndroid Build Coastguard Worker if ((bcs->enable_bitmap & (1 << n)) && !(u & (1 << n)))
526*1c60b9acSAndroid Build Coastguard Worker /* this one is becoming disabled */
527*1c60b9acSAndroid Build Coastguard Worker bc->gpio_ops->irq_mode(bc->button_map[n].gpio,
528*1c60b9acSAndroid Build Coastguard Worker LWSGGPIO_IRQ_NONE, NULL, NULL);
529*1c60b9acSAndroid Build Coastguard Worker }
530*1c60b9acSAndroid Build Coastguard Worker
531*1c60b9acSAndroid Build Coastguard Worker bcs->enable_bitmap = u;
532*1c60b9acSAndroid Build Coastguard Worker }
533