1
1
hid/devh/gpiokbd/gpiokbd.c

707 строки
22 KiB
C

/***************************************************************************
*
* Copyright 2017, CBD BC
*
* GPIO hardware keyboard driver
*
***************************************************************************/
#include <hw/inout.h>
#include "gpiokbd.h"
#include <gulliver.h>
#include <sys/usbdi.h>
#include <sys/io-hid.h>
#include <math.h>
#include <stdarg.h>
#include <sys/slog.h>
#include <sys/slogcodes.h>
#include <sys/hidut.h>
#include "sys/usbcodes.h"
#include <stdint.h>
#include <sys/mman.h>
int kbd_coid;
int kbd_chid;
pthread_t kbd_tid;
int fd;
int gpiokbd_init( void *dll_hdl, dispatch_t *dpp, io_hid_self_t *ioh, char *options );
int gpiokbd_stop();
int gpiokbd_client_attach( int reg_hdl, void *user );
int gpiokbd_client_detach( int reg_hdl, void *user );
int gpiokbd_report_read( int reg_hdl, void *user, _uint8 rid, _uint16 rtype, void *rdata, _uint16 rlen );
int gpiokbd_report_write( int reg_hdl, void *user, _uint8 rid, _uint16 rtype, void *rdata, _uint16 rlen );
int gpiokbd_get_idle( int reg_hdl, void *user, _uint8 rid, _uint16 *idle_rate );
int gpiokbd_set_idle( int reg_hdl, void *user, _uint8 rid, _uint16 idle_rate );
int gpiokbd_get_protocol( int reg_hdl, void *user, _uint8 *protocol );
int gpiokbd_set_protocol( int reg_hdl, void *user, _uint8 protocol );
int gpiokbd_reset( int reg_hdl, void *user );
int gpiokbd_rbuffer_alloc( _uint16 size, void **bptr );
int gpiokbd_get_string( int reg_hdl, void *user, _uint16 request, void **str );
int gpiokbd_get_indexed_string( int reg_hdl, void *user, _uint16 index, void **str );
int gpiokbd_rbuffer_free( void *bptr );
gpiokbd_ctrl_t KCtrl;
struct hiddi_device_instance;
io_hid_dll_entry_t io_hid_dll_entry = {
"devh-gpiokbd",
2,
gpiokbd_init,
gpiokbd_stop
};
io_hid_registrant_funcs_t gpiokbd_funcs = {
9,
gpiokbd_client_attach,
gpiokbd_client_detach,
gpiokbd_rbuffer_alloc,
gpiokbd_rbuffer_free,
gpiokbd_report_read,
gpiokbd_report_write,
gpiokbd_get_idle,
gpiokbd_set_idle,
gpiokbd_get_protocol,
gpiokbd_set_protocol,
// gpiokbd_get_string,
0,
gpiokbd_get_indexed_string,
gpiokbd_reset,
};
char *gpiokbd_opts[] = {
"poll_interval",
"verbose",
"key1mod",
"key1code",
"key2mod",
"key2code",
"key3mod",
"key3code",
"key4mod",
"key4code",
NULL
};
static unsigned char kbd_report_descr[] = { /* Report & Boot modes */
0x05, 0x01, // Usage Oage (Generic Desktop)
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection (Application)
0x05, 0x07, // Usage Page (Key Codes)
0x19, 0xE0, // Usage Minimum (224)
0x29, 0xE7, // Usage Maximum (231)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
0x81, 0x02, // Input (Data, Variable, Absolute), ;Modifier byte
0x95, 0x01, // Report Count (1)
0x75, 0x08, // Report Size (8)
0x81, 0x01, // Input (Constant), ;Reserved byte
0x95, 0x05, // Report Count (5)
0x75, 0x01, // Report Size (1)
0x05, 0x08, // Usage Page (Page# for LEDs)
0x19, 0x01, // Usage Minimum (1)
0x29, 0x05, // Usage Maximum (5)
0x91, 0x02, // Output (Data, Variable, Absolute), ;LED report
0x95, 0x01, // Report Count (1)
0x75, 0x03, // Report Size (3)
0x91, 0x01, // Output (Constant), ;LED report padding
0x95, 0x06, // Report Count (6)
0x75, 0x08, // Report Size (8)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xff, 0x00, // Logical Maximum(255)
0x05, 0x07, // Usage Page (Key Codes)
0x19, 0x00, // Usage Minimum (0)
0x2a, 0xff, 0x00, // Usage Maximum (255)
0x81, 0x00, // Input (Data, Array), ;Key arrays (6 bytes)
0xC0 // End Collection
};
/* Slog wrapper function */
ssize_t hid_slogf( const char *fmt, ... )
{
ssize_t ret = 0;
va_list arglist;
int verbosity = KCtrl.verbose;
if ( verbosity > 2 )
{
va_start( arglist, fmt );
vfprintf( stderr, fmt, arglist );
va_end( arglist );
fprintf( stderr, "\n" );
}
va_start( arglist, fmt );
ret = vslogf( _SLOGC_INPUT, _SLOG_INFO, fmt, arglist );
va_end( arglist );
return (ret);
}
/* Setup report descriptor and register with io-hid. */
int gpiokbd_get_report_descriptor( gpiokbd_ctrl_t KCtrl )
{
/* Structures we need to register device in io-hid */
io_hid_registrant_t dev;
hidd_device_ident_t device_ident;
dev.desc = kbd_report_descr;
dev.dlen = sizeof( kbd_report_descr );
device_ident.vendor_id = GPIOKBD_VENDOR_ID;
device_ident.product_id = GPIOKBD_DEVICE_ID;
device_ident.version = GPIOKBD_VERSION_ID;
dev.flags = 0;
dev.device_ident = &device_ident;
dev.user_hdl = &KCtrl;
dev.funcs = &gpiokbd_funcs;
/* register device with hiddi */
return (*(KCtrl.ioh->reg))( KCtrl.dll_hdl, &dev, &(KCtrl.hid_hdl) );
}
/* Client attaches. First attachment to the device, so enable the device. */
int gpiokbd_client_attach( int reg_hdl, void *user )
{
return (EOK);
}
/* Last client detached from the device, so disable the device. */
int gpiokbd_client_detach( int reg_hdl, void *user )
{
return (EOK);
}
/* Client called read report (polls the device) */
int gpiokbd_report_read( int reg_hdl, void *user, _uint8 rid, _uint16 rtype, void *rdata, _uint16 rlen )
{
return (ENOTSUP);
}
/* Client is sending output/feature report to device. We will use this for
* calibration requests from a controller calibration utility. */
int gpiokbd_report_write( int reg_hdl, void *user, _uint8 rid, _uint16 rtype, void *rdata, _uint16 rlen )
{
return (ENOTSUP);
}
/* Fetch the idle time. */
int gpiokbd_get_idle( int reg_hdl, void *user, _uint8 rid, _uint16 *idle_rate )
{
return (ENOTSUP);
}
/* Set the idle time. */
int gpiokbd_set_idle( int reg_hdl, void *user, _uint8 rid, _uint16 idle_rate )
{
return (EOK);
}
/* Get the protocol. */
int gpiokbd_get_protocol( int reg_hdl, void *user, _uint8 *protocol )
{
return (EOK);
}
/* Set the protocol. */
int gpiokbd_set_protocol( int reg_hdl, void *user, _uint8 protocol )
{
return (EOK);
}
int gpiokbd_get_indexed_string( int reg_hdl, void *user, _uint16 index, void **string )
{
return (EOK);
}
int gpiokbd_get_string( int reg_hdl, void *user, _uint16 request, void **string )
{
return (EOK);
}
int gpiokbd_reset( int reg_hdl, void *user )
{
return (EOK);
}
int gpiokbd_rbuffer_alloc( _uint16 size, void **bptr )
{
if ( (*bptr = malloc( size )) == NULL )
return (ENOMEM);
memset( *bptr, 0xff, size );
return (EOK);
}
int gpiokbd_rbuffer_free( void *bptr )
{
if ( NULL != bptr )
free( bptr );
return (EOK);
}
int gpiokbd_stop( void *dll_hdl )
{
return (EOK);
}
void *gpiokbd_event_handler( void *data )
{
int rcvid;
struct _gpio_key *key;
struct _pulse pulse;
keybInputReport_t buf;
int gpio_pin;
data = data;
while ( 1 )
{
if ( ( rcvid = MsgReceivePulse( kbd_chid, &pulse, sizeof( pulse ), NULL )) == -1 )
continue;
gpio_pin = pulse.value.sival_int;
hid_slogf( "devh-gpiokbd: pulse code %d recieved. GPIO interrupt from PIN %u", pulse.code, gpio_pin );
key = &KCtrl.key[0];
key->pressed = 1;
switch ( gpio_pin )
{
case GPIO_ROW_1:
if ( key->pressed == 1 )
{
buf.modifies = KCtrl.key[0].modifies;
buf.pressKeys[0] = KCtrl.key[0].scan_code;
buf.pressKeys[1] = KS_None;
} else {
buf.modifies = 0;
buf.pressKeys[0] = KS_None;
}
break;
case GPIO_ROW_2:
if ( key->pressed == 1 )
{
buf.modifies = KCtrl.key[1].modifies;
buf.pressKeys[0] = KCtrl.key[1].scan_code;
buf.pressKeys[1] = KS_None;
} else {
buf.modifies = 0;
buf.pressKeys[0] = KS_None;
}
break;
case GPIO_ROW_3:
if ( key->pressed == 1 )
{
buf.modifies = KCtrl.key[2].modifies;
buf.pressKeys[0] = KCtrl.key[2].scan_code;
buf.pressKeys[1] = KS_None;
} else {
buf.modifies = 0;
buf.pressKeys[0] = KS_None;
}
break;
case GPIO_LINE_1:
if ( key->pressed == 1 )
{
buf.modifies = KCtrl.key[3].modifies;
buf.pressKeys[0] = KCtrl.key[3].scan_code;
buf.pressKeys[1] = KS_None;
} else {
buf.modifies = 0;
buf.pressKeys[0] = KS_None;
}
break;
case GPIO_LINE_2:
if ( key->pressed == 1 )
{
buf.modifies = KCtrl.key[4].modifies;
buf.pressKeys[0] = KCtrl.key[4].scan_code;
buf.pressKeys[1] = KS_None;
} else {
buf.modifies = 0;
buf.pressKeys[0] = KS_None;
}
break;
default:
break;
}
buf.reserv = 0;
(*KCtrl.ioh->send_report)( KCtrl.hid_hdl, (void *)&buf, sizeof( buf ) );
}
}
int read_gpio_pin(int gpio_pin)
{
int status = 0;
int pin_status = -1;
gpio_devctl_t msg;
if ( KCtrl.verbose > 3 )
hid_slogf( "devh-gpiokbd: reading PIN %d", gpio_pin );
msg.set_input.pin_num = gpio_pin;
// Set pin direction
if ( (status = devctl( fd, DCMD_SET_INPUT, &msg, sizeof( msg ), NULL )) != EOK )
{
if ( KCtrl.verbose )
hid_slogf( "devh-gpiokbd: GPIO DCMD_SET_INPUT devctl failed; status = %d", status );
return (-1);
}
if ( ( status = devctl( fd, DCMD_READ, &msg, sizeof( msg ), NULL ) ) )
{
if ( KCtrl.verbose )
hid_slogf( "devh-gpiokbd: GPIO DCMD_READ devctl failed; status = %d", status );
return (-1);
}
if ( KCtrl.verbose > 3 )
hid_slogf( "devh-gpiokbd: GPIO Pin %d direction: INPUT value: %d",
msg.cmd_read.pin_num, msg.cmd_read.data );
pin_status = msg.cmd_read.data;
return pin_status;
}
void *gpiokbd_event_handler_polling( void *data )
{
struct _gpio_key *key;
keybInputReport_t buf;
int status;
gpio_devctl_t msg;
int gpio_row1 = 0;
int gpio_row2 = 0;
int gpio_row3 = 0;
int gpio_line1 = 0;
int gpio_line2 = 0;
data = data;
while ( 1 )
{
gpio_row1 = read_gpio_pin( GPIO_ROW_1 );
gpio_row2 = read_gpio_pin( GPIO_ROW_2 );
gpio_row3 = read_gpio_pin( GPIO_ROW_3 );
gpio_line1 = read_gpio_pin( GPIO_LINE_1 );
gpio_line2 = read_gpio_pin( GPIO_LINE_2 );
// Set pins direction
msg.set_output.pin_num = GPIO_LINE_1;
if ( (status = devctl( fd, DCMD_SET_OUTPUT, &msg, sizeof( msg ), NULL )) )
{
if ( KCtrl.verbose )
hid_slogf( "devh-gpiokbd: GPIO DCMD_SET_OUTPUT devctl failed: %s", strerror( status) );
}
msg.set_output.pin_num = GPIO_LINE_2;
if ( (status = devctl( fd, DCMD_SET_OUTPUT, &msg, sizeof( msg ), NULL )) )
{
if ( KCtrl.verbose )
hid_slogf( "devh-gpiokbd: GPIO DCMD_SET_OUTPUT devctl failed; status = %d", status );
}
msg.set_output.pin_num = GPIO_ROW_2;
if ( (status = devctl( fd, DCMD_SET_OUTPUT, &msg, sizeof( msg ), NULL )) )
{
if ( KCtrl.verbose )
hid_slogf( "devh-gpiokbd: GPIO DCMD_SET_OUTPUT devctl failed; status = %d", status );
}
msg.set_output.pin_num = GPIO_ROW_1;
if ( (status = devctl( fd, DCMD_SET_OUTPUT, &msg, sizeof( msg ), NULL )) )
{
if ( KCtrl.verbose )
hid_slogf( "devh-gpiokbd: GPIO DCMD_SET_OUTPUT devctl failed; status = %d", status );
}
msg.set_output.pin_num = GPIO_ROW_3;
if ( (status = devctl( fd, DCMD_SET_OUTPUT, &msg, sizeof( msg ), NULL )) )
{
if ( KCtrl.verbose )
hid_slogf( "devh-gpiokbd: GPIO DCMD_SET_OUTPUT devctl failed; status = %d", status );
}
delay( KCtrl.polling_interval );
key = &KCtrl.key[0];
if ( gpio_line1 == 1 && gpio_row1 == 0 )
{
key->pressed = 1;
buf.modifies = KCtrl.key[0].modifies;
buf.pressKeys[0] = KCtrl.key[0].scan_code;
buf.pressKeys[1] = KS_None;
}
else if ( gpio_line1 == 1 && gpio_row2 == 0 )
{
key->pressed = 1;
buf.modifies = KCtrl.key[1].modifies;
buf.pressKeys[0] = KCtrl.key[1].scan_code;
buf.pressKeys[1] = KS_None;
}
else if ( gpio_line1 == 1 && gpio_row3 == 0 )
{
key->pressed = 1;
buf.modifies = KCtrl.key[2].modifies;
buf.pressKeys[0] = KCtrl.key[2].scan_code;
buf.pressKeys[1] = KS_None;
}
else if ( gpio_line2 == 1 && gpio_row1 == 0 )
{
key->pressed = 1;
buf.modifies = KCtrl.key[3].modifies;
buf.pressKeys[0] = KCtrl.key[3].scan_code;
buf.pressKeys[1] = KS_None;
}
else if ( gpio_line2 == 1 && gpio_row2 == 0 )
{
key->pressed = 1;
buf.modifies = KCtrl.key[4].modifies;
buf.pressKeys[0] = KCtrl.key[4].scan_code;
buf.pressKeys[1] = KS_None;
}
else if ( gpio_line2 == 1 && gpio_row3 == 0 )
{
key->pressed = 1;
buf.modifies = KCtrl.key[5].modifies;
buf.pressKeys[0] = KCtrl.key[5].scan_code;
buf.pressKeys[1] = KS_None;
} else {
if ( key->pressed == 1 )
{
key->pressed = 0;
buf.modifies = 0;
buf.pressKeys[0] = KS_None;
(*KCtrl.ioh->send_report)( KCtrl.hid_hdl, (void *)&buf, sizeof( buf ) );
}
}
if ( key->pressed == 1 )
(*KCtrl.ioh->send_report)( KCtrl.hid_hdl, (void *)&buf, sizeof( buf ) );
}
}
int gpiokbd_start_driver( char *options )
{
char *value;
KCtrl.verbose = 0;
KCtrl.polling_interval = DEFAULT_POLLING_INT;
while ( options && *options != '\0' )
{
switch ( getsubopt( &options, gpiokbd_opts, &value ) )
{
case POLL_INTERVAL:
KCtrl.polling_interval = strtol( value, 0, 10 );
hid_slogf( "devh-gpiokbd: Polling interval = %d ms", KCtrl.polling_interval );
break;
case VERBOSE:
KCtrl.verbose = strtol( value, 0, 10 );
break;
case KEY_1_MODIFIES:
KCtrl.key[0].modifies = strtol( value, 0, 16 );
break;
case KEY_1_SCAN_CODE:
KCtrl.key[0].scan_code = strtol( value, 0, 16 );
break;
case KEY_2_MODIFIES:
KCtrl.key[1].modifies = strtol( value, 0, 16 );
break;
case KEY_2_SCAN_CODE:
KCtrl.key[1].scan_code = strtol( value, 0, 16 );
break;
case KEY_3_MODIFIES:
KCtrl.key[2].modifies = strtol( value, 0, 16 );
break;
case KEY_3_SCAN_CODE:
KCtrl.key[2].scan_code = strtol( value, 0, 16 );
break;
case KEY_4_MODIFIES:
KCtrl.key[3].modifies = strtol( value, 0, 16 );
break;
case KEY_4_SCAN_CODE:
KCtrl.key[3].scan_code = strtol( value, 0, 16 );
break;
case KEY_5_MODIFIES:
KCtrl.key[4].modifies = strtol( value, 0, 16 );
break;
case KEY_5_SCAN_CODE:
KCtrl.key[4].scan_code = strtol( value, 0, 16 );
break;
case KEY_6_MODIFIES:
KCtrl.key[5].modifies = strtol( value, 0, 16 );
break;
case KEY_6_SCAN_CODE:
KCtrl.key[5].scan_code = strtol( value, 0, 16 );
break;
default:
hid_slogf( "devh-gpiokbd: Invalid option" );
break;
}
}
return (EOK);
}
/* register driver with hid class */
int gpiokbd_init( void *dll_hdl, dispatch_t *dpp, io_hid_self_t *ioh, char *options )
{
pthread_attr_t attr;
gpio_devctl_t msg;
int status;
ThreadCtl( _NTO_TCTL_IO, 0 );
memset( &KCtrl, 0, sizeof( KCtrl ) );
hid_slogf( "devh-gpiokbd: Starting gpiokbd input manager" );
if ( (fd = open( GPIO_DEVICE_NAME, O_RDWR )) == -1 )
{
hid_slogf( "devh-gpiokbd: failed to open GPIO device %s: %s", GPIO_DEVICE_NAME, strerror( errno ) );
perror( "devh-gpiokbd: failed to open GPIO device " );
return (EXIT_FAILURE);
}
if ( (kbd_chid = ChannelCreate( 0 )) == -1 || (kbd_coid = ConnectAttach( 0, 0, kbd_chid, _NTO_SIDE_CHANNEL, 0 )) == -1 )
{
hid_slogf( "devh-gpiokbd: Unable to attach channel and connection" );
perror( "devh-gpiokbd: Unable to attach channel and connection " );
return (EXIT_FAILURE);
}
// register driver with io-hid
KCtrl.dll_hdl = dll_hdl;
KCtrl.ioh = ioh;
msg.cmd_intterupt.coid = kbd_coid;
msg.cmd_intterupt.pin_num = GPIO_ROW_1;
msg.cmd_intterupt.level = GPIO_INT_LEVEL_LOW;
// Set pin direction
if ( (status = devctl( fd, DCMD_SET_OUTPUT, &msg, sizeof( msg ), NULL )) )
hid_slogf( "devh-gpiokbd: GPIO DCMD_SET_OUTPUT devctl failed; status = %d", status );
msg.cmd_intterupt.pin_num = GPIO_ROW_2;
msg.cmd_intterupt.level = GPIO_INT_LEVEL_LOW;
// Set pin direction
if ( (status = devctl( fd, DCMD_SET_OUTPUT, &msg, sizeof( msg ), NULL )) )
hid_slogf( "devh-gpiokbd: GPIO DCMD_SET_OUTPUT devctl failed; status = %d", status );
msg.cmd_intterupt.pin_num = GPIO_ROW_3;
msg.cmd_intterupt.level = GPIO_INT_LEVEL_LOW;
// Set pin direction
if ( (status = devctl( fd, DCMD_SET_OUTPUT, &msg, sizeof( msg ), NULL )) )
hid_slogf( "devh-gpiokbd: GPIO DCMD_SET_OUTPUT devctl failed; status = %d", status );
msg.cmd_intterupt.pin_num = GPIO_LINE_1;
msg.cmd_intterupt.level = GPIO_INT_LEVEL_LOW;
// Set pin direction
if ( (status = devctl( fd, DCMD_SET_OUTPUT, &msg, sizeof( msg ), NULL )) )
hid_slogf( "devh-gpiokbd: GPIO DCMD_SET_OUTPUT devctl failed; status = %d", status );
msg.cmd_intterupt.pin_num = GPIO_LINE_2;
msg.cmd_intterupt.level = GPIO_INT_LEVEL_LOW;
// Set pin direction
if ( (status = devctl( fd, DCMD_SET_OUTPUT, &msg, sizeof( msg ), NULL )) )
{
hid_slogf( "devh-gpiokbd: GPIO DCMD_SET_OUTPUT devctl failed; status = %d: %s", status, strerror( errno ) );
perror( "devh-gpiokbd: GPIO DCMD_SET_OUTPUT devctl failed" );
return (EXIT_FAILURE);
}
// Default key codes
KCtrl.key[0].modifies = 0x00;
KCtrl.key[0].scan_code = KS_Left;
KCtrl.key[1].modifies = 0x00;
KCtrl.key[1].scan_code = KS_Right;
KCtrl.key[2].modifies = 0x00;
KCtrl.key[2].scan_code = KS_Escape;
KCtrl.key[3].modifies = 0x00;
KCtrl.key[3].scan_code = KS_Up;
KCtrl.key[4].modifies = 0x00;
KCtrl.key[4].scan_code = KS_Down;
KCtrl.key[5].modifies = 0x00;
KCtrl.key[5].scan_code = KS_Enter;
KCtrl.key[0].lcc = KCtrl.key[1].lcc = KCtrl.key[2].lcc = KCtrl.key[3].lcc = ClockCycles();
gpiokbd_get_report_descriptor( KCtrl );
gpiokbd_start_driver( options );
pthread_attr_init( &attr );
if ( pthread_create( &kbd_tid, &attr, (void *)gpiokbd_event_handler_polling, 0 ) )
{
hid_slogf( "devh-gpiokbd: Unable to create driver thread" );
perror( "devh-gpiokbd: Unable to create driver thread" );
return (EXIT_FAILURE);
}
return (0);
}