* drivers/devfreq/devfreq_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 <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#include <debug.h>
#include <errno.h>
#include <execinfo.h>
#include <stdio.h>
#include <string.h>
#include <nuttx/devfreq.h>
#include <nuttx/fs/fs.h>
#include <nuttx/fs/procfs.h>
#include <nuttx/kmalloc.h>
* Pre-processor Definitions
****************************************************************************/
* to handle the longest line generated by this logic.
*/
#define DEVFREQ_LINELEN 256
* Private Types
****************************************************************************/
struct devfreq_procfs_s
{
struct procfs_file_s base;
FAR struct devfreq_s *devfreq;
};
* Private Function Prototypes
****************************************************************************/
static int devfreq_open(FAR struct file *filep,
FAR const char *relpath,
int oflags, mode_t mode);
static int devfreq_close(FAR struct file *filep);
static ssize_t devfreq_read(FAR struct file *filep,
FAR char *buffer,
size_t buflen);
static ssize_t devfreq_write(FAR struct file *filep,
FAR const char *buffer,
size_t buflen);
static int devfreq_opendir(FAR const char *relpath,
FAR struct fs_dirent_s **dir);
static int devfreq_readdir(FAR struct fs_dirent_s *dir,
FAR struct dirent *entry);
static int devfreq_closedir(FAR struct fs_dirent_s *dir);
static int devfreq_rewinddir(FAR struct fs_dirent_s *dir);
static int devfreq_stat(FAR const char *relpath, FAR struct stat *buf);
* Private Data
****************************************************************************/
static const struct procfs_operations g_devfreq_operations =
{
.open = devfreq_open,
.close = devfreq_close,
.read = devfreq_read,
.write = devfreq_write,
.poll = NULL,
.opendir = devfreq_opendir,
.closedir = devfreq_closedir,
.readdir = devfreq_readdir,
.rewinddir = devfreq_rewinddir,
.stat = devfreq_stat,
};
static const struct procfs_entry_s g_devfreq_procfs_root =
{
"devfreq", &g_devfreq_operations, PROCFS_DIR_TYPE
};
static const struct procfs_entry_s g_devfreq_procfs_entry =
{
"devfreq/**", &g_devfreq_operations, PROCFS_UNKOWN_TYPE
};
* Private Functions
****************************************************************************/
* Name: devfreq_open
****************************************************************************/
static int devfreq_open(FAR struct file *filep, FAR const char *relpath,
int oflags, mode_t mode)
{
FAR struct devfreq_s *devfreq;
FAR struct devfreq_procfs_s *devfreq_procfs;
relpath += strlen("devfreq/");
devfreq = devfreq_find_by_name(relpath);
if (!devfreq)
{
return -ENOENT;
}
devfreq_procfs = kmm_zalloc(sizeof(struct devfreq_procfs_s));
if (!devfreq_procfs)
{
return -ENOMEM;
}
devfreq_procfs->devfreq = devfreq;
filep->f_priv = devfreq_procfs;
return 0;
}
* Name: devfreq_close
****************************************************************************/
static int devfreq_close(FAR struct file *filep)
{
DEBUGASSERT(filep->f_priv);
kmm_free(filep->f_priv);
filep->f_priv = NULL;
return 0;
}
* Name: devfreq_read
****************************************************************************/
static ssize_t devfreq_read(FAR struct file *filep,
FAR char *buffer, size_t buflen)
{
FAR struct devfreq_procfs_s *devfreq_procfs = filep->f_priv;
FAR struct devfreq_s *devfreq = devfreq_procfs->devfreq;
#ifdef CONFIG_DEVFREQ_PROCFS_QOS
FAR struct qos_request_s *qos;
void **stack;
int depth;
#endif
off_t offset = filep->f_pos;
size_t i;
nxmutex_lock(&devfreq->lock);
procfs_sprintf(buffer, buflen, &offset,
" devfreq: %s\n"
" governor: %s\n"
" cur_freq: %"PRIu32"\n"
" suspended: %s\n",
devfreq->name,
devfreq->governor->name,
devfreq->cur,
devfreq->suspended ? "True" : "False");
if (devfreq->freq_table)
{
procfs_sprintf(buffer, buflen, &offset, " freq_table: ");
for (i = 0; devfreq->freq_table[i] != DEVFREQ_ENTRY_END; i++)
{
if (devfreq->freq_table[i] == DEVFREQ_ENTRY_INVALID)
{
continue;
}
procfs_sprintf(buffer, buflen, &offset,
" %"PRIu32"", devfreq->freq_table[i]);
}
procfs_sprintf(buffer, buflen, &offset, "\n");
}
#ifdef CONFIG_DEVFREQ_PROCFS_QOS
procfs_sprintf(buffer, buflen, &offset,
" qos_list(min, max, backtrace):\n");
plist_for_each_entry(qos, &devfreq->constraints.min_requests, min_req)
{
stack = backtrace_get(qos->backtrace, &depth);
procfs_sprintf(buffer, buflen, &offset,
" %"PRIu32", %"PRIu32",",
qos->min_req.prio, qos->max_req.prio);
for (i = 0; i < depth; i++)
{
procfs_sprintf(buffer, buflen, &offset, " %p", stack[i]);
}
procfs_sprintf(buffer, buflen, &offset, "\n");
}
#endif
nxmutex_unlock(&devfreq->lock);
if (offset < 0)
{
offset = -offset;
}
else
{
offset = 0;
}
filep->f_pos += offset;
return offset;
}
* Name: devfreq_write
****************************************************************************/
static ssize_t devfreq_write(FAR struct file *filep,
FAR const char *buffer, size_t buflen)
{
return buflen;
}
* Name: devfreq_opendir
*
* Description:
* Open a directory for read access
*
****************************************************************************/
static int devfreq_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)
{
*dir = NULL;
return -ENOMEM;
}
level1->level = 1;
level1->nentries = UINT16_MAX;
*dir = (FAR struct fs_dirent_s *)level1;
return 0;
}
* Name: devfreq_closedir
*
* Description:
* Close the directory listing
*
****************************************************************************/
static int devfreq_closedir(FAR struct fs_dirent_s *dir)
{
kmm_free(dir);
return 0;
}
* Name: devfreq_readdir
*
* Description:
* Read the next directory entry
*
****************************************************************************/
static int devfreq_readdir(FAR struct fs_dirent_s *dir,
FAR struct dirent *entry)
{
FAR struct devfreq_s *devfreq;
FAR struct procfs_dir_priv_s *level1;
DEBUGASSERT(dir);
level1 = (FAR struct procfs_dir_priv_s *)dir;
devfreq = devfreq_find_by_index(level1->index);
if (!devfreq)
{
return -ENOENT;
}
entry->d_type = DTYPE_FILE;
strlcpy(entry->d_name, devfreq->name, NAME_MAX);
level1->index++;
return 0;
}
* Name: devfreq_rewinddir
*
* Description:
* Reset directory read to the first entry
*
****************************************************************************/
static int devfreq_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 0;
}
* Name: devfreq_stat
*
* Description:
* Return information about a file or directory
*
****************************************************************************/
static int devfreq_stat(FAR const char *relpath, FAR struct stat *buf)
{
FAR struct devfreq_s *devfreq;
memset(buf, 0, sizeof(struct stat));
if (strcmp(relpath, "devfreq") == 0 || strcmp(relpath, "devfreq/") == 0)
{
buf->st_mode = S_IFDIR | S_IROTH | S_IRGRP | S_IRUSR;
}
else
{
relpath += strlen("devfreq/");
devfreq = devfreq_find_by_name(relpath);
if (!devfreq)
{
return -ENOENT;
}
buf->st_mode = S_IFREG | S_IROTH | S_IRGRP | S_IRUSR;
}
return 0;
}
* Public Functions
****************************************************************************/
* Name: devfreq_procfs_initialize
*
* Description:
* initialize procfs for devfreq, called by devfreq_initialize()
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
****************************************************************************/
void devfreq_procfs_initialize(void)
{
int ret;
ret = procfs_register(&g_devfreq_procfs_root);
if (ret == 0)
{
ret = procfs_register(&g_devfreq_procfs_entry);
}
DEBUGASSERT(ret == 0);
}