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