Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b9abaa3f authored by Linus Torvalds's avatar Linus Torvalds
Browse files
parents 8995b161 736ce432
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -3,7 +3,7 @@ Apple Touchpad Driver (appletouch)
	Copyright (C) 2005 Stelian Pop <stelian@popies.net>

appletouch is a Linux kernel driver for the USB touchpad found on post
February 2005 Apple Alu Powerbooks.
February 2005 and October 2005 Apple Aluminium Powerbooks.

This driver is derived from Johannes Berg's appletrackpad driver[1], but it has
been improved in some areas:
@@ -13,7 +13,8 @@ been improved in some areas:

Credits go to Johannes Berg for reverse-engineering the touchpad protocol,
Frank Arnold for further improvements, and Alex Harper for some additional
information about the inner workings of the touchpad sensors.
information about the inner workings of the touchpad sensors. Michael
Hanselmann added support for the October 2005 models.

Usage:
------
+213 −280
Original line number Diff line number Diff line
@@ -146,6 +146,7 @@ static int evdev_open(struct inode * inode, struct file * file)
}

#ifdef CONFIG_COMPAT

struct input_event_compat {
	struct compat_timeval time;
	__u16 type;
@@ -165,22 +166,80 @@ struct input_event_compat {
#  define COMPAT_TEST test_thread_flag(TIF_32BIT)
#endif

static ssize_t evdev_write_compat(struct file * file, const char __user * buffer, size_t count, loff_t *ppos)
static inline size_t evdev_event_size(void)
{
	struct evdev_list *list = file->private_data;
	struct input_event_compat event;
	int retval = 0;
	return COMPAT_TEST ?
		sizeof(struct input_event_compat) : sizeof(struct input_event);
}

	while (retval < count) {
		if (copy_from_user(&event, buffer + retval, sizeof(struct input_event_compat)))
static int evdev_event_from_user(const char __user *buffer, struct input_event *event)
{
	if (COMPAT_TEST) {
		struct input_event_compat compat_event;

		if (copy_from_user(&compat_event, buffer, sizeof(struct input_event_compat)))
			return -EFAULT;

		event->time.tv_sec = compat_event.time.tv_sec;
		event->time.tv_usec = compat_event.time.tv_usec;
		event->type = compat_event.type;
		event->code = compat_event.code;
		event->value = compat_event.value;

	} else {
		if (copy_from_user(event, buffer, sizeof(struct input_event)))
			return -EFAULT;
		input_event(list->evdev->handle.dev, event.type, event.code, event.value);
		retval += sizeof(struct input_event_compat);
	}

	return retval;
	return 0;
}
#endif

static int evdev_event_to_user(char __user *buffer, const struct input_event *event)
{
	if (COMPAT_TEST) {
		struct input_event_compat compat_event;

		compat_event.time.tv_sec = event->time.tv_sec;
		compat_event.time.tv_usec = event->time.tv_usec;
		compat_event.type = event->type;
		compat_event.code = event->code;
		compat_event.value = event->value;

		if (copy_to_user(buffer, &compat_event, sizeof(struct input_event_compat)))
			return -EFAULT;

	} else {
		if (copy_to_user(buffer, event, sizeof(struct input_event)))
			return -EFAULT;
	}

	return 0;
}

#else

static inline size_t evdev_event_size(void)
{
	return sizeof(struct input_event);
}

static int evdev_event_from_user(const char __user *buffer, struct input_event *event)
{
	if (copy_from_user(event, buffer, sizeof(struct input_event)))
		return -EFAULT;

	return 0;
}

static int evdev_event_to_user(char __user *buffer, const struct input_event *event)
{
	if (copy_to_user(buffer, event, sizeof(struct input_event)))
		return -EFAULT;

	return 0;
}

#endif /* CONFIG_COMPAT */

static ssize_t evdev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos)
{
@@ -188,31 +247,26 @@ static ssize_t evdev_write(struct file * file, const char __user * buffer, size_
	struct input_event event;
	int retval = 0;

	if (!list->evdev->exist) return -ENODEV;

#ifdef CONFIG_COMPAT
	if (COMPAT_TEST)
		return evdev_write_compat(file, buffer, count, ppos);
#endif
	if (!list->evdev->exist)
		return -ENODEV;

	while (retval < count) {

		if (copy_from_user(&event, buffer + retval, sizeof(struct input_event)))
		if (evdev_event_from_user(buffer + retval, &event))
			return -EFAULT;
		input_event(list->evdev->handle.dev, event.type, event.code, event.value);
		retval += sizeof(struct input_event);
		retval += evdev_event_size();
	}

	return retval;
}

#ifdef CONFIG_COMPAT
static ssize_t evdev_read_compat(struct file * file, char __user * buffer, size_t count, loff_t *ppos)
static ssize_t evdev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos)
{
	struct evdev_list *list = file->private_data;
	int retval;

	if (count < sizeof(struct input_event_compat))
	if (count < evdev_event_size())
		return -EINVAL;

	if (list->head == list->tail && list->evdev->exist && (file->f_flags & O_NONBLOCK))
@@ -227,80 +281,118 @@ static ssize_t evdev_read_compat(struct file * file, char __user * buffer, size_
	if (!list->evdev->exist)
		return -ENODEV;

	while (list->head != list->tail && retval + sizeof(struct input_event_compat) <= count) {
	while (list->head != list->tail && retval + evdev_event_size() <= count) {

		struct input_event *event = (struct input_event *) list->buffer + list->tail;
		struct input_event_compat event_compat;
		event_compat.time.tv_sec = event->time.tv_sec;
		event_compat.time.tv_usec = event->time.tv_usec;
		event_compat.type = event->type;
		event_compat.code = event->code;
		event_compat.value = event->value;

		if (copy_to_user(buffer + retval, &event_compat,
			sizeof(struct input_event_compat))) return -EFAULT;

		if (evdev_event_to_user(buffer + retval, event))
			return -EFAULT;

		list->tail = (list->tail + 1) & (EVDEV_BUFFER_SIZE - 1);
		retval += sizeof(struct input_event_compat);
		retval += evdev_event_size();
	}

	return retval;
}
#endif

static ssize_t evdev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos)
/* No kernel lock - fine */
static unsigned int evdev_poll(struct file *file, poll_table *wait)
{
	struct evdev_list *list = file->private_data;
	int retval;
	poll_wait(file, &list->evdev->wait, wait);
	return ((list->head == list->tail) ? 0 : (POLLIN | POLLRDNORM)) |
		(list->evdev->exist ? 0 : (POLLHUP | POLLERR));
}

#ifdef CONFIG_COMPAT
	if (COMPAT_TEST)
		return evdev_read_compat(file, buffer, count, ppos);
#endif

	if (count < sizeof(struct input_event))
		return -EINVAL;
#define BITS_PER_LONG_COMPAT (sizeof(compat_long_t) * 8)
#define NBITS_COMPAT(x) ((((x) - 1) / BITS_PER_LONG_COMPAT) + 1)

	if (list->head == list->tail && list->evdev->exist && (file->f_flags & O_NONBLOCK))
		return -EAGAIN;
#ifdef __BIG_ENDIAN
static int bits_to_user(unsigned long *bits, unsigned int maxbit,
			unsigned int maxlen, void __user *p, int compat)
{
	int len, i;

	if (compat) {
		len = NBITS_COMPAT(maxbit) * sizeof(compat_long_t);
		if (len < maxlen)
			len = maxlen;

		for (i = 0; i < len / sizeof(compat_long_t); i++)
			if (copy_to_user((compat_long_t __user *) p + i,
					 (compat_long_t *) bits +
						i + 1 - ((i % 2) << 1),
					 sizeof(compat_long_t)))
				return -EFAULT;
	} else {
		len = NBITS(maxbit) * sizeof(long);
		if (len > maxlen)
			len = maxlen;

	retval = wait_event_interruptible(list->evdev->wait,
		list->head != list->tail || (!list->evdev->exist));
		if (copy_to_user(p, bits, len))
			return -EFAULT;
	}

	if (retval)
		return retval;
	return len;
}
#else
static int bits_to_user(unsigned long *bits, unsigned int maxbit,
			unsigned int maxlen, void __user *p, int compat)
{
	int len = compat ?
			NBITS_COMPAT(maxbit) * sizeof(compat_long_t) :
			NBITS(maxbit) * sizeof(long);

	if (!list->evdev->exist)
		return -ENODEV;
	if (len > maxlen)
		len = maxlen;

	while (list->head != list->tail && retval + sizeof(struct input_event) <= count) {
		if (copy_to_user(buffer + retval, list->buffer + list->tail,
			sizeof(struct input_event))) return -EFAULT;
		list->tail = (list->tail + 1) & (EVDEV_BUFFER_SIZE - 1);
		retval += sizeof(struct input_event);
	return copy_to_user(p, bits, len) ? -EFAULT : len;
}
#endif /* __BIG_ENDIAN */

	return retval;
#else

static int bits_to_user(unsigned long *bits, unsigned int maxbit,
			unsigned int maxlen, void __user *p, int compat)
{
	int len = NBITS(maxbit) * sizeof(long);

	if (len > maxlen)
		len = maxlen;

	return copy_to_user(p, bits, len) ? -EFAULT : len;
}

/* No kernel lock - fine */
static unsigned int evdev_poll(struct file *file, poll_table *wait)
#endif /* CONFIG_COMPAT */

static int str_to_user(const char *str, unsigned int maxlen, void __user *p)
{
	struct evdev_list *list = file->private_data;
	poll_wait(file, &list->evdev->wait, wait);
	return ((list->head == list->tail) ? 0 : (POLLIN | POLLRDNORM)) |
		(list->evdev->exist ? 0 : (POLLHUP | POLLERR));
	int len;

	if (!str)
		return -ENOENT;

	len = strlen(str) + 1;
	if (len > maxlen)
		len = maxlen;

	return copy_to_user(p, str, len) ? -EFAULT : len;
}

static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
				void __user *p, int compat_mode)
{
	struct evdev_list *list = file->private_data;
	struct evdev *evdev = list->evdev;
	struct input_dev *dev = evdev->handle.dev;
	struct input_absinfo abs;
	void __user *p = (void __user *)arg;
	int __user *ip = (int __user *)arg;
	int __user *ip = (int __user *)p;
	int i, t, u, v;

	if (!evdev->exist) return -ENODEV;
	if (!evdev->exist)
		return -ENODEV;

	switch (cmd) {

@@ -308,26 +400,39 @@ static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
			return put_user(EV_VERSION, ip);

		case EVIOCGID:
			return copy_to_user(p, &dev->id, sizeof(struct input_id)) ? -EFAULT : 0;
			if (copy_to_user(p, &dev->id, sizeof(struct input_id)))
				return -EFAULT;

			return 0;

		case EVIOCGKEYCODE:
			if (get_user(t, ip)) return -EFAULT;
			if (t < 0 || t >= dev->keycodemax || !dev->keycodesize) return -EINVAL;
			if (put_user(INPUT_KEYCODE(dev, t), ip + 1)) return -EFAULT;
			if (get_user(t, ip))
				return -EFAULT;
			if (t < 0 || t >= dev->keycodemax || !dev->keycodesize)
				return -EINVAL;
			if (put_user(INPUT_KEYCODE(dev, t), ip + 1))
				return -EFAULT;
			return 0;

		case EVIOCSKEYCODE:
			if (get_user(t, ip)) return -EFAULT;
			if (t < 0 || t >= dev->keycodemax || !dev->keycodesize) return -EINVAL;
			if (get_user(v, ip + 1)) return -EFAULT;
			if (v < 0 || v > KEY_MAX) return -EINVAL;
			if (dev->keycodesize < sizeof(v) && (v >> (dev->keycodesize * 8))) return -EINVAL;
			if (get_user(t, ip))
				return -EFAULT;
			if (t < 0 || t >= dev->keycodemax || !dev->keycodesize)
				return -EINVAL;
			if (get_user(v, ip + 1))
				return -EFAULT;
			if (v < 0 || v > KEY_MAX)
				return -EINVAL;
			if (dev->keycodesize < sizeof(v) && (v >> (dev->keycodesize * 8)))
				return -EINVAL;

			u = SET_INPUT_KEYCODE(dev, t, v);
			clear_bit(u, dev->keybit);
			set_bit(v, dev->keybit);
			for (i = 0; i < dev->keycodemax; i++)
				if (INPUT_KEYCODE(dev, i) == u)
					set_bit(u, dev->keybit);

			return 0;

		case EVIOCSFF:
@@ -338,17 +443,17 @@ static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
				if (copy_from_user(&effect, p, sizeof(effect)))
					return -EFAULT;
				err = dev->upload_effect(dev, &effect);
				if (put_user(effect.id, &(((struct ff_effect __user *)arg)->id)))
				if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
					return -EFAULT;
				return err;
			}
			else return -ENOSYS;
			} else
				return -ENOSYS;

		case EVIOCRMFF:
			if (dev->erase_effect) {
				return dev->erase_effect(dev, (int)arg);
			}
			else return -ENOSYS;
			if (!dev->erase_effect)
				return -ENOSYS;

			return dev->erase_effect(dev, (int)(unsigned long) p);

		case EVIOCGEFFECTS:
			if (put_user(dev->ff_effects_max, ip))
@@ -356,7 +461,7 @@ static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
			return 0;

		case EVIOCGRAB:
			if (arg) {
			if (p) {
				if (evdev->grab)
					return -EBUSY;
				if (input_grab_device(&evdev->handle))
@@ -395,62 +500,33 @@ static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
						case EV_SW:  bits = dev->swbit;  len = SW_MAX;  break;
						default: return -EINVAL;
					}
					len = NBITS(len) * sizeof(long);
					if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
					return copy_to_user(p, bits, len) ? -EFAULT : len;
					return bits_to_user(bits, len, _IOC_SIZE(cmd), p, compat_mode);
				}

				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0))) {
					int len;
					len = NBITS(KEY_MAX) * sizeof(long);
					if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
					return copy_to_user(p, dev->key, len) ? -EFAULT : len;
				}
				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0)))
					return bits_to_user(dev->key, KEY_MAX, _IOC_SIZE(cmd),
							    p, compat_mode);

				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0))) {
					int len;
					len = NBITS(LED_MAX) * sizeof(long);
					if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
					return copy_to_user(p, dev->led, len) ? -EFAULT : len;
				}
				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0)))
					return bits_to_user(dev->led, LED_MAX, _IOC_SIZE(cmd),
							    p, compat_mode);

				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0))) {
					int len;
					len = NBITS(SND_MAX) * sizeof(long);
					if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
					return copy_to_user(p, dev->snd, len) ? -EFAULT : len;
				}
				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0)))
					return bits_to_user(dev->snd, SND_MAX, _IOC_SIZE(cmd),
							    p, compat_mode);

				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSW(0))) {
					int len;
					len = NBITS(SW_MAX) * sizeof(long);
					if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
					return copy_to_user(p, dev->sw, len) ? -EFAULT : len;
				}
				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSW(0)))
					return bits_to_user(dev->sw, SW_MAX, _IOC_SIZE(cmd),
							    p, compat_mode);

				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0))) {
					int len;
					if (!dev->name) return -ENOENT;
					len = strlen(dev->name) + 1;
					if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
					return copy_to_user(p, dev->name, len) ? -EFAULT : len;
				}
				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0)))
					return str_to_user(dev->name, _IOC_SIZE(cmd), p);

				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0))) {
					int len;
					if (!dev->phys) return -ENOENT;
					len = strlen(dev->phys) + 1;
					if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
					return copy_to_user(p, dev->phys, len) ? -EFAULT : len;
				}
				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0)))
					return str_to_user(dev->phys, _IOC_SIZE(cmd), p);

				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0))) {
					int len;
					if (!dev->uniq) return -ENOENT;
					len = strlen(dev->uniq) + 1;
					if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
					return copy_to_user(p, dev->uniq, len) ? -EFAULT : len;
				}
				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0)))
					return str_to_user(dev->uniq, _IOC_SIZE(cmd), p);

				if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {

@@ -492,158 +568,15 @@ static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
	return -EINVAL;
}

#ifdef CONFIG_COMPAT

#define BITS_PER_LONG_COMPAT (sizeof(compat_long_t) * 8)
#define NBITS_COMPAT(x) ((((x)-1)/BITS_PER_LONG_COMPAT)+1)
#define OFF_COMPAT(x)  ((x)%BITS_PER_LONG_COMPAT)
#define BIT_COMPAT(x)  (1UL<<OFF_COMPAT(x))
#define LONG_COMPAT(x) ((x)/BITS_PER_LONG_COMPAT)
#define test_bit_compat(bit, array) ((array[LONG_COMPAT(bit)] >> OFF_COMPAT(bit)) & 1)

#ifdef __BIG_ENDIAN
#define bit_to_user(bit, max) \
do { \
	int i; \
	int len = NBITS_COMPAT((max)) * sizeof(compat_long_t); \
	if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); \
	for (i = 0; i < len / sizeof(compat_long_t); i++) \
		if (copy_to_user((compat_long_t __user *) p + i, \
				 (compat_long_t*) (bit) + i + 1 - ((i % 2) << 1), \
				 sizeof(compat_long_t))) \
			return -EFAULT; \
	return len; \
} while (0)
#else
#define bit_to_user(bit, max) \
do { \
	int len = NBITS_COMPAT((max)) * sizeof(compat_long_t); \
	if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); \
	return copy_to_user(p, (bit), len) ? -EFAULT : len; \
} while (0)
#endif

static long evdev_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	struct evdev_list *list = file->private_data;
	struct evdev *evdev = list->evdev;
	struct input_dev *dev = evdev->handle.dev;
	struct input_absinfo abs;
	void __user *p = compat_ptr(arg);

	if (!evdev->exist) return -ENODEV;

	switch (cmd) {

		case EVIOCGVERSION:
		case EVIOCGID:
		case EVIOCGKEYCODE:
		case EVIOCSKEYCODE:
		case EVIOCSFF:
		case EVIOCRMFF:
		case EVIOCGEFFECTS:
		case EVIOCGRAB:
			return evdev_ioctl(file, cmd, (unsigned long) p);

		default:

			if (_IOC_TYPE(cmd) != 'E')
				return -EINVAL;

			if (_IOC_DIR(cmd) == _IOC_READ) {

				if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0,0))) {
					long *bits;
					int max;

					switch (_IOC_NR(cmd) & EV_MAX) {
						case      0: bits = dev->evbit;  max = EV_MAX;  break;
						case EV_KEY: bits = dev->keybit; max = KEY_MAX; break;
						case EV_REL: bits = dev->relbit; max = REL_MAX; break;
						case EV_ABS: bits = dev->absbit; max = ABS_MAX; break;
						case EV_MSC: bits = dev->mscbit; max = MSC_MAX; break;
						case EV_LED: bits = dev->ledbit; max = LED_MAX; break;
						case EV_SND: bits = dev->sndbit; max = SND_MAX; break;
						case EV_FF:  bits = dev->ffbit;  max = FF_MAX;  break;
						case EV_SW:  bits = dev->swbit;  max = SW_MAX;  break;
						default: return -EINVAL;
					}
					bit_to_user(bits, max);
				}

				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0)))
					bit_to_user(dev->key, KEY_MAX);

				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0)))
					bit_to_user(dev->led, LED_MAX);

				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0)))
					bit_to_user(dev->snd, SND_MAX);

				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSW(0)))
					bit_to_user(dev->sw, SW_MAX);

				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0))) {
					int len;
					if (!dev->name) return -ENOENT;
					len = strlen(dev->name) + 1;
					if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
					return copy_to_user(p, dev->name, len) ? -EFAULT : len;
				}

				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0))) {
					int len;
					if (!dev->phys) return -ENOENT;
					len = strlen(dev->phys) + 1;
					if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
					return copy_to_user(p, dev->phys, len) ? -EFAULT : len;
				}

				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0))) {
					int len;
					if (!dev->uniq) return -ENOENT;
					len = strlen(dev->uniq) + 1;
					if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
					return copy_to_user(p, dev->uniq, len) ? -EFAULT : len;
				}

				if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {

					int t = _IOC_NR(cmd) & ABS_MAX;

					abs.value = dev->abs[t];
					abs.minimum = dev->absmin[t];
					abs.maximum = dev->absmax[t];
					abs.fuzz = dev->absfuzz[t];
					abs.flat = dev->absflat[t];

					if (copy_to_user(p, &abs, sizeof(struct input_absinfo)))
						return -EFAULT;

					return 0;
	return evdev_ioctl_handler(file, cmd, (void __user *)arg, 0);
}
			}

			if (_IOC_DIR(cmd) == _IOC_WRITE) {

				if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {

					int t = _IOC_NR(cmd) & ABS_MAX;

					if (copy_from_user(&abs, p, sizeof(struct input_absinfo)))
						return -EFAULT;

					dev->abs[t] = abs.value;
					dev->absmin[t] = abs.minimum;
					dev->absmax[t] = abs.maximum;
					dev->absfuzz[t] = abs.fuzz;
					dev->absflat[t] = abs.flat;

					return 0;
				}
			}
	}
	return -EINVAL;
#ifdef CONFIG_COMPAT
static long evdev_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
{
	return evdev_ioctl_handler(file, cmd, compat_ptr(arg), 1);
}
#endif

+7 −0
Original line number Diff line number Diff line
@@ -52,5 +52,12 @@ config GAMEPORT_EMU10K1
config GAMEPORT_FM801
	tristate "ForteMedia FM801 gameport support"
	depends on PCI
	help
	  Say Y here if you have ForteMedia FM801 PCI audio controller
	  (Abit AU10, Genius Sound Maker, HP Workstation zx2000,
	  and others), and want to use its gameport.

	  To compile this driver as a module, choose M here: the
	  module will be called fm801-gp.

endif
+1 −1
Original line number Diff line number Diff line
@@ -321,7 +321,7 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
	switch (code) {
		case ATKBD_RET_BAT:
			atkbd->enabled = 0;
			serio_rescan(atkbd->ps2dev.serio);
			serio_reconnect(atkbd->ps2dev.serio);
			goto out;
		case ATKBD_RET_EMUL0:
			atkbd->emul = 1;
+85 −17
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <asm/machdep.h>
#include <asm/io.h>

@@ -24,7 +25,7 @@ MODULE_AUTHOR("Richard Zidlicky <rz@linux-m68k.org>");
MODULE_DESCRIPTION("m68k beeper driver");
MODULE_LICENSE("GPL");

static struct input_dev *m68kspkr_dev;
static struct platform_device *m68kspkr_platform_device;

static int m68kspkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
@@ -47,36 +48,103 @@ static int m68kspkr_event(struct input_dev *dev, unsigned int type, unsigned int
	return 0;
}

static int __devinit m68kspkr_probe(struct platform_device *dev)
{
	struct input_dev *input_dev;
	int err;

	input_dev = input_allocate_device();
	if (!input_dev)
		return -ENOMEM;

	input_dev->name = "m68k beeper";
	input_dev->phys = "m68k/generic";
	input_dev->id.bustype = BUS_HOST;
	input_dev->id.vendor  = 0x001f;
	input_dev->id.product = 0x0001;
	input_dev->id.version = 0x0100;
	input_dev->cdev.dev = &dev->dev;

	input_dev->evbit[0] = BIT(EV_SND);
	input_dev->sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE);
	input_dev->event = m68kspkr_event;

	err = input_register_device(input_dev);
	if (err) {
		input_free_device(input_dev);
		return err;
	}

	platform_set_drvdata(dev, input_dev);

	return 0;
}

static int __devexit m68kspkr_remove(struct platform_device *dev)
{
	struct input_dev *input_dev = platform_get_drvdata(dev);

	input_unregister_device(input_dev);
	platform_set_drvdata(dev, NULL);
	/* turn off the speaker */
	m68kspkr_event(NULL, EV_SND, SND_BELL, 0);

	return 0;
}

static void m68kspkr_shutdown(struct platform_device *dev)
{
	/* turn off the speaker */
	m68kspkr_event(NULL, EV_SND, SND_BELL, 0);
}

static struct platform_driver m68kspkr_platform_driver = {
	.driver		= {
		.name	= "m68kspkr",
		.owner	= THIS_MODULE,
	},
	.probe		= m68kspkr_probe,
	.remove		= __devexit_p(m68kspkr_remove),
	.shutdown	= m68kspkr_shutdown,
};

static int __init m68kspkr_init(void)
{
	int err;

	if (!mach_beep) {
		printk(KERN_INFO "m68kspkr: no lowlevel beep support\n");
		return -ENODEV;
        }

	m68kspkr_dev = input_allocate_device();
	if (!m68kspkr_dev)
		return -ENOMEM;

	m68kspkr_dev->name = "m68k beeper";
	m68kspkr_dev->phys = "m68k/generic";
	m68kspkr_dev->id.bustype = BUS_HOST;
	m68kspkr_dev->id.vendor = 0x001f;
	m68kspkr_dev->id.product = 0x0001;
	m68kspkr_dev->id.version = 0x0100;
	err = platform_driver_register(&m68kspkr_platform_driver);
	if (err)
		return err;

	m68kspkr_dev->evbit[0] = BIT(EV_SND);
	m68kspkr_dev->sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE);
	m68kspkr_dev->event = m68kspkr_event;
	m68kspkr_platform_device = platform_device_alloc("m68kspkr", -1);
	if (!m68kspkr_platform_device) {
		err = -ENOMEM;
		goto err_unregister_driver;
	}

	input_register_device(m68kspkr_dev);
	err = platform_device_add(m68kspkr_platform_device);
	if (err)
		goto err_free_device;

	return 0;

 err_free_device:
	platform_device_put(m68kspkr_platform_device);
 err_unregister_driver:
	platform_driver_unregister(&m68kspkr_platform_driver);

	return err;
}

static void __exit m68kspkr_exit(void)
{
        input_unregister_device(m68kspkr_dev);
	platform_device_unregister(m68kspkr_platform_device);
	platform_driver_unregister(&m68kspkr_platform_driver);
}

module_init(m68kspkr_init);
Loading