#include <kernel/kernel.h>
#include <kernel/cache.h>
#include <stdlib.h>
#include <wchar.h>
#include <string.h>
typedef struct cache_t cache_t;
struct cache_t
{
device_t dev;
device_t* blockdev;
dword block_size, cache_size;
struct cacheblock_t
{
dword block_num;
byte flags;
} *blocks;
byte cache[0x4000];
};
#define BLOCK_USED 1
bool ccRequest(device_t* dev, request_t* req)
{
cache_t* cache;
dword cell, block_num;
size_t length;
status_t hr;
qword ofs;
cache = (cache_t*) dev;
switch (req->code)
{
case DEV_READ:
req->user_length = req->params.read.length;
req->params.read.length = 0;
while (req->params.read.length < req->user_length)
{
/*
* Round the offset down to the nearest multiple of the
* device's block size
*/
ofs = req->params.read.pos + req->params.read.length;
ofs &= -cache->block_size;
/* Calculate the index of the block on the device */
block_num = (dword) ofs / cache->block_size;
/* Calculate the cell number of this cached block in the list */
cell = block_num % cache->cache_size;
/*wprintf(L"%d=>%d@%d: pos = %d offset = %d\n",
(dword) req->params.read.pos,
block_num, cell,
req->params.read.pos + req->params.read.length,
req->params.read.pos + req->params.read.length - ofs);*/
/* Check for cache hit/miss based on the block cell number */
if ((cache->blocks[cell].flags & BLOCK_USED) == 0 ||
cache->blocks[cell].block_num != block_num)
{
/* Missed? Then load the whole cell from the device */
//_cputws_check(L"m");
//wprintf(L"miss\t");
length = cache->block_size;
assert(cell * cache->block_size + length <= sizeof(cache->cache));
hr = devReadSync(cache->blockdev,
ofs,
cache->cache + cell * cache->block_size,
&length);
if (hr != 0)
{
req->result = hr;
return false;
}
cache->blocks[cell].flags |= BLOCK_USED;
cache->blocks[cell].block_num = block_num;
}
//else
//wprintf(L"hit\t");
//_cputws_check(L"h");
length = min(cache->block_size,
req->user_length - req->params.read.length);
memcpy((byte*) req->params.read.buffer + req->params.read.length,
cache->cache + cell * cache->block_size +
req->params.read.pos + req->params.read.length - ofs,
length);
req->params.read.length += length;
}
hndSignal(req->event, true);
return true;
default:
return cache->blockdev->request(cache->blockdev, req);
}
}
device_t* ccInstallBlockCache(device_t* dev, dword block_size)
{
cache_t* cache;
cache = hndAlloc(sizeof(cache_t), NULL);
assert(cache != NULL);
cache->dev.request = ccRequest;
cache->block_size = block_size;
cache->blockdev = dev;
cache->cache_size = sizeof(cache->cache) / block_size;
cache->blocks = malloc(sizeof(struct cacheblock_t) * cache->cache_size);
assert(cache->blocks != NULL);
wprintf(L"ccInstallBlockCache: cached %d blocks\n", cache->cache_size);
return &cache->dev;
}