/* * Array sizes must ensure 64-bit alignment and not create holes in the * struct packing.
*/
static_assert(IS_ALIGNED(GPIO_V2_LINES_MAX, 2));
static_assert(IS_ALIGNED(GPIO_MAX_NAME_SIZE, 8));
/* Character device interface to GPIO. * * The GPIO character device, /dev/gpiochipN, provides userspace an * interface to gpiolib GPIOs via ioctl()s.
*/
/* * GPIO line handle management
*/
#ifdef CONFIG_GPIO_CDEV_V1 /** * struct linehandle_state - contains the state of a userspace handle * @gdev: the GPIO device the handle pertains to * @label: consumer label used to tag descriptors * @descs: the GPIO descriptors held by this handle * @num_descs: the number of descriptors held in the descs array
*/ struct linehandle_state { struct gpio_device *gdev; constchar *label; struct gpio_desc *descs[GPIOHANDLES_MAX];
u32 num_descs;
};
staticint linehandle_validate_flags(u32 flags)
{ /* Return an error if an unknown flag is set */ if (flags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) return -EINVAL;
/* * Do not allow both INPUT & OUTPUT flags to be set as they are * contradictory.
*/ if ((flags & GPIOHANDLE_REQUEST_INPUT) &&
(flags & GPIOHANDLE_REQUEST_OUTPUT)) return -EINVAL;
/* * Do not allow OPEN_SOURCE & OPEN_DRAIN flags in a single request. If * the hardware actually supports enabling both at the same time the * electrical result would be disastrous.
*/ if ((flags & GPIOHANDLE_REQUEST_OPEN_DRAIN) &&
(flags & GPIOHANDLE_REQUEST_OPEN_SOURCE)) return -EINVAL;
/* OPEN_DRAIN and OPEN_SOURCE flags only make sense for output mode. */ if (!(flags & GPIOHANDLE_REQUEST_OUTPUT) &&
((flags & GPIOHANDLE_REQUEST_OPEN_DRAIN) ||
(flags & GPIOHANDLE_REQUEST_OPEN_SOURCE))) return -EINVAL;
/* Bias flags only allowed for input or output mode. */ if (!((flags & GPIOHANDLE_REQUEST_INPUT) ||
(flags & GPIOHANDLE_REQUEST_OUTPUT)) &&
((flags & GPIOHANDLE_REQUEST_BIAS_DISABLE) ||
(flags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) ||
(flags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN))) return -EINVAL;
/* Only one bias flag can be set. */ if (((flags & GPIOHANDLE_REQUEST_BIAS_DISABLE) &&
(flags & (GPIOHANDLE_REQUEST_BIAS_PULL_DOWN |
GPIOHANDLE_REQUEST_BIAS_PULL_UP))) ||
((flags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) &&
(flags & GPIOHANDLE_REQUEST_BIAS_PULL_UP))) return -EINVAL;
if (!rcu_access_pointer(lh->gdev->chip)) return -ENODEV;
switch (cmd) { case GPIOHANDLE_GET_LINE_VALUES_IOCTL: /* NOTE: It's okay to read values of output lines */
ret = gpiod_get_array_value_complex(false, true,
lh->num_descs, lh->descs,
NULL, vals); if (ret) return ret;
memset(&ghd, 0, sizeof(ghd)); for (i = 0; i < lh->num_descs; i++)
ghd.values[i] = test_bit(i, vals);
if (copy_to_user(ip, &ghd, sizeof(ghd))) return -EFAULT;
return 0; case GPIOHANDLE_SET_LINE_VALUES_IOCTL: /* * All line descriptors were created at once with the same * flags so just check if the first one is really output.
*/ if (!test_bit(FLAG_IS_OUT, &lh->descs[0]->flags)) return -EPERM;
if (copy_from_user(&ghd, ip, sizeof(ghd))) return -EFAULT;
/* Clamp all values to [0,1] */ for (i = 0; i < lh->num_descs; i++)
__assign_bit(i, vals, ghd.values[i]);
/* Reuse the array setting function */ return gpiod_set_array_value_complex(false, true,
lh->num_descs,
lh->descs,
NULL,
vals); case GPIOHANDLE_SET_CONFIG_IOCTL: return linehandle_set_config(lh, ip); default: return -EINVAL;
}
}
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 = gpio_device_get_desc(gdev, offset);
if (IS_ERR(desc)) {
ret = PTR_ERR(desc); goto out_free_lh;
}
ret = gpiod_request_user(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_nonotify(desc, val); if (ret) goto out_free_lh;
} elseif (lflags & GPIOHANDLE_REQUEST_INPUT) {
ret = gpiod_direction_input_nonotify(desc); if (ret) goto out_free_lh;
}
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);
/** * struct line - contains the state of a requested line * @desc: the GPIO descriptor for this line. * @req: the corresponding line request * @irq: the interrupt triggered in response to events on this GPIO * @edflags: the edge flags, GPIO_V2_LINE_FLAG_EDGE_RISING and/or * GPIO_V2_LINE_FLAG_EDGE_FALLING, indicating the edge detection applied * @timestamp_ns: cache for the timestamp storing it between hardirq and * IRQ thread, used to bring the timestamp close to the actual event * @req_seqno: the seqno for the current edge event in the sequence of * events for the corresponding line request. This is drawn from the @req. * @line_seqno: the seqno for the current edge event in the sequence of * events for this line. * @work: the worker that implements software debouncing * @sw_debounced: flag indicating if the software debouncer is active * @level: the current debounced physical level of the line * @hdesc: the Hardware Timestamp Engine (HTE) descriptor * @raw_level: the line level at the time of event * @total_discard_seq: the running counter of the discarded events * @last_seqno: the last sequence number before debounce period expires
*/ struct line { struct gpio_desc *desc; /* * -- edge detector specific fields --
*/ struct linereq *req; unsignedint irq; /* * The flags for the active edge detector configuration. * * edflags is set by linereq_create(), linereq_free(), and * linereq_set_config(), which are themselves mutually * exclusive, and is accessed by edge_irq_thread(), * process_hw_ts_thread() and debounce_work_func(), * which can all live with a slightly stale value.
*/
u64 edflags; /* * timestamp_ns and req_seqno are accessed only by * edge_irq_handler() and edge_irq_thread(), which are themselves * mutually exclusive, so no additional protection is necessary.
*/
u64 timestamp_ns;
u32 req_seqno; /* * line_seqno is accessed by either edge_irq_thread() or * debounce_work_func(), which are themselves mutually exclusive, * so no additional protection is necessary.
*/
u32 line_seqno; /* * -- debouncer specific fields --
*/ struct delayed_work work; /* * sw_debounce is accessed by linereq_set_config(), which is the * only setter, and linereq_get_values(), which can live with a * slightly stale value.
*/ unsignedint sw_debounced; /* * level is accessed by debounce_work_func(), which is the only * setter, and linereq_get_values() which can live with a slightly * stale value.
*/ unsignedint level; #ifdef CONFIG_HTE struct hte_ts_desc hdesc; /* * HTE provider sets line level at the time of event. The valid * value is 0 or 1 and negative value for an error.
*/ int raw_level; /* * when sw_debounce is set on HTE enabled line, this is running * counter of the discarded events.
*/
u32 total_discard_seq; /* * when sw_debounce is set on HTE enabled line, this variable records * last sequence number before debounce period expires.
*/
u32 last_seqno; #endif/* CONFIG_HTE */
};
/** * struct linereq - contains the state of a userspace line request * @gdev: the GPIO device the line request pertains to * @label: consumer label used to tag GPIO descriptors * @num_lines: the number of lines in the lines array * @wait: wait queue that handles blocking reads of events * @device_unregistered_nb: notifier block for receiving gdev unregister events * @event_buffer_size: the number of elements allocated in @events * @events: KFIFO for the GPIO events * @seqno: the sequence number for edge events generated on all lines in * this line request. Note that this is not used when @num_lines is 1, as * the line_seqno is then the same and is cheaper to calculate. * @config_mutex: mutex for serializing ioctl() calls to ensure consistency * of configuration, particularly multi-step accesses to desc flags. * @lines: the lines held by this line request, with @num_lines elements.
*/ struct linereq { struct gpio_device *gdev; constchar *label;
u32 num_lines;
wait_queue_head_t wait; struct notifier_block device_unregistered_nb;
u32 event_buffer_size;
DECLARE_KFIFO_PTR(events, struct gpio_v2_line_event);
atomic_t seqno; struct mutex config_mutex; struct line lines[] __counted_by(num_lines);
};
/* Do not leak kernel stack to userspace */
memset(&le, 0, sizeof(le));
if (line->timestamp_ns) {
le.timestamp_ns = line->timestamp_ns;
} else { /* * We may be running from a nested threaded interrupt in * which case we didn't get the timestamp from * edge_irq_handler().
*/
le.timestamp_ns = line_event_timestamp(line); if (lr->num_lines != 1)
line->req_seqno = atomic_inc_return(&lr->seqno);
}
line->timestamp_ns = 0;
/* * Just store the timestamp in hardirq context so we get it as * close in time as possible to the actual event.
*/
line->timestamp_ns = line_event_timestamp(line);
if (lr->num_lines != 1)
line->req_seqno = atomic_inc_return(&lr->seqno);
return IRQ_WAKE_THREAD;
}
/* * returns the current debounced logical value.
*/ staticbool debounced_value(struct line *line)
{ bool value;
/* * minor race - debouncer may be stopped here, so edge_detector_stop() * must leave the value unchanged so the following will read the level * from when the debouncer was last running.
*/
value = READ_ONCE(line->level);
if (test_bit(FLAG_ACTIVE_LOW, &line->desc->flags))
value = !value;
/* switch from physical level to logical - if they differ */ if (edflags & GPIO_V2_LINE_FLAG_ACTIVE_LOW)
level = !level;
/* ignore edges that are not being monitored */ if (((eflags == GPIO_V2_LINE_FLAG_EDGE_RISING) && !level) ||
((eflags == GPIO_V2_LINE_FLAG_EDGE_FALLING) && level)) return;
/* Do not leak kernel stack to userspace */
memset(&le, 0, sizeof(le));
staticint debounce_setup(struct line *line, unsignedint debounce_period_us)
{ unsignedlong irqflags; int ret, level, irq; char *label;
/* * Try hardware. Skip gpiod_set_config() to avoid emitting two * CHANGED_CONFIG line state events.
*/
ret = gpio_do_set_config(line->desc,
pinconf_to_config_packed(PIN_CONFIG_INPUT_DEBOUNCE,
debounce_period_us)); if (ret != -ENOTSUPP) return ret;
if (debounce_period_us) { /* setup software debounce */
level = gpiod_get_raw_value_cansleep(line->desc); if (level < 0) return level;
if (!(IS_ENABLED(CONFIG_HTE) &&
test_bit(FLAG_EVENT_CLOCK_HTE, &line->desc->flags))) {
irq = gpiod_to_irq(line->desc); if (irq < 0) return -ENXIO;
label = make_irq_label(line->req->label); if (IS_ERR(label)) return -ENOMEM;
irqflags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;
ret = request_irq(irq, debounce_irq_handler, irqflags,
label, line); if (ret) {
free_irq_label(label); return ret;
}
line->irq = irq;
} else {
ret = hte_edge_setup(line, GPIO_V2_LINE_FLAG_EDGE_BOTH); if (ret) return ret;
}
for (i = 0; i < lc->num_attrs; i++) { if ((lc->attrs[i].attr.id == GPIO_V2_LINE_ATTR_ID_DEBOUNCE) &&
(lc->attrs[i].mask & mask)) return lc->attrs[i].attr.debounce_period_us;
} return 0;
}
staticvoid edge_detector_stop(struct line *line)
{ if (line->irq) {
free_irq_label(free_irq(line->irq, line));
line->irq = 0;
}
#ifdef CONFIG_HTE if (READ_ONCE(line->edflags) & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE)
hte_ts_put(&line->hdesc); #endif
cancel_delayed_work_sync(&line->work);
WRITE_ONCE(line->sw_debounced, 0);
WRITE_ONCE(line->edflags, 0); if (line->desc)
WRITE_ONCE(line->desc->debounce_period_us, 0); /* do not change line->level - see comment in debounced_value() */
}
staticint edge_detector_fifo_init(struct linereq *req)
{ if (kfifo_initialized(&req->events)) return 0;
label = make_irq_label(line->req->label); if (IS_ERR(label)) return PTR_ERR(label);
/* Request a thread to read the events */
ret = request_threaded_irq(irq, edge_irq_handler, edge_irq_thread,
irqflags, label, line); if (ret) {
free_irq_label(label); return ret;
}
if ((active_edflags == edflags) &&
(READ_ONCE(line->desc->debounce_period_us) == debounce_period_us)) return 0;
/* sw debounced and still will be...*/ if (debounce_period_us && READ_ONCE(line->sw_debounced)) {
WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us); /* * ensure event fifo is initialised if edge detection * is now enabled.
*/ if (edflags & GPIO_V2_LINE_EDGE_FLAGS) return edge_detector_fifo_init(line->req);
return 0;
}
/* reconfiguring edge detection or sw debounce being disabled */ if ((line->irq && !READ_ONCE(line->sw_debounced)) ||
(active_edflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE) ||
(!debounce_period_us && READ_ONCE(line->sw_debounced)))
edge_detector_stop(line);
for (i = 0; i < lc->num_attrs; i++) { if ((lc->attrs[i].attr.id == GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES) &&
(lc->attrs[i].mask & mask)) return !!(lc->attrs[i].attr.values & mask);
} return 0;
}
staticint gpio_v2_line_flags_validate(u64 flags)
{ /* Return an error if an unknown flag is set */ if (flags & ~GPIO_V2_LINE_VALID_FLAGS) return -EINVAL;
if (!IS_ENABLED(CONFIG_HTE) &&
(flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE)) return -EOPNOTSUPP;
/* * Do not allow both INPUT and OUTPUT flags to be set as they are * contradictory.
*/ if ((flags & GPIO_V2_LINE_FLAG_INPUT) &&
(flags & GPIO_V2_LINE_FLAG_OUTPUT)) return -EINVAL;
/* Only allow one event clock source */ if (IS_ENABLED(CONFIG_HTE) &&
(flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME) &&
(flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE)) return -EINVAL;
/* * Do not allow OPEN_SOURCE and OPEN_DRAIN flags in a single * request. If the hardware actually supports enabling both at the * same time the electrical result would be disastrous.
*/ if ((flags & GPIO_V2_LINE_FLAG_OPEN_DRAIN) &&
(flags & GPIO_V2_LINE_FLAG_OPEN_SOURCE)) return -EINVAL;
/* Only one bias flag can be set. */ if (((flags & GPIO_V2_LINE_FLAG_BIAS_DISABLED) &&
(flags & (GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN |
GPIO_V2_LINE_FLAG_BIAS_PULL_UP))) ||
((flags & GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN) &&
(flags & GPIO_V2_LINE_FLAG_BIAS_PULL_UP))) return -EINVAL;
/* NOTE: It's ok to read values of output lines. */ if (copy_from_user(&lv, ip, sizeof(lv))) return -EFAULT;
/* * gpiod_get_array_value_complex() requires compacted desc and val * arrays, rather than the sparse ones in lv. * Calculation of num_get and construction of the desc array is * optimized to avoid allocation for the desc array for the common * num_get == 1 case.
*/ /* scan requested lines to calculate the subset to get */ for (num_get = 0, i = 0; i < lr->num_lines; i++) { if (lv.mask & BIT_ULL(i)) {
num_get++; /* capture desc for the num_get == 1 case */
descs = &lr->lines[i].desc;
}
}
if (num_get == 0) return -EINVAL;
if (num_get != 1) { /* build compacted desc array */
descs = kmalloc_array(num_get, sizeof(*descs), GFP_KERNEL); if (!descs) return -ENOMEM; for (didx = 0, i = 0; i < lr->num_lines; i++) { if (lv.mask & BIT_ULL(i)) {
descs[didx] = lr->lines[i].desc;
didx++;
}
}
}
ret = gpiod_get_array_value_complex(false, true, num_get,
descs, NULL, vals);
if (num_get != 1)
kfree(descs); if (ret) return ret;
lv.bits = 0; for (didx = 0, i = 0; i < lr->num_lines; i++) { /* unpack compacted vals for the response */ if (lv.mask & BIT_ULL(i)) { if (lr->lines[i].sw_debounced)
val = debounced_value(&lr->lines[i]); else
val = test_bit(didx, vals); if (val)
lv.bits |= BIT_ULL(i);
didx++;
}
}
if (copy_to_user(ip, &lv, sizeof(lv))) return -EFAULT;
if (copy_from_user(&lv, ip, sizeof(lv))) return -EFAULT;
guard(mutex)(&lr->config_mutex);
/* * gpiod_set_array_value_complex() requires compacted desc and val * arrays, rather than the sparse ones in lv. * Calculation of num_set and construction of the descs and vals arrays * is optimized to minimize scanning the lv->mask, and to avoid * allocation for the desc array for the common num_set == 1 case.
*/
bitmap_zero(vals, GPIO_V2_LINES_MAX); /* scan requested lines to determine the subset to be set */ for (num_set = 0, i = 0; i < lr->num_lines; i++) { if (lv.mask & BIT_ULL(i)) { /* add to compacted values */ if (lv.bits & BIT_ULL(i))
__set_bit(num_set, vals);
num_set++; /* capture desc for the num_set == 1 case */
descs = &lr->lines[i].desc;
}
} if (num_set == 0) return -EINVAL;
if (num_set != 1) { /* build compacted desc array */
descs = kmalloc_array(num_set, sizeof(*descs), GFP_KERNEL); if (!descs) return -ENOMEM; for (didx = 0, i = 0; i < lr->num_lines; i++) { if (lv.mask & BIT_ULL(i)) {
descs[didx] = lr->lines[i].desc;
didx++;
}
}
}
ret = gpiod_set_array_value_complex(false, true, num_set,
descs, NULL, vals);
if (copy_from_user(&lc, ip, sizeof(lc))) return -EFAULT;
ret = gpio_v2_line_config_validate(&lc, lr->num_lines); if (ret) return ret;
guard(mutex)(&lr->config_mutex);
for (i = 0; i < lr->num_lines; i++) {
line = &lr->lines[i];
desc = lr->lines[i].desc;
flags = gpio_v2_line_config_flags(&lc, i); /* * Lines not explicitly reconfigured as input or output * are left unchanged.
*/ if (!(flags & GPIO_V2_LINE_DIRECTION_FLAGS)) continue;
gpio_v2_line_config_flags_to_desc_flags(flags, &desc->flags);
edflags = flags & GPIO_V2_LINE_EDGE_DETECTOR_FLAGS; if (flags & GPIO_V2_LINE_FLAG_OUTPUT) { int val = gpio_v2_line_config_output_value(&lc, i);
edge_detector_stop(line);
ret = gpiod_direction_output_nonotify(desc, val); if (ret) return ret;
} else {
ret = gpiod_direction_input_nonotify(desc); if (ret) return ret;
ret = edge_detector_update(line, &lc, i, edflags); if (ret) return ret;
}
if (!rcu_access_pointer(lr->gdev->chip)) return -ENODEV;
if (count < sizeof(le)) return -EINVAL;
do {
scoped_guard(spinlock, &lr->wait.lock) { if (kfifo_is_empty(&lr->events)) { if (bytes_read) return bytes_read;
if (file->f_flags & O_NONBLOCK) return -EAGAIN;
ret = wait_event_interruptible_locked(lr->wait,
!kfifo_is_empty(&lr->events)); if (ret) return ret;
}
if (kfifo_out(&lr->events, &le, 1) != 1) { /* * This should never happen - we hold the * lock from the moment we learned the fifo * is no longer empty until now.
*/
WARN(1, "failed to read from non-empty kfifo"); return -EIO;
}
}
if (copy_to_user(buf + bytes_read, &le, sizeof(le))) return -EFAULT;
bytes_read += sizeof(le);
} while (count >= bytes_read + sizeof(le));
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 (!mem_is_zero(ulr.padding, sizeof(ulr.padding))) return -EINVAL;
lc = &ulr.config;
ret = gpio_v2_line_config_validate(lc, ulr.num_lines); if (ret) return ret;
lr = kvzalloc(struct_size(lr, lines, ulr.num_lines), GFP_KERNEL); if (!lr) return -ENOMEM;
lr->num_lines = ulr.num_lines;
lr->gdev = gpio_device_get(gdev);
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;
}
}
ret = gpiod_set_transitory(desc, false); if (ret < 0) goto out_free_linereq;
edflags = flags & GPIO_V2_LINE_EDGE_DETECTOR_FLAGS; /* * 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_nonotify(desc, val); if (ret) goto out_free_linereq;
} elseif (flags & GPIO_V2_LINE_FLAG_INPUT) {
ret = gpiod_direction_input_nonotify(desc); if (ret) goto out_free_linereq;
ret = edge_detector_setup(&lr->lines[i], lc, i,
edflags); if (ret) goto out_free_linereq;
}
dev_dbg(&gdev->dev, "registered chardev handle for line %d\n",
offset);
}
lr->device_unregistered_nb.notifier_call = linereq_unregistered_notify;
ret = blocking_notifier_chain_register(&gdev->device_notifier,
&lr->device_unregistered_nb); if (ret) goto out_free_linereq;
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);
/** * struct lineevent_state - contains the state of a userspace event * @gdev: the GPIO device the event pertains to * @label: consumer label used to tag descriptors * @desc: the GPIO descriptor held by this event * @eflags: the event flags this line was requested with * @irq: the interrupt that trigger in response to events on this GPIO * @wait: wait queue that handles blocking reads of events * @device_unregistered_nb: notifier block for receiving gdev unregister events * @events: KFIFO for the GPIO events * @timestamp: cache for the timestamp storing it between hardirq * and IRQ thread, used to bring the timestamp close to the actual * event
*/ struct lineevent_state { struct gpio_device *gdev; constchar *label; struct gpio_desc *desc;
u32 eflags; int irq;
wait_queue_head_t wait; struct notifier_block device_unregistered_nb;
DECLARE_KFIFO(events, struct gpioevent_data, 16);
u64 timestamp;
};
if (!rcu_access_pointer(le->gdev->chip)) return -ENODEV;
/* * When compatible system call is being used the struct gpioevent_data, * in case of at least ia32, has different size due to the alignment * differences. Because we have first member 64 bits followed by one of * 32 bits there is no gap between them. The only difference is the * padding at the end of the data structure. Hence, we calculate the * actual sizeof() and pass this as an argument to copy_to_user() to * drop unneeded bytes from the output.
*/ if (compat_need_64bit_alignment_fixup())
ge_size = sizeof(struct compat_gpioeevent_data); else
ge_size = sizeof(struct gpioevent_data); if (count < ge_size) return -EINVAL;
do {
scoped_guard(spinlock, &le->wait.lock) { if (kfifo_is_empty(&le->events)) { if (bytes_read) return bytes_read;
if (file->f_flags & O_NONBLOCK) return -EAGAIN;
ret = wait_event_interruptible_locked(le->wait,
!kfifo_is_empty(&le->events)); if (ret) return ret;
}
if (kfifo_out(&le->events, &ge, 1) != 1) { /* * This should never happen - we hold the * lock from the moment we learned the fifo * is no longer empty until now.
*/
WARN(1, "failed to read from non-empty kfifo"); return -EIO;
}
}
if (copy_to_user(buf + bytes_read, &ge, ge_size)) return -EFAULT;
bytes_read += ge_size;
} while (count >= bytes_read + ge_size);
return bytes_read;
}
staticvoid lineevent_free(struct lineevent_state *le)
{ if (le->device_unregistered_nb.notifier_call)
blocking_notifier_chain_unregister(&le->gdev->device_notifier,
&le->device_unregistered_nb); if (le->irq)
free_irq_label(free_irq(le->irq, le)); if (le->desc)
gpiod_free(le->desc);
kfree(le->label);
gpio_device_put(le->gdev);
kfree(le);
}
if (!rcu_access_pointer(le->gdev->chip)) return -ENODEV;
/* * We can get the value for an event line but not set it, * because it is input by definition.
*/ if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) { int val;
memset(&ghd, 0, sizeof(ghd));
val = gpiod_get_value_cansleep(le->desc); if (val < 0) return val;
ghd.values[0] = val;
if (copy_to_user(ip, &ghd, sizeof(ghd))) return -EFAULT;
/* Do not leak kernel stack to userspace */
memset(&ge, 0, sizeof(ge));
/* * We may be running from a nested threaded interrupt in which case * we didn't get the timestamp from lineevent_irq_handler().
*/ if (!le->timestamp)
ge.timestamp = ktime_get_ns(); else
ge.timestamp = le->timestamp;
if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE
&& le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) { int level = gpiod_get_value_cansleep(le->desc);
ret = kfifo_in_spinlocked_noirqsave(&le->events, &ge,
1, &le->wait.lock); if (ret)
wake_up_poll(&le->wait, EPOLLIN); else
pr_debug_ratelimited("event FIFO is full - event dropped\n");
desc = gpio_device_get_desc(gdev, offset); if (IS_ERR(desc)) return PTR_ERR(desc);
/* Return an error if a unknown flag is set */ if ((lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) ||
(eflags & ~GPIOEVENT_REQUEST_VALID_FLAGS)) return -EINVAL;
/* This is just wrong: we don't look for events on output lines */ if ((lflags & GPIOHANDLE_REQUEST_OUTPUT) ||
(lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) ||
(lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)) return -EINVAL;
/* Only one bias flag can be set. */ if (((lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE) &&
(lflags & (GPIOHANDLE_REQUEST_BIAS_PULL_DOWN |
GPIOHANDLE_REQUEST_BIAS_PULL_UP))) ||
((lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) &&
(lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP))) return -EINVAL;
le = kzalloc(sizeof(*le), GFP_KERNEL); if (!le) return -ENOMEM;
le->gdev = gpio_device_get(gdev);
if (eventreq.consumer_label[0] != '\0') { /* label is only initialized if consumer_label is set */
le->label = kstrndup(eventreq.consumer_label, sizeof(eventreq.consumer_label) - 1,
GFP_KERNEL); if (!le->label) {
ret = -ENOMEM; goto out_free_le;
}
}
ret = gpiod_request_user(desc, le->label); if (ret) goto out_free_le;
le->desc = desc;
le->eflags = eflags;
le->device_unregistered_nb.notifier_call = lineevent_unregistered_notify;
ret = blocking_notifier_chain_register(&gdev->device_notifier,
&le->device_unregistered_nb); if (ret) goto out_free_le;
label = make_irq_label(le->label); if (IS_ERR(label)) {
ret = PTR_ERR(label); goto out_free_le;
}
/* Request a thread to read the events */
ret = request_threaded_irq(irq,
lineevent_irq_handler,
lineevent_irq_thread,
irqflags,
label,
le); if (ret) {
free_irq_label(label); goto out_free_le;
}
le->irq = irq;
fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); if (fd < 0) {
ret = fd; goto out_free_le;
}
file = anon_inode_getfile("gpio-event",
&lineevent_fileops,
le,
O_RDONLY | O_CLOEXEC); if (IS_ERR(file)) {
ret = PTR_ERR(file); goto out_put_unused_fd;
}
eventreq.fd = fd; if (copy_to_user(ip, &eventreq, sizeof(eventreq))) { /* * 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;
}
/* * Userspace only need know that the kernel is using this GPIO so it * can't use it. * The calculation of the used flag is slightly racy, as it may read * desc, gc and pinctrl state without a lock covering all three at * once. Worst case if the line is in transition and the calculation * is inconsistent then it looks to the user like they performed the * read on the other side of the transition - but that can always * happen. * The definitive test that a line is available to userspace is to * request it.
*/ if (test_bit(FLAG_REQUESTED, &dflags) ||
test_bit(FLAG_IS_HOGGED, &dflags) ||
test_bit(FLAG_EXPORT, &dflags) ||
test_bit(FLAG_SYSFS, &dflags) ||
!gpiochip_line_is_valid(guard.gc, info->offset)) {
info->flags |= GPIO_V2_LINE_FLAG_USED;
} elseif (!atomic) { if (!pinctrl_gpio_can_use_line(guard.gc, info->offset))
info->flags |= GPIO_V2_LINE_FLAG_USED;
}
if (test_bit(FLAG_IS_OUT, &dflags))
info->flags |= GPIO_V2_LINE_FLAG_OUTPUT; else
info->flags |= GPIO_V2_LINE_FLAG_INPUT;
if (test_bit(FLAG_ACTIVE_LOW, &dflags))
info->flags |= GPIO_V2_LINE_FLAG_ACTIVE_LOW;
if (test_bit(FLAG_OPEN_DRAIN, &dflags))
info->flags |= GPIO_V2_LINE_FLAG_OPEN_DRAIN; if (test_bit(FLAG_OPEN_SOURCE, &dflags))
info->flags |= GPIO_V2_LINE_FLAG_OPEN_SOURCE;
if (test_bit(FLAG_BIAS_DISABLE, &dflags))
info->flags |= GPIO_V2_LINE_FLAG_BIAS_DISABLED; if (test_bit(FLAG_PULL_DOWN, &dflags))
info->flags |= GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN; if (test_bit(FLAG_PULL_UP, &dflags))
info->flags |= GPIO_V2_LINE_FLAG_BIAS_PULL_UP;
if (test_bit(FLAG_EDGE_RISING, &dflags))
info->flags |= GPIO_V2_LINE_FLAG_EDGE_RISING; if (test_bit(FLAG_EDGE_FALLING, &dflags))
info->flags |= GPIO_V2_LINE_FLAG_EDGE_FALLING;
#ifdef CONFIG_GPIO_CDEV_V1 /* * returns 0 if the versions match, else the previously selected ABI version
*/ staticint lineinfo_ensure_abi_version(struct gpio_chardev_data *cdata, unsignedint version)
{ int abiv = atomic_cmpxchg(&cdata->watch_abi_version, 0, version);
if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) return -EFAULT;
/* this doubles as a range check on line_offset */
desc = gpio_device_get_desc(cdev->gdev, lineinfo.line_offset); if (IS_ERR(desc)) return PTR_ERR(desc);
if (watch) { if (lineinfo_ensure_abi_version(cdev, 1)) return -EPERM;
if (test_and_set_bit(lineinfo.line_offset, cdev->watched_lines)) return -EBUSY;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.