Changeset View
Standalone View
sys/dev/evdev/cdev.c
| Show First 20 Lines • Show All 72 Lines • ▼ Show 20 Lines | |||||
| static d_write_t evdev_write; | static d_write_t evdev_write; | ||||
| static d_ioctl_t evdev_ioctl; | static d_ioctl_t evdev_ioctl; | ||||
| static d_poll_t evdev_poll; | static d_poll_t evdev_poll; | ||||
| static d_kqfilter_t evdev_kqfilter; | static d_kqfilter_t evdev_kqfilter; | ||||
| static int evdev_kqread(struct knote *kn, long hint); | static int evdev_kqread(struct knote *kn, long hint); | ||||
| static void evdev_kqdetach(struct knote *kn); | static void evdev_kqdetach(struct knote *kn); | ||||
| static void evdev_dtor(void *); | static void evdev_dtor(void *); | ||||
| static int evdev_ioctl_eviocgbit(struct evdev_dev *, int, int, caddr_t); | static int evdev_ioctl_eviocgbit(struct evdev_dev *, int, int, caddr_t, | ||||
| struct thread *); | |||||
| static void evdev_client_filter_queue(struct evdev_client *, uint16_t); | static void evdev_client_filter_queue(struct evdev_client *, uint16_t); | ||||
| static struct cdevsw evdev_cdevsw = { | static struct cdevsw evdev_cdevsw = { | ||||
| .d_version = D_VERSION, | .d_version = D_VERSION, | ||||
| .d_open = evdev_open, | .d_open = evdev_open, | ||||
| .d_read = evdev_read, | .d_read = evdev_read, | ||||
| .d_write = evdev_write, | .d_write = evdev_write, | ||||
| .d_ioctl = evdev_ioctl, | .d_ioctl = evdev_ioctl, | ||||
| ▲ Show 20 Lines • Show All 481 Lines • ▼ Show 20 Lines | case EVIOCSCLOCKID: | ||||
| default: | default: | ||||
| return (EINVAL); | return (EINVAL); | ||||
| } | } | ||||
| } | } | ||||
| /* evdev variable-length ioctls handling */ | /* evdev variable-length ioctls handling */ | ||||
| switch (IOCBASECMD(cmd)) { | switch (IOCBASECMD(cmd)) { | ||||
| case EVIOCGNAME(0): | case EVIOCGNAME(0): | ||||
| strlcpy(data, evdev->ev_name, len); | /* Linux evdev does not terminate truncated strings with 0 */ | ||||
| limit = MIN(strlen(evdev->ev_name) + 1, len); | |||||
| memcpy(data, evdev->ev_name, limit); | |||||
| td->td_retval[0] = limit; | |||||
wulf: Could you use limit variable to be consistent with existing code?
Also couple words about why… | |||||
Not Done Inline Actions
ok
I missed the reason for this modification, on Linux when ioctl is EVIOCGNAME | EVIOCGPHYS | EVIOCGUNIQ, it use str_to_user to copy data. static int str_to_user(const char *str, unsigned int maxlen, void __user *p)
{
int len;
if (!str)
return -ENOENT;
len = strlen(str) + 1;
if (len > maxlen)
len = maxlen;
return copy_to_user(p, str, len) ? -EFAULT : len;
}ankohuu_outlook.com: > Could you use limit variable to be consistent with existing code?
ok
>
> Also couple words… | |||||
Not Done Inline ActionsThis requires one line comment as it is not obvious and refers Linux sources. wulf: This requires one line comment as it is not obvious and refers Linux sources. | |||||
| return (0); | return (0); | ||||
| case EVIOCGPHYS(0): | case EVIOCGPHYS(0): | ||||
| if (evdev->ev_shortname[0] == 0) | if (evdev->ev_shortname[0] == 0) | ||||
| return (ENOENT); | return (ENOENT); | ||||
| strlcpy(data, evdev->ev_shortname, len); | limit = MIN(strlen(evdev->ev_shortname) + 1, len); | ||||
| memcpy(data, evdev->ev_shortname, limit); | |||||
| td->td_retval[0] = limit; | |||||
| return (0); | return (0); | ||||
| case EVIOCGUNIQ(0): | case EVIOCGUNIQ(0): | ||||
| if (evdev->ev_serial[0] == 0) | if (evdev->ev_serial[0] == 0) | ||||
| return (ENOENT); | return (ENOENT); | ||||
| strlcpy(data, evdev->ev_serial, len); | limit = MIN(strlen(evdev->ev_serial) + 1, len); | ||||
| memcpy(data, evdev->ev_serial, limit); | |||||
| td->td_retval[0] = limit; | |||||
| return (0); | return (0); | ||||
| case EVIOCGPROP(0): | case EVIOCGPROP(0): | ||||
| limit = MIN(len, bitstr_size(INPUT_PROP_CNT)); | limit = MIN(len, bitstr_size(INPUT_PROP_CNT)); | ||||
| memcpy(data, evdev->ev_prop_flags, limit); | memcpy(data, evdev->ev_prop_flags, limit); | ||||
| td->td_retval[0] = limit; | |||||
| return (0); | return (0); | ||||
| case EVIOCGMTSLOTS(0): | case EVIOCGMTSLOTS(0): | ||||
| /* EVIOCGMTSLOTS always returns 0 on success */ | |||||
| if (evdev->ev_mt == NULL) | if (evdev->ev_mt == NULL) | ||||
| return (EINVAL); | return (EINVAL); | ||||
| if (len < sizeof(uint32_t)) | if (len < sizeof(uint32_t)) | ||||
| return (EINVAL); | return (EINVAL); | ||||
| code = *(uint32_t *)data; | code = *(uint32_t *)data; | ||||
| if (!ABS_IS_MT(code)) | if (!ABS_IS_MT(code)) | ||||
| return (EINVAL); | return (EINVAL); | ||||
| nvalues = | nvalues = | ||||
| MIN(len / sizeof(int32_t) - 1, MAXIMAL_MT_SLOT(evdev) + 1); | MIN(len / sizeof(int32_t) - 1, MAXIMAL_MT_SLOT(evdev) + 1); | ||||
| for (int i = 0; i < nvalues; i++) | for (int i = 0; i < nvalues; i++) | ||||
| ((int32_t *)data)[i + 1] = | ((int32_t *)data)[i + 1] = | ||||
| evdev_get_mt_value(evdev, i, code); | evdev_get_mt_value(evdev, i, code); | ||||
Not Done Inline ActionsDo EVIOCGMTSLOTS returns the length? wulf: Do EVIOCGMTSLOTS returns the length? | |||||
Not Done Inline Actions
It returns 0 when correct, ankohuu_outlook.com: > Do EVIOCGMTSLOTS returns the length?
It returns 0 when correct,
when device is not MT or… | |||||
Not Done Inline ActionsThis requires one line comment to as it does not matches other cases. wulf: This requires one line comment to as it does not matches other cases. | |||||
| return (0); | return (0); | ||||
| case EVIOCGKEY(0): | case EVIOCGKEY(0): | ||||
| limit = MIN(len, bitstr_size(KEY_CNT)); | limit = MIN(len, bitstr_size(KEY_CNT)); | ||||
| EVDEV_LOCK(evdev); | EVDEV_LOCK(evdev); | ||||
| evdev_client_filter_queue(client, EV_KEY); | evdev_client_filter_queue(client, EV_KEY); | ||||
| memcpy(data, evdev->ev_key_states, limit); | memcpy(data, evdev->ev_key_states, limit); | ||||
| EVDEV_UNLOCK(evdev); | EVDEV_UNLOCK(evdev); | ||||
| td->td_retval[0] = limit; | |||||
| return (0); | return (0); | ||||
| case EVIOCGLED(0): | case EVIOCGLED(0): | ||||
| limit = MIN(len, bitstr_size(LED_CNT)); | limit = MIN(len, bitstr_size(LED_CNT)); | ||||
| EVDEV_LOCK(evdev); | EVDEV_LOCK(evdev); | ||||
| evdev_client_filter_queue(client, EV_LED); | evdev_client_filter_queue(client, EV_LED); | ||||
| memcpy(data, evdev->ev_led_states, limit); | memcpy(data, evdev->ev_led_states, limit); | ||||
| EVDEV_UNLOCK(evdev); | EVDEV_UNLOCK(evdev); | ||||
| td->td_retval[0] = limit; | |||||
| return (0); | return (0); | ||||
| case EVIOCGSND(0): | case EVIOCGSND(0): | ||||
| limit = MIN(len, bitstr_size(SND_CNT)); | limit = MIN(len, bitstr_size(SND_CNT)); | ||||
| EVDEV_LOCK(evdev); | EVDEV_LOCK(evdev); | ||||
| evdev_client_filter_queue(client, EV_SND); | evdev_client_filter_queue(client, EV_SND); | ||||
| memcpy(data, evdev->ev_snd_states, limit); | memcpy(data, evdev->ev_snd_states, limit); | ||||
| EVDEV_UNLOCK(evdev); | EVDEV_UNLOCK(evdev); | ||||
| td->td_retval[0] = limit; | |||||
| return (0); | return (0); | ||||
| case EVIOCGSW(0): | case EVIOCGSW(0): | ||||
| limit = MIN(len, bitstr_size(SW_CNT)); | limit = MIN(len, bitstr_size(SW_CNT)); | ||||
| EVDEV_LOCK(evdev); | EVDEV_LOCK(evdev); | ||||
| evdev_client_filter_queue(client, EV_SW); | evdev_client_filter_queue(client, EV_SW); | ||||
| memcpy(data, evdev->ev_sw_states, limit); | memcpy(data, evdev->ev_sw_states, limit); | ||||
| EVDEV_UNLOCK(evdev); | EVDEV_UNLOCK(evdev); | ||||
| td->td_retval[0] = limit; | |||||
| return (0); | return (0); | ||||
| case EVIOCGBIT(0, 0) ... EVIOCGBIT(EV_MAX, 0): | case EVIOCGBIT(0, 0) ... EVIOCGBIT(EV_MAX, 0): | ||||
| type_num = IOCBASECMD(cmd) - EVIOCGBIT(0, 0); | type_num = IOCBASECMD(cmd) - EVIOCGBIT(0, 0); | ||||
| debugf(client, "EVIOCGBIT(%d): data=%p, len=%d", type_num, | debugf(client, "EVIOCGBIT(%d): data=%p, len=%d", type_num, | ||||
| data, len); | data, len); | ||||
| return (evdev_ioctl_eviocgbit(evdev, type_num, len, data)); | return (evdev_ioctl_eviocgbit(evdev, type_num, len, data, td)); | ||||
Not Done Inline ActionsIMO returning of length in len or limit field rather in td->td_retval would make the code cleaner. wulf: IMO returning of length in len or limit field rather in td->td_retval would make the code… | |||||
Done Inline Actions
I didn't understand, do you mean I need change return value's meaning of evdev_ioctl_eviocgbit, now it represents operation EVIOCGBIT's error code, ankohuu_outlook.com: > IMO returning of length in len or limit field rather in td->td_retval would make the code… | |||||
Not Done Inline Actions
I meant exactly that. Passing &len as output parameter looks cleaner than passing td as output parameter. And does not require 1 extra argument to evdev_ioctl_eviocgbit(). wulf: > Or should I change evdev_ioctl_eviocgbit's argument len to &len
I meant exactly that. | |||||
| } | } | ||||
| return (EINVAL); | return (EINVAL); | ||||
| } | } | ||||
| static int | static int | ||||
| evdev_ioctl_eviocgbit(struct evdev_dev *evdev, int type, int len, caddr_t data) | evdev_ioctl_eviocgbit(struct evdev_dev *evdev, int type, int len, caddr_t data, | ||||
| struct thread *td) | |||||
| { | { | ||||
| unsigned long *bitmap; | unsigned long *bitmap; | ||||
| int limit; | int limit; | ||||
| switch (type) { | switch (type) { | ||||
| case 0: | case 0: | ||||
| bitmap = evdev->ev_type_flags; | bitmap = evdev->ev_type_flags; | ||||
| limit = EV_CNT; | limit = EV_CNT; | ||||
| Show All 27 Lines | case EV_SW: | ||||
| limit = SW_CNT; | limit = SW_CNT; | ||||
| break; | break; | ||||
| case EV_FF: | case EV_FF: | ||||
| /* | /* | ||||
| * We don't support EV_FF now, so let's | * We don't support EV_FF now, so let's | ||||
| * just fake it returning only zeros. | * just fake it returning only zeros. | ||||
| */ | */ | ||||
| bzero(data, len); | bzero(data, len); | ||||
| td->td_retval[0] = len; | |||||
| return (0); | return (0); | ||||
| default: | default: | ||||
| return (ENOTTY); | return (ENOTTY); | ||||
| } | } | ||||
| /* | /* | ||||
| * Clear ioctl data buffer in case it's bigger than | * Clear ioctl data buffer in case it's bigger than | ||||
| * bitmap size | * bitmap size | ||||
| */ | */ | ||||
| bzero(data, len); | bzero(data, len); | ||||
| limit = bitstr_size(limit); | limit = bitstr_size(limit); | ||||
| len = MIN(limit, len); | len = MIN(limit, len); | ||||
| memcpy(data, bitmap, len); | memcpy(data, bitmap, len); | ||||
| td->td_retval[0] = len; | |||||
| return (0); | return (0); | ||||
| } | } | ||||
| void | void | ||||
| evdev_revoke_client(struct evdev_client *client) | evdev_revoke_client(struct evdev_client *client) | ||||
| { | { | ||||
| EVDEV_LIST_LOCK_ASSERT(client->ec_evdev); | EVDEV_LIST_LOCK_ASSERT(client->ec_evdev); | ||||
| ▲ Show 20 Lines • Show All 211 Lines • Show Last 20 Lines | |||||
Could you use limit variable to be consistent with existing code?
Also couple words about why strlen/memcpy is used instead of strlcopy would be nice.