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