in gpiolib-cdev.c [285:404]
static int linehandle_create(struct gpio_device *gdev, void __user *ip)
{
struct gpiohandle_request handlereq;
struct linehandle_state *lh;
struct file *file;
int fd, i, ret;
u32 lflags;
if (copy_from_user(&handlereq, ip, sizeof(handlereq)))
return -EFAULT;
if ((handlereq.lines == 0) || (handlereq.lines > GPIOHANDLES_MAX))
return -EINVAL;
lflags = handlereq.flags;
ret = linehandle_validate_flags(lflags);
if (ret)
return ret;
lh = kzalloc(sizeof(*lh), GFP_KERNEL);
if (!lh)
return -ENOMEM;
lh->gdev = gdev;
get_device(&gdev->dev);
if (handlereq.consumer_label[0] != '\0') {
/* label is only initialized if consumer_label is set */
lh->label = kstrndup(handlereq.consumer_label,
sizeof(handlereq.consumer_label) - 1,
GFP_KERNEL);
if (!lh->label) {
ret = -ENOMEM;
goto out_free_lh;
}
}
lh->num_descs = handlereq.lines;
/* Request each GPIO */
for (i = 0; i < handlereq.lines; i++) {
u32 offset = handlereq.lineoffsets[i];
struct gpio_desc *desc = gpiochip_get_desc(gdev->chip, offset);
if (IS_ERR(desc)) {
ret = PTR_ERR(desc);
goto out_free_lh;
}
ret = gpiod_request(desc, lh->label);
if (ret)
goto out_free_lh;
lh->descs[i] = desc;
linehandle_flags_to_desc_flags(handlereq.flags, &desc->flags);
ret = gpiod_set_transitory(desc, false);
if (ret < 0)
goto out_free_lh;
/*
* Lines have to be requested explicitly for input
* or output, else the line will be treated "as is".
*/
if (lflags & GPIOHANDLE_REQUEST_OUTPUT) {
int val = !!handlereq.default_values[i];
ret = gpiod_direction_output(desc, val);
if (ret)
goto out_free_lh;
} else if (lflags & GPIOHANDLE_REQUEST_INPUT) {
ret = gpiod_direction_input(desc);
if (ret)
goto out_free_lh;
}
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_lh;
}
file = anon_inode_getfile("gpio-linehandle",
&linehandle_fileops,
lh,
O_RDONLY | O_CLOEXEC);
if (IS_ERR(file)) {
ret = PTR_ERR(file);
goto out_put_unused_fd;
}
handlereq.fd = fd;
if (copy_to_user(ip, &handlereq, sizeof(handlereq))) {
/*
* 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",
lh->num_descs);
return 0;
out_put_unused_fd:
put_unused_fd(fd);
out_free_lh:
linehandle_free(lh);
return ret;
}