Skip to content

Commit

Permalink
[event-input] All explicitly defining input device types. Fixes MER#1634
Browse files Browse the repository at this point in the history
It might not be possible to fine tune mce input device probing heuristics
to deal with quirks present in some devices - especially during initial
porting efforts.

Allow use of hw adaptation specific configuration files to be used for
explicitly stating input device types instead of relying on built in
probing logic.
  • Loading branch information
spiiroin committed Aug 12, 2016
1 parent e1bb3f9 commit c211563
Show file tree
Hide file tree
Showing 2 changed files with 215 additions and 6 deletions.
189 changes: 183 additions & 6 deletions event-input.c
Expand Up @@ -232,9 +232,13 @@ typedef enum {
/** Keyboard device */
EVDEV_KEYBOARD,

/** Device type was not explicitly set in configuration */
EVDEV_UNKNOWN,

} evin_evdevtype_t;

static const char *evin_evdevtype_repr (evin_evdevtype_t type);
static evin_evdevtype_t evin_evdevtype_parse (const char *name);
static evin_evdevtype_t evin_evdevtype_from_info (evin_evdevinfo_t *info);

/* ------------------------------------------------------------------------- *
Expand All @@ -247,6 +251,13 @@ static void evin_doubletap_setting_cb (GConfClient *co

#endif // ENABLE_DOUBLETAP_EMULATION

/* ------------------------------------------------------------------------- *
* INI_FILE_HELPERS
* ------------------------------------------------------------------------- */

static bool evio_is_valid_key_char(int ch);
static char *evio_sanitize_key_name(const char *name);

/* ------------------------------------------------------------------------- *
* EVDEV_IO_MONITORING
* ------------------------------------------------------------------------- */
Expand Down Expand Up @@ -768,7 +779,7 @@ evin_event_mapper_translate_event(struct input_event *ev)
static void
evin_event_mapper_init(void)
{
static const char grp[] = "EVDEV";
static const char grp[] = MCE_CONF_EVDEV_GROUP;

gchar **keys = 0;
gsize count = 0;
Expand Down Expand Up @@ -1285,11 +1296,64 @@ evin_evdevtype_repr(evin_evdevtype_t type)
[EVDEV_ALS] = "AMBIENT LIGHT SENSOR",
[EVDEV_VOLKEY] = "VOLUME KEYS",
[EVDEV_KEYBOARD] = "KEYBOARD",
[EVDEV_UNKNOWN] = "UNKNOWN",
};

return lut[type];
}

/** Convert textual evdev classification from config file to enum value
*
* @param name evdev device type from configuration file
*
* @return Return corresponding device type id, or EVDEV_UNKNOWN
*/
static evin_evdevtype_t
evin_evdevtype_parse(const char *name)
{
static const struct
{
const char *key;
int val;
} lut[] =
{
{ "REJECT", EVDEV_REJECT, },
{ "TOUCH", EVDEV_TOUCH, },
{ "INPUT", EVDEV_INPUT, },
{ "ACTIVITY", EVDEV_ACTIVITY, },
{ "IGNORE", EVDEV_IGNORE, },
{ "DOUBLE_TAP", EVDEV_DBLTAP, },
{ "DBLTAP", EVDEV_DBLTAP, },
{ "PS", EVDEV_PS, },
{ "PROXIMITY_SENSOR", EVDEV_PS, },
{ "ALS", EVDEV_ALS, },
{ "LIGHT_SENSOR", EVDEV_ALS, },
{ "VOLKEY", EVDEV_VOLKEY, },
{ "VOLUME_KEYS", EVDEV_VOLKEY, },
{ "KEYBOARD", EVDEV_KEYBOARD, },

/* Note: EVDEV_UNKNOWN is left out on purpose as it
* signifies parsing error and thus is not
* meant to be used in configuration files. */
};

evin_evdevtype_t type = EVDEV_UNKNOWN;

if( !name )
goto EXIT;

for( size_t i = 0; i < G_N_ELEMENTS(lut); ++i ) {
if( strcmp(lut[i].key, name) )
continue;

type = lut[i].val;
break;
}

EXIT:
return type;
}

/** Use heuristics to determine what mce should do with an evdev device node
*
* @param info Event types and codes emitted by a evdev device
Expand Down Expand Up @@ -1536,6 +1600,100 @@ evin_doubletap_setting_cb(GConfClient *const client, const guint id,

#endif /* ENABLE_DOUBLETAP_EMULATION */

/* ========================================================================= *
* INI_FILE_HELPERS
* ========================================================================= */

/** Predicate for: character can be used in glib keyfile key keyname
*
* @param ch character
*
* @return true if character can be used as is, false otherwise
*/
static bool
evio_is_valid_key_char(int ch)
{
/* Skip negatives, ascii control chars / white space */
if( ch <= 0x20 )
return false;

/* Keys must be utf-8 and we do not control what kernel
* returns -> skip stuff that is not ascii-7 pure */
if( ch >= 0x80 )
return false;

/* Square brackets are used for keyfile groups or
* specifying language specific variant values */
if( ch == '[' || ch == ']' )
return false;

/* And '=' is used for separating keys from values */
if( ch == '=' )
return false;

/* Assume everything else is ok */
return true;
}

/** Sanitize c-string to "usable as ini file key" form
*
* Dynamically obtained strings - such as evdev device names queried
* from kernel - might contain characters that are not allowed in
* glib keyfile keys. This function performs one way transformation
* that allows turning any c-string into a form that can be used
* as key name.
*
* Leading and trailing illegal characters are skipped altogether.
*
* Sequences of mid-string illegal characters are squeezed into
* single underscores.
*
* Caller must release the returned string via free().
*
* Examples:
* "gpio-keys" -> "gpio-keys" (no change)
* " some thing [x=7] " -> "some_thing_x_7"
*
* @param name Device name
*
* @return Device name without illegal characters, or NULL
*/
static char *
evio_sanitize_key_name(const char *name)
{
char *key = 0;

if( !name )
goto EXIT;

if( !(key = strdup(name)) )
goto EXIT;

char *src = key;
char *dst = key;

while( *src && !evio_is_valid_key_char(*src) )
++src;

for( ;; ) {
while( *src && evio_is_valid_key_char(*src) )
*dst++ = *src++;

while( *src && !evio_is_valid_key_char(*src) )
++src;

if( !*src )
break;

*dst++ = '_';
}

*dst = 0;

EXIT:
return key;
}

/* ========================================================================= *
* EVDEV_IO_MONITORING
* ========================================================================= */
Expand All @@ -1560,16 +1718,31 @@ static evin_iomon_extra_t *
evin_iomon_extra_create(int fd, const char *name)
{
evin_iomon_extra_t *self = calloc(1, sizeof *self);
gchar *type = 0;
char *key = 0;

self->ex_name = strdup(name);
self->ex_info = evin_evdevinfo_create();
/* Initialize extra info to sane defaults */
self->ex_name = strdup(name);
self->ex_info = evin_evdevinfo_create();
self->ex_type = EVDEV_UNKNOWN;
self->ex_sw_keypad_slide = 0;
self->ex_mt_state = 0;

evin_evdevinfo_probe(self->ex_info, fd);

self->ex_type = evin_evdevtype_from_info(self->ex_info);
/* Check if evdev device type has been set in the configuration */
key = evio_sanitize_key_name(name);
type = mce_conf_get_string(MCE_CONF_EVDEV_TYPE_GROUP, key, 0);
if( type ) {
self->ex_type = evin_evdevtype_parse(type);
if( self->ex_type == EVDEV_UNKNOWN )
mce_log(LL_WARN, "unknown evdev device type '%s'", type);
}

self->ex_sw_keypad_slide = 0;
self->ex_mt_state = 0;
/* In case of missing / faulty configuration, use heuristics
* to determine the device type */
if( self->ex_type == EVDEV_UNKNOWN )
self->ex_type = evin_evdevtype_from_info(self->ex_info);

if( self->ex_type == EVDEV_KEYBOARD ) {
self->ex_sw_keypad_slide = mce_conf_get_string("SW_KEYPAD_SLIDE",
Expand All @@ -1582,6 +1755,9 @@ evin_iomon_extra_create(int fd, const char *name)
self->ex_mt_state = mt_state_create(protocol_b);
}

g_free(type);
free(key);

return self;
}

Expand Down Expand Up @@ -2175,6 +2351,7 @@ evin_iomon_device_add(const gchar *path)

case EVDEV_REJECT:
case EVDEV_IGNORE:
case EVDEV_UNKNOWN:
goto EXIT;

default:
Expand Down
32 changes: 32 additions & 0 deletions event-input.h
Expand Up @@ -36,6 +36,38 @@
/** Path to the GPIO key disable interface */
# define GPIO_KEY_DISABLE_PATH "/sys/devices/platform/gpio-keys/disabled_keys"

/** Name of the evdev event mapping configuration group
*
* This configuration group can be used to remap the events
* sent by the kernel to the ones expected by mce.
*
* Items in the group are of form:
*
* <event_emitted_by_kernel>=<event_expected_by_mce>
*
* Where both event_emitted_by_kernel and event_expected_by_mce are event
* code names such: as SW_LID, SW_KEYPAD_SLIDE, ...
*
* See inifiles/evdev.ini for details and examples.
*/
#define MCE_CONF_EVDEV_GROUP "EVDEV"

/** Name of the evdev type configuration group
*
* This configuration group can be used to explicitly define input device
* types in cases where probing heuristic in mce code fails to work.
*
* Items in the group are of form:
*
* <device_name>=<device_type>
*
* Where device_name is sanitized evdev device name obtained via EVIOCGNAME
* ioctl(), and device_type is one of : TOUCH, VOLKEY, DBLTAP, ...
*
* See evio_sanitize_key_name() and evin_evdevtype_parse() for details.
*/
#define MCE_CONF_EVDEV_TYPE_GROUP "EVDEV_TYPE"

/* ========================================================================= *
* Settings
* ========================================================================= */
Expand Down

0 comments on commit c211563

Please sign in to comment.