evdev: Add COMPAT_FREEBSD32 support for amd64 arch

Incompatibility between i386 and amd64 evdev ABIs was caused by presence of
'struct timeval' in evdev protocol. Replace it with 'struct timeval32' for
32 bit binaries.

Big-endian platforms may require additional work due to bitstr_t (array of
unsigned longs) usage in ioctl interface.

MFC after:	2 weeks
This commit is contained in:
Vladimir Kondratyev 2020-03-29 23:00:33 +00:00
parent c3e9752ea1
commit 80ddbddfd7

View File

@ -47,6 +47,18 @@
#include <dev/evdev/evdev_private.h>
#include <dev/evdev/input.h>
#ifdef COMPAT_FREEBSD32
#include <sys/mount.h>
#include <sys/sysent.h>
#include <compat/freebsd32/freebsd32.h>
struct input_event32 {
struct timeval32 time;
uint16_t type;
uint16_t code;
int32_t value;
};
#endif
#ifdef EVDEV_DEBUG
#define debugf(client, fmt, args...) printf("evdev cdev: "fmt"\n", ##args)
#else
@ -161,7 +173,14 @@ static int
evdev_read(struct cdev *dev, struct uio *uio, int ioflag)
{
struct evdev_client *client;
struct input_event event;
union {
struct input_event t;
#ifdef COMPAT_FREEBSD32
struct input_event32 t32;
#endif
} event;
struct input_event *head;
size_t evsize;
int ret = 0;
int remaining;
@ -175,11 +194,18 @@ evdev_read(struct cdev *dev, struct uio *uio, int ioflag)
if (client->ec_revoked)
return (ENODEV);
#ifdef COMPAT_FREEBSD32
if (SV_CURPROC_FLAG(SV_ILP32))
evsize = sizeof(struct input_event32);
else
#endif
evsize = sizeof(struct input_event);
/* Zero-sized reads are allowed for error checking */
if (uio->uio_resid != 0 && uio->uio_resid < sizeof(struct input_event))
if (uio->uio_resid != 0 && uio->uio_resid < evsize)
return (EINVAL);
remaining = uio->uio_resid / sizeof(struct input_event);
remaining = uio->uio_resid / evsize;
EVDEV_CLIENT_LOCKQ(client);
@ -196,14 +222,24 @@ evdev_read(struct cdev *dev, struct uio *uio, int ioflag)
}
while (ret == 0 && !EVDEV_CLIENT_EMPTYQ(client) && remaining > 0) {
memcpy(&event, &client->ec_buffer[client->ec_buffer_head],
sizeof(struct input_event));
head = client->ec_buffer + client->ec_buffer_head;
#ifdef COMPAT_FREEBSD32
if (SV_CURPROC_FLAG(SV_ILP32)) {
bzero(&event.t32, sizeof(struct input_event32));
TV_CP(*head, event.t32, time);
CP(*head, event.t32, type);
CP(*head, event.t32, code);
CP(*head, event.t32, value);
} else
#endif
bcopy(head, &event.t, evsize);
client->ec_buffer_head =
(client->ec_buffer_head + 1) % client->ec_buffer_size;
remaining--;
EVDEV_CLIENT_UNLOCKQ(client);
ret = uiomove(&event, sizeof(struct input_event), uio);
ret = uiomove(&event, evsize, uio);
EVDEV_CLIENT_LOCKQ(client);
}
@ -217,7 +253,13 @@ evdev_write(struct cdev *dev, struct uio *uio, int ioflag)
{
struct evdev_dev *evdev = dev->si_drv1;
struct evdev_client *client;
struct input_event event;
union {
struct input_event t;
#ifdef COMPAT_FREEBSD32
struct input_event32 t32;
#endif
} event;
size_t evsize;
int ret = 0;
ret = devfs_get_cdevpriv((void **)&client);
@ -230,16 +272,30 @@ evdev_write(struct cdev *dev, struct uio *uio, int ioflag)
if (client->ec_revoked || evdev == NULL)
return (ENODEV);
if (uio->uio_resid % sizeof(struct input_event) != 0) {
#ifdef COMPAT_FREEBSD32
if (SV_CURPROC_FLAG(SV_ILP32))
evsize = sizeof(struct input_event32);
else
#endif
evsize = sizeof(struct input_event);
if (uio->uio_resid % evsize != 0) {
debugf(client, "write size not multiple of input_event size");
return (EINVAL);
}
while (uio->uio_resid > 0 && ret == 0) {
ret = uiomove(&event, sizeof(struct input_event), uio);
if (ret == 0)
ret = evdev_inject_event(evdev, event.type, event.code,
event.value);
ret = uiomove(&event, evsize, uio);
if (ret == 0) {
#ifdef COMPAT_FREEBSD32
if (SV_CURPROC_FLAG(SV_ILP32))
ret = evdev_inject_event(evdev, event.t32.type,
event.t32.code, event.t32.value);
else
#endif
ret = evdev_inject_event(evdev, event.t.type,
event.t.code, event.t.value);
}
}
return (ret);