通过uinput机制模拟了input设备,实现了蜂鸣器的控制。
系统内核:2.6.21 发布版本fedora7
1. 主要流程如下:
2. 详细代码如下
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <linux/uinput.h>
#include <fcntl.h>
#include <linux/input.h>
#include <unistd.h>
#include "beep_drv.h"
#define BEEP_DEBUG 1
#define USER_CTL_DEV "/dev/input/uinput"
#define VENDOR 0x001f
#define PRODUCT 0x0001
#define VERSION 1
#define MAGIC_NUMBER 0x884
int s_beep_fd = 0;
int snd_handle(int value)
{
#if BEEP_DEBUG
if (s_beep_fd <= 0)
return -1;
#endif
if (value == MAGIC_NUMBER){
#if BEEP_DEBUG
set_beep_on(s_beep_fd);
#else
printf("beep on\n");
#endif
} else {
#if BEEP_DEBUG
set_beep_off(s_beep_fd);
#else
printf("beep off\n");
#endif
}
return 0;
}
int event_handler_loop(int fd)
{
int ret = 0;
fd_set rd;
FD_ZERO(&rd);
FD_SET(fd,&rd);
struct input_event event;
while(1){
ret = select(fd+1,&rd,NULL,NULL,NULL);
if (ret < 0){
printf("select io err\n");
return -1;
}
ret = read(fd,&event,sizeof(struct input_event));
if (ret <= 0){
printf("read io err\n");
continue;
}
//printf("get event: %x,%x,%x\n",event.type,event.code,event.value);
if(event.type == EV_SND){
switch(event.code){
case SND_TONE:
snd_handle(event.value);
break;
default:
printf("unknown event\n");
break;
}
}
}
return 0;
}
int beep_probe(void)
{
int beep_fd = beeplight_open();
if (beep_fd < 0){
return -1;
}
s_beep_fd = beep_fd;
return beep_fd;
}
int main(void)
{
struct uinput_user_dev beeper_dev;
int beep_fd, fd, ret;
#if BEEP_DEBUG
beep_fd = beep_probe();
if (beep_fd < 0){
printf("beep device not found!\n");
return -1;
}
#endif
fd = open(USER_CTL_DEV, O_RDWR);
if (fd < 0) {
printf("no user input stuct\n");
return fd;
}
ret = ioctl(fd, UI_SET_EVBIT, EV_SND);
ret = ioctl(fd, UI_SET_SNDBIT, SND_TONE);
memset(&beeper_dev, 0, sizeof(struct uinput_user_dev));
snprintf(beeper_dev.name, UINPUT_MAX_NAME_SIZE, "Beeper");
beeper_dev.id.bustype = BUS_USB;
beeper_dev.id.vendor = VENDOR;
beeper_dev.id.product = PRODUCT;
beeper_dev.id.version = VERSION;
ret = write(fd, &beeper_dev, sizeof(struct uinput_user_dev));
ret = ioctl(fd, UI_DEV_CREATE);
if (ret < 0) {
close(fd);
return ret;
}
ret = event_handler_loop(fd);
if (ret < 0){
printf("event loop exit\n");
}
ioctl(fd, UI_DEV_DESTROY);
#if BEEP_DEBUG
beeplight_close(beep_fd);
#endif
close(fd);
return 0;
}
3. 运行上述代码,没问题的话会在/dev/input/目录下产生event设备节点,类似于一般的鼠标键盘,也可以通过cat /proc/bus/input/devices查看,因为没有在事件处理中write上报事件,所以直接读设备节点/dev/input/event*是没有数据的,只有写设备节点的功能。因为只处理了EV_SND事件,所以只要按照EV_SND格式封装struct input_event数据结构,往这个设备节点写,就会传入到事件循环,进而控制蜂鸣器开和关,注意MAGIC_NUMBER是区别其他设备的参数,测试程序test如下:
#include <linux/input.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#define MAGIC_NUMBER 0x884
int main(int argc,char*argv[])
{
int i = 0;
int fd = 0;
char dev_name[32];
struct input_id event_id;
struct input_event event;
int beep_on = 0;
if (argc != 3){
printf("./beep_test </dev/input/event*> <0|1>\n");
return -1;
}
beep_on = atoi(argv[2]);
if (beep_on)
beep_on = MAGIC_NUMBER;
snprintf(dev_name,32,"%s",argv[1]);
fd = open(dev_name,O_RDWR);
if (fd < 0){
perror("open:");
return fd;
}
if(!ioctl(fd,EVIOCGID,&event_id)){
printf("vendor:%04x,product:%04x\n",event_id.vendor,event_id.product);
}
event.type = EV_SND;
event.code = SND_TONE;
event.value = beep_on;
write(fd,&event,sizeof(struct input_event));
close(fd);
return 0;
}