joydev.c

来自「linux 内核源代码」· C语言 代码 · 共 900 行 · 第 1/2 页

C
900
字号
/* * Joystick device driver for the input driver suite. * * Copyright (c) 1999-2002 Vojtech Pavlik * Copyright (c) 1999 Colin Van Dyke * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */#include <asm/io.h>#include <asm/system.h>#include <linux/delay.h>#include <linux/errno.h>#include <linux/joystick.h>#include <linux/input.h>#include <linux/kernel.h>#include <linux/major.h>#include <linux/slab.h>#include <linux/mm.h>#include <linux/miscdevice.h>#include <linux/module.h>#include <linux/poll.h>#include <linux/init.h>#include <linux/device.h>MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");MODULE_DESCRIPTION("Joystick device interfaces");MODULE_SUPPORTED_DEVICE("input/js");MODULE_LICENSE("GPL");#define JOYDEV_MINOR_BASE	0#define JOYDEV_MINORS		16#define JOYDEV_BUFFER_SIZE	64struct joydev {	int exist;	int open;	int minor;	char name[16];	struct input_handle handle;	wait_queue_head_t wait;	struct list_head client_list;	spinlock_t client_lock; /* protects client_list */	struct mutex mutex;	struct device dev;	struct js_corr corr[ABS_MAX + 1];	struct JS_DATA_SAVE_TYPE glue;	int nabs;	int nkey;	__u16 keymap[KEY_MAX - BTN_MISC + 1];	__u16 keypam[KEY_MAX - BTN_MISC + 1];	__u8 absmap[ABS_MAX + 1];	__u8 abspam[ABS_MAX + 1];	__s16 abs[ABS_MAX + 1];};struct joydev_client {	struct js_event buffer[JOYDEV_BUFFER_SIZE];	int head;	int tail;	int startup;	spinlock_t buffer_lock; /* protects access to buffer, head and tail */	struct fasync_struct *fasync;	struct joydev *joydev;	struct list_head node;};static struct joydev *joydev_table[JOYDEV_MINORS];static DEFINE_MUTEX(joydev_table_mutex);static int joydev_correct(int value, struct js_corr *corr){	switch (corr->type) {	case JS_CORR_NONE:		break;	case JS_CORR_BROKEN:		value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 :			((corr->coef[3] * (value - corr->coef[1])) >> 14)) :			((corr->coef[2] * (value - corr->coef[0])) >> 14);		break;	default:		return 0;	}	return value < -32767 ? -32767 : (value > 32767 ? 32767 : value);}static void joydev_pass_event(struct joydev_client *client,			      struct js_event *event){	struct joydev *joydev = client->joydev;	/*	 * IRQs already disabled, just acquire the lock	 */	spin_lock(&client->buffer_lock);	client->buffer[client->head] = *event;	if (client->startup == joydev->nabs + joydev->nkey) {		client->head++;		client->head &= JOYDEV_BUFFER_SIZE - 1;		if (client->tail == client->head)			client->startup = 0;	}	spin_unlock(&client->buffer_lock);	kill_fasync(&client->fasync, SIGIO, POLL_IN);}static void joydev_event(struct input_handle *handle,			 unsigned int type, unsigned int code, int value){	struct joydev *joydev = handle->private;	struct joydev_client *client;	struct js_event event;	switch (type) {	case EV_KEY:		if (code < BTN_MISC || value == 2)			return;		event.type = JS_EVENT_BUTTON;		event.number = joydev->keymap[code - BTN_MISC];		event.value = value;		break;	case EV_ABS:		event.type = JS_EVENT_AXIS;		event.number = joydev->absmap[code];		event.value = joydev_correct(value,					&joydev->corr[event.number]);		if (event.value == joydev->abs[event.number])			return;		joydev->abs[event.number] = event.value;		break;	default:		return;	}	event.time = jiffies_to_msecs(jiffies);	rcu_read_lock();	list_for_each_entry_rcu(client, &joydev->client_list, node)		joydev_pass_event(client, &event);	rcu_read_unlock();	wake_up_interruptible(&joydev->wait);}static int joydev_fasync(int fd, struct file *file, int on){	int retval;	struct joydev_client *client = file->private_data;	retval = fasync_helper(fd, file, on, &client->fasync);	return retval < 0 ? retval : 0;}static void joydev_free(struct device *dev){	struct joydev *joydev = container_of(dev, struct joydev, dev);	kfree(joydev);}static void joydev_attach_client(struct joydev *joydev,				 struct joydev_client *client){	spin_lock(&joydev->client_lock);	list_add_tail_rcu(&client->node, &joydev->client_list);	spin_unlock(&joydev->client_lock);	synchronize_rcu();}static void joydev_detach_client(struct joydev *joydev,				 struct joydev_client *client){	spin_lock(&joydev->client_lock);	list_del_rcu(&client->node);	spin_unlock(&joydev->client_lock);	synchronize_rcu();}static int joydev_open_device(struct joydev *joydev){	int retval;	retval = mutex_lock_interruptible(&joydev->mutex);	if (retval)		return retval;	if (!joydev->exist)		retval = -ENODEV;	else if (!joydev->open++) {		retval = input_open_device(&joydev->handle);		if (retval)			joydev->open--;	}	mutex_unlock(&joydev->mutex);	return retval;}static void joydev_close_device(struct joydev *joydev){	mutex_lock(&joydev->mutex);	if (joydev->exist && !--joydev->open)		input_close_device(&joydev->handle);	mutex_unlock(&joydev->mutex);}/* * Wake up users waiting for IO so they can disconnect from * dead device. */static void joydev_hangup(struct joydev *joydev){	struct joydev_client *client;	spin_lock(&joydev->client_lock);	list_for_each_entry(client, &joydev->client_list, node)		kill_fasync(&client->fasync, SIGIO, POLL_HUP);	spin_unlock(&joydev->client_lock);	wake_up_interruptible(&joydev->wait);}static int joydev_release(struct inode *inode, struct file *file){	struct joydev_client *client = file->private_data;	struct joydev *joydev = client->joydev;	joydev_fasync(-1, file, 0);	joydev_detach_client(joydev, client);	kfree(client);	joydev_close_device(joydev);	put_device(&joydev->dev);	return 0;}static int joydev_open(struct inode *inode, struct file *file){	struct joydev_client *client;	struct joydev *joydev;	int i = iminor(inode) - JOYDEV_MINOR_BASE;	int error;	if (i >= JOYDEV_MINORS)		return -ENODEV;	error = mutex_lock_interruptible(&joydev_table_mutex);	if (error)		return error;	joydev = joydev_table[i];	if (joydev)		get_device(&joydev->dev);	mutex_unlock(&joydev_table_mutex);	if (!joydev)		return -ENODEV;	client = kzalloc(sizeof(struct joydev_client), GFP_KERNEL);	if (!client) {		error = -ENOMEM;		goto err_put_joydev;	}	spin_lock_init(&client->buffer_lock);	client->joydev = joydev;	joydev_attach_client(joydev, client);	error = joydev_open_device(joydev);	if (error)		goto err_free_client;	file->private_data = client;	return 0; err_free_client:	joydev_detach_client(joydev, client);	kfree(client); err_put_joydev:	put_device(&joydev->dev);	return error;}static int joydev_generate_startup_event(struct joydev_client *client,					 struct input_dev *input,					 struct js_event *event){	struct joydev *joydev = client->joydev;	int have_event;	spin_lock_irq(&client->buffer_lock);	have_event = client->startup < joydev->nabs + joydev->nkey;	if (have_event) {		event->time = jiffies_to_msecs(jiffies);		if (client->startup < joydev->nkey) {			event->type = JS_EVENT_BUTTON | JS_EVENT_INIT;			event->number = client->startup;			event->value = !!test_bit(joydev->keypam[event->number],						  input->key);		} else {			event->type = JS_EVENT_AXIS | JS_EVENT_INIT;			event->number = client->startup - joydev->nkey;			event->value = joydev->abs[event->number];		}		client->startup++;	}	spin_unlock_irq(&client->buffer_lock);	return have_event;}static int joydev_fetch_next_event(struct joydev_client *client,				   struct js_event *event){	int have_event;	spin_lock_irq(&client->buffer_lock);	have_event = client->head != client->tail;	if (have_event) {		*event = client->buffer[client->tail++];		client->tail &= JOYDEV_BUFFER_SIZE - 1;	}	spin_unlock_irq(&client->buffer_lock);	return have_event;}/* * Old joystick interface */static ssize_t joydev_0x_read(struct joydev_client *client,			      struct input_dev *input,			      char __user *buf){	struct joydev *joydev = client->joydev;	struct JS_DATA_TYPE data;	int i;	spin_lock_irq(&input->event_lock);	/*	 * Get device state	 */	for (data.buttons = i = 0; i < 32 && i < joydev->nkey; i++)		data.buttons |=			test_bit(joydev->keypam[i], input->key) ? (1 << i) : 0;	data.x = (joydev->abs[0] / 256 + 128) >> joydev->glue.JS_CORR.x;	data.y = (joydev->abs[1] / 256 + 128) >> joydev->glue.JS_CORR.y;	/*	 * Reset reader's event queue	 */	spin_lock(&client->buffer_lock);	client->startup = 0;	client->tail = client->head;	spin_unlock(&client->buffer_lock);	spin_unlock_irq(&input->event_lock);	if (copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE)))		return -EFAULT;	return sizeof(struct JS_DATA_TYPE);}static inline int joydev_data_pending(struct joydev_client *client){	struct joydev *joydev = client->joydev;	return client->startup < joydev->nabs + joydev->nkey ||		client->head != client->tail;}static ssize_t joydev_read(struct file *file, char __user *buf,			   size_t count, loff_t *ppos){	struct joydev_client *client = file->private_data;	struct joydev *joydev = client->joydev;	struct input_dev *input = joydev->handle.dev;	struct js_event event;	int retval;	if (!joydev->exist)		return -ENODEV;	if (count < sizeof(struct js_event))		return -EINVAL;	if (count == sizeof(struct JS_DATA_TYPE))		return joydev_0x_read(client, input, buf);	if (!joydev_data_pending(client) && (file->f_flags & O_NONBLOCK))		return -EAGAIN;	retval = wait_event_interruptible(joydev->wait,			!joydev->exist || joydev_data_pending(client));	if (retval)		return retval;	if (!joydev->exist)		return -ENODEV;	while (retval + sizeof(struct js_event) <= count &&	       joydev_generate_startup_event(client, input, &event)) {		if (copy_to_user(buf + retval, &event, sizeof(struct js_event)))			return -EFAULT;		retval += sizeof(struct js_event);	}	while (retval + sizeof(struct js_event) <= count &&	       joydev_fetch_next_event(client, &event)) {		if (copy_to_user(buf + retval, &event, sizeof(struct js_event)))			return -EFAULT;		retval += sizeof(struct js_event);	}	return retval;}/* No kernel lock - fine */static unsigned int joydev_poll(struct file *file, poll_table *wait){

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?