src/liblpfk.c

Tue, 26 Aug 2008 13:18:18 +0100

author
Philip Pemberton <philpem@philpem.me.uk>
date
Tue, 26 Aug 2008 13:18:18 +0100
changeset 1
3e775df109e6
parent 0
745037d69d81
child 2
071056e12321
permissions
-rw-r--r--

added: Vim folding markers to all functions
added: Cached LED write (TODO: add to test app)
added: LPFK keys no longer active by default
TODO: build Doxyfile for Doxygen documentation generation

     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(const char *port, LPFK_CTX *ctx)
    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 		// Enable the LPFK
   152 		write(fd, "\x08", 1);
   153 		ctx->enabled = true;
   155 		// Return OK status
   156 		return LPFK_E_OK;
   157 	}
   158 }
   160 /* }}} */
   162 /* lpfk_close {{{ */
   163 int lpfk_close(LPFK_CTX *ctx)
   164 {
   165 	int status;
   167 	// 0x09: DISABLE. Stop the LPFK responding to keystrokes.
   168 	write(ctx->fd, "\x09", 1);
   170 	// turn all the LEDs off
   171 	lpfk_set_leds(ctx, false);
   173 	// set RTS false to put the LPFK into reset
   174 	ioctl(ctx->fd, TIOCMGET, &status);
   175 	status &= ~TIOCM_RTS;
   176 	ioctl(ctx->fd, TIOCMSET, &status);
   178 	// Restore the port state and close the serial port.
   179 	tcsetattr(ctx->fd, TCSANOW, &ctx->oldtio);
   180 	close(ctx->fd);
   182 	// Done!
   183 	return LPFK_E_OK;
   184 }
   185 /* }}} */
   187 /* lpfk_enable {{{ */
   188 int lpfk_enable(LPFK_CTX *ctx, int val)
   189 {
   190 	if (val) {
   191 		// val == true, enable the LPFK
   192 		if (write(ctx->fd, "\x08", 1) != 1) {
   193 			ctx->enabled = true;
   194 			return LPFK_E_COMMS;
   195 		}
   196 	} else {
   197 		// val == false, disable the LPFK
   198 		if (write(ctx->fd, "\x09", 1) != 1) {
   199 			return LPFK_E_COMMS;
   200 		}
   201 	}
   203 	// update the context, return success
   204 	ctx->enabled = val;
   205 	return LPFK_E_OK;
   206 }
   208 /* }}} */
   210 /* lpfk_set_led_cached {{{ */
   211 int lpfk_set_led_cached(LPFK_CTX *ctx, const int num, const int state)
   212 {
   213 	int i;
   214 	time_t tm;
   215 	unsigned long mask, leds;
   216 	unsigned char buf[5];
   217 	unsigned char status;
   219 	// check parameters
   220 	if ((num < 0) || (num > 31)) {
   221 		return LPFK_E_PARAM;
   222 	}
   224 	// parameters OK, now build the LED mask
   225 	mask = (0x80 >> (num % 8)) << ((3 - (num / 8)) * 8);
   227 	// mask the specified bit
   228 	if (state) {
   229 		ctx->led_mask |= mask;
   230 	} else {
   231 		ctx->led_mask &= ~mask;
   232 	}
   234 	return LPFK_E_OK;
   235 }
   236 /* }}} */
   238 /* lpfk_set_leds_cached {{{ */
   239 int lpfk_set_leds_cached(LPFK_CTX *ctx, const int state)
   240 {
   241 	int i;
   242 	time_t tm;
   243 	unsigned long leds;
   244 	unsigned char buf[5];
   245 	unsigned char status;
   247 	if (state) {
   248 		// all LEDs on
   249 		ctx->led_mask = 0xFFFFFFFF;
   250 	} else {
   251 		// all LEDs off
   252 		ctx->led_mask = 0x00000000;
   253 	}
   255 	return LPFK_E_OK;
   256 }
   257 /* }}} */
   259 /* lpfk_update_leds {{{ */
   260 int lpfk_update_leds(LPFK_CTX *ctx)
   261 {
   262 	int i;
   263 	time_t tm;
   264 	unsigned char buf[5];
   265 	unsigned char status;
   267 	// send new LED mask to the LPFK
   268 	buf[0] = 0x94;
   269 	buf[1] = ctx->led_mask >> 24;
   270 	buf[2] = ctx->led_mask >> 16;
   271 	buf[3] = ctx->led_mask >> 8;
   272 	buf[4] = ctx->led_mask & 0xff;
   274 	// make 5 attempts to set the LEDs
   275 	for (i=0; i<5; i++) {
   276 		if (write(ctx->fd, &buf, 5) < 5) {
   277 			continue;
   278 		}
   280 		// check for response -- 0x81 = OK, 0x80 = retransmit
   281 		// save current time (in seconds)
   282 		tm = time(NULL);
   284 		// loop until 2 seconds have passed, or LPFK responds
   285 		status = 0x00;
   286 		do {
   287 			// read data, loop if not successful
   288 			if (read(ctx->fd, &status, 1) < 1) {
   289 				continue;
   290 			}
   292 			// we got some data, what is it?
   293 			if (status == 0x81) {
   294 				// 0x81 -- received successfully
   295 				break;
   296 			}
   297 		} while ((time(NULL) - tm) < 2);
   299 		// status OK?
   300 		if (status == 0x81) {
   301 			// 0x81: OK
   302 			break;
   303 		} else if (status == 0x80) {
   304 			// 0x80: Retransmit request
   305 			continue;
   306 		}
   307 	}
   309 	return LPFK_E_OK;
   310 }
   311 /* }}} */
   313 /* lpfk_set_led {{{ */
   314 int lpfk_set_led(LPFK_CTX *ctx, const int num, const int state)
   315 {
   316 	lpfk_set_led_cached(ctx, num, state);
   317 	return lpfk_update_leds(ctx);
   318 }
   319 /* }}} */
   321 /* lpfk_set_leds {{{ */
   322 int lpfk_set_leds(LPFK_CTX *ctx, const int state)
   323 {
   324 	lpfk_set_leds_cached(ctx, state);
   325 	return lpfk_update_leds(ctx);
   326 }
   327 /* }}} */
   329 /* lpfk_get_led {{{ */
   330 int lpfk_get_led(LPFK_CTX *ctx, const int num)
   331 {
   332 	unsigned long mask;
   334 	// check parameters
   335 	if ((num < 0) || (num > 31)) {
   336 		return false;
   337 	}
   339 	// parameters OK, now build the LED mask
   340 	mask = (0x80 >> (num % 8)) << ((3 - (num / 8)) * 8);
   341 	if (ctx->led_mask & mask) {
   342 		return true;
   343 	} else {
   344 		return false;
   345 	}
   346 }
   347 /* }}} */
   349 /* lpfk_read {{{ */
   350 int lpfk_read(LPFK_CTX *ctx)
   351 {
   352 	int nbytes;
   353 	unsigned char key;
   355 	// make sure the LPFK is enabled before trying to read a scancode
   356 	if (!ctx->enabled) {
   357 		return LPFK_E_NOT_ENABLED;
   358 	}
   360 	// try and read a byte (keycode) from the LPFK
   361 	nbytes = read(ctx->fd, &key, 1);
   363 	if ((nbytes < 1) || (key > 31)) {
   364 		// no keys buffered, or keycode invalid.
   365 		return -1;
   366 	} else {
   367 		// key buffered, pass it along.
   368 		return key;
   369 	}
   370 }
   371 /* }}} */