diff options
Diffstat (limited to 'src/drivers/ao_bufio.c')
| -rw-r--r-- | src/drivers/ao_bufio.c | 321 | 
1 files changed, 321 insertions, 0 deletions
| diff --git a/src/drivers/ao_bufio.c b/src/drivers/ao_bufio.c new file mode 100644 index 00000000..c0fe604a --- /dev/null +++ b/src/drivers/ao_bufio.c @@ -0,0 +1,321 @@ +/* + * Copyright © 2013 Keith Packard <keithp@keithp.com> + * + * 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. + */ + +#ifndef AO_FAT_TEST +#include "ao.h" +#endif + +/* Include bufio commands */ +#ifndef AO_FAT_TEST +#define BUFIO_COMMANDS	0 +#endif + +#include "ao_sdcard.h" +#include "ao_bufio.h" + +#define AO_NUM_BUF		16 +#define AO_BUFSIZ		512 + +struct ao_bufio { +	uint32_t	block; +	int16_t		seqno; +	uint8_t		busy;	 +	uint8_t		dirty; +}; + +static struct ao_bufio	ao_bufio[AO_NUM_BUF]; +static uint8_t		ao_buffer[AO_NUM_BUF][AO_BUFSIZ]; +static int16_t		ao_seqno; +static uint8_t		ao_bufio_mutex; + +#if 0 +#define DBG(...) printf(__VA_ARGS__) +#else +#define DBG(...) +#endif + +static inline void +ao_bufio_lock(void) +{ +	ao_mutex_get(&ao_bufio_mutex); +} + +static inline void +ao_bufio_unlock(void) +{ +	ao_mutex_put(&ao_bufio_mutex); +} + +static inline int16_t +ao_seqno_age(int16_t s) +{ +	return ao_seqno - s; +} + +static inline int16_t +ao_seqno_next(void) +{ +	return ++ao_seqno; +} + +static inline int +ao_seqno_older(int16_t a, int16_t b) +{ +	return ao_seqno_age(a) > ao_seqno_age(b); +} + +static inline void +ao_validate_bufno(int b) +{ +	if (b < 0 || AO_NUM_BUF <= b) +		ao_panic(AO_PANIC_BUFIO); +} + +static inline int +ao_buf_to_num(uint8_t *buf) +{ +	int b = (buf - &ao_buffer[0][0]) / AO_BUFSIZ; + +	ao_validate_bufno(b); +	return b; +} + +static inline int +ao_bufio_to_num(struct ao_bufio *bufio) +{ +	int b = (bufio - ao_bufio); + +	ao_validate_bufno(b); +	return b; +} + +static inline struct ao_bufio * +ao_buf_to_bufio(uint8_t *buf) +{ +	int b = ao_buf_to_num(buf); +	struct ao_bufio *bufio; + +	bufio = &ao_bufio[b]; +	DBG ("buf %08x is %d bufio %08x\n", buf, b, bufio); +	return bufio; +} + +static inline uint8_t * +ao_bufio_to_buf(struct ao_bufio *bufio) +{ +	int b = ao_bufio_to_num(bufio); +	uint8_t *buf; + +	buf = &ao_buffer[b][0]; +	DBG ("bufio %08x is %d buf %08x\n", bufio, b, buf); +	return buf; +} + +/* + * Write a buffer to storage if it is dirty + */ +static void +ao_bufio_write(struct ao_bufio *bufio) +{ +	if (bufio->dirty) { +		ao_sdcard_write_block(bufio->block, ao_bufio_to_buf(bufio)); +		bufio->dirty = 0; +	} +} + +/* + * Read a buffer from storage + */ +static uint8_t +ao_bufio_read(struct ao_bufio *bufio) +{ +	uint8_t	*buf = ao_bufio_to_buf(bufio); + +	return ao_sdcard_read_block(bufio->block, buf); +} + +/* + * Find a buffer containing the specified block + */ +static struct ao_bufio * +ao_bufio_find_block(uint32_t block) +{ +	int b; + +	for (b = 0; b < AO_NUM_BUF; b++) { +		struct ao_bufio *bufio = &ao_bufio[b]; +		if (bufio->block == block) { +			DBG ("Found existing buffer %d (seqno %d)\n", +				ao_bufio_to_num(bufio), bufio->seqno); +			return bufio; +		} +	} +	return NULL; +} + +/* + * Find the least recently used idle buffer + */ +static struct ao_bufio * +ao_bufio_find_idle(void) +{ +	int b; +	struct ao_bufio *oldest = NULL; + +	for (b = 0; b < AO_NUM_BUF; b++) { +		struct ao_bufio *bufio = &ao_bufio[b]; +		if (!bufio->busy) +			if (!oldest || ao_seqno_older(bufio->seqno, oldest->seqno)) +				oldest = bufio; +	} +	if (oldest) +		DBG ("Using idle buffer %d (seqno %d)\n", +			ao_bufio_to_num(oldest), oldest->seqno); +	return oldest; +} + +/* + * Return a pointer to a buffer containing + * the contents of the specified block + */ +uint8_t * +ao_bufio_get(uint32_t block) +{ +	struct ao_bufio *bufio; +	uint8_t	*buf = NULL; + +	ao_bufio_lock(); +	bufio = ao_bufio_find_block(block); +	if (!bufio) { +		bufio = ao_bufio_find_idle(); +		if (bufio) { +			ao_bufio_write(bufio); +			bufio->block = block; +			DBG ("read buffer\n"); +			if (!ao_bufio_read(bufio)) { +				bufio->block = 0xffffffff; +				bufio = NULL; +			} +		} else +			ao_panic(AO_PANIC_BUFIO); +	} +	if (bufio) { +		bufio->busy++; +		if (!bufio->busy) +			ao_panic(AO_PANIC_BUFIO); +		buf = ao_bufio_to_buf(bufio); +	} +	ao_bufio_unlock(); +	return buf; +} + +/* + * Release a buffer, marking it dirty + * if it has been written to + */ +void +ao_bufio_put(uint8_t *buf, uint8_t write) +{ +	struct ao_bufio *bufio; + +	ao_bufio_lock(); +	bufio = ao_buf_to_bufio(buf); +	 +	if (!bufio->busy) +		ao_panic(AO_PANIC_BUFIO); + +	DBG ("idle buffer %d write %d\n", ao_bufio_to_num(bufio), write); +	bufio->dirty |= write; +	if (!--bufio->busy) { +		bufio->seqno = ao_seqno_next(); +		DBG ("not busy, seqno %d\n", bufio->seqno); +	} +	ao_bufio_unlock(); +} + +/* + * Flush a single buffer immediately. Useful + * if write order is important + */ +void +ao_bufio_flush_one(uint8_t *buf) +{ +	ao_bufio_lock(); +	ao_bufio_write(ao_buf_to_bufio(buf)); +	ao_bufio_unlock(); +} + +/* + * Flush all buffers to storage + */ +void +ao_bufio_flush(void) +{ +	int	b; + +	ao_bufio_lock(); +	for (b = 0; b < AO_NUM_BUF; b++) +		ao_bufio_write(&ao_bufio[b]); +	ao_bufio_unlock(); +} + +#if BUFIO_COMMANDS +static void +ao_bufio_test_read(void) +{ +	uint8_t	*buf; +	ao_cmd_decimal(); +	if (ao_cmd_status != ao_cmd_success) +		return; +	if ((buf = ao_bufio_get(ao_cmd_lex_u32))) { +		int i; +		for (i = 0; i < 512; i++) { +			printf (" %02x", buf[i]); +			if ((i & 0xf) == 0xf) +				printf("\n"); +		} +		ao_bufio_put(buf, 0); +	} +} + +static const struct ao_cmds ao_bufio_cmds[] = { +	{ ao_bufio_test_read,	"q\0Test bufio read" }, +	{ 0, NULL }, +}; +#endif + +void +ao_bufio_setup(void) +{ +	int b; + +	for (b = 0; b < AO_NUM_BUF; b++) { +		ao_bufio[b].dirty = 0; +		ao_bufio[b].busy = 0; +		ao_bufio[b].block = 0xffffffff; +	} +} + +void +ao_bufio_init(void) +{ +	ao_bufio_setup(); +	ao_sdcard_init(); +#if BUFIO_COMMANDS +	ao_cmd_register(&ao_bufio_cmds[0]); +#endif +} | 
