src/liblpfk.c

Tue, 26 Aug 2008 22:00:24 +0100

author
Philip Pemberton <philpem@philpem.me.uk>
date
Tue, 26 Aug 2008 22:00:24 +0100
changeset 6
aed399ff850b
parent 4
81d49d42ad6a
permissions
-rw-r--r--

fix typo in makefile that broke 'make clean'

     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 LPFK_E_NO_KEYS;
   365 	} else {
   366 		// key buffered, pass it along.
   367 		return key;
   368 	}
   369 }
   370 /* }}} */