/* * Creation Date: <2003/12/03 21:20:58 samuel> * Time-stamp: <2004/01/07 19:34:50 samuel> * * * * 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 ); }