-
Notifications
You must be signed in to change notification settings - Fork 8.2k
[RFC] Device: generalizing and improving control features (sync, lock and timeout) #24511
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
7e1c9f9
0f43d45
68b863d
dc10b68
21bc832
31e7aa7
bd85df6
fcd0b3c
9818b10
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| # Device configuration options | ||
|
|
||
| # Copyright (c) 2020 Intel Corporation | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| menu "Device driver generic options" | ||
|
|
||
| config DEVICE_CONCURRENT_ACCESS | ||
| bool "Enable concurrent access" | ||
| default y | ||
| help | ||
| If disabled, devices will not be protected over concurrent access. | ||
| This can be a valid possibility if you know your devices will not be | ||
| accessed by 2+ threads, and thus will reduce the memory footprint. | ||
|
|
||
| config DEVICE_CALL_TIMEOUT | ||
| bool "Have a timeout on device calls" | ||
| help | ||
| If enabled, this will insert a timeout on device calls. Either | ||
| a generic one via DEVICE_CALL_TIMEOUT_VALUE or a dedicated one. | ||
| This is disabled by defautl to follow the legacy behavior. | ||
|
|
||
| config DEVICE_NO_LOCK_TIMEOUT | ||
| bool "No locking timeout" | ||
| depends on DEVICE_CONCURRENT_ACCESS && DEVICE_CALL_TIMEOUT | ||
| default y | ||
| help | ||
| If enabled, no timeout will ever be applied on locking a device. | ||
| This is the default to follow the legacy behavior. | ||
|
|
||
| config DEVICE_CALL_TIMEOUT_VALUE | ||
| int "The default device call timeout" | ||
| default -1 | ||
| help | ||
| This will set the generic timeout for device locking and syncing. | ||
| By default -1 is set (K_FOREVER), any positive value is valid. | ||
| Make sure to select a suitable value for all the devices. | ||
|
|
||
| endmenu | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,6 +23,8 @@ extern const struct init_entry __init_SMP_start[]; | |
| extern struct device __device_start[]; | ||
| extern struct device __device_end[]; | ||
|
|
||
| extern struct device_context __device_context_start[]; | ||
|
||
|
|
||
| #ifdef CONFIG_DEVICE_POWER_MANAGEMENT | ||
| extern u32_t __device_busy_start[]; | ||
| extern u32_t __device_busy_end[]; | ||
|
|
@@ -177,3 +179,116 @@ void device_busy_clear(struct device *busy_dev) | |
| ARG_UNUSED(busy_dev); | ||
| #endif | ||
| } | ||
|
|
||
| #ifdef CONFIG_DEVICE_CALL_TIMEOUT | ||
| static void device_call_set_timeout(struct device *dev, k_timeout_t timeout) | ||
| { | ||
| struct device_context *dc = | ||
| (struct device_context *)__device_context_start + | ||
| (dev - __device_start); | ||
|
|
||
| dc->timeout = timeout; | ||
| } | ||
|
|
||
| static void device_call_cancel(struct device *dev) | ||
| { | ||
| if (dev->cancel != NULL) { | ||
| dev->cancel(dev); | ||
| } | ||
| } | ||
| #else | ||
| #define device_call_set_timeout(...) | ||
| #define device_call_cancel(...) | ||
| #endif /* CONFIG_DEVICE_CALL_TIMEOUT */ | ||
|
|
||
| int device_lock_timeout(struct device *dev, k_timeout_t timeout) | ||
| { | ||
| #ifdef CONFIG_DEVICE_CONCURRENT_ACCESS | ||
| struct device_context *dc = | ||
|
||
| (struct device_context *)__device_context_start + | ||
| (dev - __device_start); | ||
| k_timeout_t lock_timeout = timeout; | ||
|
|
||
| if (k_is_in_isr()) { | ||
| return 0; | ||
| } | ||
|
|
||
|
|
||
| #ifdef CONFIG_DEVICE_CALL_TIMEOUT | ||
| #ifdef CONFIG_DEVICE_NO_LOCK_TIMEOUT | ||
| lock_timeout = K_FOREVER; | ||
| #else | ||
| u32_t time_spent; | ||
|
|
||
| if (!K_TIMEOUT_EQ(timeout, K_FOREVER)) { | ||
| time_spent = k_uptime_get_32(); | ||
| } | ||
| #endif /* CONFIG_DEVICE_NO_LOCK_TIMEOUT */ | ||
| #endif /* CONFIG_DEVICE_CALL_TIMEOUT */ | ||
|
|
||
| if (k_sem_take(&dc->lock, lock_timeout) != 0) { | ||
| return -EAGAIN; | ||
| } | ||
|
|
||
| #if defined(CONFIG_DEVICE_CALL_TIMEOUT) && \ | ||
| !defined(CONFIG_DEVICE_NO_LOCK_TIMEOUT) | ||
|
|
||
| if (!K_TIMEOUT_EQ(timeout, K_FOREVER)) { | ||
| if (k_uptime_get_32() < time_spent) { | ||
| time_spent = k_uptime_get_32() + | ||
| (UINT32_MAX - time_spent); | ||
| } else { | ||
| time_spent = k_uptime_get_32() + time_spent; | ||
| } | ||
|
|
||
| timeout = K_MSEC(k_ticks_to_ms_floor32(tiemout) - time_spent); | ||
| } | ||
| #endif /* CONFIG_DEVICE_CALL_TIMEOUT */ | ||
|
|
||
| #endif /* CONFIG_DEVICE_CONCURRENT_ACCESS */ | ||
|
|
||
| device_call_set_timeout(dev, timeout); | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| void device_call_complete(struct device *dev, int status) | ||
| { | ||
| struct device_context *dc = | ||
| (struct device_context *)__device_context_start + | ||
| (dev - __device_start); | ||
|
|
||
| dc->call_status = status; | ||
|
|
||
| if (!k_is_in_isr()) { | ||
| k_sem_give(&dc->sync); | ||
| } | ||
| } | ||
|
|
||
| int device_release(struct device *dev) | ||
| { | ||
| struct device_context *dc = | ||
| (struct device_context *)__device_context_start + | ||
| (dev - __device_start); | ||
| u32_t status; | ||
|
|
||
| if (!k_is_in_isr()) { | ||
| #ifdef CONFIG_DEVICE_CALL_TIMEOUT | ||
| if (k_sem_take(&dc->sync, dc->timeout) == -EAGAIN) { | ||
| device_call_cancel(dev); | ||
| status = -ECANCELED; | ||
| } | ||
| #else | ||
| k_sem_take(&dc->sync, K_FOREVER); | ||
| #endif /* CONFIG_DEVICE_CALL_TIMEOUT */ | ||
| } | ||
|
|
||
| status = dc->call_status; | ||
|
|
||
| #ifdef CONFIG_DEVICE_CONCURRENT_ACCESS | ||
| if (!k_is_in_isr()) { | ||
| k_sem_give(&dc->lock); | ||
| } | ||
| #endif | ||
| return status; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| cmake_minimum_required(VERSION 3.13.1) | ||
| find_package(Zephyr HINTS $ENV{ZEPHYR_BASE}) | ||
| project(device_control) | ||
|
|
||
| FILE(GLOB app_sources src/*.c) | ||
| target_sources(app PRIVATE ${app_sources}) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| CONFIG_ZTEST=y |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| /* | ||
| * Copyright (c) 2020 Intel Corporation | ||
| * | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| #include <device.h> | ||
|
|
||
| #define FAKE_DRV_NAME "FAKE_DRV" | ||
|
|
||
| struct fake_api { | ||
| int (*sync_int_call)(struct device *dev); | ||
| int (*sync_poll_call)(struct device *dev); | ||
| int (*lock_int_call)(struct device *dev, int *val); | ||
| int (*lock_poll_call)(struct device *dev, int *val); | ||
| int (*no_to_call)(struct device *dev, int *val); | ||
|
|
||
| }; | ||
|
|
||
| static inline int fake_sync_int_call(struct device *dev) | ||
| { | ||
| const struct fake_api *api = (const struct fake_api *)dev->driver_api; | ||
|
|
||
| return api->sync_int_call(dev); | ||
| } | ||
|
|
||
| static inline int fake_sync_poll_call(struct device *dev) | ||
| { | ||
| const struct fake_api *api = (const struct fake_api *)dev->driver_api; | ||
|
|
||
| return api->sync_poll_call(dev); | ||
| } | ||
|
|
||
| static inline int fake_lock_int_call(struct device *dev, int *val) | ||
| { | ||
| const struct fake_api *api = (const struct fake_api *)dev->driver_api; | ||
|
|
||
| return api->lock_int_call(dev, val); | ||
| } | ||
|
|
||
| static inline int fake_lock_poll_call(struct device *dev, int *val) | ||
| { | ||
| const struct fake_api *api = (const struct fake_api *)dev->driver_api; | ||
|
|
||
| return api->lock_poll_call(dev, val); | ||
| } | ||
|
|
||
| static inline int fake_no_timeout_call(struct device *dev, int *val) | ||
| { | ||
| const struct fake_api *api = (const struct fake_api *)dev->driver_api; | ||
|
|
||
| return api->no_to_call(dev, val); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this value be device specific? Perhaps we could have it in DEVICE_DEFINE(), then we could avoid global value that might not be suitable for all devices.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, like the presence of a sync or lock actually. I did not push any change of the macros yet (or then there would be a big patch changing the macros everywhere it's being called), so we can settle on what would be exposed - or not - as parameters there.