static int cpuload_open(FAR struct file *filep, FAR const char *relpath, int oflags, mode_t mode) { FAR struct cpuload_file_s *attr; fvdbg("Open '%s'\n", relpath); /* PROCFS is read-only. Any attempt to open with any kind of write * access is not permitted. * * REVISIT: Write-able proc files could be quite useful. */ if ((oflags & O_WRONLY) != 0 || (oflags & O_RDONLY) == 0) { fdbg("ERROR: Only O_RDONLY supported\n"); return -EACCES; } /* "cpuload" is the only acceptable value for the relpath */ if (strcmp(relpath, "cpuload") != 0) { fdbg("ERROR: relpath is '%s'\n", relpath); return -ENOENT; } /* Allocate a container to hold the file attributes */ attr = (FAR struct cpuload_file_s *)kmm_zalloc(sizeof(struct cpuload_file_s)); if (!attr) { fdbg("ERROR: Failed to allocate file attributes\n"); return -ENOMEM; } /* Save the attributes as the open-specific state in filep->f_priv */ filep->f_priv = (FAR void *)attr; return OK; }
static int romfs_close(FAR struct file *filep) { FAR struct romfs_mountpt_s *rm; FAR struct romfs_file_s *rf; int ret = OK; fvdbg("Closing\n"); /* Sanity checks */ DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); /* Recover our private data from the struct file instance */ rf = filep->f_priv; rm = filep->f_inode->i_private; DEBUGASSERT(rm != NULL); /* Do not check if the mount is healthy. We must support closing of * the file even when there is healthy mount. */ /* Deallocate the memory structures created when the open method * was called. * * Free the sector buffer that was used to manage partial sector * accesses. */ if (!rm->rm_xipbase && rf->rf_buffer) { kfree(rf->rf_buffer); } /* Then free the file structure itself. */ kfree(rf); filep->f_priv = NULL; return ret; }
static int lpc43_pagewrite(FAR struct lpc43_dev_s *priv, FAR uint8_t *dest, FAR const uint8_t *src, size_t nbytes) { int result; /* Write FLASH pages: * * dest - Specifies the first address to be programmed or erased, either in * the SPIFI memory area or as a zero-based device address. It must * be at an offset that is an exact multiple of the erase block size. * length - The number of bytes to be programmed or erased */ priv->operands.dest = dest; priv->operands.length = nbytes; fvdbg("SPIFI_PROGRAM: src=%p dest=%p length=%d\n", src, priv->operands.dest, priv->operands.length); result = SPIFI_PROGRAM(priv, &priv->rom, src, &priv->operands); if (result != 0) { fdbg("ERROR: SPIFI_PROGRAM failed: %05x\n", result); return -EIO; } /* Verify the data that was written by comparing to the data visible in the * SPIFI address space. */ #ifdef CONFIG_SPIFI_VERIFY result = lpc43_verify(priv, dest, src, nbytes); if (result != 0) { fdbg("ERROR: lpc43_verify failed: %05x\n", result); return -EIO; } #endif return OK; }
static ssize_t procfs_write(FAR struct file *filep, FAR const char *buffer, size_t buflen) { FAR struct procfs_file_s *handler; ssize_t ret = 0; fvdbg("buffer=%p buflen=%d\n", buffer, (int)buflen); /* Recover our private data from the struct file instance */ handler = (FAR struct procfs_file_s *)filep->f_priv; DEBUGASSERT(handler); /* Call the handler's read routine */ if (handler->procfsentry->ops->write) { ret = handler->procfsentry->ops->write(filep, buffer, buflen); } return ret; }
int nxffs_opendir(FAR struct inode *mountpt, FAR const char *relpath, FAR struct fs_dirent_s *dir) { struct nxffs_volume_s *volume; int ret; fvdbg("relpath: \"%s\"\n", relpath ? relpath : "NULL"); /* Sanity checks */ DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); /* Recover the file system state from the NuttX inode instance */ volume = mountpt->i_private; ret = sem_wait(&volume->exclsem); if (ret != OK) { goto errout; } /* The requested directory must be the volume-relative "root" directory */ if (relpath && relpath[0] != '\0') { ret = -ENOENT; goto errout_with_semaphore; } /* Set the offset to the offset to the first valid inode */ dir->u.nxffs.nx_offset = volume->inoffset; ret = OK; errout_with_semaphore: sem_post(&volume->exclsem); errout: return ret; }
static void uart_pollnotify(FAR uart_dev_t *dev, pollevent_t eventset) { int i; for (i = 0; i < CONFIG_SERIAL_NPOLLWAITERS; i++) { struct pollfd *fds = dev->fds[i]; if (fds) { #ifdef CONFIG_SERIAL_REMOVABLE fds->revents |= ((fds->events | (POLLERR|POLLHUP)) & eventset); #else fds->revents |= (fds->events & eventset); #endif if (fds->revents != 0) { fvdbg("Report events: %02x\n", fds->revents); sem_post(fds->sem); } } } }
static int tiva_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks) { #if !defined(CONFIG_QEMU_SRAM) && !defined(CONFIG_QEMU_SDRAM) int curpage; uint32_t pageaddr; #endif DEBUGASSERT(nblocks <= TIVA_VIRTUAL_NPAGES); #if defined(CONFIG_QEMU_SRAM) || defined(CONFIG_QEMU_SDRAM) /* Qemu doesnt implement tiva flash memory controller. * Hence set the erase state to 0xFF using memset */ memset((void *)(TIVA_VIRTUAL_BASE + startblock * TIVA_FLASH_PAGESIZE), 0xFF, nblocks * TIVA_FLASH_PAGESIZE); #else for (curpage = startblock; curpage < nblocks; curpage++) { pageaddr = TIVA_VIRTUAL_BASE + curpage * TIVA_FLASH_PAGESIZE; fvdbg("Erase page at %08x\n", pageaddr); /* set page address */ putreg32((pageaddr << FLASH_FMA_OFFSET_SHIFT) & FLASH_FMA_OFFSET_MASK, TIVA_FLASH_FMA); /* set flash write key and erase bit */ putreg32(FLASH_FMC_WRKEY | FLASH_FMC_ERASE, TIVA_FLASH_FMC); /* wait until erase has finished */ while (getreg32(TIVA_FLASH_FMC) & FLASH_FMC_ERASE) ; } #endif return OK; }
static void lpc43_cacheerase(struct lpc43_dev_s *priv, off_t sector) { FAR uint8_t *dest; /* First, make sure that the erase block containing the 512 byte sector is in * the cache. */ dest = lpc43_cacheread(priv, sector); /* Erase the block containing this sector if it is not already erased. * The erased indicated will be cleared when the data from the erase sector * is read into the cache and set here when we erase the block. */ if (!IS_ERASED(priv)) { off_t blkno = sector >> (SPIFI_BLKSHIFT - SPIFI_512SHIFT); fvdbg("sector: %ld blkno: %d\n", sector, blkno); lpc43_blockerase(priv, blkno); SET_ERASED(priv); }
static int skel_open(FAR struct file *filep, FAR const char *relpath, int oflags, mode_t mode) { FAR struct skel_file_s *priv; fvdbg("Open '%s'\n", relpath); /* PROCFS is read-only. Any attempt to open with any kind of write * access is not permitted. * * REVISIT: Write-able proc files could be quite useful. */ if (((oflags & O_WRONLY) != 0 || (oflags & O_RDONLY) == 0) && (skel_procfsoperations.write == NULL)) { fdbg("ERROR: Only O_RDONLY supported\n"); return -EACCES; } /* Allocate a container to hold the task and attribute selection */ priv = (FAR struct skel_file_s *)kmm_zalloc(sizeof(struct skel_file_s)); if (!priv) { fdbg("ERROR: Failed to allocate file attributes\n"); return -ENOMEM; } /* TODO: Initialize the context specific data here */ /* Save the index as the open-specific state in filep->f_priv */ filep->f_priv = (FAR void *)priv; return OK; }
int nxffs_statfs(FAR struct inode *mountpt, FAR struct statfs *buf) { FAR struct nxffs_volume_s *volume; int ret; fvdbg("Entry\n"); /* Sanity checks */ DEBUGASSERT(mountpt && mountpt->i_private); /* Get the mountpoint private data from the NuttX inode structure */ volume = mountpt->i_private; ret = sem_wait(&volume->exclsem); if (ret != OK) { goto errout; } /* Fill in the statfs info * * REVISIT: Need f_bfree, f_bavail, f_files, f_ffree calculation */ memset(buf, 0, sizeof(struct statfs)); buf->f_type = NXFFS_MAGIC; buf->f_bsize = volume->geo.blocksize; buf->f_blocks = volume->nblocks; buf->f_namelen = volume->geo.blocksize - SIZEOF_NXFFS_BLOCK_HDR - SIZEOF_NXFFS_INODE_HDR; ret = OK; sem_post(&volume->exclsem); errout: return ret; }
static void lpc43_blockerase(struct lpc43_dev_s *priv, off_t sector) { int result; /* Erase one block on the chip: * * dest - Specifies the first address to be programmed or erased, either in * the SPIFI memory area or as a zero-based device address. It must * be at an offset that is an exact multiple of the erase block size. * length - The number of bytes to be programmed or erased */ priv->operands.dest = SPIFI_BASE + (sector << SPIFI_BLKSHIFT); priv->operands.length = SPIFI_BLKSIZE; fvdbg("SPIFI_ERASE: dest=%p length=%d\n", priv->operands.dest, priv->operands.length); result = SPIFI_ERASE(priv, &priv->rom, &priv->operands); if (result != 0) { fdbg("ERROR: SPIFI_ERASE failed: %05x\n", result); } }
static ssize_t skel_read(FAR struct file *filep, FAR char *buffer, size_t buflen) { FAR struct skel_file_s *priv; ssize_t ret; fvdbg("buffer=%p buflen=%d\n", buffer, (int)buflen); /* Recover our private data from the struct file instance */ priv = (FAR struct skel_file_s *)filep->f_priv; DEBUGASSERT(priv); /* TODO: Provide the requested data */ ret = 0; /* Update the file offset */ if (ret > 0) { filep->f_pos += ret; } return ret; }
int nxffs_nextblock(FAR struct nxffs_volume_s *volume, off_t offset, FAR struct nxffs_blkentry_s *blkentry) { int nmagic; int ch; int nerased; int ret; /* Seek to the first FLASH offset provided by the caller. */ nxffs_ioseek(volume, offset); /* Skip the block header */ if (volume->iooffset < SIZEOF_NXFFS_BLOCK_HDR) { volume->iooffset = SIZEOF_NXFFS_BLOCK_HDR; } /* Then begin searching */ nerased = 0; nmagic = 0; for (;;) { /* Read the next character */ ch = nxffs_getc(volume, SIZEOF_NXFFS_DATA_HDR - nmagic); if (ch < 0) { fdbg("ERROR: nxffs_getc failed: %d\n", -ch); return ch; } /* Check for another erased byte */ else if (ch == CONFIG_NXFFS_ERASEDSTATE) { /* If we have encountered NXFFS_NERASED number of consecutive * erased bytes, then presume we have reached the end of valid * data. */ if (++nerased >= NXFFS_NERASED) { fvdbg("No entry found\n"); return -ENOENT; } } else { nerased = 0; /* Check for the magic sequence indicating the start of an NXFFS * data block or start of the next inode. There is the possibility * of this magic sequnce occurring in FLASH data. However, the * data block CRC should distinguish between real NXFFS data blocks * headers and such false alarms. */ if (ch != g_datamagic[nmagic]) { /* Ooops... this is the not the right character for the magic * Sequence. Check if we need to restart or to cancel the sequence: */ if (ch == g_datamagic[0]) { nmagic = 1; } else { nmagic = 0; } } else if (nmagic < NXFFS_MAGICSIZE - 1) { /* We have one more character in the magic sequence */ nmagic++; } /* We have found the magic sequence in the FLASH data that may * indicate the beginning of an NXFFS data block. */ else { /* The offset to the header must be 4 bytes before the current * FLASH seek location. */ blkentry->hoffset = nxffs_iotell(volume) - NXFFS_MAGICSIZE; /* Read the block header and verify the block at that address */ ret = nxffs_rdblkhdr(volume, blkentry->hoffset, &blkentry->datlen); if (ret == OK) { fvdbg("Found a valid data block, offset: %d datlen: %d\n", blkentry->hoffset, blkentry->datlen); return OK; } /* False alarm.. Restore the volume cache position (that was * destroyed by nxfs_rdblkhdr()) and keep looking. */ nxffs_ioseek(volume, blkentry->hoffset + NXFFS_MAGICSIZE); nmagic = 0; } } } /* We won't get here, but to keep some compilers happy: */ return -ENOENT; }
ssize_t nxffs_read(FAR struct file *filep, FAR char *buffer, size_t buflen) { FAR struct nxffs_volume_s *volume; FAR struct nxffs_ofile_s *ofile; struct nxffs_blkentry_s blkentry; ssize_t total; size_t available; size_t readsize; int ret; fvdbg("Read %d bytes from offset %d\n", buflen, filep->f_pos); /* Sanity checks */ DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); /* Recover the open file state from the struct file instance */ ofile = (FAR struct nxffs_ofile_s *)filep->f_priv; /* Recover the volume state from the open file */ volume = (FAR struct nxffs_volume_s *)filep->f_inode->i_private; DEBUGASSERT(volume != NULL); /* Get exclusive access to the volume. Note that the volume exclsem * protects the open file list. */ ret = sem_wait(&volume->exclsem); if (ret != OK) { ret = -get_errno(); fdbg("ERROR: sem_wait failed: %d\n", ret); goto errout; } /* Check if the file was opened with read access */ if ((ofile->oflags & O_RDOK) == 0) { fdbg("ERROR: File not open for read access\n"); ret = -EACCES; goto errout_with_semaphore; } /* Loop until all bytes have been read */ for (total = 0; total < buflen; ) { /* Don't seek past the end of the file */ if (filep->f_pos >= ofile->entry.datlen) { /* Return the partial read */ filep->f_pos = ofile->entry.datlen; break; } /* Seek to the current file offset */ ret = nxffs_rdseek(volume, &ofile->entry, filep->f_pos, &blkentry); if (ret < 0) { fdbg("ERROR: nxffs_rdseek failed: %d\n", -ret); ret = -EACCES; goto errout_with_semaphore; } /* How many bytes are available at this offset */ available = blkentry.datlen - blkentry.foffset; /* Don't read more than we need to */ readsize = buflen - total; if (readsize > available) { readsize = available; } /* Read data from that file offset */ memcpy(&buffer[total], &volume->cache[volume->iooffset], readsize); /* Update the file offset */ filep->f_pos += readsize; total += readsize; } sem_post(&volume->exclsem); return total; errout_with_semaphore: sem_post(&volume->exclsem); errout: return (ssize_t)ret; }
static int proc_open(FAR struct file *filep, FAR const char *relpath, int oflags, mode_t mode) { FAR struct proc_file_s *procfile; FAR const struct proc_node_s *node; FAR struct tcb_s *tcb; FAR char *ptr; irqstate_t flags; unsigned long tmp; pid_t pid; fvdbg("Open '%s'\n", relpath); /* PROCFS is read-only. Any attempt to open with any kind of write * access is not permitted. * * REVISIT: Write-able proc files could be quite useful. */ if ((oflags & O_WRONLY) != 0 || (oflags & O_RDONLY) == 0) { fdbg("ERROR: Only O_RDONLY supported\n"); return -EACCES; } /* The first segment of the relative path should be a task/thread ID */ ptr = NULL; tmp = strtoul(relpath, &ptr, 10); if (!ptr || *ptr != '/') { fdbg("ERROR: Invalid path \"%s\"\n", relpath); return -ENOENT; } /* Skip over the slash */ ptr++; /* A valid PID would be in the range of 0-32767 (0 is reserved for the * IDLE thread). */ if (tmp >= 32768) { fdbg("ERROR: Invalid PID %ld\n", tmp); return -ENOENT; } /* Now verify that a task with this task/thread ID exists */ pid = (pid_t)tmp; flags = irqsave(); tcb = sched_gettcb(pid); irqrestore(flags); if (!tcb) { fdbg("ERROR: PID %d is no longer valid\n", (int)pid); return -ENOENT; } /* The remaining segments of the relpath should be a well known node in * the task/thread tree. */ node = proc_findnode(ptr); if (!node) { fdbg("ERROR: Invalid path \"%s\"\n", relpath); return -ENOENT; } /* The node must be a file, not a directory */ if (node->dtype != DTYPE_FILE) { fdbg("ERROR: Path \"%s\" is a directory\n", relpath); return -EISDIR; } /* Allocate a container to hold the task and node selection */ procfile = (FAR struct proc_file_s *)kzalloc(sizeof(struct proc_file_s)); if (!procfile) { fdbg("ERROR: Failed to allocate file container\n"); return -ENOMEM; } /* Initialize the file container */ procfile->pid = pid; procfile->node = node; /* Save the index as the open-specific state in filep->f_priv */ filep->f_priv = (FAR void *)procfile; return OK; }
static int procfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir) { FAR struct procfs_dir_priv_s *priv; FAR struct procfs_level0_s *level0; FAR struct tcb_s *tcb; FAR const char *name = NULL; unsigned int index; irqstate_t flags; pid_t pid; int ret = -ENOENT; DEBUGASSERT(mountpt && dir && dir->u.procfs); priv = dir->u.procfs; /* Are we reading the 1st directory level with dynamic PID and static * entries? */ if (priv->level == 0) { level0 = (FAR struct procfs_level0_s *)priv; /* Have we reached the end of the PID information */ index = priv->index; if (index >= priv->nentries) { /* We must report the next static entry ... no more PID entries. * skip any entries with wildcards in the first segment of the * directory name. */ while (index < priv->nentries + g_procfsentrycount) { name = g_procfsentries[index - priv->nentries].pathpattern; while (*name != '/' && *name != '\0') { if (*name == '*' || *name == '[' || *name == '?') { /* Wildcard found. Skip this entry */ index++; name = NULL; break; } name++; } /* Test if we skipped this entry */ if (name != NULL) { /* This entry is okay to report. Test if it has a duplicate * first level name as the one we just reported. This could * happen in the event of procfs_entry_s such as: * * fs/smartfs * fs/nfs * fs/nxffs */ name = g_procfsentries[index - priv->nentries].pathpattern; if (!level0->lastlen || (strncmp(name, level0->lastread, level0->lastlen) != 0)) { /* Not a duplicate, return the first segment of this * entry */ break; } else { /* Skip this entry ... duplicate 1st level name found */ index++; } } } /* Test if we are at the end of the directory */ if (index >= priv->nentries + g_procfsentrycount) { /* We signal the end of the directory by returning the special * error -ENOENT */ fvdbg("Entry %d: End of directory\n", index); ret = -ENOENT; } else { /* Report the next static entry */ level0->lastlen = strcspn(name, "/"); level0->lastread = name; strncpy(dir->fd_dir.d_name, name, level0->lastlen); dir->fd_dir.d_name[level0->lastlen] = '\0'; if (name[level0->lastlen] == '/') { dir->fd_dir.d_type = DTYPE_DIRECTORY; } else { dir->fd_dir.d_type = DTYPE_FILE; } /* Advance to next entry for the next read */ priv->index = index; ret = OK; } } #ifndef CONFIG_FS_PROCFS_EXCLUDE_PROCESS else { /* Verify that the pid still refers to an active task/thread */ pid = level0->pid[index]; flags = irqsave(); tcb = sched_gettcb(pid); irqrestore(flags); if (!tcb) { fdbg("ERROR: PID %d is no longer valid\n", (int)pid); return -ENOENT; } /* Save the filename=pid and file type=directory */ dir->fd_dir.d_type = DTYPE_DIRECTORY; snprintf(dir->fd_dir.d_name, NAME_MAX+1, "%d", (int)pid); /* Set up the next directory entry offset. NOTE that we could use the * standard f_pos instead of our own private index. */ level0->base.index = index + 1; ret = OK; } #endif /* CONFIG_FS_PROCFS_EXCLUDE_PROCESS */ } /* Are we reading an intermediate subdirectory? */ else if (priv->level > 0 && priv->procfsentry == NULL) { FAR struct procfs_level1_s *level1; level1 = (FAR struct procfs_level1_s *) priv; /* Test if this entry matches. We assume all entries of the same * subdirectory are listed in order in the procfs_entry array. */ if (strncmp(g_procfsentries[level1->base.index].pathpattern, g_procfsentries[level1->firstindex].pathpattern, level1->subdirlen) == 0) { /* This entry matches. Report the subdir entry */ name = &g_procfsentries[level1->base.index].pathpattern[ level1->subdirlen + 1]; level1->lastlen = strcspn(name, "/"); level1->lastread = name; strncpy(dir->fd_dir.d_name, name, level1->lastlen); /* Some of the search entries contain '**' wildcards. When we * report the entry name, we must remove this wildcard search * specifier. */ while (dir->fd_dir.d_name[level1->lastlen - 1] == '*') { level1->lastlen--; } dir->fd_dir.d_name[level1->lastlen] = '\0'; if (name[level1->lastlen] == '/') { dir->fd_dir.d_type = DTYPE_DIRECTORY; } else { dir->fd_dir.d_type = DTYPE_FILE; } level1->base.index++; ret = OK; } else { /* No more entries in the subdirectory */ ret = -ENOENT; } } else { /* We are performing a directory search of one of the subdirectories * and we must let the handler perform the read. */ DEBUGASSERT(priv->procfsentry && priv->procfsentry->ops->readdir); ret = priv->procfsentry->ops->readdir(dir); } return ret; }
static int procfs_opendir(FAR struct inode *mountpt, FAR const char *relpath, FAR struct fs_dirent_s *dir) { FAR struct procfs_level0_s *level0; FAR struct procfs_dir_priv_s *dirpriv; FAR void *priv = NULL; irqstate_t flags; fvdbg("relpath: \"%s\"\n", relpath ? relpath : "NULL"); DEBUGASSERT(mountpt && relpath && dir && !dir->u.procfs); /* The relative must be either: * * "" - The top level directory of task/thread IDs * "<pid>" - The sub-directory of task/thread attributes */ if (!relpath || relpath[0] == '\0') { /* The path refers to the top level directory. Allocate the level0 * dirent structure. */ level0 = (FAR struct procfs_level0_s *) kmm_zalloc(sizeof(struct procfs_level0_s)); if (!level0) { fdbg("ERROR: Failed to allocate the level0 directory structure\n"); return -ENOMEM; } /* Take a snapshot of all currently active tasks. Any new tasks * added between the opendir() and closedir() call will not be * visible. * * NOTE that interrupts must be disabled throughout the traversal. */ #ifndef CONFIG_FS_PROCFS_EXCLUDE_PROCESS flags = irqsave(); sched_foreach(procfs_enum, level0); irqrestore(flags); #else level0->base.index = 0; level0->base.nentries = 0; #endif /* Initialze lastread entries */ level0->lastread = ""; level0->lastlen = 0; level0->base.procfsentry = NULL; priv = (FAR void *)level0; } else { int x, ret; int len = strlen(relpath); /* Search the static array of procfs_entries */ for (x = 0; x < g_procfsentrycount; x++) { /* Test if the path matches this entry's specification */ if (match(g_procfsentries[x].pathpattern, relpath)) { /* Match found! Call the handler's opendir routine. If successful, * this opendir routine will create an entry derived from struct * procfs_dir_priv_s as dir->u.procfs. */ DEBUGASSERT(g_procfsentries[x].ops && g_procfsentries[x].ops->opendir); ret = g_procfsentries[x].ops->opendir(relpath, dir); if (ret == OK) { DEBUGASSERT(dir->u.procfs); /* Set the procfs_entry handler */ dirpriv = (FAR struct procfs_dir_priv_s *)dir->u.procfs; dirpriv->procfsentry = &g_procfsentries[x]; } return ret; } /* Test for a sub-string match (e.g. "ls /proc/fs") */ else if (strncmp(g_procfsentries[x].pathpattern, relpath, len) == 0) { FAR struct procfs_level1_s *level1; /* Doing an intermediate directory search */ /* The path refers to the top level directory. Allocate the level0 * dirent structure. */ level1 = (FAR struct procfs_level1_s *) kmm_zalloc(sizeof(struct procfs_level1_s)); if (!level1) { fdbg("ERROR: Failed to allocate the level0 directory structure\n"); return -ENOMEM; } level1->base.level = 1; level1->base.index = x; level1->firstindex = x; level1->subdirlen = len; level1->lastread = ""; level1->lastlen = 0; level1->base.procfsentry = NULL; priv = (FAR void *)level1; break; } } } dir->u.procfs = priv; return OK; }
int rwb_write(FAR struct rwbuffer_s *rwb, off_t startblock, size_t nblocks, FAR const uint8_t *wrbuffer) { int ret; #ifdef CONFIG_DRVR_READAHEAD if (rhmaxblocks > 0) { /* If the new write data overlaps any part of the read buffer, then * flush the data from the read buffer. We could attempt some more * exotic handling -- but this simple logic is well-suited for simple * streaming applications. */ rwb_semtake(&rwb->rhsem); if (rwb_overlap(rwb->rhblockstart, rwb->rhnblocks, startblock, nblocks)) { rwb_resetrhbuffer(rwb); } rwb_semgive(&rwb->rhsem); } #endif #ifdef CONFIG_DRVR_WRITEBUFFER if (rwb->wrmaxblocks > 0) { fvdbg("startblock=%d wrbuffer=%p\n", startblock, wrbuffer); /* Use the block cache unless the buffer size is bigger than block cache */ if (nblocks > rwb->wrmaxblocks) { /* First flush the cache */ rwb_semtake(&rwb->wrsem); rwb_wrflush(rwb); rwb_semgive(&rwb->wrsem); /* Then transfer the data directly to the media */ ret = rwb->wrflush(rwb->dev, startblock, nblocks, wrbuffer); } else { /* Buffer the data in the write buffer */ ret = rwb_writebuffer(rwb, startblock, nblocks, wrbuffer); } /* On success, return the number of blocks that we were requested to * write. This is for compatibility with the normal return of a block * driver write method */ } else #else { /* No write buffer.. just pass the write operation through via the * flush callback. */ ret = rwb->wrflush(rwb->dev, startblock, nblocks, wrbuffer); } #endif return ret; }
int nxffs_stat(FAR struct inode *mountpt, FAR const char *relpath, FAR struct stat *buf) { FAR struct nxffs_volume_s *volume; struct nxffs_entry_s entry; int ret; fvdbg("Entry\n"); /* Sanity checks */ DEBUGASSERT(mountpt && mountpt->i_private && buf); /* Get the mountpoint private data from the NuttX inode structure */ volume = mountpt->i_private; ret = sem_wait(&volume->exclsem); if (ret != OK) { goto errout; } /* Initialize the return stat instance */ memset(buf, 0, sizeof(struct stat)); buf->st_blksize = volume->geo.blocksize; buf->st_blocks = entry.datlen / (volume->geo.blocksize - SIZEOF_NXFFS_BLOCK_HDR); /* The requested directory must be the volume-relative "root" directory */ if (relpath && relpath[0] != '\0') { /* Not the top directory.. find the NXFFS inode with this name */ ret = nxffs_findinode(volume, relpath, &entry); if (ret < 0) { fdbg("ERROR: Inode '%s' not found: %d\n", -ret); goto errout_with_semaphore; } buf->st_mode = S_IFREG|S_IXOTH|S_IXGRP|S_IXUSR; buf->st_size = entry.datlen; buf->st_atime = entry.utc; buf->st_mtime = entry.utc; buf->st_ctime = entry.utc; /* Free inode resources */ nxffs_freeentry(&entry); } else { /* It's a read/execute-only directory name */ buf->st_mode = S_IFDIR|S_IROTH|S_IRGRP|S_IRUSR|S_IXOTH|S_IXGRP|S_IXUSR; } ret = OK; errout_with_semaphore: sem_post(&volume->exclsem); errout: return ret; }
int rwb_initialize(FAR struct rwbuffer_s *rwb) { uint32_t allocsize; /* Sanity checking */ DEBUGASSERT(rwb != NULL); DEBUGASSERT(rwb->blocksize > 0); DEBUGASSERT(rwb->nblocks > 0); DEBUGASSERT(rwb->dev != NULL); /* Setup so that rwb_uninitialize can handle a failure */ #ifdef CONFIG_DRVR_WRITEBUFFER DEBUGASSERT(rwb->wrflush!= NULL); rwb->wrbuffer = NULL; #endif #ifdef CONFIG_DRVR_READAHEAD DEBUGASSERT(rwb->rhreload != NULL); rwb->rhbuffer = NULL; #endif #ifdef CONFIG_DRVR_WRITEBUFFER if (rwb->wrmaxblocks > 0) { fvdbg("Initialize the write buffer\n"); /* Initialize the write buffer access semaphore */ sem_init(&rwb->wrsem, 0, 1); /* Initialize write buffer parameters */ rwb_resetwrbuffer(rwb); /* Allocate the write buffer */ rwb->wrbuffer = NULL; if (rwb->wrmaxblocks > 0) { allocsize = rwb->wrmaxblocks * rwb->blocksize; rwb->wrbuffer = kmm_malloc(allocsize); if (!rwb->wrbuffer) { fdbg("Write buffer kmm_malloc(%d) failed\n", allocsize); return -ENOMEM; } } fvdbg("Write buffer size: %d bytes\n", allocsize); } #endif /* CONFIG_DRVR_WRITEBUFFER */ #ifdef CONFIG_DRVR_READAHEAD if (rhmaxblocks > 0) { fvdbg("Initialize the read-ahead buffer\n"); /* Initialize the read-ahead buffer access semaphore */ sem_init(&rwb->rhsem, 0, 1); /* Initialize read-ahead buffer parameters */ rwb_resetrhbuffer(rwb); /* Allocate the read-ahead buffer */ rwb->rhbuffer = NULL; if (rwb->rhmaxblocks > 0) { allocsize = rwb->rhmaxblocks * rwb->blocksize; rwb->rhbuffer = kmm_malloc(allocsize); if (!rwb->rhbuffer) { fdbg("Read-ahead buffer kmm_malloc(%d) failed\n", allocsize); return -ENOMEM; } } fvdbg("Read-ahead buffer size: %d bytes\n", allocsize); } #endif /* CONFIG_DRVR_READAHEAD */ return OK; }
int rwb_read(FAR struct rwbuffer_s *rwb, off_t startblock, uint32_t nblocks, FAR uint8_t *rdbuffer) { uint32_t remaining; fvdbg("startblock=%ld nblocks=%ld rdbuffer=%p\n", (long)startblock, (long)nblocks, rdbuffer); #ifdef CONFIG_DRVR_WRITEBUFFER /* If the new read data overlaps any part of the write buffer, then * flush the write data onto the physical media before reading. We * could attempt some more exotic handling -- but this simple logic * is well-suited for simple streaming applications. */ if (rwb->wrmaxblocks > 0) { /* If the write buffer overlaps the block(s) requested, then flush the * write buffer. */ rwb_semtake(&rwb->wrsem); if (rwb_overlap(rwb->wrblockstart, rwb->wrnblocks, startblock, nblocks)) { rwb_wrflush(rwb); } rwb_semgive(&rwb->wrsem); } #endif #ifdef CONFIG_DRVR_READAHEAD if (rhmaxblocks > 0) { /* Loop until we have read all of the requested blocks */ rwb_semtake(&rwb->rhsem); for (remaining = nblocks; remaining > 0;) { /* Is there anything in the read-ahead buffer? */ if (rwb->rhnblocks > 0) { off_t startblock = startblock; size_t nbufblocks = 0; off_t bufferend; /* Loop for each block we find in the read-head buffer. Count * the number of buffers that we can read from read-ahead * buffer. */ bufferend = rwb->rhblockstart + rwb->rhnblocks; while ((startblock >= rwb->rhblockstart) && (startblock < bufferend) && (remaining > 0)) { /* This is one more that we will read from the read ahead * buffer. */ nbufblocks++; /* And one less that we will read from the media */ startblock++; remaining--; } /* Then read the data from the read-ahead buffer */ rwb_bufferread(rwb, startblock, nbufblocks, &rdbuffer); } /* If we did not get all of the data from the buffer, then we have * to refill the buffer and try again. */ if (remaining > 0) { int ret = rwb_rhreload(rwb, startblock); if (ret < 0) { fdbg("ERROR: Failed to fill the read-ahead buffer: %d\n", ret); return ret; } } } /* On success, return the number of blocks that we were requested to * read. This is for compatibility with the normal return of a block * driver read method */ rwb_semgive(&rwb->rhsem); ret = nblocks; } else #else { /* No read-ahead buffering, (re)load the data directly into * the user buffer. */ ret = rwb->rhreload(rwb->dev, startblock, nblocks, rdbuffer); } #endif return ret; }
int nxffs_ioctl(FAR struct file *filep, int cmd, unsigned long arg) { FAR struct nxffs_volume_s *volume; int ret; fvdbg("cmd: %d arg: %08lx\n", cmd, arg); /* Sanity checks */ DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); /* Recover the file system state from the open file */ volume = filep->f_inode->i_private; DEBUGASSERT(volume != NULL); /* Get exclusive access to the volume. Note that the volume exclsem * protects the open file list. */ ret = sem_wait(&volume->exclsem); if (ret != OK) { ret = -errno; fdbg("sem_wait failed: %d\n", ret); goto errout; } /* Only a reformat and optimize commands are supported */ if (cmd == FIOC_REFORMAT) { fvdbg("Reformat command\n"); /* We cannot reformat the volume if there are any open inodes */ if (volume->ofiles) { fdbg("Open files\n"); ret = -EBUSY; goto errout_with_semaphore; } /* Re-format the volume -- all is lost */ ret = nxffs_reformat(volume); } else if (cmd == FIOC_OPTIMIZE) { fvdbg("Optimize command\n"); /* Pack the volume */ ret = nxffs_pack(volume); } else { /* No other commands supported */ ret = -ENOTTY; } errout_with_semaphore: sem_post(&volume->exclsem); errout: return ret; }
int rwb_invalidate_writebuffer(FAR struct rwbuffer_s *rwb, off_t startblock, size_t blockcount) { int ret; if (rwb->wrmaxblocks > 0 && wrnblocks > 0) { off_t wrbend; off_t invend; fvdbg("startblock=%d blockcount=%p\n", startblock, blockcount); rwb_semtake(&rwb->wrsem); /* Now there are five cases: * * 1. We invalidate nothing */ wrbend = rwb->wrblockstart + rwb->wrnblocks; invend = startblock + blockcount; if (rwb->wrblockstart > invend || wrbend < startblock) { ret = OK; } /* 2. We invalidate the entire write buffer. */ else if (rwb->wrblockstart >= startblock && wrbend <= invend) { rwb->wrnblocks = 0; ret = OK; } /* We are going to invalidate a subset of the write buffer. Three * more cases to consider: * * 2. We invalidate a portion in the middle of the write buffer */ else if (rwb->wrblockstart < startblock && wrbend > invend) { uint8_t *src; off_t block; off_t offset; size_t nblocks; /* Write the blocks at the end of the media to hardware */ nblocks = wrbend - invend; block = invend; offset = block - rwb->wrblockstart; src = rwb->wrbuffer + offset * rwb->blocksize; ret = rwb->wrflush(rwb->dev, block, nblocks, src); if (ret < 0) { fdbg("ERROR: wrflush failed: %d\n", ret); } /* Keep the blocks at the beginning of the buffer up the * start of the invalidated region. */ else { rwb->wrnblocks = startblock - rwb->wrblockstart; ret = OK; } } /* 3. We invalidate a portion at the end of the write buffer */ else if (wrbend > startblock && wrbend <= invend) { rwb->wrnblocks = wrbend - startblock; ret = OK; } /* 4. We invalidate a portion at the beginning of the write buffer */ else /* if (rwb->wrblockstart >= startblock && wrbend < invend) */ { uint8_t *src; size_t ninval; size_t nkeep; DEBUGASSERT(rwb->wrblockstart >= startblock && wrbend < invend); /* Copy the data from the uninvalidated region to the beginning * of the write buffer. * * First calculate the source and destination of the transfer. */ ninval = invend - rwb->wrblockstart; src = rwb->wrbuffer + ninval * rwb->blocksize; /* Calculate the number of blocks we are keeping. We keep * the ones that we don't invalidate. */ nkeep = rwb->wrnblocks - ninval; /* Then move the data that we are keeping to the beginning * the write buffer. */ memcpy(rwb->wrbuffer, src, nkeep * rwb->blocksize); /* Update the block info. The first block is now the one just * after the invalidation region and the number buffered blocks * is the number that we kept. */ rwb->wrblockstart = invend; rwb->wrnblocks = nkeep; ret = OK; } rwb_semgive(&rwb->wrsem); } return ret; }
static int rd_close(FAR struct inode *inode) { fvdbg("Entry\n"); return OK; }
int nxffs_close(FAR struct file *filep) { FAR struct nxffs_volume_s *volume; FAR struct nxffs_ofile_s *ofile; int ret; fvdbg("Closing\n"); /* Sanity checks */ DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); /* Recover the open file state from the struct file instance */ ofile = (FAR struct nxffs_ofile_s *)filep->f_priv; /* Recover the volume state from the open file */ volume = (FAR struct nxffs_volume_s *)filep->f_inode->i_private; DEBUGASSERT(volume != NULL); /* Get exclusive access to the volume. Note that the volume exclsem * protects the open file list. */ ret = sem_wait(&volume->exclsem); if (ret != OK) { ret = -get_errno(); fdbg("ERROR: sem_wait failed: %d\n", ret); goto errout; } /* Decrement the reference count on the open file */ ret = OK; if (ofile->crefs == 1) { /* Decrementing the reference count would take it zero. * * Remove the entry from the open file list. We do this early * to avoid some chick-and-egg problems with file truncation. */ nxffs_remofile(volume, ofile); /* Handle special finalization of the write operation. */ if ((ofile->oflags & O_WROK) != 0) { ret = nxffs_wrclose(volume, (FAR struct nxffs_wrfile_s *)ofile); } /* Release all resouces held by the open file */ nxffs_freeofile(volume, ofile); } else { /* Just decrement the reference count */ ofile->crefs--; } filep->f_priv = NULL; sem_post(&volume->exclsem); errout: return ret; }
FAR struct mtd_dev_s *mtd_rwb_initialize(FAR struct mtd_dev_s *mtd) { FAR struct mtd_rwbuffer_s *priv; struct mtd_geometry_s geo; int ret; fvdbg("mtd: %p\n", mtd); DEBUGASSERT(mtd && mtd->ioctl); /* Get the device geometry */ ret = mtd->ioctl(mtd, MTDIOC_GEOMETRY, (unsigned long)((uintptr_t)&geo)); if (ret < 0) { fdbg("ERROR: MTDIOC_GEOMETRY ioctl failed: %d\n", ret); return NULL; } /* Allocate a state structure (we allocate the structure instead of using * a fixed, static allocation so that we can handle multiple FLASH devices. * The current implementation would handle only one FLASH part per SPI * device (only because of the SPIDEV_FLASH definition) and so would have * to be extended to handle multiple FLASH parts on the same SPI bus. */ priv = (FAR struct mtd_rwbuffer_s *)kmm_zalloc(sizeof(struct mtd_rwbuffer_s)); if (priv) { /* Initialize the allocated structure. (unsupported methods/fields * were already nullified by kmm_zalloc). */ priv->mtd.erase = mtd_erase; /* Our MTD erase method */ priv->mtd.bread = mtd_bread; /* Our MTD bread method */ priv->mtd.bwrite = mtd_bwrite; /* Our MTD bwrite method */ priv->mtd.read = mtd_read; /* Our MTD read method */ priv->mtd.ioctl = mtd_ioctl; /* Our MTD ioctl method */ priv->dev = mtd; /* The contained MTD instance */ /* Sectors per block. The erase block size must be an even multiple * of the sector size. */ priv->spb = geo.erasesize / geo.blocksize; DEBUGASSERT((size_t)priv->spb * geo_blocksize = geo.erasesize); /* Values must be provided to rwb_initialize() */ /* Supported geometry */ priv->rwb.blocksize = geo.blocksize; priv->rwb.nblocks = geo.neraseblocks * priv->spb; /* Buffer setup */ #ifdef CONFIG_DRVR_WRITEBUFFER priv->rwb.wrmaxblocks = CONFIG_MTD_NWRBLOCKS; #endif #ifdef CONFIG_DRVR_READAHEAD priv->rwb.rhmaxblocks = CONFIG_MTD_NRDBLOCKS; #endif /* Callouts */ priv->rwb.dev = priv; /* Device state passed to callouts */ priv->rwb.wrflush = mtd_flush; /* Callout to flush buffer */ priv->rwb.rhreload = mtd_reload; /* Callout to reload buffer */ /* Initialize read-ahead/write buffering */ ret = rwb_initialize(&priv->rwb); if (ret < 0) { fdbg("ERROR: rwb_initialize failed: %d\n", ret); kmm_free(priv); return NULL; } } /* Register the MTD with the procfs system if enabled */ #ifdef CONFIG_MTD_REGISTRATION mtd_register(&priv->mtd, "rwbuffer"); #endif /* Return the implementation-specific state structure as the MTD device */ fvdbg("Return %p\n", priv); return &priv->mtd; }
static inline int nxffs_rdopen(FAR struct nxffs_volume_s *volume, FAR const char *name, FAR struct nxffs_ofile_s **ppofile) { FAR struct nxffs_ofile_s *ofile; int ret; /* Get exclusive access to the volume. Note that the volume exclsem * protects the open file list. */ ret = sem_wait(&volume->exclsem); if (ret != OK) { fdbg("ERROR: sem_wait failed: %d\n", ret); ret = -get_errno(); goto errout; } /* Check if the file has already been opened (for reading) */ ofile = nxffs_findofile(volume, name); if (ofile) { /* The file is already open. * Limitation: Files cannot be open both for reading and writing. */ if ((ofile->oflags & O_WROK) != 0) { fdbg("ERROR: File is open for writing\n"); ret = -ENOSYS; goto errout_with_exclsem; } /* Just increment the reference count on the ofile */ ofile->crefs++; fvdbg("crefs: %d\n", ofile->crefs); } /* The file has not yet been opened. * Limitation: The file must exist. We do not support creation of files * read-only. */ else { /* Not already open.. create a new open structure */ ofile = (FAR struct nxffs_ofile_s *)kmm_zalloc(sizeof(struct nxffs_ofile_s)); if (!ofile) { fdbg("ERROR: ofile allocation failed\n"); ret = -ENOMEM; goto errout_with_exclsem; } /* Initialize the open file state structure */ ofile->crefs = 1; ofile->oflags = O_RDOK; /* Find the file on this volume associated with this file name */ ret = nxffs_findinode(volume, name, &ofile->entry); if (ret != OK) { fvdbg("Inode '%s' not found: %d\n", name, -ret); goto errout_with_ofile; } /* Add the open file structure to the head of the list of open files */ ofile->flink = volume->ofiles; volume->ofiles = ofile; } /* Return the open file state structure */ *ppofile = ofile; sem_post(&volume->exclsem); return OK; errout_with_ofile: kmm_free(ofile); errout_with_exclsem: sem_post(&volume->exclsem); errout: return ret; }
static int mtd_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg) { FAR struct mtd_rwbuffer_s *priv = (FAR struct mtd_rwbuffer_s *)dev; int ret = -EINVAL; /* Assume good command with bad parameters */ fvdbg("cmd: %d \n", cmd); switch (cmd) { case MTDIOC_GEOMETRY: { FAR struct mtd_geometry_s *geo = (FAR struct mtd_geometry_s *)((uintptr_t)arg); if (geo) { /* Populate the geometry structure with information need to know * the capacity and how to access the device. * * NOTE: that the device is treated as though it where just an array * of fixed size blocks. That is most likely not true, but the client * will expect the device logic to do whatever is necessary to make it * appear so. */ geo->blocksize = priv->rwb.blocksize; geo->erasesize = priv->rwb.blocksize* priv->spb; geo->neraseblocks = priv->rwb.nblocks * priv->spb; ret = OK; fvdbg("blocksize: %d erasesize: %d neraseblocks: %d\n", geo->blocksize, geo->erasesize, geo->neraseblocks); } } break; case MTDIOC_BULKERASE: { /* Erase the entire device */ ret = priv->dev->ioctl(priv->dev, MTDIOC_BULKERASE, 0); if (ret >= 0) { fdbg("ERROR: Device ioctl failed: %d\n", ret); break; } /* Then invalidate in cached data */ ret = rwb_invalidate(&priv->rwb,0, priv->rwb.nblocks); if (ret < 0) { fdbg("ERROR: rwb_invalidate failed: %d\n", ret); } } break; case MTDIOC_XIPBASE: default: ret = -ENOTTY; /* Bad command */ break; } fvdbg("return %d\n", ret); return ret; }
int rwb_invalidate_readahead(FAR struct rwbuffer_s *rwb, off_t startblock, size_t blockcount) { int ret; if (rwb->rhmaxblocks > 0 && rhnblocks > 0) { off_t rhbend; off_t invend; fvdbg("startblock=%d blockcount=%p\n", startblock, blockcount); rwb_semtake(&rwb->rhsem); /* Now there are five cases: * * 1. We invalidate nothing */ rhbend = rwb->rhblockstart + rwb->rhnblocks; invend = startblock + blockcount; if (rwb->rhblockstart > invend || rhbend < startblock) { ret = OK; } /* 2. We invalidate the entire read-ahead buffer. */ else if (rwb->rhblockstart >= startblock && rhbend <= invend) { rwb->rhnblocks = 0; ret = OK; } /* We are going to invalidate a subset of the read-ahead buffer. * Three more cases to consider: * * 2. We invalidate a portion in the middle of the write buffer */ else if (rwb->rhblockstart < startblock && rhbend > invend) { /* Keep the blocks at the beginning of the buffer up the * start of the invalidated region. */ rwb->rhnblocks = startblock - rwb->rhblockstart; ret = OK; } /* 3. We invalidate a portion at the end of the read-ahead buffer */ else if (rhbend > startblock && rhbend <= invend) { rwb->rhnblocks = rhbend - startblock; ret = OK; } /* 4. We invalidate a portion at the beginning of the write buffer */ else /* if (rwb->rhblockstart >= startblock && rhbend < invend) */ { /* Let's just force the whole read-ahead buffer to be reloaded. * That might cost s small amount of performance, but well worth * the lower complexity. */ DEBUGASSERT(rwb->rhblockstart >= startblock && rhbend < invend); rwb->rhnblocks = 0; ret = OK; } rwb_semgive(&rwb->rhsem); } return ret; }
static ssize_t uptime_read(FAR struct file *filep, FAR char *buffer, size_t buflen) { FAR struct uptime_file_s *attr; size_t linesize; off_t offset; ssize_t ret; #ifdef CONFIG_SYSTEM_TIME64 uint64_t ticktime; #if !defined(CONFIG_HAVE_DOUBLE) || !defined(CONFIG_LIBC_FLOATINGPOINT) uint64_t sec; #endif #else uint32_t ticktime; #if !defined(CONFIG_HAVE_DOUBLE) || !defined(CONFIG_LIBC_FLOATINGPOINT) uint32_t sec; #endif #endif #if defined(CONFIG_HAVE_DOUBLE) && defined(CONFIG_LIBC_FLOATINGPOINT) double now; #else unsigned int remainder; unsigned int csec; #endif fvdbg("buffer=%p buflen=%d\n", buffer, (int)buflen); /* Recover our private data from the struct file instance */ attr = (FAR struct uptime_file_s *)filep->f_priv; DEBUGASSERT(attr); /* If f_pos is zero, then sample the system time. Otherwise, use * the cached system time from the previous read(). It is necessary * save the cached value in case, for example, the user is reading * the time one byte at a time. In that case, the time must remain * stable throughout the reads. */ if (filep->f_pos == 0) { #ifdef CONFIG_SYSTEM_TIME64 /* 64-bit timer */ ticktime = clock_systimer64(); #else /* 32-bit timer */ ticktime = clock_systimer(); #endif #if defined(CONFIG_HAVE_DOUBLE) && defined(CONFIG_LIBC_FLOATINGPOINT) /* Convert the system up time to a seconds + hundredths of seconds string */ now = (double)ticktime / (double)CLOCKS_PER_SEC; linesize = snprintf(attr->line, UPTIME_LINELEN, "%10.2f\n", now); #else /* Convert the system up time to seconds + hundredths of seconds */ sec = ticktime / CLOCKS_PER_SEC; remainder = (unsigned int)(ticktime % CLOCKS_PER_SEC); csec = (100 * remainder + (CLOCKS_PER_SEC / 2)) / CLOCKS_PER_SEC; /* Make sure that rounding did not force the hundredths of a second above 99 */ if (csec > 99) { sec++; csec -= 100; } /* Convert the seconds + hundredths of seconds to a string */ linesize = snprintf(attr->line, UPTIME_LINELEN, "%7lu.%02u\n", sec, csec); #endif /* Save the linesize in case we are re-entered with f_pos > 0 */ attr->linesize = linesize; } /* Transfer the system up time to user receive buffer */ offset = filep->f_pos; ret = procfs_memcpy(attr->line, attr->linesize, buffer, buflen, &offset); /* Update the file offset */ if (ret > 0) { filep->f_pos += ret; } return ret; }