in gpiolib-cdev.c [1313:1457]
static int linereq_create(struct gpio_device *gdev, void __user *ip)
{
struct gpio_v2_line_request ulr;
struct gpio_v2_line_config *lc;
struct linereq *lr;
struct file *file;
u64 flags;
unsigned int i;
int fd, ret;
if (copy_from_user(&ulr, ip, sizeof(ulr)))
return -EFAULT;
if ((ulr.num_lines == 0) || (ulr.num_lines > GPIO_V2_LINES_MAX))
return -EINVAL;
if (memchr_inv(ulr.padding, 0, sizeof(ulr.padding)))
return -EINVAL;
lc = &ulr.config;
ret = gpio_v2_line_config_validate(lc, ulr.num_lines);
if (ret)
return ret;
lr = kzalloc(struct_size(lr, lines, ulr.num_lines), GFP_KERNEL);
if (!lr)
return -ENOMEM;
lr->gdev = gdev;
get_device(&gdev->dev);
for (i = 0; i < ulr.num_lines; i++) {
lr->lines[i].req = lr;
WRITE_ONCE(lr->lines[i].sw_debounced, 0);
INIT_DELAYED_WORK(&lr->lines[i].work, debounce_work_func);
}
if (ulr.consumer[0] != '\0') {
/* label is only initialized if consumer is set */
lr->label = kstrndup(ulr.consumer, sizeof(ulr.consumer) - 1,
GFP_KERNEL);
if (!lr->label) {
ret = -ENOMEM;
goto out_free_linereq;
}
}
mutex_init(&lr->config_mutex);
init_waitqueue_head(&lr->wait);
lr->event_buffer_size = ulr.event_buffer_size;
if (lr->event_buffer_size == 0)
lr->event_buffer_size = ulr.num_lines * 16;
else if (lr->event_buffer_size > GPIO_V2_LINES_MAX * 16)
lr->event_buffer_size = GPIO_V2_LINES_MAX * 16;
atomic_set(&lr->seqno, 0);
lr->num_lines = ulr.num_lines;
/* Request each GPIO */
for (i = 0; i < ulr.num_lines; i++) {
u32 offset = ulr.offsets[i];
struct gpio_desc *desc = gpiochip_get_desc(gdev->chip, offset);
if (IS_ERR(desc)) {
ret = PTR_ERR(desc);
goto out_free_linereq;
}
ret = gpiod_request(desc, lr->label);
if (ret)
goto out_free_linereq;
lr->lines[i].desc = desc;
flags = gpio_v2_line_config_flags(lc, i);
gpio_v2_line_config_flags_to_desc_flags(flags, &desc->flags);
ret = gpiod_set_transitory(desc, false);
if (ret < 0)
goto out_free_linereq;
/*
* Lines have to be requested explicitly for input
* or output, else the line will be treated "as is".
*/
if (flags & GPIO_V2_LINE_FLAG_OUTPUT) {
int val = gpio_v2_line_config_output_value(lc, i);
ret = gpiod_direction_output(desc, val);
if (ret)
goto out_free_linereq;
} else if (flags & GPIO_V2_LINE_FLAG_INPUT) {
ret = gpiod_direction_input(desc);
if (ret)
goto out_free_linereq;
ret = edge_detector_setup(&lr->lines[i], lc, i,
flags & GPIO_V2_LINE_EDGE_FLAGS);
if (ret)
goto out_free_linereq;
}
blocking_notifier_call_chain(&desc->gdev->notifier,
GPIO_V2_LINE_CHANGED_REQUESTED, desc);
dev_dbg(&gdev->dev, "registered chardev handle for line %d\n",
offset);
}
fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
if (fd < 0) {
ret = fd;
goto out_free_linereq;
}
file = anon_inode_getfile("gpio-line", &line_fileops, lr,
O_RDONLY | O_CLOEXEC);
if (IS_ERR(file)) {
ret = PTR_ERR(file);
goto out_put_unused_fd;
}
ulr.fd = fd;
if (copy_to_user(ip, &ulr, sizeof(ulr))) {
/*
* fput() will trigger the release() callback, so do not go onto
* the regular error cleanup path here.
*/
fput(file);
put_unused_fd(fd);
return -EFAULT;
}
fd_install(fd, file);
dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n",
lr->num_lines);
return 0;
out_put_unused_fd:
put_unused_fd(fd);
out_free_linereq:
linereq_free(lr);
return ret;
}