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 capability for the keypad data. */ |
paul@0 | 43 | |
paul@152 | 44 | void Input_keypad_client::init_memory() |
paul@0 | 45 | { |
paul@152 | 46 | _mem = ipc_cap_alloc(); |
paul@0 | 47 | } |
paul@0 | 48 | |
paul@0 | 49 | /* Obtain a reference to the keypad. */ |
paul@0 | 50 | |
paul@152 | 51 | void Input_keypad_client::init_keypad() |
paul@0 | 52 | { |
paul@152 | 53 | _keypad_cap = l4re_env_get_cap("keypad"); |
paul@0 | 54 | } |
paul@0 | 55 | |
paul@0 | 56 | /* Access the keypad server memory. */ |
paul@0 | 57 | |
paul@152 | 58 | void Input_keypad_client::init_keypad_data() |
paul@0 | 59 | { |
paul@0 | 60 | /* Obtain a reference to the keypad data. */ |
paul@0 | 61 | |
paul@152 | 62 | client_Keypad keypad(_keypad_cap); |
paul@152 | 63 | |
paul@153 | 64 | if (keypad.get_keypad_data(&_mem)) |
paul@0 | 65 | return; |
paul@0 | 66 | |
paul@0 | 67 | /* Attach the keypad data to a region in this task. */ |
paul@0 | 68 | |
paul@152 | 69 | unsigned long size; |
paul@152 | 70 | |
paul@152 | 71 | if (ipc_dataspace_size(_mem, &size)) |
paul@152 | 72 | return; |
paul@152 | 73 | |
paul@152 | 74 | if (ipc_attach_dataspace(_mem, size, &_keymem)) |
paul@0 | 75 | return; |
paul@0 | 76 | |
paul@0 | 77 | /* Allocate memory for a copy of the keypad data. */ |
paul@0 | 78 | |
paul@152 | 79 | _keymem_previous = malloc(size); |
paul@0 | 80 | } |
paul@0 | 81 | |
paul@0 | 82 | /* Release capabilities and the memory for a copy of the keypad data. */ |
paul@0 | 83 | |
paul@152 | 84 | void Input_keypad_client::release_keypad_data() |
paul@0 | 85 | { |
paul@152 | 86 | if (l4_is_valid_cap(_mem)) |
paul@152 | 87 | ipc_cap_free(_mem); |
paul@0 | 88 | |
paul@152 | 89 | if (l4_is_valid_cap(_keypad_cap)) |
paul@152 | 90 | ipc_cap_free(_keypad_cap); |
paul@0 | 91 | |
paul@0 | 92 | if (_keymem_previous && (_keymem_previous != NULL)) |
paul@0 | 93 | free(_keymem_previous); |
paul@0 | 94 | } |
paul@0 | 95 | |
paul@0 | 96 | /* Scan the keypad, compare old and new states, and generate key events. */ |
paul@0 | 97 | |
paul@152 | 98 | void Input_keypad_client::scan_keypad() |
paul@0 | 99 | { |
paul@0 | 100 | uint32_t *keymem = (uint32_t *) _keymem; |
paul@0 | 101 | uint32_t *keymem_previous = (uint32_t *) _keymem_previous; |
paul@0 | 102 | int row, column, key_pressed; |
paul@0 | 103 | uint32_t changed, mask; |
paul@0 | 104 | Input_event event; |
paul@0 | 105 | |
paul@0 | 106 | for (column = 0; column < _keypad->columns(); column++) |
paul@0 | 107 | { |
paul@0 | 108 | /* Test for differences within each column. */ |
paul@0 | 109 | |
paul@0 | 110 | changed = keymem[column] ^ keymem_previous[column]; |
paul@0 | 111 | |
paul@0 | 112 | /* Transfer the current values to the previous values once used. */ |
paul@0 | 113 | |
paul@0 | 114 | keymem_previous[column] = keymem[column]; |
paul@0 | 115 | |
paul@0 | 116 | /* Move to the next column if no changes occurred. */ |
paul@0 | 117 | |
paul@0 | 118 | if (!changed) continue; |
paul@0 | 119 | |
paul@0 | 120 | /* Inspect each changed row position. */ |
paul@0 | 121 | |
paul@0 | 122 | mask = 1 << (_keypad->rows() - 1); |
paul@0 | 123 | |
paul@0 | 124 | for (row = 0; row < _keypad->rows(); row++) |
paul@0 | 125 | { |
paul@0 | 126 | if (changed & mask) |
paul@0 | 127 | { |
paul@0 | 128 | key_pressed = keymem[column] & mask; |
paul@0 | 129 | |
paul@0 | 130 | /* Construct an event using the appropriate key code. */ |
paul@0 | 131 | |
paul@0 | 132 | event = (Input_event) { |
paul@0 | 133 | L4RE_EV_KEY, _keypad->code(column, row), key_pressed |
paul@0 | 134 | }; |
paul@0 | 135 | |
paul@0 | 136 | /* Invoke the supplied handler. */ |
paul@0 | 137 | |
paul@0 | 138 | _handler(event, _priv); |
paul@0 | 139 | } |
paul@0 | 140 | mask >>= 1; |
paul@0 | 141 | } |
paul@0 | 142 | } |
paul@0 | 143 | } |
paul@0 | 144 | |
paul@0 | 145 | /* Perform keypad scanning in a separate thread. */ |
paul@0 | 146 | |
paul@152 | 147 | void *Input_keypad_client::scan_mainloop(void *data) |
paul@0 | 148 | { |
paul@0 | 149 | /* Since this is a static method, obtain instance details from the data. */ |
paul@0 | 150 | |
paul@0 | 151 | Input_keypad_client *self = reinterpret_cast<Input_keypad_client *>(data); |
paul@0 | 152 | |
paul@0 | 153 | while (1) |
paul@0 | 154 | { |
paul@0 | 155 | if (self->_handler) self->scan_keypad(); |
paul@0 | 156 | l4_sleep(20); /* 20ms -> 50Hz */ |
paul@0 | 157 | } |
paul@0 | 158 | |
paul@0 | 159 | return 0; |
paul@0 | 160 | } |
paul@0 | 161 | |
paul@0 | 162 | /* Thread initialisation for the keypad scanning activity. */ |
paul@0 | 163 | |
paul@152 | 164 | void Input_keypad_client::attach(Input_handler handler, void *priv) |
paul@0 | 165 | { |
paul@0 | 166 | pthread_attr_t thread_attr; |
paul@0 | 167 | struct sched_param sp; |
paul@0 | 168 | |
paul@0 | 169 | /* Record the handler and bundled private data. */ |
paul@0 | 170 | |
paul@0 | 171 | _handler = handler; |
paul@0 | 172 | _priv = priv; |
paul@0 | 173 | |
paul@0 | 174 | /* Thread initialisation boilerplate. */ |
paul@0 | 175 | |
paul@0 | 176 | if (pthread_attr_init(&thread_attr)) |
paul@0 | 177 | return; |
paul@0 | 178 | |
paul@0 | 179 | sp.sched_priority = 0x20; |
paul@0 | 180 | pthread_attr_setschedparam(&thread_attr, &sp); |
paul@0 | 181 | pthread_attr_setschedpolicy(&thread_attr, SCHED_L4); |
paul@0 | 182 | pthread_attr_setinheritsched(&thread_attr, PTHREAD_EXPLICIT_SCHED); |
paul@0 | 183 | |
paul@0 | 184 | /* Provide this instance as the private data. */ |
paul@0 | 185 | |
paul@0 | 186 | pthread_create(&_pthread, &thread_attr, scan_mainloop, this); |
paul@0 | 187 | } |