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 + -
显示快捷键?