/* $FreeBSD: head/tools/tools/bus_autoconf/bus_sections.c 223534 2011-06-25 13:44:05Z hselasky $ */ /*- * Copyright (c) 2011 Hans Petter Selasky. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ #include #include #include #include #include #include #include #include #include "bus_sections.h" #define MAX_STRING 64 struct format_info; typedef TAILQ_HEAD(,format_info) format_info_head_t; typedef TAILQ_ENTRY(format_info) format_info_entry_t; static format_info_head_t format_head = TAILQ_HEAD_INITIALIZER(format_head); struct format_info { format_info_entry_t entry; format_info_head_t fields; char name[MAX_STRING]; uint16_t bit_offset; uint16_t bit_size; }; static struct format_info * format_info_new(char *pstr, uint16_t bo, uint16_t bs) { struct format_info *pfi; pfi = malloc(sizeof(*pfi)); if (pfi == NULL) errx(EX_SOFTWARE, "Out of memory."); memset(pfi, 0, sizeof(*pfi)); TAILQ_INIT(&pfi->fields); strlcpy(pfi->name, pstr, sizeof(pfi->name)); pfi->bit_offset = bo; pfi->bit_size = bs; return (pfi); } static const struct format_info * format_get_section(const char *section) { const struct format_info *psub; static const struct format_info *psub_last; static const char *psub_cache; if (psub_cache && strcmp(psub_cache, section) == 0) return (psub_last); TAILQ_FOREACH(psub, &format_head, entry) { if (strcmp(section, psub->name) == 0) { psub_cache = section; psub_last = psub; return (psub); } } warnx("Section '%s' not found", section); psub_cache = section; psub_last = psub; return (NULL); } uint16_t format_get_section_size(const char *section) { const struct format_info *pfi; pfi = format_get_section(section); if (pfi == NULL) return (0); return ((pfi->bit_offset + 7) / 8); } uint8_t format_get_field(const char *section, const char *field, const uint8_t *ptr, uint16_t size) { const struct format_info *pfi; const struct format_info *psub; uint16_t rem; uint16_t off; uint16_t sz; pfi = format_get_section(section); if (pfi == NULL) return (0); /* skip until we find the fields */ while (pfi && TAILQ_FIRST(&pfi->fields) == NULL) pfi = TAILQ_NEXT(pfi, entry); if (pfi == NULL) return (0); TAILQ_FOREACH(psub, &pfi->fields, entry) { if (strcmp(field, psub->name) == 0) { /* range check */ if (((psub->bit_offset + psub->bit_size) / 8) > size) return (0); /* compute byte offset */ rem = psub->bit_offset & 7; off = psub->bit_offset / 8; sz = psub->bit_size; /* extract bit-field */ return ((ptr[off] >> rem) & ((1 << sz) - 1)); } } warnx("Field '%s' not found in '%s'", field, pfi->name); return (0); } void format_parse_entries(const uint8_t *ptr, uint32_t len) { static const char *command_list = "012345678:"; const char *cmd; struct format_info *pfi; struct format_info *pfi_last = NULL; char linebuf[3][MAX_STRING]; uint32_t off = 0; uint16_t bit_offset = 0; uint8_t state = 0; uint8_t cmd_index; int c; /* * The format we are parsing: * {string,string}{...} */ while (len--) { c = *(ptr++); /* skip some characters */ if (c == 0 || c == '\n' || c == '\r' || c == ' ' || c == '\t') continue; /* accumulate non-field delimiters */ if (strchr("{,}", c) == NULL) { if (off < (MAX_STRING - 1)) { linebuf[state][off] = c; off++; } continue; } /* parse keyword */ linebuf[state][off] = 0; off = 0; state++; if (state == 3) { /* check for command in command list */ cmd = strchr(command_list, linebuf[2][0]); if (cmd != NULL) cmd_index = cmd - command_list; else cmd_index = 255; /* * Check for new field, format is: * * {bit_offset_xor, bit_size} */ if (cmd_index < 9 && pfi_last != NULL) { pfi = format_info_new(linebuf[0], bit_offset ^ atoi(linebuf[1]), cmd_index); TAILQ_INSERT_TAIL(&pfi_last->fields, pfi, entry); bit_offset += cmd_index; } /* * Check for new section, format is: * * {section_bit_size, :} */ if (cmd_index == 9) { pfi_last = format_info_new(linebuf[0], atoi(linebuf[1]), cmd_index); TAILQ_INSERT_TAIL(&format_head, pfi_last, entry); bit_offset = 0; } state = 0; continue; } } }