sam7fc/src/fifo.c

251 lines
6.1 KiB
C
Raw Normal View History

2008-02-03 21:41:39 +01:00
/***************************************************************************
2008-06-23 15:33:07 +02:00
* sam7fc - FIFOs for use with PDC / USB Hardware *
* *
2008-02-03 21:41:39 +01:00
* Copyright (C) 01/2008 by Olaf Rempel *
* razzor@kopf-tisch.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; version 2 of the License *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
2008-02-06 15:30:54 +01:00
#include <stdio.h>
2008-02-03 21:41:39 +01:00
#include "AT91SAM7S256.h"
#include "atomic.h"
#include "fifo.h"
2008-02-06 14:20:47 +01:00
#include "memalloc.h"
2008-02-03 21:41:39 +01:00
#define FIFO_MASK(x) ((x)->size -1)
/*
* get used bytes (under lock)
* all other operations don't need locks:
2008-02-06 14:20:47 +01:00
* - only fifo_put/fifo_rxpdc are allowed to increment fifo->in
2008-02-03 21:41:39 +01:00
* - only fifo_get/fifo_txpdc are allowed to increment fifo->out
2008-06-23 15:33:07 +02:00
* FIXME: a integer overflow (4gb) of fifo->in / fifo->out could cause trouble
2008-02-03 21:41:39 +01:00
*/
static uint32_t fifo_used(struct fifo *fifo)
{
disable_irqs();
uint32_t used = fifo->in - fifo->out;
restore_irqs();
2008-02-06 14:20:47 +01:00
return used;
2008-02-03 21:41:39 +01:00
}
/*
* append data to fifo
* returns number of bytes copied
*/
2008-02-06 17:39:53 +01:00
uint32_t fifo_put(struct fifo *fifo, char *buf, uint32_t len)
2008-02-03 21:41:39 +01:00
{
uint32_t left = fifo->size - fifo_used(fifo);
if (len > left)
2008-02-06 17:39:53 +01:00
return 0;
2008-02-03 21:41:39 +01:00
2008-02-06 14:20:47 +01:00
uint32_t count = len;
2008-02-03 21:41:39 +01:00
while (count--)
fifo->buf[fifo->in++ & FIFO_MASK(fifo)] = *buf++;
2008-02-06 14:20:47 +01:00
2008-02-03 21:41:39 +01:00
return len;
2008-02-06 14:20:47 +01:00
}
2008-02-03 21:41:39 +01:00
/*
* get data from fifo
* returns number of bytes copied
*/
uint32_t fifo_get(struct fifo *fifo, char *buf, uint32_t len)
{
uint32_t used = fifo_used(fifo);
if (len > used)
len = used;
2008-02-06 14:20:47 +01:00
2008-02-03 21:41:39 +01:00
uint32_t count = len;
while (count--)
*buf++ = fifo->buf[fifo->out++ & FIFO_MASK(fifo)];
2008-02-06 14:20:47 +01:00
return len;
2008-02-03 21:41:39 +01:00
}
2008-02-06 15:30:54 +01:00
/*
2008-02-06 17:39:53 +01:00
* get data from fifo, without changing the internal state
2008-02-06 15:30:54 +01:00
*/
2008-02-06 17:39:53 +01:00
uint32_t fifo_peek(struct fifo *fifo, char *buf, uint32_t len)
2008-02-06 15:30:54 +01:00
{
uint32_t used = fifo_used(fifo);
if (len > used)
2008-02-06 17:39:53 +01:00
len = used;
uint32_t count = len;
uint32_t out = fifo->out;
while (count--)
*buf++ = fifo->buf[out++ & FIFO_MASK(fifo)];
return len;
}
2008-02-06 15:30:54 +01:00
2008-02-06 17:39:53 +01:00
/* removes data without reading it (eg. after a peek) */
uint32_t fifo_remove(struct fifo *fifo, uint32_t len)
{
uint32_t used = fifo_used(fifo);
if (len > used)
len = used;
fifo->out += len;
return len;
2008-02-06 15:30:54 +01:00
}
2008-02-03 21:41:39 +01:00
/*
* receive data via PDC and put it into fifo
*/
uint32_t fifo_rxpdc(struct fifo *fifo, AT91S_PDC *pdc, uint16_t maxsize)
{
/* account previous PDC transfer */
fifo->in += fifo->pdc_rx;
2008-02-06 14:20:47 +01:00
uint32_t left = fifo->size - fifo_used(fifo);
if (left) {
2008-02-03 21:41:39 +01:00
/* calc pointer for next transfer */
uint32_t first_idx = (fifo->in & FIFO_MASK(fifo));
pdc->PDC_RPR = (uint32_t)(fifo->buf + first_idx);
2008-02-06 14:20:47 +01:00
2008-02-03 21:41:39 +01:00
/* check for buffer end -> split transfer */
2008-02-06 14:20:47 +01:00
if (first_idx + left <= (fifo->size -1)) {
fifo->pdc_rx = left;
2008-02-03 21:41:39 +01:00
} else {
fifo->pdc_rx = fifo->size - first_idx;
}
/* split in maxsize chunks */
if (maxsize && fifo->pdc_rx > maxsize)
fifo->pdc_rx = maxsize;
2008-02-06 14:20:47 +01:00
/* start transfer */
2008-02-03 21:41:39 +01:00
pdc->PDC_RCR = fifo->pdc_rx;
2008-02-06 14:20:47 +01:00
2008-02-03 21:41:39 +01:00
} else {
/* no data in buffer */
fifo->pdc_rx = 0;
}
2008-02-06 14:20:47 +01:00
2008-02-03 21:41:39 +01:00
return fifo->pdc_rx;
}
/*
* transmit fifo via PDC
* returns 0 if no transfer was started
2008-02-06 14:20:47 +01:00
*/
2008-02-03 21:41:39 +01:00
uint32_t fifo_txpdc(struct fifo *fifo, AT91S_PDC *pdc, uint16_t maxsize)
{
/* account previous PDC transfer */
fifo->out += fifo->pdc_tx;
2008-02-06 14:20:47 +01:00
2008-02-03 21:41:39 +01:00
uint32_t used = fifo_used(fifo);
if (used) {
/* calc pointer for next transfer */
uint32_t first_idx = (fifo->out & FIFO_MASK(fifo));
pdc->PDC_TPR = (uint32_t)(fifo->buf + first_idx);
2008-02-06 14:20:47 +01:00
2008-02-03 21:41:39 +01:00
/* check for buffer end -> split transfer */
if (first_idx + used <= (fifo->size -1)) {
fifo->pdc_tx = used;
} else {
fifo->pdc_tx = fifo->size - first_idx;
}
/* split in maxsize chunks */
if (maxsize && fifo->pdc_tx > maxsize)
fifo->pdc_tx = maxsize;
2008-02-06 14:20:47 +01:00
/* start transfer */
2008-02-03 21:41:39 +01:00
pdc->PDC_TCR = fifo->pdc_tx;
2008-02-06 14:20:47 +01:00
2008-02-03 21:41:39 +01:00
} else {
/* no data in buffer */
fifo->pdc_tx = 0;
}
2008-02-06 14:20:47 +01:00
2008-02-03 21:41:39 +01:00
return fifo->pdc_tx;
}
2008-02-06 22:18:33 +01:00
uint32_t fifo_rxudp(struct fifo *fifo, uint32_t ep, uint32_t maxsize)
{
uint32_t left = fifo->size - fifo_used(fifo);
if (left > maxsize)
left = maxsize;
uint32_t count = left;
while (count--)
fifo->buf[fifo->in++ & FIFO_MASK(fifo)] = AT91C_UDP_FDR[ep];
return left;
}
uint32_t fifo_txudp(struct fifo *fifo, uint32_t ep, uint32_t maxsize)
{
uint32_t size = fifo_used(fifo);
if (size > maxsize)
size = maxsize;
uint32_t count = size;
while (count--)
AT91C_UDP_FDR[ep] = fifo->buf[fifo->out++ & FIFO_MASK(fifo)];
return size;
}
2008-02-03 21:41:39 +01:00
/*
* put one byte into the fifo
* returns 0 if fifo was full
*/
uint32_t fifo_putbyte(struct fifo *fifo, char c)
{
uint32_t left = fifo->size - fifo_used(fifo);
if (left) {
fifo->buf[fifo->in++ & FIFO_MASK(fifo)] = c;
return 1;
}
return 0;
}
/*
* gets one byte from fifo
* returns 0 if fifo was empty
*/
uint32_t fifo_getbyte(struct fifo *fifo, char *p)
{
uint32_t used = fifo_used(fifo);
if (used) {
*p = fifo->buf[fifo->out++ & FIFO_MASK(fifo)];
return 1;
}
return 0;
}
/*
* allocs a fifo from static_alloc space
*/
struct fifo * fifo_alloc(uint32_t size)
{
size = next_powerof2(size);
struct fifo *fifo = static_alloc(sizeof(struct fifo) + size);
fifo->size = size;
fifo->in = 0;
fifo->out = 0;
fifo->pdc_tx = 0;
fifo->pdc_rx = 0;
return fifo;
}