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