blob: be3be25bb898f549376945e9ad5fd6a365e40ade [file] [log] [blame]
/*
* hdlcdrv.h -- HDLC packet radio network driver.
* The Linux soundcard driver for 1200 baud and 9600 baud packet radio
* (C) 1996-1998 by Thomas Sailer, HB9JNX/AE4WA
*/
#ifndef _HDLCDRV_H
#define _HDLCDRV_H
#include <linux/netdevice.h>
#include <linux/if.h>
#include <linux/spinlock.h>
#include <uapi/linux/hdlcdrv.h>
#define HDLCDRV_MAGIC 0x5ac6e778
#define HDLCDRV_HDLCBUFFER 32 /* should be a power of 2 for speed reasons */
#define HDLCDRV_BITBUFFER 256 /* should be a power of 2 for speed reasons */
#undef HDLCDRV_LOOPBACK /* define for HDLC debugging purposes */
#define HDLCDRV_DEBUG
/* maximum packet length, excluding CRC */
#define HDLCDRV_MAXFLEN 400
struct hdlcdrv_hdlcbuffer {
spinlock_t lock;
unsigned rd, wr;
unsigned short buf[HDLCDRV_HDLCBUFFER];
};
#ifdef HDLCDRV_DEBUG
struct hdlcdrv_bitbuffer {
unsigned int rd;
unsigned int wr;
unsigned int shreg;
unsigned char buffer[HDLCDRV_BITBUFFER];
};
static inline void hdlcdrv_add_bitbuffer(struct hdlcdrv_bitbuffer *buf,
unsigned int bit)
{
unsigned char new;
new = buf->shreg & 1;
buf->shreg >>= 1;
buf->shreg |= (!!bit) << 7;
if (new) {
buf->buffer[buf->wr] = buf->shreg;
buf->wr = (buf->wr+1) % sizeof(buf->buffer);
buf->shreg = 0x80;
}
}
static inline void hdlcdrv_add_bitbuffer_word(struct hdlcdrv_bitbuffer *buf,
unsigned int bits)
{
buf->buffer[buf->wr] = bits & 0xff;
buf->wr = (buf->wr+1) % sizeof(buf->buffer);
buf->buffer[buf->wr] = (bits >> 8) & 0xff;
buf->wr = (buf->wr+1) % sizeof(buf->buffer);
}
#endif /* HDLCDRV_DEBUG */
/* -------------------------------------------------------------------- */
/*
* Information that need to be kept for each driver.
*/
struct hdlcdrv_ops {
/*
* first some informations needed by the hdlcdrv routines
*/
const char *drvname;
const char *drvinfo;
/*
* the routines called by the hdlcdrv routines
*/
int (*open)(struct net_device *);
int (*close)(struct net_device *);
int (*ioctl)(struct net_device *, struct ifreq *,
struct hdlcdrv_ioctl *, int);
};
struct hdlcdrv_state {
int magic;
int opened;
const struct hdlcdrv_ops *ops;
struct {
int bitrate;
} par;
struct hdlcdrv_pttoutput {
int dma2;
int seriobase;
int pariobase;
int midiiobase;
unsigned int flags;
} ptt_out;
struct hdlcdrv_channel_params ch_params;
struct hdlcdrv_hdlcrx {
struct hdlcdrv_hdlcbuffer hbuf;
unsigned long in_hdlc_rx;
/* 0 = sync hunt, != 0 receiving */
int rx_state;
unsigned int bitstream;
unsigned int bitbuf;
int numbits;
unsigned char dcd;
int len;
unsigned char *bp;
unsigned char buffer[HDLCDRV_MAXFLEN+2];
} hdlcrx;
struct hdlcdrv_hdlctx {
struct hdlcdrv_hdlcbuffer hbuf;
unsigned long in_hdlc_tx;
/*
* 0 = send flags
* 1 = send txtail (flags)
* 2 = send packet
*/
int tx_state;
int numflags;
unsigned int bitstream;
unsigned char ptt;
int calibrate;
int slotcnt;
unsigned int bitbuf;
int numbits;
int len;
unsigned char *bp;
unsigned char buffer[HDLCDRV_MAXFLEN+2];
} hdlctx;
#ifdef HDLCDRV_DEBUG
struct hdlcdrv_bitbuffer bitbuf_channel;
struct hdlcdrv_bitbuffer bitbuf_hdlc;
#endif /* HDLCDRV_DEBUG */
int ptt_keyed;
/* queued skb for transmission */
struct sk_buff *skb;
};
/* -------------------------------------------------------------------- */
static inline int hdlcdrv_hbuf_full(struct hdlcdrv_hdlcbuffer *hb)
{
unsigned long flags;
int ret;
spin_lock_irqsave(&hb->lock, flags);
ret = !((HDLCDRV_HDLCBUFFER - 1 + hb->rd - hb->wr) % HDLCDRV_HDLCBUFFER);
spin_unlock_irqrestore(&hb->lock, flags);
return ret;
}
/* -------------------------------------------------------------------- */
static inline int hdlcdrv_hbuf_empty(struct hdlcdrv_hdlcbuffer *hb)
{
unsigned long flags;
int ret;
spin_lock_irqsave(&hb->lock, flags);
ret = (hb->rd == hb->wr);
spin_unlock_irqrestore(&hb->lock, flags);
return ret;
}
/* -------------------------------------------------------------------- */
static inline unsigned short hdlcdrv_hbuf_get(struct hdlcdrv_hdlcbuffer *hb)
{
unsigned long flags;
unsigned short val;
unsigned newr;
spin_lock_irqsave(&hb->lock, flags);
if (hb->rd == hb->wr)
val = 0;
else {
newr = (hb->rd+1) % HDLCDRV_HDLCBUFFER;
val = hb->buf[hb->rd];
hb->rd = newr;
}
spin_unlock_irqrestore(&hb->lock, flags);
return val;
}
/* -------------------------------------------------------------------- */
static inline void hdlcdrv_hbuf_put(struct hdlcdrv_hdlcbuffer *hb,
unsigned short val)
{
unsigned newp;
unsigned long flags;
spin_lock_irqsave(&hb->lock, flags);
newp = (hb->wr+1) % HDLCDRV_HDLCBUFFER;
if (newp != hb->rd) {
hb->buf[hb->wr] = val & 0xffff;
hb->wr = newp;
}
spin_unlock_irqrestore(&hb->lock, flags);
}
/* -------------------------------------------------------------------- */
static inline void hdlcdrv_putbits(struct hdlcdrv_state *s, unsigned int bits)
{
hdlcdrv_hbuf_put(&s->hdlcrx.hbuf, bits);
}
static inline unsigned int hdlcdrv_getbits(struct hdlcdrv_state *s)
{
unsigned int ret;
if (hdlcdrv_hbuf_empty(&s->hdlctx.hbuf)) {
if (s->hdlctx.calibrate > 0)
s->hdlctx.calibrate--;
else
s->hdlctx.ptt = 0;
ret = 0;
} else
ret = hdlcdrv_hbuf_get(&s->hdlctx.hbuf);
#ifdef HDLCDRV_LOOPBACK
hdlcdrv_hbuf_put(&s->hdlcrx.hbuf, ret);
#endif /* HDLCDRV_LOOPBACK */
return ret;
}
static inline void hdlcdrv_channelbit(struct hdlcdrv_state *s, unsigned int bit)
{
#ifdef HDLCDRV_DEBUG
hdlcdrv_add_bitbuffer(&s->bitbuf_channel, bit);
#endif /* HDLCDRV_DEBUG */
}
static inline void hdlcdrv_setdcd(struct hdlcdrv_state *s, int dcd)
{
s->hdlcrx.dcd = !!dcd;
}
static inline int hdlcdrv_ptt(struct hdlcdrv_state *s)
{
return s->hdlctx.ptt || (s->hdlctx.calibrate > 0);
}
/* -------------------------------------------------------------------- */
void hdlcdrv_receiver(struct net_device *, struct hdlcdrv_state *);
void hdlcdrv_transmitter(struct net_device *, struct hdlcdrv_state *);
void hdlcdrv_arbitrate(struct net_device *, struct hdlcdrv_state *);
struct net_device *hdlcdrv_register(const struct hdlcdrv_ops *ops,
unsigned int privsize, const char *ifname,
unsigned int baseaddr, unsigned int irq,
unsigned int dma);
void hdlcdrv_unregister(struct net_device *dev);
/* -------------------------------------------------------------------- */
#endif /* _HDLCDRV_H */