/**************************************************************************** * drivers/mmcsd/mmcsd_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 #include #include "mmcsd.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /* Determines the size of an intermediate buffer that must be large enough * to handle the longest line generated by this logic (plus a couple of * bytes). */ #define MMCSD_LINELEN 512 typedef CODE ssize_t (*mmcsd_read_t)(FAR struct file *filep, FAR char *buffer, size_t buflen, FAR struct mmcsd_state_s *priv); /**************************************************************************** * Private Function Prototypes ****************************************************************************/ /* File system methods */ static int mmcsd_open(FAR struct file *filep, FAR const char *relpath, int oflags, mode_t mode); static int mmcsd_close(FAR struct file *filep); static ssize_t mmcsd_read_cid(FAR struct file *filep, FAR char *buffer, size_t buflen, FAR struct mmcsd_state_s *priv); static ssize_t mmcsd_read_csd(FAR struct file *filep, FAR char *buffer, size_t buflen, FAR struct mmcsd_state_s *priv); static ssize_t mmcsd_read_type(FAR struct file *filep, FAR char *buffer, size_t buflen, FAR struct mmcsd_state_s *priv); static ssize_t mmcsd_read(FAR struct file *filep, FAR char *buffer, size_t buflen); static int mmcsd_dup(FAR const struct file *oldp, FAR struct file *newp); static int mmcsd_opendir(FAR const char *relpath, FAR struct fs_dirent_s **dir); static int mmcsd_closedir(FAR struct fs_dirent_s *dir); static int mmcsd_readdir(FAR struct fs_dirent_s *dir, FAR struct dirent *entry); static int mmcsd_rewinddir(FAR struct fs_dirent_s *dir); static int mmcsd_stat(FAR const char *relpath, FAR struct stat *buf); static int mmcsd_get_file_index(FAR const char *relpath); /**************************************************************************** * Private Types ****************************************************************************/ /* This structure describes one open "file" */ struct mmcsd_file_s { struct procfs_file_s base; /* Base open file structure */ char line[MMCSD_LINELEN]; /* Pre-allocated buffer for formatted lines */ int index; /* Device node index */ mmcsd_read_t read; /* Read function */ }; struct mmcsd_file_ops_s { FAR const char *name; mmcsd_read_t read; }; static const struct procfs_operations g_mmcsd_operations = { mmcsd_open, /* open */ mmcsd_close, /* close */ mmcsd_read, /* read */ NULL, /* write */ NULL, /* poll */ mmcsd_dup, /* dup */ mmcsd_opendir, /* opendir */ mmcsd_closedir, /* closedir */ mmcsd_readdir, /* readdir */ mmcsd_rewinddir, /* rewinddir */ mmcsd_stat /* stat */ }; static const struct procfs_entry_s g_mmcsd_procfs1 = { "mmcsd", &g_mmcsd_operations, PROCFS_DIR_TYPE }; static const struct procfs_entry_s g_mmcsd_procfs2 = { "mmcsd/**", &g_mmcsd_operations, PROCFS_UNKOWN_TYPE }; static const struct mmcsd_file_ops_s g_mmcsd_files[] = { {"cid", mmcsd_read_cid}, {"csd", mmcsd_read_csd}, {"type", mmcsd_read_type}, }; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: mmcsd_get_file_index ****************************************************************************/ static int mmcsd_get_file_index(FAR const char *relpath) { int i; for (i = 0; i < nitems(g_mmcsd_files); i++) { if (strncmp(relpath, g_mmcsd_files[i].name, strlen(g_mmcsd_files[i].name)) == 0) { return i; } } return -1; } /**************************************************************************** * Name: mmcsd_open ****************************************************************************/ static int mmcsd_open(FAR struct file *filep, FAR const char *relpath, int oflags, mode_t mode) { FAR struct mmcsd_file_s *mmcsdfile; int ret; /* This PROCFS file is read-only. Any attempt to open with write access * is not permitted. */ if ((oflags & O_WRONLY) != 0 || (oflags & O_RDONLY) == 0) { finfo("ERROR: Only O_RDONLY supported\n"); return -EACCES; } relpath += strlen("mmcsd/"); ret = mmcsd_get_file_index(relpath); if (ret < 0) { return -ENOENT; } /* Allocate a container to hold the file attributes */ mmcsdfile = kmm_zalloc(sizeof(struct mmcsd_file_s)); if (mmcsdfile == NULL) { finfo("ERROR: Failed to allocate file attributes\n"); return -ENOMEM; } mmcsdfile->read = g_mmcsd_files[ret].read; mmcsdfile->index = atoi(relpath + strlen(g_mmcsd_files[ret].name)); /* Save the attributes as the open-specific state in filep->f_priv */ filep->f_priv = mmcsdfile; return OK; } /**************************************************************************** * Name: mmcsd_close ****************************************************************************/ static int mmcsd_close(FAR struct file *filep) { DEBUGASSERT(filep->f_priv); /* Release the file attributes structure */ kmm_free(filep->f_priv); filep->f_priv = NULL; return OK; } /**************************************************************************** * Name: mmcsd_read_cid ****************************************************************************/ static ssize_t mmcsd_read_cid(FAR struct file *filep, FAR char *buffer, size_t buflen, FAR struct mmcsd_state_s *priv) { FAR struct mmcsd_file_s *mmcsdfile; size_t totalsize; size_t linesize; off_t offset; /* Recover our private data from the struct file instance */ mmcsdfile = filep->f_priv; /* Save the file offset and the user buffer information */ offset = filep->f_pos; linesize = snprintf(mmcsdfile->line, MMCSD_LINELEN, "%08lx%08lx%08lx%08lx", priv->cid[0], priv->cid[1], priv->cid[2], priv->cid[3]); totalsize = procfs_memcpy(mmcsdfile->line, linesize, buffer, buflen, &offset); filep->f_pos += totalsize; return totalsize; } /**************************************************************************** * Name: mmcsd_read_csd ****************************************************************************/ static ssize_t mmcsd_read_csd(FAR struct file *filep, FAR char *buffer, size_t buflen, FAR struct mmcsd_state_s *priv) { FAR struct mmcsd_file_s *mmcsdfile; size_t totalsize; size_t linesize; off_t offset; /* Recover our private data from the struct file instance */ mmcsdfile = filep->f_priv; /* Save the file offset and the user buffer information */ offset = filep->f_pos; linesize = snprintf(mmcsdfile->line, MMCSD_LINELEN, "%08lx%08lx%08lx%08lx", priv->csd[0], priv->csd[1], priv->csd[2], priv->csd[3]); totalsize = procfs_memcpy(mmcsdfile->line, linesize, buffer, buflen, &offset); filep->f_pos += totalsize; return totalsize; } /**************************************************************************** * Name: mmcsd_read_type ****************************************************************************/ static ssize_t mmcsd_read_type(FAR struct file *filep, FAR char *buffer, size_t buflen, FAR struct mmcsd_state_s *priv) { FAR struct mmcsd_file_s *mmcsdfile; size_t totalsize; size_t linesize; off_t offset; mmcsdfile = filep->f_priv; /* Save the file offset and the user buffer information */ offset = filep->f_pos; switch (priv->type) { case MMCSD_CARDTYPE_SDV1: case MMCSD_CARDTYPE_SDV2: case MMCSD_CARDTYPE_SDV2 | MMCSD_CARDTYPE_BLOCK: linesize = snprintf(mmcsdfile->line, MMCSD_LINELEN, "SD"); break; case MMCSD_CARDTYPE_MMC: case MMCSD_CARDTYPE_MMC | MMCSD_CARDTYPE_BLOCK: linesize = snprintf(mmcsdfile->line, MMCSD_LINELEN, "MMC"); break; /* Unknown card type */ case MMCSD_CARDTYPE_UNKNOWN: default: ferr("ERROR: Invalid media type (%d)\n", priv->type); return -EINVAL; } totalsize = procfs_memcpy(mmcsdfile->line, linesize, buffer, buflen, &offset); filep->f_pos += totalsize; return totalsize; } /**************************************************************************** * Name: mmcsd_read ****************************************************************************/ static ssize_t mmcsd_read(FAR struct file *filep, FAR char *buffer, size_t buflen) { FAR struct mmcsd_file_s *mmcsdfile = filep->f_priv; FAR struct inode *inode; char path[32]; ssize_t ret; snprintf(path, sizeof(path), "/dev/mmcsd%d", mmcsdfile->index); ret = open_blockdriver(path, MS_RDONLY, &inode); if (ret < 0) { return ret; } DEBUGASSERT(filep->f_priv); ret = mmcsdfile->read(filep, buffer, buflen, inode->i_private); close_blockdriver(inode); return ret; } /**************************************************************************** * Name: mmcsd_dup * * Description: * Duplicate open file data in the new file structure. * ****************************************************************************/ static int mmcsd_dup(FAR const struct file *oldp, FAR struct file *newp) { FAR struct mmcsd_file_s *oldattr; FAR struct mmcsd_file_s *newattr; /* Recover our private data from the old struct file instance */ oldattr = oldp->f_priv; DEBUGASSERT(oldattr); /* Allocate a new container to hold the task and attribute selection */ newattr = kmm_malloc(sizeof(struct mmcsd_file_s)); if (newattr == NULL) { ferr("ERROR: Failed to allocate file attributes\n"); return -ENOMEM; } /* The copy the file attributes from the old attributes to the new */ memcpy(newattr, oldattr, sizeof(struct mmcsd_file_s)); /* Save the new attributes in the new file structure */ newp->f_priv = newattr; return OK; } /**************************************************************************** * Name: mmcsd_opendir * * Description: * Open a directory for read access * ****************************************************************************/ static int mmcsd_opendir(FAR const char *relpath, FAR struct fs_dirent_s **dir) { FAR struct procfs_dir_priv_s *level1; DEBUGASSERT(relpath); /* Assume that path refers to the 1st level subdirectory. Allocate the * level1 the dirent structure before checking. */ level1 = kmm_zalloc(sizeof(struct procfs_dir_priv_s)); if (level1 == NULL) { ferr("ERROR: Failed to allocate the level1 directory structure\n"); return -ENOMEM; } /* Initialize base structure components */ level1->level = 1; level1->nentries = nitems(g_mmcsd_files); *dir = (FAR struct fs_dirent_s *)level1; return OK; } /**************************************************************************** * Name: mmcsd_closedir * * Description: Close the directory listing * ****************************************************************************/ static int mmcsd_closedir(FAR struct fs_dirent_s *dir) { DEBUGASSERT(dir); kmm_free(dir); return OK; } /**************************************************************************** * Name: mmcsd_readdir * * Description: Read the next directory entry * ****************************************************************************/ static int mmcsd_readdir(FAR struct fs_dirent_s *dir, FAR struct dirent *entry) { FAR struct procfs_dir_priv_s *level1; int index; int fpos; DEBUGASSERT(dir); level1 = (FAR struct procfs_dir_priv_s *)dir; index = level1->index; if (index >= level1->nentries) { /* We signal the end of the directory by returning the special * error -ENOENT */ finfo("Entry %d: End of directory\n", index); return -ENOENT; } fpos = index % nitems(g_mmcsd_files); index = index / nitems(g_mmcsd_files); entry->d_type = DTYPE_FILE; snprintf(entry->d_name, NAME_MAX + 1, "%s%d", g_mmcsd_files[fpos].name, index); level1->index++; return OK; } /**************************************************************************** * Name: mmcsd_rewindir * * Description: Reset directory read to the first entry * ****************************************************************************/ static int mmcsd_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; } /**************************************************************************** * Name: mmcsd_stat * * Description: Return information about a file or directory * ****************************************************************************/ static int mmcsd_stat(FAR const char *relpath, FAR struct stat *buf) { memset(buf, 0, sizeof(struct stat)); if (strcmp(relpath, "mmcsd") == 0 || strcmp(relpath, "mmcsd/") == 0) { buf->st_mode = S_IFDIR | S_IROTH | S_IRGRP | S_IRUSR; } else { relpath += strlen("mmcsd/"); if (mmcsd_get_file_index(relpath) < 0) { return -ENOENT; } buf->st_mode = S_IFREG | S_IROTH | S_IRGRP | S_IRUSR; } return OK; } /**************************************************************************** * Name: mmcsd_initialize_procfs * * Description: * Initialize mmcsd procfs * ****************************************************************************/ void mmcsd_initialize_procfs(void) { int ret; ret = procfs_register(&g_mmcsd_procfs1); if (ret == OK) { ret = procfs_register(&g_mmcsd_procfs2); } DEBUGASSERT(ret == OK); }