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

  ul_drv.h	- driver definitions and types

  (C) Copyright 1996,1997 by Pavel Pisa 

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

#ifndef _LINUX_UL_DRV_H
#define _LINUX_UL_DRV_H

#define INLINE extern inline

/* Transfer controll byte with B8=1 */

#define	UL_ERR	0x17F
#define	UL_ARQ	0x17A
#define	UL_PRQ	0x179
#define	UL_AAP	0x176
#define	UL_BEG	0x175
#define	UL_END	0x17C

/* Transfer acknowledge byte with B8=0 */

#define	UL_ACK	0x19
#define	UL_NAK	0x7F
#define	UL_WAK	0x25

/* Memory structures */

#define UL_BLK_SIZE 64
#define UL_BLK_FSIZE (UL_MEM_BLK_SIZE-sizeof(ul_mess_head))
#define UL_BLK_FDATA(mem_blk) (mem_blk->blk.head.fdata)
#define UL_BLK_NDATA(mem_blk) (mem_blk->blk.ndata)
#define UL_BLK_HEAD(mem_blk) (mem_blk->blk.head)

/* ul_mess_head flags definition defines proccessing 
   of message or its frames stored in bll */
#define UL_BFL_LOCK 0x8000	/* locked message is pointed only once */
#define UL_BFL_MSST 0x4000      /* Message must be received by some proccess */
#define UL_BFL_M2IN 0x2000      /* After succesfull proccessing move to proc_bll */
#define UL_BFL_LNMM 0x1000      /* Length of received frame must match expected len */
#define UL_BFL_FAIL 0x0800      /* Message cannot be proccessed - error */
#define UL_BFL_TAIL 0x0400      /* Multiframe message continues by next bll block */
#define UL_BFL_SND  0x0200      /* Send this frame */
#define UL_BFL_REC  0x0100      /* Receive answer frame into this bll block */
#define UL_BFL_VERL 0x0040      /* Verify free space in buffer of destination station */
#define UL_BFL_NORE 0x0020      /* Do not try repeat if error occured */
#define UL_BFL_PRQ  0x0002      /* Request imediate proccessing of frame by receiver station */
#define UL_BFL_ARQ  0x0001      /* Request imediate acknowledge by receiving station */

typedef struct ul_mess_head {	/* head of frame */
	struct ul_mem_blk *next;/* BLL of frames */
	struct ul_mem_blk *prev;
	struct ul_blk_bll *bll;	/* BLL in which is head */
	int	 ref_cnt;	/* usage count */
	unsigned flg;		/* frame flags */
	unsigned len;		/* length of message in bytes */
	char   cmd;		/* command or service */
	char   dadr;		/* destination address */
	char   sadr;		/* source address */
	char   fdata[0]; /* data in first block */
} ul_mess_head;

typedef struct ul_mem_blk {
	struct ul_mem_blk *next; /* next relating memory block */
	union {	ul_mess_head head;
		char	ndata[UL_BLK_SIZE];
	} blk;
} ul_mem_blk;

typedef struct ul_blk_bll {	/* bidir linked list of frames */
	ul_mem_blk *first;
	ul_mem_blk *last;
	int cnt;
	struct ul_drv *udrv;	/* owener of bll list */
} ul_blk_bll;

/* Data iterator structure */

typedef struct ul_data_it {
	ul_mem_blk *blk;	/* actual proccessed memory block */
	char	*ptr;		/* pointer to actual data byte */
	int	pos;		/* actual position for seek operations */
	ul_mem_blk *head_blk;	/* first memory block of message */
	int	trans_len;	/* for send/rec routines */
} ul_data_it;

INLINE void ul_di_init(ul_data_it *di,ul_mem_blk *head_blk)
{
 di->blk=di->head_blk=head_blk;
 di->pos=0;
 di->ptr=UL_BLK_HEAD(head_blk).fdata;
};

INLINE void ul_di_inc(ul_data_it *di)
{
 di->ptr++;
 di->pos++;
};

INLINE int ul_di_adjust(ul_data_it *di)
{
 int offs;
 while((offs=di->ptr-UL_BLK_NDATA(di->blk)-UL_BLK_SIZE)>=0)
 {
  if(!di->blk->next) return 0;
  di->blk=di->blk->next;
  di->ptr=UL_BLK_NDATA(di->blk)+offs;
 };
 return 1;
};

INLINE char *ul_di_byte(ul_data_it *di)
{
 return di->ptr;
};

/* Chip driver ctrl functions */

#define	UL_CC_DIN  1
#define	UL_CC_DOUT 2

/* Driver controll structure */

typedef int(ul_call_fnc)(struct ul_drv *udrv, int ret_code);

typedef struct ul_drv {
	int	magic;		/* magic number */
	/* parameters used by chip driver */
	int	baud_div;	/* used baud divisor */
	int	port;		/* base port number */
	int	irq;		/* irq number */
	int	flags;		/* flags */
	void	*chip_info;	/* additional info for chip */
	/* function sequencer */
	ul_call_fnc *fnc_act;	/* actual state call function */
	ul_call_fnc *fnc_stack[10]; /* stack of interupted calls */
	ul_call_fnc **fnc_sp;	/* pointer into stack */
	ul_call_fnc *fnc_recch;	/* character receive function */
	ul_call_fnc *fnc_sndch;	/* character send function */
	ul_call_fnc *fnc_wait;	/* waits for time or receive */
	ul_call_fnc *fnc_connect;/* connect to line */
	int (*fnc_pool)(struct ul_drv *udrv);/* test interrupt flags state */
	int (*fnc_cctrl)(struct ul_drv *udrv,int ctrl_fnc);/* chip drv ctrl */
	unsigned char_buff;	/* rec/snd char buffer */
	unsigned wait_time;	/* time limit for fnc_wait */
	unsigned xor_sum;	/* xor+1 checksum */
	unsigned last_ctrl;	/* last snd/rec controll char */
	/* message and frame proccessor */
	ul_mem_blk *con_message;/* proccessed message chain */
	ul_mem_blk *con_frame;	/* actual proccessed frame */
	unsigned   char my_adr;	/* addres of this computer */
	unsigned   char con_dadr;/* connection destination address */
	unsigned   char con_sadr;/* connection sender address */
	unsigned   char con_cmd;/* connection command */
	unsigned   char con_flg;/* connection frame flags */
	ul_data_it con_di;	/* data iterator for transmit/receive */
	/* memory management */
	void	   *mem_ptr;	/* pointer to allocated memory */
	ul_mem_blk *free_blk;	/* single linked list of free blks */
	int     free_blk_cnt;	/* number of block on free list */
	ul_blk_bll prep_bll;	/* list of frames prepared for output */
	ul_blk_bll proc_bll;	/* list of proccessed frames */
} ul_drv;

#define UL_FNEXT(FNEXT)	{udrv->fnc_act=&FNEXT;}
#define UL_FCALL(FCALL)	{*(udrv->fnc_sp++)=udrv->fnc_act;\
			 udrv->fnc_act=&FCALL;}
#define UL_FCALL2(FCALL,FNEXT) \
			{*(udrv->fnc_sp++)=FNEXT;\
			 udrv->fnc_act=&FCALL;}
#define UL_FRET		{udrv->fnc_act=*(--udrv->fnc_sp);}

#define UL_DRV_LOCK
#define UL_DRV_UNLOCK

/* get one free block or NULL */
INLINE ul_mem_blk *ul_alloc_blk(ul_drv *udrv)
{
 ul_mem_blk *p;
 UL_DRV_LOCK;
 if((p=udrv->free_blk)!=NULL)
 {
  udrv->free_blk_cnt--;
  udrv->free_blk=p->next;
  p->next=NULL;
 };
 UL_DRV_UNLOCK;
 return p;
};

/* free one block */
INLINE void ul_free_blk(ul_drv *udrv, ul_mem_blk *blk)
{
 UL_DRV_LOCK;
 blk->next=udrv->free_blk;
 udrv->free_blk=blk;
 udrv->free_blk_cnt++;
 UL_DRV_UNLOCK;
};

#endif /* _LINUX_UL_DRV_H */
