都是以前写过的代码,现在整理下,怕忘了。
还是mstar安卓电视方案添加的功能,阿三客户要添加一个工厂调试的功能:在电脑上设置类似白平衡参数的一些数值,然后把数值和命令通过红外发射器发出,电视接收到后根据命令和数值自动调节白平衡等参数,红外协议是NEC。
我采用的方法是在红外驱动中添加一个虚拟设备,然后判断红外接收到的数据,如果是工厂调试命令就把数据写到这个虚拟设备中;应用层就启动一个线程一直去读这个设备的数据,根据读到的数据来调用mstar SuperNova的接口去调节白平衡等参数;因为无法用中断做,所以就想到了用poll这个方法做。
首先在红外驱动中添加一个虚拟设备,为这个设备添加open,read,poll文件操作函数:
static int FA_major = 0;
static struct cdev cdev;
//FA_send这个变量用做检测进程唤醒的条件
static volatile int FA_send = 0;
//生成一个等待队列头
static DECLARE_WAIT_QUEUE_HEAD(FA_waitq);
static int FA_open(struct inode* inode, struct file* filp);
static ssize_t FA_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos);
static unsigned int FA_poll(struct file * filp, poll_table * wait);
static const struct file_operations FA_fops = {
.owner = THIS_MODULE,
.read = FA_read,
.open = FA_open,
.poll = FA_poll,
};
//这个结构体用来存储红外命令
struct FA_dev {
char type;
char value;
};
static struct FA_dev* fa_dev = NULL;
static struct class* fa_class = NULL;
static int FA_open(struct inode* inode, struct file* filp) {
filp->private_data = fa_dev;
return 0;
}
static ssize_t FA_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos) {
ssize_t err = 0;
struct FA_dev* dev = filp->private_data;
if(count < sizeof(struct FA_dev)) {
goto out;
}
//将红外命令写到这个设备中
dev->type = fa_dev->type;
dev->value = fa_dev->value;
//FA_send由判断红外命令的方法赋值,如果是工厂命令就置为1,
//否则为0,wait_event_interruptible根据FA_send的值来
//唤醒进程
wait_event_interruptible(FA_waitq, FA_send);
if(copy_to_user(buf, dev, sizeof(struct FA_dev))) {
err = -EFAULT;
goto out;
}
//读完数据,FA_send 置0
FA_send = 0;
err = sizeof(struct FA_dev);
out:
return err;
}
static unsigned int FA_poll(struct file * filp, poll_table * wait) {
unsigned int mask = 0;
//把当前进程添加到FA_waitq等待列表
poll_wait(filp, &FA_waitq, wait);
//如果红外接收到工厂命令则返回可读
if (FA_send)
mask |= POLLIN | POLLRDNORM;
return mask;
}
在红外驱动初始化时也初始化这个虚拟设备
dev_t devno = 0;
struct device* temp = NULL;
result = alloc_chrdev_region(&devno, 0, 1, "FA");
if(result < 0) {
printk("Failed to alloc char dev region.\n");
}
FA_major = MAJOR(devno);
cdev_init(&cdev, &FA_fops);
cdev.owner = THIS_MODULE;
cdev.ops = &FA_fops;
cdev_add(&cdev, MKDEV(FA_major, 0), 1);
fa_dev = kmalloc(sizeof(struct FA_dev), GFP_KERNEL);
if(!fa_dev) {
printk("Failed to alloc fa_dev.\n");
}
memset(fa_dev, 0, sizeof(struct FA_dev));
fa_dev->type = 0;
fa_dev->value = 0;
fa_class = class_create(THIS_MODULE, "FA");
if(IS_ERR(fa_class)) {
printk("Failed to create FA class.\n");
}
temp = device_create(fa_class, NULL, devno, "%s", "FA");
if(IS_ERR(temp)) {
printk("Failed to create FA device.\n");
}
dev_set_drvdata(temp, fa_dev);
在红外数据接收的代码中添加判断工厂命令的代码
//如果数据格式是工厂命令
if(XXXX) {
//为工厂命令数据的结构体赋值,这个值会被用户层的read函数读到
fa_dev->type=u8IRSwModeBuf[1];
fa_dev->value=u8IRSwModeBuf[2];
//是工厂命令,唤醒条件置1
FA_send = 1;
//唤醒注册到FA_waitq等待队列上的进程
wake_up_interruptible(&FA_waitq);
}
驱动部分的就这些了,最后在用户层用poll和read函数去读数据就行了
const char *name = "/dev/FA";
struct pollfd FAfd;
int FAret;
unsigned char buf[2];
//打开虚拟设备
int fd = open(name, O_RDONLY);
if (fd < 0) {
printf("---open fail\n");
return NULL;
}
//设置监视的文件描述符
FAfd.fd = fd;
//设置监视文件描述符的事件掩码
FAfd.events = POLLIN;
while(1) {
//第三个参数-1代表一直阻塞,0则是立即返回,正数是阻塞的超时时间
FAret = poll(&FAfd, 1, -1);
//如果在超时前没有任何事件发生,则返回0,如果成功会返回文件描述符个数
if (FAret <= 0)
continue;
//如果监视文件描述符的事件结果是有数据可读,则执行下一步
if (FAfd.revents & POLLIN) {
FAret = read(fd,buf,sizeof(buf));
if (FAret <= 0)
continue;
printf("buf---:0x%2.2X%2.2X.\n", buf[0], buf[1]);
}
}
close(fd);
return NULL;