222 lines
4.4 KiB
C
222 lines
4.4 KiB
C
|
/*
|
||
|
* Creation Date: <2003/12/03 21:20:58 samuel>
|
||
|
* Time-stamp: <2004/01/07 19:34:50 samuel>
|
||
|
*
|
||
|
* <deblocker.c>
|
||
|
*
|
||
|
* deblocker implementation
|
||
|
*
|
||
|
* Copyright (C) 2003, 2004 Samuel Rydh (samuel@ibrium.se)
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of the GNU General Public License
|
||
|
* version 2
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include "config.h"
|
||
|
#include "libopenbios/bindings.h"
|
||
|
#include "libc/diskio.h"
|
||
|
#include "packages.h"
|
||
|
|
||
|
typedef struct {
|
||
|
ucell mark_hi, mark_lo;
|
||
|
xt_t read_xt;
|
||
|
xt_t write_xt;
|
||
|
|
||
|
int max_xfer;
|
||
|
int blksize;
|
||
|
char *buf;
|
||
|
} deblk_info_t;
|
||
|
|
||
|
DECLARE_NODE( deblocker, 0, sizeof(deblk_info_t), "+/packages/deblocker" );
|
||
|
|
||
|
/* ( -- flag ) */
|
||
|
static void
|
||
|
deblk_open( deblk_info_t *di )
|
||
|
{
|
||
|
xt_t xt;
|
||
|
|
||
|
di->read_xt = find_parent_method("read-blocks");
|
||
|
di->write_xt = find_parent_method("write-blocks");
|
||
|
|
||
|
if( !di->read_xt )
|
||
|
RET(0);
|
||
|
|
||
|
di->blksize = di->max_xfer = 512;
|
||
|
if( (xt=find_parent_method("block-size")) ) {
|
||
|
call_parent( xt );
|
||
|
di->blksize = POP();
|
||
|
}
|
||
|
if( (xt=find_parent_method("max-transfer")) ) {
|
||
|
call_parent( xt );
|
||
|
di->max_xfer = POP();
|
||
|
}
|
||
|
/* printk("block-size: %x max_xfer: %x read_xt %x write_xt %x\n",
|
||
|
di->blksize, di->max_xfer, di->write_xt, di->read_xt ); */
|
||
|
|
||
|
di->buf = malloc( di->blksize );
|
||
|
PUSH(-1);
|
||
|
}
|
||
|
|
||
|
/* ( -- ) */
|
||
|
static void
|
||
|
deblk_close( deblk_info_t *di )
|
||
|
{
|
||
|
free( di->buf );
|
||
|
}
|
||
|
|
||
|
/* ( pos_lo pos_hi -- status ) */
|
||
|
static void
|
||
|
deblk_seek( deblk_info_t *di )
|
||
|
{
|
||
|
ucell pos_hi = POP();
|
||
|
ucell pos_lo = POP();
|
||
|
ducell mark = ((ducell)pos_hi << BITS) | pos_lo;
|
||
|
|
||
|
/* printk("deblk_seek %x %08x\n", pos_hi, pos_lo ); */
|
||
|
|
||
|
/* -1 means seek to EOF (at least in our implementation) */
|
||
|
if( (dcell)mark == -1 )
|
||
|
RET(-1);
|
||
|
di->mark_hi = pos_hi;
|
||
|
di->mark_lo = pos_lo;
|
||
|
|
||
|
/* 0,1 == success, -1 == error */
|
||
|
PUSH(0);
|
||
|
}
|
||
|
|
||
|
/* ( -- mark.d ) */
|
||
|
static void
|
||
|
deblk_tell( deblk_info_t *di )
|
||
|
{
|
||
|
PUSH( di->mark_lo );
|
||
|
PUSH( di->mark_hi );
|
||
|
}
|
||
|
|
||
|
|
||
|
#define DO_IO( xt, buf, blk, n ) \
|
||
|
({ PUSH3(pointer2cell(buf), blk, n); call_parent(xt); POP(); })
|
||
|
|
||
|
typedef struct {
|
||
|
/* block operation */
|
||
|
char *blk_buf;
|
||
|
int nblks;
|
||
|
|
||
|
/* byte operation */
|
||
|
cell offs;
|
||
|
int len;
|
||
|
char *data; /* start of data */
|
||
|
} work_t;
|
||
|
|
||
|
static void
|
||
|
split( deblk_info_t *di, char *data, int len, work_t w[3] )
|
||
|
{
|
||
|
ducell mark = ((ducell)di->mark_hi << BITS) | di->mark_lo;
|
||
|
memset( w, 0, sizeof(work_t[3]) );
|
||
|
|
||
|
w[0].offs = mark % di->blksize;
|
||
|
w[0].blk_buf = di->buf;
|
||
|
w[0].data = data;
|
||
|
if( w[0].offs ) {
|
||
|
w[0].len = MIN( len, di->blksize - w[0].offs );
|
||
|
w[0].nblks = w[0].len ? 1:0;
|
||
|
data += w[0].len;
|
||
|
len -= w[0].len;
|
||
|
}
|
||
|
|
||
|
w[1].blk_buf = data;
|
||
|
w[1].nblks = (len / di->blksize);
|
||
|
w[1].len = w[1].nblks * di->blksize;
|
||
|
data += w[1].len;
|
||
|
len -= w[1].len;
|
||
|
|
||
|
w[2].blk_buf = di->buf;
|
||
|
w[2].data = data;
|
||
|
w[2].len = len;
|
||
|
w[2].nblks = len ? 1:0;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
do_readwrite( deblk_info_t *di, int is_write, xt_t xt )
|
||
|
{
|
||
|
int blk, i, n, len = POP();
|
||
|
char *dest = (char*)cell2pointer(POP());
|
||
|
int last=0, retlen=0;
|
||
|
work_t w[3];
|
||
|
ducell mark = ((ducell)di->mark_hi << BITS) | di->mark_lo;
|
||
|
|
||
|
/* printk("read: %x %x\n", (int)dest, len ); */
|
||
|
|
||
|
if( !xt )
|
||
|
return -1;
|
||
|
|
||
|
blk = mark / di->blksize;
|
||
|
split( di, dest, len, w );
|
||
|
|
||
|
for( i=0; !last && i<3; i++ ) {
|
||
|
if( !w[i].nblks )
|
||
|
continue;
|
||
|
|
||
|
if( is_write && i != 1 ) {
|
||
|
DO_IO( di->read_xt, w[i].blk_buf, blk, w[i].nblks );
|
||
|
memcpy( w[i].blk_buf + w[i].offs, w[i].data, w[i].len );
|
||
|
}
|
||
|
|
||
|
n = DO_IO( xt, w[i].blk_buf, blk, w[i].nblks );
|
||
|
if( n < 0 ) {
|
||
|
if( !retlen )
|
||
|
retlen = -1;
|
||
|
break;
|
||
|
}
|
||
|
if( n != w[i].nblks ) {
|
||
|
w[i].len = MIN( n*di->blksize, w[i].len );
|
||
|
last = 1;
|
||
|
}
|
||
|
if( !is_write && i != 1 )
|
||
|
memcpy( w[i].data, w[i].blk_buf + w[i].offs, w[i].len );
|
||
|
retlen += w[i].len;
|
||
|
blk += n;
|
||
|
}
|
||
|
if( retlen > 0 ) {
|
||
|
mark += retlen;
|
||
|
di->mark_hi = mark >> BITS;
|
||
|
di->mark_lo = mark & (ucell) -1;
|
||
|
}
|
||
|
return retlen;
|
||
|
}
|
||
|
|
||
|
/* ( addr len -- actual ) */
|
||
|
static void
|
||
|
deblk_read( deblk_info_t *di )
|
||
|
{
|
||
|
/* printk("deblk_read\n"); */
|
||
|
int ret = do_readwrite( di, 0, di->read_xt );
|
||
|
PUSH( ret );
|
||
|
}
|
||
|
|
||
|
/* ( buf len --- actlen ) */
|
||
|
static void
|
||
|
deblk_write( deblk_info_t *di )
|
||
|
{
|
||
|
int ret = do_readwrite( di, 1, di->write_xt );
|
||
|
PUSH( ret );
|
||
|
}
|
||
|
|
||
|
/* remember to fix is-deblocker if new methods are added */
|
||
|
NODE_METHODS( deblocker ) = {
|
||
|
{ "open", deblk_open },
|
||
|
{ "close", deblk_close },
|
||
|
{ "read", deblk_read },
|
||
|
{ "write", deblk_write },
|
||
|
{ "seek", deblk_seek },
|
||
|
{ "tell", deblk_tell },
|
||
|
};
|
||
|
|
||
|
|
||
|
void
|
||
|
deblocker_init( void )
|
||
|
{
|
||
|
REGISTER_NODE( deblocker );
|
||
|
}
|