/*
* Common data handling layer for bas_gigaset
*
* Copyright (c) 2005 by Tilman Schmidt <tilman@imap.cc>,
* Hansjoerg Lipp <hjlipp@web.de>.
*
* =====================================================================
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* =====================================================================
*/
#include "gigaset.h"
#include <linux/crc-ccitt.h>
#include <linux/bitrev.h>
/* access methods for isowbuf_t */
/* ============================ */
/* initialize buffer structure
*/
void gigaset_isowbuf_init(struct isowbuf_t *iwb, unsigned char idle)
{
iwb->read = 0;
iwb->nextread = 0;
iwb->write = 0;
atomic_set(&iwb->writesem, 1);
iwb->wbits = 0;
iwb->idle = idle;
memset(iwb->data + BAS_OUTBUFSIZE, idle, BAS_OUTBUFPAD);
}
/* compute number of bytes which can be appended to buffer
* so that there is still room to append a maximum frame of flags
*/
static inline int isowbuf_freebytes(struct isowbuf_t *iwb)
{
int read, write, freebytes;
read = iwb->read;
write = iwb->write;
freebytes = read - write;
if (freebytes > 0) {
/* no wraparound: need padding space within regular area */
return freebytes - BAS_OUTBUFPAD;
} else if (read < BAS_OUTBUFPAD) {
/* wraparound: can use space up to end of regular area */
return BAS_OUTBUFSIZE - write;
} else {
/* following the wraparound yields more space */
return freebytes + BAS_OUTBUFSIZE - BAS_OUTBUFPAD;
}
}
/* start writing
* acquire the write semaphore
* return 0 if acquired, <0 if busy
*/
static inline int isowbuf_startwrite(struct isowbuf_t *iwb)
{
if (!atomic_dec_and_test(&iwb->writesem)) {
atomic_inc(&iwb->writesem);
gig_dbg(DEBUG_ISO, "%s: couldn't acquire iso write semaphore",
__func__);
return -EBUSY;
}
gig_dbg(DEBUG_ISO,
"%s: acquired iso write semaphore, data[write]=%02x, nbits=%d",
__func__, iwb->data[iwb->write], iwb->wbits);
return 0;
}
/* finish writing
* release the write semaphore
* returns the current write position
*/
static inline int isowbuf_donewrite(struct isowbuf_t *iwb)
{
int write = iwb->write;
atomic_inc(&iwb->writesem);
return write;
}
/* append bits to buffer without any checks
* - data contains bits to append, starting at LSB
* - nbits is number of bits to append (0..24)
* must be called with the write semaphore held
* If more than nbits bits are set in data, the extraneous bits are set in the
* buffer too, but the write position is only advanced by nbits.
*/
static inline void isowbuf_putbits(struct isowbuf_t *iwb, u32 data, int nbits)
{
int write = iwb->write;
data <<= iwb->wbits;
data |= iwb->data[write];
nbits += iwb->wbits;
while (nbits >= 8) {
iwb->data[write++] = data & 0xff;
write %= BAS_OUTBUFSIZE;
data >>= 8;
nbits -= 8;
}
iwb->wbits = nbits;
iwb->data[write] = data & 0xff;
iwb->write = write;
}
/* put final flag on HDLC bitstream
* also sets the idle fill byte to the correspondingly shifted flag pattern
* must be called with the write semaphore held
*/
static inline void isowbuf_putflag(struct isowbuf_t *iwb)
{
int write;
/* add two flags, thus reliably covering one byte */
isowbuf_putbits(iwb, 0x7e7e, 8);
/* recover the idle flag byte */
write = iwb->write;
iwb->idle = iwb->data[write];
gig_dbg(DEBUG_ISO, "idle fill byte %02x", iwb->idle);
/* mask extraneous bits in buffer */
iwb->data[write] &= (1 << iwb->wbits) - 1;
}
/* retrieve a block of bytes for sending
* The requested number of bytes is provided as a contiguous block.
* If necessary, the frame is filled to the requested number of bytes
* with the idle value.
* returns offset to frame, < 0 on busy or error
*/
int gigaset_isowbuf_getbytes(struct isowbuf_t *iwb, int size)
{
int read, write, limit, src, dst;
unsigned char pbyte;
read = iwb->nextread;
write = iwb->write;
if (likely(read == write)) {
/* return idle frame */
return read < BAS_OUTBUFPAD ?
BAS_OUTBUFSIZE : read - BAS_OUTBUFPAD;
}
limit = read + size;
gig_dbg(DEBUG_STREAM, "%s: