blob: b2613dde515f66c559440dea314506fa25930269 [file] [log] [blame]
/****************************************************************************
* arch/arm/src/lc823450/lc823450_mmcl.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/ioctl.h>
#include <inttypes.h>
#include <stdio.h>
#include <assert.h>
#include <debug.h>
#include <errno.h>
#include <nuttx/kmalloc.h>
#include <nuttx/fs/fs.h>
#include <nuttx/fs/ioctl.h>
#include <nuttx/mtd/mtd.h>
#include "lc823450_mmcl.h"
/****************************************************************************
* Private Definitions
****************************************************************************/
/****************************************************************************
* Private Types
****************************************************************************/
struct mmcl_dev_s
{
#ifdef CONFIG_FS_EVFAT
uint32_t channel; /* 0: eMMC, 1: SD */
#endif
struct mtd_dev_s *mtd; /* Contained MTD interface */
struct mtd_geometry_s geo; /* Device geometry */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static int mmcl_open(struct inode *inode);
static int mmcl_close(struct inode *inode);
static ssize_t mmcl_read(struct inode *inode, unsigned char *buffer,
blkcnt_t start_sector, unsigned int nsectors);
static ssize_t mmcl_write(struct inode *inode,
const unsigned char *buffer, blkcnt_t start_sector,
unsigned int nsectors);
static int mmcl_geometry(struct inode *inode,
struct geometry *geometry);
static int mmcl_ioctl(struct inode *inode, int cmd,
unsigned long arg);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct block_operations g_bops =
{
mmcl_open, /* open */
mmcl_close, /* close */
mmcl_read, /* read */
mmcl_write, /* write */
mmcl_geometry, /* geometry */
mmcl_ioctl /* ioctl */
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: mmcl_open
*
* Description: Open the block device
*
****************************************************************************/
static int mmcl_open(struct inode *inode)
{
finfo("Entry\n");
return OK;
}
/****************************************************************************
* Name: mmcl_close
*
* Description: close the block device
*
****************************************************************************/
static int mmcl_close(struct inode *inode)
{
finfo("Entry\n");
return OK;
}
/****************************************************************************
* Name: mmcl_read
*
* Description: Read the specified number of sectors
*
****************************************************************************/
static ssize_t mmcl_read(struct inode *inode, unsigned char *buffer,
blkcnt_t start_sector, unsigned int nsectors)
{
ssize_t nread;
struct mmcl_dev_s *dev;
finfo("sector: %" PRIuOFF " nsectors: %u\n", start_sector, nsectors);
DEBUGASSERT(inode->i_private);
dev = inode->i_private;
nread = MTD_BREAD(dev->mtd, start_sector, nsectors, buffer);
if (nread != nsectors)
{
finfo("Read %u blocks starting at block %" PRIuOFF " failed: %d\n",
nsectors, start_sector, nread);
return -EIO;
}
return nread;
}
/****************************************************************************
* Name: mmcl_write
*
* Description: Write the specified number of sectors
*
****************************************************************************/
static ssize_t mmcl_write(struct inode *inode,
const unsigned char *buffer, blkcnt_t start_sector,
unsigned int nsectors)
{
ssize_t nwrite;
struct mmcl_dev_s *dev;
finfo("sector: %" PRIuOFF " nsectors: %u\n", start_sector, nsectors);
DEBUGASSERT(inode->i_private);
dev = inode->i_private;
nwrite = MTD_BWRITE(dev->mtd, start_sector, nsectors, buffer);
if (nwrite != nsectors)
{
finfo("Write %u blocks starting at block %" PRIuOFF " failed: %d\n",
nsectors, start_sector, nwrite);
return -EIO;
}
return nwrite;
}
/****************************************************************************
* Name: mmcl_geometry
*
* Description: Return device geometry
*
****************************************************************************/
static int mmcl_geometry(struct inode *inode, struct geometry *geometry)
{
struct mmcl_dev_s *dev;
finfo("Entry\n");
if (geometry)
{
dev = inode->i_private;
memset(geometry, 0, sizeof(*geometry));
geometry->geo_available = true;
geometry->geo_mediachanged = false;
geometry->geo_writeenabled = true;
geometry->geo_nsectors = dev->geo.neraseblocks;
geometry->geo_sectorsize = dev->geo.blocksize;
finfo("available: true mediachanged: false writeenabled: %s\n",
geometry->geo_writeenabled ? "true" : "false");
finfo("nsectors: %" PRIuOFF " sectorsize: %" PRIi16 "\n",
geometry->geo_nsectors, geometry->geo_sectorsize);
return OK;
}
return -EINVAL;
}
/****************************************************************************
* Name: mmcl_ioctl
*
* Description: Return device geometry
*
****************************************************************************/
static int mmcl_ioctl(struct inode *inode, int cmd, unsigned long arg)
{
struct mmcl_dev_s *dev ;
int ret;
finfo("Entry\n");
DEBUGASSERT(inode->i_private);
dev = inode->i_private;
ret = MTD_IOCTL(dev->mtd, cmd, arg);
if (ret < 0)
{
finfo("ERROR: MTD ioctl(%04x) failed: %d\n", cmd, ret);
}
return ret;
}
/****************************************************************************
* Name: mmcl_allocdev
****************************************************************************/
static struct mmcl_dev_s *mmcl_allocdev(int number,
struct mtd_dev_s *mtd)
{
struct mmcl_dev_s *dev;
int ret;
/* Allocate a MMCL device structure */
dev = kmm_malloc(sizeof(struct mmcl_dev_s));
if (dev)
{
/* Initialize the MMCL device structure */
dev->mtd = mtd;
/* Get the device geometry. (casting to uintptr_t first eliminates
* complaints on some architectures where the sizeof long is different
* from the size of a pointer).
*/
ret = MTD_IOCTL(mtd, MTDIOC_GEOMETRY,
(unsigned long)((uintptr_t)&dev->geo));
if (ret < 0)
{
finfo("MTD ioctl(MTDIOC_GEOMETRY) failed: %d\n", ret);
kmm_free(dev);
return NULL;
}
}
return dev;
}
/****************************************************************************
* Name: mmcl_freedev
****************************************************************************/
static void mmcl_freedev(struct mmcl_dev_s *dev)
{
DEBUGASSERT(dev);
kmm_free(dev);
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: mmcl_initialize
*
* Description:
* Initialize to provide a block driver wrapper around an MTD interface
*
* Input Parameters:
* minor - The minor device number. The MTD block device will be
* registered as as /dev/mtdblockN where N is the minor number.
* mtd - The MTD device that supports the FLASH interface.
*
****************************************************************************/
int mmcl_initialize(int minor, struct mtd_dev_s *mtd)
{
struct mmcl_dev_s *dev;
const char *devname[CONFIG_MTD_DEV_MAX] =
{
CONFIG_MTD_DEVPATH0,
#if CONFIG_MTD_DEV_MAX > 1
CONFIG_MTD_DEVPATH1,
#endif
};
int ret = -ENOMEM;
/* Sanity check */
#ifdef CONFIG_DEBUG
if (minor < 0 || minor > 255 || !mtd)
{
return -EINVAL;
}
#endif
DEBUGASSERT(minor < CONFIG_MTD_DEV_MAX);
/* Allocate a MTD block device structure */
dev = mmcl_allocdev(minor, mtd);
DEBUGASSERT(dev);
#ifdef CONFIG_FS_EVFAT
dev->channel = minor;
#endif
/* Create a MTD block device name */
/* Inode private data is a reference to the MMCL device structure */
ret = register_blockdriver(devname[minor], &g_bops, 0, dev);
if (ret < 0)
{
finfo("register_blockdriver failed: %d\n", -ret);
kmm_free(dev);
}
return ret;
}
/****************************************************************************
* Name: mmcl_uninitialize
****************************************************************************/
int mmcl_uninitialize(const char *devname)
{
int ret;
struct inode *inode;
struct mmcl_dev_s *dev;
DEBUGASSERT(devname);
ret = open_blockdriver(devname, 0, &inode);
if (ret != OK)
{
finfo("open_blockdriver failed: %d\n", ret);
return ret;
}
dev = inode->i_private;
DEBUGASSERT(dev);
ret = close_blockdriver(inode);
if (ret != OK)
{
finfo("close_blockdriver failed: %d\n", ret);
}
ret = unregister_blockdriver(devname);
if (ret != OK)
{
finfo("unregister_blockdriver failed: %d\n", ret);
}
mmcl_freedev(dev);
return ret;
}
/****************************************************************************
* Name: mmcl_createpartition
****************************************************************************/
int mmcl_createpartition(int minor, int number, struct mtd_dev_s *mtd)
{
struct mmcl_dev_s *dev;
char devname[32];
int ret = -ENOMEM;
/* Sanity check */
#ifdef CONFIG_DEBUG
if (minor < 0 || minor > 255 || !mtd)
{
return -EINVAL;
}
if (number < 0 || number > 255 || !mtd)
{
return -EINVAL;
}
#endif
/* Allocate a MMCL device structure */
dev = mmcl_allocdev(minor, mtd);
DEBUGASSERT(dev);
#ifdef CONFIG_FS_EVFAT
dev->channel = minor;
#endif
/* Create a MTD block device name */
snprintf(devname, sizeof(devname), "/dev/mtdblock%dp%d", minor, number);
/* Inode private data is a reference to the MMCL device structure */
ret = register_blockdriver(devname, &g_bops, 0, dev);
if (ret < 0)
{
finfo("register_blockdriver failed: %d\n", -ret);
kmm_free(dev);
}
return ret;
}