root/src/add-ons/kernel/drivers/disk/norflash/norflash.cpp
/*
 * Copyright 2012, Haiku, Inc.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Ithamar R. Adema <ithamar@upgrade-android.com>
 */


#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>

#include <drivers/device_manager.h>
#include <drivers/KernelExport.h>
#include <drivers/Drivers.h>
#include <kernel/OS.h>


//#define TRACE_NORFLASH
#ifdef TRACE_NORFLASH
#define TRACE(x...)     dprintf("nor: " x)
#else
#define TRACE(x...)
#endif


#define NORFLASH_DEVICE_MODULE_NAME     "drivers/disk/norflash/device_v1"
#define NORFLASH_DRIVER_MODULE_NAME     "drivers/disk/norflash/driver_v1"


#define NORFLASH_ADDR   0x00000000
#define SIZE_IN_BLOCKS  256

/* Hide the start of NOR since U-Boot lives there */
#define HIDDEN_BLOCKS   2

struct nor_driver_info {
        device_node *node;
        size_t blocksize;
        size_t totalsize;

        area_id id;
        uint8 *mapped;
};


static device_manager_info *sDeviceManager;


static status_t
nor_init_device(void *_info, void **_cookie)
{
        TRACE("init_device\n");
        nor_driver_info *info = (nor_driver_info*)_info;

        info->mapped = NULL;
        info->blocksize = 128 * 1024;
        info->totalsize = (SIZE_IN_BLOCKS - HIDDEN_BLOCKS) * info->blocksize;

        info->id = map_physical_memory("NORFlash", NORFLASH_ADDR, info->totalsize, B_ANY_KERNEL_ADDRESS, B_READ_AREA, (void **)&info->mapped);
        if (info->id < 0)
                return info->id;

        info->mapped += HIDDEN_BLOCKS * info->blocksize;


        *_cookie = info;
        return B_OK;
}


static void
nor_uninit_device(void *_cookie)
{
        TRACE("uninit_device\n");
        nor_driver_info *info = (nor_driver_info*)_cookie;
        if (info)
                delete_area(info->id);  
}


static status_t
nor_open(void *deviceCookie, const char *path, int openMode,
        void **_cookie)
{
        TRACE("open(%s)\n", path);
        *_cookie = deviceCookie;
        return B_OK;
}


static status_t
nor_close(void *_cookie)
{
        TRACE("close()\n");
        return B_OK;
}


static status_t
nor_free(void *_cookie)
{
        TRACE("free()\n");
        return B_OK;
}


static status_t
nor_ioctl(void *cookie, uint32 op, void *buffer, size_t length)
{
        nor_driver_info *info = (nor_driver_info*)cookie;
        TRACE("ioctl(%ld,%lu)\n", op, length);

        switch (op) {
                case B_GET_GEOMETRY:
                {
                        device_geometry *deviceGeometry = (device_geometry*)buffer;
                        deviceGeometry->removable = false;
                        deviceGeometry->bytes_per_sector = info->blocksize;
                        deviceGeometry->sectors_per_track = info->totalsize / info->blocksize;
                        deviceGeometry->cylinder_count = 1;
                        deviceGeometry->head_count = 1;
                        deviceGeometry->device_type = B_DISK;
                        deviceGeometry->removable = false;
                        deviceGeometry->read_only = true;
                        deviceGeometry->write_once = false;
                        return B_OK;
                }
                break;

                case B_GET_DEVICE_NAME:
                        strlcpy((char*)buffer, "NORFlash", length);
                        break;
        }

        return B_ERROR;
}


static status_t
nor_read(void *_cookie, off_t position, void *data, size_t *numbytes)
{
        nor_driver_info *info = (nor_driver_info*)_cookie;
        TRACE("read(%lld,%lu)\n", position, *numbytes);

        position += HIDDEN_BLOCKS * info->blocksize;

        if (position + *numbytes > info->totalsize)
                *numbytes = info->totalsize - (position + *numbytes);

        memcpy(data, info->mapped + position, *numbytes);

        return B_OK;
}


static status_t
nor_write(void *_cookie, off_t position, const void *data, size_t *numbytes)
{
        TRACE("write(%lld,%lu)\n", position, *numbytes);
        *numbytes = 0;
        return B_ERROR;
}


static float
nor_supports_device(device_node *parent)
{
        const char *bus;
        TRACE("supports_device\n");

        if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
                return B_ERROR;

        if (strcmp(bus, "generic"))
                return 0.0;

        return 1.0;
}


static status_t
nor_register_device(device_node *node)
{
        TRACE("register_device\n");
        // ready to register
        device_attr attrs[] = {
                { NULL }
        };

        return sDeviceManager->register_node(node, NORFLASH_DRIVER_MODULE_NAME,
                attrs, NULL, NULL);
}


static status_t
nor_init_driver(device_node *node, void **cookie)
{
        TRACE("init_driver\n");

        nor_driver_info *info = (nor_driver_info*)malloc(sizeof(nor_driver_info));
        if (info == NULL)
                return B_NO_MEMORY;

        memset(info, 0, sizeof(*info));

        info->node = node;

        *cookie = info;
        return B_OK;
}


static void
nor_uninit_driver(void *_cookie)
{
        TRACE("uninit_driver\n");
        nor_driver_info *info = (nor_driver_info*)_cookie;
        free(info);
}


static status_t
nor_register_child_devices(void *_cookie)
{
        TRACE("register_child_devices\n");
        nor_driver_info *info = (nor_driver_info*)_cookie;
        status_t status;

        status = sDeviceManager->publish_device(info->node, "disk/nor/0/raw",
                NORFLASH_DEVICE_MODULE_NAME);

        return status;
}


struct device_module_info sNORFlashDiskDevice = {
        {
                NORFLASH_DEVICE_MODULE_NAME,
                0,
                NULL
        },

        nor_init_device,
        nor_uninit_device,
        NULL, //nor_remove,

        nor_open,
        nor_close,
        nor_free,
        nor_read,
        nor_write,
        NULL,   // nor_io,
        nor_ioctl,

        NULL,   // select
        NULL,   // deselect
};



struct driver_module_info sNORFlashDiskDriver = {
        {
                NORFLASH_DRIVER_MODULE_NAME,
                0,
                NULL
        },

        nor_supports_device,
        nor_register_device,
        nor_init_driver,
        nor_uninit_driver,
        nor_register_child_devices,
        NULL,   // rescan
        NULL,   // removed
};


module_dependency module_dependencies[] = {
        { B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager },
        { }
};


module_info *modules[] = {
        (module_info*)&sNORFlashDiskDriver,
        (module_info*)&sNORFlashDiskDevice,
        NULL
};