| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623 |
- /*
- * Copyright 2010 by egnite GmbH
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the copyright holders nor the names of
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
- * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * For additional information see http://www.ethernut.de/
- */
- /*
- * \file gorp/buffer/perci.c
- * \brief Persistent circular buffer.
- *
- * Applications may use this module for logging any kind of data.
- *
- * \verbatim
- * $Id$
- * \endverbatim
- */
- #include <io.h>
- #include <fcntl.h>
- #include <sys/stat.h>
- #include <string.h>
- #include <stdarg.h>
- #include <time.h>
- #include <errno.h>
- #include <sys/nutdebug.h>
- #include <sys/event.h>
- #include <gorp/perci.h>
- /*!
- * \addtogroup xgPerci
- */
- /*@{*/
- //#define NUTDEBUG
- /*!
- * \brief Dump ring buffer file to given stream.
- *
- * \param path Path name of the file.
- * \param stream Stream to dump the contents to.
- */
- void PerCiDump(FILE *stream, char *path)
- {
- #ifdef NUTDEBUG
- int fd;
- int got;
- perci_fast_reclen_t i;
- perci_fast_reclen_t ll;
- perci_recnum_t recnum;
- perci_reclen_t reclen;
- uint8_t *recbuf;
- fd = _open(path, _O_RDWR | _O_BINARY);
- if (fd == -1) {
- fprintf(stream, "Error %d opening %s\n", errno, path);
- return;
- } else {
- fprintf(stream, "Dump %ld bytes in %s\n", _filelength(fd), path);
- recbuf = malloc(PERCI_DATASIZE);
- for (recnum = 0;; recnum++) {
- got = _read(fd, &reclen, sizeof(perci_reclen_t));
- if (got <= 0) {
- break;
- }
- fprintf(stream, "%03u %03u ", recnum, (unsigned int)reclen);
- got = _read(fd, recbuf, PERCI_DATASIZE);
- for (i = 0, ll = 0; i < (perci_fast_reclen_t) got && i <= (perci_fast_reclen_t) reclen; i++) {
- if (recbuf[i] < ' ') {
- ll += 2;
- if (recbuf[i] == '\n') {
- fputs("\\n", stream);
- }
- else if (recbuf[i] == '\r') {
- fputs("\\r", stream);
- }
- else if (recbuf[i] == '\t') {
- fputs("\\t", stream);
- } else {
- fprintf(stream, "\\x%02x", recbuf[i]);
- ll += 2;
- }
- } else {
- fputc(recbuf[i], stream);
- ll++;
- }
- if (ll > 80) {
- fprintf(stream, "...");
- break;
- }
- }
- fputc('\n', stream);
- }
- _close(fd);
- free(recbuf);
- }
- #endif
- }
- /*!
- * \brief Initialize a ring buffer file.
- *
- * If the file doesn't exist, a new file will be created. If the file
- * exists, any buffered data will be erased.
- *
- * The total size of the file is
- * \code
- * recs * PERCI_RECSIZE + sizeof(perci_reclen_t)
- * \endcode
- * bytes.
- *
- * The available data space is
- * \code
- * recs * PERCI_DATASIZE
- * \endcode
- * bytes.
- *
- * \param path Path name of the file.
- * \param recs Number of buffer records, at least 2.
- *
- * \return 0 on success and -1 on failure.
- */
- int PerCiInit(char *path, int recs)
- {
- int fd;
- int i;
- PERCI_RECORD *rec;
- /* Check function parameters. */
- NUTASSERT(path != NULL);
- NUTASSERT(recs >= 2);
- fd = _open(path, _O_RDWR | _O_CREAT | _O_TRUNC | _O_BINARY);
- if (fd == -1) {
- return -1;
- }
- rec = calloc(PERCI_RECSIZE, 1);
- if (rec) {
- for (i = 0; i < recs; i++) {
- _write(fd, rec, PERCI_RECSIZE);
- }
- _write(fd, rec, sizeof(perci_reclen_t));
- free(rec);
- }
- _close(fd);
- return 0;
- }
- /*!
- * \brief Open a ring buffer file.
- *
- * The file must have been created by a previous call to PerCiInit().
- *
- * The function failes, if the file doesn't exist or if it is corrupted.
- * Applications may use the following sequence to open a circular buffer.
- * \code
- * PERCI_WRITER *perci;
- * char *path = "UFLASH0:data.log";
- *
- * while ((perci = PerCiOpen(path)) == NULL) {
- * if (PerCiInit(path, 128)) {
- * printf("Error %d creating %s\n", errno, path);
- * break;
- * }
- * }
- * \endcode
- *
- * \param path Path name of the file.
- *
- * \return A pointer to a PERCI_WRITER structure on success. The return
- * of a NULL pointer indicates a failure.
- */
- PERCI_WRITER *PerCiOpen(char *path)
- {
- uint_fast8_t ok = 0;
- PERCI_WRITER *writer;
- perci_reclen_t reclen;
- /* Check function parameter. */
- NUTASSERT(path != NULL);
- /* Allocate a writer structure and open the file. */
- writer = calloc(1, sizeof(PERCI_WRITER));
- if (writer) {
- /* Open the file. If this fails, release the writer structure
- and return a NULL pointer. */
- writer->pcw_fd = _open(path, _O_RDWR | _O_BINARY);
- if (writer->pcw_fd != -1) {
- /* File exists, determine its size. If it doesn't contain at
- least 2 records, we consider it corrupted. */
- writer->pcw_size = _filelength(writer->pcw_fd);
- if (writer->pcw_size >= 2 * PERCI_RECSIZE + sizeof(perci_reclen_t)) {
- writer->pcw_size -= sizeof(perci_reclen_t);
- /*
- * Scan the file for the next available record.
- */
- _seek(writer->pcw_fd, 0, SEEK_SET);
- for (writer->pcw_recnum = 0; writer->pcw_recnum < PERCI_MAX_RECORDS; writer->pcw_recnum++) {
- /* Read the length of the current record. */
- if (_read(writer->pcw_fd, &reclen, sizeof(perci_reclen_t)) != sizeof(perci_reclen_t)) {
- /* Corrupted. */
- break;
- }
- /* If this record isn't completely filled, then we continue
- writing at this point. */
- if (reclen < PERCI_DATASIZE) {
- /* Fill the record buffer. */
- writer->pcw_rec.pcd_len = reclen;
- if (reclen && _read(writer->pcw_fd, writer->pcw_rec.pcd_data, reclen) != reclen) {
- break;
- }
- NutEventPost(&writer->pcw_mutex);
- ok = 1;
- break;
- }
- /* This record is filled, move to the next one. */
- _seek(writer->pcw_fd, PERCI_DATASIZE, SEEK_CUR);
- }
- }
- }
- }
- /* Release resources on error. */
- if (!ok && writer) {
- if (writer->pcw_fd != -1) {
- _close(writer->pcw_fd);
- }
- free(writer);
- writer = NULL;
- }
- return writer;
- }
- /*!
- * \brief Flush the current write buffer.
- *
- * \param writer Pointer to a PERCI_WRITER structure obtained by a
- * previous call to PerCiOpen().
- */
- void PerCiFlush(PERCI_WRITER * writer)
- {
- /* Check function parameter. */
- NUTASSERT(writer != NULL);
- NUTASSERT(writer->pcw_fd != -1);
- _seek(writer->pcw_fd, writer->pcw_recnum * PERCI_RECSIZE, SEEK_SET);
- _write(writer->pcw_fd, &writer->pcw_rec, sizeof(perci_reclen_t) + writer->pcw_rec.pcd_len);
- }
- /*!
- * \brief Close a ring buffer file.
- *
- * \param writer Pointer to a PERCI_WRITER structure obtained by a
- * previous call to PerCiOpen().
- */
- void PerCiClose(PERCI_WRITER * writer)
- {
- /* Check function parameter. */
- NUTASSERT(writer != NULL);
- if (writer->pcw_rec.pcd_len) {
- PerCiFlush(writer);
- }
- _close(writer->pcw_fd);
- free(writer);
- }
- /*!
- * \brief Write to a ring buffer file.
- *
- * \param writer Pointer to a PERCI_WRITER structure obtained by a
- * previous call to PerCiOpen().
- *
- * \return The number of bytes successfully written or -1 on failure.
- */
- int PerCiWrite(PERCI_WRITER * writer, const char *data, int len)
- {
- perci_fast_reclen_t cnt = 0;
- perci_fast_reclen_t num;
- perci_fast_reclen_t reclen;
- /* Check parameters. */
- NUTASSERT(writer != NULL);
- NUTASSERT(writer->pcw_fd != -1);
- NUTASSERT(writer->pcw_rec.pcd_len <= PERCI_DATASIZE);
- NutEventWait(&writer->pcw_mutex, NUT_WAIT_INFINITE);
- while ((perci_fast_reclen_t) len > cnt) {
- /* Calculate the number of bytes to write in the next step. */
- reclen = (perci_fast_reclen_t) writer->pcw_rec.pcd_len;
- num = (perci_fast_reclen_t) len - cnt;
- if (num > PERCI_DATASIZE - reclen) {
- num = PERCI_DATASIZE - reclen;
- }
- /* Move the bytes to the record buffer. */
- memcpy(&writer->pcw_rec.pcd_data[reclen], data, num);
- writer->pcw_rec.pcd_len += num;
- cnt += num;
- data += num;
- /* If the buffered record is completely filled, then write it
- back to the file and claim the next one. Note, that we write
- a whole record, which is sizeof(perci_reclen_t) larger than
- the real record size. This way we automatically override the
- length of the following record with zero. */
- if (writer->pcw_rec.pcd_len == PERCI_DATASIZE) {
- _seek(writer->pcw_fd, writer->pcw_recnum * PERCI_RECSIZE, SEEK_SET);
- if (_write(writer->pcw_fd, &writer->pcw_rec, sizeof(PERCI_RECORD)) != sizeof(PERCI_RECORD)) {
- NutEventPost(&writer->pcw_mutex);
- return -1;
- }
- writer->pcw_rec.pcd_len = 0;
- writer->pcw_recnum++;
- /* Check for wrap around. */
- if (writer->pcw_recnum * PERCI_RECSIZE >= writer->pcw_size) {
- writer->pcw_recnum = 0;
- _seek(writer->pcw_fd, 0, SEEK_SET);
- if (_write(writer->pcw_fd, &writer->pcw_rec.pcd_len, sizeof(perci_reclen_t)) != sizeof(perci_reclen_t)) {
- NutEventPost(&writer->pcw_mutex);
- return -1;
- }
- }
- }
- }
- NutEventPost(&writer->pcw_mutex);
- return cnt;
- }
- /*!
- * \brief Write formatted line to a ring buffer file.
- *
- * Alternate form of PerCiWriteFormat(), in which the arguments have
- * already been captured using the variable-length argument facilities.
- *
- * \param writer Pointer to a PERCI_WRITER structure obtained by a
- * previous call to PerCiOpen().
- * \param fmt Format string containing conversion specifications
- * like printf.
- * \param ap Pointer to the list of arguments.
- *
- * \return The number of bytes successfully written or -1 on failure.
- */
- int PerCiWriteVarList(PERCI_WRITER * writer, const char *fmt, va_list ap)
- {
- int cnt;
- char *line;
- /* Check function parameter. */
- NUTASSERT(fmt != NULL);
- line = malloc(PERCI_DATASIZE);
- if (line) {
- cnt = vsnprintf(line, PERCI_DATASIZE, fmt, ap);
- cnt = PerCiWrite(writer, line, strlen(line));
- free(line);
- } else {
- cnt = -1;
- }
- return cnt;
- }
- /*!
- * \brief Write formatted line to a ring buffer file.
- *
- * \param writer Pointer to a PERCI_WRITER structure obtained by a
- * previous call to PerCiOpen().
- * \param fmt Format string containing conversion specifications
- * like printf.
- *
- * \return The number of bytes successfully written or -1 on failure.
- */
- int PerCiWriteFormat(PERCI_WRITER * writer, const char *fmt, ...)
- {
- int rc;
- va_list ap;
- /* Check function parameter. */
- NUTASSERT(fmt != NULL);
- va_start(ap, fmt);
- rc = PerCiWriteVarList(writer, fmt, ap);
- va_end(ap);
- return rc;
- }
- /*!
- * \brief Find next record with data.
- *
- * \param writer Pointer to a PERCI_WRITER structure obtained by a
- * previous call to PerCiOpen().
- * \param recnum Pointer to a variable, which contains the number of
- * the start record upon entry and which will contain
- * the number of the record found upon exit.
- *
- * \return The number of bytes contained in the record found, or 0 if
- * no more records with data are available.
- */
- static perci_fast_reclen_t FindNextData(PERCI_WRITER * writer, perci_fast_recnum_t * recnum)
- {
- perci_reclen_t reclen;
- long pos;
- int got;
- /* Check parameters. */
- NUTASSERT(writer != NULL);
- NutEventWait(&writer->pcw_mutex, NUT_WAIT_INFINITE);
- pos = *recnum * PERCI_RECSIZE;
- for (;;) {
- /* If we reached the end of the file, then continue at its
- beginning. */
- if (pos >= writer->pcw_size) {
- pos = 0;
- *recnum = 0;
- }
- /* If we reached the latest record, then return the number
- of bytes in the write buffer. */
- if (*recnum == writer->pcw_recnum) {
- reclen = writer->pcw_rec.pcd_len;
- break;
- }
- /* Otherwise read the number of bytes available in the record.
- This value is stored at the beginning of each record. */
- _seek(writer->pcw_fd, pos, SEEK_SET);
- got = _read(writer->pcw_fd, &reclen, sizeof(perci_reclen_t));
- if (got == sizeof(perci_reclen_t) && reclen) {
- break;
- }
- /* Either this record is empty or the read failed, which we
- sliently ignore for now. Move on to the next record. */
- pos += PERCI_RECSIZE;
- (*recnum)++;
- }
- NutEventPost(&writer->pcw_mutex);
- return reclen;
- }
- /*!
- * \brief Start reading from a ring buffer file.
- *
- * It is assumed, that the ring buffer is kept open while the application
- * is running. From time to time the buffered data will be retrieved.
- * Therefore, a reader is attached to a previously open file and reading
- * starts at the oldest record.
- *
- * When all data has been read, the application must call PerCiDetachReader()
- * to relase all allocated resources for reading.
- *
- * Multiple readers may be concurrently attached to the same file.
- *
- * \param writer Pointer to a PERCI_WRITER structure obtained by a
- * previous call to PerCiOpen().
- *
- * \return A pointer to a PERCI_READER structure on success. The return
- * of a NULL pointer indicates a failure.
- */
- PERCI_READER *PerCiAttachReader(PERCI_WRITER * writer)
- {
- PERCI_READER *reader;
- /* Check parameters. */
- NUTASSERT(writer != NULL);
- reader = malloc(sizeof(PERCI_READER));
- reader->pcr_cil = writer;
- reader->pcr_recpos = 0;
- /* Search the oldest record that contains data. Start with the one
- above the current record of the writer. */
- reader->pcr_recnum = writer->pcw_recnum + 1;
- reader->pcr_reclen = FindNextData(writer, &reader->pcr_recnum);
- return reader;
- }
- /*!
- * \brief Stop reading from a ring buffer file.
- *
- * \param reader Pointer to a PERCI_READER structure obtained by a
- * previous call to PerCiAttachReader().
- */
- void PerCiDetachReader(PERCI_READER * reader)
- {
- /* Check parameters. */
- NUTASSERT(reader != NULL);
- free(reader);
- }
- /*!
- * \brief Read data from a ring buffer file.
- *
- * \param reader Pointer to a PERCI_READER structure obtained by a
- * previous call to PerCiAttachReader().
- * \param data Pointer to the buffer that receives the data.
- * \param len Number of bytes to read.
- *
- * \return The number of bytes read. If this is lower than the
- * requested length, then we reached the end.
- */
- int PerCiRead(PERCI_READER * reader, char *data, int len)
- {
- int cnt = 0;
- perci_fast_reclen_t num;
- int got;
- /* Check parameters. */
- NUTASSERT(reader != NULL);
- /*
- * Loop for for number of requested bytes.
- */
- while (len > cnt) {
- /* Calculate the number of bytes to read in the next step. */
- num = len - cnt;
- if (num > reader->pcr_reclen - reader->pcr_recpos) {
- num = reader->pcr_reclen - reader->pcr_recpos;
- }
- /* Check if we reached the last record. This is the one currently
- used by the writer. */
- if (reader->pcr_recnum == reader->pcr_cil->pcw_recnum) {
- if (num) {
- /* Get the data from the write buffer. */
- memcpy(data, &reader->pcr_cil->pcw_rec.pcd_data[reader->pcr_recpos], num);
- data += num;
- cnt += num;
- reader->pcr_recpos += num;
- } else {
- /* All data read. */
- break;
- }
- }
- /* Not the last record. Continue reading from the file. */
- else {
- /* Check if we reached the end of the current record. */
- if (num) {
- got = _read(reader->pcr_cil->pcw_fd, data, num);
- if (got != num) {
- break;
- }
- data += num;
- cnt += num;
- reader->pcr_recpos += num;
- } else {
- /* We consumed all data in this record. Step to the next. */
- reader->pcr_recnum++;
- reader->pcr_recpos = 0;
- /* Find the next record containing data. */
- reader->pcr_reclen = FindNextData(reader->pcr_cil, &reader->pcr_recnum);
- if (reader->pcr_reclen == 0) {
- /* No more data available. */
- break;
- }
- }
- }
- }
- return cnt;
- }
- /*!
- * \brief Read a text line from a ring buffer file.
- *
- * \param reader Pointer to a PERCI_READER structure obtained by a
- * previous call to PerCiAttachReader().
- * \param data Pointer to the buffer that receives the data.
- * \param len Number of bytes to read.
- *
- * \return The number of bytes read. If this is lower than the
- * requested length, then we reached the end.
- */
- int PerCiReadLine(PERCI_READER * reader, char *line, int len)
- {
- int cnt = 0;
- char *cp = line;
- /* Check function parameters. */
- NUTASSERT(reader != NULL);
- NUTASSERT(line != NULL);
- while (cnt < len) {
- if (PerCiRead(reader, cp, 1) != 1) {
- break;
- }
- cnt++;
- if (*cp++ == '\n') {
- break;
- }
- }
- *cp = 0;
- return cnt;
- }
- /*@}*/
|