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

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

#include #include #include #include #include #include #include #include #include #define EVENT_TYPE_LIGHTABS_MISC #define LIGHTSENSOR_NAME"lightsensor" #define S5PV210_SENSOR_CHANNEL2 #define INIT_POLL_INTERVAL10000#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; intlight_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 =https://www.it610.com/article/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 = https://www.it610.com/article/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 intlightsensor_enable(struct lightsensor_data *sensor) { // open the input dev struct input_dev*input = sensor->input_poll_dev->input; atomic_cmpxchg(&sensor->enabled, 0, 1); return 0; }static intlightsensor_disable(struct lightsensor_data *sensor) { // close the input dev struct 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); else lightsensor_disable(sensor); return size; }static ssize_tattr_light_value_show(struct device *dev, struct device_attribute *attr, char *buf) { //input_get_drvdata is obidient here, because input_get_drvdata = https://www.it610.com/article/sensor_data->input_poll_dev struct 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= https://www.it610.com/article/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 = https://www.it610.com/article/88; printk("%ssensor->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 #include #include #include #include #include #include #define INPUTFILE "/dev/input/event0" int gotdata=https://www.it610.com/article/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=https://www.it610.com/article/0; } }


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

这样利用input子系统,一个光传感器的驱动就写好了。代码量很少。
另外,在input子系统中, 事件的汇报采用的是异步的机制,也就是说当report event时,也会向应用程序发送SIGIO信号,因此只要在应用程序中将file设置成异步模式,就可以读到SIGNO信号,然后在中断程序中读取信号,也是可以的。因为event都是不可预知的,所以采用异步的方式更加科学。
【input|input 子系统的分析与学习(二)】
记录一下,我在写这个驱动时候,犯的错误,自己调试了很长时间。
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子系统中几个重要的结构体。




    推荐阅读