#!/usr/bin/python # Copyright 2015 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """Explain device capabilities from /proc/bus/input/devices. This tool processes the contents of /proc/bus/input/devices, expanding bitfields of event codes into lists of names for those events. """ import argparse import evdev import sys def parse_bitfield(bitfield, word_bits): """Parse a serialized bitfield from /proc/bus/input/devices. Returns a list of bits in the set. Example: parse_bitfield('10 3', 64) == [0, 1, 68] """ groups = bitfield.split(' ') group_count = len(groups) result_bits = [] for group_index in xrange(group_count): group_val = int(groups[group_count - 1 - group_index], 16) for group_bit in xrange(word_bits): if group_val & (1 << group_bit): result_bits.append(group_index * word_bits + group_bit) return result_bits PROP_NAMES = { 0x0: 'INPUT_PROP_POINTER', 0x1: 'INPUT_PROP_DIRECT', 0x2: 'INPUT_PROP_BUTTONPAD', 0x3: 'INPUT_PROP_SEMI_MT', 0x4: 'INPUT_PROP_TOPBUTTONPAD', } KEY_NAMES = evdev.ecodes.bytype[evdev.ecodes.EV_KEY].copy() # Fix keys with multiple names. KEY_NAMES[evdev.ecodes.KEY_MUTE] = 'KEY_MUTE' KEY_NAMES[evdev.ecodes.KEY_SCREENLOCK] = 'KEY_SCREENLOCK' KEY_NAMES[evdev.ecodes.KEY_HANGEUL] = 'KEY_HANGEUL' KEY_NAMES[evdev.ecodes.BTN_LEFT] = 'BTN_LEFT' KEY_NAMES[evdev.ecodes.BTN_TRIGGER] = 'BTN_TRIGGER' KEY_NAMES[evdev.ecodes.BTN_0] = 'BTN_0' BITFIELDS = [ ('B: EV=', evdev.ecodes.EV), ('B: PROP=', PROP_NAMES), ('B: KEY=', KEY_NAMES), ('B: REL=', evdev.ecodes.bytype[evdev.ecodes.EV_REL]), ('B: MSC=', evdev.ecodes.bytype[evdev.ecodes.EV_MSC]), ('B: LED=', evdev.ecodes.bytype[evdev.ecodes.EV_LED]), ('B: ABS=', evdev.ecodes.bytype[evdev.ecodes.EV_ABS]), ('B: SW=', evdev.ecodes.bytype[evdev.ecodes.EV_SW]), ('B: REP=', evdev.ecodes.bytype[evdev.ecodes.EV_REP]), ('B: SND=', evdev.ecodes.bytype[evdev.ecodes.EV_SND]), ] def explain_bitfield(serialized_bits, bit_names, word_size, output_file): """Annotate a bitfield using the provided symbolic names for each bit.""" bits = parse_bitfield(serialized_bits, word_size) for bit in bits: if bit in bit_names: output_file.write(' %s\n' % bit_names[bit]) else: output_file.write(' 0x%x\n' % bit) def explain(input_file, output_file, word_size): """Annotate an input file formatted like /proc/bus/input/devices.""" for line in input_file: output_file.write(line) for prefix, bit_names in BITFIELDS: if line.startswith(prefix): explain_bitfield(line[len(prefix):], bit_names, word_size, output_file) def main(argv): parser = argparse.ArgumentParser() parser.add_argument('input_file', nargs='?', default='/proc/bus/input/devices', help="filename to read input from") parser.add_argument('output_file', nargs='?', help="filename to write output to") parser.add_argument('--word_size', type=int, default=64, help="word size of the system that generated the input file") args = parser.parse_args(argv) input_file = sys.stdin if args.input_file: input_file = open(args.input_file, 'r') output_file = sys.stdout if args.output_file: output_file = open(args.output_file, 'w') explain(input_file, output_file, args.word_size) return 0 if __name__ == '__main__': sys.exit(main(sys.argv[1:]))