/*******************************************************************
  uLan Communication - low level and link driver

  ul_test.c	- test driver implementation

  (C) Copyright 1996,1997 by Pavel Pisa 

  The uLan driver is distributed under the Gnu General Public Licence. 
  See file COPYING for details.
 *******************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <asm/io.h>
#include "ul_drv.h"

#if 1
 #define LOG_CHIO	printf
#else
 #define LOG_CHIO(x,args...)
#endif

#if 1
 #define RDTSC \
   ({unsigned long l,h;\
    asm volatile ("rdtsc" : "=a" (l), "=d" (h));\
    l;\
   })
#else
 #define RDTSC 0
#endif

/*******************************************************************/
/* Chip driver for 82510 */

#if 1
 /* this enables use of shadow 82510 bank switch */
 /* be !!! carefull !!!, use U510_SBANK_FINI at begin of every */
 /* function containing u510_outb, u510_inb */
 /* such function must not call another such function */
 #define _U510_SBANK_SHADOW_VAR sbank_sv
 #define U510_SBANK_FINI int sbank_sv=-1
#else
 #define U510_SBANK_FINI
#endif

#include "u510_reg.h"

/*** Test interrupt request state ***/
int u510_pool(ul_drv *udrv)
{
 return u510_pool_gir(udrv->port);
};

/*** Receive character into char_buff ***/
int u510_recch(ul_drv *udrv, int ret_code)
{
 U510_SBANK_FINI;
 unsigned char uc;
 if(u510_inb(udrv->port,U510_MCR)&U510_MCR_OEQ)
 { /* direction switch necessary */
  if(!(u510_inb(udrv->port,U510_GSR)&U510_GER_TM))
  { /* wait to end of previous transfer */
   u510_outb(udrv->port,U510_GER, U510_GER_TM);
   return 0;
  }
  u510_outb(udrv->port,U510_MCR, U510_MCR_IE);
  u510_outb(udrv->port,U510_RCM, U510_RCM_FLUSH);
 };

 u510_outb(udrv->port,U510_GER,U510_GER_RI);
 uc=u510_inb(udrv->port,U510_RST);
 if(!(uc&0x01)) return 0;

 uc=u510_inb(udrv->port,U510_RXF);
 udrv->char_buff=u510_inb(udrv->port,U510_RXD);
 if(uc&0x08)
 { /* store last ctrl for connect line busy/ready */
  udrv->char_buff|=0x100; /* controll char */
  udrv->last_ctrl=udrv->char_buff;
 } else udrv->last_ctrl&=0x7F;
 udrv->xor_sum^=udrv->char_buff;udrv->xor_sum++;

 UL_FRET;
 if(uc&0x02) 
 {
  LOG_CHIO(" ER:%03X",udrv->char_buff);
  return -1; /* frame error */
 };
 LOG_CHIO(" R:%03X",udrv->char_buff);
 return 1;
};

/*-- Helper functions for sndchar --*/

int u510_sndch(ul_drv *udrv, int ret_code);

int u510_sndch_w2(ul_drv *udrv, int ret_code)
{
 U510_SBANK_FINI;
 u510_outb(udrv->port,U510_MCR, U510_MCR_OE);
 UL_FNEXT(u510_sndch);
 return 1;
}

int u510_sndch_w1(ul_drv *udrv, int ret_code)
{
 U510_SBANK_FINI;
 unsigned u;
 u=udrv->baud_div*11*16;
 u510_outb(udrv->port,U510_TMCR,0);
 u510_inb(udrv->port,U510_TMST);
 u510_outb(udrv->port,U510_GER,U510_GER_TIE);
 u510_outb(udrv->port,U510_LCR,U510_LCR_C|U510_LCR_DLAB);
 u510_outb(udrv->port,U510_BBL,u);
 u510_outb(udrv->port,U510_BBH,u>>8);
 u510_outb(udrv->port,U510_LCR,U510_LCR_C);
 u510_outb(udrv->port,U510_TMIE,2);
 u510_outb(udrv->port,U510_TMCR,0x22);
 UL_FNEXT(u510_sndch_w2);
 return 0;
}

/*** Send character from char_buff ***/
int u510_sndch(ul_drv *udrv, int ret_code)
{
 U510_SBANK_FINI;
 if(!(u510_inb(udrv->port,U510_MCR)&U510_MCR_OEQ))
 { /* direction switch necessary */
  UL_FNEXT(u510_sndch_w1);
  if(!(u510_inb(udrv->port,U510_GSR)&U510_GER_TM))
  { /* wait to end of previous transfer */
   u510_outb(udrv->port,U510_GER, U510_GER_TM);
   return 0;
  }
  return 1;
 };

 u510_outb(udrv->port,U510_GER,U510_GER_TI);
 if(!(u510_inb(udrv->port,U510_LSR)&U510_LSR_TRE)) return 0;

 if(udrv->char_buff&0x100)
 { /* store last ctrl for connect line busy/ready */
  udrv->last_ctrl=udrv->char_buff;
  u510_outb(udrv->port,U510_TXF, 0x80); /* controll char */
 } else u510_outb(udrv->port,U510_TXF, 0x00); /* regular char */;
 u510_outb(udrv->port,U510_TXD, udrv->char_buff); /* char code */
 udrv->xor_sum^=udrv->char_buff;udrv->xor_sum++;
 
 LOG_CHIO(" T:%03X",udrv->char_buff);
 UL_FRET;
 return 1;
};

/*-- Helper functions for wait --*/

int u510_wait(ul_drv *udrv, int ret_code);

int u510_wait_1(ul_drv *udrv, int ret_code)
{
 U510_SBANK_FINI;
 if(u510_inb(udrv->port,U510_RST)&1)
 {
  UL_FNEXT(u510_recch);
  return 1;
 };
 
 if(!udrv->wait_time--)
 {
  LOG_CHIO(" Timeout!");
  UL_FRET;
  return -2;
 };

 u510_outb(udrv->port,U510_TMCR,0);
 u510_inb(udrv->port,U510_TMST);
 
 u510_outb(udrv->port,U510_LCR,U510_LCR_C|U510_LCR_DLAB);
 u510_outb(udrv->port,U510_BBL,0xFF);
 u510_outb(udrv->port,U510_BBH,0xFF);
 u510_outb(udrv->port,U510_LCR,U510_LCR_C);
 u510_outb(udrv->port,U510_TMIE,2);
 u510_outb(udrv->port,U510_TMCR,0x22);
 
 return 0;
};

/*** Wait for time or received character ***/
int u510_wait(ul_drv *udrv, int ret_code)
{
 U510_SBANK_FINI;
 unsigned u;
 
 if(!(u510_inb(udrv->port,U510_GSR)&U510_GER_TM))
 { /* wait to end of previous transfer */
  u510_outb(udrv->port,U510_GER, U510_GER_TM);
  return 0;
 };
 if(u510_inb(udrv->port,U510_MCR)&U510_MCR_OEQ)
 { /* direction switch necessary */
  u510_outb(udrv->port,U510_MCR, U510_MCR_IE);
  u510_outb(udrv->port,U510_RCM, U510_RCM_FLUSH);
 };

 UL_FNEXT(u510_wait_1);

 u=udrv->wait_time*11*16*udrv->baud_div;
 udrv->wait_time=u>>16;
 u510_outb(udrv->port,U510_TMCR,0);
 u510_inb(udrv->port,U510_TMST);
 u510_outb(udrv->port,U510_GER,U510_GER_TIE+U510_GER_RI);
 if((u&0xFFFF)<=2) return 1;
 
 u510_outb(udrv->port,U510_LCR,U510_LCR_C|U510_LCR_DLAB);
 u510_outb(udrv->port,U510_BBL,u);
 u510_outb(udrv->port,U510_BBH,u>>8);
 u510_outb(udrv->port,U510_LCR,U510_LCR_C);
 u510_outb(udrv->port,U510_TMIE,2);
 u510_outb(udrv->port,U510_TMCR,0x22);

 return 0;
};

/*** Connect to RS485 bus ***/
int u510_connect(ul_drv *udrv, int ret_code)
{
 UL_FRET; 
 return 1;
};

/*** Control functions of chip driver  ***/
int u510_cctrl(ul_drv *udrv, int ctrl_fnc)
{
 U510_SBANK_FINI;
 switch (ctrl_fnc)
 {
  case UL_CC_DIN:
	{ u510_outb(udrv->port,U510_MCR, U510_MCR_IE); return 1;};
  case UL_CC_DOUT:
	{ u510_outb(udrv->port,U510_MCR, U510_MCR_OE); return 1;};
 };
 return -1;
};

/*
 UL_FNEXT(u510_recch);
 UL_FCALL(u510_recch);
 UL_FCALL2(u510_recch,u510_recch);
 UL_FRET;
*/

/*** 82510 chip initialize ***/
int u510_init(ul_drv *udrv, int baud, int port, int irq)
{
 U510_SBANK_FINI;
 unsigned u;
 for(u=0;u<0x80;u+=0x20) 
 {
  outb(u,port+U510_GIR);
  if((inb(port+U510_GIR)^u)&0x60)
  {
   outb(0,port+U510_GIR);
   return(-1);
  };
 };
 if (!baud) baud=9600;
 u=(0x8CA00+baud/2)/baud;
 if (u>0xFFFF) u=1;
 udrv->baud_div=u;
 udrv->port=port;
 udrv->irq=irq; /* !!!!!!! */
 u510_outb(udrv->port,U510_ICM, 0x10);
 u510_outb(udrv->port,U510_GER, 0);
 u510_outb(udrv->port,U510_LCR, U510_LCR_C);
 u510_outb(udrv->port,U510_IMD, 0xB);
 u510_outb(udrv->port,U510_TMD, 0);	/* 0x18 -> RTS */
 u510_outb(udrv->port,U510_FMD, 0);
 u510_outb(udrv->port,U510_RMD, 0x40);	/* Addresss opens RxFIFO */
 u510_outb(udrv->port,U510_CLCF,0x50);	/* RxC a TxC are 16x from BRGA */
 u510_outb(udrv->port,U510_BACF,0x04);	/* Baud gen system clock */
 u510_outb(udrv->port,U510_BBCF,0x00);	/* Timer system clock */
 u510_outb(udrv->port,U510_PMD, 0x7C);	/* Konfigure pins */

 u510_outb(udrv->port,U510_LCR, U510_LCR_C|U510_LCR_DLAB);
 u510_outb(udrv->port,U510_BAL, (unsigned char)udrv->baud_div);
 u510_outb(udrv->port,U510_BAH, (unsigned char)(udrv->baud_div>>8));
 u510_outb(udrv->port,U510_LCR, U510_LCR_C);

 u510_outb(udrv->port,U510_LCR, U510_LCR_C|U510_LCR_C);
 u510_outb(udrv->port,U510_LCR, U510_LCR_C);
 udrv->fnc_recch=&u510_recch;
 udrv->fnc_sndch=&u510_sndch;
 udrv->fnc_wait=&u510_wait;
 udrv->fnc_pool=&u510_pool;
 udrv->fnc_connect=&u510_connect;
 udrv->fnc_cctrl=&u510_cctrl;
 u510_outb(udrv->port,U510_GER, U510_GER_MI);
 return(0);
};

/*******************************************************************/
/* Frame begin/end */

/*-- Helper functions for sndbeg --*/

int uld_sndbeg_2(ul_drv *udrv, int ret_code)
{
 udrv->char_buff=udrv->con_cmd;
 UL_FNEXT(*udrv->fnc_sndch);
 return 1;
};

int uld_sndbeg_1(ul_drv *udrv, int ret_code)
{
 udrv->char_buff=udrv->char_buff=udrv->con_sadr;
 UL_FCALL2(*udrv->fnc_sndch,uld_sndbeg_2);
 return 1;
};

/*** Send frame begin ***/
int uld_sndbeg(ul_drv *udrv, int ret_code)
{
 udrv->xor_sum=0;
 if(udrv->con_flg&4) udrv->char_buff=UL_BEG;
 else udrv->char_buff=udrv->con_dadr|0x100;
 UL_FCALL2(*udrv->fnc_sndch,uld_sndbeg_1);
 return 1;
};

/*-- Helper functions for sndend --*/

int uld_sndend_3(ul_drv *udrv, int ret_code)
{
 UL_FRET;
 if(ret_code<0) return ret_code;
 if(udrv->char_buff==UL_ACK) return 1;
 if(udrv->char_buff==UL_NAK) return -3;
 if(udrv->char_buff==UL_WAK) return -4;
 return -1;
};

int uld_sndend_2(ul_drv *udrv, int ret_code)
{
 if(!(udrv->con_flg&1))
 {
  UL_FRET;
  return 1;
 };
 udrv->wait_time=4;
 UL_FCALL2(*udrv->fnc_wait,uld_sndend_3);
 return 1;
};

int uld_sndend_1(ul_drv *udrv, int ret_code)
{
 udrv->char_buff=udrv->xor_sum&0xFF;
 UL_FCALL2(*udrv->fnc_sndch,uld_sndend_2);
 return 1;
};

/*** Send frame end ***/
int uld_sndend(ul_drv *udrv, int ret_code)
{
 static sndend_arr[]={UL_END,UL_ARQ,UL_PRQ,UL_AAP};
 udrv->char_buff=sndend_arr[udrv->con_flg];
 UL_FCALL2(*udrv->fnc_sndch,uld_sndend_1);
 return 1;
};

/*-- Helper functions for recbeg --*/

int uld_recbeg_3(ul_drv *udrv, int ret_code)
{
 if(ret_code<0) {UL_FRET;return ret_code;};
 if(udrv->char_buff&0xFF00) {UL_FRET; return -1;};
 udrv->con_cmd=udrv->char_buff;
 UL_FRET;
 return 1;
};

int uld_recbeg_2(ul_drv *udrv, int ret_code)
{
 if(ret_code<0) {UL_FRET;return ret_code;};
 if(udrv->char_buff&0xFF00) {UL_FRET; return -1;};
 udrv->con_sadr=udrv->char_buff;
 udrv->wait_time=4;
 UL_FCALL2(*udrv->fnc_wait,uld_recbeg_3);
 return 1;
};

int uld_recbeg_1(ul_drv *udrv, int ret_code)
{
 if((udrv->char_buff!=(udrv->my_adr|0x100))
   &&((udrv->char_buff!=UL_BEG)||(!(udrv->con_flg&8))))
  {UL_FRET;return -10;};
 udrv->con_dadr=udrv->char_buff&0xFF;
 udrv->con_flg|=8;
 udrv->wait_time=4;
 UL_FCALL2(*udrv->fnc_wait,uld_recbeg_2);
 return 1;
};

/*** Receive frame begin ***/
int uld_recbeg(ul_drv *udrv, int ret_code)
{
 udrv->xor_sum=0;
 udrv->wait_time=4;
 UL_FCALL2(*udrv->fnc_wait,uld_recbeg_1);
 return 1;
};

/*-- Helper functions for recend --*/

int uld_recend_3(ul_drv *udrv, int ret_code)
{
 UL_FRET;
 if(udrv->con_flg&0xC0) return -1;
 return 1;
};

int uld_recend_2(ul_drv *udrv, int ret_code)
{
 if(ret_code<0) {UL_FRET;return ret_code;};
 if(udrv->char_buff&0xFF00) {UL_FRET; return -10;};
 if((udrv->char_buff^udrv->xor_sum)&0xFF) udrv->con_flg|=0x80;
 if(!(udrv->con_flg&1))
 {
  UL_FRET;
  return 1;
 };
 if(udrv->con_flg|0x80) udrv->char_buff=UL_NAK;
 else if(udrv->con_flg|0x40) udrv->char_buff=UL_WAK;
 else udrv->char_buff=UL_ACK;
 UL_FCALL2(*udrv->fnc_sndch,uld_recend_3);
 return 1;
};

int uld_recend_1(ul_drv *udrv, int ret_code)
{
 if(ret_code<0) {UL_FRET;return ret_code;};
 if(!(udrv->char_buff&0x100))
 {
  udrv->wait_time=4;
  UL_FCALL(*udrv->fnc_wait);
  udrv->con_flg|=0x80;
  return 1;
 };
 udrv->con_flg&=~3;
 switch(udrv->char_buff)
 {
  case UL_END: break;
  case UL_ARQ: udrv->con_flg|=1; break;
  case UL_PRQ: udrv->con_flg|=2; break;
  case UL_AAP: udrv->con_flg|=3; break;
  default: UL_FRET; return -1;
 };
 udrv->wait_time=4;
 UL_FCALL2(*udrv->fnc_wait,uld_recend_2);
 return 1;
};

/*** Receive frame end ***/
int uld_recend(ul_drv *udrv, int ret_code)
{
 if((udrv->char_buff&0xFF00)==0x100) 
 {
  UL_FNEXT(uld_recend_1);
 }else{
  udrv->wait_time=4;
  UL_FCALL2(*udrv->fnc_wait,uld_recend_1);
 };
 return 1;
};

/*******************************************************************/
/* Data iterator */

long ul_di_seek(ul_data_it *di,long pos, int whence)
{
 switch(whence)
 {
  case 1: pos+=di->pos; break;
  case 2: pos+=UL_BLK_HEAD(di->head_blk).len; break;
 };
 if(pos<di->pos)
 {
  di->blk=di->head_blk;
  di->pos=0;
  di->ptr=UL_BLK_HEAD(di->head_blk).fdata;
  if(pos<0) return 0;
 };
 di->ptr+=pos-di->pos;
 di->pos=pos;
 return pos;
};

char ul_di_read1(ul_data_it *di)
{
 char ch;
 if(ul_di_adjust(di))
  ch=*ul_di_byte(di);
 else ch=0;
 ul_di_inc(di);
 return ch;
};

char ul_di_write1(ul_data_it *di, char ch)
{
 ul_mem_blk *blk;
 while(!ul_di_adjust(di))
 {
  if(!(blk=ul_alloc_blk(UL_BLK_HEAD(di->head_blk).bll->udrv))) return 0;
  di->blk->next=blk;
 };
 *ul_di_byte(di)=ch;
 ul_di_inc(di);
 return 1;
};

/*-- Helper functions for snddata --*/
int uld_snddata_1(ul_drv *udrv, int ret_code)
{
 if(ret_code<0) {UL_FRET;return ret_code;};
 if(udrv->con_di.trans_len--)
 {
  udrv->char_buff=ul_di_read1(&udrv->con_di);
  UL_FCALL(*udrv->fnc_sndch);
 } else UL_FRET;
 return 1;
};

/*** Send data ***/
int uld_snddata(ul_drv *udrv, int ret_code)
{
 ul_di_init(&udrv->con_di,udrv->con_frame);
 udrv->con_di.trans_len=UL_BLK_HEAD(udrv->con_frame).len;
 if(!udrv->con_di.trans_len) {UL_FRET; return 1;};
 UL_FNEXT(uld_snddata_1)
 return 1;
};

/*-- Helper functions for recdata --*/
int uld_recdata_1(ul_drv *udrv, int ret_code)
{
 if(ret_code<0) {UL_FRET;return ret_code;};
 if(!(udrv->char_buff&0xFF00))
 {
  ret_code=ul_di_write1(&udrv->con_di,udrv->char_buff);
  if(!ret_code) return -10;
  if(--udrv->con_di.trans_len)
  {
   UL_FCALL(*udrv->fnc_recch);
   return 1;
  };
 };
 if(!(UL_BLK_HEAD(udrv->con_frame).flg&UL_BFL_LNMM))
  UL_BLK_HEAD(udrv->con_frame).len=udrv->con_di.pos;
 UL_FRET;
 return 1;
};

/*** Receive data ***/
int uld_recdata(ul_drv *udrv, int ret_code)
{
 ul_di_init(&udrv->con_di,udrv->con_frame);
 if(UL_BLK_HEAD(udrv->con_frame).flg&UL_BFL_LNMM)
  udrv->con_di.trans_len=UL_BLK_HEAD(udrv->con_frame).len;
 else
  udrv->con_di.trans_len=-1;
 if(!udrv->con_di.trans_len) {UL_FRET; return 1;};
 UL_FCALL2(*udrv->fnc_recch,uld_recdata_1)
 return 1;
};

/*******************************************************************/
/* Message and frame proccessor */

int uld_prmess_frame(ul_drv *udrv, int ret_code);
int uld_prmess(ul_drv *udrv, int ret_code);

/*-- Helper functions for prmess --*/
int uld_prmess_error(ul_drv *udrv, int ret_code)
{
 UL_FRET;
 return ret_code;
};

int uld_prmess_next(ul_drv *udrv, int ret_code)
{
 if(ret_code<0) {UL_FNEXT(uld_prmess_error);return -14;};
 if(UL_BLK_HEAD(udrv->con_frame).flg&UL_BFL_TAIL)
 {
  if(UL_BLK_HEAD(udrv->con_frame).next)
  {
   udrv->con_frame=UL_BLK_HEAD(udrv->con_frame).next;
   UL_FNEXT(uld_prmess_frame);
   return 1;
  };
  UL_FRET;
  return -12;
 };
 UL_FRET;
 return 1;
};

int uld_prmess_rec2(ul_drv *udrv, int ret_code)
{
 if(ret_code<0) {UL_FNEXT(uld_prmess_error);return -14;};
 UL_FCALL2(uld_recend,uld_prmess_next)
 return 1;
};

int uld_prmess_rec1(ul_drv *udrv, int ret_code)
{
 if(ret_code<0) {UL_FNEXT(uld_prmess_error);return -14;};
 UL_BLK_HEAD(udrv->con_frame).dadr=udrv->con_dadr;
 UL_BLK_HEAD(udrv->con_frame).sadr=udrv->con_sadr;
 UL_BLK_HEAD(udrv->con_frame).cmd=udrv->con_cmd;
 UL_FCALL2(uld_recdata,uld_prmess_rec2)
 return 1;
};

int uld_prmess_snd2(ul_drv *udrv, int ret_code)
{
 if(ret_code<0) {UL_FNEXT(uld_prmess_error);return -14;};
 UL_FCALL2(uld_sndend,uld_prmess_next)
 return 1;
};

int uld_prmess_snd1(ul_drv *udrv, int ret_code)
{
 if(ret_code<0) {UL_FNEXT(uld_prmess_error);return -14;};
 UL_FCALL2(uld_snddata,uld_prmess_snd2)
 return 1;
};

int uld_prmess_frame(ul_drv *udrv, int ret_code)
{
 if(ret_code<0) {UL_FNEXT(uld_prmess_error);return -14;};
 if(UL_BLK_HEAD(udrv->con_frame).flg&UL_BFL_SND)
 {
  udrv->con_dadr=UL_BLK_HEAD(udrv->con_frame).dadr;
  udrv->con_sadr=UL_BLK_HEAD(udrv->con_frame).sadr;
  udrv->con_cmd=UL_BLK_HEAD(udrv->con_frame).cmd;
  udrv->con_flg=UL_BLK_HEAD(udrv->con_frame).flg&3;
  UL_FCALL2(uld_sndbeg,uld_prmess_snd1)
 }
 else if(UL_BLK_HEAD(udrv->con_frame).flg&UL_BFL_REC)
 {
  udrv->con_flg=8;
  UL_FCALL2(uld_recbeg,uld_prmess_rec1);
 }
 else UL_FNEXT(uld_prmess_next);
 return 1;
};

/*** Proccess outgoing message ***/
int uld_prmess(ul_drv *udrv, int ret_code)
{
 if(!udrv->con_message) udrv->con_message=udrv->prep_bll.first;
 if(!udrv->con_message)
 {
  UL_FRET;
  return 1;
 };
 udrv->con_frame=udrv->con_message;
 UL_FCALL2(*udrv->fnc_connect,uld_prmess_frame)
 return 1;
};

/*******************************************************************/

#include <malloc.h>

int ul_mem_init(ul_drv *udrv, int req_size)
{
 void **pptr;
 ul_mem_blk *bptr;
 pptr=(void **)(udrv->mem_ptr=malloc(req_size+2*sizeof(void*)));
 if(!pptr) return -1;
 *(pptr++)=0;
 *(pptr++)=(void *)req_size;
 udrv->free_blk=bptr=(ul_mem_blk *)pptr;
 udrv->free_blk_cnt=1;

 while((req_size-=sizeof(ul_mem_blk))>=sizeof(ul_mem_blk))
 {
  bptr->next=bptr+1;
  bptr++;
  udrv->free_blk_cnt++;
 }; 
 bptr->next=NULL;
 udrv->prep_bll.first=udrv->prep_bll.last=NULL;
 udrv->prep_bll.cnt=0;
 udrv->prep_bll.udrv=udrv; 
 udrv->proc_bll.first=udrv->proc_bll.last=NULL;
 udrv->proc_bll.cnt=0;
 udrv->proc_bll.udrv=udrv;
 udrv->con_message=NULL;

 return 0;
};

void ul_bll_ins(ul_blk_bll *bll, ul_mem_blk *blk)
{
 /* ul_drv *udrv=bll->udrv; */
 UL_BLK_HEAD(blk).bll=bll;
 /* UL_BLK_HEAD(blk).next=NULL; */
 /* UL_BLK_HEAD(blk).ref_cnt=1; */
 /* UL_BLK_HEAD(blk).flg|=UL_BFL_LOCK; */
 UL_DRV_LOCK;
 if((UL_BLK_HEAD(blk).prev=bll->last)!=NULL)
  UL_BLK_HEAD(bll->last).next=blk;
 else bll->first=blk;
 bll->last=blk;
 bll->cnt++;
 UL_DRV_UNLOCK;
};

void ul_bll_del(ul_mem_blk *blk)
{
 ul_blk_bll *bll;
 ul_mem_blk *p;
 bll=UL_BLK_HEAD(blk).bll;
 UL_DRV_LOCK;

 if((p=UL_BLK_HEAD(blk).next)!=NULL) 
 {UL_BLK_HEAD(p).prev=UL_BLK_HEAD(blk).prev;
  UL_BLK_HEAD(blk).next=NULL;
 }else bll->last=UL_BLK_HEAD(blk).prev;

 if((p=UL_BLK_HEAD(blk).prev)!=NULL) 
 {UL_BLK_HEAD(p).next=UL_BLK_HEAD(blk).next;
  UL_BLK_HEAD(blk).prev=NULL;
 }else bll->first=UL_BLK_HEAD(blk).next;

 UL_BLK_HEAD(blk).bll=NULL;
 UL_DRV_UNLOCK;
};

void ul_bll_free(void);

/*******************************************************************/

#include <ctype.h>
#include <stdlib.h>

ul_drv ul_drv1;

void printblk(ul_mem_blk *blk)
{
 while(blk)
 {
  printf("BLK : dadr=%02X sadr=%02X flg=%04X len=%04X\n",
   UL_BLK_HEAD(blk).dadr,
   UL_BLK_HEAD(blk).sadr,
   UL_BLK_HEAD(blk).flg,
   UL_BLK_HEAD(blk).len
  );
  {
   ul_data_it di;
   char ch;
   printf("      |");
   ul_di_init(&di,blk);
   di.trans_len=UL_BLK_HEAD(blk).len;
   while(di.pos<di.trans_len)
   {
    ch=ul_di_read1(&di);
    if(ch>=0x20&&ch<0x7F) printf("%c",ch);
    else printf("<%X>",(int)ch);
   };
   printf("|\n");
  };
  blk=UL_BLK_HEAD(blk).next;
 };
};

void gentestblk(ul_drv *udrv)
{
 ul_mem_blk *blk;
 ul_data_it di;
 blk=ul_alloc_blk(udrv);
 UL_BLK_HEAD(blk).prev=NULL;
 UL_BLK_HEAD(blk).next=NULL;
 UL_BLK_HEAD(blk).ref_cnt=0;
 UL_BLK_HEAD(blk).flg=	UL_BFL_M2IN|UL_BFL_TAIL|UL_BFL_SND|
 			UL_BFL_PRQ|UL_BFL_ARQ;
 UL_BLK_HEAD(blk).len=0;
 UL_BLK_HEAD(blk).cmd=0xF0;
 UL_BLK_HEAD(blk).dadr=3;
 UL_BLK_HEAD(blk).sadr=2;
 ul_di_init(&di,blk);
 /* ul_di_write1(&di,0); */
 ul_di_write1(&di,1);
 ul_di_write1(&di,2);
 ul_di_write1(&di,3);
 UL_BLK_HEAD(blk).len=di.pos;
 ul_bll_ins(&udrv->prep_bll,blk);

 blk=ul_alloc_blk(udrv);
 UL_BLK_HEAD(blk).prev=NULL;
 UL_BLK_HEAD(blk).next=NULL;
 UL_BLK_HEAD(blk).ref_cnt=0;
 UL_BLK_HEAD(blk).flg=	UL_BFL_M2IN|UL_BFL_REC|
 			UL_BFL_PRQ|UL_BFL_ARQ;
 UL_BLK_HEAD(blk).len=0;
 UL_BLK_HEAD(blk).cmd=0;
 UL_BLK_HEAD(blk).dadr=0;
 UL_BLK_HEAD(blk).sadr=0;
 ul_bll_ins(&udrv->prep_bll,blk);

};


int fnc_test(ul_drv *udrv, int ret_code)
{
 static char *line=NULL;
 static char *actch=NULL;
 static size_t linelen=0;
 static int state=0;
 static int temp=0;

 switch(state)
 {
  case 1:
	if(ret_code<0)printf("e");
	printf("t:%X ",udrv->char_buff);
	break;
  case 2: 
	if(ret_code<0)printf("e");
	printf("r:%X ",udrv->char_buff);
	break;
  case 3:
	if(temp--)
	{
	 UL_FCALL(*udrv->fnc_sndch);
	 return 1;
	};
  	break;
  case 4:
	printf(" ret_code=%i",ret_code);
        printf("\nprep_bll :\n");
	printblk(udrv->prep_bll.first);
        printf("\nproc_bll :\n");
	printblk(udrv->proc_bll.first);
        printf("\n");
  	break;
 };

 state=0;
 if(!actch)
 {
  printf("\n>");
  if(getdelim(&line,&linelen,'\n',stdin)!=-1) actch=line;
  else actch=NULL;
  udrv->xor_sum=0;
 }

 if(actch)
 {
  while(*actch==' ') actch++;
  if((*actch==0)||(*actch=='\n')){
	actch=NULL;
  }else if(isdigit(*actch)){
	udrv->char_buff=strtol(actch,&actch,0);
	UL_FCALL(*udrv->fnc_sndch);
	state=1;
	return 1;
  }else if(toupper(*actch)=='R'){
	actch++;
	UL_FCALL(*udrv->fnc_recch);
	state=2;
	return 1;
  }else if(toupper(*actch)=='C'){
	actch++;
	udrv->char_buff=udrv->xor_sum&0xFF;
	UL_FCALL(*udrv->fnc_sndch);
	state=1;
	return 1;
  }else if(toupper(*actch)=='T'){
	actch++;
	temp=10000;
	state=3;
	return 1;
  }else if(toupper(*actch)=='I'){
	actch++;
	gentestblk(udrv);
	UL_FCALL(uld_prmess);
	state=4;
	return 1;
  }else if(toupper(*actch)=='S'){
	actch++;
	udrv->fnc_cctrl(udrv,UL_CC_DIN);
	state=0;
	return 1;
  }else if(toupper(*actch)=='W'){
	actch++;
	udrv->wait_time=strtol(actch,&actch,0);
	UL_FCALL(*udrv->fnc_wait);
	state=0;
	return 1;
  };

 };

 return 1;
};
	
void main(void)
{
 ul_drv* udrv=&ul_drv1;
 long delay_cnt=0,cycle_cnt=0;
 long tsc_ser, tsc_int, tsc_last;
 int i,ret_code;
 udrv->port=0x3E8;
 udrv->fnc_sp=&udrv->fnc_stack[0];
 udrv->fnc_act=&fnc_test;
 udrv->my_adr=2;

 i=ioperm(udrv->port,8l,1);
 printf("ioperm ret %d\n",i);

 i=u510_init(udrv, 9600, udrv->port, 0);
 printf("u510_init ret %d\n",i);

 i=ul_mem_init(udrv,0x4000);
 printf("ul_mem_init ret %d\n",i);

 tsc_ser=tsc_last=RDTSC;
 while(1)
 {
  if((ret_code=udrv->fnc_pool(udrv))!=0)
  {
   tsc_int=RDTSC;
   printf(" !%li(%li).%li(%li)!",cycle_cnt,tsc_ser-tsc_last
   				,delay_cnt,tsc_int-tsc_last);
   tsc_last=tsc_int;
   delay_cnt=0; cycle_cnt=1; tsc_last=RDTSC;
   while((ret_code=udrv->fnc_act(udrv,ret_code))!=0) cycle_cnt++;
   tsc_ser=RDTSC;
   fflush(stdout);
  } else delay_cnt++;
 };

 i=ioperm(udrv->port,8l,0);
 printf("ioperm ret %d\n",i);
 
};

/*******************************************************************/

void main1(void)
{
 unsigned long ser_port_base=0x3E8;
 int i;

 i=ioperm(ser_port_base,8l,1);
 printf("ioperm ret %d\n",i);

 printf("Waiting for key\n");
 getchar();

 printf("Triing input\n");
 i=inb(ser_port_base);
 printf("inb ret %d\n",i);

 printf("Triing output\n");
 outb(10,ser_port_base);
 printf("out ok\n");

 i=ioperm(ser_port_base,8l,0);
 printf("ioperm ret %d\n",i);
 
};

void main2(void)
{
 U510_SBANK_FINI;
 unsigned long ser_port_base=0x3E8;
 int i;

 i=ioperm(ser_port_base,8l,1);
 printf("ioperm ret %d\n",i);

 printf("Waiting for key\n");
 getchar();

 printf("Triing output\n");
 u510_outb(ser_port_base,U510_MCR,0xAA);
 printf("out ok\n");

 printf("Triing input\n");
 i=u510_inb(ser_port_base,U510_MCR);
 printf("inb ret %X\n",i);

 printf("Triing send\n");
 u510_outb(ser_port_base,U510_MCR,U510_MCR_OE);
 sleep(1);
 u510_outb(ser_port_base,U510_TXF,0x80);
 u510_outb(ser_port_base,U510_TXD,0xA5);
 sleep(2);
 u510_outb(ser_port_base,U510_MCR,U510_MCR_IE);
 printf("send ok\n");

 printf("Triing receive\n");
 i=u510_inb(ser_port_base,U510_RXD);
 printf("receive ret %X\n",i);

 i=ioperm(ser_port_base,8l,0);
 printf("ioperm ret %d\n",i);
 
};
