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