input 子系统的分析与学习(二)

来源:互联网 发布:数据库开发教育 编辑:程序博客网 时间:2024/06/06 00:02

下面分析一下自己实现的光传感器的驱动。先附上代码:

 

#include <linux/init.h>   #include <linux/module.h>   #include <linux/types.h>   #include <linux/fs.h>   #include <linux/proc_fs.h>   #include <linux/device.h>   #include <asm/uaccess.h>   #include <linux/input-polldev.h>#include <linux/mutex.h>#define EVENT_TYPE_LIGHT  ABS_MISC#define LIGHTSENSOR_NAME  "lightsensor"#define S5PV210_SENSOR_CHANNEL   2#define INIT_POLL_INTERVAL  10000#define sensor_dbg(format, arg...) do {\printk("%s : " format , __func__,  ## arg) ;\} while (0)static DEFINE_MUTEX(sensor_mutex);MODULE_LICENSE("GPL");  MODULE_DESCRIPTION("Light Sensor Driver");  struct lightsensor_data {struct input_polled_dev *input_poll_dev;atomic_t enabled;int  light_value;};static struct lightsensor_data *sensor_data;static int poll_cnt = 0;static int lightsensor_set_value(struct lightsensor_data *sensor, int value){mutex_lock(&sensor_mutex);sensor->light_value =  value;mutex_unlock(&sensor_mutex);return 0;}static int lightsensor_get_value(struct lightsensor_data *sensor){printk("%s  %d\n", __func__,  sensor->light_value);int ret;mutex_lock(&sensor_mutex);ret  = sensor->light_value ;mutex_unlock(&sensor_mutex);return ret;}void lightsensor_input_poll_func(struct input_polled_dev *input) {struct lightsensor_data *sensor = input->private;u32 enabled = atomic_read(&sensor->enabled); printk("%s, sensor %p enabled: %u,   light_value:  %d poll_rate: %d \n", __func__,  sensor, enabled, sensor->light_value,  sensor->input_poll_dev->poll_interval);if(enalbed) {// int value = mini2440_adc_get_light();int value = poll_cnt++;input_event(input->input, EV_FF, EVENT_TYPE_LIGHT,  value); input_sync(input->input);sensor_dbg("input_report\n");//lightsensor_set_value( sensor, value);}  //else //sensor_dbg("input_not report\n");}static int  lightsensor_enable(struct lightsensor_data *sensor){// open the input devstruct input_dev*  input = sensor->input_poll_dev->input;atomic_cmpxchg(&sensor->enabled, 0, 1);return 0;}static int  lightsensor_disable(struct lightsensor_data *sensor){// close the input devstruct input_dev*  input = sensor->input_poll_dev->input;atomic_cmpxchg(&sensor->enabled, 1, 0) ;return 0;}static ssize_t attr_polling_rate_show(struct device *dev,  struct device_attribute *attr, char *buf){   unsigned int val;struct lightsensor_data *sensor = sensor_data;mutex_lock(&sensor_mutex);val = sensor->input_poll_dev->poll_interval;mutex_unlock(&sensor_mutex);return sprintf(buf, "%u\n", val);}static ssize_t attr_polling_rate_store(struct device *dev,     struct device_attribute *attr,     const char *buf, size_t size){struct lightsensor_data *sensor = sensor_data;long interval_ms;if (strict_strtol(buf, 10, &interval_ms))return -EINVAL;if (!interval_ms)return -EINVAL;mutex_lock(&sensor_mutex);sensor->input_poll_dev->poll_interval = interval_ms; mutex_unlock(&sensor_mutex);return size;}static ssize_t attr_enable_show(struct device *dev,       struct device_attribute *attr, char *buf){struct lightsensor_data *sensor = sensor_data;u32 val = atomic_read(&sensor->enabled);return sprintf(buf, "%d\n", val);}static ssize_t attr_enable_store(struct device *dev,       struct device_attribute *attr,       const char *buf, size_t size){struct lightsensor_data *sensor = sensor_data;unsigned long val;if (strict_strtoul(buf, 10, &val))return -EINVAL;if (val)lightsensor_enable(sensor);elselightsensor_disable(sensor);return size;}static ssize_t  attr_light_value_show(struct device *dev,       struct device_attribute *attr, char *buf){//input_get_drvdata is obidient here, because input_get_drvdata = sensor_data->input_poll_devstruct lightsensor_data *sensor = sensor_data;int val = lightsensor_get_value(sensor);printk("%s: sensor_data %p, sensor %p val %d\n", __func__, sensor_data, sensor, val);return sprintf(buf, "%d\n", val);}static struct device_attribute attributes[] = {__ATTR(pollrate_ms, 0666, attr_polling_rate_show, attr_polling_rate_store),__ATTR(enable_device, 0666, attr_enable_show, attr_enable_store),__ATTR(light_value, 0444, attr_light_value_show, NULL),};static int create_sysfs_interfaces(struct device *dev){int i;for (i = 0; i < ARRAY_SIZE(attributes); i++)if (device_create_file(dev, attributes + i))goto error;return 0;error:printk("%s error, i = %d\n", __func__, i );for ( ; i >= 0; i--)      device_remove_file(dev, attributes + i);      dev_err(dev, "%s:Unable to create interface\n", __func__);     return -1;}static int remove_sysfs_interfaces(struct device *dev){int i;for (i = 0; i < ARRAY_SIZE(attributes); i++)device_remove_file(dev, attributes + i);return 0;}static int __init lightsensor_init(void){int ret = -1;struct input_dev *input;sensor_data= kmalloc(sizeof(struct lightsensor_data),  GFP_KERNEL) ;if(!sensor_data) {printk("kmalloc\n");goto kmalloc_error;}sensor_data->input_poll_dev = input_allocate_polled_device();if(! sensor_data->input_poll_dev) {sensor_dbg("error\n");goto alloc_error;}sensor_data->input_poll_dev->private = sensor_data;sensor_data->input_poll_dev->poll = lightsensor_input_poll_func;sensor_data->input_poll_dev->poll_interval = INIT_POLL_INTERVAL;input = sensor_data->input_poll_dev->input;input->name = LIGHTSENSOR_NAME;//input_set_drvdata(sensor_data->input_poll_dev->input, sensor_data);set_bit(EV_FF, input->evbit); ret = input_register_polled_device(sensor_data->input_poll_dev);if(ret) {printk("input_register_polled_device error\n");return ret;}sensor_dbg("\n");atomic_set(&sensor_data->enabled, 0);sensor_data->light_value = 88;printk("%s  sensor->light_value:%d \n ",   __func__,  sensor_data->light_value);if ( create_sysfs_interfaces(&input->dev) ) {printk("create_sysfs_interface failed\n");goto sys_error;}return ret;sys_error:input_unregister_polled_device(sensor_data->input_poll_dev);alloc_error:kfree(sensor_data);kmalloc_error:return ret;}static void __exit lightsensor_exit(void){remove_sysfs_interfaces( &sensor_data->input_poll_dev->input->dev);input_unregister_polled_device(sensor_data->input_poll_dev);input_free_polled_device(sensor_data->input_poll_dev);kfree(sensor_data);}module_init(lightsensor_init);  module_exit(lightsensor_exit); 


我将这个设备实现成了一个input_poll_dev,这个设备的具体实现是在driver/input/input-polldev.c中的。我们这里是用了一个它的模型。因为 我们的光传感器要做的事情就是周期性的向应用程序汇报光的值。 而input-polldev正好实现了这个功能,具体到我们的光传感器主要就是要实现Poll这个函数。为了实现控制这个光传感器,另外增加了sysfs中的接口enable、 pollrate 、light_value。可以看到利用Inut子系统,我们在写驱动时候就非常的方便。

 

对应的在用户态的 应用程序如下:

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <signal.h>#include <fcntl.h>#include <linux/input.h>#define INPUTFILE "/dev/input/event0"int gotdata=0;void sighandler(int signo){if (signo==SIGIO)gotdata++;return;}char buffer[4096];int main(int argc, char **argv){int count;struct sigaction action;struct input_event event;    int fd = open(INPUTFILE, O_RDONLY);memset(&action, 0, sizeof(action));action.sa_handler = sighandler;action.sa_flags = 0;sigaction(SIGIO, &action, NULL);fcntl(fd, F_SETOWN, getpid());fcntl(fd, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | FASYNC);while(1) {/* this only returns if a signal arrives */sleep(86400); /* one day */if (!gotdata)continue;count=read(fd, &event, sizeof(event));printf("event: %d %d %d\n", event.type, event.code, event.value);/* buggy: if avail data is more than 4kbytes... */gotdata=0;}}

 

刚开始看这个input子系统时,知道驱动report了event,但是不知道该去哪里读,自己研究半天。终于找到了对应的可操作的文件就是 /dev/input/inputX这些文件。其中/dev/input/是在/dev系统里面创建了一个目录,应该也是利用device_create接口创建的,但是目前自己还不是很明确在/dev下面创建directory的方法。明天再继续看。

 

这样利用input子系统,一个光传感器的驱动就写好了。代码量很少。

另外,在input子系统中, 事件的汇报采用的是异步的机制,也就是说当report event时,也会向应用程序发送SIGIO信号,因此只要在应用程序中将file设置成异步模式,就可以读到SIGNO信号,然后在中断程序中读取信号,也是可以的。因为event都是不可预知的,所以采用异步的方式更加科学。

 

记录一下,我在写这个驱动时候,犯的错误,自己调试了很长时间。

1. 首先,我在写lightsensor.c时,给input_dev注册了自己的read\write 函数。 就像注册poll函数一样。

sensor_data->input_poll_dev->read = lightsensor_input_read_func;但是这些函数总是不被调用。 后来发现是,在调用input_register_polled_device(sensor_data->input_poll_dev)时,这里会注册新的open\read\write函数,在open的时候创建delayd work queue。所以我们在用input_poll_dev时,自己只需要实现poll函数就好了,如果注册了其他函数,并替换掉了inpu_poll_dev本身的函数,就会导致这个设备不能正常的工作。

2.  关于sysfs 的错误,这个错误自己也调试了挺长时间。其实就是由于input_get_drvdata和input_set_drvdata的使用有误造成的。我在lightsensor.c 中,设置了dev的private,  input_set_drvdata(sensor_data->input_poll_dev->input, sensor_data); 然后在 sysfs的  static ssize_t attr_polling_rate_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) 函数中调用input_get_drvdata,但是得到的指针指向的不是sensor_data。 我以为是sysfs的使用错误,调试了很久。结果发现是在input_poll_dev中 也使用了这个函数对,将这个指针指向了别的地方。 所以,这就更说明了一点,在我们基于input_poll_dev实现的设备中,不能利用input_poll_dev中使用的input_dev的方法。编程时需要非常的注意,否则就会因为小错调试很长时间。

 

明天继续分析INPUT子系统中几个重要的结构体。

 

 


原创粉丝点击