Oopenvela-robotsem: inline semaphore
7195c1f8创建于 4月21日历史提交
/****************************************************************************
 * fs/driver/fs_blockmerge.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 <sys/types.h>
#include <sys/mount.h>
#include <sys/param.h>
#include <debug.h>
#include <nuttx/kmalloc.h>
#include <stdarg.h>
#include <fcntl.h>
#include <assert.h>
#include <errno.h>
#include <inttypes.h>

#include <nuttx/fs/fs.h>
#include <nuttx/fs/partition.h>

/****************************************************************************
 * Private Types
 ****************************************************************************/

struct merge_part_s
{
  FAR struct inode *inode;
  struct geometry geo;
  char path[PATH_MAX];
};

struct merge_priv_s
{
  size_t npart;
  struct merge_part_s part[0];
};

/****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

static int     merge_open(FAR struct inode *inode);
static int     merge_close(FAR struct inode *inode);
static ssize_t merge_read(FAR struct inode *inode, FAR unsigned char *buffer,
                          blkcnt_t startsector, unsigned int nsectors);
static ssize_t merge_write(FAR struct inode *inode,
                           FAR const unsigned char *buffer,
                           blkcnt_t startsector, unsigned int nsectors);
static int     merge_geometry(FAR struct inode *inode,
                              FAR struct geometry *geometry);

/****************************************************************************
 * Private Data
 ****************************************************************************/

static const struct block_operations g_merge_bops =
{
  merge_open,     /* open     */
  merge_close,    /* close    */
  merge_read,     /* read     */
  merge_write,    /* write    */
  merge_geometry, /* geometry */
  NULL            /* ioctl    */
};

/****************************************************************************
 * Private Functions
 ****************************************************************************/

static int merge_open(FAR struct inode *inode)
{
  FAR struct merge_priv_s *priv;
  int ret = 0;
  size_t i;

  DEBUGASSERT(inode && inode->i_private);
  priv = inode->i_private;

  for (i = 0; i < priv->npart; i++)
    {
      ret = open_blockdriver(priv->part[i].path, 0, &inode);
      if (ret < 0)
        {
          ferr("ERROR: Failed to open block driver for %s\n",
               priv->part[i].path);
          goto err_with_open;
        }

      priv->part[i].inode = inode;

      if (inode->u.i_bops->geometry == NULL ||
          inode->u.i_bops->geometry(inode, &priv->part[i].geo) < 0 ||
          priv->part[i].geo.geo_sectorsize <= 0 ||
          priv->part[i].geo.geo_nsectors <= 0)
        {
          ferr("ERROR: Failed to get geometry for %s\n", priv->part[i].path);
          ret = -EINVAL;
          goto err_with_inode;
        }

      if (priv->part[0].geo.geo_sectorsize !=
          priv->part[i].geo.geo_sectorsize)
        {
          ferr("ERROR: Inconsistent sectorsize!\n");
          ret = -EINVAL;
          goto err_with_inode;
        }

      finfo("[%s] nsectors: %" PRIuOFF " sectorsize:%u\n",
            priv->part[i].path, priv->part[i].geo.geo_nsectors,
            priv->part[i].geo.geo_sectorsize);
    }

  return ret;

err_with_inode:
  close_blockdriver(priv->part[i].inode);
err_with_open:
  while (i--)
    {
      close_blockdriver(priv->part[i].inode);
    }

  return ret;
}

static int merge_close(FAR struct inode *inode)
{
  FAR struct merge_priv_s *priv;
  size_t i;

  DEBUGASSERT(inode && inode->i_private);
  priv = inode->i_private;

  for (i = 0; i < priv->npart; i++)
    {
      int ret = close_blockdriver(priv->part[i].inode);
      if (ret < 0)
        {
          ferr("ERROR: Failed to close block driver for %s\n",
               priv->part[i].path);
          return ret;
        }
    }

  return OK;
}

static ssize_t merge_read(FAR struct inode *inode, FAR unsigned char *buffer,
                          blkcnt_t startsector, unsigned int nsectors)
{
  FAR struct merge_priv_s *priv;
  ssize_t nread = 0;
  size_t ntotal = 0;
  size_t i;

  DEBUGASSERT(inode && inode->i_private);
  priv = inode->i_private;

  for (i = 0; nsectors > 0 && i < priv->npart; i++)
    {
      if ((startsector >= priv->part[i].geo.geo_nsectors))
        {
          startsector -= priv->part[i].geo.geo_nsectors;
          continue;
        }

      inode = priv->part[i].inode;
      nread = MIN(priv->part[i].geo.geo_nsectors - startsector, nsectors);
      nread = inode->u.i_bops->read(inode, buffer, startsector, nread);
      if (nread < 0)
        {
          break;
        }

      buffer += nread * priv->part[i].geo.geo_sectorsize;
      nsectors -= nread;
      ntotal += nread;
      startsector = 0;
    }

  /* On success, return the number of blocks read */

  return ntotal ? ntotal : nread;
}

static ssize_t merge_write(FAR struct inode *inode,
                           FAR const unsigned char *buffer,
                           blkcnt_t startsector, unsigned int nsectors)
{
  FAR struct merge_priv_s *priv;
  ssize_t nwritten = 0;
  size_t ntotal = 0;
  size_t i;

  DEBUGASSERT(inode && inode->i_private);
  priv = inode->i_private;

  for (i = 0; nsectors > 0 && i < priv->npart; i++)
    {
      if (startsector >= priv->part[i].geo.geo_nsectors)
        {
          startsector -= priv->part[i].geo.geo_nsectors;
          continue;
        }

      inode = priv->part[i].inode;
      nwritten = MIN(priv->part[i].geo.geo_nsectors - startsector, nsectors);
      nwritten = inode->u.i_bops->write(inode, buffer, startsector,
                                        nwritten);
      if (nwritten < 0)
        {
          break;
        }

      buffer += nwritten * priv->part[i].geo.geo_sectorsize;
      nsectors -= nwritten;
      ntotal += nwritten;
      startsector = 0;
    }

  /* On success, return the number of blocks written */

  return ntotal ? ntotal : nwritten;
}

static int merge_geometry(FAR struct inode *inode,
                          FAR struct geometry *geometry)
{
  DEBUGASSERT(inode && inode->i_private);

  if (geometry)
    {
      FAR struct merge_priv_s *priv = inode->i_private;
      size_t i;

      memcpy(geometry, &priv->part[0].geo, sizeof(*geometry));
      for (i = 1; i < priv->npart; i++)
        {
          geometry->geo_nsectors += priv->part[i].geo.geo_nsectors;
        }

      finfo("nsectors: %" PRIuOFF " sectorsize:%u\n",
            geometry->geo_nsectors, geometry->geo_sectorsize);
      return OK;
    }

  return -EINVAL;
}

/****************************************************************************
 * Public Functions
 ****************************************************************************/

int register_merge_blockdriver(FAR const char *merge, ...)
{
  FAR struct merge_priv_s *priv;
  FAR const char *name;
  va_list args;
  size_t i;
  int ret;

  va_start(args, merge);
  for (i = 0; ; i++)
    {
      name = va_arg(args, FAR const char *);
      if (name == NULL)
        {
          break;
        }
    }

  va_end(args);

  if (i < 2)
    {
      ferr("ERROR: invalid number of partitions\n");
      return -EINVAL;
    }

  priv = kmm_zalloc(sizeof(struct merge_priv_s) + i *
                    sizeof(struct merge_part_s));
  if (priv == NULL)
    {
      ferr("ERROR: out of memory\n");
      return -ENOMEM;
    }

  priv->npart = i;

  va_start(args, merge);
  for (i = 0; i < priv->npart; i++)
    {
      name = va_arg(args, FAR const char *);
      strlcpy(priv->part[i].path, name, PATH_MAX);
    }

  ret = register_blockdriver(merge, &g_merge_bops, 0, priv);
  if (ret < 0)
    {
      ferr("ERROR: register blockdriver failed: %d\n", ret);
      goto err;
    }

  va_end(args);
  return ret;

err:
  va_end(args);
  kmm_free(priv);
  return ret;
}