xref: /aosp_15_r20/external/libwebsockets/minimal-examples/embedded/esp32/esp-c3dev/lws-button.c (revision 1c60b9aca93fdbc9b5f19b2d2194c91294b22281)
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 	uint8_t					state;
65*1c60b9acSAndroid Build Coastguard Worker 	/**^ lws_button_classify_states_t */
66*1c60b9acSAndroid Build Coastguard Worker 	uint8_t					isr_pending;
67*1c60b9acSAndroid Build Coastguard Worker } lws_button_each_t;
68*1c60b9acSAndroid Build Coastguard Worker 
69*1c60b9acSAndroid Build Coastguard Worker #if defined(LWS_PLAT_TIMER_START)
70*1c60b9acSAndroid Build Coastguard Worker static const lws_button_regime_t default_regime = {
71*1c60b9acSAndroid Build Coastguard Worker 	.ms_min_down			= 20,
72*1c60b9acSAndroid Build Coastguard Worker 	.ms_min_down_longpress		= 300,
73*1c60b9acSAndroid Build Coastguard Worker 	.ms_up_settle			= 20,
74*1c60b9acSAndroid Build Coastguard Worker 	.ms_doubleclick_grace		= 120,
75*1c60b9acSAndroid Build Coastguard Worker 	.flags				= LWSBTNRGMFLAG_CLASSIFY_DOUBLECLICK
76*1c60b9acSAndroid Build Coastguard Worker };
77*1c60b9acSAndroid Build Coastguard Worker #endif
78*1c60b9acSAndroid Build Coastguard Worker 
79*1c60b9acSAndroid Build Coastguard Worker 
80*1c60b9acSAndroid Build Coastguard Worker /*
81*1c60b9acSAndroid Build Coastguard Worker  * This is happening in interrupt context, we have to schedule a bottom half to
82*1c60b9acSAndroid Build Coastguard Worker  * do the foreground lws_smd queueing, using, eg, a platform timer.
83*1c60b9acSAndroid Build Coastguard Worker  *
84*1c60b9acSAndroid Build Coastguard Worker  * All the buttons point here and use one timer per button controller.  An
85*1c60b9acSAndroid Build Coastguard Worker  * interrupt here means, "something happened to one or more buttons"
86*1c60b9acSAndroid Build Coastguard Worker  */
87*1c60b9acSAndroid Build Coastguard Worker #if defined(LWS_PLAT_TIMER_START)
88*1c60b9acSAndroid Build Coastguard Worker void
lws_button_irq_cb_t(void * arg)89*1c60b9acSAndroid Build Coastguard Worker lws_button_irq_cb_t(void *arg)
90*1c60b9acSAndroid Build Coastguard Worker {
91*1c60b9acSAndroid Build Coastguard Worker 	lws_button_each_t *each = (lws_button_each_t *)arg;
92*1c60b9acSAndroid Build Coastguard Worker 
93*1c60b9acSAndroid Build Coastguard Worker 	each->isr_pending = 1;
94*1c60b9acSAndroid Build Coastguard Worker 	LWS_PLAT_TIMER_START(each->bcs->timer);
95*1c60b9acSAndroid Build Coastguard Worker }
96*1c60b9acSAndroid Build Coastguard Worker #endif
97*1c60b9acSAndroid Build Coastguard Worker 
98*1c60b9acSAndroid Build Coastguard Worker /*
99*1c60b9acSAndroid Build Coastguard Worker  * This is the bottom-half scheduled via a timer set in the ISR.  From here
100*1c60b9acSAndroid Build Coastguard Worker  * we are allowed to hold mutexes etc.  We are coming here because any button
101*1c60b9acSAndroid Build Coastguard Worker  * interrupt arrived, we have to try to figure out which events have happened.
102*1c60b9acSAndroid Build Coastguard Worker  */
103*1c60b9acSAndroid Build Coastguard Worker 
104*1c60b9acSAndroid Build Coastguard Worker #if defined(LWS_PLAT_TIMER_CB)
LWS_PLAT_TIMER_CB(lws_button_bh,th)105*1c60b9acSAndroid Build Coastguard Worker static LWS_PLAT_TIMER_CB(lws_button_bh, th)
106*1c60b9acSAndroid Build Coastguard Worker {
107*1c60b9acSAndroid Build Coastguard Worker 	lws_button_state_t *bcs = LWS_PLAT_TIMER_CB_GET_OPAQUE(th);
108*1c60b9acSAndroid Build Coastguard Worker 	const lws_button_controller_t *bc = bcs->controller;
109*1c60b9acSAndroid Build Coastguard Worker 	lws_button_each_t *each = (lws_button_each_t *)&bcs[1];
110*1c60b9acSAndroid Build Coastguard Worker 	size_t n;
111*1c60b9acSAndroid Build Coastguard Worker 
112*1c60b9acSAndroid Build Coastguard Worker 	/*
113*1c60b9acSAndroid Build Coastguard Worker 	 * The ISR and bottom-half is shared by all the buttons.  Each gpio
114*1c60b9acSAndroid Build Coastguard Worker 	 * IRQ has an individual opaque ptr pointing to the corresponding
115*1c60b9acSAndroid Build Coastguard Worker 	 * button's dynamic lws_button_each_t, the ISR marks the button's
116*1c60b9acSAndroid Build Coastguard Worker 	 * each->isr_pending and schedules this bottom half.
117*1c60b9acSAndroid Build Coastguard Worker 	 *
118*1c60b9acSAndroid Build Coastguard Worker 	 * So now the bh timer has fired and something to do, we need to go
119*1c60b9acSAndroid Build Coastguard Worker 	 * through all the buttons that have isr_pending set and service their
120*1c60b9acSAndroid Build Coastguard Worker 	 * state.  Intermediate states should start / bump the refcount on the
121*1c60b9acSAndroid Build Coastguard Worker 	 * mon timer.  That's refcounted so it only runs when a button down.
122*1c60b9acSAndroid Build Coastguard Worker 	 */
123*1c60b9acSAndroid Build Coastguard Worker 
124*1c60b9acSAndroid Build Coastguard Worker 	for (n = 0; n < bc->count_buttons; n++) {
125*1c60b9acSAndroid Build Coastguard Worker 
126*1c60b9acSAndroid Build Coastguard Worker 		if (!each[n].isr_pending)
127*1c60b9acSAndroid Build Coastguard Worker 			continue;
128*1c60b9acSAndroid Build Coastguard Worker 
129*1c60b9acSAndroid Build Coastguard Worker 		/*
130*1c60b9acSAndroid Build Coastguard Worker 		 * Hide what we're about to do from the delicate eyes of the
131*1c60b9acSAndroid Build Coastguard Worker 		 * IRQ controller...
132*1c60b9acSAndroid Build Coastguard Worker 		 */
133*1c60b9acSAndroid Build Coastguard Worker 
134*1c60b9acSAndroid Build Coastguard Worker 		bc->gpio_ops->irq_mode(bc->button_map[n].gpio,
135*1c60b9acSAndroid Build Coastguard Worker 				       LWSGGPIO_IRQ_NONE, NULL, NULL);
136*1c60b9acSAndroid Build Coastguard Worker 
137*1c60b9acSAndroid Build Coastguard Worker 		each[n].isr_pending = 0;
138*1c60b9acSAndroid Build Coastguard Worker 
139*1c60b9acSAndroid Build Coastguard Worker 		/*
140*1c60b9acSAndroid Build Coastguard Worker 		 * Force the network around the switch to the
141*1c60b9acSAndroid Build Coastguard Worker 		 * active level briefly
142*1c60b9acSAndroid Build Coastguard Worker 		 */
143*1c60b9acSAndroid Build Coastguard Worker 
144*1c60b9acSAndroid Build Coastguard Worker 		bc->gpio_ops->set(bc->button_map[n].gpio,
145*1c60b9acSAndroid Build Coastguard Worker 				  !!(bc->active_state_bitmap & (1 << n)));
146*1c60b9acSAndroid Build Coastguard Worker 		bc->gpio_ops->mode(bc->button_map[n].gpio, LWSGGPIO_FL_WRITE);
147*1c60b9acSAndroid Build Coastguard Worker 
148*1c60b9acSAndroid Build Coastguard Worker 		if (each[n].state == LBCS_IDLE) {
149*1c60b9acSAndroid Build Coastguard Worker 			/*
150*1c60b9acSAndroid Build Coastguard Worker 			 * If this is the first sign something happening on this
151*1c60b9acSAndroid Build Coastguard Worker 			 * button, make sure the monitor timer is running to
152*1c60b9acSAndroid Build Coastguard Worker 			 * classify it over time
153*1c60b9acSAndroid Build Coastguard Worker 			 */
154*1c60b9acSAndroid Build Coastguard Worker 
155*1c60b9acSAndroid Build Coastguard Worker 			each[n].state = LBCS_MIN_DOWN_QUALIFY;
156*1c60b9acSAndroid Build Coastguard Worker 			each[n].mon_timer_comp = bcs->mon_timer_count;
157*1c60b9acSAndroid Build Coastguard Worker 
158*1c60b9acSAndroid Build Coastguard Worker 			if (!bcs->mon_refcount++) {
159*1c60b9acSAndroid Build Coastguard Worker #if defined(LWS_PLAT_TIMER_START)
160*1c60b9acSAndroid Build Coastguard Worker 				// lwsl_notice("%s: starting mon timer\n", __func__);
161*1c60b9acSAndroid Build Coastguard Worker 				LWS_PLAT_TIMER_START(bcs->timer_mon);
162*1c60b9acSAndroid Build Coastguard Worker #endif
163*1c60b9acSAndroid Build Coastguard Worker 			}
164*1c60b9acSAndroid Build Coastguard Worker 		}
165*1c60b9acSAndroid Build Coastguard Worker 
166*1c60b9acSAndroid Build Coastguard Worker 		/*
167*1c60b9acSAndroid Build Coastguard Worker 		 * Just for a us or two inbetween here, we're driving it to the
168*1c60b9acSAndroid Build Coastguard Worker 		 * level we were informed by the interrupt it had enetered, to
169*1c60b9acSAndroid Build Coastguard Worker 		 * force to charge on the actual and parasitic network around
170*1c60b9acSAndroid Build Coastguard Worker 		 * the switch to a deterministic-ish state.
171*1c60b9acSAndroid Build Coastguard Worker 		 *
172*1c60b9acSAndroid Build Coastguard Worker 		 * If the switch remains in that state, well, it makes no
173*1c60b9acSAndroid Build Coastguard Worker 		 * difference; if it was a pre-contact and the charge on the
174*1c60b9acSAndroid Build Coastguard Worker 		 * network was left indeterminate, this will dispose it to act
175*1c60b9acSAndroid Build Coastguard Worker 		 * consistently in the short term until the pullup / pulldown
176*1c60b9acSAndroid Build Coastguard Worker 		 * has time to act on it or the switch comes and forces the
177*1c60b9acSAndroid Build Coastguard Worker 		 * network charge state itself.
178*1c60b9acSAndroid Build Coastguard Worker 		 */
179*1c60b9acSAndroid Build Coastguard Worker 		bc->gpio_ops->mode(bc->button_map[n].gpio, LWSGGPIO_FL_READ);
180*1c60b9acSAndroid Build Coastguard Worker 
181*1c60b9acSAndroid Build Coastguard Worker 		/*
182*1c60b9acSAndroid Build Coastguard Worker 		 * We could do a better job manipulating the irq mode according
183*1c60b9acSAndroid Build Coastguard Worker 		 * to the switch state.  But if an interrupt comes and we have
184*1c60b9acSAndroid Build Coastguard Worker 		 * done that, we can't tell if it's from before or after the
185*1c60b9acSAndroid Build Coastguard Worker 		 * mode change... ie, we don't know what the interrupt was
186*1c60b9acSAndroid Build Coastguard Worker 		 * telling us.  We can't trust the gpio state if we read it now
187*1c60b9acSAndroid Build Coastguard Worker 		 * to be related to what the irq from some time before was
188*1c60b9acSAndroid Build Coastguard Worker 		 * trying to tell us.  So always set it back to the same mode
189*1c60b9acSAndroid Build Coastguard Worker 		 * and accept the limitation.
190*1c60b9acSAndroid Build Coastguard Worker 		 */
191*1c60b9acSAndroid Build Coastguard Worker 
192*1c60b9acSAndroid Build Coastguard Worker 		bc->gpio_ops->irq_mode(bc->button_map[n].gpio,
193*1c60b9acSAndroid Build Coastguard Worker 				       bc->active_state_bitmap & (1 << n) ?
194*1c60b9acSAndroid Build Coastguard Worker 					   LWSGGPIO_IRQ_RISING :
195*1c60b9acSAndroid Build Coastguard Worker 					   LWSGGPIO_IRQ_FALLING,
196*1c60b9acSAndroid Build Coastguard Worker 					      lws_button_irq_cb_t, &each[n]);
197*1c60b9acSAndroid Build Coastguard Worker 	}
198*1c60b9acSAndroid Build Coastguard Worker }
199*1c60b9acSAndroid Build Coastguard Worker #endif
200*1c60b9acSAndroid Build Coastguard Worker 
201*1c60b9acSAndroid Build Coastguard Worker #if defined(LWS_PLAT_TIMER_CB)
LWS_PLAT_TIMER_CB(lws_button_mon,th)202*1c60b9acSAndroid Build Coastguard Worker static LWS_PLAT_TIMER_CB(lws_button_mon, th)
203*1c60b9acSAndroid Build Coastguard Worker {
204*1c60b9acSAndroid Build Coastguard Worker 	lws_button_state_t *bcs = LWS_PLAT_TIMER_CB_GET_OPAQUE(th);
205*1c60b9acSAndroid Build Coastguard Worker 	lws_button_each_t *each = (lws_button_each_t *)&bcs[1];
206*1c60b9acSAndroid Build Coastguard Worker 	const lws_button_controller_t *bc = bcs->controller;
207*1c60b9acSAndroid Build Coastguard Worker 	const lws_button_regime_t *regime;
208*1c60b9acSAndroid Build Coastguard Worker 	const char *event_name;
209*1c60b9acSAndroid Build Coastguard Worker 	int comp_age_ms;
210*1c60b9acSAndroid Build Coastguard Worker 	char active;
211*1c60b9acSAndroid Build Coastguard Worker 	size_t n;
212*1c60b9acSAndroid Build Coastguard Worker 
213*1c60b9acSAndroid Build Coastguard Worker 	bcs->mon_timer_count++;
214*1c60b9acSAndroid Build Coastguard Worker 
215*1c60b9acSAndroid Build Coastguard Worker 	for (n = 0; n < bc->count_buttons; n++) {
216*1c60b9acSAndroid Build Coastguard Worker 
217*1c60b9acSAndroid Build Coastguard Worker 		if (each[n].state == LBCS_IDLE)
218*1c60b9acSAndroid Build Coastguard Worker 			continue;
219*1c60b9acSAndroid Build Coastguard Worker 
220*1c60b9acSAndroid Build Coastguard Worker 		if (bc->button_map[n].regime)
221*1c60b9acSAndroid Build Coastguard Worker 			regime = bc->button_map[n].regime;
222*1c60b9acSAndroid Build Coastguard Worker 		else
223*1c60b9acSAndroid Build Coastguard Worker 			regime = &default_regime;
224*1c60b9acSAndroid Build Coastguard Worker 
225*1c60b9acSAndroid Build Coastguard Worker 		comp_age_ms = (bcs->mon_timer_count - each[n].mon_timer_comp) *
226*1c60b9acSAndroid Build Coastguard Worker 				LWS_BUTTON_MON_TIMER_MS;
227*1c60b9acSAndroid Build Coastguard Worker 
228*1c60b9acSAndroid Build Coastguard Worker 		active = bc->gpio_ops->read(bc->button_map[n].gpio) ^
229*1c60b9acSAndroid Build Coastguard Worker 			       (!(bc->active_state_bitmap & (1 << n)));
230*1c60b9acSAndroid Build Coastguard Worker 
231*1c60b9acSAndroid Build Coastguard Worker 		// lwsl_notice("%d\n", each[n].state);
232*1c60b9acSAndroid Build Coastguard Worker 
233*1c60b9acSAndroid Build Coastguard Worker 		switch (each[n].state) {
234*1c60b9acSAndroid Build Coastguard Worker 		case LBCS_MIN_DOWN_QUALIFY:
235*1c60b9acSAndroid Build Coastguard Worker 			/*
236*1c60b9acSAndroid Build Coastguard Worker 			 * We're trying to figure out if the initial down event
237*1c60b9acSAndroid Build Coastguard Worker 			 * is a glitch, or if it meets the criteria for being
238*1c60b9acSAndroid Build Coastguard Worker 			 * treated as the definitive start of some kind of click
239*1c60b9acSAndroid Build Coastguard Worker 			 * action.  To get past this, he has to be solidly down
240*1c60b9acSAndroid Build Coastguard Worker 			 * for the time mentioned in the applied regime (at
241*1c60b9acSAndroid Build Coastguard Worker 			 * least when we sample it).
242*1c60b9acSAndroid Build Coastguard Worker 			 *
243*1c60b9acSAndroid Build Coastguard Worker 			 * Significant bounce at the start will abort this try,
244*1c60b9acSAndroid Build Coastguard Worker 			 * but if it's really down there will be a subsequent
245*1c60b9acSAndroid Build Coastguard Worker 			 * solid down period... it will simply restart this flow
246*1c60b9acSAndroid Build Coastguard Worker 			 * from a new interrupt and pass the filter then.
247*1c60b9acSAndroid Build Coastguard Worker 			 *
248*1c60b9acSAndroid Build Coastguard Worker 			 * The "brief drive on edge" strategy considerably
249*1c60b9acSAndroid Build Coastguard Worker 			 * reduces inconsistencies here.  But physical bounce
250*1c60b9acSAndroid Build Coastguard Worker 			 * will continue to be observed.
251*1c60b9acSAndroid Build Coastguard Worker 			 */
252*1c60b9acSAndroid Build Coastguard Worker 
253*1c60b9acSAndroid Build Coastguard Worker 			if (!active) {
254*1c60b9acSAndroid Build Coastguard Worker 				/* We ignore stuff for a bit after discard */
255*1c60b9acSAndroid Build Coastguard Worker 				each[n].mon_timer_comp = bcs->mon_timer_count;
256*1c60b9acSAndroid Build Coastguard Worker 				each[n].state = LBCS_UP_SETTLE2;
257*1c60b9acSAndroid Build Coastguard Worker 				continue;
258*1c60b9acSAndroid Build Coastguard Worker 			}
259*1c60b9acSAndroid Build Coastguard Worker 
260*1c60b9acSAndroid Build Coastguard Worker 			if (comp_age_ms >= regime->ms_min_down) {
261*1c60b9acSAndroid Build Coastguard Worker 
262*1c60b9acSAndroid Build Coastguard Worker 				/* We made it through the initial regime filter,
263*1c60b9acSAndroid Build Coastguard Worker 				 * the next step is wait and see if this down
264*1c60b9acSAndroid Build Coastguard Worker 				 * event evolves into a single/double click or
265*1c60b9acSAndroid Build Coastguard Worker 				 * we can call it as a long-click
266*1c60b9acSAndroid Build Coastguard Worker 				 */
267*1c60b9acSAndroid Build Coastguard Worker 
268*1c60b9acSAndroid Build Coastguard Worker 				each[n].state = LBCS_ASSESS_DOWN_HOLD;
269*1c60b9acSAndroid Build Coastguard Worker 				break;
270*1c60b9acSAndroid Build Coastguard Worker 			}
271*1c60b9acSAndroid Build Coastguard Worker 			break;
272*1c60b9acSAndroid Build Coastguard Worker 
273*1c60b9acSAndroid Build Coastguard Worker 		case LBCS_ASSESS_DOWN_HOLD:
274*1c60b9acSAndroid Build Coastguard Worker 			/*
275*1c60b9acSAndroid Build Coastguard Worker 			 * How long is he going to hold it?  If he holds it
276*1c60b9acSAndroid Build Coastguard Worker 			 * past the long-click threshold, we can call it as a
277*1c60b9acSAndroid Build Coastguard Worker 			 * long-click and do the up processing afterwards.
278*1c60b9acSAndroid Build Coastguard Worker 			 */
279*1c60b9acSAndroid Build Coastguard Worker 			if (comp_age_ms >= regime->ms_min_down_longpress) {
280*1c60b9acSAndroid Build Coastguard Worker 				/* call it as a longclick */
281*1c60b9acSAndroid Build Coastguard Worker 				event_name = "longclick";
282*1c60b9acSAndroid Build Coastguard Worker 				each[n].state = LBCS_WAIT_UP;
283*1c60b9acSAndroid Build Coastguard Worker 				goto classify;
284*1c60b9acSAndroid Build Coastguard Worker 			}
285*1c60b9acSAndroid Build Coastguard Worker 
286*1c60b9acSAndroid Build Coastguard Worker 			if (!active) {
287*1c60b9acSAndroid Build Coastguard Worker 				/*
288*1c60b9acSAndroid Build Coastguard Worker 				 * He didn't hold it past the long-click
289*1c60b9acSAndroid Build Coastguard Worker 				 * threshold... we could end up classifying it
290*1c60b9acSAndroid Build Coastguard Worker 				 * as either a click or a double-click then.
291*1c60b9acSAndroid Build Coastguard Worker 				 *
292*1c60b9acSAndroid Build Coastguard Worker 				 * If double-clicks are not allowed to be
293*1c60b9acSAndroid Build Coastguard Worker 				 * classified, then we can already classify it
294*1c60b9acSAndroid Build Coastguard Worker 				 * as a single-click.
295*1c60b9acSAndroid Build Coastguard Worker 				 */
296*1c60b9acSAndroid Build Coastguard Worker 				if (!(regime->flags & LWSBTNRGMFLAG_CLASSIFY_DOUBLECLICK))
297*1c60b9acSAndroid Build Coastguard Worker 					goto classify_single;
298*1c60b9acSAndroid Build Coastguard Worker 
299*1c60b9acSAndroid Build Coastguard Worker 				/*
300*1c60b9acSAndroid Build Coastguard Worker 				 * Just wait for the up settle time then start
301*1c60b9acSAndroid Build Coastguard Worker 				 * looking for a second down.
302*1c60b9acSAndroid Build Coastguard Worker 				 */
303*1c60b9acSAndroid Build Coastguard Worker 				each[n].mon_timer_comp = bcs->mon_timer_count;
304*1c60b9acSAndroid Build Coastguard Worker 				each[n].state = LBCS_UP_SETTLE1;
305*1c60b9acSAndroid Build Coastguard Worker 			}
306*1c60b9acSAndroid Build Coastguard Worker 			break;
307*1c60b9acSAndroid Build Coastguard Worker 
308*1c60b9acSAndroid Build Coastguard Worker 		case LBCS_UP_SETTLE1:
309*1c60b9acSAndroid Build Coastguard Worker 			if (comp_age_ms > regime->ms_up_settle)
310*1c60b9acSAndroid Build Coastguard Worker 				/*
311*1c60b9acSAndroid Build Coastguard Worker 				 * Just block anything for the up settle time
312*1c60b9acSAndroid Build Coastguard Worker 				 */
313*1c60b9acSAndroid Build Coastguard Worker 				each[n].state = LBCS_WAIT_DOUBLECLICK;
314*1c60b9acSAndroid Build Coastguard Worker 			break;
315*1c60b9acSAndroid Build Coastguard Worker 
316*1c60b9acSAndroid Build Coastguard Worker 		case LBCS_WAIT_DOUBLECLICK:
317*1c60b9acSAndroid Build Coastguard Worker 			if (active) {
318*1c60b9acSAndroid Build Coastguard Worker 				/*
319*1c60b9acSAndroid Build Coastguard Worker 				 * He has gone down again inside the regime's
320*1c60b9acSAndroid Build Coastguard Worker 				 * doubleclick grace period... he's going down
321*1c60b9acSAndroid Build Coastguard Worker 				 * the double-click path
322*1c60b9acSAndroid Build Coastguard Worker 				 */
323*1c60b9acSAndroid Build Coastguard Worker 				each[n].mon_timer_comp = bcs->mon_timer_count;
324*1c60b9acSAndroid Build Coastguard Worker 				each[n].state = LBCS_MIN_DOWN_QUALIFY2;
325*1c60b9acSAndroid Build Coastguard Worker 				break;
326*1c60b9acSAndroid Build Coastguard Worker 			}
327*1c60b9acSAndroid Build Coastguard Worker 
328*1c60b9acSAndroid Build Coastguard Worker 			if (comp_age_ms >= regime->ms_doubleclick_grace) {
329*1c60b9acSAndroid Build Coastguard Worker 				/*
330*1c60b9acSAndroid Build Coastguard Worker 				 * The grace period expired, the second click
331*1c60b9acSAndroid Build Coastguard Worker 				 * was either not forthcoming at all, or coming
332*1c60b9acSAndroid Build Coastguard Worker 				 * quick enough to count: we classify it as a
333*1c60b9acSAndroid Build Coastguard Worker 				 * single-click
334*1c60b9acSAndroid Build Coastguard Worker 				 */
335*1c60b9acSAndroid Build Coastguard Worker 
336*1c60b9acSAndroid Build Coastguard Worker 				goto classify_single;
337*1c60b9acSAndroid Build Coastguard Worker 			}
338*1c60b9acSAndroid Build Coastguard Worker 			break;
339*1c60b9acSAndroid Build Coastguard Worker 
340*1c60b9acSAndroid Build Coastguard Worker 		case LBCS_MIN_DOWN_QUALIFY2:
341*1c60b9acSAndroid Build Coastguard Worker 			if (!active) {
342*1c60b9acSAndroid Build Coastguard Worker classify_single:
343*1c60b9acSAndroid Build Coastguard Worker 				/*
344*1c60b9acSAndroid Build Coastguard Worker 				 * He went up again too quickly, classify it
345*1c60b9acSAndroid Build Coastguard Worker 				 * as a single-click.  It could be bounce in
346*1c60b9acSAndroid Build Coastguard Worker 				 * which case you might want to increase
347*1c60b9acSAndroid Build Coastguard Worker 				 * the ms_up_settle in the regime
348*1c60b9acSAndroid Build Coastguard Worker 				 */
349*1c60b9acSAndroid Build Coastguard Worker 				event_name = "click";
350*1c60b9acSAndroid Build Coastguard Worker 				each[n].mon_timer_comp = bcs->mon_timer_count;
351*1c60b9acSAndroid Build Coastguard Worker 				each[n].state = LBCS_UP_SETTLE2;
352*1c60b9acSAndroid Build Coastguard Worker 				goto classify;
353*1c60b9acSAndroid Build Coastguard Worker 			}
354*1c60b9acSAndroid Build Coastguard Worker 
355*1c60b9acSAndroid Build Coastguard Worker 			if (comp_age_ms >= regime->ms_min_down) {
356*1c60b9acSAndroid Build Coastguard Worker 				/*
357*1c60b9acSAndroid Build Coastguard Worker 				 * It's a double-click
358*1c60b9acSAndroid Build Coastguard Worker 				 */
359*1c60b9acSAndroid Build Coastguard Worker 				event_name = "doubleclick";
360*1c60b9acSAndroid Build Coastguard Worker 				each[n].state = LBCS_WAIT_UP;
361*1c60b9acSAndroid Build Coastguard Worker 				goto classify;
362*1c60b9acSAndroid Build Coastguard Worker 			}
363*1c60b9acSAndroid Build Coastguard Worker 			break;
364*1c60b9acSAndroid Build Coastguard Worker 
365*1c60b9acSAndroid Build Coastguard Worker 		case LBCS_WAIT_UP:
366*1c60b9acSAndroid Build Coastguard Worker 			if (!active) {
367*1c60b9acSAndroid Build Coastguard Worker 				each[n].mon_timer_comp = bcs->mon_timer_count;
368*1c60b9acSAndroid Build Coastguard Worker 				each[n].state = LBCS_UP_SETTLE2;
369*1c60b9acSAndroid Build Coastguard Worker 			}
370*1c60b9acSAndroid Build Coastguard Worker 			break;
371*1c60b9acSAndroid Build Coastguard Worker 
372*1c60b9acSAndroid Build Coastguard Worker 		case LBCS_UP_SETTLE2:
373*1c60b9acSAndroid Build Coastguard Worker 			if (comp_age_ms < regime->ms_up_settle)
374*1c60b9acSAndroid Build Coastguard Worker 				break;
375*1c60b9acSAndroid Build Coastguard Worker 
376*1c60b9acSAndroid Build Coastguard Worker 			each[n].state = LBCS_IDLE;
377*1c60b9acSAndroid Build Coastguard Worker 			if (!(--bcs->mon_refcount)) {
378*1c60b9acSAndroid Build Coastguard Worker #if defined(LWS_PLAT_TIMER_STOP)
379*1c60b9acSAndroid Build Coastguard Worker 				LWS_PLAT_TIMER_STOP(bcs->timer_mon);
380*1c60b9acSAndroid Build Coastguard Worker #endif
381*1c60b9acSAndroid Build Coastguard Worker 			}
382*1c60b9acSAndroid Build Coastguard Worker 			break;
383*1c60b9acSAndroid Build Coastguard Worker 		}
384*1c60b9acSAndroid Build Coastguard Worker 
385*1c60b9acSAndroid Build Coastguard Worker 		continue;
386*1c60b9acSAndroid Build Coastguard Worker 
387*1c60b9acSAndroid Build Coastguard Worker classify:
388*1c60b9acSAndroid Build Coastguard Worker 		lws_smd_msg_printf(bcs->ctx, LWSSMDCL_INTERACTION,
389*1c60b9acSAndroid Build Coastguard Worker 		   "{\"btn\":\"%s/%s\", \"s\":\"%s\"}",
390*1c60b9acSAndroid Build Coastguard Worker 		   bc->smd_bc_name,
391*1c60b9acSAndroid Build Coastguard Worker 		   bc->button_map[n].smd_interaction_name,
392*1c60b9acSAndroid Build Coastguard Worker 		   event_name);
393*1c60b9acSAndroid Build Coastguard Worker 	}
394*1c60b9acSAndroid Build Coastguard Worker }
395*1c60b9acSAndroid Build Coastguard Worker #endif
396*1c60b9acSAndroid Build Coastguard Worker 
397*1c60b9acSAndroid Build Coastguard Worker struct lws_button_state *
lws_button_controller_create(struct lws_context * ctx,const lws_button_controller_t * controller)398*1c60b9acSAndroid Build Coastguard Worker lws_button_controller_create(struct lws_context *ctx,
399*1c60b9acSAndroid Build Coastguard Worker 			     const lws_button_controller_t *controller)
400*1c60b9acSAndroid Build Coastguard Worker {
401*1c60b9acSAndroid Build Coastguard Worker 	lws_button_state_t *bcs = lws_zalloc(sizeof(lws_button_state_t) +
402*1c60b9acSAndroid Build Coastguard Worker 			(controller->count_buttons * sizeof(lws_button_each_t)),
403*1c60b9acSAndroid Build Coastguard Worker 			__func__);
404*1c60b9acSAndroid Build Coastguard Worker 	lws_button_each_t *each = (lws_button_each_t *)&bcs[1];
405*1c60b9acSAndroid Build Coastguard Worker 	size_t n;
406*1c60b9acSAndroid Build Coastguard Worker 
407*1c60b9acSAndroid Build Coastguard Worker 	if (!bcs)
408*1c60b9acSAndroid Build Coastguard Worker 		return NULL;
409*1c60b9acSAndroid Build Coastguard Worker 
410*1c60b9acSAndroid Build Coastguard Worker 	bcs->controller = controller;
411*1c60b9acSAndroid Build Coastguard Worker 	bcs->ctx = ctx;
412*1c60b9acSAndroid Build Coastguard Worker 
413*1c60b9acSAndroid Build Coastguard Worker 	for (n = 0; n < controller->count_buttons; n++)
414*1c60b9acSAndroid Build Coastguard Worker 		each[n].bcs = bcs;
415*1c60b9acSAndroid Build Coastguard Worker 
416*1c60b9acSAndroid Build Coastguard Worker #if defined(LWS_PLAT_TIMER_CREATE)
417*1c60b9acSAndroid Build Coastguard Worker 	/* this only runs inbetween a gpio ISR and the bottom half */
418*1c60b9acSAndroid Build Coastguard Worker 	bcs->timer = LWS_PLAT_TIMER_CREATE("bcst",
419*1c60b9acSAndroid Build Coastguard Worker 			1, 0, bcs, (TimerCallbackFunction_t)lws_button_bh);
420*1c60b9acSAndroid Build Coastguard Worker 	if (!bcs->timer)
421*1c60b9acSAndroid Build Coastguard Worker 		return NULL;
422*1c60b9acSAndroid Build Coastguard Worker 	/* this only runs when a button activity is being classified */
423*1c60b9acSAndroid Build Coastguard Worker 	bcs->timer_mon = LWS_PLAT_TIMER_CREATE("bcmon", LWS_BUTTON_MON_TIMER_MS, 1, bcs,
424*1c60b9acSAndroid Build Coastguard Worker 			(TimerCallbackFunction_t)lws_button_mon);
425*1c60b9acSAndroid Build Coastguard Worker 	if (!bcs->timer_mon)
426*1c60b9acSAndroid Build Coastguard Worker 		return NULL;
427*1c60b9acSAndroid Build Coastguard Worker #endif
428*1c60b9acSAndroid Build Coastguard Worker 
429*1c60b9acSAndroid Build Coastguard Worker 	return bcs;
430*1c60b9acSAndroid Build Coastguard Worker }
431*1c60b9acSAndroid Build Coastguard Worker 
432*1c60b9acSAndroid Build Coastguard Worker void
lws_button_controller_destroy(struct lws_button_state * bcs)433*1c60b9acSAndroid Build Coastguard Worker lws_button_controller_destroy(struct lws_button_state *bcs)
434*1c60b9acSAndroid Build Coastguard Worker {
435*1c60b9acSAndroid Build Coastguard Worker 	/* disable them all */
436*1c60b9acSAndroid Build Coastguard Worker 	lws_button_enable(bcs, 0, 0);
437*1c60b9acSAndroid Build Coastguard Worker 
438*1c60b9acSAndroid Build Coastguard Worker #if defined(LWS_PLAT_TIMER_DELETE)
439*1c60b9acSAndroid Build Coastguard Worker 	LWS_PLAT_TIMER_DELETE(&bcs->timer);
440*1c60b9acSAndroid Build Coastguard Worker 	LWS_PLAT_TIMER_DELETE(&bcs->timer_mon);
441*1c60b9acSAndroid Build Coastguard Worker #endif
442*1c60b9acSAndroid Build Coastguard Worker 
443*1c60b9acSAndroid Build Coastguard Worker 	lws_free(bcs);
444*1c60b9acSAndroid Build Coastguard Worker }
445*1c60b9acSAndroid Build Coastguard Worker 
446*1c60b9acSAndroid Build Coastguard Worker lws_button_idx_t
lws_button_get_bit(struct lws_button_state * bcs,const char * name)447*1c60b9acSAndroid Build Coastguard Worker lws_button_get_bit(struct lws_button_state *bcs, const char *name)
448*1c60b9acSAndroid Build Coastguard Worker {
449*1c60b9acSAndroid Build Coastguard Worker 	const lws_button_controller_t *bc = bcs->controller;
450*1c60b9acSAndroid Build Coastguard Worker 	int n;
451*1c60b9acSAndroid Build Coastguard Worker 
452*1c60b9acSAndroid Build Coastguard Worker 	for (n = 0; n < bc->count_buttons; n++)
453*1c60b9acSAndroid Build Coastguard Worker 		if (!strcmp(name, bc->button_map[n].smd_interaction_name))
454*1c60b9acSAndroid Build Coastguard Worker 			return 1 << n;
455*1c60b9acSAndroid Build Coastguard Worker 
456*1c60b9acSAndroid Build Coastguard Worker 	return 0; /* not found */
457*1c60b9acSAndroid Build Coastguard Worker }
458*1c60b9acSAndroid Build Coastguard Worker 
459*1c60b9acSAndroid Build Coastguard Worker void
lws_button_enable(lws_button_state_t * bcs,lws_button_idx_t _reset,lws_button_idx_t _set)460*1c60b9acSAndroid Build Coastguard Worker lws_button_enable(lws_button_state_t *bcs,
461*1c60b9acSAndroid Build Coastguard Worker 		  lws_button_idx_t _reset, lws_button_idx_t _set)
462*1c60b9acSAndroid Build Coastguard Worker {
463*1c60b9acSAndroid Build Coastguard Worker 	lws_button_idx_t u = (bcs->enable_bitmap & (~_reset)) | _set;
464*1c60b9acSAndroid Build Coastguard Worker 	const lws_button_controller_t *bc = bcs->controller;
465*1c60b9acSAndroid Build Coastguard Worker #if defined(LWS_PLAT_TIMER_START)
466*1c60b9acSAndroid Build Coastguard Worker 	lws_button_each_t *each = (lws_button_each_t *)&bcs[1];
467*1c60b9acSAndroid Build Coastguard Worker #endif
468*1c60b9acSAndroid Build Coastguard Worker 	int n;
469*1c60b9acSAndroid Build Coastguard Worker 
470*1c60b9acSAndroid Build Coastguard Worker 	for (n = 0; n < bcs->controller->count_buttons; n++) {
471*1c60b9acSAndroid Build Coastguard Worker 		if (!(bcs->enable_bitmap & (1 << n)) && (u & (1 << n))) {
472*1c60b9acSAndroid Build Coastguard Worker 			/* set as input with pullup or pulldown appropriately */
473*1c60b9acSAndroid Build Coastguard Worker 			bc->gpio_ops->mode(bc->button_map[n].gpio,
474*1c60b9acSAndroid Build Coastguard Worker 				LWSGGPIO_FL_READ |
475*1c60b9acSAndroid Build Coastguard Worker 				((bc->active_state_bitmap & (1 << n)) ?
476*1c60b9acSAndroid Build Coastguard Worker 				LWSGGPIO_FL_PULLDOWN : LWSGGPIO_FL_PULLUP));
477*1c60b9acSAndroid Build Coastguard Worker #if defined(LWS_PLAT_TIMER_START)
478*1c60b9acSAndroid Build Coastguard Worker 			/*
479*1c60b9acSAndroid Build Coastguard Worker 			 * This one is becoming enabled... the opaque for the
480*1c60b9acSAndroid Build Coastguard Worker 			 * ISR is the indvidual lws_button_each_t, they all
481*1c60b9acSAndroid Build Coastguard Worker 			 * point to the same ISR
482*1c60b9acSAndroid Build Coastguard Worker 			 */
483*1c60b9acSAndroid Build Coastguard Worker 			bc->gpio_ops->irq_mode(bc->button_map[n].gpio,
484*1c60b9acSAndroid Build Coastguard Worker 					bc->active_state_bitmap & (1 << n) ?
485*1c60b9acSAndroid Build Coastguard Worker 						LWSGGPIO_IRQ_RISING :
486*1c60b9acSAndroid Build Coastguard Worker 							LWSGGPIO_IRQ_FALLING,
487*1c60b9acSAndroid Build Coastguard Worker 						lws_button_irq_cb_t, &each[n]);
488*1c60b9acSAndroid Build Coastguard Worker #endif
489*1c60b9acSAndroid Build Coastguard Worker 		}
490*1c60b9acSAndroid Build Coastguard Worker 		if ((bcs->enable_bitmap & (1 << n)) && !(u & (1 << n)))
491*1c60b9acSAndroid Build Coastguard Worker 			/* this one is becoming disabled */
492*1c60b9acSAndroid Build Coastguard Worker 			bc->gpio_ops->irq_mode(bc->button_map[n].gpio,
493*1c60b9acSAndroid Build Coastguard Worker 						LWSGGPIO_IRQ_NONE, NULL, NULL);
494*1c60b9acSAndroid Build Coastguard Worker 	}
495*1c60b9acSAndroid Build Coastguard Worker 
496*1c60b9acSAndroid Build Coastguard Worker 	bcs->enable_bitmap = u;
497*1c60b9acSAndroid Build Coastguard Worker }
498