/**************************************************************************** * 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 #include #include #include #include #include #include #include #include #include #include #include /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /* Determines the size of an intermediate buffer that must be large enough * 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 ****************************************************************************/ /* File system methods */ 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_dup(FAR const struct file *oldp, FAR struct file *newp); 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, /* open */ .close = devfreq_close, /* close */ .read = devfreq_read, /* read */ .write = devfreq_write, /* write */ .poll = NULL, /* poll */ .dup = devfreq_dup, /* dup */ .opendir = devfreq_opendir, /* opendir */ .closedir = devfreq_closedir, /* closedir */ .readdir = devfreq_readdir, /* readdir */ .rewinddir = devfreq_rewinddir, /* rewinddir */ .stat = devfreq_stat, /* 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; #if defined(CONFIG_LIBC_BACKTRACE_BUFFSIZE) && CONFIG_LIBC_BACKTRACE_BUFFSIZE > 0 FAR void **stack; int depth; #endif #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_get_frequency(devfreq), 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) { procfs_sprintf(buffer, buflen, &offset, " %"PRIu32", %"PRIu32",", qos->min_req.prio, qos->max_req.prio); #if defined(CONFIG_LIBC_BACKTRACE_BUFFSIZE) && CONFIG_LIBC_BACKTRACE_BUFFSIZE > 0 stack = backtrace_get(qos->backtrace, &depth); for (i = 0; i < depth; i++) { procfs_sprintf(buffer, buflen, &offset, " %p", stack[i]); } #endif 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 * * Description: * Handle write to devfreq procfs entry. * Format: " " in kHz. * This creates or updates a QoS request to constrain the frequency. * Write "0 0" to remove the QoS constraint. * ****************************************************************************/ static ssize_t devfreq_write(FAR struct file *filep, FAR const char *buffer, size_t buflen) { FAR struct devfreq_procfs_s *devfreq_procfs = filep->f_priv; FAR struct devfreq_s *devfreq = devfreq_procfs->devfreq; uint32_t min_freq; uint32_t max_freq; FAR char *endptr; char tmp[32]; int ret; if (buflen == 0 || buflen >= sizeof(tmp)) { return -EINVAL; } memcpy(tmp, buffer, buflen); tmp[buflen] = '\0'; min_freq = strtoul(tmp, &endptr, 10); if (endptr == tmp) { return buflen; } if (*endptr == ',' || *endptr == ' ') { endptr++; } max_freq = strtoul(endptr, &endptr, 10); /* Write "0 0" to remove the QoS constraint */ if (min_freq == 0 && max_freq == 0) { if (devfreq->procfs_qos) { devfreq_qos_remove_request(devfreq, devfreq->procfs_qos); devfreq->procfs_qos = NULL; } return buflen; } if (min_freq > max_freq) { return -EINVAL; } if (devfreq->procfs_qos) { ret = devfreq_qos_update_request(devfreq, devfreq->procfs_qos, min_freq, max_freq); if (ret < 0) { return ret; } } else { devfreq->procfs_qos = devfreq_qos_add_request(devfreq, min_freq, max_freq); if (!devfreq->procfs_qos) { return -ENOMEM; } } return buflen; } /**************************************************************************** * Name: devfreq_dup * * Description: * Duplicate open file data in the new file structure. * ****************************************************************************/ static int devfreq_dup(FAR const struct file *oldp, FAR struct file *newp) { newp->f_priv = oldp->f_priv; return 0; } /**************************************************************************** * 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 | S_IWOTH | S_IWGRP | S_IWUSR; } 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); }