| /**************************************************************************** |
| * fs/driver/fs_blockpartition.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 <nuttx/config.h> |
| |
| #include <errno.h> |
| #include <sys/mount.h> |
| #include <sys/stat.h> |
| |
| #include <nuttx/fs/fs.h> |
| #include <nuttx/fs/ioctl.h> |
| #include <nuttx/mtd/mtd.h> |
| #include <nuttx/kmalloc.h> |
| |
| #include "driver/driver.h" |
| #include "inode/inode.h" |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| struct part_struct_s |
| { |
| FAR struct inode *parent; |
| size_t sectorsize; |
| off_t firstsector; |
| off_t nsectors; |
| }; |
| |
| /**************************************************************************** |
| * Private Function Prototypes |
| ****************************************************************************/ |
| |
| static int part_open(FAR struct inode *inode); |
| static int part_close(FAR struct inode *inode); |
| static ssize_t part_read(FAR struct inode *inode, FAR unsigned char *buffer, |
| blkcnt_t start_sector, unsigned int nsectors); |
| static ssize_t part_write(FAR struct inode *inode, |
| FAR const unsigned char *buffer, blkcnt_t start_sector, |
| unsigned int nsectors); |
| static int part_geometry(FAR struct inode *inode, |
| FAR struct geometry *geometry); |
| static int part_ioctl(FAR struct inode *inode, int cmd, |
| unsigned long arg); |
| #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS |
| static int part_unlink(FAR struct inode *inode); |
| #endif |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| static const struct block_operations g_part_bops = |
| { |
| part_open, /* open */ |
| part_close, /* close */ |
| part_read, /* read */ |
| part_write, /* write */ |
| part_geometry, /* geometry */ |
| part_ioctl /* ioctl */ |
| #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS |
| , part_unlink /* unlink */ |
| #endif |
| }; |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: part_open |
| * |
| * Description: Open the block device |
| * |
| ****************************************************************************/ |
| |
| static int part_open(FAR struct inode *inode) |
| { |
| FAR struct part_struct_s *dev = inode->i_private; |
| FAR struct inode *parent = dev->parent; |
| int ret = OK; |
| |
| /* Open the parent block device */ |
| |
| if (parent->u.i_bops->open) |
| { |
| ret = parent->u.i_bops->open(parent); |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: part_close |
| * |
| * Description: close the block device |
| * |
| ****************************************************************************/ |
| |
| static int part_close(FAR struct inode *inode) |
| { |
| FAR struct part_struct_s *dev = inode->i_private; |
| FAR struct inode *parent = dev->parent; |
| int ret = OK; |
| |
| if (parent->u.i_bops->close) |
| { |
| ret = parent->u.i_bops->close(parent); |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: part_read |
| * |
| * Description: Read the specified number of sectors |
| * |
| ****************************************************************************/ |
| |
| static ssize_t part_read(FAR struct inode *inode, unsigned char *buffer, |
| blkcnt_t start_sector, unsigned int nsectors) |
| { |
| FAR struct part_struct_s *dev = inode->i_private; |
| FAR struct inode *parent = dev->parent; |
| |
| if (start_sector + nsectors > dev->nsectors) |
| { |
| nsectors = dev->nsectors - start_sector; |
| } |
| |
| start_sector += dev->firstsector; |
| |
| return parent->u.i_bops->read(parent, buffer, start_sector, nsectors); |
| } |
| |
| /**************************************************************************** |
| * Name: part_write |
| * |
| * Description: Write (or buffer) the specified number of sectors |
| * |
| ****************************************************************************/ |
| |
| static ssize_t part_write(FAR struct inode *inode, |
| FAR const unsigned char *buffer, |
| blkcnt_t start_sector, unsigned int nsectors) |
| { |
| FAR struct part_struct_s *dev = inode->i_private; |
| FAR struct inode *parent = dev->parent; |
| |
| if (start_sector + nsectors > dev->nsectors) |
| { |
| nsectors = dev->nsectors - start_sector; |
| } |
| |
| start_sector += dev->firstsector; |
| |
| return parent->u.i_bops->write(parent, buffer, start_sector, nsectors); |
| } |
| |
| /**************************************************************************** |
| * Name: part_geometry |
| * |
| * Description: Return device geometry |
| * |
| ****************************************************************************/ |
| |
| static int part_geometry(FAR struct inode *inode, struct geometry *geometry) |
| { |
| FAR struct part_struct_s *dev = inode->i_private; |
| FAR struct inode *parent = dev->parent; |
| int ret; |
| |
| ret = parent->u.i_bops->geometry(parent, geometry); |
| if (ret >= 0) |
| { |
| geometry->geo_nsectors = dev->nsectors; |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: part_ioctl |
| * |
| * Description: Return device geometry |
| * |
| ****************************************************************************/ |
| |
| static int part_ioctl(FAR struct inode *inode, int cmd, unsigned long arg) |
| { |
| FAR uintptr_t ptr_arg = (uintptr_t)arg; |
| FAR struct part_struct_s *dev = inode->i_private; |
| FAR struct inode *parent = dev->parent; |
| int ret = -ENOTTY; |
| |
| switch (cmd) |
| { |
| case BIOC_PARTINFO: |
| { |
| FAR struct partition_info_s *info = |
| (FAR struct partition_info_s *)ptr_arg; |
| if (info != NULL) |
| { |
| info->numsectors = dev->nsectors; |
| info->sectorsize = dev->sectorsize; |
| info->startsector = dev->firstsector; |
| |
| strlcpy(info->parent, dev->parent->i_name, |
| sizeof(info->parent)); |
| |
| ret = OK; |
| } |
| } |
| break; |
| |
| default: |
| if (parent->u.i_bops->ioctl) |
| { |
| if (cmd == MTDIOC_PROTECT || cmd == MTDIOC_UNPROTECT) |
| { |
| FAR struct mtd_protect_s *prot = |
| (FAR struct mtd_protect_s *)ptr_arg; |
| |
| prot->startblock += dev->firstsector; |
| } |
| |
| ret = parent->u.i_bops->ioctl(parent, cmd, arg); |
| if (ret >= 0) |
| { |
| if (cmd == BIOC_XIPBASE) |
| { |
| FAR void **base = (FAR void **)ptr_arg; |
| |
| *(FAR uint8_t *)base += |
| dev->firstsector * dev->sectorsize; |
| } |
| else if (cmd == MTDIOC_GEOMETRY) |
| { |
| FAR struct mtd_geometry_s *mgeo = |
| (FAR struct mtd_geometry_s *)ptr_arg; |
| uint32_t blkper = mgeo->erasesize / mgeo->blocksize; |
| |
| mgeo->neraseblocks = dev->nsectors / blkper; |
| } |
| } |
| } |
| break; |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: part_unlink |
| ****************************************************************************/ |
| |
| #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS |
| static int part_unlink(FAR struct inode *inode) |
| { |
| FAR struct part_struct_s *dev = inode->i_private; |
| FAR struct inode *parent = dev->parent; |
| |
| inode_release(parent); |
| kmm_free(dev); |
| |
| return OK; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: register_blockpartition/register_partition_with_inode |
| * |
| * Description: |
| * Register a block partition driver inode the pseudo file system. |
| * |
| * Input Parameters: |
| * partition - The path to the partition inode |
| * parent - The parent path or inode |
| * firstsector - The offset in sectors to the partition |
| * nsectors - The number of sectors in the partition |
| * |
| * Returned Value: |
| * Zero on success (with the inode point in 'inode'); A negated errno |
| * value is returned on a failure (all error values returned by |
| * inode_reserve): |
| * |
| * EINVAL - 'path' is invalid for this operation |
| * EEXIST - An inode already exists at 'path' |
| * ENOMEM - Failed to allocate in-memory resources for the operation |
| * |
| ****************************************************************************/ |
| |
| int register_partition_with_inode(FAR const char *partition, |
| mode_t mode, FAR struct inode *parent, |
| off_t firstsector, off_t nsectors) |
| { |
| FAR struct part_struct_s *dev; |
| struct geometry geo; |
| int ret; |
| |
| if (parent == NULL) |
| { |
| return -EINVAL; |
| } |
| |
| /* Allocate a partition device structure */ |
| |
| dev = kmm_zalloc(sizeof(*dev)); |
| if (dev == NULL) |
| { |
| return -ENOMEM; |
| } |
| |
| inode_addref(parent); |
| dev->parent = parent; |
| dev->firstsector = firstsector; |
| dev->nsectors = nsectors; |
| |
| /* Get sector size */ |
| |
| ret = parent->u.i_bops->geometry(parent, &geo); |
| if (ret < 0) |
| { |
| goto errout_free; |
| } |
| |
| dev->sectorsize = geo.geo_sectorsize; |
| |
| /* Inode private data is a reference to the partition device structure */ |
| |
| ret = register_blockdriver(partition, &g_part_bops, mode, dev); |
| if (ret < 0) |
| { |
| goto errout_free; |
| } |
| |
| return OK; |
| |
| errout_free: |
| inode_release(parent); |
| kmm_free(dev); |
| return ret; |
| } |
| |
| int register_blockpartition(FAR const char *partition, |
| mode_t mode, FAR const char *parent, |
| off_t firstsector, off_t nsectors) |
| { |
| FAR struct inode *inode; |
| int ret; |
| |
| /* Find the block driver */ |
| |
| if (mode & (S_IWOTH | S_IWGRP | S_IWUSR)) |
| { |
| ret = find_blockdriver(parent, 0, &inode); |
| } |
| else |
| { |
| ret = find_blockdriver(parent, MS_RDONLY, &inode); |
| } |
| |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| ret = register_partition_with_inode(partition, mode, |
| inode, firstsector, nsectors); |
| inode_release(inode); |
| |
| return ret; |
| } |