source: mainline/uspace/srv/hid/input/gsp.c@ c8ea6eca

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since c8ea6eca was 76d0981d, checked in by Jiri Svoboda <jiri@…>, 8 years ago

Use proper boolean constant in while loops.

  • Property mode set to 100644
File size: 7.0 KB
RevLine 
[b0b5628]1/*
2 * Copyright (c) 2009 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/**
30 * @addtogroup kbdgen generic
[5f88293]31 * @ingroup input
[b0b5628]32 * @{
[b6a088f]33 */
[b0b5628]34/** @file
[b6a088f]35 * @brief Generic scancode parser.
[b0b5628]36 *
37 * The scancode parser is a simple finite state machine. It is described
38 * using sequences of input symbols (scancodes) and the corresponding output
39 * value (mods, key pair). When the parser recognizes a sequence,
40 * it outputs the value and restarts. If a transition is undefined,
41 * the parser restarts, too.
42 *
43 * Apart from precise values, GSP_DEFAULT allows to catch general cases.
44 * I.e. if we knew that after 0x1b 0x4f there always follow two more
45 * scancodes, we can define (0x1b, 0x4f, GSP_DEFAULT, GSP_DEFAULT, GSP_END)
46 * with null output. This will force the parser to read the entire sequence,
47 * not leaving garbage on the input if it does not recognize the specific
48 * sequence.
49 */
50
[d9c8c81]51#include <adt/hash_table.h>
[062d900]52#include <adt/hash.h>
[76d0981d]53#include <stdbool.h>
[b0b5628]54#include <stdlib.h>
55#include <stdio.h>
[b6a088f]56#include "gsp.h"
[b0b5628]57
58/*
[062d900]59 * Transition function hash table operations.
[b0b5628]60 */
[062d900]61typedef struct {
62 int old_state;
63 int input;
64} trans_key_t;
65
66static size_t trans_key_hash(void *key)
67{
68 trans_key_t *trans_key = (trans_key_t *)key;
69 return hash_combine(trans_key->input, trans_key->old_state);
70}
[b0b5628]71
[062d900]72static size_t trans_hash(const ht_link_t *item)
73{
74 gsp_trans_t *t = hash_table_get_inst(item, gsp_trans_t, link);
75 return hash_combine(t->input, t->old_state);
76}
[b0b5628]77
[062d900]78static bool trans_key_equal(void *key, const ht_link_t *item)
79{
80 trans_key_t *trans_key = (trans_key_t *)key;
81 gsp_trans_t *t = hash_table_get_inst(item, gsp_trans_t, link);
[a35b458]82
[062d900]83 return trans_key->input == t->input && trans_key->old_state == t->old_state;
84}
85
86static hash_table_ops_t trans_ops = {
87 .hash = trans_hash,
88 .key_hash = trans_key_hash,
89 .key_equal = trans_key_equal,
[4e00f87]90 .equal = NULL,
91 .remove_callback = NULL
[b0b5628]92};
93
[062d900]94
[b0b5628]95static gsp_trans_t *trans_lookup(gsp_t *p, int state, int input);
96static void trans_insert(gsp_t *p, gsp_trans_t *t);
97static gsp_trans_t *trans_new(void);
98
[062d900]99/** Initialize scancode parser. */
[b0b5628]100void gsp_init(gsp_t *p)
101{
102 p->states = 1;
[062d900]103 hash_table_create(&p->trans, 0, 0, &trans_ops);
[b0b5628]104}
105
106/** Insert a series of definitions into the parser.
107 *
108 * @param p The parser.
109 * @param defs Definition list. Each definition starts with two output values
110 * (mods, key) and continues with a sequence of input values
111 * terminated with GSP_END. The definition list is terminated
112 * with two zeroes (0, 0) for output values.
113 */
114int gsp_insert_defs(gsp_t *p, const int *defs)
115{
116 unsigned mods, key;
117 const int *dp;
118 int rc;
119
120 dp = defs;
121
[76d0981d]122 while (true) {
[b0b5628]123 /* Read the output values. */
124 mods = *dp++;
125 key = *dp++;
[1433ecda]126 if (key == 0)
127 break;
[b0b5628]128
[dcbb3ec]129 /* Insert one sequence. */
[b0b5628]130 rc = gsp_insert_seq(p, dp, mods, key);
131 if (rc != 0)
132 return rc;
133
134 /* Skip to the next definition. */
135 while (*dp != GSP_END)
136 ++dp;
137 ++dp;
138 }
139
140 return 0;
141}
142
143/** Insert one sequence into the parser.
144 *
145 * @param p The parser.
146 * @param seq Sequence of input values terminated with GSP_END.
147 * @param mods Corresponsing output value.
148 * @param key Corresponsing output value.
149 */
150int gsp_insert_seq(gsp_t *p, const int *seq, unsigned mods, unsigned key)
151{
152 int state;
153 gsp_trans_t *t;
154
155 state = 0;
156 t = NULL;
157
158 /* Input sequence must be non-empty. */
159 if (*seq == GSP_END)
160 return -1;
161
162 while (*(seq + 1) != GSP_END) {
163 t = trans_lookup(p, state, *seq);
164 if (t == NULL) {
165 /* Create new state. */
166 t = trans_new();
167 t->old_state = state;
168 t->input = *seq;
169 t->new_state = p->states++;
170
171 t->out_mods = 0;
172 t->out_key = 0;
173
174 trans_insert(p, t);
175 }
176 state = t->new_state;
177 ++seq;
178 }
179
180 /* Process the last transition. */
181 t = trans_lookup(p, state, *seq);
182 if (t != NULL) {
183 exit(1);
184 return -1; /* Conflicting definition. */
185 }
186
187 t = trans_new();
188 t->old_state = state;
189 t->input = *seq;
190 t->new_state = 0;
191
192 t->out_mods = mods;
193 t->out_key = key;
194
195 trans_insert(p, t);
196
197 return 0;
198}
199
200/** Compute one parser step.
201 *
202 * Computes the next state and output values for a given state and input.
203 * This handles everything including restarts and default branches.
204 *
205 * @param p The parser.
206 * @param state Old state.
207 * @param input Input symbol (scancode).
208 * @param mods Output value (modifier).
209 * @param key Output value (key).
210 * @return New state.
211 */
212int gsp_step(gsp_t *p, int state, int input, unsigned *mods, unsigned *key)
213{
214 gsp_trans_t *t;
215
216 t = trans_lookup(p, state, input);
217 if (t == NULL) {
218 t = trans_lookup(p, state, GSP_DEFAULT);
219 }
220
221 if (t == NULL) {
[dcbb3ec]222 printf("gsp_step: not found, state=%d, input=0x%x\n",
223 state, input);
[0b4a67a]224 *mods = 0;
225 *key = 0;
[b0b5628]226 return 0;
227 }
228
229 *mods = t->out_mods;
230 *key = t->out_key;
[dcbb3ec]231
[b0b5628]232 return t->new_state;
233}
234
235/** Transition function lookup.
236 *
237 * Returns the value of the transition function for the given state
238 * and input. Note that the transition must be specified precisely,
239 * to obtain the default branch use input = GSP_DEFAULT.
240 *
241 * @param p Parser.
242 * @param state Current state.
243 * @param input Input value.
244 * @return The transition or @c NULL if not defined.
245 */
246static gsp_trans_t *trans_lookup(gsp_t *p, int state, int input)
247{
[062d900]248 ht_link_t *item;
[a35b458]249
[062d900]250 trans_key_t key = {
251 .input = input,
252 .old_state = state
253 };
254
255 item = hash_table_find(&p->trans, &key);
[1433ecda]256 if (item == NULL)
257 return NULL;
[b0b5628]258
[062d900]259 return hash_table_get_inst(item, gsp_trans_t, link);
[b0b5628]260}
261
262/** Define a new transition.
263 *
264 * @param p The parser.
265 * @param t Transition with all fields defined.
266 */
267static void trans_insert(gsp_t *p, gsp_trans_t *t)
268{
[062d900]269 hash_table_insert(&p->trans, &t->link);
[b0b5628]270}
271
272/** Allocate transition structure. */
273static gsp_trans_t *trans_new(void)
274{
275 gsp_trans_t *t;
276
277 t = malloc(sizeof(gsp_trans_t));
278 if (t == NULL) {
279 printf("Memory allocation failed.\n");
280 exit(1);
281 }
282
283 return t;
284}
285
286
287/**
288 * @}
[1b20da0]289 */
Note: See TracBrowser for help on using the repository browser.