gpiolib: cdev: document that line eflags are shared
The line.eflags field is shared so document this fact and highlight it throughout using READ_ONCE() and WRITE_ONCE() accessors. Also use a local copy of the eflags in edge_irq_thread() to ensure consistent control flow even if eflags changes. This is only a defensive measure as edge_irq_thread() is currently disabled when the eflags are changed. Signed-off-by: Kent Gibson <warthog618@gmail.com> Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
This commit is contained in:
parent
7b58696d9a
commit
3ffb7c45d1
@ -428,6 +428,12 @@ struct line {
|
|||||||
*/
|
*/
|
||||||
struct linereq *req;
|
struct linereq *req;
|
||||||
unsigned int irq;
|
unsigned int irq;
|
||||||
|
/*
|
||||||
|
* eflags is set by edge_detector_setup(), edge_detector_stop() and
|
||||||
|
* edge_detector_update(), which are themselves mutually exclusive,
|
||||||
|
* and is accessed by edge_irq_thread() and debounce_work_func(),
|
||||||
|
* which can both live with a slightly stale value.
|
||||||
|
*/
|
||||||
u64 eflags;
|
u64 eflags;
|
||||||
/*
|
/*
|
||||||
* timestamp_ns and req_seqno are accessed only by
|
* timestamp_ns and req_seqno are accessed only by
|
||||||
@ -534,6 +540,7 @@ static irqreturn_t edge_irq_thread(int irq, void *p)
|
|||||||
struct line *line = p;
|
struct line *line = p;
|
||||||
struct linereq *lr = line->req;
|
struct linereq *lr = line->req;
|
||||||
struct gpio_v2_line_event le;
|
struct gpio_v2_line_event le;
|
||||||
|
u64 eflags;
|
||||||
|
|
||||||
/* Do not leak kernel stack to userspace */
|
/* Do not leak kernel stack to userspace */
|
||||||
memset(&le, 0, sizeof(le));
|
memset(&le, 0, sizeof(le));
|
||||||
@ -552,8 +559,9 @@ static irqreturn_t edge_irq_thread(int irq, void *p)
|
|||||||
}
|
}
|
||||||
line->timestamp_ns = 0;
|
line->timestamp_ns = 0;
|
||||||
|
|
||||||
if (line->eflags == (GPIO_V2_LINE_FLAG_EDGE_RISING |
|
eflags = READ_ONCE(line->eflags);
|
||||||
GPIO_V2_LINE_FLAG_EDGE_FALLING)) {
|
if (eflags == (GPIO_V2_LINE_FLAG_EDGE_RISING |
|
||||||
|
GPIO_V2_LINE_FLAG_EDGE_FALLING)) {
|
||||||
int level = gpiod_get_value_cansleep(line->desc);
|
int level = gpiod_get_value_cansleep(line->desc);
|
||||||
|
|
||||||
if (level)
|
if (level)
|
||||||
@ -562,10 +570,10 @@ static irqreturn_t edge_irq_thread(int irq, void *p)
|
|||||||
else
|
else
|
||||||
/* Emit high-to-low event */
|
/* Emit high-to-low event */
|
||||||
le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE;
|
le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE;
|
||||||
} else if (line->eflags == GPIO_V2_LINE_FLAG_EDGE_RISING) {
|
} else if (eflags == GPIO_V2_LINE_FLAG_EDGE_RISING) {
|
||||||
/* Emit low-to-high event */
|
/* Emit low-to-high event */
|
||||||
le.id = GPIO_V2_LINE_EVENT_RISING_EDGE;
|
le.id = GPIO_V2_LINE_EVENT_RISING_EDGE;
|
||||||
} else if (line->eflags == GPIO_V2_LINE_FLAG_EDGE_FALLING) {
|
} else if (eflags == GPIO_V2_LINE_FLAG_EDGE_FALLING) {
|
||||||
/* Emit high-to-low event */
|
/* Emit high-to-low event */
|
||||||
le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE;
|
le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE;
|
||||||
} else {
|
} else {
|
||||||
@ -634,6 +642,7 @@ static void debounce_work_func(struct work_struct *work)
|
|||||||
struct line *line = container_of(work, struct line, work.work);
|
struct line *line = container_of(work, struct line, work.work);
|
||||||
struct linereq *lr;
|
struct linereq *lr;
|
||||||
int level;
|
int level;
|
||||||
|
u64 eflags;
|
||||||
|
|
||||||
level = gpiod_get_raw_value_cansleep(line->desc);
|
level = gpiod_get_raw_value_cansleep(line->desc);
|
||||||
if (level < 0) {
|
if (level < 0) {
|
||||||
@ -647,7 +656,8 @@ static void debounce_work_func(struct work_struct *work)
|
|||||||
WRITE_ONCE(line->level, level);
|
WRITE_ONCE(line->level, level);
|
||||||
|
|
||||||
/* -- edge detection -- */
|
/* -- edge detection -- */
|
||||||
if (!line->eflags)
|
eflags = READ_ONCE(line->eflags);
|
||||||
|
if (!eflags)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* switch from physical level to logical - if they differ */
|
/* switch from physical level to logical - if they differ */
|
||||||
@ -655,8 +665,8 @@ static void debounce_work_func(struct work_struct *work)
|
|||||||
level = !level;
|
level = !level;
|
||||||
|
|
||||||
/* ignore edges that are not being monitored */
|
/* ignore edges that are not being monitored */
|
||||||
if (((line->eflags == GPIO_V2_LINE_FLAG_EDGE_RISING) && !level) ||
|
if (((eflags == GPIO_V2_LINE_FLAG_EDGE_RISING) && !level) ||
|
||||||
((line->eflags == GPIO_V2_LINE_FLAG_EDGE_FALLING) && level))
|
((eflags == GPIO_V2_LINE_FLAG_EDGE_FALLING) && level))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Do not leak kernel stack to userspace */
|
/* Do not leak kernel stack to userspace */
|
||||||
@ -755,7 +765,7 @@ static void edge_detector_stop(struct line *line)
|
|||||||
|
|
||||||
cancel_delayed_work_sync(&line->work);
|
cancel_delayed_work_sync(&line->work);
|
||||||
WRITE_ONCE(line->sw_debounced, 0);
|
WRITE_ONCE(line->sw_debounced, 0);
|
||||||
line->eflags = 0;
|
WRITE_ONCE(line->eflags, 0);
|
||||||
/* do not change line->level - see comment in debounced_value() */
|
/* do not change line->level - see comment in debounced_value() */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -774,7 +784,7 @@ static int edge_detector_setup(struct line *line,
|
|||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
line->eflags = eflags;
|
WRITE_ONCE(line->eflags, eflags);
|
||||||
if (gpio_v2_line_config_debounced(lc, line_idx)) {
|
if (gpio_v2_line_config_debounced(lc, line_idx)) {
|
||||||
debounce_period_us = gpio_v2_line_config_debounce_period(lc, line_idx);
|
debounce_period_us = gpio_v2_line_config_debounce_period(lc, line_idx);
|
||||||
ret = debounce_setup(line, debounce_period_us);
|
ret = debounce_setup(line, debounce_period_us);
|
||||||
@ -817,13 +827,13 @@ static int edge_detector_update(struct line *line,
|
|||||||
unsigned int debounce_period_us =
|
unsigned int debounce_period_us =
|
||||||
gpio_v2_line_config_debounce_period(lc, line_idx);
|
gpio_v2_line_config_debounce_period(lc, line_idx);
|
||||||
|
|
||||||
if ((line->eflags == eflags) && !polarity_change &&
|
if ((READ_ONCE(line->eflags) == eflags) && !polarity_change &&
|
||||||
(READ_ONCE(line->desc->debounce_period_us) == debounce_period_us))
|
(READ_ONCE(line->desc->debounce_period_us) == debounce_period_us))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* sw debounced and still will be...*/
|
/* sw debounced and still will be...*/
|
||||||
if (debounce_period_us && READ_ONCE(line->sw_debounced)) {
|
if (debounce_period_us && READ_ONCE(line->sw_debounced)) {
|
||||||
line->eflags = eflags;
|
WRITE_ONCE(line->eflags, eflags);
|
||||||
WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us);
|
WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user