Landfall

Annotated pkg/devices/keypad/src/letux400/keypad-letux400.cc

0:89a1bc19c1fc
2018-05-13 Paul Boddie Added device libraries and programs, configuration files and examples. Also added an installation script and copyright and licensing information.
paul@0 1
/*
paul@0 2
 * Export the keypad GPIOs on the Letux 400 as a data space accessible via the
paul@0 3
 * "keypad" capability.
paul@0 4
 *
paul@0 5
 * (c) 2018 Paul Boddie <paul@boddie.org.uk>
paul@0 6
 *
paul@0 7
 * This program is free software; you can redistribute it and/or
paul@0 8
 * modify it under the terms of the GNU General Public License as
paul@0 9
 * published by the Free Software Foundation; either version 2 of
paul@0 10
 * the License, or (at your option) any later version.
paul@0 11
 *
paul@0 12
 * This program is distributed in the hope that it will be useful,
paul@0 13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
paul@0 14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
paul@0 15
 * GNU General Public License for more details.
paul@0 16
 *
paul@0 17
 * You should have received a copy of the GNU General Public License
paul@0 18
 * along with this program; if not, write to the Free Software
paul@0 19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
paul@0 20
 * Boston, MA  02110-1301, USA
paul@0 21
 */
paul@0 22
paul@0 23
#include <l4/devices/gpio-jz4730.h>
paul@0 24
#include <l4/devices/dataspace.h>
paul@0 25
#include <l4/devices/memory.h>
paul@0 26
#include "keypad-server.h"
paul@0 27
paul@0 28
#include <l4/sys/thread.h>
paul@0 29
#include <pthread.h>
paul@0 30
#include <pthread-l4.h>
paul@0 31
paul@0 32
#include <l4/cxx/ipc_server>
paul@0 33
#include <l4/re/dataspace>
paul@0 34
#include <l4/re/env>
paul@0 35
#include <l4/re/rm>
paul@0 36
#include <l4/re/util/cap_alloc>
paul@0 37
#include <l4/re/util/object_registry>
paul@0 38
paul@0 39
#include <l4/sys/cache.h>
paul@0 40
#include <l4/util/util.h>
paul@0 41
paul@0 42
#include <stdint.h>
paul@0 43
paul@0 44
enum Jz4730_keypad_gpio
paul@0 45
{
paul@0 46
  Jz4730_keypad_gpio_inputs_count = 8,
paul@0 47
  Jz4730_keypad_gpio_outputs_count = 17,
paul@0 48
};
paul@0 49
paul@0 50
/* Port A input pins. */
paul@0 51
paul@0 52
const uint8_t Jz4730_keypad_inputs[Jz4730_keypad_gpio_inputs_count] = {
paul@0 53
  0, 1, 2, 3, 4, 5, 6, 7
paul@0 54
};
paul@0 55
paul@0 56
const Pin_slice Jz4730_keypad_inputs_mask = {0x000000ff, 0};
paul@0 57
paul@0 58
/* Port D output pins. */
paul@0 59
paul@0 60
const uint8_t Jz4730_keypad_outputs[Jz4730_keypad_gpio_outputs_count] = {
paul@0 61
  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 29
paul@0 62
};
paul@0 63
paul@0 64
const Pin_slice Jz4730_keypad_outputs_mask = {0x2000ffff, 0};
paul@0 65
paul@0 66
/* Peripheral memory regions. */
paul@0 67
paul@0 68
static l4_addr_t gpio_virt_base = 0, gpio_virt_base_end = 0;
paul@0 69
paul@0 70
/* GPIO abstractions. */
paul@0 71
paul@0 72
static void *gpio_port_a, *gpio_port_d;
paul@0 73
paul@0 74
/* Keypad status: an array of keypad column values. */
paul@0 75
paul@0 76
uint32_t *keypad = 0;
paul@0 77
paul@0 78
/* Imported keypad memory referenced by the array. */
paul@0 79
paul@0 80
void *keymem = 0;
paul@0 81
paul@0 82
/* Thread details. */
paul@0 83
paul@0 84
static pthread_t _pthread;
paul@0 85
paul@0 86
paul@0 87
paul@0 88
/* Initialise the pins for scanning the keypad. */
paul@0 89
paul@0 90
static void init_keyscan(void)
paul@0 91
{
paul@0 92
  jz4730_gpio_multi_setup(gpio_port_a, &Jz4730_keypad_inputs_mask, Hw::Gpio_chip::Input, 0);
paul@0 93
  jz4730_gpio_multi_config_pull(gpio_port_a, &Jz4730_keypad_inputs_mask, Hw::Gpio_chip::Pull_up);
paul@0 94
  jz4730_gpio_multi_setup(gpio_port_d, &Jz4730_keypad_outputs_mask, Hw::Gpio_chip::Input, 0);
paul@0 95
}
paul@0 96
paul@0 97
/*
paul@0 98
Scan the keypad by enabling each output column and inspecting each input row.
paul@0 99
Store each column bitmap in the keypad array.
paul@0 100
*/
paul@0 101
paul@0 102
static void scan_keypad(void)
paul@0 103
{
paul@0 104
  uint8_t column, row, value;
paul@0 105
paul@0 106
  for (column = 0; column < Jz4730_keypad_gpio_outputs_count; column++)
paul@0 107
  {
paul@0 108
    jz4730_gpio_setup(gpio_port_d, Jz4730_keypad_outputs[column], Hw::Gpio_chip::Output, 0);
paul@0 109
    l4_sleep(1);
paul@0 110
paul@0 111
    value = 0;
paul@0 112
paul@0 113
    for (row = 0; row < Jz4730_keypad_gpio_inputs_count; row++)
paul@0 114
      value = (value << 1) | (jz4730_gpio_get(gpio_port_a, Jz4730_keypad_inputs[row]) ? 0 : 1);
paul@0 115
paul@0 116
    keypad[column] = value;
paul@0 117
paul@0 118
    jz4730_gpio_setup(gpio_port_d, Jz4730_keypad_outputs[column], Hw::Gpio_chip::Input, 0);
paul@0 119
  }
paul@0 120
paul@0 121
  l4_cache_clean_data((unsigned long) keypad,
paul@0 122
                      (unsigned long) keypad + Jz4730_keypad_gpio_outputs_count);
paul@0 123
}
paul@0 124
paul@0 125
/* Set up access to memory. */
paul@0 126
paul@0 127
static int setup_memory(void)
paul@0 128
{
paul@0 129
  if (get_memory("jz4730-gpio", &gpio_virt_base, &gpio_virt_base_end) < 0)
paul@0 130
    return 1;
paul@0 131
paul@0 132
  gpio_port_a = jz4730_gpio_init(gpio_virt_base, gpio_virt_base + 0x30, 32);
paul@0 133
  gpio_port_d = jz4730_gpio_init(gpio_virt_base + 0x90, gpio_virt_base + 0xc0, 32);
paul@0 134
paul@0 135
  return 0;
paul@0 136
}
paul@0 137
paul@0 138
paul@0 139
paul@0 140
/* Worker thread for scanning the keypad. */
paul@0 141
paul@0 142
static void *scan_thread(void *data)
paul@0 143
{
paul@0 144
  (void) data;
paul@0 145
paul@0 146
  while (1)
paul@0 147
  {
paul@0 148
    scan_keypad();
paul@0 149
    l4_sleep(20); /* 20ms -> 50Hz */
paul@0 150
  }
paul@0 151
paul@0 152
  return 0;
paul@0 153
}
paul@0 154
paul@0 155
/* Thread initialisation. */
paul@0 156
paul@0 157
static int init_thread(void)
paul@0 158
{
paul@0 159
  pthread_attr_t thread_attr;
paul@0 160
  struct sched_param sp;
paul@0 161
paul@0 162
  if (pthread_attr_init(&thread_attr))
paul@0 163
    return 1;
paul@0 164
paul@0 165
  sp.sched_priority = 0x20;
paul@0 166
  pthread_attr_setschedpolicy(&thread_attr, SCHED_L4);
paul@0 167
  pthread_attr_setschedparam(&thread_attr, &sp);
paul@0 168
  pthread_attr_setinheritsched(&thread_attr, PTHREAD_EXPLICIT_SCHED);
paul@0 169
paul@0 170
  return pthread_create(&_pthread, &thread_attr, scan_thread, 0);
paul@0 171
}
paul@0 172
paul@0 173
paul@0 174
paul@0 175
static L4Re::Util::Registry_server<> server;
paul@0 176
paul@0 177
paul@0 178
paul@0 179
/* Main program. */
paul@0 180
paul@0 181
int main(void)
paul@0 182
{
paul@0 183
  /* Memory allocation capability for the keypad data. */
paul@0 184
paul@0 185
  L4::Cap<L4Re::Dataspace> mem;
paul@0 186
  l4_size_t mem_size = Jz4730_keypad_gpio_outputs_count * sizeof(uint32_t);
paul@0 187
paul@0 188
  if (setup_memory()) return 1;
paul@0 189
paul@0 190
  mem = allocate_data(mem_size, &keymem);
paul@0 191
paul@0 192
  if (!mem.is_valid()) return 1;
paul@0 193
paul@0 194
  keypad = (uint32_t *) keymem;
paul@0 195
paul@0 196
  /* Set up keypad access and start scanning. */
paul@0 197
paul@0 198
  init_keyscan();
paul@0 199
paul@0 200
  /* Set up a thread to scan the keypad concurrently with the server loop. */
paul@0 201
paul@0 202
  init_thread();
paul@0 203
paul@0 204
  /* Initialise and register a server object. */
paul@0 205
paul@0 206
  Keypad_server server_obj(mem);
paul@0 207
  server.registry()->register_obj(&server_obj, "keypad");
paul@0 208
paul@0 209
  /* Enter the IPC server loop. */
paul@0 210
paul@0 211
  server.loop();
paul@0 212
  return 0;
paul@0 213
}