d0b72e2590
The buflib memory allocator is handle based and can free and compact, move or resize memory on demand. This allows to effeciently allocate memory dynamically without an MMU, by avoiding fragmentation through memory compaction. This patch adds the buflib library to the core, along with convinience wrappers to omit the context parameter. Compaction is not yet enabled, but will be in a later patch. Therefore, this acts as a replacement for buffer_alloc/buffer_get_buffer() with the benifit of a debug menu. See buflib.h for some API documentation. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30380 a1c6a512-1295-4272-9138-f99709370657
542 lines
15 KiB
C
542 lines
15 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2008 by Maurus Cuelenaere
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#include "config.h"
|
|
#include "cpu.h"
|
|
#include "kernel.h"
|
|
#include "thread.h"
|
|
#include "system.h"
|
|
#include "power.h"
|
|
#include "panic.h"
|
|
#include "ata-target.h"
|
|
#include "dm320.h"
|
|
#include "ata.h"
|
|
#include "string.h"
|
|
#include "core_alloc.h"
|
|
#include "logf.h"
|
|
#include "ata-defines.h"
|
|
|
|
#undef ata_read_sectors
|
|
#undef ata_write_sectors
|
|
|
|
static void sleep_ms(int ms)
|
|
{
|
|
sleep(ms*HZ/1000);
|
|
}
|
|
|
|
void ide_power_enable(bool on)
|
|
{
|
|
/* Disabled until figured out what's wrong */
|
|
#if 0
|
|
int old_level = disable_irq_save();
|
|
if(on)
|
|
{
|
|
IO_GIO_BITSET0 = (1 << 14);
|
|
ata_reset();
|
|
}
|
|
else
|
|
IO_GIO_BITCLR0 = (1 << 14);
|
|
restore_irq(old_level);
|
|
#else
|
|
(void)on;
|
|
#endif
|
|
}
|
|
|
|
inline bool ide_powered()
|
|
{
|
|
#if 0
|
|
return (IO_GIO_BITSET0 & (1 << 14));
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
void ata_reset(void)
|
|
{
|
|
int old_level = disable_irq_save();
|
|
if(!ide_powered())
|
|
{
|
|
ide_power_enable(true);
|
|
sleep_ms(150);
|
|
}
|
|
else
|
|
{
|
|
IO_GIO_BITSET0 = (1 << 5);
|
|
IO_GIO_BITCLR0 = (1 << 3);
|
|
sleep_ms(1);
|
|
}
|
|
IO_GIO_BITCLR0 = (1 << 5);
|
|
sleep_ms(10);
|
|
IO_GIO_BITSET0 = (1 << 3);
|
|
while(!(ATA_COMMAND & STATUS_RDY))
|
|
sleep_ms(10);
|
|
restore_irq(old_level);
|
|
}
|
|
|
|
void ata_enable(bool on)
|
|
{
|
|
(void)on;
|
|
return;
|
|
}
|
|
|
|
bool ata_is_coldstart(void)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void ata_device_init(void)
|
|
{
|
|
IO_INTC_EINT1 |= INTR_EINT1_EXT2; /* enable GIO2 interrupt */
|
|
/* TODO: mimic OF inits... */
|
|
return;
|
|
}
|
|
|
|
void GIO2(void)
|
|
{
|
|
#ifdef DEBUG
|
|
logf("GIO2 interrupt...");
|
|
#endif
|
|
IO_INTC_IRQ1 = INTR_IRQ1_EXT2; /* Mask GIO2 interrupt */
|
|
return;
|
|
}
|
|
|
|
/*
|
|
---------------------------------------------------------------------------
|
|
CreativeFileSystem parsing/handling code
|
|
---------------------------------------------------------------------------
|
|
*/
|
|
|
|
#define VFAT_SECTOR_SIZE(x) ( (x)/0x8000 ) /* 1GB array requires 80kB of RAM */
|
|
|
|
extern int ata_read_sectors(IF_MD2(int drive,) unsigned long start, int count, void* buf);
|
|
extern int ata_write_sectors(IF_MD2(int drive,) unsigned long start, int count, const void* buf);
|
|
|
|
struct main_header
|
|
{
|
|
char mblk[4];
|
|
unsigned int sector_size;
|
|
long long disk_size;
|
|
struct partition_header
|
|
{
|
|
unsigned long end;
|
|
unsigned long start;
|
|
char name[8];
|
|
} partitions[31];
|
|
};
|
|
|
|
struct cfs_header
|
|
{
|
|
unsigned int unk;
|
|
unsigned int unk2;
|
|
unsigned int sector_size;
|
|
unsigned int unk4;
|
|
unsigned int unk5;
|
|
char identifier[4];
|
|
unsigned int first_inode;
|
|
unsigned int unk8;
|
|
unsigned int unk9;
|
|
unsigned int unk10;
|
|
unsigned int unk11;
|
|
};
|
|
|
|
struct cfs_inode
|
|
{
|
|
unsigned char magic[4];
|
|
unsigned int number;
|
|
unsigned int parent;
|
|
unsigned int unk;
|
|
unsigned int type;
|
|
unsigned int created_time;
|
|
unsigned int lastmodified_time;
|
|
unsigned int unk2;
|
|
unsigned int first_class_chain[12];
|
|
unsigned int unk3;
|
|
unsigned int unk4;
|
|
unsigned int second_class_chain_first_cluster;
|
|
unsigned int unk9;
|
|
unsigned int unk10;
|
|
unsigned int second_class_chain_second_cluster;
|
|
unsigned int unk11;
|
|
unsigned int unk12;
|
|
unsigned int unk13;
|
|
unsigned int filesize;
|
|
unsigned int serial_number;
|
|
unsigned int number_of_metadata_records;
|
|
};
|
|
|
|
struct cfs_direntry
|
|
{
|
|
unsigned char identifier[4];
|
|
unsigned int unk;
|
|
unsigned int items;
|
|
unsigned int unk2;
|
|
unsigned char maxlen[2];
|
|
unsigned char padding[202];
|
|
/* struct cfs_direntry_item _items[items]; */
|
|
};
|
|
struct cfs_direntry_item
|
|
{
|
|
unsigned int inode_number;
|
|
unsigned short strlen;
|
|
unsigned short bytesperchar;
|
|
char string[32];
|
|
};
|
|
|
|
static bool cfs_inited = false;
|
|
static unsigned long cfs_start;
|
|
#ifdef BOOTLOADER
|
|
static unsigned long *sectors;
|
|
#else
|
|
static int sectors_handle;
|
|
#endif
|
|
|
|
#define CFS_START ( ((hdr->partitions[1].start*hdr->sector_size) & ~0xFFFF) + 0x10000 )
|
|
#define CFS_CLUSTER2CLUSTER(x) ( (CFS_START/512)+((x)-1)*64 )
|
|
|
|
/* Limited version of UCS -> ASCII */
|
|
static char* ucs2letostring(unsigned char* s)
|
|
{
|
|
static char res[256];
|
|
int i;
|
|
|
|
for(i=0; (s[i] == 0 && s[i+1] == 0 && i<256); i++)
|
|
res[i] = s[i*2];
|
|
|
|
return (char*)&res;
|
|
}
|
|
|
|
static void cfs_init(void)
|
|
{
|
|
struct main_header *hdr;
|
|
struct cfs_header *cfs;
|
|
struct cfs_inode *root_inode, *vfat_inode, *inode;
|
|
struct cfs_direntry *root_direntry, *vfat_direntry;
|
|
struct cfs_direntry_item *root_direntry_items, *vfat_direntry_items;
|
|
unsigned int i, j, k, vfat_inode_nr=0, vfat_inodes_nr[10], vfat_sector_count;
|
|
unsigned char sector[512];
|
|
static unsigned int vfat_data[2][0x8000];
|
|
static unsigned char sector2[0x8000];
|
|
|
|
if(cfs_inited)
|
|
return;
|
|
|
|
/* Read MBLK */
|
|
_ata_read_sectors(0, 1, §or);
|
|
hdr = (struct main_header*)§or;
|
|
|
|
logf("CFS is at 0x%x [0x%x]", CFS_START, CFS_START/512);
|
|
|
|
/* Read CFS header */
|
|
_ata_read_sectors(CFS_START/512, 64, §or2);
|
|
cfs = (struct cfs_header*)§or2;
|
|
|
|
logf("First inode = 0x%x", cfs->first_inode);
|
|
|
|
/* Read root inode */
|
|
_ata_read_sectors(CFS_CLUSTER2CLUSTER(cfs->first_inode), 64, §or2);
|
|
root_inode = (struct cfs_inode*)§or2;
|
|
|
|
logf("Root inode = 0x%x", root_inode);
|
|
|
|
logf("0x%x 0x%x", CFS_CLUSTER2CLUSTER(root_inode->first_class_chain[0]), root_inode->first_class_chain[0]);
|
|
|
|
/* Read root inode's first sector */
|
|
_ata_read_sectors(CFS_CLUSTER2CLUSTER(root_inode->first_class_chain[0]), 64, §or2);
|
|
root_direntry = (struct cfs_direntry*)§or2;
|
|
root_direntry_items = (struct cfs_direntry_item*)(§or2+sizeof(struct cfs_direntry));
|
|
|
|
logf("0x%x", root_direntry->identifier);
|
|
|
|
logf("%d", root_direntry->items);
|
|
|
|
/* Search VFAT inode */
|
|
for(i=0; i < root_direntry->items; i++)
|
|
{
|
|
if(root_direntry_items[i].inode_number != 0)
|
|
{
|
|
//logf(" * [%s] at 0x%x", ucs2letostring(&root_direntry_items[i].string[0]), root_direntry_items[i].inode_number);
|
|
if(strcmp(ucs2letostring(&root_direntry_items[i].string[0]), "VFAT") == 0)
|
|
vfat_inode_nr = root_direntry_items[i].inode_number;
|
|
}
|
|
}
|
|
|
|
logf("VFAT inode = 0x%x", vfat_inode_nr);
|
|
|
|
if(vfat_inode_nr != 0)
|
|
{
|
|
/* Read VFAT inode */
|
|
_ata_read_sectors(CFS_CLUSTER2CLUSTER(vfat_inode_nr), 64, §or2);
|
|
vfat_inode = (struct cfs_inode*)§or2;
|
|
|
|
/* Read VFAT inode's first sector */
|
|
_ata_read_sectors(CFS_CLUSTER2CLUSTER(vfat_inode->first_class_chain[0]), 64, §or2);
|
|
vfat_direntry = (struct cfs_direntry*)§or2;
|
|
vfat_direntry_items = (struct cfs_direntry_item*)(§or2+sizeof(struct cfs_direntry));
|
|
|
|
/* Search for VFAT's subinodes */
|
|
for(i=0; i < vfat_direntry->items; i++)
|
|
{
|
|
logf(" * [%s] at 0x%x\n", ucs2letostring(&vfat_direntry_items[i].string[0]), vfat_direntry_items[i].inode_number);
|
|
if(i > 0)
|
|
vfat_inodes_nr[i-1] = vfat_direntry_items[i].inode_number;
|
|
}
|
|
|
|
/* Determine size of VFAT file */
|
|
_ata_read_sectors(CFS_CLUSTER2CLUSTER(vfat_inodes_nr[1]), 1, §or);
|
|
inode = (struct cfs_inode*)§or;
|
|
#ifndef BOOTLOADER
|
|
sectors_handle = core_alloc("ata sectors", VFAT_SECTOR_SIZE(inode->filesize));
|
|
unsigned long *sectors = core_get_data(sectors_handle);
|
|
#else
|
|
static unsigned long _sector[VFAT_SECTOR_SIZE(1024*1024*1024)]; /* 1GB guess */
|
|
sectors = _sector;
|
|
#endif
|
|
|
|
logf("VFAT file size: 0x%x", inode->filesize);
|
|
|
|
/* Clear data sectors */
|
|
memset(§ors, 0, VFAT_SECTOR_SIZE(inode->filesize)*sizeof(unsigned long));
|
|
|
|
/* Read all data sectors' addresses in memory */
|
|
vfat_sector_count = 0;
|
|
for(i=0; vfat_inodes_nr[i] != 0; i++)
|
|
{
|
|
_ata_read_sectors(CFS_CLUSTER2CLUSTER(vfat_inodes_nr[i]), 1, §or);
|
|
inode = (struct cfs_inode*)§or;
|
|
|
|
/* Read second & third class chain */
|
|
_ata_read_sectors(CFS_CLUSTER2CLUSTER(inode->second_class_chain_first_cluster), 64, &vfat_data[0]);
|
|
_ata_read_sectors(CFS_CLUSTER2CLUSTER(inode->second_class_chain_second_cluster), 64, &vfat_data[1]);
|
|
|
|
/* First class chain */
|
|
#ifndef BOOTLOADER
|
|
sectors = core_get_data(sectors_handle);
|
|
#endif
|
|
for(j=0; j<12; j++)
|
|
{
|
|
if( (inode->first_class_chain[j] & 0xFFFF) != 0xFFFF &&
|
|
inode->first_class_chain[j] != 0
|
|
)
|
|
sectors[vfat_sector_count++] = inode->first_class_chain[j];
|
|
}
|
|
|
|
/* Second class chain */
|
|
#ifndef BOOTLOADER
|
|
sectors = core_get_data(sectors_handle);
|
|
#endif
|
|
for(j=0; j<0x8000/4; j++)
|
|
{
|
|
if( (vfat_data[0][j] & 0xFFFF) != 0xFFFF &&
|
|
vfat_data[0][j] != 0
|
|
)
|
|
sectors[vfat_sector_count++] = vfat_data[0][j];
|
|
}
|
|
|
|
/* Third class chain */
|
|
for(j=0; j<0x8000/4; j++)
|
|
{
|
|
if( (vfat_data[1][j] & 0xFFFF) != 0xFFFF &&
|
|
vfat_data[1][j] != 0
|
|
)
|
|
{
|
|
memset(&vfat_data[0], 0, 0x8000*sizeof(unsigned int));
|
|
|
|
/* Read third class subchain(s) */
|
|
_ata_read_sectors(CFS_CLUSTER2CLUSTER(vfat_data[1][j]), 64, &vfat_data[0]);
|
|
|
|
#ifndef BOOTLOADER
|
|
sectors = core_get_data(sectors_handle);
|
|
#endif
|
|
for(k=0; k<0x8000/4; k++)
|
|
{
|
|
if( (vfat_data[0][k] & 0xFFFF) != 0xFFFF &&
|
|
vfat_data[0][k] != 0
|
|
)
|
|
sectors[vfat_sector_count++] = vfat_data[0][k];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
logf("Sector count: %d 0x%x", vfat_sector_count, vfat_sector_count);
|
|
}
|
|
else
|
|
panicf("Cannot find virtual FAT filesystem!");
|
|
|
|
cfs_inited = true;
|
|
}
|
|
|
|
static inline unsigned long map_sector(unsigned long sector)
|
|
{
|
|
/*
|
|
* Sector mapping: start of CFS + FAT_SECTOR2CFS_SECTOR(sector) + missing part
|
|
* FAT works with sectors of 0x200 bytes, CFS with sectors of 0x8000 bytes.
|
|
*/
|
|
#ifndef BOOTLOADER
|
|
unsigned long *sectors = core_get_data(sectors_handle);
|
|
#endif
|
|
return cfs_start+sectors[sector/64]*64+sector%64;
|
|
}
|
|
|
|
int ata_read_sectors(IF_MD2(int drive,) unsigned long start, int count, void* buf)
|
|
{
|
|
if(!cfs_inited)
|
|
cfs_init();
|
|
|
|
/* Check if count is lesser than or equal to 1 native CFS sector */
|
|
if(count <= 64)
|
|
return _ata_read_sectors(IF_MD2(drive,) map_sector(start), count, buf);
|
|
else
|
|
{
|
|
int i;
|
|
unsigned char* dest = (unsigned char*)buf;
|
|
|
|
/* Read sectors in parts of 0x8000 */
|
|
for(i=0; i<count; i+=64)
|
|
{
|
|
int ret = _ata_read_sectors(IF_MD2(drive,) map_sector(start+i), (count-i >= 64 ? 64 : count-i), (void*)dest);
|
|
if(ret != 0)
|
|
return ret;
|
|
|
|
dest += (count-i >= 64 ? 0x8000 : (count-i)*512);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int ata_write_sectors(IF_MD2(int drive,) unsigned long start, int count, const void* buf)
|
|
{
|
|
if(!cfs_inited)
|
|
cfs_init();
|
|
|
|
#if 0 /* Disabled for now */
|
|
/* Check if count is lesser than or equal to 1 native CFS sector */
|
|
if(count <= 64)
|
|
return _ata_write_sectors(IF_MD2(drive,) map_sector(start), count, buf);
|
|
else
|
|
{
|
|
int i, ret;
|
|
unsigned char* dest = (unsigned char*)buf;
|
|
|
|
/* Read sectors in parts of 0x8000 */
|
|
for(i=0; i<count; i+=64)
|
|
{
|
|
ret = _ata_write_sectors(IF_MD2(drive,) map_sector(start+i), (count-i >= 64 ? 64 : count-i), (const void*)dest);
|
|
if(ret != 0)
|
|
return ret;
|
|
|
|
dest += (count-i >= 64 ? 0x8000 : (count-i)*512);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#else
|
|
(void)start;
|
|
(void)count;
|
|
(void)buf;
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
#ifdef BOOTLOADER
|
|
|
|
/*
|
|
---------------------------------------------------------------------------
|
|
MiniFileSystem parsing code
|
|
---------------------------------------------------------------------------
|
|
*/
|
|
|
|
struct minifs_file
|
|
{
|
|
char name[0x10];
|
|
unsigned int unk;
|
|
unsigned long size;
|
|
unsigned int chain1;
|
|
unsigned int chain2;
|
|
};
|
|
|
|
struct minifs_chain
|
|
{
|
|
unsigned int unknown;
|
|
unsigned short chain[0x27FE];
|
|
unsigned int unknown2;
|
|
unsigned long length;
|
|
};
|
|
|
|
|
|
#define DIR_BITMAP_START 0x0143
|
|
#define DIR_START 0x0144
|
|
#define DATASPACE_BITMAP_START 0x0145
|
|
#define DATASPACE_START 0x0146
|
|
|
|
#define CLUSTER_CHAIN_SIZE 0x5008
|
|
#define CLUSTER_CHAIN_HEAD 0x0000
|
|
#define CLUSTER_CHAIN_BITMAP 0x0001
|
|
#define CLUSTER_CHAIN_CHAIN 0x0002
|
|
|
|
|
|
int load_minifs_file(char* filename, unsigned char* location)
|
|
{
|
|
struct main_header *hdr;
|
|
static struct minifs_file files[128];
|
|
struct minifs_chain *chain;
|
|
unsigned int i;
|
|
int found = -1;
|
|
unsigned char sector[512];
|
|
static unsigned char chain_data[42*512]; /* stack overflow if not static */
|
|
|
|
/* Read MBLK */
|
|
_ata_read_sectors(0, 1, §or);
|
|
hdr = (struct main_header*)§or;
|
|
|
|
/* Read directory listing */
|
|
#define CLUSTER2SECTOR(x) ( (hdr->partitions[0].start + (x)*8) )
|
|
_ata_read_sectors(CLUSTER2SECTOR(DIR_START), 8, &files);
|
|
|
|
for(i=0; i<127; i++)
|
|
{
|
|
if(strcmp(files[i].name, filename) == 0)
|
|
found = i;
|
|
}
|
|
|
|
if(found == -1)
|
|
return -1;
|
|
|
|
#define GET_CHAIN(x) ( CLUSTER2SECTOR(CLUSTER_CHAIN_CHAIN)*512 + (x)*CLUSTER_CHAIN_SIZE )
|
|
#define FILE2SECTOR(x) ( CLUSTER2SECTOR(DATASPACE_START + (x)) )
|
|
|
|
/* Read chain list */
|
|
_ata_read_sectors(GET_CHAIN(files[found].chain1)/512, 41, &chain_data[0]);
|
|
|
|
chain = (struct minifs_chain*)&chain_data[GET_CHAIN(files[found].chain1)%512];
|
|
|
|
/* Copy data */
|
|
for(i=0; i<chain->length; i++)
|
|
{
|
|
_ata_read_sectors(FILE2SECTOR(chain->chain[i]), 8, location);
|
|
location += 0x1000;
|
|
}
|
|
|
|
return files[found].size;
|
|
}
|
|
|
|
#endif
|