Tue, 26 Aug 2008 18:45:45 +0100
made lpfk_enable 'val' param const to match header
1 /****************************************************************************
2 * Project: liblpfk
3 * Purpose: Driver library for the IBM 6094-020 Lighted Program Function
4 * Keyboard.
5 * Version: 1.0
6 * Author: Philip Pemberton <philpem@philpem.me.uk>
7 *
8 * The latest version of this library is available from
9 * <http://www.philpem.me.uk/code/liblpfk/>.
10 *
11 * Copyright (c) 2008, Philip Pemberton
12 * All rights reserved.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 *
18 * * Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
20 * * Redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in
22 * the documentation and/or other materials provided with the
23 * distribution.
24 * * Neither the name of the project nor the names of its
25 * contributors may be used to endorse or promote products derived
26 * from this software without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
31 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
32 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
33 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
34 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
35 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
36 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
37 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
38 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 ****************************************************************************/
41 /**
42 * @file liblpfk.c
43 * @brief liblpfk library, main source
44 */
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <fcntl.h>
49 #include <unistd.h>
50 #include <termios.h>
51 #include <sys/ioctl.h>
52 #include <time.h>
53 #include <string.h>
54 #include <stdbool.h>
55 #include <stdio.h>
57 #include "liblpfk.h"
59 /* lpfk_open {{{ */
60 int lpfk_open(LPFK_CTX *ctx, const char *port)
61 {
62 struct termios newtio;
63 int status;
64 int fd;
65 int i;
67 // open the serial port
68 fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY);
69 if (fd < 0) return LPFK_E_PORT_OPEN;
71 // save current port settings
72 tcgetattr(fd, &ctx->oldtio);
74 // set up new parameters
75 memset(&newtio, 0, sizeof(newtio));
76 // 9600 baud, 8 bits, parity enabled, odd parity
77 newtio.c_cflag = B9600 | CS8 | PARENB | PARODD | CLOCAL | CREAD;
78 newtio.c_iflag = 0;
79 newtio.c_oflag = 0;
81 // set input mode -- non canonical, no echo
82 newtio.c_lflag = 0;
84 // inter-character timer unused
85 newtio.c_cc[VTIME] = 0;
86 // read does not block waiting for characters if there are none in the buffer
87 newtio.c_cc[VMIN] = 0;
89 // flush input buffer
90 tcflush(fd, TCIFLUSH);
92 // set new port config
93 tcsetattr(fd, TCSANOW, &newtio);
95 // set RTS true to pull the LPFK out of reset
96 ioctl(fd, TIOCMGET, &status);
97 status |= TIOCM_RTS;
98 ioctl(fd, TIOCMSET, &status);
100 // wait a few seconds for the LPFK to become ready
101 sleep(2);
103 // 0x06: READ CONFIGURATION. LPFK sends 0x03 in response.
104 // Try five times to wake it up.
105 status = false;
106 for (i=0; i<5; i++) {
107 unsigned char buf;
108 time_t tm;
110 // Send 0x06: READ CONFIGURATION, loop on failure
111 if (write(fd, "\x06", 1) < 1) {
112 continue;
113 }
115 // save current time (in seconds)
116 tm = time(NULL);
118 // loop until 2 seconds have passed, or LPFK responds
119 status = false;
120 do {
121 // read data, loop if not successful
122 if (read(fd, &buf, 1) < 1) {
123 continue;
124 }
126 // we got some data, what is it?
127 if (buf == 0x03) {
128 // 0x03 -- correct response. we're done.
129 status = true;
130 }
131 } while (((time(NULL) - tm) < 2) && (!status));
133 // exit loop if we got the LPFK to talk
134 if (status) {
135 break;
136 }
137 }
139 // Did the LPFK respond?
140 if (!status) {
141 // LPFK isn't talking. Restore serial port state and exit.
142 tcsetattr(fd, TCSANOW, &ctx->oldtio);
143 close(fd);
145 return LPFK_E_NOT_PRESENT;
146 } else {
147 // Initialise LPFK context
148 ctx->led_mask = 0;
149 ctx->fd = fd;
151 // Disable LPFK keyboard scanning
152 lpfk_enable(ctx, false);
154 // Return OK status
155 return LPFK_E_OK;
156 }
157 }
159 /* }}} */
161 /* lpfk_close {{{ */
162 int lpfk_close(LPFK_CTX *ctx)
163 {
164 int status;
166 // 0x09: DISABLE. Stop the LPFK responding to keystrokes.
167 write(ctx->fd, "\x09", 1);
169 // turn all the LEDs off
170 lpfk_set_leds(ctx, false);
172 // set RTS false to put the LPFK into reset
173 ioctl(ctx->fd, TIOCMGET, &status);
174 status &= ~TIOCM_RTS;
175 ioctl(ctx->fd, TIOCMSET, &status);
177 // Restore the port state and close the serial port.
178 tcsetattr(ctx->fd, TCSANOW, &ctx->oldtio);
179 close(ctx->fd);
181 // Done!
182 return LPFK_E_OK;
183 }
184 /* }}} */
186 /* lpfk_enable {{{ */
187 int lpfk_enable(LPFK_CTX *ctx, const int val)
188 {
189 if (val) {
190 // val == true, enable the LPFK
191 if (write(ctx->fd, "\x08", 1) != 1) {
192 ctx->enabled = true;
193 return LPFK_E_COMMS;
194 }
195 } else {
196 // val == false, disable the LPFK
197 if (write(ctx->fd, "\x09", 1) != 1) {
198 return LPFK_E_COMMS;
199 }
200 }
202 // update the context, return success
203 ctx->enabled = val;
204 return LPFK_E_OK;
205 }
207 /* }}} */
209 /* lpfk_set_led_cached {{{ */
210 int lpfk_set_led_cached(LPFK_CTX *ctx, const int num, const int state)
211 {
212 int i;
213 time_t tm;
214 unsigned long mask, leds;
215 unsigned char buf[5];
216 unsigned char status;
218 // check parameters
219 if ((num < 0) || (num > 31)) {
220 return LPFK_E_PARAM;
221 }
223 // parameters OK, now build the LED mask
224 mask = (0x80 >> (num % 8)) << ((3 - (num / 8)) * 8);
226 // mask the specified bit
227 if (state) {
228 ctx->led_mask |= mask;
229 } else {
230 ctx->led_mask &= ~mask;
231 }
233 return LPFK_E_OK;
234 }
235 /* }}} */
237 /* lpfk_set_leds_cached {{{ */
238 int lpfk_set_leds_cached(LPFK_CTX *ctx, const int state)
239 {
240 int i;
241 time_t tm;
242 unsigned long leds;
243 unsigned char buf[5];
244 unsigned char status;
246 if (state) {
247 // all LEDs on
248 ctx->led_mask = 0xFFFFFFFF;
249 } else {
250 // all LEDs off
251 ctx->led_mask = 0x00000000;
252 }
254 return LPFK_E_OK;
255 }
256 /* }}} */
258 /* lpfk_update_leds {{{ */
259 int lpfk_update_leds(LPFK_CTX *ctx)
260 {
261 int i;
262 time_t tm;
263 unsigned char buf[5];
264 unsigned char status;
266 // send new LED mask to the LPFK
267 buf[0] = 0x94;
268 buf[1] = ctx->led_mask >> 24;
269 buf[2] = ctx->led_mask >> 16;
270 buf[3] = ctx->led_mask >> 8;
271 buf[4] = ctx->led_mask & 0xff;
273 // make 5 attempts to set the LEDs
274 for (i=0; i<5; i++) {
275 if (write(ctx->fd, &buf, 5) < 5) {
276 continue;
277 }
279 // check for response -- 0x81 = OK, 0x80 = retransmit
280 // save current time (in seconds)
281 tm = time(NULL);
283 // loop until 2 seconds have passed, or LPFK responds
284 status = 0x00;
285 do {
286 // read data, loop if not successful
287 if (read(ctx->fd, &status, 1) < 1) {
288 continue;
289 }
291 // we got some data, what is it?
292 if (status == 0x81) {
293 // 0x81 -- received successfully
294 break;
295 }
296 } while ((time(NULL) - tm) < 2);
298 // status OK?
299 if (status == 0x81) {
300 // 0x81: OK
301 break;
302 } else if (status == 0x80) {
303 // 0x80: Retransmit request
304 continue;
305 }
306 }
308 return LPFK_E_OK;
309 }
310 /* }}} */
312 /* lpfk_set_led {{{ */
313 int lpfk_set_led(LPFK_CTX *ctx, const int num, const int state)
314 {
315 lpfk_set_led_cached(ctx, num, state);
316 return lpfk_update_leds(ctx);
317 }
318 /* }}} */
320 /* lpfk_set_leds {{{ */
321 int lpfk_set_leds(LPFK_CTX *ctx, const int state)
322 {
323 lpfk_set_leds_cached(ctx, state);
324 return lpfk_update_leds(ctx);
325 }
326 /* }}} */
328 /* lpfk_get_led {{{ */
329 int lpfk_get_led(LPFK_CTX *ctx, const int num)
330 {
331 unsigned long mask;
333 // check parameters
334 if ((num < 0) || (num > 31)) {
335 return false;
336 }
338 // parameters OK, now build the LED mask
339 mask = (0x80 >> (num % 8)) << ((3 - (num / 8)) * 8);
340 if (ctx->led_mask & mask) {
341 return true;
342 } else {
343 return false;
344 }
345 }
346 /* }}} */
348 /* lpfk_read {{{ */
349 int lpfk_read(LPFK_CTX *ctx)
350 {
351 int nbytes;
352 unsigned char key;
354 // make sure the LPFK is enabled before trying to read a scancode
355 if (!ctx->enabled) {
356 return LPFK_E_NOT_ENABLED;
357 }
359 // try and read a byte (keycode) from the LPFK
360 nbytes = read(ctx->fd, &key, 1);
362 if ((nbytes < 1) || (key > 31)) {
363 // no keys buffered, or keycode invalid.
364 return -1;
365 } else {
366 // key buffered, pass it along.
367 return key;
368 }
369 }
370 /* }}} */