Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / packages / deblocker.c
diff --git a/qemu/roms/openbios/packages/deblocker.c b/qemu/roms/openbios/packages/deblocker.c
new file mode 100644 (file)
index 0000000..5007185
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ *   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 );
+}