paul@0 | 1 | /* |
paul@0 | 2 | * Common keypad client functionality for input event generation. |
paul@0 | 3 | * |
paul@142 | 4 | * Copyright (C) 2018, 2023 Paul Boddie <paul@boddie.org.uk> |
paul@0 | 5 | * |
paul@0 | 6 | * This program is free software; you can redistribute it and/or |
paul@0 | 7 | * modify it under the terms of the GNU General Public License as |
paul@0 | 8 | * published by the Free Software Foundation; either version 2 of |
paul@0 | 9 | * the License, or (at your option) any later version. |
paul@0 | 10 | * |
paul@0 | 11 | * This program is distributed in the hope that it will be useful, |
paul@0 | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
paul@0 | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
paul@0 | 14 | * GNU General Public License for more details. |
paul@0 | 15 | * |
paul@0 | 16 | * You should have received a copy of the GNU General Public License |
paul@0 | 17 | * along with this program; if not, write to the Free Software |
paul@0 | 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, |
paul@0 | 19 | * Boston, MA 02110-1301, USA |
paul@0 | 20 | */ |
paul@0 | 21 | |
paul@0 | 22 | #include <l4/devices/input-keypad-client.h> |
paul@0 | 23 | |
paul@0 | 24 | #include <pthread.h> |
paul@0 | 25 | #include <pthread-l4.h> |
paul@0 | 26 | |
paul@152 | 27 | #include <l4/re/env.h> |
paul@0 | 28 | |
paul@0 | 29 | #include <l4/re/event_enums.h> |
paul@0 | 30 | #include <l4/sys/thread.h> |
paul@0 | 31 | #include <l4/util/util.h> |
paul@0 | 32 | |
paul@152 | 33 | #include <ipc/cap_alloc.h> |
paul@152 | 34 | #include <ipc/mem_ipc.h> |
paul@152 | 35 | |
paul@0 | 36 | #include <stdlib.h> |
paul@0 | 37 | |
paul@153 | 38 | #include "keypad_client.h" |
paul@152 | 39 | |
paul@152 | 40 | |
paul@152 | 41 | |
paul@0 | 42 | /* Obtain a reference to the keypad. */ |
paul@0 | 43 | |
paul@152 | 44 | void Input_keypad_client::init_keypad() |
paul@0 | 45 | { |
paul@152 | 46 | _keypad_cap = l4re_env_get_cap("keypad"); |
paul@0 | 47 | } |
paul@0 | 48 | |
paul@0 | 49 | /* Access the keypad server memory. */ |
paul@0 | 50 | |
paul@152 | 51 | void Input_keypad_client::init_keypad_data() |
paul@0 | 52 | { |
paul@0 | 53 | /* Obtain a reference to the keypad data. */ |
paul@0 | 54 | |
paul@152 | 55 | client_Keypad keypad(_keypad_cap); |
paul@152 | 56 | |
paul@153 | 57 | if (keypad.get_keypad_data(&_mem)) |
paul@0 | 58 | return; |
paul@0 | 59 | |
paul@0 | 60 | /* Attach the keypad data to a region in this task. */ |
paul@0 | 61 | |
paul@278 | 62 | offset_t size; |
paul@152 | 63 | |
paul@152 | 64 | if (ipc_dataspace_size(_mem, &size)) |
paul@152 | 65 | return; |
paul@152 | 66 | |
paul@152 | 67 | if (ipc_attach_dataspace(_mem, size, &_keymem)) |
paul@0 | 68 | return; |
paul@0 | 69 | |
paul@0 | 70 | /* Allocate memory for a copy of the keypad data. */ |
paul@0 | 71 | |
paul@152 | 72 | _keymem_previous = malloc(size); |
paul@0 | 73 | } |
paul@0 | 74 | |
paul@0 | 75 | /* Release capabilities and the memory for a copy of the keypad data. */ |
paul@0 | 76 | |
paul@152 | 77 | void Input_keypad_client::release_keypad_data() |
paul@0 | 78 | { |
paul@152 | 79 | if (l4_is_valid_cap(_mem)) |
paul@152 | 80 | ipc_cap_free(_mem); |
paul@0 | 81 | |
paul@152 | 82 | if (l4_is_valid_cap(_keypad_cap)) |
paul@152 | 83 | ipc_cap_free(_keypad_cap); |
paul@0 | 84 | |
paul@0 | 85 | if (_keymem_previous && (_keymem_previous != NULL)) |
paul@0 | 86 | free(_keymem_previous); |
paul@0 | 87 | } |
paul@0 | 88 | |
paul@0 | 89 | /* Scan the keypad, compare old and new states, and generate key events. */ |
paul@0 | 90 | |
paul@152 | 91 | void Input_keypad_client::scan_keypad() |
paul@0 | 92 | { |
paul@0 | 93 | uint32_t *keymem = (uint32_t *) _keymem; |
paul@0 | 94 | uint32_t *keymem_previous = (uint32_t *) _keymem_previous; |
paul@0 | 95 | int row, column, key_pressed; |
paul@0 | 96 | uint32_t changed, mask; |
paul@0 | 97 | Input_event event; |
paul@0 | 98 | |
paul@0 | 99 | for (column = 0; column < _keypad->columns(); column++) |
paul@0 | 100 | { |
paul@0 | 101 | /* Test for differences within each column. */ |
paul@0 | 102 | |
paul@0 | 103 | changed = keymem[column] ^ keymem_previous[column]; |
paul@0 | 104 | |
paul@0 | 105 | /* Transfer the current values to the previous values once used. */ |
paul@0 | 106 | |
paul@0 | 107 | keymem_previous[column] = keymem[column]; |
paul@0 | 108 | |
paul@0 | 109 | /* Move to the next column if no changes occurred. */ |
paul@0 | 110 | |
paul@0 | 111 | if (!changed) continue; |
paul@0 | 112 | |
paul@0 | 113 | /* Inspect each changed row position. */ |
paul@0 | 114 | |
paul@0 | 115 | mask = 1 << (_keypad->rows() - 1); |
paul@0 | 116 | |
paul@0 | 117 | for (row = 0; row < _keypad->rows(); row++) |
paul@0 | 118 | { |
paul@0 | 119 | if (changed & mask) |
paul@0 | 120 | { |
paul@0 | 121 | key_pressed = keymem[column] & mask; |
paul@0 | 122 | |
paul@0 | 123 | /* Construct an event using the appropriate key code. */ |
paul@0 | 124 | |
paul@0 | 125 | event = (Input_event) { |
paul@0 | 126 | L4RE_EV_KEY, _keypad->code(column, row), key_pressed |
paul@0 | 127 | }; |
paul@0 | 128 | |
paul@0 | 129 | /* Invoke the supplied handler. */ |
paul@0 | 130 | |
paul@0 | 131 | _handler(event, _priv); |
paul@0 | 132 | } |
paul@0 | 133 | mask >>= 1; |
paul@0 | 134 | } |
paul@0 | 135 | } |
paul@0 | 136 | } |
paul@0 | 137 | |
paul@0 | 138 | /* Perform keypad scanning in a separate thread. */ |
paul@0 | 139 | |
paul@152 | 140 | void *Input_keypad_client::scan_mainloop(void *data) |
paul@0 | 141 | { |
paul@0 | 142 | /* Since this is a static method, obtain instance details from the data. */ |
paul@0 | 143 | |
paul@0 | 144 | Input_keypad_client *self = reinterpret_cast<Input_keypad_client *>(data); |
paul@0 | 145 | |
paul@0 | 146 | while (1) |
paul@0 | 147 | { |
paul@0 | 148 | if (self->_handler) self->scan_keypad(); |
paul@0 | 149 | l4_sleep(20); /* 20ms -> 50Hz */ |
paul@0 | 150 | } |
paul@0 | 151 | |
paul@0 | 152 | return 0; |
paul@0 | 153 | } |
paul@0 | 154 | |
paul@0 | 155 | /* Thread initialisation for the keypad scanning activity. */ |
paul@0 | 156 | |
paul@152 | 157 | void Input_keypad_client::attach(Input_handler handler, void *priv) |
paul@0 | 158 | { |
paul@0 | 159 | pthread_attr_t thread_attr; |
paul@0 | 160 | struct sched_param sp; |
paul@0 | 161 | |
paul@0 | 162 | /* Record the handler and bundled private data. */ |
paul@0 | 163 | |
paul@0 | 164 | _handler = handler; |
paul@0 | 165 | _priv = priv; |
paul@0 | 166 | |
paul@0 | 167 | /* Thread initialisation boilerplate. */ |
paul@0 | 168 | |
paul@0 | 169 | if (pthread_attr_init(&thread_attr)) |
paul@0 | 170 | return; |
paul@0 | 171 | |
paul@0 | 172 | sp.sched_priority = 0x20; |
paul@0 | 173 | pthread_attr_setschedparam(&thread_attr, &sp); |
paul@0 | 174 | pthread_attr_setschedpolicy(&thread_attr, SCHED_L4); |
paul@0 | 175 | pthread_attr_setinheritsched(&thread_attr, PTHREAD_EXPLICIT_SCHED); |
paul@0 | 176 | |
paul@0 | 177 | /* Provide this instance as the private data. */ |
paul@0 | 178 | |
paul@0 | 179 | pthread_create(&_pthread, &thread_attr, scan_mainloop, this); |
paul@0 | 180 | } |