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