lws-button.c 15 KB

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