diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index d02327b6eb..4f795a97b2 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -30,4 +30,11 @@ config THERMAL_CDEV_CPUFREQ ---help--- Enable thermal cpufreq cooling device. +config THERMAL_PROCFS + bool "Thermal PROCFS support" + default n + depends on FS_PROCFS + ---help--- + Enable thermal procfs. + endif # THERMAL diff --git a/drivers/thermal/Make.defs b/drivers/thermal/Make.defs index fe2e806860..9e95fe4cfe 100644 --- a/drivers/thermal/Make.defs +++ b/drivers/thermal/Make.defs @@ -32,6 +32,10 @@ ifeq ($(CONFIG_THERMAL_CDEV_CPUFREQ),y) CSRCS += thermal_cpufreq_cooling.c endif +ifeq ($(CONFIG_THERMAL_PROCFS),y) +CSRCS += thermal_procfs.c +endif + DEPPATH += --dep-path thermal VPATH += thermal diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index ec8c5a80ce..741bd9a84a 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -741,6 +741,10 @@ thermal_zone_device_register(FAR const char *name, device_bind(zdev, cdev); } +#ifdef CONFIG_THERMAL_PROCFS + thermal_zone_procfs_register(zdev); +#endif + nxmutex_unlock(&g_thermal_lock); thinfo("Registered zone device %s\n", zdev->name); @@ -780,6 +784,11 @@ void thermal_zone_device_unregister(FAR struct thermal_zone_device_s *zdev) } list_delete(&zdev->node); + +#ifdef CONFIG_THERMAL_PROCFS + thermal_zone_procfs_unregister(zdev); +#endif + zone_set_governor(zdev, NULL); kmm_free(zdev); nxmutex_unlock(&g_thermal_lock); diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 46537dff7d..8b7bcc37f5 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -82,4 +82,11 @@ int thermal_zone_get_trip_hyst(FAR struct thermal_zone_device_s *zdev, int thermal_register_step_wise_governor(void); +/* ProcFS */ + +#ifdef CONFIG_THERMAL_PROCFS +int thermal_zone_procfs_register(FAR struct thermal_zone_device_s *zdev); +void thermal_zone_procfs_unregister(FAR struct thermal_zone_device_s *zdev); +#endif + #endif /* __DRIVERS_THERMAL_THERMAL_CORE_H */ diff --git a/drivers/thermal/thermal_procfs.c b/drivers/thermal/thermal_procfs.c new file mode 100644 index 0000000000..67b018584d --- /dev/null +++ b/drivers/thermal/thermal_procfs.c @@ -0,0 +1,343 @@ +/**************************************************************************** + * drivers/thermal/thermal_procfs.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include +#include + +#include "thermal_core.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct thermal_procfs_s +{ + struct procfs_file_s base; + FAR struct thermal_zone_device_s *zdev; + struct list_node node; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Procfs operations */ + +static int thermal_procfs_open (FAR struct file *filep, + FAR const char *relpath, + int oflags, mode_t mode); +static int thermal_procfs_close (FAR struct file *filep); +static ssize_t thermal_procfs_read (FAR struct file *filep, + FAR char *buffer, + size_t buflen); +static ssize_t thermal_procfs_write (FAR struct file *filep, + FAR const char *buffer, + size_t buflen); +static int thermal_procfs_dup (FAR const struct file *oldp, + FAR struct file *newp); +static int thermal_procfs_opendir (FAR const char *relpath, + FAR struct fs_dirent_s **dir); +static int thermal_procfs_closedir (FAR struct fs_dirent_s *dir); +static int thermal_procfs_readdir (FAR struct fs_dirent_s *dir, + FAR struct dirent *entry); +static int thermal_procfs_rewinddir(FAR struct fs_dirent_s *dir); +static int thermal_procfs_stat (FAR const char *relpath, + FAR struct stat *buf); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct list_node +g_thermal_procfs_list = LIST_INITIAL_VALUE(g_thermal_procfs_list); +static mutex_t g_thermal_procfs_lock = NXMUTEX_INITIALIZER; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const struct procfs_operations g_thermal_operations = +{ + .open = thermal_procfs_open, + .close = thermal_procfs_close, + .read = thermal_procfs_read, + .write = thermal_procfs_write, + .poll = NULL, + .dup = thermal_procfs_dup, + .opendir = thermal_procfs_opendir, + .closedir = thermal_procfs_closedir, + .readdir = thermal_procfs_readdir, + .rewinddir = thermal_procfs_rewinddir, + .stat = thermal_procfs_stat, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int thermal_procfs_open(FAR struct file *filep, + FAR const char *relpath, + int oflags, mode_t mode) +{ + FAR struct thermal_procfs_s *child; + + relpath += strlen("thermal/"); + nxmutex_lock(&g_thermal_procfs_lock); + + list_for_every_entry(&g_thermal_procfs_list, child, + struct thermal_procfs_s, node) + { + if (!strcmp(child->zdev->name, relpath)) + { + filep->f_priv = child; + nxmutex_unlock(&g_thermal_procfs_lock); + return OK; + } + } + + nxmutex_unlock(&g_thermal_procfs_lock); + return -ENOENT; +} + +static int thermal_procfs_close(FAR struct file *filep) +{ + filep->f_priv = NULL; + return OK; +} + +static ssize_t thermal_procfs_read(FAR struct file *filep, + FAR char *buffer, + size_t buflen) +{ + FAR struct thermal_procfs_s *p = filep->f_priv; + FAR struct thermal_instance_s *ins; + off_t offset = filep->f_pos; + unsigned int current; + + list_for_every_entry(&p->zdev->instance_list, ins, + struct thermal_instance_s, zdev_node) + { + ins->cdev->ops->get_state(ins->cdev, ¤t); + procfs_sprintf(buffer, buflen, &offset, + "z:%s t:%d t:%d h:%u l:%u c:%s s:%u|%u\n", + ins->zdev->name, + ins->zdev->temperature, + ins->trip, + ins->upper, + ins->lower, + ins->cdev->name, + current, + ins->target); + } + + if (offset < 0) + { + offset = -offset; + } + else + { + offset = 0; + } + + filep->f_pos += offset; + return offset; +} + +static ssize_t thermal_procfs_write(FAR struct file *filep, + FAR const char *buffer, + size_t buflen) +{ + FAR struct thermal_procfs_s *p = filep->f_priv; + + thermal_zone_enable(p->zdev, atoi(buffer) ? true : false); + return buflen; +} + +static int thermal_procfs_dup(FAR const struct file *oldp, + FAR struct file *newp) +{ + newp->f_priv = oldp->f_priv; + return OK; +} + +static int thermal_procfs_opendir(FAR const char *relpath, + FAR struct fs_dirent_s **dir) +{ + FAR struct procfs_dir_priv_s *level1; + + level1 = kmm_zalloc(sizeof(struct procfs_dir_priv_s)); + if (level1 == NULL) + { + *dir = NULL; + return -ENOMEM; + } + + level1->level = 1; + + nxmutex_lock(&g_thermal_procfs_lock); + level1->nentries = list_length(&g_thermal_procfs_list); + nxmutex_unlock(&g_thermal_procfs_lock); + + *dir = (FAR struct fs_dirent_s *)level1; + return OK; +} + +static int thermal_procfs_closedir(FAR struct fs_dirent_s *dir) +{ + kmm_free(dir); + return OK; +} + +static int thermal_procfs_readdir(FAR struct fs_dirent_s *dir, + FAR struct dirent *entry) +{ + FAR struct procfs_dir_priv_s *level1; + FAR struct thermal_procfs_s *child; + int index = 0; + + DEBUGASSERT(dir); + level1 = (FAR struct procfs_dir_priv_s *)dir; + + if (level1->index >= level1->nentries) + { + /* We signal the end of the directory by returning the special + * error -ENOENT + */ + + return -ENOENT; + } + + nxmutex_lock(&g_thermal_procfs_lock); + + list_for_every_entry(&g_thermal_procfs_list, child, + struct thermal_procfs_s, node) + { + if (index == level1->index) + { + entry->d_type = DTYPE_FILE; + + strlcpy(entry->d_name, child->zdev->name, NAME_MAX); + level1->index++; + nxmutex_unlock(&g_thermal_procfs_lock); + return OK; + } + + index++; + } + + nxmutex_unlock(&g_thermal_procfs_lock); + return -ENOENT; +} + +static int thermal_procfs_rewinddir(FAR struct fs_dirent_s *dir) +{ + FAR struct procfs_dir_priv_s *level1; + + DEBUGASSERT(dir); + level1 = (FAR struct procfs_dir_priv_s *)dir; + level1->index = 0; + return OK; +} + +static int thermal_procfs_stat(FAR const char *relpath, + FAR struct stat *buf) +{ + FAR struct thermal_procfs_s *child; + + memset(buf, 0, sizeof(struct stat)); + + if (strcmp(relpath, "thermal") == 0 || strcmp(relpath, "thermal/") == 0) + { + buf->st_mode = S_IFDIR | S_IROTH | S_IRGRP | S_IRUSR; + return OK; + } + else + { + relpath += strlen("thermal/"); + + nxmutex_lock(&g_thermal_procfs_lock); + + list_for_every_entry(&g_thermal_procfs_list, child, + struct thermal_procfs_s, node) + { + if (!strcmp(child->zdev->name, relpath)) + { + buf->st_mode = S_IFREG | S_IROTH | S_IRGRP | S_IRUSR; + nxmutex_unlock(&g_thermal_procfs_lock); + return OK; + } + } + + nxmutex_unlock(&g_thermal_procfs_lock); + } + + return -ENOENT; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int thermal_zone_procfs_register(FAR struct thermal_zone_device_s *zdev) +{ + FAR struct thermal_procfs_s *p; + + p = kmm_zalloc(sizeof(struct thermal_procfs_s)); + if (!p) + { + nxmutex_unlock(&g_thermal_procfs_lock); + return -ENOMEM; + } + + p->zdev = zdev; + + nxmutex_lock(&g_thermal_procfs_lock); + list_add_tail(&g_thermal_procfs_list, &p->node); + nxmutex_unlock(&g_thermal_procfs_lock); + return OK; +} + +void thermal_zone_procfs_unregister(FAR struct thermal_zone_device_s *zdev) +{ + FAR struct thermal_procfs_s *p; + + nxmutex_lock(&g_thermal_procfs_lock); + + list_for_every_entry(&g_thermal_procfs_list, p, + FAR struct thermal_procfs_s, node) + { + if (p->zdev == zdev) + { + list_delete(&p->node); + kmm_free(p); + break; + } + } + + nxmutex_unlock(&g_thermal_procfs_lock); +} diff --git a/fs/procfs/fs_procfs.c b/fs/procfs/fs_procfs.c index 75a46b0a96..c66fed8a27 100644 --- a/fs/procfs/fs_procfs.c +++ b/fs/procfs/fs_procfs.c @@ -67,6 +67,7 @@ extern const struct procfs_operations g_module_operations; extern const struct procfs_operations g_pm_operations; extern const struct procfs_operations g_proc_operations; extern const struct procfs_operations g_tcbinfo_operations; +extern const struct procfs_operations g_thermal_operations; extern const struct procfs_operations g_uptime_operations; extern const struct procfs_operations g_version_operations; extern const struct procfs_operations g_pressure_operations; @@ -192,6 +193,11 @@ static const struct procfs_entry_s g_procfs_entries[] = { "tcbinfo", &g_tcbinfo_operations, PROCFS_FILE_TYPE }, #endif +#ifdef CONFIG_THERMAL_PROCFS + { "thermal", &g_thermal_operations, PROCFS_DIR_TYPE }, + { "thermal/**", &g_thermal_operations, PROCFS_UNKOWN_TYPE }, +#endif + #ifndef CONFIG_FS_PROCFS_EXCLUDE_UPTIME { "uptime", &g_uptime_operations, PROCFS_FILE_TYPE }, #endif