source: mainline/uspace/srv/hid/input/generic/gsp.c@ 0ca7286

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 0ca7286 was 0ca7286, checked in by Adam Hraska <adam.hraska+hos@…>, 13 years ago

Added resizing to user space (single-threaded) hash_table. Resizes in a way to mitigate effects of bad hash functions. Change of interface affected many files.

  • Property mode set to 100644
File size: 7.1 KB
Line 
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
31 * @ingroup input
32 * @{
33 */
34/** @file
35 * @brief Generic scancode parser.
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
51#include <gsp.h>
52#include <adt/hash_table.h>
53#include <stdlib.h>
54#include <stdio.h>
55
56/*
57 * Hash table operations for the transition function.
58 */
59
60static size_t trans_op_hash(const link_t *item);
61static size_t trans_op_key_hash(unsigned long key[]);
62static bool trans_op_match(unsigned long key[], size_t keys, const link_t *item);
63
64static hash_table_ops_t trans_ops = {
65 .hash = trans_op_hash,
66 .key_hash = trans_op_key_hash,
67 .match = trans_op_match,
68 .equal = 0,
69 .remove_callback = 0
70};
71
72static gsp_trans_t *trans_lookup(gsp_t *p, int state, int input);
73static void trans_insert(gsp_t *p, gsp_trans_t *t);
74static gsp_trans_t *trans_new(void);
75
76/** Initialize scancode parser. */
77void gsp_init(gsp_t *p)
78{
79 p->states = 1;
80 hash_table_create(&p->trans, 0, 2, &trans_ops);
81}
82
83/** Insert a series of definitions into the parser.
84 *
85 * @param p The parser.
86 * @param defs Definition list. Each definition starts with two output values
87 * (mods, key) and continues with a sequence of input values
88 * terminated with GSP_END. The definition list is terminated
89 * with two zeroes (0, 0) for output values.
90 */
91int gsp_insert_defs(gsp_t *p, const int *defs)
92{
93 unsigned mods, key;
94 const int *dp;
95 int rc;
96
97 dp = defs;
98
99 while (1) {
100 /* Read the output values. */
101 mods = *dp++;
102 key = *dp++;
103 if (key == 0) break;
104
105 /* Insert one sequence. */
106 rc = gsp_insert_seq(p, dp, mods, key);
107 if (rc != 0)
108 return rc;
109
110 /* Skip to the next definition. */
111 while (*dp != GSP_END)
112 ++dp;
113 ++dp;
114 }
115
116 return 0;
117}
118
119/** Insert one sequence into the parser.
120 *
121 * @param p The parser.
122 * @param seq Sequence of input values terminated with GSP_END.
123 * @param mods Corresponsing output value.
124 * @param key Corresponsing output value.
125 */
126int gsp_insert_seq(gsp_t *p, const int *seq, unsigned mods, unsigned key)
127{
128 int state;
129 gsp_trans_t *t;
130
131 state = 0;
132 t = NULL;
133
134 /* Input sequence must be non-empty. */
135 if (*seq == GSP_END)
136 return -1;
137
138 while (*(seq + 1) != GSP_END) {
139 t = trans_lookup(p, state, *seq);
140 if (t == NULL) {
141 /* Create new state. */
142 t = trans_new();
143 t->old_state = state;
144 t->input = *seq;
145 t->new_state = p->states++;
146
147 t->out_mods = 0;
148 t->out_key = 0;
149
150 trans_insert(p, t);
151 }
152 state = t->new_state;
153 ++seq;
154 }
155
156 /* Process the last transition. */
157 t = trans_lookup(p, state, *seq);
158 if (t != NULL) {
159 exit(1);
160 return -1; /* Conflicting definition. */
161 }
162
163 t = trans_new();
164 t->old_state = state;
165 t->input = *seq;
166 t->new_state = 0;
167
168 t->out_mods = mods;
169 t->out_key = key;
170
171 trans_insert(p, t);
172
173 return 0;
174}
175
176/** Compute one parser step.
177 *
178 * Computes the next state and output values for a given state and input.
179 * This handles everything including restarts and default branches.
180 *
181 * @param p The parser.
182 * @param state Old state.
183 * @param input Input symbol (scancode).
184 * @param mods Output value (modifier).
185 * @param key Output value (key).
186 * @return New state.
187 */
188int gsp_step(gsp_t *p, int state, int input, unsigned *mods, unsigned *key)
189{
190 gsp_trans_t *t;
191
192 t = trans_lookup(p, state, input);
193 if (t == NULL) {
194 t = trans_lookup(p, state, GSP_DEFAULT);
195 }
196
197 if (t == NULL) {
198 printf("gsp_step: not found, state=%d, input=0x%x\n",
199 state, input);
200 *mods = 0;
201 *key = 0;
202 return 0;
203 }
204
205 *mods = t->out_mods;
206 *key = t->out_key;
207
208 return t->new_state;
209}
210
211/** Transition function lookup.
212 *
213 * Returns the value of the transition function for the given state
214 * and input. Note that the transition must be specified precisely,
215 * to obtain the default branch use input = GSP_DEFAULT.
216 *
217 * @param p Parser.
218 * @param state Current state.
219 * @param input Input value.
220 * @return The transition or @c NULL if not defined.
221 */
222static gsp_trans_t *trans_lookup(gsp_t *p, int state, int input)
223{
224 link_t *item;
225 unsigned long key[2];
226
227 key[0] = state;
228 key[1] = input;
229
230 item = hash_table_find(&p->trans, key);
231 if (item == NULL) return NULL;
232
233 return hash_table_get_instance(item, gsp_trans_t, link);
234}
235
236/** Define a new transition.
237 *
238 * @param p The parser.
239 * @param t Transition with all fields defined.
240 */
241static void trans_insert(gsp_t *p, gsp_trans_t *t)
242{
243 hash_table_insert(&p->trans, &t->link);
244}
245
246/** Allocate transition structure. */
247static gsp_trans_t *trans_new(void)
248{
249 gsp_trans_t *t;
250
251 t = malloc(sizeof(gsp_trans_t));
252 if (t == NULL) {
253 printf("Memory allocation failed.\n");
254 exit(1);
255 }
256
257 return t;
258}
259
260/*
261 * Transition function hash table operations.
262 */
263
264static size_t trans_op_key_hash(unsigned long key[])
265{
266 return (key[0] * 17 + key[1]);
267}
268
269static size_t trans_op_hash(const link_t *item)
270{
271 gsp_trans_t *t = hash_table_get_instance(item, gsp_trans_t, link);
272 unsigned long key[] = {
273 t->old_state,
274 t->input
275 };
276
277 return trans_op_key_hash(key);
278}
279
280static bool trans_op_match(unsigned long key[], size_t keys, const link_t *item)
281{
282 gsp_trans_t *t = hash_table_get_instance(item, gsp_trans_t, link);
283 return ((key[0] == (unsigned long) t->old_state)
284 && (key[1] == (unsigned long) t->input));
285}
286
287/**
288 * @}
289 */
Note: See TracBrowser for help on using the repository browser.