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

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

 

#include    
#include    
#include    
#include    
#include    
#include    
#include    
#include 
#include 

#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 dev
	
	struct 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 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_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_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= 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 
#include 
#include 
#include 
#include 
#include 
#include 
#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子系统中几个重要的结构体。

 

 


你可能感兴趣的:(input 子系统的分析与学习(二))