在Android平台通常有多个输入设备,遥控器,按键板,触摸屏/触摸框,鼠标,蓝牙笔等等。
归根结柢都是属于Linux 平台的一个input device,上面的多个种类的输入设备是input
device 的封装。
下面以手上平台为例,一起学习了解遥控器设备的注册流程。
- android 输入设备的查询
如图:
上面描述了四个输入设备:触摸框(libxTouchScreen),按键板(MStar Smart TV Keypad),空鼠(cv simulate mouse),遥控器(Sharp Smart TV IR Receiver)。如果想看各个输入设备和android层按键的的映射关系(*.kl)。
- linux event 与 android keyevent 映射
可以通过dumpsys input查看
INPUT MANAGER (dumpsys input)
Input Manager State:
Interactive: true
System UI Visibility: 0x0
Pointer Speed: 0
Pointer Gestures Enabled: true
Show Touches: false
Event Hub State:
BuiltInKeyboardId: -2
Devices:
-1: Virtual
Classes: 0x40000023
Path: <virtual>
Descriptor: a718a782d34bc767f4689c232d64d527998ea7fd
Location:
ControllerNumber: 0
UniqueId: <virtual>
Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000
KeyLayoutFile: /system/usr/keylayout/Generic.kl
KeyCharacterMapFile: /system/usr/keychars/Virtual.kcm
ConfigurationFile:
HaveKeyboardLayoutOverlay: false
1: libxTouchScreen
Classes: 0x80000014
Path: /dev/input/event4
Descriptor: d0adc8211820a452e94d90216845733a1280d769
Location:
ControllerNumber: 0
UniqueId:
Identifier: bus=0x0003, vendor=0x0000, product=0x0000, version=0x0004
KeyLayoutFile:
KeyCharacterMapFile:
ConfigurationFile:
HaveKeyboardLayoutOverlay: false
2: MStar Smart TV Keypad
Classes: 0x00000001
Path: /dev/input/event3
Descriptor: 8f43d929a9472e8dc54d48a6c41e2435e8eaff35
Location:
ControllerNumber: 0
UniqueId:
Identifier: bus=0x0006, vendor=0x3697, product=0x0002, version=0x0000
KeyLayoutFile: /system/usr/keylayout/Vendor_3697_Product_0002.kl
KeyCharacterMapFile: /system/usr/keychars/Generic.kcm
ConfigurationFile:
HaveKeyboardLayoutOverlay: false
3: cv simulate mouse
Classes: 0x00000009
Path: /dev/input/event2
Descriptor: 4055b8a032ccf50ef66dbe2ff99f3b2474e9eab5
Location: smouse/input0
ControllerNumber: 0
UniqueId:
Identifier: bus=0x0019, vendor=0xbeef, product=0xdead, version=0x0001
KeyLayoutFile: /system/usr/keylayout/Generic.kl
KeyCharacterMapFile: /system/usr/keychars/Generic.kcm
ConfigurationFile:
HaveKeyboardLayoutOverlay: false
4: MCE IR Keyboard/Mouse (ir)
Classes: 0x0000000b
Path: /dev/input/event1
Descriptor: 2b764a30c0f74e1362d8ef86c5e4f12150af666f
Location: /input0
ControllerNumber: 0
UniqueId:
Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000
KeyLayoutFile: /system/usr/keylayout/Generic.kl
KeyCharacterMapFile: /system/usr/keychars/Generic.kcm
ConfigurationFile:
HaveKeyboardLayoutOverlay: false
5: Sharp Smart TV IR Receiver
Classes: 0x00000001
Path: /dev/input/event0
Descriptor: 0e50bdc18d3ae0b6f247100cbd99062d93c208eb
Location: /dev/ir
ControllerNumber: 0
UniqueId:
Identifier: bus=0x0018, vendor=0x3697, product=0x0001, version=0x0001
KeyLayoutFile: /system/usr/keylayout/Vendor_3697_Product_0001.kl
KeyCharacterMapFile: /system/usr/keychars/Generic.kcm
ConfigurationFile:
HaveKeyboardLayoutOverlay: false
遥控器和按键板事件与android的映射关系,可以看KeyLayoutFile 参数值,例如:
/system/usr/keylayout/Vendor_3697_Product_0002.kl 和/system/usr/keylayout/Vendor_3697_Product_0001.kl 分别是按键板,遥控器的映射表。
- linux input 设备注册流程
下面以一张图来简答介绍一下注册流程,如下:
各个厂商的遥控器,长虹,创维,康佳,tcl,海信等等,会注册到mstar ir 的模块中,中间再注册到Input 中,最后设备添加到内核中。
- 通过rc_map_register (@rc-main.c) 将rc_map注册到rc_map_list 链表中
static int __init init_rc_map_cultraview_tv(void)
{
return rc_map_register(&cultraview_tv_map);
}
static void __exit exit_rc_map_cultraview_tv(void)
{
rc_map_unregister(&cultraview_tv_map);
}
- mstar ir 在驱动加载过程中通过probe注册设备
static int mstar_ir_drv_probe(struct platform_device *pdev) {
int retval=0;
if (!(pdev->name) || strcmp(pdev->name,"Mstar-ir")
|| pdev->id!=0) {
retval = -ENXIO;
}
IRDev.u32IRFlag = 0;
retval = mod_ir_init();
if (!retval) {
pdev->dev.platform_data=&IRDev;
}
#ifdef CONFIG_CV_SIMULATE_MOUSE
if (device_create_file(&pdev->dev, &dev_attr_smouse_enable))
printk("Warnning: can't create sysfs smouse_enable attribute\n");
#endif
return retval;
}
mod_ir_init 继续往下走
static int mod_ir_init(void) {
int ret;
dev_t dev;
#ifdef CONFIG_MSTAR_UDEV_NODE
ir_class = class_create(THIS_MODULE, MDRV_NAME_IR);
if (IS_ERR(ir_class))
{
return PTR_ERR(ir_class);
}
#endif
if (IRDev.s32IRMajor) {
dev = MKDEV(IRDev.s32IRMajor, IRDev.s32IRMinor);
ret = register_chrdev_region(dev, MOD_IR_DEVICE_COUNT, MDRV_NAME_IR);
} else {
ret = alloc_chrdev_region(&dev, IRDev.s32IRMinor, MOD_IR_DEVICE_COUNT, MDRV_NAME_IR);
IRDev.s32IRMajor = MAJOR(dev);
}
if ( 0 > ret) {
IR_PRINT("Unable to get major %d\n", IRDev.s32IRMajor);
return ret;
}
cdev_init(&IRDev.cDevice, &IRDev.IRFop);
if (0!= (ret= cdev_add(&IRDev.cDevice, dev, MOD_IR_DEVICE_COUNT))) {
IR_PRINT("Unable add a character device\n");
unregister_chrdev_region(dev, MOD_IR_DEVICE_COUNT);
return ret;
}
#ifdef CONFIG_MSTAR_IR_INPUT_DEVICE
MDrv_IR_Input_Init();
MDrv_IR_Init(0);
IRDev.u32IRFlag |= (IRFLAG_IRENABLE|IRFLAG_HWINITED);
#endif
#ifdef CONFIG_MSTAR_UDEV_NODE
device_create(ir_class, NULL, dev, NULL, MDRV_NAME_IR);
#endif
return 0;
}
MDrv_IR_Input_Init 接着注册RC device设备
int MDrv_IR_Input_Init(void) {
char *map_name = RC_MAP_MSTAR_TV;
char *input_name = "MStar Smart TV IR Receiver"; // 设备名称
__u16 vendor_id = 0x3697UL;
int err = 0;
struct rc_dev *dev;
ir = kzalloc(sizeof(struct mstar_ir), GFP_KERNEL);
dev = rc_allocate_device();
if (!ir || !dev)
return -ENOMEM;
// init input device
ir->dev = dev;
// 设置input设备的属性,名称,类型,vendor id,product id,支持的遥控器协议等
// 可以dumpsys input 查看设备对应的属性,都是此处设置
dev->driver_name = MDRV_NAME_IR;
dev->map_name = map_name;
dev->driver_type = RC_DRIVER_IR_RAW;
dev->input_name = input_name;
dev->input_phys = "/dev/ir";
dev->input_id.bustype = BUS_I2C;
dev->input_id.vendor = vendor_id;
dev->input_id.product = 0x0001UL;
dev->input_id.version = 1;
dev->allowed_protos = RC_TYPE_ALL;
err = rc_register_device(dev);
if (err) {
rc_free_device(dev);
kfree(ir);
return err;
}
// No auto-repeat.
clear_bit(EV_REP, ir->dev->input_dev->evbit);
#ifdef CONFIG_CV_SIMULATE_MOUSE
//if smouse init fail just ignore.
ir->sm = smouse_init(ir->dev);
if (ir->sm == NULL)
printk("csign: smouse_init failed\n");
#endif
init_completion(&key_completion);
key_dispatch_workqueue = create_workqueue("keydispatch_wq");
queue_work(key_dispatch_workqueue, &key_dispatch_thread);
return 0;
}
- 分解rc device 为input device
将分解开的rc device 设备继续向下注册
int input_register_device(struct input_dev *dev)
{
static atomic_t input_no = ATOMIC_INIT(0);
struct input_devres *devres = NULL;
struct input_handler *handler;
unsigned int packet_size;
const char *path;
int error;
......
error = device_add(&dev->dev);
......
}
至此,完成了遥控器设备(“Sharp Smart TV IR Receiver”)的整个注册流程。另外“cv simulate mouse”设备的注册如下:
struct smouse *smouse_init(struct rc_dev *rc)
{
struct smouse *s = NULL;
struct input_dev *idev = NULL;
s = (struct smouse *)kzalloc(sizeof(*s), GFP_KERNEL);
idev = input_allocate_device();
if (!s || !idev) {
goto fail1;
}
s->rc = rc;
s->idev = idev;
s->rc_state = RC_UP;
s->sm_mode = 0; //default isn't simulate mouse mode
// input 设备的属性设置
idev->name = "cv simulate mouse";
idev->phys = "smouse/input0";
idev->id.bustype = BUS_HOST;
idev->id.vendor = 0xbeef;
idev->id.product = 0xdead;
idev->id.version = 0x0001;
idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
idev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
idev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT);
set_bit(KEY_UNKNOWN, idev->keybit);
// input 注册
if (input_register_device(idev))
goto fail2;
init_timer(&s->slide_timer);
s->slide_timer.data = (long)s;
s->slide_timer.function = mouse_slide_fn;
s->slide_delay = SLIDE_DELAY; //250;
s->slide_period = SLIDE_PERIOD; //33;
return s;
fail2:
input_free_device(idev);
fail1:
if (s)
kfree(s);
return NULL;
}
- input 设备(RC)的 event 设置
这个event 设置也就是所谓的遥控码值设置,定义一个rc_map_table
static struct rc_map_table hisense_tv[] = {
// 1st IR controller. 第一款遥控码值表
{ 0xBF0D, KEY_POWER },
{ 0xBF0E, KEY_MUTE },
{ 0xBF1A, KEY_SLEEP },
{ 0xBF11, KEY_AUDIO }, // SOUND_MODE
{ 0xBF10, KEY_CAMERA }, // PICTURE_MODE
{ 0xBF13, KEY_ZOOM }, // ASPECT_RATIO
{ 0xBF0B, KEY_CHANNEL }, // CHANNEL_RETURN
......
// Hisense extended
{ 0xBF1C, KEY_F1 }, // HISENSE_SAVEMODE
{ 0xBF49, KEY_F2 }, // HISENSE_AUDIO_TRACK
{ 0xBF5D, KEY_F3 }, // HISENSE_BROADCAST
// 2nd IR cotroller. 第二款遥控器码值表
......
{ 0xFC50, KEY_FN_RDRV_PLUS },
{ 0xFC46, KEY_FN_RDRV_INC },
{ 0xFC4C, KEY_FN_GDRV_PLUS },
{ 0xFC5A, KEY_FN_GDRV_INC },
{ 0xFC49, KEY_FN_BDRV_PLUS },
{ 0xFC4A, KEY_FN_BDRV_INC },
{ 0xFC44, KEY_FN_RCUT_PLUS },
{ 0xFC41, KEY_FN_RCUT_INC },
{ 0xFC4B, KEY_FN_GCUT_PLUS },
{ 0xFC51, KEY_FN_GCUT_INC },
{ 0xFC08, KEY_FN_BCUT_PLUS },
{ 0xFC45, KEY_FN_BCUT_INC },
......
};
映射表中右侧是驱动定义(@input.h)的事件,且每个事件都是唯一的,左侧十六进制是遥控头码和码值的组合值(遥控器头码+按键码值),如上面数组,头码有0xBF,0XFC,理论上就需要可以支持两组遥控器,一般有一个工厂遥控器和客供遥控器。
- 头码设置
主要是设置IR_HEADER_CODE0,IR_HEADER_CODE1,在头文件@ K:\huiyi\base_648\vendor\mstar\kernel\linaro\mstar2\drv\ir\IR_${VendorName}.h,海信对应的就是IR_HISENSE.h
- 多遥控器遥控器支持
mdrv_ir.c 中会根据客户情况,设置多个头码,例如IR_HISENSE.h中
FCTORY_HEADER_RECEIVE 和PCCOMMAND_HEADER_RECEIVE 是设置多遥控必须开启的,IR_FHEADER_CODE0,IR_FHEADER_CODE1 和IR_PHEADER_CODE0,IR_PHEADER_CODE1 两组则属于扩展部分的遥控器。
双遥控器,如下:
其中IR_FHEADER_CV_CODE0,IR_FHEADER_CV_CODE1 是第二组遥控器头码码值。以上是整个遥控器设备按键配置,注册流程。后续分享m平台模拟input 设备的知识。
- 新增标准HID设备键值配置
首先查看所有input 设备的属性,特别是vendor id ,product id。
cat /proc/bus/input/devices 执行如下:
我们可以看到两组vendor id ,product id, 这样我们在 /system/usr/keylayout/ 目录
这里添加Vendor_xxxx_Product_yyyy.kl 文件,然后通过getevent 将获取的物理键值和
android 里面KeyEvent 里面对应的按键字符匹配上即可。
这里按键就可以一步步流到android层了。