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