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