From: Olivier Matz Date: Mon, 28 Sep 2015 17:03:55 +0000 (+0200) Subject: initial revision X-Git-Url: http://git.droids-corp.org/?p=ucgine.git;a=commitdiff_plain;h=8df722823eb3371a8fd9082b78e51077bfd277df initial revision --- 8df722823eb3371a8fd9082b78e51077bfd277df diff --git a/arch/avr/include/errno.h b/arch/avr/include/errno.h new file mode 100644 index 0000000..58b9e75 --- /dev/null +++ b/arch/avr/include/errno.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) 1982, 1986, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * 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 University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + * + * @(#)errno.h 8.5 (Berkeley) 1/21/94 + */ + +#ifndef UCG_ERRNO_H_ +#define UCG_ERRNO_H_ + +#define EPERM 1 /* Operation not permitted */ +#define ENOENT 2 /* No such file or directory */ +#define ESRCH 3 /* No such process */ +#define EINTR 4 /* Interrupted system call */ +#define EIO 5 /* Input/output error */ +#define ENXIO 6 /* Device not configured */ +#define E2BIG 7 /* Argument list too long */ +#define ENOEXEC 8 /* Exec format error */ +#define EBADF 9 /* Bad file descriptor */ +#define ECHILD 10 /* No child processes */ +#define EDEADLK 11 /* Resource deadlock avoided */ + /* 11 was EAGAIN */ +#define ENOMEM 12 /* Cannot allocate memory */ +#define EACCES 13 /* Permission denied */ +#define EFAULT 14 /* Bad address */ +#define ENOTBLK 15 /* Block device required */ +#define EBUSY 16 /* Device busy */ +#define EEXIST 17 /* File exists */ +#define EXDEV 18 /* Cross-device link */ +#define ENODEV 19 /* Operation not supported by device */ +#define ENOTDIR 20 /* Not a directory */ +#define EISDIR 21 /* Is a directory */ +#define EINVAL 22 /* Invalid argument */ +#define ENFILE 23 /* Too many open files in system */ +#define EMFILE 24 /* Too many open files */ +#define ENOTTY 25 /* Inappropriate ioctl for device */ +#define ETXTBSY 26 /* Text file busy */ +#define EFBIG 27 /* File too large */ +#define ENOSPC 28 /* No space left on device */ +#define ESPIPE 29 /* Illegal seek */ +#define EROFS 30 /* Read-only file system */ +#define EMLINK 31 /* Too many links */ +#define EPIPE 32 /* Broken pipe */ + +/* math software */ +#define EDOM 33 /* Numerical argument out of domain */ +#define ERANGE 34 /* Result too large or too small */ + +/* non-blocking and interrupt i/o */ +#define EAGAIN 35 /* Resource temporarily unavailable */ +#define EWOULDBLOCK EAGAIN /* Operation would block */ +#define EINPROGRESS 36 /* Operation now in progress */ +#define EALREADY 37 /* Operation already in progress */ + +/* ipc/network software -- argument errors */ +#define ENOTSOCK 38 /* Socket operation on non-socket */ +#define EDESTADDRREQ 39 /* Destination address required */ +#define EMSGSIZE 40 /* Message too long */ +#define EPROTOTYPE 41 /* Protocol wrong type for socket */ +#define ENOPROTOOPT 42 /* Protocol option not available */ +#define EPROTONOSUPPORT 43 /* Protocol not supported */ +#define ESOCKTNOSUPPORT 44 /* Socket type not supported */ +#define EOPNOTSUPP 45 /* Operation not supported */ +#define EPFNOSUPPORT 46 /* Protocol family not supported */ +#define EAFNOSUPPORT 47 /* Address family not supported by protocol family */ +#define EADDRINUSE 48 /* Address already in use */ +#define EADDRNOTAVAIL 49 /* Can't assign requested address */ + +/* ipc/network software -- operational errors */ +#define ENETDOWN 50 /* Network is down */ +#define ENETUNREACH 51 /* Network is unreachable */ +#define ENETRESET 52 /* Network dropped connection on reset */ +#define ECONNABORTED 53 /* Software caused connection abort */ +#define ECONNRESET 54 /* Connection reset by peer */ +#define ENOBUFS 55 /* No buffer space available */ +#define EISCONN 56 /* Socket is already connected */ +#define ENOTCONN 57 /* Socket is not connected */ +#define ESHUTDOWN 58 /* Can't send after socket shutdown */ +#define ETOOMANYREFS 59 /* Too many references: can't splice */ +#define ETIMEDOUT 60 /* Operation timed out */ +#define ECONNREFUSED 61 /* Connection refused */ + +#define ELOOP 62 /* Too many levels of symbolic links */ +#define ENAMETOOLONG 63 /* File name too long */ + +/* should be rearranged */ +#define EHOSTDOWN 64 /* Host is down */ +#define EHOSTUNREACH 65 /* No route to host */ +#define ENOTEMPTY 66 /* Directory not empty */ + +/* quotas & mush */ +#define EPROCLIM 67 /* Too many processes */ +#define EUSERS 68 /* Too many users */ +#define EDQUOT 69 /* Disc quota exceeded */ + +/* Network File System */ +#define ESTALE 70 /* Stale NFS file handle */ +#define EREMOTE 71 /* Too many levels of remote in path */ +#define EBADRPC 72 /* RPC struct is bad */ +#define ERPCMISMATCH 73 /* RPC version wrong */ +#define EPROGUNAVAIL 74 /* RPC prog. not avail */ +#define EPROGMISMATCH 75 /* Program version wrong */ +#define EPROCUNAVAIL 76 /* Bad procedure for program */ + +#define ENOLCK 77 /* No locks available */ +#define ENOSYS 78 /* Function not implemented */ + +#define EFTYPE 79 /* Inappropriate file type or format */ +#define EAUTH 80 /* Authentication error */ +#define ENEEDAUTH 81 /* Need authenticator */ + +/* SystemV IPC */ +#define EIDRM 82 /* Identifier removed */ +#define ENOMSG 83 /* No message of desired type */ +#define EOVERFLOW 84 /* Value too large to be stored in data type */ + +/* Wide/multibyte-character handling, ISO/IEC 9899/AMD1:1995 */ +#define EILSEQ 85 /* Illegal byte sequence */ + +/* From IEEE Std 1003.1-2001 */ +/* Base, Realtime, Threads or Thread Priority Scheduling option errors */ +#define ENOTSUP 86 /* Not supported */ + +/* Realtime option errors */ +#define ECANCELED 87 /* Operation canceled */ + +/* Realtime, XSI STREAMS option errors */ +#define EBADMSG 88 /* Bad or Corrupt message */ + +/* XSI STREAMS option errors */ +#define ENODATA 89 /* No message available */ +#define ENOSR 90 /* No STREAM resources */ +#define ENOSTR 91 /* Not a STREAM */ +#define ETIME 92 /* STREAM ioctl timeout */ + +/* File system extended attribute errors */ +#define ENOATTR 93 /* Attribute not found */ + +/* Realtime, XSI STREAMS option errors */ +#define EMULTIHOP 94 /* Multihop attempted */ +#define ENOLINK 95 /* Link has been severed */ +#define EPROTO 96 /* Protocol error */ + +#define ELAST 96 /* Must equal largest errno */ + +#include_next + +#endif /* !UCG_ERRNO_H_ */ diff --git a/arch/avr/include/fcntl.h b/arch/avr/include/fcntl.h new file mode 100644 index 0000000..d949999 --- /dev/null +++ b/arch/avr/include/fcntl.h @@ -0,0 +1 @@ +/* empty, just have it for compat */ diff --git a/arch/avr/include/stdio.h b/arch/avr/include/stdio.h new file mode 100644 index 0000000..fd3208c --- /dev/null +++ b/arch/avr/include/stdio.h @@ -0,0 +1,40 @@ +/* + * Copyright 2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +#ifndef UCG_STDIO_H_ +#define UCG_STDIO_H_ + +#include_next + +static inline void setbuf(FILE *stream, char *buf) +{ + /* ignore setbuf, it is not implemented in avr-libc */ + (void)stream; + (void)buf; +} + +#endif /* !UCG_ERRNO_H_ */ diff --git a/arch/avr/include/sys/queue.h b/arch/avr/include/sys/queue.h new file mode 100644 index 0000000..00dab04 --- /dev/null +++ b/arch/avr/include/sys/queue.h @@ -0,0 +1,844 @@ +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. 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 University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues, and circular queues. + * + * A singly-linked list is headed by a single forward pointer. The + * elements are singly linked for minimum space and pointer manipulation + * overhead at the expense of O(n) removal for arbitrary elements. New + * elements can be added to the list after an existing element or at the + * head of the list. Elements being removed from the head of the list + * should use the explicit macro for this purpose for optimum + * efficiency. A singly-linked list may only be traversed in the forward + * direction. Singly-linked lists are ideal for applications with large + * datasets and few or no removals or for implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so elements can only be removed from the + * head of the list. New elements can be added to the list after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +/* + * Include the definition of NULL only on NetBSD because sys/null.h + * is not available elsewhere. This conditional makes the header + * portable and it can simply be dropped verbatim into any system. + * The caveat is that on other systems some other header + * must provide NULL before the macros can be used. + */ +#ifdef __NetBSD__ +#include +#endif + +#if defined(QUEUEDEBUG) +# if defined(_KERNEL) +# define QUEUEDEBUG_ABORT(...) panic(__VA_ARGS__) +# else +# include +# define QUEUEDEBUG_ABORT(...) err(1, __VA_ARGS__) +# endif +#endif + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List access methods. + */ +#define SLIST_FIRST(head) ((head)->slh_first) +#define SLIST_END(head) NULL +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = (head)->slh_first; \ + (var) != SLIST_END(head); \ + (var) = (var)->field.sle_next) + +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST((head)); \ + (var) != SLIST_END(head) && \ + ((tvar) = SLIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +/* + * Singly-linked List functions. + */ +#define SLIST_INIT(head) do { \ + (head)->slh_first = SLIST_END(head); \ +} while (/*CONSTCOND*/0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define SLIST_REMOVE_AFTER(slistelm, field) do { \ + (slistelm)->field.sle_next = \ + SLIST_NEXT(SLIST_NEXT((slistelm), field), field); \ +} while (/*CONSTCOND*/0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (/*CONSTCOND*/0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = (head)->slh_first; \ + while(curelm->field.sle_next != (elm)) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + } \ +} while (/*CONSTCOND*/0) + + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List access methods. + */ +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_END(head) NULL +#define LIST_EMPTY(head) ((head)->lh_first == LIST_END(head)) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = ((head)->lh_first); \ + (var) != LIST_END(head); \ + (var) = ((var)->field.le_next)) + +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST((head)); \ + (var) != LIST_END(head) && \ + ((tvar) = LIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define LIST_MOVE(head1, head2) do { \ + LIST_INIT((head2)); \ + if (!LIST_EMPTY((head1))) { \ + (head2)->lh_first = (head1)->lh_first; \ + LIST_INIT((head1)); \ + } \ +} while (/*CONSTCOND*/0) + +/* + * List functions. + */ +#if defined(QUEUEDEBUG) +#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) \ + if ((head)->lh_first && \ + (head)->lh_first->field.le_prev != &(head)->lh_first) \ + QUEUEDEBUG_ABORT("LIST_INSERT_HEAD %p %s:%d", (head), \ + __FILE__, __LINE__); +#define QUEUEDEBUG_LIST_OP(elm, field) \ + if ((elm)->field.le_next && \ + (elm)->field.le_next->field.le_prev != \ + &(elm)->field.le_next) \ + QUEUEDEBUG_ABORT("LIST_* forw %p %s:%d", (elm), \ + __FILE__, __LINE__); \ + if (*(elm)->field.le_prev != (elm)) \ + QUEUEDEBUG_ABORT("LIST_* back %p %s:%d", (elm), \ + __FILE__, __LINE__); +#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) \ + (elm)->field.le_next = (void *)1L; \ + (elm)->field.le_prev = (void *)1L; +#else +#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) +#define QUEUEDEBUG_LIST_OP(elm, field) +#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) +#endif + +#define LIST_INIT(head) do { \ + (head)->lh_first = LIST_END(head); \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + QUEUEDEBUG_LIST_OP((listelm), field) \ + if (((elm)->field.le_next = (listelm)->field.le_next) != \ + LIST_END(head)) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + QUEUEDEBUG_LIST_OP((listelm), field) \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + QUEUEDEBUG_LIST_INSERT_HEAD((head), (elm), field) \ + if (((elm)->field.le_next = (head)->lh_first) != LIST_END(head))\ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (/*CONSTCOND*/0) + +#define LIST_REMOVE(elm, field) do { \ + QUEUEDEBUG_LIST_OP((elm), field) \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ + QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \ +} while (/*CONSTCOND*/0) + +#define LIST_REPLACE(elm, elm2, field) do { \ + if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ + (elm2)->field.le_next->field.le_prev = \ + &(elm2)->field.le_next; \ + (elm2)->field.le_prev = (elm)->field.le_prev; \ + *(elm2)->field.le_prev = (elm2); \ + QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \ +} while (/*CONSTCOND*/0) + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define SIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Simple queue access methods. + */ +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_END(head) NULL +#define SIMPLEQ_EMPTY(head) ((head)->sqh_first == SIMPLEQ_END(head)) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + +#define SIMPLEQ_FOREACH(var, head, field) \ + for ((var) = ((head)->sqh_first); \ + (var) != SIMPLEQ_END(head); \ + (var) = ((var)->field.sqe_next)) + +#define SIMPLEQ_FOREACH_SAFE(var, head, field, next) \ + for ((var) = ((head)->sqh_first); \ + (var) != SIMPLEQ_END(head) && \ + ((next = ((var)->field.sqe_next)), 1); \ + (var) = (next)) + +/* + * Simple queue functions. + */ +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ + if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \ + == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE(head, elm, type, field) do { \ + if ((head)->sqh_first == (elm)) { \ + SIMPLEQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->sqh_first; \ + while (curelm->field.sqe_next != (elm)) \ + curelm = curelm->field.sqe_next; \ + if ((curelm->field.sqe_next = \ + curelm->field.sqe_next->field.sqe_next) == NULL) \ + (head)->sqh_last = &(curelm)->field.sqe_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_CONCAT(head1, head2) do { \ + if (!SIMPLEQ_EMPTY((head2))) { \ + *(head1)->sqh_last = (head2)->sqh_first; \ + (head1)->sqh_last = (head2)->sqh_last; \ + SIMPLEQ_INIT((head2)); \ + } \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_LAST(head, type, field) \ + (SIMPLEQ_EMPTY((head)) ? \ + NULL : \ + ((struct type *)(void *) \ + ((char *)((head)->sqh_last) - offsetof(struct type, field)))) + +/* + * Tail queue definitions. + */ +#define _TAILQ_HEAD(name, type, qual) \ +struct name { \ + qual type *tqh_first; /* first element */ \ + qual type *qual *tqh_last; /* addr of last next element */ \ +} +#define TAILQ_HEAD(name, type) _TAILQ_HEAD(name, struct type,) + +#define TAILQ_HEAD_INITIALIZER(head) \ + { TAILQ_END(head), &(head).tqh_first } + +#define _TAILQ_ENTRY(type, qual) \ +struct { \ + qual type *tqe_next; /* next element */ \ + qual type *qual *tqe_prev; /* address of previous next element */\ +} +#define TAILQ_ENTRY(type) _TAILQ_ENTRY(struct type,) + +/* + * Tail queue access methods. + */ +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_END(head) (NULL) +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) +#define TAILQ_EMPTY(head) (TAILQ_FIRST(head) == TAILQ_END(head)) + + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = ((head)->tqh_first); \ + (var) != TAILQ_END(head); \ + (var) = ((var)->field.tqe_next)) + +#define TAILQ_FOREACH_SAFE(var, head, field, next) \ + for ((var) = ((head)->tqh_first); \ + (var) != TAILQ_END(head) && \ + ((next) = TAILQ_NEXT(var, field), 1); (var) = (next)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last));\ + (var) != TAILQ_END(head); \ + (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last))) + +#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, prev) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var) != TAILQ_END(head) && \ + ((prev) = TAILQ_PREV((var), headname, field), 1); (var) = (prev)) + +/* + * Tail queue functions. + */ +#if defined(QUEUEDEBUG) +#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) \ + if ((head)->tqh_first && \ + (head)->tqh_first->field.tqe_prev != &(head)->tqh_first) \ + QUEUEDEBUG_ABORT("TAILQ_INSERT_HEAD %p %s:%d", (head), \ + __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) \ + if (*(head)->tqh_last != NULL) \ + QUEUEDEBUG_ABORT("TAILQ_INSERT_TAIL %p %s:%d", (head), \ + __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_OP(elm, field) \ + if ((elm)->field.tqe_next && \ + (elm)->field.tqe_next->field.tqe_prev != \ + &(elm)->field.tqe_next) \ + QUEUEDEBUG_ABORT("TAILQ_* forw %p %s:%d", (elm), \ + __FILE__, __LINE__); \ + if (*(elm)->field.tqe_prev != (elm)) \ + QUEUEDEBUG_ABORT("TAILQ_* back %p %s:%d", (elm), \ + __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field) \ + if ((elm)->field.tqe_next == NULL && \ + (head)->tqh_last != &(elm)->field.tqe_next) \ + QUEUEDEBUG_ABORT("TAILQ_PREREMOVE head %p elm %p %s:%d",\ + (head), (elm), __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) \ + (elm)->field.tqe_next = (void *)1L; \ + (elm)->field.tqe_prev = (void *)1L; +#else +#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) +#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) +#define QUEUEDEBUG_TAILQ_OP(elm, field) +#define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field) +#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) +#endif + +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = TAILQ_END(head); \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + QUEUEDEBUG_TAILQ_INSERT_HEAD((head), (elm), field) \ + if (((elm)->field.tqe_next = (head)->tqh_first) != TAILQ_END(head))\ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + QUEUEDEBUG_TAILQ_INSERT_TAIL((head), (elm), field) \ + (elm)->field.tqe_next = TAILQ_END(head); \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + QUEUEDEBUG_TAILQ_OP((listelm), field) \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != \ + TAILQ_END(head)) \ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + QUEUEDEBUG_TAILQ_OP((listelm), field) \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + QUEUEDEBUG_TAILQ_PREREMOVE((head), (elm), field) \ + QUEUEDEBUG_TAILQ_OP((elm), field) \ + if (((elm)->field.tqe_next) != TAILQ_END(head)) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ + QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \ +} while (/*CONSTCOND*/0) + +#define TAILQ_REPLACE(head, elm, elm2, field) do { \ + if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != \ + TAILQ_END(head)) \ + (elm2)->field.tqe_next->field.tqe_prev = \ + &(elm2)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm2)->field.tqe_next; \ + (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ + *(elm2)->field.tqe_prev = (elm2); \ + QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \ +} while (/*CONSTCOND*/0) + +#define TAILQ_CONCAT(head1, head2, field) do { \ + if (!TAILQ_EMPTY(head2)) { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + } \ +} while (/*CONSTCOND*/0) + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first; /* first element */ \ + struct type **stqh_last; /* addr of last next element */ \ +} + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +/* + * Singly-linked Tail queue access methods. + */ +#define STAILQ_FIRST(head) ((head)->stqh_first) +#define STAILQ_END(head) NULL +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) +#define STAILQ_EMPTY(head) (STAILQ_FIRST(head) == STAILQ_END(head)) + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_INIT(head) do { \ + (head)->stqh_first = NULL; \ + (head)->stqh_last = &(head)->stqh_first; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \ + (head)->stqh_last = &(elm)->field.stqe_next; \ + (head)->stqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.stqe_next = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &(elm)->field.stqe_next; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\ + (head)->stqh_last = &(elm)->field.stqe_next; \ + (listelm)->field.stqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \ + (head)->stqh_last = &(head)->stqh_first; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + if ((head)->stqh_first == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->stqh_first; \ + while (curelm->field.stqe_next != (elm)) \ + curelm = curelm->field.stqe_next; \ + if ((curelm->field.stqe_next = \ + curelm->field.stqe_next->field.stqe_next) == NULL) \ + (head)->stqh_last = &(curelm)->field.stqe_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define STAILQ_FOREACH(var, head, field) \ + for ((var) = ((head)->stqh_first); \ + (var); \ + (var) = ((var)->field.stqe_next)) + +#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = STAILQ_FIRST((head)); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define STAILQ_CONCAT(head1, head2) do { \ + if (!STAILQ_EMPTY((head2))) { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ +} while (/*CONSTCOND*/0) + +#define STAILQ_LAST(head, type, field) \ + (STAILQ_EMPTY((head)) ? \ + NULL : \ + ((struct type *)(void *) \ + ((char *)((head)->stqh_last) - offsetof(struct type, field)))) + + +#ifndef _KERNEL +/* + * Circular queue definitions. Do not use. We still keep the macros + * for compatibility but because of pointer aliasing issues their use + * is discouraged! + */ + +/* + * __launder_type(): We use this ugly hack to work around the the compiler + * noticing that two types may not alias each other and elide tests in code. + * We hit this in the CIRCLEQ macros when comparing 'struct name *' and + * 'struct type *' (see CIRCLEQ_HEAD()). Modern compilers (such as GCC + * 4.8) declare these comparisons as always false, causing the code to + * not run as designed. + * + * This hack is only to be used for comparisons and thus can be fully const. + * Do not use for assignment. + * + * If we ever choose to change the ABI of the CIRCLEQ macros, we could fix + * this by changing the head/tail sentinal values, but see the note above + * this one. + */ +static __inline const void * __launder_type(const void *); +static __inline const void * +__launder_type(const void *__x) +{ + __asm __volatile("" : "+r" (__x)); + return __x; +} + +#if defined(QUEUEDEBUG) +#define QUEUEDEBUG_CIRCLEQ_HEAD(head, field) \ + if ((head)->cqh_first != CIRCLEQ_ENDC(head) && \ + (head)->cqh_first->field.cqe_prev != CIRCLEQ_ENDC(head)) \ + QUEUEDEBUG_ABORT("CIRCLEQ head forw %p %s:%d", (head), \ + __FILE__, __LINE__); \ + if ((head)->cqh_last != CIRCLEQ_ENDC(head) && \ + (head)->cqh_last->field.cqe_next != CIRCLEQ_ENDC(head)) \ + QUEUEDEBUG_ABORT("CIRCLEQ head back %p %s:%d", (head), \ + __FILE__, __LINE__); +#define QUEUEDEBUG_CIRCLEQ_ELM(head, elm, field) \ + if ((elm)->field.cqe_next == CIRCLEQ_ENDC(head)) { \ + if ((head)->cqh_last != (elm)) \ + QUEUEDEBUG_ABORT("CIRCLEQ elm last %p %s:%d", \ + (elm), __FILE__, __LINE__); \ + } else { \ + if ((elm)->field.cqe_next->field.cqe_prev != (elm)) \ + QUEUEDEBUG_ABORT("CIRCLEQ elm forw %p %s:%d", \ + (elm), __FILE__, __LINE__); \ + } \ + if ((elm)->field.cqe_prev == CIRCLEQ_ENDC(head)) { \ + if ((head)->cqh_first != (elm)) \ + QUEUEDEBUG_ABORT("CIRCLEQ elm first %p %s:%d", \ + (elm), __FILE__, __LINE__); \ + } else { \ + if ((elm)->field.cqe_prev->field.cqe_next != (elm)) \ + QUEUEDEBUG_ABORT("CIRCLEQ elm prev %p %s:%d", \ + (elm), __FILE__, __LINE__); \ + } +#define QUEUEDEBUG_CIRCLEQ_POSTREMOVE(elm, field) \ + (elm)->field.cqe_next = (void *)1L; \ + (elm)->field.cqe_prev = (void *)1L; +#else +#define QUEUEDEBUG_CIRCLEQ_HEAD(head, field) +#define QUEUEDEBUG_CIRCLEQ_ELM(head, elm, field) +#define QUEUEDEBUG_CIRCLEQ_POSTREMOVE(elm, field) +#endif + +#define CIRCLEQ_HEAD(name, type) \ +struct name { \ + struct type *cqh_first; /* first element */ \ + struct type *cqh_last; /* last element */ \ +} + +#define CIRCLEQ_HEAD_INITIALIZER(head) \ + { CIRCLEQ_END(&head), CIRCLEQ_END(&head) } + +#define CIRCLEQ_ENTRY(type) \ +struct { \ + struct type *cqe_next; /* next element */ \ + struct type *cqe_prev; /* previous element */ \ +} + +/* + * Circular queue functions. + */ +#define CIRCLEQ_INIT(head) do { \ + (head)->cqh_first = CIRCLEQ_END(head); \ + (head)->cqh_last = CIRCLEQ_END(head); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ + QUEUEDEBUG_CIRCLEQ_ELM((head), (listelm), field) \ + (elm)->field.cqe_next = (listelm)->field.cqe_next; \ + (elm)->field.cqe_prev = (listelm); \ + if ((listelm)->field.cqe_next == CIRCLEQ_ENDC(head)) \ + (head)->cqh_last = (elm); \ + else \ + (listelm)->field.cqe_next->field.cqe_prev = (elm); \ + (listelm)->field.cqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ + QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ + QUEUEDEBUG_CIRCLEQ_ELM((head), (listelm), field) \ + (elm)->field.cqe_next = (listelm); \ + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ + if ((listelm)->field.cqe_prev == CIRCLEQ_ENDC(head)) \ + (head)->cqh_first = (elm); \ + else \ + (listelm)->field.cqe_prev->field.cqe_next = (elm); \ + (listelm)->field.cqe_prev = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ + QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ + (elm)->field.cqe_next = (head)->cqh_first; \ + (elm)->field.cqe_prev = CIRCLEQ_END(head); \ + if ((head)->cqh_last == CIRCLEQ_ENDC(head)) \ + (head)->cqh_last = (elm); \ + else \ + (head)->cqh_first->field.cqe_prev = (elm); \ + (head)->cqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ + QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ + (elm)->field.cqe_next = CIRCLEQ_END(head); \ + (elm)->field.cqe_prev = (head)->cqh_last; \ + if ((head)->cqh_first == CIRCLEQ_ENDC(head)) \ + (head)->cqh_first = (elm); \ + else \ + (head)->cqh_last->field.cqe_next = (elm); \ + (head)->cqh_last = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_REMOVE(head, elm, field) do { \ + QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ + QUEUEDEBUG_CIRCLEQ_ELM((head), (elm), field) \ + if ((elm)->field.cqe_next == CIRCLEQ_ENDC(head)) \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + else \ + (elm)->field.cqe_next->field.cqe_prev = \ + (elm)->field.cqe_prev; \ + if ((elm)->field.cqe_prev == CIRCLEQ_ENDC(head)) \ + (head)->cqh_first = (elm)->field.cqe_next; \ + else \ + (elm)->field.cqe_prev->field.cqe_next = \ + (elm)->field.cqe_next; \ + QUEUEDEBUG_CIRCLEQ_POSTREMOVE((elm), field) \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_FOREACH(var, head, field) \ + for ((var) = ((head)->cqh_first); \ + (var) != CIRCLEQ_ENDC(head); \ + (var) = ((var)->field.cqe_next)) + +#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ + for ((var) = ((head)->cqh_last); \ + (var) != CIRCLEQ_ENDC(head); \ + (var) = ((var)->field.cqe_prev)) + +/* + * Circular queue access methods. + */ +#define CIRCLEQ_FIRST(head) ((head)->cqh_first) +#define CIRCLEQ_LAST(head) ((head)->cqh_last) +/* For comparisons */ +#define CIRCLEQ_ENDC(head) (__launder_type(head)) +/* For assignments */ +#define CIRCLEQ_END(head) ((void *)(head)) +#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) +#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) +#define CIRCLEQ_EMPTY(head) \ + (CIRCLEQ_FIRST(head) == CIRCLEQ_ENDC(head)) + +#define CIRCLEQ_LOOP_NEXT(head, elm, field) \ + (((elm)->field.cqe_next == CIRCLEQ_ENDC(head)) \ + ? ((head)->cqh_first) \ + : (elm->field.cqe_next)) +#define CIRCLEQ_LOOP_PREV(head, elm, field) \ + (((elm)->field.cqe_prev == CIRCLEQ_ENDC(head)) \ + ? ((head)->cqh_last) \ + : (elm->field.cqe_prev)) +#endif /* !_KERNEL */ + +#endif /* !_SYS_QUEUE_H_ */ diff --git a/arch/avr/include/sys/types.h b/arch/avr/include/sys/types.h new file mode 100644 index 0000000..51403a3 --- /dev/null +++ b/arch/avr/include/sys/types.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +#ifndef UCG_SYS_TYPES_H_ +#define UCG_SYS_TYPES_H_ + +typedef intptr_t ssize_t; + +#endif /* UCG_SYS_TYPES_H_ */ diff --git a/arch/avr/include/ucg_delay.h b/arch/avr/include/ucg_delay.h new file mode 100644 index 0000000..59d18c9 --- /dev/null +++ b/arch/avr/include/ucg_delay.h @@ -0,0 +1,43 @@ +/* + * Copyright 2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +#ifndef UCG_DELAY_H_ +#define UCG_DELAY_H_ + +#include + +#include + +/** + * Loop during *ms* milliseconds + */ +static inline void ucg_delay_ms(uint16_t ms) +{ + _delay_ms(ms); +} + +#endif /* UCG_DELAY_H_ */ diff --git a/arch/avr/include/ucg_irq.h b/arch/avr/include/ucg_irq.h new file mode 100644 index 0000000..4505881 --- /dev/null +++ b/arch/avr/include/ucg_irq.h @@ -0,0 +1,64 @@ +/* + * Copyright 2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +#ifndef UCG_IRQ_H_ +#define UCG_IRQ_H_ + +#include + +typedef uint8_t ucg_irqflags_t; + +static inline void ucg_irq_lock(void) +{ + cli(); +} + +static inline void ucg_irq_unlock(void) +{ + sei(); +} + +static inline ucg_irqflags_t ucg_irq_lock_save(void) +{ + ucg_irqflags_t flags; + + flags = SREG; + cli(); + return flags; +} + +static inline void ucg_irq_unlock_restore(ucg_irqflags_t flags) +{ + SREG = flags; +} + +static inline int ucg_irq_locked(void) +{ + return !(bit_is_set(SREG,7)); +} + +#endif /* UCG_IRQ_H_ */ diff --git a/arch/avr/include/ucg_reent_intr.h b/arch/avr/include/ucg_reent_intr.h new file mode 100644 index 0000000..b253f74 --- /dev/null +++ b/arch/avr/include/ucg_reent_intr.h @@ -0,0 +1,34 @@ +/* + * Copyright 2016, Fabrice DESCLAUX + * Copyright 2016, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +#ifndef UCG_REENT_INTR_H_ +#define UCG_REENT_INTR_H_ + +#define UCG_REENT_INTR(f) f() + +#endif diff --git a/arch/avr/include/unistd.h b/arch/avr/include/unistd.h new file mode 100644 index 0000000..d949999 --- /dev/null +++ b/arch/avr/include/unistd.h @@ -0,0 +1 @@ +/* empty, just have it for compat */ diff --git a/arch/avr/mk/ucgine-arch.mk b/arch/avr/mk/ucgine-arch.mk new file mode 100644 index 0000000..50e81c1 --- /dev/null +++ b/arch/avr/mk/ucgine-arch.mk @@ -0,0 +1,35 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. +# + +ARCH_CFLAGS += -Wall -W +ARCH_CFLAGS += -I$(UCGINE)/arch/$(UCGINE_ARCH)/include +ARCH_CFLAGS += -Os +ARCH_CFLAGS += -ffunction-sections + +ARCH_LDFLAGS += -Wl,--gc-sections + +ARCH_CROSS := avr- diff --git a/arch/avr/uart/include/ucg_avr_uart.h b/arch/avr/uart/include/ucg_avr_uart.h new file mode 100644 index 0000000..400b6cf --- /dev/null +++ b/arch/avr/uart/include/ucg_avr_uart.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2015, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +#ifndef UGC_AVR_UART_H_ +#define UGC_AVR_UART_H_ + +#include + +typedef volatile uint8_t *uart_reg_t; + +struct ucg_avr_uart { + /* Example: */ + uart_reg_t reg_udr; /* &UDR0 */ + uart_reg_t reg_ucsra; /* &UCSR0A */ + uart_reg_t reg_ucsrb; /* &UCSR0B */ + uart_reg_t reg_ucsrc; /* &UCSR0C */ + uart_reg_t reg_ubrrl; /* &UBRR0L */ + uart_reg_t reg_ubrrh; /* &UBRR0H */ + + uint32_t bit_udre:3; /* UDRE0 */ + uint32_t bit_rxc:3; /* RXC0 */ + uint32_t bit_udrie:3; /* UDRIE0 */ + uint32_t bit_txen:3; /* RXEN0 */ + uint32_t bit_rxen:3; /* TXEN0 */ + uint32_t bit_rxcie:3; /* RXCIE0 */ + uint32_t bit_u2x:3; /* U2X0 */ +}; + +const struct ucg_uart_driver_ops avr_uart_ops; + +#endif /* UGC_AVR_UART_H_ */ diff --git a/arch/avr/uart/ucg_avr_uart.c b/arch/avr/uart/ucg_avr_uart.c new file mode 100644 index 0000000..8c239f4 --- /dev/null +++ b/arch/avr/uart/ucg_avr_uart.c @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2015, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 + +static void disable_tx_irq(struct ucg_uart *uart) +{ + struct ucg_avr_uart *avr_uart = uart->driver_data; + *avr_uart->reg_ucsrb &= ~(1 << avr_uart->bit_udrie); +} + +static void enable_tx_irq(struct ucg_uart *uart) +{ + struct ucg_avr_uart *avr_uart = uart->driver_data; + *avr_uart->reg_ucsrb |= (1 << avr_uart->bit_udrie); +} + +static uint8_t tx_ready(struct ucg_uart *uart) +{ + struct ucg_avr_uart *avr_uart = uart->driver_data; + return !!(*avr_uart->reg_ucsra & (1 << avr_uart->bit_udre)); +} + +static uint8_t rx_ready(struct ucg_uart *uart) +{ + struct ucg_avr_uart *avr_uart = uart->driver_data; + return !!(*avr_uart->reg_ucsra & (1 << avr_uart->bit_rxc)); +} + +static void set_udr(struct ucg_uart *uart, char c) +{ + struct ucg_avr_uart *avr_uart = uart->driver_data; + *avr_uart->reg_udr = c; +} + +static char get_udr(struct ucg_uart *uart) +{ + struct ucg_avr_uart *avr_uart = uart->driver_data; + return *avr_uart->reg_udr; +} + +static int set_conf(struct ucg_uart *uart, const struct ucg_uart_config *conf) +{ + struct ucg_avr_uart *avr_uart = uart->driver_data; + uint16_t baudreg; + uint8_t lo, hi; + uint8_t use_u2x = 1; /* always use double speed */ + + if (conf->enable == 0) { + *avr_uart->reg_ucsrb = 0; + return 0; + } + + if (use_u2x) + baudreg = (F_CPU / (conf->baudrate * 8UL)) - 1; + else + baudreg = (F_CPU / (conf->baudrate * 16UL)) - 1; + + lo = (uint8_t)baudreg; + hi = (uint8_t)((baudreg >> 8) & 0xF); + + *avr_uart->reg_ubrrl = lo; + *avr_uart->reg_ubrrh = hi; + + *avr_uart->reg_ucsra = ( + (use_u2x << avr_uart->bit_u2x)); + *avr_uart->reg_ucsrb = ( + (1 << avr_uart->bit_txen) | + (1 << avr_uart->bit_rxen) | + (1 << avr_uart->bit_rxcie)); + + return 0; +} + +const struct ucg_uart_driver_ops avr_uart_ops = { + .disable_tx_irq = disable_tx_irq, + .enable_tx_irq = enable_tx_irq, + .tx_ready = tx_ready, + .rx_ready = rx_ready, + .set_udr = set_udr, + .get_udr = get_udr, + .set_conf = set_conf, +}; diff --git a/arch/posix/include/ucg_delay.h b/arch/posix/include/ucg_delay.h new file mode 100644 index 0000000..a8e7fa3 --- /dev/null +++ b/arch/posix/include/ucg_delay.h @@ -0,0 +1,43 @@ +/* + * Copyright 2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +#ifndef UCG_DELAY_H_ +#define UCG_DELAY_H_ + +#include +#include + +/** + * Loop during *ms* milliseconds + */ +static inline void ucg_delay_ms(uint16_t ms) +{ + (void)ms; + //usleep(ms * 1000); +} + +#endif /* UCG_DELAY_H_ */ diff --git a/arch/posix/include/ucg_irq.h b/arch/posix/include/ucg_irq.h new file mode 100644 index 0000000..435f7e5 --- /dev/null +++ b/arch/posix/include/ucg_irq.h @@ -0,0 +1,58 @@ +/* + * Copyright 2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +#ifndef UCG_IRQ_H_ +#define UCG_IRQ_H_ + +#include + +typedef uint8_t ucg_irqflags_t; + +static inline void ucg_irq_lock(void) +{ +} + +static inline void ucg_irq_unlock(void) +{ +} + +static inline ucg_irqflags_t ucg_irq_lock_save(void) +{ + return 0; +} + +static inline void ucg_irq_unlock_restore(ucg_irqflags_t flags) +{ + (void)flags; +} + +static inline int ucg_irq_locked(void) +{ + return 0; +} + +#endif /* UCG_IRQ_H_ */ diff --git a/arch/posix/mk/ucgine-arch.mk b/arch/posix/mk/ucgine-arch.mk new file mode 100644 index 0000000..61d64de --- /dev/null +++ b/arch/posix/mk/ucgine-arch.mk @@ -0,0 +1,30 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. +# + +ARCH_CFLAGS += -Wall -W +ARCH_CFLAGS += -I$(UCGINE)/arch/$(UCGINE_ARCH)/include +ARCH_CFLAGS += -O3 diff --git a/arch/stm32/include/ucg_delay.h b/arch/stm32/include/ucg_delay.h new file mode 100644 index 0000000..3022936 --- /dev/null +++ b/arch/stm32/include/ucg_delay.h @@ -0,0 +1,46 @@ +/* + * Copyright 2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +#ifndef UCG_DELAY_H_ +#define UCG_DELAY_H_ + +#include + +/** + * Loop during *ms* milliseconds + */ +static inline void ucg_delay_ms(uint16_t ms) +{ + /* XXX use F_CPU */ + while (ms-- > 0) { + volatile int x = 5971; + while (x-- > 0) + __asm("nop"); + } +} + +#endif /* UCG_DELAY_H_ */ diff --git a/arch/stm32/include/ucg_irq.h b/arch/stm32/include/ucg_irq.h new file mode 100644 index 0000000..4245e83 --- /dev/null +++ b/arch/stm32/include/ucg_irq.h @@ -0,0 +1,87 @@ +/* + * Copyright 2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +#ifndef UCG_IRQ_H_ +#define UCG_IRQ_H_ + +#include + +typedef uint32_t ucg_irqflags_t; + +static inline uint32_t __ucg_get_primask(void) +{ + ucg_irqflags_t primask_reg; + + asm volatile ( + "mrs %0, primask\n" + : "=r" (primask_reg) + : + : ); + + return primask_reg; +} + +static inline void __ucg_set_primask(ucg_irqflags_t primask_reg) +{ + asm volatile ( + "msr primask, %0\n" + : + : "r" (primask_reg) + : ); +} + +static inline void ucg_irq_lock(void) +{ + __asm volatile ("cpsid i"); +} + +static inline void ucg_irq_unlock(void) +{ + __asm volatile ("cpsie i"); +} + +static inline ucg_irqflags_t ucg_irq_lock_save(void) +{ + ucg_irqflags_t flags; + + flags = __ucg_get_primask(); + ucg_irq_lock(); + return flags; +} + +static inline void ucg_irq_unlock_restore(ucg_irqflags_t flags) +{ + __ucg_set_primask(flags); +} + +static inline int ucg_irq_locked(void) +{ + ucg_irqflags_t flags = __ucg_get_primask(); + return !!flags; +} + +#endif /* UCG_IRQ_H_ */ diff --git a/arch/stm32/include/ucg_reent_intr.h b/arch/stm32/include/ucg_reent_intr.h new file mode 100644 index 0000000..99fd578 --- /dev/null +++ b/arch/stm32/include/ucg_reent_intr.h @@ -0,0 +1,81 @@ +/* + * Copyright 2016, Fabrice DESCLAUX + * Copyright 2016, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +#ifndef UCG_REENT_INTR_H_ +#define UCG_REENT_INTR_H_ + +/* XXX doc */ +void ucg_reent_intr(uint32_t *context, void *fct); +//void ucg_reent_intr(uint32_t *context); + +/* +.align 4 +.long my_var +*/ +#define UCG_REENT_INTR(f) \ + __asm__ volatile ( \ + "SUB SP, SP, 0x38 \n" \ + "STR LR, [SP] \n" \ + "ADD R0, SP, 0x10 \n" \ + "LDR R1, =" #f " \n" \ + "BL ucg_reent_intr \n" \ + "POP {LR} \n" \ + "ADD SP, SP, 0xC \n" \ + "BX LR \n" \ + : \ + : \ + : /* No clobbers */ \ + ) + +#define old_UCG_REENT_INTR(f) \ + __asm__ volatile ( \ + "SUB SP, SP, 0x38 \n" \ + "STR LR, [SP] \n" \ + "ADD R0, SP, 0x10 \n" \ + "MOV R1, %[value] \n" \ + "BL ucg_reent_intr \n" \ + "POP {LR} \n" \ + "ADD SP, SP, 0xC \n" \ + "BX LR \n" \ + : \ + : [value]"r" ((uint32_t)f) \ + : /* No clobbers */ \ + ) + +#define XXX_UCG_REENT_INTR(f) \ + __asm__ volatile ( \ + "SUB SP, SP, 0x38 \n" \ + "STR LR, [SP] \n" \ + "ADD R0, SP, 0x10 \n" \ + "BL ucg_reent_intr \n" \ + "POP {LR} \n" \ + "ADD SP, SP, 0xC \n" \ + "BX LR \n" \ + ) + +#endif diff --git a/arch/stm32/mk/ucgine-arch.mk b/arch/stm32/mk/ucgine-arch.mk new file mode 100644 index 0000000..3967986 --- /dev/null +++ b/arch/stm32/mk/ucgine-arch.mk @@ -0,0 +1,39 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. +# + +ARCH_CFLAGS += -I$(UCGINE)/arch/$(UCGINE_ARCH)/include +ARCH_CFLAGS += -DUSE_STDPERIPH_DRIVER +ARCH_CFLAGS += -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork +ARCH_CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16 +ARCH_CFLAGS += -ffunction-sections + +ARCH_LDFLAGS += -Wl,--gc-sections +ARCH_LDFLAGS += --specs=rdimon.specs -lc +ARCH_LDFLAGS += -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork +ARCH_LDFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16 + +ARCH_CROSS := arm-none-eabi- diff --git a/arch/stm32/uart/include/ucg_stm32_uart.h b/arch/stm32/uart/include/ucg_stm32_uart.h new file mode 100644 index 0000000..a98cc5d --- /dev/null +++ b/arch/stm32/uart/include/ucg_stm32_uart.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2015, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +#ifndef UGC_STM32_UART_H_ +#define UGC_STM32_UART_H_ + +#include + +#include + +struct ucg_stm32_uart { + /* Example: */ + USART_TypeDef *uart; /* USART2 */ + uint32_t rcc_uart; /* RCC_APB1Periph_USART2 */ + uint32_t rcc_gpio; /* RCC_AHB1Periph_GPIOA */ + GPIO_TypeDef *gpio; /* GPIOA */ + uint8_t gpio_af; /* GPIO_AF_USART2 */ + uint16_t gpio_pins; /* GPIO_Pin_2 | GPIO_Pin_3 */ + uint8_t gpio_speed; /* GPIO_Speed_25MHz */ + uint8_t irq; /* USART2_IRQn */ + uint8_t irq_preempt_prio; /* 0 */ + uint8_t irq_sub_prio; /* 0 */ +}; + +const struct ucg_uart_driver_ops stm32_uart_ops; + +#endif /* UGC_STM32_UART_H_ */ diff --git a/arch/stm32/uart/ucg_stm32_uart.c b/arch/stm32/uart/ucg_stm32_uart.c new file mode 100644 index 0000000..19de514 --- /dev/null +++ b/arch/stm32/uart/ucg_stm32_uart.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2015, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 + +static void disable_tx_irq(struct ucg_uart *uart) +{ + struct ucg_stm32_uart *stm32_uart = uart->driver_data; + + stm32_uart->uart->SR &= ~(USART_FLAG_TXE); + stm32_uart->uart->CR1 &= ~(USART_CR1_TXEIE); +} + +static void enable_tx_irq(struct ucg_uart *uart) +{ + struct ucg_stm32_uart *stm32_uart = uart->driver_data; + + stm32_uart->uart->CR1 |= USART_CR1_TXEIE; +} + +static uint8_t tx_ready(struct ucg_uart *uart) +{ + struct ucg_stm32_uart *stm32_uart = uart->driver_data; + + return !!(stm32_uart->uart->SR & USART_FLAG_TXE); +} + +static uint8_t rx_ready(struct ucg_uart *uart) +{ + struct ucg_stm32_uart *stm32_uart = uart->driver_data; + + return !!(stm32_uart->uart->SR & USART_FLAG_RXNE); +} + +static void set_udr(struct ucg_uart *uart, char c) +{ + struct ucg_stm32_uart *stm32_uart = uart->driver_data; + + stm32_uart->uart->DR = c; +} + +static char get_udr(struct ucg_uart *uart) +{ + struct ucg_stm32_uart *stm32_uart = uart->driver_data; + + return stm32_uart->uart->DR; +} + +static int set_conf(struct ucg_uart *uart, const struct ucg_uart_config *conf) +{ + USART_InitTypeDef u; + GPIO_InitTypeDef gpio; + NVIC_InitTypeDef nvic; + struct ucg_stm32_uart *stm32_uart = uart->driver_data; + int i; + + /* even if it is asked to disable, keep the RCC enabled */ + + /* Enable the peripheral clock. */ + RCC_APB1PeriphClockCmd(stm32_uart->rcc_uart, ENABLE); + __asm("dsb"); + + /* Enable the peripheral clock for GPIO. */ + RCC_AHB1PeriphClockCmd(stm32_uart->rcc_gpio, ENABLE); + __asm("dsb"); + + if (conf->enable == 0) { + USART_ITConfig(stm32_uart->uart, USART_IT_TXE, DISABLE); + USART_ITConfig(stm32_uart->uart, USART_IT_RXNE, DISABLE); + USART_Cmd(stm32_uart->uart, ENABLE); + + nvic.NVIC_IRQChannel = stm32_uart->irq; + nvic.NVIC_IRQChannelCmd = DISABLE; + NVIC_Init(&nvic); + return 0; + } + + if (conf->nbits != 8) + return -EINVAL; + + /* connect to alternate function */ + for (i = 0; i < 16; i++) { + if ((1 << i) & stm32_uart->gpio_pins) { + GPIO_PinAFConfig(stm32_uart->gpio, i, + stm32_uart->gpio_af); + } + } + + GPIO_StructInit(&gpio); + gpio.GPIO_Speed = stm32_uart->gpio_speed; + + /* configure rx and tx */ + gpio.GPIO_Pin = stm32_uart->gpio_pins; + gpio.GPIO_Mode = GPIO_Mode_AF; + GPIO_Init(stm32_uart->gpio, &gpio); + + /* USART configuration */ + u.USART_BaudRate = conf->baudrate; + u.USART_WordLength = USART_WordLength_8b; + u.USART_StopBits = USART_StopBits_1; + u.USART_Parity = USART_Parity_No; + u.USART_HardwareFlowControl = USART_HardwareFlowControl_None; + u.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; + USART_Init(stm32_uart->uart, &u); + + /* Enable and set EXTI Interrupt to the defined priority */ + nvic.NVIC_IRQChannel = stm32_uart->irq; + /* nvic.NVIC_IRQChannelPreemptionPriority = stm32_uart->irq_preempt_prio; */ + /* nvic.NVIC_IRQChannelSubPriority = stm32_uart->irq_sub_prio; */ + nvic.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&nvic); + + /* Enable RX interruption */ + USART_ITConfig(stm32_uart->uart, USART_IT_RXNE, ENABLE); + + /* Enable USART */ + USART_Cmd(stm32_uart->uart, ENABLE); + + return 0; +} + +const struct ucg_uart_driver_ops stm32_uart_ops = { + .disable_tx_irq = disable_tx_irq, + .enable_tx_irq = enable_tx_irq, + .tx_ready = tx_ready, + .rx_ready = rx_ready, + .set_udr = set_udr, + .get_udr = get_udr, + .set_conf = set_conf, +}; diff --git a/arch/stm32/ucg_reent_intr.c b/arch/stm32/ucg_reent_intr.c new file mode 100644 index 0000000..b45439c --- /dev/null +++ b/arch/stm32/ucg_reent_intr.c @@ -0,0 +1,192 @@ +/* + * Copyright 2016, Fabrice DESCLAUX + * Copyright 2016, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 "ucg_reent_intr.h" + +struct saved_stack { + uint32_t r0; + uint32_t r1; + uint32_t r2; + uint32_t r3; + uint32_t r12; + uint32_t lr; + uint32_t pc; + uint32_t psr; +}; + +struct wrapper_stack { + uint32_t psr; + uint32_t lr; + uint32_t r0; + uint32_t r1; + uint32_t r2; + uint32_t r3; + uint32_t r12; + uint32_t pc; +}; + +__attribute__((naked)) uint32_t +ret_wrapper_stkalign_float(void) +{ + __asm__ volatile ( + /* restore float */ + "ADD SP, SP, 0x50 \n" + /* restore PSR, LR */ + "POP {R0, LR} \n" + /* restore PSR */ + //"MSR PSR, R0 \n" + "msr APSR_nzcvq, R0 \n" + /* restore PSR, LR */ + "POP {R0-R3, R12, PC} \n" + ); +} + + +__attribute__((naked)) uint32_t +ret_wrapper_stknoalign_float(void) +{ + __asm__ volatile ( + /* restore float */ + "ADD SP, SP, 0x50 \n" + /* remove padding */ + "ADD SP, SP, 0x4 \n" + /* restore PSR, LR */ + "POP {R0, LR} \n" + /* restore PSR */ + //"MSR PSR, R0 \n" + "msr APSR_nzcvq, R0 \n" + /* restore PSR, LR */ + "POP {R0-R3, R12, PC} \n" + ); +} + + +__attribute__((naked)) uint32_t +ret_wrapper_stkalign_nofloat(void) +{ + __asm__ volatile ( + /* restore float */ + "ADD SP, SP, 0x8 \n" + /* restore PSR, LR */ + "POP {R0, LR} \n" + /* restore PSR */ + //"MSR PSR, R0 \n" + "msr APSR_nzcvq, R0 \n" + /* restore PSR, LR */ + "POP {R0-R3, R12, PC} \n" + ); +} + +__attribute__((naked)) uint32_t +ret_wrapper_stknoalign_nofloat(void) +{ + __asm__ volatile ( + /* restore float */ + "ADD SP, SP, 0x8 \n" + /* remove padding */ + "ADD SP, SP, 0x4 \n" + /* restore PSR, LR */ + "POP {R0, LR} \n" + /* restore PSR */ + //"MSR PSR, R0 \n" + "msr APSR_nzcvq, R0 \n" + /* restore PSR, LR */ + "POP {R0-R3, R12, PC} \n" + ); +} + +void reent_intr(void); + +void ucg_reent_intr(uint32_t *context, void *fct) +//void ucg_reent_intr(uint32_t *context) +{ + struct saved_stack *stk_new = (void *)context; + struct saved_stack *stk_old = (void *)(&context[10]); + uint32_t exe_return; + int stk_padding; + struct wrapper_stack wstack; + + stk_padding = (stk_old->psr & 0x200)?1:0; + + /* copy saved ctxt */ + wstack.r0 = stk_old->r0; + wstack.r1 = stk_old->r1; + wstack.r2 = stk_old->r2; + wstack.r3 = stk_old->r3; + wstack.r12 = stk_old->r12; + wstack.lr = stk_old->lr; + wstack.psr = stk_old->psr & ~0x200; + wstack.pc = stk_old->pc | 1; + + exe_return = (uint32_t)context[-4]; + + + stk_new->pc = (uint32_t)fct; +// stk_new->pc = (uint32_t)reent_intr; + /* set arguments */ + /* stk_new->r0 = 0; */ + /* stk_new->r1 = 1; */ + /* stk_new->r2 = 2; */ + /* stk_new->r3 = 3; */ + stk_new->r12 = wstack.r12; + stk_new->psr = 0x21000000; + + if (exe_return & 0x10) { + /* No Float stacked */ + if (stk_padding) { + stk_new->lr = (uint32_t)ret_wrapper_stknoalign_nofloat; + memcpy(&context[10+1], &wstack, sizeof(wstack)); + } else { + memcpy(&context[10], &wstack, sizeof(wstack)); + stk_new->lr = (uint32_t)ret_wrapper_stkalign_nofloat; + } + } else { + /* Float stacked */ + //STM_EVAL_LEDOn(LED7); + memmove(&context[10], &context[18], 18*4); + if (stk_padding) { + //STM_EVAL_LEDOn(LED10); + memcpy(&context[10+18+1], &wstack, sizeof(wstack)); + stk_new->lr = (uint32_t)ret_wrapper_stknoalign_float; + } else { + //STM_EVAL_LEDOn(LED9); + memcpy(&context[10+18], &wstack, sizeof(wstack)); + stk_new->lr = (uint32_t)ret_wrapper_stkalign_float; + } + + } + + /* force return no fpu */ + context[-4] |= 0x10; + /* create exe_return for real state return */ + context[8] = 0x1337beef; + context[8+1] = exe_return; +} diff --git a/examples/Makefile b/examples/Makefile new file mode 100644 index 0000000..4940940 --- /dev/null +++ b/examples/Makefile @@ -0,0 +1,49 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + +UCGINE ?= $(abspath ..) + +CROSS = arm-none-eabi- + +ifeq ($(CROSS),arm-none-eabi-) +subdir-y := spi-flash spi-flash-client test-callout test-cmd test-mk test-uart +endif +ifeq ($(CROSS),avr-) +subdir-y := spi-flash spi-flash-client test-callout test-cmd test-mk test-uart +endif +ifeq ($(CROSS),) +subdir-y := test-cmdline test-mk +endif + +subdir-y := $(dir $(wildcard */Makefile)) + +include $(UCGINE)/mk/ucgine.mk + +.PHONY: all +all: $(all-targets) + +.PHONY: clean +clean: _ucgine_clean diff --git a/examples/spi-flash-client/Makefile b/examples/spi-flash-client/Makefile new file mode 100644 index 0000000..079eee9 --- /dev/null +++ b/examples/spi-flash-client/Makefile @@ -0,0 +1,77 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + +# XXX +UCGINE ?= /home/zer0/projects/ucgine + +MCU = atmega328p +F_CPU = 8000000UL +AVRDUDE_PORT = /dev/ttyUSB0 +AVRDUDE_PROG = arduino +AVRDUDE_BAUD = 57600 + +O ?= $(CURDIR)/build + +CROSS = avr- +ifneq ($(CROSS),avr-) +$(error AVR target only, cannot override CROSS) +endif + +CFLAGS += -g -O2 -Wall +CFLAGS += -I$(UCGINE)/arch/avr/include +CFLAGS += -mmcu=$(MCU) +CFLAGS += -DF_CPU=$(F_CPU) +LDFLAGS += -mmcu=$(MCU) + +# cirbuf +CFLAGS += -I$(UCGINE)/lib/cirbuf/include +exe-y-$(O)/spi-client += $(UCGINE)/lib/cirbuf/ucg_cirbuf.c +# uart +CFLAGS += -I$(UCGINE)/arch/avr/include +CFLAGS += -I$(UCGINE)/arch/avr/uart/include +CFLAGS += -I$(UCGINE)/lib/uart +CFLAGS += -I$(UCGINE)/lib/uart/include +exe-y-$(O)/spi-client += $(UCGINE)/lib/uart/ucg_uart.c +exe-y-$(O)/spi-client += $(UCGINE)/arch/avr/uart/ucg_avr_uart.c + +# local files +exe-y-$(O)/spi-client += main.c uart.c + +objcopy-hex-y-$(O)/spi-client.hex := $(O)/spi-client +objcopy-bin-y-$(O)/spi-client.bin := $(O)/spi-client + +include $(UCGINE)/mk/ucgine.mk + +.PHONY: all +all: $(all-targets) + +.PHONY: clean +clean: _ucgine_clean + +.PHONY: burn +burn: all + avrdude -e -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROG) \ + -b $(AVRDUDE_BAUD) -U flash:w:$(O)/spi-client:e diff --git a/examples/spi-flash-client/main.c b/examples/spi-flash-client/main.c new file mode 100644 index 0000000..2fcaeff --- /dev/null +++ b/examples/spi-flash-client/main.c @@ -0,0 +1,129 @@ +/* + * Copyright 2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 "uart.h" + +static void ss_high(void) +{ + printf("ss_high\r\n"); + PORTB |= (1 << 2); +} + +static void ss_low(void) +{ + printf("ss_low\r\n"); + PORTB &= (~(1 << 2)); +} + +static uint8_t spi_sendrecv(uint8_t tx) +{ + uint8_t rx; + + SPDR = tx; + + /* Wait for transmission complete */ + while ((SPSR & (1 << SPIF)) == 0) + ; + + ucg_delay_ms(100); + rx = SPDR; + + printf("sent 0x%2.2x, recvd 0x%2.2x '%c'\r\n", + tx, rx, isprint(rx) ? rx : '.'); + ucg_delay_ms(1000); + + return rx; +} + +int main(void) +{ + uint8_t i; + + /* spi: SS (PB2), MOSI (PB3), SCK (PB5) */ + DDRB = (1 << 2) | (1 << 3) | (1 << 5); + + ss_high(); + + /* blink led before start (unfortunatly it's on SCK, we can't + * use it during spi transfer) */ + for (i = 0; i < 3; i++) { + PORTB |= (1 << 5); + ucg_delay_ms(500); + PORTB &= (~(1 << 5)); + ucg_delay_ms(500); + } + + uart_init(); + + ucg_irq_unlock(); + + printf("hello\r\n"); + +#if 0 /* test serial (echo) */ + { + char c; + int ret; + + while (1) { + ret = fread(&c, 1, 1, stdin); + if (ret == 1) + fwrite(&c, 1, 1, stdout); + } + } +#endif + + /* remove power reduction on spi */ + PRR &= ~(1 << PRSPI); + + /* Enable SPI, Master, set clock rate fck/16 */ + SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR0); + + ss_low(); + ucg_delay_ms(1000); + + /* read 5 bytes at address 0x1000 */ + spi_sendrecv(0x03); + spi_sendrecv(0x00); + spi_sendrecv(0x10); + spi_sendrecv(0x00); + for (i = 0; i < 12; i++) + spi_sendrecv(0x00); /* data 0 to 12 */ + ss_high(); + + while (1); + return 0; +} diff --git a/examples/spi-flash-client/uart.c b/examples/spi-flash-client/uart.c new file mode 100644 index 0000000..630dc9a --- /dev/null +++ b/examples/spi-flash-client/uart.c @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2015, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 +#include + +#include "uart.h" + +/* rx & tx buffers */ +static char rx_buf[128]; +static struct ucg_cirbuf rx_cirbuf; +static char tx_buf[128]; +static struct ucg_cirbuf tx_cirbuf; +/* generic uart struct */ +static struct ucg_uart main_uart; +/* avr-specific uart struct */ +static struct ucg_avr_uart avr_uart_data = { + .reg_udr = &UDR0, + .reg_ucsra = &UCSR0A, + .reg_ucsrb = &UCSR0B, + .reg_ucsrc = &UCSR0C, + .reg_ubrrl = &UBRR0L, + .reg_ubrrh = &UBRR0H, + .bit_udre = UDRE0, + .bit_rxc = RXC0, + .bit_udrie = UDRIE0, + .bit_rxen = RXEN0, + .bit_txen = TXEN0, + .bit_rxcie = RXCIE0, + .bit_u2x = U2X0, +}; + +/* send on stdout */ +static int std_send(char c, FILE *f) +{ + (void)f; + ucg_uart_send(&main_uart, c, WAIT); + return 0; +} + +/* recv on stdin */ +static int std_recv(FILE *f) +{ + int16_t c; + + (void)f; + c = ucg_uart_recv(&main_uart, NOWAIT); + if (c < 0) + return _FDEV_EOF; + + return c; +} + +SIGNAL(USART_RX_vect) +{ + ucg_uart_rx_intr(&main_uart); +} + +SIGNAL(USART_UDRE_vect) +{ + ucg_uart_tx_intr(&main_uart); +} + +int uart_init(void) +{ + int ret; + struct ucg_uart_config conf; + + ret = ucg_uart_init(&main_uart, &avr_uart_ops, &avr_uart_data, + &rx_cirbuf, rx_buf, sizeof(rx_buf), + &tx_cirbuf, tx_buf, sizeof(tx_buf)); + if (ret < 0) + return ret; + ucg_uart_getconf(&main_uart, &conf); + conf.baudrate = 57600; + ret = ucg_uart_setconf(&main_uart, &conf); + if (ret < 0) + return ret; + + fdevopen(std_send, std_recv); + return ret; +} diff --git a/examples/spi-flash-client/uart.h b/examples/spi-flash-client/uart.h new file mode 100644 index 0000000..ca1a139 --- /dev/null +++ b/examples/spi-flash-client/uart.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2015, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +#ifndef UART_H_ +#define UART_H_ + +#include + +int uart_init(void); + +#endif diff --git a/examples/spi-flash/Makefile b/examples/spi-flash/Makefile new file mode 100644 index 0000000..de5be09 --- /dev/null +++ b/examples/spi-flash/Makefile @@ -0,0 +1,84 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + +# XXX +STLINK ?= /home/zer0/projects/stm32/stlink +UCGINE ?= /home/zer0/projects/ucgine +STM_COMMON ?= /home/zer0/projects/stm32/stm32_discovery_arm_gcc/STM32F4-Discovery_FW_V1.1.0 + +O ?= $(CURDIR)/build +PROG = $(O)/spi-flash + +CROSS = arm-none-eabi- + +CFLAGS = -g -O2 -Wall -Tstm32_flash.ld +CFLAGS += -DUSE_STDPERIPH_DRIVER +CFLAGS += -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork +CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16 +CFLAGS += -I. +CFLAGS += -I$(UCGINE)/arch/stm32/include + +# Include files from STM libraries +CFLAGS += -I$(STM_COMMON)/Utilities/STM32F4-Discovery +CFLAGS += -I$(STM_COMMON)/Libraries/CMSIS/Include +CFLAGS += -I$(STM_COMMON)/Libraries/CMSIS/ST/STM32F4xx/Include +CFLAGS += -I$(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/inc + +CFLAGS += -I$(UCGINE)/lib/gloss/include + +LDFLAGS = -Tstm32_flash.ld --specs=rdimon.specs -lc +LDFLAGS += -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork +LDFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16 + +# local files +exe-y-$(PROG) := main.c uart.c system_stm32f4xx.c +# stm32 standard periph lib +exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_exti.c +exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_gpio.c +exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_rcc.c +exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_spi.c +exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_usart.c +# startup file +exe-y-$(PROG) += $(STM_COMMON)/Libraries/CMSIS/ST/STM32F4xx/Source/Templates/TrueSTUDIO/startup_stm32f4xx.s +# gloss +exe-y-$(PROG) += $(UCGINE)/lib/gloss/ucg_gloss_stubs.c +exe-y-$(PROG) += $(UCGINE)/lib/gloss/ucg_gloss_chardev.c + +objcopy-hex-y-$(PROG).hex := $(PROG) +objcopy-bin-y-$(PROG).bin := $(PROG) + +include $(UCGINE)/mk/ucgine.mk + +.PHONY: all +all: $(all-targets) + +.PHONY: clean +clean: _ucgine_clean + +# Flash the STM32F4 +.PHONY: burn +burn: all + $(STLINK)/st-flash write $(PROG).bin 0x8000000 diff --git a/examples/spi-flash/main.c b/examples/spi-flash/main.c new file mode 100644 index 0000000..616eb3b --- /dev/null +++ b/examples/spi-flash/main.c @@ -0,0 +1,324 @@ +/* + * Copyright 2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 "uart.h" + +//#define debug_printf(args...) printf(args) +#define debug_printf(args...) + +struct spi_state { + uint8_t cmd; /* current command */ +#define SPI_F_WREN 0x01 /* write enabled */ + uint8_t status; /* status flags */ + uint32_t len; /* number of rx/tx bytes since last reset */ + uint32_t addr; /* current rd/wr address */ +}; + +static struct spi_state spi_state; + +#define SPI_DATA_LEN 8192 /* must be a power of 2 */ +static uint8_t spi_data[SPI_DATA_LEN]; + +static void led_on(void) +{ + GPIOD->ODR |= (1 << 13); +} + +static void led_off(void) +{ + GPIOD->ODR &= (~(1 << 13)); +} + +static int spi_init(void) +{ + SPI_InitTypeDef spi; + GPIO_InitTypeDef gpio; + + /* Enable peripheral clock */ + RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); + + /* Enable the AHB1 peripheral clock for GPIOA. */ + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); + __asm("dsb"); + + /* connect the pins to the desired alternate function, + * configure them: */ + GPIO_StructInit(&gpio); + gpio.GPIO_Speed = GPIO_Speed_25MHz; /* common */ + gpio.GPIO_OType = GPIO_OType_PP; + + /* SPI1_NSS: PA4 */ + gpio.GPIO_Pin = GPIO_Pin_4; + gpio.GPIO_Mode = GPIO_Mode_IN; /* do not use the hw NSS */ + GPIO_Init(GPIOA, &gpio); + + /* SPI1_SCK: PA5 */ + gpio.GPIO_Pin = GPIO_Pin_5; + gpio.GPIO_Mode = GPIO_Mode_AF; + GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1); + GPIO_Init(GPIOA, &gpio); + + /* SPI1_MISO: PA6 */ + gpio.GPIO_Pin = GPIO_Pin_6; + gpio.GPIO_Mode = GPIO_Mode_AF; + GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1); + GPIO_Init(GPIOA, &gpio); + + /* SPI1_MOSI: PA7 */ + gpio.GPIO_Pin = GPIO_Pin_7; + gpio.GPIO_Mode = GPIO_Mode_AF; + GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1); + GPIO_Init(GPIOA, &gpio); + + /* Program the Polarity, Phase, First Data, Baud Rate Prescaler, Slave + * Management, Peripheral Mode and CRC Polynomial values */ + SPI_StructInit(&spi); + spi.SPI_Direction = SPI_Direction_2Lines_FullDuplex; + spi.SPI_Mode = SPI_Mode_Slave; + spi.SPI_DataSize = SPI_DataSize_8b; + spi.SPI_CPOL = SPI_CPOL_Low; /* CK to 0 when idle */ + spi.SPI_CPHA = SPI_CPHA_1Edge; /* data on first transition */ + spi.SPI_NSS = SPI_NSS_Soft; /* manage nss by software */ + spi.SPI_FirstBit = SPI_FirstBit_MSB; + SPI_Init(SPI1, &spi); + + /* Enable the SPI */ + SPI_Cmd(SPI1, ENABLE); + + return 0; +} + +static uint8_t spi_data_read(uint32_t addr) +{ + addr &= (SPI_DATA_LEN - 1); + return spi_data[addr]; +} + +static void spi_data_write(uint32_t addr, uint8_t val) +{ + addr &= (SPI_DATA_LEN - 1); + spi_data[addr] = val; +} + +/* executed at startup */ +static void spi_state_init(void) +{ + memset(&spi_state, 0, sizeof(spi_state)); +} + +/* executed between each command when nss goes low */ +static void spi_state_reset(void) +{ + SPI_SendData(SPI1, 0); + spi_state.cmd = 0; + spi_state.len = 0; + spi_state.addr = 0; +} + +static void spi_data_intr(void) +{ + uint8_t rd_c; + uint8_t wr_c = 0; + + rd_c = SPI_ReceiveData(SPI1); + + /* first byte, set command */ + if (spi_state.len == 0) + spi_state.cmd = rd_c; + + switch(spi_state.cmd) { + /* Read Memory, no dummy cycle */ + case 0x03: + if (spi_state.len == 0) { + spi_state.addr = 0; + } else if (spi_state.len <= 3) { + spi_state.addr <<= 8; + spi_state.addr |= rd_c; + } + /* send data */ + if (spi_state.len >= 3) { + wr_c = spi_data_read(spi_state.addr); + spi_state.addr++; + } + break; + /* Read Memory with dummy cycle */ + case 0x0B: + if (spi_state.len == 0) { + spi_state.addr = 0; + } else if (spi_state.len <= 3) { + spi_state.addr <<= 8; + spi_state.addr |= rd_c; + } else { + wr_c = spi_data_read(spi_state.addr); + spi_state.addr++; + } + break; + + /* Byte-Program (02H) */ + case 0x02: + if (spi_state.len == 0) { + spi_state.addr = 0; + } else if (spi_state.len <= 3) { + spi_state.addr <<= 8; + spi_state.addr |= rd_c; + } else if (spi_state.len == 4 && + (spi_state.status & SPI_F_WREN)) { + spi_data_write(spi_state.addr, rd_c); + } else { + /* ignore next bytes */ + } + break; + + /* Write-Enable (06H) */ + case 0x06: + if (spi_state.len == 0) + spi_state.status |= SPI_F_WREN; + /* ignore next bytes */ + break; + + /* Write-Disable (04H) */ + case 0x04: + if (spi_state.len == 0) + spi_state.status &= (~SPI_F_WREN); + /* ignore next bytes */ + break; + + /* Auto Address Increment Programming (ADH) */ + case 0xAD: + /* Erase 4 KByte of memory array (20H) */ + case 0x20: + /* Erase 32 KByte block of memory (52H) */ + case 0x52: + /* Erase 64 KByte block of memory (D8H) */ + case 0xD8: + /* Chip-Erase (60H) or (C7H) */ + case 0x60: + case 0xC7: + /* Read-Status-Register (05H) */ + case 0x05: + /* Enable-Write-Status-Register (50H) */ + case 0x50: + /* Write-Status-Register (01H) */ + case 0x01: + /* Read-ID (90H) or (ABH) */ + case 0x90: + case 0xAB: + /* JEDEC-ID (9FH) */ + case 0x9F: + /* EnableSOtooutputRY/BY# status during AAI programming (70H) */ + case 0x70: + /* Disable SO as RY/BY# status during AAI programming (80H) */ + case 0x80: + + default: + /* switch on LED on unknown command */ + led_on(); + break; + } + + SPI_SendData(SPI1, wr_c); + spi_state.len++; + debug_printf("rx=0x%2.2x tx=0x%2.2x\n", rd_c, wr_c); +} + +/* called on state transition on the NSS pin */ +static void spi_nss_intr(uint8_t nss) +{ + if (nss) { + debug_printf("ss high\n"); + SPI_NSSInternalSoftwareConfig(SPI1, SPI_NSSInternalSoft_Set); + } else { + debug_printf("ss low\n"); + SPI_NSSInternalSoftwareConfig(SPI1, SPI_NSSInternalSoft_Reset); + /* it's a new command, reset our spi_state structure */ + spi_state_reset(); + } +} + +int main(void) +{ + unsigned i; + uint8_t prev_nss = 1, nss; + + /* enable the clock to GPIOD, and stall instruction pipeline as per + * errata 2.1.13 "Delay after an RCC peripheral clock enabling" */ + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); + __asm("dsb"); + + /* set pin 13 to be general purpose output */ + GPIOD->MODER = (1 << 26); + + /* toggle the pin */ + for (i = 0; i < 3; i++) { + led_on(); + ucg_delay_ms(500); + led_off(); + ucg_delay_ms(500); + } + + uart_init(); + uart_register_stdio(); + printf("salut\n"); + + if (spi_init() < 0) { + printf("SPI init failed\n"); + goto end; + } + + spi_state_init(); + + /* init spi data */ + memset(spi_data, 0, sizeof(spi_data)); + strcpy((char *)&spi_data[0x1000], "Dr0idZ C0rp"); + + while (1) { + /* inspect nss changes */ + nss = !!(GPIOA->IDR & (1 << 4)); + if (nss != prev_nss) { + spi_nss_intr(nss); + prev_nss = nss; + } + + /* data received */ + if (SPI_GetFlagStatus(SPI1, SPI_FLAG_RXNE) != RESET) + spi_data_intr(); + } + + end: + printf("end\n"); + while (1); + return 0; +} diff --git a/examples/spi-flash/stm32_flash.ld b/examples/spi-flash/stm32_flash.ld new file mode 100755 index 0000000..350c05b --- /dev/null +++ b/examples/spi-flash/stm32_flash.ld @@ -0,0 +1,172 @@ +/* +***************************************************************************** +** +** File : stm32_flash.ld +** +** Abstract : Linker script for STM32F407VG Device with +** 1024KByte FLASH, 192KByte RAM +** +** Set heap size, stack size and stack location according +** to application requirements. +** +** Set memory bank area and size if external memory is used. +** +** Target : STMicroelectronics STM32 +** +** Environment : Atollic TrueSTUDIO(R) +** +** Distribution: The file is distributed “as is,” without any warranty +** of any kind. +** +** (c)Copyright Atollic AB. +** You may use this file as-is or modify it according to the needs of your +** project. Distribution of this file (unmodified or modified) is not +** permitted. Atollic AB permit registered Atollic TrueSTUDIO(R) users the +** rights to distribute the assembled, compiled & linked contents of this +** file as part of an application binary file, provided that it is built +** using the Atollic TrueSTUDIO(R) toolchain. +** +***************************************************************************** +*/ + +/* Entry Point */ +ENTRY(Reset_Handler) + +/* Highest address of the user mode stack */ +_estack = 0x20020000; /* end of 128K RAM on AHB bus*/ + +/* Generate a link error if heap and stack don't fit into RAM */ +_Min_Heap_Size = 0; /* required amount of heap */ +_Min_Stack_Size = 0x400; /* required amount of stack */ + +/* Specify the memory areas */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K + MEMORY_B1 (rx) : ORIGIN = 0x60000000, LENGTH = 0K +} + +/* Define output sections */ +SECTIONS +{ + /* The startup code goes first into FLASH */ + .isr_vector : + { + . = ALIGN(4); + KEEP(*(.isr_vector)) /* Startup code */ + . = ALIGN(4); + } >FLASH + + /* The program code and other data goes into FLASH */ + .text : + { + . = ALIGN(4); + *(.text) /* .text sections (code) */ + *(.text*) /* .text* sections (code) */ + *(.rodata) /* .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + *(.glue_7) /* glue arm to thumb code */ + *(.glue_7t) /* glue thumb to arm code */ + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + . = ALIGN(4); + _etext = .; /* define a global symbols at end of code */ + _exit = .; + } >FLASH + + + .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH + .ARM : { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } >FLASH + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array*)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } >FLASH + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array*)) + PROVIDE_HIDDEN (__init_array_end = .); + } >FLASH + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(.fini_array*)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE_HIDDEN (__fini_array_end = .); + } >FLASH + + /* used by the startup to initialize data */ + _sidata = .; + + /* Initialized data sections goes into RAM, load LMA copy after code */ + .data : AT ( _sidata ) + { + . = ALIGN(4); + _sdata = .; /* create a global symbol at data start */ + *(.data) /* .data sections */ + *(.data*) /* .data* sections */ + + . = ALIGN(4); + _edata = .; /* define a global symbol at data end */ + } >RAM + + /* Uninitialized data section */ + . = ALIGN(4); + .bss : + { + /* This is used by the startup in order to initialize the .bss secion */ + _sbss = .; /* define a global symbol at bss start */ + __bss_start__ = _sbss; + *(.bss) + *(.bss*) + *(COMMON) + + . = ALIGN(4); + _ebss = .; /* define a global symbol at bss end */ + __bss_end__ = _ebss; + } >RAM + + /* User_heap_stack section, used to check that there is enough RAM left */ + ._user_heap_stack : + { + . = ALIGN(4); + PROVIDE ( end = . ); + PROVIDE ( _end = . ); + PROVIDE ( __end__ = . ); + . = . + _Min_Heap_Size; + . = . + _Min_Stack_Size; + . = ALIGN(4); + } >RAM + + /* MEMORY_bank1 section, code must be located here explicitly */ + /* Example: extern int foo(void) __attribute__ ((section (".mb1text"))); */ + .memory_b1_text : + { + *(.mb1text) /* .mb1text sections (code) */ + *(.mb1text*) /* .mb1text* sections (code) */ + *(.mb1rodata) /* read-only data (constants) */ + *(.mb1rodata*) + } >MEMORY_B1 + + /* Remove information from the standard libraries */ + /DISCARD/ : + { + libc.a ( * ) + libm.a ( * ) + libgcc.a ( * ) + } + + .ARM.attributes 0 : { *(.ARM.attributes) } +} diff --git a/examples/spi-flash/stm32f4xx_conf.h b/examples/spi-flash/stm32f4xx_conf.h new file mode 100644 index 0000000..74447a8 --- /dev/null +++ b/examples/spi-flash/stm32f4xx_conf.h @@ -0,0 +1,94 @@ +/** + ****************************************************************************** + * @file stm32f4xx_conf.h + * @author MCD Application Team + * @version V1.0.0 + * @date 19-September-2011 + * @brief Library configuration file. + ****************************************************************************** + * @attention + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2011 STMicroelectronics

+ ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F4xx_CONF_H +#define __STM32F4xx_CONF_H + +#if defined (HSE_VALUE) +/* Redefine the HSE value; it's equal to 8 MHz on the STM32F4-DISCOVERY Kit */ + #undef HSE_VALUE + #define HSE_VALUE ((uint32_t)8000000) +#endif /* HSE_VALUE */ + +/* Includes ------------------------------------------------------------------*/ +/* Uncomment the line below to enable peripheral header file inclusion */ +#include "stm32f4xx_adc.h" +#include "stm32f4xx_can.h" +#include "stm32f4xx_crc.h" +#include "stm32f4xx_cryp.h" +#include "stm32f4xx_dac.h" +#include "stm32f4xx_dbgmcu.h" +#include "stm32f4xx_dcmi.h" +#include "stm32f4xx_dma.h" +#include "stm32f4xx_exti.h" +#include "stm32f4xx_flash.h" +#include "stm32f4xx_fsmc.h" +#include "stm32f4xx_hash.h" +#include "stm32f4xx_gpio.h" +#include "stm32f4xx_i2c.h" +#include "stm32f4xx_iwdg.h" +#include "stm32f4xx_pwr.h" +#include "stm32f4xx_rcc.h" +#include "stm32f4xx_rng.h" +#include "stm32f4xx_rtc.h" +#include "stm32f4xx_sdio.h" +#include "stm32f4xx_spi.h" +#include "stm32f4xx_syscfg.h" +#include "stm32f4xx_tim.h" +#include "stm32f4xx_usart.h" +#include "stm32f4xx_wwdg.h" +#include "misc.h" /* High level functions for NVIC and SysTick (add-on to CMSIS functions) */ + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ + +/* If an external clock source is used, then the value of the following define + should be set to the value of the external clock source, else, if no external + clock is used, keep this define commented */ +/*#define I2S_EXTERNAL_CLOCK_VAL 12288000 */ /* Value of the external clock in Hz */ + + +/* Uncomment the line below to expanse the "assert_param" macro in the + Standard Peripheral Library drivers code */ +/* #define USE_FULL_ASSERT 1 */ + +/* Exported macro ------------------------------------------------------------*/ +#ifdef USE_FULL_ASSERT + +/** + * @brief The assert_param macro is used for function's parameters check. + * @param expr: If expr is false, it calls assert_failed function + * which reports the name of the source file and the source + * line number of the call that failed. + * If expr is true, it returns no value. + * @retval None + */ + #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__)) +/* Exported functions ------------------------------------------------------- */ + void assert_failed(uint8_t* file, uint32_t line); +#else + #define assert_param(expr) ((void)0) +#endif /* USE_FULL_ASSERT */ + +#endif /* __STM32F4xx_CONF_H */ + +/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/ diff --git a/examples/spi-flash/system_stm32f4xx.c b/examples/spi-flash/system_stm32f4xx.c new file mode 100644 index 0000000..98e8e04 --- /dev/null +++ b/examples/spi-flash/system_stm32f4xx.c @@ -0,0 +1,553 @@ +/** + ****************************************************************************** + * @file system_stm32f4xx.c + * @author MCD Application Team + * @version V1.0.0 + * @date 30-September-2011 + * @brief CMSIS Cortex-M4 Device Peripheral Access Layer System Source File. + * This file contains the system clock configuration for STM32F4xx devices, + * and is generated by the clock configuration tool + * stm32f4xx_Clock_Configuration_V1.0.0.xls + * + * 1. This file provides two functions and one global variable to be called from + * user application: + * - SystemInit(): Setups the system clock (System clock source, PLL Multiplier + * and Divider factors, AHB/APBx prescalers and Flash settings), + * depending on the configuration made in the clock xls tool. + * This function is called at startup just after reset and + * before branch to main program. This call is made inside + * the "startup_stm32f4xx.s" file. + * + * - SystemCoreClock variable: Contains the core clock (HCLK), it can be used + * by the user application to setup the SysTick + * timer or configure other parameters. + * + * - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must + * be called whenever the core clock is changed + * during program execution. + * + * 2. After each device reset the HSI (16 MHz) is used as system clock source. + * Then SystemInit() function is called, in "startup_stm32f4xx.s" file, to + * configure the system clock before to branch to main program. + * + * 3. If the system clock source selected by user fails to startup, the SystemInit() + * function will do nothing and HSI still used as system clock source. User can + * add some code to deal with this issue inside the SetSysClock() function. + * + * 4. The default value of HSE crystal is set to 25MHz, refer to "HSE_VALUE" define + * in "stm32f4xx.h" file. When HSE is used as system clock source, directly or + * through PLL, and you are using different crystal you have to adapt the HSE + * value to your own configuration. + * + * 5. This file configures the system clock as follows: + *============================================================================= + *============================================================================= + * Supported STM32F4xx device revision | Rev A + *----------------------------------------------------------------------------- + * System Clock source | PLL (HSE) + *----------------------------------------------------------------------------- + * SYSCLK(Hz) | 168000000 + *----------------------------------------------------------------------------- + * HCLK(Hz) | 168000000 + *----------------------------------------------------------------------------- + * AHB Prescaler | 1 + *----------------------------------------------------------------------------- + * APB1 Prescaler | 4 + *----------------------------------------------------------------------------- + * APB2 Prescaler | 2 + *----------------------------------------------------------------------------- + * HSE Frequency(Hz) | 25000000 + *----------------------------------------------------------------------------- + * PLL_M | 25 + *----------------------------------------------------------------------------- + * PLL_N | 336 + *----------------------------------------------------------------------------- + * PLL_P | 2 + *----------------------------------------------------------------------------- + * PLL_Q | 7 + *----------------------------------------------------------------------------- + * PLLI2S_N | NA + *----------------------------------------------------------------------------- + * PLLI2S_R | NA + *----------------------------------------------------------------------------- + * I2S input clock | NA + *----------------------------------------------------------------------------- + * VDD(V) | 3.3 + *----------------------------------------------------------------------------- + * Main regulator output voltage | Scale1 mode + *----------------------------------------------------------------------------- + * Flash Latency(WS) | 5 + *----------------------------------------------------------------------------- + * Prefetch Buffer | OFF + *----------------------------------------------------------------------------- + * Instruction cache | ON + *----------------------------------------------------------------------------- + * Data cache | ON + *----------------------------------------------------------------------------- + * Require 48MHz for USB OTG FS, | Enabled + * SDIO and RNG clock | + *----------------------------------------------------------------------------- + *============================================================================= + ****************************************************************************** + * @attention + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2011 STMicroelectronics

+ ****************************************************************************** + */ + +/** @addtogroup CMSIS + * @{ + */ + +/** @addtogroup stm32f4xx_system + * @{ + */ + +/** @addtogroup STM32F4xx_System_Private_Includes + * @{ + */ + +#include "stm32f4xx.h" + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Private_TypesDefinitions + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Private_Defines + * @{ + */ + +/************************* Miscellaneous Configuration ************************/ +/*!< Uncomment the following line if you need to use external SRAM mounted + on STM324xG_EVAL board as data memory */ +/* #define DATA_IN_ExtSRAM */ + +/*!< Uncomment the following line if you need to relocate your vector Table in + Internal SRAM. */ +/* #define VECT_TAB_SRAM */ +#define VECT_TAB_OFFSET 0x00 /*!< Vector Table base offset field. + This value must be a multiple of 0x200. */ +/******************************************************************************/ + +/************************* PLL Parameters *************************************/ +/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */ +#define PLL_M 25 +#define PLL_N 336 + +/* SYSCLK = PLL_VCO / PLL_P */ +#define PLL_P 2 + +/* USB OTG FS, SDIO and RNG Clock = PLL_VCO / PLLQ */ +#define PLL_Q 7 + +/******************************************************************************/ + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Private_Variables + * @{ + */ + + uint32_t SystemCoreClock = 168000000; + + __I uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9}; + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Private_FunctionPrototypes + * @{ + */ + +static void SetSysClock(void); +#ifdef DATA_IN_ExtSRAM + static void SystemInit_ExtMemCtl(void); +#endif /* DATA_IN_ExtSRAM */ + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Private_Functions + * @{ + */ + +/** + * @brief Setup the microcontroller system + * Initialize the Embedded Flash Interface, the PLL and update the + * SystemFrequency variable. + * @param None + * @retval None + */ +void SystemInit(void) +{ + /* FPU settings ------------------------------------------------------------*/ + #if (__FPU_PRESENT == 1) && (__FPU_USED == 1) + SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */ + #endif + + /* Reset the RCC clock configuration to the default reset state ------------*/ + /* Set HSION bit */ + RCC->CR |= (uint32_t)0x00000001; + + /* Reset CFGR register */ + RCC->CFGR = 0x00000000; + + /* Reset HSEON, CSSON and PLLON bits */ + RCC->CR &= (uint32_t)0xFEF6FFFF; + + /* Reset PLLCFGR register */ + RCC->PLLCFGR = 0x24003010; + + /* Reset HSEBYP bit */ + RCC->CR &= (uint32_t)0xFFFBFFFF; + + /* Disable all interrupts */ + RCC->CIR = 0x00000000; + +#ifdef DATA_IN_ExtSRAM + SystemInit_ExtMemCtl(); +#endif /* DATA_IN_ExtSRAM */ + + /* Configure the System clock source, PLL Multiplier and Divider factors, + AHB/APBx prescalers and Flash settings ----------------------------------*/ + SetSysClock(); + + /* Configure the Vector Table location add offset address ------------------*/ +#ifdef VECT_TAB_SRAM + SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */ +#else + SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */ +#endif +} + +/** + * @brief Update SystemCoreClock variable according to Clock Register Values. + * The SystemCoreClock variable contains the core clock (HCLK), it can + * be used by the user application to setup the SysTick timer or configure + * other parameters. + * + * @note Each time the core clock (HCLK) changes, this function must be called + * to update SystemCoreClock variable value. Otherwise, any configuration + * based on this variable will be incorrect. + * + * @note - The system frequency computed by this function is not the real + * frequency in the chip. It is calculated based on the predefined + * constant and the selected clock source: + * + * - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*) + * + * - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**) + * + * - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**) + * or HSI_VALUE(*) multiplied/divided by the PLL factors. + * + * (*) HSI_VALUE is a constant defined in stm32f4xx.h file (default value + * 16 MHz) but the real value may vary depending on the variations + * in voltage and temperature. + * + * (**) HSE_VALUE is a constant defined in stm32f4xx.h file (default value + * 25 MHz), user has to ensure that HSE_VALUE is same as the real + * frequency of the crystal used. Otherwise, this function may + * have wrong result. + * + * - The result of this function could be not correct when using fractional + * value for HSE crystal. + * + * @param None + * @retval None + */ +void SystemCoreClockUpdate(void) +{ + uint32_t tmp = 0, pllvco = 0, pllp = 2, pllsource = 0, pllm = 2; + + /* Get SYSCLK source -------------------------------------------------------*/ + tmp = RCC->CFGR & RCC_CFGR_SWS; + + switch (tmp) + { + case 0x00: /* HSI used as system clock source */ + SystemCoreClock = HSI_VALUE; + break; + case 0x04: /* HSE used as system clock source */ + SystemCoreClock = HSE_VALUE; + break; + case 0x08: /* PLL used as system clock source */ + + /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N + SYSCLK = PLL_VCO / PLL_P + */ + pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) >> 22; + pllm = RCC->PLLCFGR & RCC_PLLCFGR_PLLM; + + if (pllsource != 0) + { + /* HSE used as PLL clock source */ + pllvco = (HSE_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6); + } + else + { + /* HSI used as PLL clock source */ + pllvco = (HSI_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6); + } + + pllp = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLP) >>16) + 1 ) *2; + SystemCoreClock = pllvco/pllp; + break; + default: + SystemCoreClock = HSI_VALUE; + break; + } + /* Compute HCLK frequency --------------------------------------------------*/ + /* Get HCLK prescaler */ + tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)]; + /* HCLK frequency */ + SystemCoreClock >>= tmp; +} + +/** + * @brief Configures the System clock source, PLL Multiplier and Divider factors, + * AHB/APBx prescalers and Flash settings + * @Note This function should be called only once the RCC clock configuration + * is reset to the default reset state (done in SystemInit() function). + * @param None + * @retval None + */ +static void SetSysClock(void) +{ +/******************************************************************************/ +/* PLL (clocked by HSE) used as System clock source */ +/******************************************************************************/ + __IO uint32_t StartUpCounter = 0, HSEStatus = 0; + + /* Enable HSE */ + RCC->CR |= ((uint32_t)RCC_CR_HSEON); + + /* Wait till HSE is ready and if Time out is reached exit */ + do + { + HSEStatus = RCC->CR & RCC_CR_HSERDY; + StartUpCounter++; + } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); + + if ((RCC->CR & RCC_CR_HSERDY) != RESET) + { + HSEStatus = (uint32_t)0x01; + } + else + { + HSEStatus = (uint32_t)0x00; + } + + if (HSEStatus == (uint32_t)0x01) + { + /* Select regulator voltage output Scale 1 mode, System frequency up to 168 MHz */ + RCC->APB1ENR |= RCC_APB1ENR_PWREN; + PWR->CR |= PWR_CR_VOS; + + /* HCLK = SYSCLK / 1*/ + RCC->CFGR |= RCC_CFGR_HPRE_DIV1; + + /* PCLK2 = HCLK / 2*/ + RCC->CFGR |= RCC_CFGR_PPRE2_DIV2; + + /* PCLK1 = HCLK / 4*/ + RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; + + /* Configure the main PLL */ + RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) | + (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24); + + /* Enable the main PLL */ + RCC->CR |= RCC_CR_PLLON; + + /* Wait till the main PLL is ready */ + while((RCC->CR & RCC_CR_PLLRDY) == 0) + { + } + + /* Configure Flash prefetch, Instruction cache, Data cache and wait state */ + FLASH->ACR = FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS; + + /* Select the main PLL as system clock source */ + RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); + RCC->CFGR |= RCC_CFGR_SW_PLL; + + /* Wait till the main PLL is used as system clock source */ + while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL); + { + } + } + else + { /* If HSE fails to start-up, the application will have wrong clock + configuration. User can add here some code to deal with this error */ + } + +} + +/** + * @brief Setup the external memory controller. Called in startup_stm32f4xx.s + * before jump to __main + * @param None + * @retval None + */ +#ifdef DATA_IN_ExtSRAM +/** + * @brief Setup the external memory controller. + * Called in startup_stm32f4xx.s before jump to main. + * This function configures the external SRAM mounted on STM324xG_EVAL board + * This SRAM will be used as program data memory (including heap and stack). + * @param None + * @retval None + */ +void SystemInit_ExtMemCtl(void) +{ +/*-- GPIOs Configuration -----------------------------------------------------*/ +/* + +-------------------+--------------------+------------------+------------------+ + + SRAM pins assignment + + +-------------------+--------------------+------------------+------------------+ + | PD0 <-> FSMC_D2 | PE0 <-> FSMC_NBL0 | PF0 <-> FSMC_A0 | PG0 <-> FSMC_A10 | + | PD1 <-> FSMC_D3 | PE1 <-> FSMC_NBL1 | PF1 <-> FSMC_A1 | PG1 <-> FSMC_A11 | + | PD4 <-> FSMC_NOE | PE3 <-> FSMC_A19 | PF2 <-> FSMC_A2 | PG2 <-> FSMC_A12 | + | PD5 <-> FSMC_NWE | PE4 <-> FSMC_A20 | PF3 <-> FSMC_A3 | PG3 <-> FSMC_A13 | + | PD8 <-> FSMC_D13 | PE7 <-> FSMC_D4 | PF4 <-> FSMC_A4 | PG4 <-> FSMC_A14 | + | PD9 <-> FSMC_D14 | PE8 <-> FSMC_D5 | PF5 <-> FSMC_A5 | PG5 <-> FSMC_A15 | + | PD10 <-> FSMC_D15 | PE9 <-> FSMC_D6 | PF12 <-> FSMC_A6 | PG9 <-> FSMC_NE2 | + | PD11 <-> FSMC_A16 | PE10 <-> FSMC_D7 | PF13 <-> FSMC_A7 |------------------+ + | PD12 <-> FSMC_A17 | PE11 <-> FSMC_D8 | PF14 <-> FSMC_A8 | + | PD13 <-> FSMC_A18 | PE12 <-> FSMC_D9 | PF15 <-> FSMC_A9 | + | PD14 <-> FSMC_D0 | PE13 <-> FSMC_D10 |------------------+ + | PD15 <-> FSMC_D1 | PE14 <-> FSMC_D11 | + | | PE15 <-> FSMC_D12 | + +-------------------+--------------------+ +*/ + /* Enable GPIOD, GPIOE, GPIOF and GPIOG interface clock */ + RCC->AHB1ENR = 0x00000078; + + /* Connect PDx pins to FSMC Alternate function */ + GPIOD->AFR[0] = 0x00cc00cc; + GPIOD->AFR[1] = 0xcc0ccccc; + /* Configure PDx pins in Alternate function mode */ + GPIOD->MODER = 0xaaaa0a0a; + /* Configure PDx pins speed to 100 MHz */ + GPIOD->OSPEEDR = 0xffff0f0f; + /* Configure PDx pins Output type to push-pull */ + GPIOD->OTYPER = 0x00000000; + /* No pull-up, pull-down for PDx pins */ + GPIOD->PUPDR = 0x00000000; + + /* Connect PEx pins to FSMC Alternate function */ + GPIOE->AFR[0] = 0xc00cc0cc; + GPIOE->AFR[1] = 0xcccccccc; + /* Configure PEx pins in Alternate function mode */ + GPIOE->MODER = 0xaaaa828a; + /* Configure PEx pins speed to 100 MHz */ + GPIOE->OSPEEDR = 0xffffc3cf; + /* Configure PEx pins Output type to push-pull */ + GPIOE->OTYPER = 0x00000000; + /* No pull-up, pull-down for PEx pins */ + GPIOE->PUPDR = 0x00000000; + + /* Connect PFx pins to FSMC Alternate function */ + GPIOF->AFR[0] = 0x00cccccc; + GPIOF->AFR[1] = 0xcccc0000; + /* Configure PFx pins in Alternate function mode */ + GPIOF->MODER = 0xaa000aaa; + /* Configure PFx pins speed to 100 MHz */ + GPIOF->OSPEEDR = 0xff000fff; + /* Configure PFx pins Output type to push-pull */ + GPIOF->OTYPER = 0x00000000; + /* No pull-up, pull-down for PFx pins */ + GPIOF->PUPDR = 0x00000000; + + /* Connect PGx pins to FSMC Alternate function */ + GPIOG->AFR[0] = 0x00cccccc; + GPIOG->AFR[1] = 0x000000c0; + /* Configure PGx pins in Alternate function mode */ + GPIOG->MODER = 0x00080aaa; + /* Configure PGx pins speed to 100 MHz */ + GPIOG->OSPEEDR = 0x000c0fff; + /* Configure PGx pins Output type to push-pull */ + GPIOG->OTYPER = 0x00000000; + /* No pull-up, pull-down for PGx pins */ + GPIOG->PUPDR = 0x00000000; + +/*-- FSMC Configuration ------------------------------------------------------*/ + /* Enable the FSMC interface clock */ + RCC->AHB3ENR = 0x00000001; + + /* Configure and enable Bank1_SRAM2 */ + FSMC_Bank1->BTCR[2] = 0x00001015; + FSMC_Bank1->BTCR[3] = 0x00010603; + FSMC_Bank1E->BWTR[2] = 0x0fffffff; +/* + Bank1_SRAM2 is configured as follow: + + p.FSMC_AddressSetupTime = 3; + p.FSMC_AddressHoldTime = 0; + p.FSMC_DataSetupTime = 6; + p.FSMC_BusTurnAroundDuration = 1; + p.FSMC_CLKDivision = 0; + p.FSMC_DataLatency = 0; + p.FSMC_AccessMode = FSMC_AccessMode_A; + + FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM2; + FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable; + FSMC_NORSRAMInitStructure.FSMC_MemoryType = FSMC_MemoryType_PSRAM; + FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b; + FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable; + FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait = FSMC_AsynchronousWait_Disable; + FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low; + FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable; + FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState; + FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable; + FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable; + FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable; + FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable; + FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &p; + FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &p; +*/ + +} +#endif /* DATA_IN_ExtSRAM */ + + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ +/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/ diff --git a/examples/spi-flash/uart.c b/examples/spi-flash/uart.c new file mode 100644 index 0000000..ceb16ea --- /dev/null +++ b/examples/spi-flash/uart.c @@ -0,0 +1,148 @@ +/* + * Copyright 2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 "stm32f4xx.h" + +#include + +static _ssize_t +uart_write_r(__attribute__((unused)) struct _reent *r, + __attribute__((unused)) int fd, + const void *ptr, size_t len) +{ + size_t i; + const char *buf = ptr; + + + for (i = 0; i < len; i++) { + /* wait that uart is ready */ + while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET) + ; + USART_SendData(USART2, buf[i]); + } + return len; +} + +static _ssize_t +uart_read_r(__attribute__((unused)) struct _reent *r, + __attribute__((unused)) int fd, + void *ptr, size_t len) +{ + size_t i; + char *buf = ptr; + + for (i = 0; i < len; i++) { + /* wait that uart is ready */ + while (USART_GetFlagStatus(USART2, USART_FLAG_RXNE) == RESET) + ; + buf[i] = USART_ReceiveData(USART2); + } + return len; +} + +struct ucg_chardev uart_stdin_dev = { + .name = "stdin", + .open_r = NULL, + .close_r = NULL, + .read_r = uart_read_r, + .write_r = NULL, +}; + +struct ucg_chardev uart_stdout_dev = { + .name = "stdout", + .open_r = NULL, + .close_r = NULL, + .read_r = NULL, + .write_r = uart_write_r, +}; + +struct ucg_chardev uart_stderr_dev = { + .name = "stderr", + .open_r = NULL, + .close_r = NULL, + .read_r = NULL, + .write_r = uart_write_r, +}; + +void uart_init(void) +{ + USART_InitTypeDef uart; + GPIO_InitTypeDef gpio; + + /* Enable the USART2 peripheral clock. */ + RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); + __asm("dsb"); + + /* Enable the AHB1 peripheral clock for GPIOA. */ + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); + __asm("dsb"); + + /* connect to alternate function */ + GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2); + GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2); + + GPIO_StructInit(&gpio); + gpio.GPIO_Speed = GPIO_Speed_25MHz; + + /* configure tx on PA2 */ + gpio.GPIO_Pin = GPIO_Pin_2; + gpio.GPIO_Mode = GPIO_Mode_AF; + GPIO_Init(GPIOA, &gpio); + + /* configure rx on PA3 */ + gpio.GPIO_Pin = GPIO_Pin_3; + gpio.GPIO_Mode = GPIO_Mode_AF; + GPIO_Init(GPIOA, &gpio); + + /* USART configuration */ + uart.USART_BaudRate = 115200; + uart.USART_WordLength = USART_WordLength_8b; + uart.USART_StopBits = USART_StopBits_1; + uart.USART_Parity = USART_Parity_No; + uart.USART_HardwareFlowControl = USART_HardwareFlowControl_None; + uart.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; + USART_Init(USART2, &uart); + + /* Enable USART */ + USART_Cmd(USART2, ENABLE); +} + +void uart_register_stdio(void) +{ + ucg_chardev_register(&uart_stdin_dev); + ucg_chardev_register(&uart_stdout_dev); + ucg_chardev_register(&uart_stderr_dev); + + /* Disable buffering */ + setbuf(stdin, NULL); + setbuf(stdout, NULL); + setbuf(stderr, NULL); +} + diff --git a/examples/spi-flash/uart.h b/examples/spi-flash/uart.h new file mode 100644 index 0000000..ac8951a --- /dev/null +++ b/examples/spi-flash/uart.h @@ -0,0 +1,37 @@ +/* + * Copyright 2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +#ifndef UART_H_ +#define UART_H_ + +/* initialize uart periph */ +void uart_init(void); + +/* register standard input/output */ +void uart_register_stdio(void); + +#endif /* UART_H_ */ diff --git a/examples/test-callout/Makefile b/examples/test-callout/Makefile new file mode 100644 index 0000000..cb136d7 --- /dev/null +++ b/examples/test-callout/Makefile @@ -0,0 +1,138 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + +UCGINE ?= $(abspath ../..) +include $(UCGINE)/mk/ucgine-pre.mk + +O ?= $(CURDIR)/build +PROG = $(O)/test-callout + +UCGINE_SUBARCH ?= stm32f4 +CFLAGS += -DUCGINE_ARCH_$(UCGINE_ARCH) +CFLAGS += -DUCGINE_SUBARCH_$(UCGINE_SUBARCH) +CFLAGS += $(ARCH_CFLAGS) +CFLAGS += -g -Werror -I. + +LDFLAGS += $(ARCH_LDFLAGS) + +ifeq ($(UCGINE_ARCH),avr) +MCU = atmega328p +F_CPU = 8000000UL +AVRDUDE_PORT = /dev/ttyUSB0 +AVRDUDE_PROG = arduino +AVRDUDE_BAUD = 57600 +CFLAGS += -DF_CPU=$(F_CPU) +CFLAGS += -I$(UCGINE)/arch/avr/uart/include +CFLAGS += -mmcu=$(MCU) +LDFLAGS += -mmcu=$(MCU) +endif + +ifeq ($(UCGINE_ARCH),stm32) +ifeq ($(UCGINE_SUBARCH),stm32f3) +ver = 3 +STLINK ?= /home/zer0/projects/stm32/stlink +STM_COMMON ?= /home/zer0/projects/stm32/stm32_discovery_arm_gcc/STM32F3-Discovery_FW_V1.1.0 +CFLAGS += -Tstm32_flash.ld +CFLAGS += -I$(STM_COMMON)/src +CFLAGS += -I$(STM_COMMON)/Libraries +CFLAGS += -I$(STM_COMMON)/Libraries/CMSIS/Include +CFLAGS += -I$(STM_COMMON)/Libraries/CMSIS/Device/ST/STM32F30x/Include +CFLAGS += -I$(STM_COMMON)/Libraries/STM32F30x_StdPeriph_Driver/inc +CFLAGS += -I$(UCGINE)/lib/gloss/include +CFLAGS += -I$(UCGINE)/arch/stm32/include +CFLAGS += -I$(UCGINE)/arch/stm32/uart/include +LDFLAGS += -Tstm32_flash.ld +# stm32 standard periph lib +exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F30x_StdPeriph_Driver/src/stm32f30x_exti.c +exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F30x_StdPeriph_Driver/src/stm32f30x_gpio.c +exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F30x_StdPeriph_Driver/src/stm32f30x_rcc.c +exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F30x_StdPeriph_Driver/src/stm32f30x_misc.c +# leds +exe-y-$(PROG) += $(STM_COMMON)/src/stm32f3_discovery.c +# system & startup file +exe-y-$(PROG) += $(STM_COMMON)/src/system_stm32f30x.c +exe-y-$(PROG) += $(STM_COMMON)/Libraries/CMSIS/Device/ST/STM32F30x/Source/Templates/TrueSTUDIO/startup_stm32f30x.s +# reentrant interrupts +exe-y-$(PROG) += $(UCGINE)/arch/stm32/ucg_reent_intr.c +else +ver = 4 +STLINK ?= /home/zer0/projects/stm32/stlink +STM_COMMON ?= /home/zer0/projects/stm32/stm32_discovery_arm_gcc/STM32F4-Discovery_FW_V1.1.0 +CFLAGS += -Tstm32_flash.ld +CFLAGS += -I$(STM_COMMON)/Utilities/STM32F4-Discovery +CFLAGS += -I$(STM_COMMON)/Libraries/CMSIS/Include +CFLAGS += -I$(STM_COMMON)/Libraries/CMSIS/ST/STM32F4xx/Include +CFLAGS += -I$(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/inc +CFLAGS += -I$(UCGINE)/lib/gloss/include +CFLAGS += -I$(UCGINE)/arch/stm32/include +CFLAGS += -I$(UCGINE)/arch/stm32/uart/include +LDFLAGS += -Tstm32_flash.ld +exe-y-$(PROG) += system_stm32f4xx.c +# stm32 standard periph lib +exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_exti.c +exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_gpio.c +exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_rcc.c +exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/misc.c +# leds +exe-y-$(PROG) += $(STM_COMMON)/Utilities/STM32F4-Discovery/stm32f4_discovery.c +# startup file +exe-y-$(PROG) += $(STM_COMMON)/Libraries/CMSIS/ST/STM32F4xx/Source/Templates/TrueSTUDIO/startup_stm32f4xx.s +# reentrant interrupts +exe-y-$(PROG) += $(UCGINE)/arch/stm32/ucg_reent_intr.c +endif +endif + +ifeq ($(UCGINE_ARCH),posix) +$(error Not supported on posix) +endif + +# callout +CFLAGS += -I$(UCGINE)/lib/callout/include +exe-y-$(PROG) += $(UCGINE)/lib/callout/ucg_callout.c + +exe-y-$(PROG) += main.c + +objcopy-hex-y-$(PROG).hex := $(PROG) +objcopy-bin-y-$(PROG).bin := $(PROG) + +include $(UCGINE)/mk/ucgine-post.mk + +.PHONY: all +all: $(all-targets) + +.PHONY: clean +clean: _ucgine_clean + +# Flash the STM32 +.PHONY: burn +burn: all +ifeq ($(CROSS),arm-none-eabi-) + $(STLINK)/st-flash write $(PROG).bin 0x8000000 +endif +ifeq ($(CROSS),avr-) + avrdude -e -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROG) \ + -b $(AVRDUDE_BAUD) -U flash:w:$(PROG):e +endif diff --git a/examples/test-callout/main.c b/examples/test-callout/main.c new file mode 100644 index 0000000..b18654c --- /dev/null +++ b/examples/test-callout/main.c @@ -0,0 +1,221 @@ +/* + * Copyright 2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 + +static volatile uint32_t global_ms; +static volatile uint8_t brightness; /* 0 to 100 */ +static volatile int8_t brightness_inc = 1; +static struct ucg_callout_mgr *p_intr_cm = NULL; + +#if defined(UCGINE_ARCH_stm32) + +#if defined(UCGINE_SUBARCH_stm32f3) +#include +#include +#elif defined(UCGINE_SUBARCH_stm32f4) +#include +#include +#endif + +static void target_init(void) +{ + RCC_ClocksTypeDef RCC_Clocks; + + /* Get SYSCLK, HCLK and PCLKx frequency */ + RCC_GetClocksFreq(&RCC_Clocks); + /* generate an interrupt every ms */ + SysTick_Config(RCC_Clocks.HCLK_Frequency / 1000); + + /* Initialize LEDs */ + STM_EVAL_LEDInit(LED6); +} + +static void led_on(void) +{ + STM_EVAL_LEDOn(LED6); +} + +static void led_off(void) +{ + STM_EVAL_LEDOff(LED6); +} + +void reent_intr(void) +{ + global_ms++; + if (p_intr_cm != NULL) + ucg_callout_manage(p_intr_cm); +} + +/* called every ms */ +__attribute__ ((naked)) void SysTick_Handler(void) +{ + UCG_REENT_INTR(reent_intr); +} +#elif defined(UCGINE_ARCH_avr) +#include + +static void led_on(void) +{ + PORTB |= (1 << 5); +} + +static void led_off(void) +{ + PORTB &= (~(1 << 5)); +} + +SIGNAL(TIMER0_OVF_vect) +{ + static uint16_t i = 0; + + i++; + if ((i & 0x3) != 0) + return; + + global_ms++; + if (p_intr_cm != NULL) + ucg_callout_manage(p_intr_cm); +} + +static void target_init(void) +{ + DDRB = (1 << 5); + + TCCR0B = (1 << CS01); /* div = 8 */ + TIMSK0 |= (1 << TOIE0); /* enable timer0 intr */ +} +#endif + +static uint16_t get_time_ms(void) +{ + ucg_irqflags_t flags; + uint32_t ms; + + flags = ucg_irq_lock_save(); + ms = global_ms; + ucg_irq_unlock_restore(flags); + return ms; +} + +/* every ms, enable or disable the led depending on brightness */ +static void led_control_cb(struct ucg_callout_mgr *cm, + struct ucg_callout *tim, void *arg) +{ + static uint8_t accu; + + (void)arg; + + accu += brightness; + if (accu < 100) { + led_off(); + } else { + led_on(); + accu -= 100; + } + + ucg_callout_reschedule(cm, tim, 1); +} + +/* every 50 ms, change increase or decrease the brightness from 0 to 100 */ +static void led_update_brightness_cb(struct ucg_callout_mgr *cm, + struct ucg_callout *tim, void *arg) +{ + (void)arg; + + brightness += brightness_inc; + if (brightness == 0) + brightness_inc = 1; + else if (brightness == 100) + brightness_inc = -1; + + ucg_callout_reschedule(cm, tim, 10); +} + +/* every second, sleep during 100ms */ +static void sleep_cb(struct ucg_callout_mgr *cm, + struct ucg_callout *tim, void *arg) +{ + (void)arg; + + ucg_delay_ms(200); + ucg_callout_reschedule(cm, tim, 300); +} + +int main(void) +{ + struct ucg_callout_mgr intr_cm; + struct ucg_callout_mgr loop_cm; + struct ucg_callout br_timer; + struct ucg_callout led_timer; + struct ucg_callout sleep_timer; + int i; + + target_init(); + + /* toggle the pin before starting */ + for (i = 0; i < 3; i++) { + ucg_delay_ms(500); + ucg_delay_ms(500); + } + + /* init loop callout manager */ + ucg_callout_mgr_init(&loop_cm, get_time_ms); + + /* load a timer in the loop cmgr that will update the brightness */ + ucg_callout_init(&br_timer, led_update_brightness_cb, NULL, 128); + ucg_callout_schedule(&loop_cm, &br_timer, 0); + + /* init intr callout manager */ + ucg_callout_mgr_init(&intr_cm, get_time_ms); + ucg_irq_lock(); + p_intr_cm = &intr_cm; + ucg_irq_unlock(); + + /* load a timer in the intr cmgr that control led on/off */ + ucg_callout_init(&led_timer, led_control_cb, NULL, 128); + ucg_callout_schedule(&intr_cm, &led_timer, 0); + + /* load a lower prio timer in the intr cmgr that just sleeps */ + ucg_callout_init(&sleep_timer, sleep_cb, NULL, 100); + ucg_callout_schedule(&intr_cm, &sleep_timer, 0); + + while (1) { + ucg_callout_manage(&loop_cm); + } + + while (1); + return 0; +} diff --git a/examples/test-callout/stm32_flash.ld b/examples/test-callout/stm32_flash.ld new file mode 100755 index 0000000..350c05b --- /dev/null +++ b/examples/test-callout/stm32_flash.ld @@ -0,0 +1,172 @@ +/* +***************************************************************************** +** +** File : stm32_flash.ld +** +** Abstract : Linker script for STM32F407VG Device with +** 1024KByte FLASH, 192KByte RAM +** +** Set heap size, stack size and stack location according +** to application requirements. +** +** Set memory bank area and size if external memory is used. +** +** Target : STMicroelectronics STM32 +** +** Environment : Atollic TrueSTUDIO(R) +** +** Distribution: The file is distributed “as is,” without any warranty +** of any kind. +** +** (c)Copyright Atollic AB. +** You may use this file as-is or modify it according to the needs of your +** project. Distribution of this file (unmodified or modified) is not +** permitted. Atollic AB permit registered Atollic TrueSTUDIO(R) users the +** rights to distribute the assembled, compiled & linked contents of this +** file as part of an application binary file, provided that it is built +** using the Atollic TrueSTUDIO(R) toolchain. +** +***************************************************************************** +*/ + +/* Entry Point */ +ENTRY(Reset_Handler) + +/* Highest address of the user mode stack */ +_estack = 0x20020000; /* end of 128K RAM on AHB bus*/ + +/* Generate a link error if heap and stack don't fit into RAM */ +_Min_Heap_Size = 0; /* required amount of heap */ +_Min_Stack_Size = 0x400; /* required amount of stack */ + +/* Specify the memory areas */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K + MEMORY_B1 (rx) : ORIGIN = 0x60000000, LENGTH = 0K +} + +/* Define output sections */ +SECTIONS +{ + /* The startup code goes first into FLASH */ + .isr_vector : + { + . = ALIGN(4); + KEEP(*(.isr_vector)) /* Startup code */ + . = ALIGN(4); + } >FLASH + + /* The program code and other data goes into FLASH */ + .text : + { + . = ALIGN(4); + *(.text) /* .text sections (code) */ + *(.text*) /* .text* sections (code) */ + *(.rodata) /* .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + *(.glue_7) /* glue arm to thumb code */ + *(.glue_7t) /* glue thumb to arm code */ + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + . = ALIGN(4); + _etext = .; /* define a global symbols at end of code */ + _exit = .; + } >FLASH + + + .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH + .ARM : { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } >FLASH + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array*)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } >FLASH + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array*)) + PROVIDE_HIDDEN (__init_array_end = .); + } >FLASH + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(.fini_array*)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE_HIDDEN (__fini_array_end = .); + } >FLASH + + /* used by the startup to initialize data */ + _sidata = .; + + /* Initialized data sections goes into RAM, load LMA copy after code */ + .data : AT ( _sidata ) + { + . = ALIGN(4); + _sdata = .; /* create a global symbol at data start */ + *(.data) /* .data sections */ + *(.data*) /* .data* sections */ + + . = ALIGN(4); + _edata = .; /* define a global symbol at data end */ + } >RAM + + /* Uninitialized data section */ + . = ALIGN(4); + .bss : + { + /* This is used by the startup in order to initialize the .bss secion */ + _sbss = .; /* define a global symbol at bss start */ + __bss_start__ = _sbss; + *(.bss) + *(.bss*) + *(COMMON) + + . = ALIGN(4); + _ebss = .; /* define a global symbol at bss end */ + __bss_end__ = _ebss; + } >RAM + + /* User_heap_stack section, used to check that there is enough RAM left */ + ._user_heap_stack : + { + . = ALIGN(4); + PROVIDE ( end = . ); + PROVIDE ( _end = . ); + PROVIDE ( __end__ = . ); + . = . + _Min_Heap_Size; + . = . + _Min_Stack_Size; + . = ALIGN(4); + } >RAM + + /* MEMORY_bank1 section, code must be located here explicitly */ + /* Example: extern int foo(void) __attribute__ ((section (".mb1text"))); */ + .memory_b1_text : + { + *(.mb1text) /* .mb1text sections (code) */ + *(.mb1text*) /* .mb1text* sections (code) */ + *(.mb1rodata) /* read-only data (constants) */ + *(.mb1rodata*) + } >MEMORY_B1 + + /* Remove information from the standard libraries */ + /DISCARD/ : + { + libc.a ( * ) + libm.a ( * ) + libgcc.a ( * ) + } + + .ARM.attributes 0 : { *(.ARM.attributes) } +} diff --git a/examples/test-callout/stm32f4xx_conf.h b/examples/test-callout/stm32f4xx_conf.h new file mode 100644 index 0000000..74447a8 --- /dev/null +++ b/examples/test-callout/stm32f4xx_conf.h @@ -0,0 +1,94 @@ +/** + ****************************************************************************** + * @file stm32f4xx_conf.h + * @author MCD Application Team + * @version V1.0.0 + * @date 19-September-2011 + * @brief Library configuration file. + ****************************************************************************** + * @attention + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2011 STMicroelectronics

+ ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F4xx_CONF_H +#define __STM32F4xx_CONF_H + +#if defined (HSE_VALUE) +/* Redefine the HSE value; it's equal to 8 MHz on the STM32F4-DISCOVERY Kit */ + #undef HSE_VALUE + #define HSE_VALUE ((uint32_t)8000000) +#endif /* HSE_VALUE */ + +/* Includes ------------------------------------------------------------------*/ +/* Uncomment the line below to enable peripheral header file inclusion */ +#include "stm32f4xx_adc.h" +#include "stm32f4xx_can.h" +#include "stm32f4xx_crc.h" +#include "stm32f4xx_cryp.h" +#include "stm32f4xx_dac.h" +#include "stm32f4xx_dbgmcu.h" +#include "stm32f4xx_dcmi.h" +#include "stm32f4xx_dma.h" +#include "stm32f4xx_exti.h" +#include "stm32f4xx_flash.h" +#include "stm32f4xx_fsmc.h" +#include "stm32f4xx_hash.h" +#include "stm32f4xx_gpio.h" +#include "stm32f4xx_i2c.h" +#include "stm32f4xx_iwdg.h" +#include "stm32f4xx_pwr.h" +#include "stm32f4xx_rcc.h" +#include "stm32f4xx_rng.h" +#include "stm32f4xx_rtc.h" +#include "stm32f4xx_sdio.h" +#include "stm32f4xx_spi.h" +#include "stm32f4xx_syscfg.h" +#include "stm32f4xx_tim.h" +#include "stm32f4xx_usart.h" +#include "stm32f4xx_wwdg.h" +#include "misc.h" /* High level functions for NVIC and SysTick (add-on to CMSIS functions) */ + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ + +/* If an external clock source is used, then the value of the following define + should be set to the value of the external clock source, else, if no external + clock is used, keep this define commented */ +/*#define I2S_EXTERNAL_CLOCK_VAL 12288000 */ /* Value of the external clock in Hz */ + + +/* Uncomment the line below to expanse the "assert_param" macro in the + Standard Peripheral Library drivers code */ +/* #define USE_FULL_ASSERT 1 */ + +/* Exported macro ------------------------------------------------------------*/ +#ifdef USE_FULL_ASSERT + +/** + * @brief The assert_param macro is used for function's parameters check. + * @param expr: If expr is false, it calls assert_failed function + * which reports the name of the source file and the source + * line number of the call that failed. + * If expr is true, it returns no value. + * @retval None + */ + #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__)) +/* Exported functions ------------------------------------------------------- */ + void assert_failed(uint8_t* file, uint32_t line); +#else + #define assert_param(expr) ((void)0) +#endif /* USE_FULL_ASSERT */ + +#endif /* __STM32F4xx_CONF_H */ + +/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/ diff --git a/examples/test-callout/system_stm32f4xx.c b/examples/test-callout/system_stm32f4xx.c new file mode 100644 index 0000000..98e8e04 --- /dev/null +++ b/examples/test-callout/system_stm32f4xx.c @@ -0,0 +1,553 @@ +/** + ****************************************************************************** + * @file system_stm32f4xx.c + * @author MCD Application Team + * @version V1.0.0 + * @date 30-September-2011 + * @brief CMSIS Cortex-M4 Device Peripheral Access Layer System Source File. + * This file contains the system clock configuration for STM32F4xx devices, + * and is generated by the clock configuration tool + * stm32f4xx_Clock_Configuration_V1.0.0.xls + * + * 1. This file provides two functions and one global variable to be called from + * user application: + * - SystemInit(): Setups the system clock (System clock source, PLL Multiplier + * and Divider factors, AHB/APBx prescalers and Flash settings), + * depending on the configuration made in the clock xls tool. + * This function is called at startup just after reset and + * before branch to main program. This call is made inside + * the "startup_stm32f4xx.s" file. + * + * - SystemCoreClock variable: Contains the core clock (HCLK), it can be used + * by the user application to setup the SysTick + * timer or configure other parameters. + * + * - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must + * be called whenever the core clock is changed + * during program execution. + * + * 2. After each device reset the HSI (16 MHz) is used as system clock source. + * Then SystemInit() function is called, in "startup_stm32f4xx.s" file, to + * configure the system clock before to branch to main program. + * + * 3. If the system clock source selected by user fails to startup, the SystemInit() + * function will do nothing and HSI still used as system clock source. User can + * add some code to deal with this issue inside the SetSysClock() function. + * + * 4. The default value of HSE crystal is set to 25MHz, refer to "HSE_VALUE" define + * in "stm32f4xx.h" file. When HSE is used as system clock source, directly or + * through PLL, and you are using different crystal you have to adapt the HSE + * value to your own configuration. + * + * 5. This file configures the system clock as follows: + *============================================================================= + *============================================================================= + * Supported STM32F4xx device revision | Rev A + *----------------------------------------------------------------------------- + * System Clock source | PLL (HSE) + *----------------------------------------------------------------------------- + * SYSCLK(Hz) | 168000000 + *----------------------------------------------------------------------------- + * HCLK(Hz) | 168000000 + *----------------------------------------------------------------------------- + * AHB Prescaler | 1 + *----------------------------------------------------------------------------- + * APB1 Prescaler | 4 + *----------------------------------------------------------------------------- + * APB2 Prescaler | 2 + *----------------------------------------------------------------------------- + * HSE Frequency(Hz) | 25000000 + *----------------------------------------------------------------------------- + * PLL_M | 25 + *----------------------------------------------------------------------------- + * PLL_N | 336 + *----------------------------------------------------------------------------- + * PLL_P | 2 + *----------------------------------------------------------------------------- + * PLL_Q | 7 + *----------------------------------------------------------------------------- + * PLLI2S_N | NA + *----------------------------------------------------------------------------- + * PLLI2S_R | NA + *----------------------------------------------------------------------------- + * I2S input clock | NA + *----------------------------------------------------------------------------- + * VDD(V) | 3.3 + *----------------------------------------------------------------------------- + * Main regulator output voltage | Scale1 mode + *----------------------------------------------------------------------------- + * Flash Latency(WS) | 5 + *----------------------------------------------------------------------------- + * Prefetch Buffer | OFF + *----------------------------------------------------------------------------- + * Instruction cache | ON + *----------------------------------------------------------------------------- + * Data cache | ON + *----------------------------------------------------------------------------- + * Require 48MHz for USB OTG FS, | Enabled + * SDIO and RNG clock | + *----------------------------------------------------------------------------- + *============================================================================= + ****************************************************************************** + * @attention + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2011 STMicroelectronics

+ ****************************************************************************** + */ + +/** @addtogroup CMSIS + * @{ + */ + +/** @addtogroup stm32f4xx_system + * @{ + */ + +/** @addtogroup STM32F4xx_System_Private_Includes + * @{ + */ + +#include "stm32f4xx.h" + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Private_TypesDefinitions + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Private_Defines + * @{ + */ + +/************************* Miscellaneous Configuration ************************/ +/*!< Uncomment the following line if you need to use external SRAM mounted + on STM324xG_EVAL board as data memory */ +/* #define DATA_IN_ExtSRAM */ + +/*!< Uncomment the following line if you need to relocate your vector Table in + Internal SRAM. */ +/* #define VECT_TAB_SRAM */ +#define VECT_TAB_OFFSET 0x00 /*!< Vector Table base offset field. + This value must be a multiple of 0x200. */ +/******************************************************************************/ + +/************************* PLL Parameters *************************************/ +/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */ +#define PLL_M 25 +#define PLL_N 336 + +/* SYSCLK = PLL_VCO / PLL_P */ +#define PLL_P 2 + +/* USB OTG FS, SDIO and RNG Clock = PLL_VCO / PLLQ */ +#define PLL_Q 7 + +/******************************************************************************/ + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Private_Variables + * @{ + */ + + uint32_t SystemCoreClock = 168000000; + + __I uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9}; + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Private_FunctionPrototypes + * @{ + */ + +static void SetSysClock(void); +#ifdef DATA_IN_ExtSRAM + static void SystemInit_ExtMemCtl(void); +#endif /* DATA_IN_ExtSRAM */ + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Private_Functions + * @{ + */ + +/** + * @brief Setup the microcontroller system + * Initialize the Embedded Flash Interface, the PLL and update the + * SystemFrequency variable. + * @param None + * @retval None + */ +void SystemInit(void) +{ + /* FPU settings ------------------------------------------------------------*/ + #if (__FPU_PRESENT == 1) && (__FPU_USED == 1) + SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */ + #endif + + /* Reset the RCC clock configuration to the default reset state ------------*/ + /* Set HSION bit */ + RCC->CR |= (uint32_t)0x00000001; + + /* Reset CFGR register */ + RCC->CFGR = 0x00000000; + + /* Reset HSEON, CSSON and PLLON bits */ + RCC->CR &= (uint32_t)0xFEF6FFFF; + + /* Reset PLLCFGR register */ + RCC->PLLCFGR = 0x24003010; + + /* Reset HSEBYP bit */ + RCC->CR &= (uint32_t)0xFFFBFFFF; + + /* Disable all interrupts */ + RCC->CIR = 0x00000000; + +#ifdef DATA_IN_ExtSRAM + SystemInit_ExtMemCtl(); +#endif /* DATA_IN_ExtSRAM */ + + /* Configure the System clock source, PLL Multiplier and Divider factors, + AHB/APBx prescalers and Flash settings ----------------------------------*/ + SetSysClock(); + + /* Configure the Vector Table location add offset address ------------------*/ +#ifdef VECT_TAB_SRAM + SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */ +#else + SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */ +#endif +} + +/** + * @brief Update SystemCoreClock variable according to Clock Register Values. + * The SystemCoreClock variable contains the core clock (HCLK), it can + * be used by the user application to setup the SysTick timer or configure + * other parameters. + * + * @note Each time the core clock (HCLK) changes, this function must be called + * to update SystemCoreClock variable value. Otherwise, any configuration + * based on this variable will be incorrect. + * + * @note - The system frequency computed by this function is not the real + * frequency in the chip. It is calculated based on the predefined + * constant and the selected clock source: + * + * - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*) + * + * - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**) + * + * - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**) + * or HSI_VALUE(*) multiplied/divided by the PLL factors. + * + * (*) HSI_VALUE is a constant defined in stm32f4xx.h file (default value + * 16 MHz) but the real value may vary depending on the variations + * in voltage and temperature. + * + * (**) HSE_VALUE is a constant defined in stm32f4xx.h file (default value + * 25 MHz), user has to ensure that HSE_VALUE is same as the real + * frequency of the crystal used. Otherwise, this function may + * have wrong result. + * + * - The result of this function could be not correct when using fractional + * value for HSE crystal. + * + * @param None + * @retval None + */ +void SystemCoreClockUpdate(void) +{ + uint32_t tmp = 0, pllvco = 0, pllp = 2, pllsource = 0, pllm = 2; + + /* Get SYSCLK source -------------------------------------------------------*/ + tmp = RCC->CFGR & RCC_CFGR_SWS; + + switch (tmp) + { + case 0x00: /* HSI used as system clock source */ + SystemCoreClock = HSI_VALUE; + break; + case 0x04: /* HSE used as system clock source */ + SystemCoreClock = HSE_VALUE; + break; + case 0x08: /* PLL used as system clock source */ + + /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N + SYSCLK = PLL_VCO / PLL_P + */ + pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) >> 22; + pllm = RCC->PLLCFGR & RCC_PLLCFGR_PLLM; + + if (pllsource != 0) + { + /* HSE used as PLL clock source */ + pllvco = (HSE_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6); + } + else + { + /* HSI used as PLL clock source */ + pllvco = (HSI_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6); + } + + pllp = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLP) >>16) + 1 ) *2; + SystemCoreClock = pllvco/pllp; + break; + default: + SystemCoreClock = HSI_VALUE; + break; + } + /* Compute HCLK frequency --------------------------------------------------*/ + /* Get HCLK prescaler */ + tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)]; + /* HCLK frequency */ + SystemCoreClock >>= tmp; +} + +/** + * @brief Configures the System clock source, PLL Multiplier and Divider factors, + * AHB/APBx prescalers and Flash settings + * @Note This function should be called only once the RCC clock configuration + * is reset to the default reset state (done in SystemInit() function). + * @param None + * @retval None + */ +static void SetSysClock(void) +{ +/******************************************************************************/ +/* PLL (clocked by HSE) used as System clock source */ +/******************************************************************************/ + __IO uint32_t StartUpCounter = 0, HSEStatus = 0; + + /* Enable HSE */ + RCC->CR |= ((uint32_t)RCC_CR_HSEON); + + /* Wait till HSE is ready and if Time out is reached exit */ + do + { + HSEStatus = RCC->CR & RCC_CR_HSERDY; + StartUpCounter++; + } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); + + if ((RCC->CR & RCC_CR_HSERDY) != RESET) + { + HSEStatus = (uint32_t)0x01; + } + else + { + HSEStatus = (uint32_t)0x00; + } + + if (HSEStatus == (uint32_t)0x01) + { + /* Select regulator voltage output Scale 1 mode, System frequency up to 168 MHz */ + RCC->APB1ENR |= RCC_APB1ENR_PWREN; + PWR->CR |= PWR_CR_VOS; + + /* HCLK = SYSCLK / 1*/ + RCC->CFGR |= RCC_CFGR_HPRE_DIV1; + + /* PCLK2 = HCLK / 2*/ + RCC->CFGR |= RCC_CFGR_PPRE2_DIV2; + + /* PCLK1 = HCLK / 4*/ + RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; + + /* Configure the main PLL */ + RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) | + (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24); + + /* Enable the main PLL */ + RCC->CR |= RCC_CR_PLLON; + + /* Wait till the main PLL is ready */ + while((RCC->CR & RCC_CR_PLLRDY) == 0) + { + } + + /* Configure Flash prefetch, Instruction cache, Data cache and wait state */ + FLASH->ACR = FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS; + + /* Select the main PLL as system clock source */ + RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); + RCC->CFGR |= RCC_CFGR_SW_PLL; + + /* Wait till the main PLL is used as system clock source */ + while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL); + { + } + } + else + { /* If HSE fails to start-up, the application will have wrong clock + configuration. User can add here some code to deal with this error */ + } + +} + +/** + * @brief Setup the external memory controller. Called in startup_stm32f4xx.s + * before jump to __main + * @param None + * @retval None + */ +#ifdef DATA_IN_ExtSRAM +/** + * @brief Setup the external memory controller. + * Called in startup_stm32f4xx.s before jump to main. + * This function configures the external SRAM mounted on STM324xG_EVAL board + * This SRAM will be used as program data memory (including heap and stack). + * @param None + * @retval None + */ +void SystemInit_ExtMemCtl(void) +{ +/*-- GPIOs Configuration -----------------------------------------------------*/ +/* + +-------------------+--------------------+------------------+------------------+ + + SRAM pins assignment + + +-------------------+--------------------+------------------+------------------+ + | PD0 <-> FSMC_D2 | PE0 <-> FSMC_NBL0 | PF0 <-> FSMC_A0 | PG0 <-> FSMC_A10 | + | PD1 <-> FSMC_D3 | PE1 <-> FSMC_NBL1 | PF1 <-> FSMC_A1 | PG1 <-> FSMC_A11 | + | PD4 <-> FSMC_NOE | PE3 <-> FSMC_A19 | PF2 <-> FSMC_A2 | PG2 <-> FSMC_A12 | + | PD5 <-> FSMC_NWE | PE4 <-> FSMC_A20 | PF3 <-> FSMC_A3 | PG3 <-> FSMC_A13 | + | PD8 <-> FSMC_D13 | PE7 <-> FSMC_D4 | PF4 <-> FSMC_A4 | PG4 <-> FSMC_A14 | + | PD9 <-> FSMC_D14 | PE8 <-> FSMC_D5 | PF5 <-> FSMC_A5 | PG5 <-> FSMC_A15 | + | PD10 <-> FSMC_D15 | PE9 <-> FSMC_D6 | PF12 <-> FSMC_A6 | PG9 <-> FSMC_NE2 | + | PD11 <-> FSMC_A16 | PE10 <-> FSMC_D7 | PF13 <-> FSMC_A7 |------------------+ + | PD12 <-> FSMC_A17 | PE11 <-> FSMC_D8 | PF14 <-> FSMC_A8 | + | PD13 <-> FSMC_A18 | PE12 <-> FSMC_D9 | PF15 <-> FSMC_A9 | + | PD14 <-> FSMC_D0 | PE13 <-> FSMC_D10 |------------------+ + | PD15 <-> FSMC_D1 | PE14 <-> FSMC_D11 | + | | PE15 <-> FSMC_D12 | + +-------------------+--------------------+ +*/ + /* Enable GPIOD, GPIOE, GPIOF and GPIOG interface clock */ + RCC->AHB1ENR = 0x00000078; + + /* Connect PDx pins to FSMC Alternate function */ + GPIOD->AFR[0] = 0x00cc00cc; + GPIOD->AFR[1] = 0xcc0ccccc; + /* Configure PDx pins in Alternate function mode */ + GPIOD->MODER = 0xaaaa0a0a; + /* Configure PDx pins speed to 100 MHz */ + GPIOD->OSPEEDR = 0xffff0f0f; + /* Configure PDx pins Output type to push-pull */ + GPIOD->OTYPER = 0x00000000; + /* No pull-up, pull-down for PDx pins */ + GPIOD->PUPDR = 0x00000000; + + /* Connect PEx pins to FSMC Alternate function */ + GPIOE->AFR[0] = 0xc00cc0cc; + GPIOE->AFR[1] = 0xcccccccc; + /* Configure PEx pins in Alternate function mode */ + GPIOE->MODER = 0xaaaa828a; + /* Configure PEx pins speed to 100 MHz */ + GPIOE->OSPEEDR = 0xffffc3cf; + /* Configure PEx pins Output type to push-pull */ + GPIOE->OTYPER = 0x00000000; + /* No pull-up, pull-down for PEx pins */ + GPIOE->PUPDR = 0x00000000; + + /* Connect PFx pins to FSMC Alternate function */ + GPIOF->AFR[0] = 0x00cccccc; + GPIOF->AFR[1] = 0xcccc0000; + /* Configure PFx pins in Alternate function mode */ + GPIOF->MODER = 0xaa000aaa; + /* Configure PFx pins speed to 100 MHz */ + GPIOF->OSPEEDR = 0xff000fff; + /* Configure PFx pins Output type to push-pull */ + GPIOF->OTYPER = 0x00000000; + /* No pull-up, pull-down for PFx pins */ + GPIOF->PUPDR = 0x00000000; + + /* Connect PGx pins to FSMC Alternate function */ + GPIOG->AFR[0] = 0x00cccccc; + GPIOG->AFR[1] = 0x000000c0; + /* Configure PGx pins in Alternate function mode */ + GPIOG->MODER = 0x00080aaa; + /* Configure PGx pins speed to 100 MHz */ + GPIOG->OSPEEDR = 0x000c0fff; + /* Configure PGx pins Output type to push-pull */ + GPIOG->OTYPER = 0x00000000; + /* No pull-up, pull-down for PGx pins */ + GPIOG->PUPDR = 0x00000000; + +/*-- FSMC Configuration ------------------------------------------------------*/ + /* Enable the FSMC interface clock */ + RCC->AHB3ENR = 0x00000001; + + /* Configure and enable Bank1_SRAM2 */ + FSMC_Bank1->BTCR[2] = 0x00001015; + FSMC_Bank1->BTCR[3] = 0x00010603; + FSMC_Bank1E->BWTR[2] = 0x0fffffff; +/* + Bank1_SRAM2 is configured as follow: + + p.FSMC_AddressSetupTime = 3; + p.FSMC_AddressHoldTime = 0; + p.FSMC_DataSetupTime = 6; + p.FSMC_BusTurnAroundDuration = 1; + p.FSMC_CLKDivision = 0; + p.FSMC_DataLatency = 0; + p.FSMC_AccessMode = FSMC_AccessMode_A; + + FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM2; + FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable; + FSMC_NORSRAMInitStructure.FSMC_MemoryType = FSMC_MemoryType_PSRAM; + FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b; + FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable; + FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait = FSMC_AsynchronousWait_Disable; + FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low; + FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable; + FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState; + FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable; + FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable; + FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable; + FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable; + FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &p; + FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &p; +*/ + +} +#endif /* DATA_IN_ExtSRAM */ + + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ +/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/ diff --git a/examples/test-cmd/Makefile b/examples/test-cmd/Makefile new file mode 100644 index 0000000..6e63c88 --- /dev/null +++ b/examples/test-cmd/Makefile @@ -0,0 +1,129 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + +UCGINE ?= $(abspath ../..) +include $(UCGINE)/mk/ucgine-pre.mk + +O ?= $(CURDIR)/build +PROG = $(O)/test-cmd + +CFLAGS += $(ARCH_CFLAGS) +CFLAGS += -g -Werror -I. + +LDFLAGS += $(ARCH_LDFLAGS) + +# local files +exe-y-$(PROG) := main.c commands.c uart.c + +ifeq ($(UCGINE_ARCH),avr) +MCU = atmega328p +F_CPU = 8000000UL +AVRDUDE_PORT = /dev/ttyUSB0 +AVRDUDE_PROG = arduino +AVRDUDE_BAUD = 57600 +CFLAGS += -DF_CPU=$(F_CPU) +CFLAGS += -I$(UCGINE)/arch/avr/uart/include +CFLAGS += -mmcu=$(MCU) +CFLAGS += -DUCG_CMD_NO_PAGER +LDFLAGS += -mmcu=$(MCU) +# uart +exe-y-$(PROG) += $(UCGINE)/arch/avr/uart/ucg_avr_uart.c +endif +ifeq ($(UCGINE_ARCH),stm32) +STLINK ?= /home/zer0/projects/stm32/stlink +STM_COMMON ?= /home/zer0/projects/stm32/stm32_discovery_arm_gcc/STM32F4-Discovery_FW_V1.1.0 +CFLAGS += -Tstm32_flash.ld +CFLAGS += -I$(STM_COMMON)/Utilities/STM32F4-Discovery +CFLAGS += -I$(STM_COMMON)/Libraries/CMSIS/Include +CFLAGS += -I$(STM_COMMON)/Libraries/CMSIS/ST/STM32F4xx/Include +CFLAGS += -I$(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/inc +CFLAGS += -I$(UCGINE)/lib/gloss/include +CFLAGS += -I$(UCGINE)/arch/stm32/include +CFLAGS += -I$(UCGINE)/arch/stm32/uart/include +LDFLAGS += -Tstm32_flash.ld +exe-y-$(PROG) += system_stm32f4xx.c +# stm32 standard periph lib +exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_exti.c +exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_gpio.c +exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_rcc.c +exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_usart.c +exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/misc.c +# startup file +exe-y-$(PROG) += $(STM_COMMON)/Libraries/CMSIS/ST/STM32F4xx/Source/Templates/TrueSTUDIO/startup_stm32f4xx.s +# gloss +exe-y-$(PROG) += $(UCGINE)/lib/gloss/ucg_gloss_stubs.c +exe-y-$(PROG) += $(UCGINE)/lib/gloss/ucg_gloss_chardev.c +# uart +exe-y-$(PROG) += $(UCGINE)/arch/stm32/uart/ucg_stm32_uart.c +endif +ifeq ($(UCGINE_ARCH),posix) +CFLAGS += -DUCG_CMD_HAVE_SOCKET -DUCG_CMD_HAVE_TERMIOS +endif + +# cirbuf +CFLAGS += -I$(UCGINE)/lib/cirbuf/include +exe-y-$(PROG) += $(UCGINE)/lib/cirbuf/ucg_cirbuf.c +# uart +CFLAGS += -I$(UCGINE)/lib/uart/include +exe-y-$(PROG) += $(UCGINE)/lib/uart/ucg_uart.c +# cmd +CFLAGS += -I$(UCGINE)/lib/cmd/include +exe-y-$(PROG) += $(UCGINE)/lib/cmd/ucg_cmd.c +exe-y-$(PROG) += $(UCGINE)/lib/cmd/ucg_cmd_parse.c +ifeq ($(CROSS),) +exe-y-$(PROG) += $(UCGINE)/lib/cmd/ucg_cmd_parse_etheraddr.c +exe-y-$(PROG) += $(UCGINE)/lib/cmd/ucg_cmd_parse_file.c +exe-y-$(PROG) += $(UCGINE)/lib/cmd/ucg_cmd_parse_ipaddr.c +exe-y-$(PROG) += $(UCGINE)/lib/cmd/ucg_cmd_socket.c +endif +exe-y-$(PROG) += $(UCGINE)/lib/cmd/ucg_cmd_termios.c +exe-y-$(PROG) += $(UCGINE)/lib/cmd/ucg_cmd_parse_num.c +exe-y-$(PROG) += $(UCGINE)/lib/cmd/ucg_cmd_parse_string.c +exe-y-$(PROG) += $(UCGINE)/lib/cmd/ucg_cmd_rdline.c +exe-y-$(PROG) += $(UCGINE)/lib/cmd/ucg_cmd_vt100.c + +objcopy-hex-y-$(PROG).hex := $(PROG) +objcopy-bin-y-$(PROG).bin := $(PROG) + +# XXX where to define the all target? +include $(UCGINE)/mk/ucgine-post.mk + +.PHONY: all +all: $(all-targets) + $(CROSS)size $(PROG) + +.PHONY: clean +clean: _ucgine_clean + +.PHONY: burn +burn: all +ifeq ($(UCGINE_ARCH),stm32) + $(STLINK)/st-flash write $(PROG).bin 0x8000000 +endif +ifeq ($(UCGINE_ARCH),avr) + avrdude -e -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROG) \ + -b $(AVRDUDE_BAUD) -U flash:w:$(PROG):e +endif diff --git a/examples/test-cmd/commands.c b/examples/test-cmd/commands.c new file mode 100644 index 0000000..9b24303 --- /dev/null +++ b/examples/test-cmd/commands.c @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2015, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 + +/**********************************************************/ + +struct cmd_hello_result { + ucg_cmd_fixed_string_t hello; + ucg_cmd_fixed_string_t name; +}; + +static void cmd_hello_parsed(void *parsed_result, + struct ucg_cmd *cl, void *data) +{ + struct cmd_hello_result *res = parsed_result; + + (void)data; + ucg_cmd_printf(cl, "hello %s\n", res->name); +} + +static ucg_cmd_tk_string_t cmd_hello_hello = + UCG_CMD_TK_STRING(struct cmd_hello_result, hello, "hello"); + +static ucg_cmd_tk_string_t cmd_hello_name = + UCG_CMD_TK_STRING(struct cmd_hello_result, name, NULL); + +static ucg_cmd_inst_t cmd_hello = { + .f = cmd_hello_parsed, /* function to call */ + .data = NULL, /* 2nd arg of func */ + .help_str = "Say hello", + .tokens = { /* token list, NULL terminated */ + (void *)&cmd_hello_hello, + (void *)&cmd_hello_name, + NULL, + }, +}; + +/**********************************************************/ + +struct cmd_hello_tutu_result { + ucg_cmd_fixed_string_t hello; + ucg_cmd_fixed_string_t name1; + ucg_cmd_fixed_string_t name2; +}; + +static void cmd_hello_tutu_parsed(void *parsed_result, + struct ucg_cmd *cl, void *data) +{ + struct cmd_hello_tutu_result *res = parsed_result; + + (void)data; + ucg_cmd_printf(cl, "hello %s and %s\n", res->name1, res->name2); +} + +static ucg_cmd_tk_string_t cmd_hello_tutu_hello = + UCG_CMD_TK_STRING(struct cmd_hello_tutu_result, hello, "hello"); + +static ucg_cmd_tk_string_t cmd_hello_tutu_name1 = + UCG_CMD_TK_STRING(struct cmd_hello_tutu_result, name1, "tutu"); + +static ucg_cmd_tk_string_t cmd_hello_tutu_name2 = + UCG_CMD_TK_STRING(struct cmd_hello_tutu_result, name2, NULL); + +static ucg_cmd_inst_t cmd_hello_tutu = { + .f = cmd_hello_tutu_parsed, /* function to call */ + .data = NULL, /* 2nd arg of func */ + .help_str = "Say hello to tutu and someone else", + .tokens = { /* token list, NULL terminated */ + (void *)&cmd_hello_tutu_hello, + (void *)&cmd_hello_tutu_name1, + (void *)&cmd_hello_tutu_name2, + NULL, + }, +}; + +/**********************************************************/ + +struct cmd_hello_toto_result { + ucg_cmd_fixed_string_t hello; + ucg_cmd_fixed_string_t name; + uint16_t count; +}; + +static void cmd_hello_toto_parsed(void *parsed_result, + struct ucg_cmd *cl, void *data) +{ + uint16_t i; + struct cmd_hello_toto_result *res = parsed_result; + + (void)data; + for (i = 0; i < res->count; i++) + ucg_cmd_printf(cl, "hello %s\n", res->name); +} + +static ucg_cmd_tk_string_t cmd_hello_toto_hello = + UCG_CMD_TK_STRING(struct cmd_hello_toto_result, hello, "hello"); + +static ucg_cmd_tk_string_t cmd_hello_toto_name = + UCG_CMD_TK_STRING(struct cmd_hello_toto_result, name, "toto#titi"); + +static ucg_cmd_tk_num_t cmd_hello_toto_count = + UCG_CMD_TK_NUM(struct cmd_hello_toto_result, count, UINT16); + +static ucg_cmd_inst_t cmd_hello_toto = { + .f = cmd_hello_toto_parsed, /* function to call */ + .data = NULL, /* 2nd arg of func */ + .help_str = "Say hello to toto or titi several times", + .tokens = { /* token list, NULL terminated */ + (void *)&cmd_hello_toto_hello, + (void *)&cmd_hello_toto_name, + (void *)&cmd_hello_toto_count, + NULL, + }, +}; + +/****** CONTEXT (list of instruction) */ + +ucg_cmd_ctx_t main_ctx = { + .name = "main", + .insts = { + &cmd_hello, + &cmd_hello_tutu, + &cmd_hello_toto, + NULL, + }, +}; diff --git a/examples/test-cmd/commands.h b/examples/test-cmd/commands.h new file mode 100644 index 0000000..f80c5f6 --- /dev/null +++ b/examples/test-cmd/commands.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2015, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +#ifndef COMMANDS_H_ +#define COMMANDS_H_ + +#include + +extern ucg_cmd_ctx_t main_ctx; + +#endif /* COMMANDS_H_ */ diff --git a/examples/test-cmd/main.c b/examples/test-cmd/main.c new file mode 100644 index 0000000..a7feaf6 --- /dev/null +++ b/examples/test-cmd/main.c @@ -0,0 +1,133 @@ +/* + * Copyright 2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 "commands.h" + +#include "uart.h" + +#if defined(__ARM_EABI__) +#include + +static void led_on(void) +{ + GPIOD->ODR |= (1 << 13); +} + +static void led_off(void) +{ + GPIOD->ODR &= (~(1 << 13)); +} + +static void target_init(void) +{ + /* enable the clock to GPIOD, and stall instruction pipeline as per + * errata 2.1.13 "Delay after an RCC peripheral clock enabling" */ + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); + __asm("dsb"); + + /* set pin 13 to be general purpose output */ + GPIOD->MODER = (1 << 26); +} +#elif defined(__AVR__) +#include + +static void led_on(void) +{ + PORTB |= (1 << 5); +} + +static void led_off(void) +{ + PORTB &= (~(1 << 5)); +} + +static void target_init(void) +{ + DDRB = (1 << 5); +} +#else +static void led_on(void) +{ + printf("led on\n"); +} + +static void led_off(void) +{ + printf("led off\n"); +} + +static void target_init(void) +{ +} +#endif + +int main(void) +{ + unsigned i; + struct ucg_cmd cl; + const char *prompt = "\033[32mtest> \033[0m"; + unsigned cl_flags = 0; + + target_init(); + + /* toggle the pin */ + for (i = 0; i < 3; i++) { + led_on(); + ucg_delay_ms(500); + led_off(); + ucg_delay_ms(500); + } + + uart_init(); + ucg_irq_unlock(); + printf("hello\n"); + ucg_delay_ms(500); + +#if defined( __AVR__) || defined(__ARM_EABI__) + cl_flags = UCG_CMD_F_IGNORE_EOF; +#endif + ucg_cmd_init(&cl, &main_ctx, prompt, stdin, stdout); + if (ucg_cmd_termios_raw(&cl) < 0) { + printf("cannot set termios in raw mode\n"); + return 1; + } + + ucg_cmd_interact(&cl, cl_flags); + ucg_cmd_termios_restore(&cl); + + return 0; +} diff --git a/examples/test-cmd/stm32_flash.ld b/examples/test-cmd/stm32_flash.ld new file mode 100755 index 0000000..350c05b --- /dev/null +++ b/examples/test-cmd/stm32_flash.ld @@ -0,0 +1,172 @@ +/* +***************************************************************************** +** +** File : stm32_flash.ld +** +** Abstract : Linker script for STM32F407VG Device with +** 1024KByte FLASH, 192KByte RAM +** +** Set heap size, stack size and stack location according +** to application requirements. +** +** Set memory bank area and size if external memory is used. +** +** Target : STMicroelectronics STM32 +** +** Environment : Atollic TrueSTUDIO(R) +** +** Distribution: The file is distributed “as is,” without any warranty +** of any kind. +** +** (c)Copyright Atollic AB. +** You may use this file as-is or modify it according to the needs of your +** project. Distribution of this file (unmodified or modified) is not +** permitted. Atollic AB permit registered Atollic TrueSTUDIO(R) users the +** rights to distribute the assembled, compiled & linked contents of this +** file as part of an application binary file, provided that it is built +** using the Atollic TrueSTUDIO(R) toolchain. +** +***************************************************************************** +*/ + +/* Entry Point */ +ENTRY(Reset_Handler) + +/* Highest address of the user mode stack */ +_estack = 0x20020000; /* end of 128K RAM on AHB bus*/ + +/* Generate a link error if heap and stack don't fit into RAM */ +_Min_Heap_Size = 0; /* required amount of heap */ +_Min_Stack_Size = 0x400; /* required amount of stack */ + +/* Specify the memory areas */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K + MEMORY_B1 (rx) : ORIGIN = 0x60000000, LENGTH = 0K +} + +/* Define output sections */ +SECTIONS +{ + /* The startup code goes first into FLASH */ + .isr_vector : + { + . = ALIGN(4); + KEEP(*(.isr_vector)) /* Startup code */ + . = ALIGN(4); + } >FLASH + + /* The program code and other data goes into FLASH */ + .text : + { + . = ALIGN(4); + *(.text) /* .text sections (code) */ + *(.text*) /* .text* sections (code) */ + *(.rodata) /* .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + *(.glue_7) /* glue arm to thumb code */ + *(.glue_7t) /* glue thumb to arm code */ + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + . = ALIGN(4); + _etext = .; /* define a global symbols at end of code */ + _exit = .; + } >FLASH + + + .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH + .ARM : { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } >FLASH + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array*)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } >FLASH + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array*)) + PROVIDE_HIDDEN (__init_array_end = .); + } >FLASH + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(.fini_array*)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE_HIDDEN (__fini_array_end = .); + } >FLASH + + /* used by the startup to initialize data */ + _sidata = .; + + /* Initialized data sections goes into RAM, load LMA copy after code */ + .data : AT ( _sidata ) + { + . = ALIGN(4); + _sdata = .; /* create a global symbol at data start */ + *(.data) /* .data sections */ + *(.data*) /* .data* sections */ + + . = ALIGN(4); + _edata = .; /* define a global symbol at data end */ + } >RAM + + /* Uninitialized data section */ + . = ALIGN(4); + .bss : + { + /* This is used by the startup in order to initialize the .bss secion */ + _sbss = .; /* define a global symbol at bss start */ + __bss_start__ = _sbss; + *(.bss) + *(.bss*) + *(COMMON) + + . = ALIGN(4); + _ebss = .; /* define a global symbol at bss end */ + __bss_end__ = _ebss; + } >RAM + + /* User_heap_stack section, used to check that there is enough RAM left */ + ._user_heap_stack : + { + . = ALIGN(4); + PROVIDE ( end = . ); + PROVIDE ( _end = . ); + PROVIDE ( __end__ = . ); + . = . + _Min_Heap_Size; + . = . + _Min_Stack_Size; + . = ALIGN(4); + } >RAM + + /* MEMORY_bank1 section, code must be located here explicitly */ + /* Example: extern int foo(void) __attribute__ ((section (".mb1text"))); */ + .memory_b1_text : + { + *(.mb1text) /* .mb1text sections (code) */ + *(.mb1text*) /* .mb1text* sections (code) */ + *(.mb1rodata) /* read-only data (constants) */ + *(.mb1rodata*) + } >MEMORY_B1 + + /* Remove information from the standard libraries */ + /DISCARD/ : + { + libc.a ( * ) + libm.a ( * ) + libgcc.a ( * ) + } + + .ARM.attributes 0 : { *(.ARM.attributes) } +} diff --git a/examples/test-cmd/stm32f4xx_conf.h b/examples/test-cmd/stm32f4xx_conf.h new file mode 100644 index 0000000..74447a8 --- /dev/null +++ b/examples/test-cmd/stm32f4xx_conf.h @@ -0,0 +1,94 @@ +/** + ****************************************************************************** + * @file stm32f4xx_conf.h + * @author MCD Application Team + * @version V1.0.0 + * @date 19-September-2011 + * @brief Library configuration file. + ****************************************************************************** + * @attention + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2011 STMicroelectronics

+ ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F4xx_CONF_H +#define __STM32F4xx_CONF_H + +#if defined (HSE_VALUE) +/* Redefine the HSE value; it's equal to 8 MHz on the STM32F4-DISCOVERY Kit */ + #undef HSE_VALUE + #define HSE_VALUE ((uint32_t)8000000) +#endif /* HSE_VALUE */ + +/* Includes ------------------------------------------------------------------*/ +/* Uncomment the line below to enable peripheral header file inclusion */ +#include "stm32f4xx_adc.h" +#include "stm32f4xx_can.h" +#include "stm32f4xx_crc.h" +#include "stm32f4xx_cryp.h" +#include "stm32f4xx_dac.h" +#include "stm32f4xx_dbgmcu.h" +#include "stm32f4xx_dcmi.h" +#include "stm32f4xx_dma.h" +#include "stm32f4xx_exti.h" +#include "stm32f4xx_flash.h" +#include "stm32f4xx_fsmc.h" +#include "stm32f4xx_hash.h" +#include "stm32f4xx_gpio.h" +#include "stm32f4xx_i2c.h" +#include "stm32f4xx_iwdg.h" +#include "stm32f4xx_pwr.h" +#include "stm32f4xx_rcc.h" +#include "stm32f4xx_rng.h" +#include "stm32f4xx_rtc.h" +#include "stm32f4xx_sdio.h" +#include "stm32f4xx_spi.h" +#include "stm32f4xx_syscfg.h" +#include "stm32f4xx_tim.h" +#include "stm32f4xx_usart.h" +#include "stm32f4xx_wwdg.h" +#include "misc.h" /* High level functions for NVIC and SysTick (add-on to CMSIS functions) */ + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ + +/* If an external clock source is used, then the value of the following define + should be set to the value of the external clock source, else, if no external + clock is used, keep this define commented */ +/*#define I2S_EXTERNAL_CLOCK_VAL 12288000 */ /* Value of the external clock in Hz */ + + +/* Uncomment the line below to expanse the "assert_param" macro in the + Standard Peripheral Library drivers code */ +/* #define USE_FULL_ASSERT 1 */ + +/* Exported macro ------------------------------------------------------------*/ +#ifdef USE_FULL_ASSERT + +/** + * @brief The assert_param macro is used for function's parameters check. + * @param expr: If expr is false, it calls assert_failed function + * which reports the name of the source file and the source + * line number of the call that failed. + * If expr is true, it returns no value. + * @retval None + */ + #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__)) +/* Exported functions ------------------------------------------------------- */ + void assert_failed(uint8_t* file, uint32_t line); +#else + #define assert_param(expr) ((void)0) +#endif /* USE_FULL_ASSERT */ + +#endif /* __STM32F4xx_CONF_H */ + +/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/ diff --git a/examples/test-cmd/system_stm32f4xx.c b/examples/test-cmd/system_stm32f4xx.c new file mode 100644 index 0000000..98e8e04 --- /dev/null +++ b/examples/test-cmd/system_stm32f4xx.c @@ -0,0 +1,553 @@ +/** + ****************************************************************************** + * @file system_stm32f4xx.c + * @author MCD Application Team + * @version V1.0.0 + * @date 30-September-2011 + * @brief CMSIS Cortex-M4 Device Peripheral Access Layer System Source File. + * This file contains the system clock configuration for STM32F4xx devices, + * and is generated by the clock configuration tool + * stm32f4xx_Clock_Configuration_V1.0.0.xls + * + * 1. This file provides two functions and one global variable to be called from + * user application: + * - SystemInit(): Setups the system clock (System clock source, PLL Multiplier + * and Divider factors, AHB/APBx prescalers and Flash settings), + * depending on the configuration made in the clock xls tool. + * This function is called at startup just after reset and + * before branch to main program. This call is made inside + * the "startup_stm32f4xx.s" file. + * + * - SystemCoreClock variable: Contains the core clock (HCLK), it can be used + * by the user application to setup the SysTick + * timer or configure other parameters. + * + * - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must + * be called whenever the core clock is changed + * during program execution. + * + * 2. After each device reset the HSI (16 MHz) is used as system clock source. + * Then SystemInit() function is called, in "startup_stm32f4xx.s" file, to + * configure the system clock before to branch to main program. + * + * 3. If the system clock source selected by user fails to startup, the SystemInit() + * function will do nothing and HSI still used as system clock source. User can + * add some code to deal with this issue inside the SetSysClock() function. + * + * 4. The default value of HSE crystal is set to 25MHz, refer to "HSE_VALUE" define + * in "stm32f4xx.h" file. When HSE is used as system clock source, directly or + * through PLL, and you are using different crystal you have to adapt the HSE + * value to your own configuration. + * + * 5. This file configures the system clock as follows: + *============================================================================= + *============================================================================= + * Supported STM32F4xx device revision | Rev A + *----------------------------------------------------------------------------- + * System Clock source | PLL (HSE) + *----------------------------------------------------------------------------- + * SYSCLK(Hz) | 168000000 + *----------------------------------------------------------------------------- + * HCLK(Hz) | 168000000 + *----------------------------------------------------------------------------- + * AHB Prescaler | 1 + *----------------------------------------------------------------------------- + * APB1 Prescaler | 4 + *----------------------------------------------------------------------------- + * APB2 Prescaler | 2 + *----------------------------------------------------------------------------- + * HSE Frequency(Hz) | 25000000 + *----------------------------------------------------------------------------- + * PLL_M | 25 + *----------------------------------------------------------------------------- + * PLL_N | 336 + *----------------------------------------------------------------------------- + * PLL_P | 2 + *----------------------------------------------------------------------------- + * PLL_Q | 7 + *----------------------------------------------------------------------------- + * PLLI2S_N | NA + *----------------------------------------------------------------------------- + * PLLI2S_R | NA + *----------------------------------------------------------------------------- + * I2S input clock | NA + *----------------------------------------------------------------------------- + * VDD(V) | 3.3 + *----------------------------------------------------------------------------- + * Main regulator output voltage | Scale1 mode + *----------------------------------------------------------------------------- + * Flash Latency(WS) | 5 + *----------------------------------------------------------------------------- + * Prefetch Buffer | OFF + *----------------------------------------------------------------------------- + * Instruction cache | ON + *----------------------------------------------------------------------------- + * Data cache | ON + *----------------------------------------------------------------------------- + * Require 48MHz for USB OTG FS, | Enabled + * SDIO and RNG clock | + *----------------------------------------------------------------------------- + *============================================================================= + ****************************************************************************** + * @attention + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2011 STMicroelectronics

+ ****************************************************************************** + */ + +/** @addtogroup CMSIS + * @{ + */ + +/** @addtogroup stm32f4xx_system + * @{ + */ + +/** @addtogroup STM32F4xx_System_Private_Includes + * @{ + */ + +#include "stm32f4xx.h" + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Private_TypesDefinitions + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Private_Defines + * @{ + */ + +/************************* Miscellaneous Configuration ************************/ +/*!< Uncomment the following line if you need to use external SRAM mounted + on STM324xG_EVAL board as data memory */ +/* #define DATA_IN_ExtSRAM */ + +/*!< Uncomment the following line if you need to relocate your vector Table in + Internal SRAM. */ +/* #define VECT_TAB_SRAM */ +#define VECT_TAB_OFFSET 0x00 /*!< Vector Table base offset field. + This value must be a multiple of 0x200. */ +/******************************************************************************/ + +/************************* PLL Parameters *************************************/ +/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */ +#define PLL_M 25 +#define PLL_N 336 + +/* SYSCLK = PLL_VCO / PLL_P */ +#define PLL_P 2 + +/* USB OTG FS, SDIO and RNG Clock = PLL_VCO / PLLQ */ +#define PLL_Q 7 + +/******************************************************************************/ + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Private_Variables + * @{ + */ + + uint32_t SystemCoreClock = 168000000; + + __I uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9}; + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Private_FunctionPrototypes + * @{ + */ + +static void SetSysClock(void); +#ifdef DATA_IN_ExtSRAM + static void SystemInit_ExtMemCtl(void); +#endif /* DATA_IN_ExtSRAM */ + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Private_Functions + * @{ + */ + +/** + * @brief Setup the microcontroller system + * Initialize the Embedded Flash Interface, the PLL and update the + * SystemFrequency variable. + * @param None + * @retval None + */ +void SystemInit(void) +{ + /* FPU settings ------------------------------------------------------------*/ + #if (__FPU_PRESENT == 1) && (__FPU_USED == 1) + SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */ + #endif + + /* Reset the RCC clock configuration to the default reset state ------------*/ + /* Set HSION bit */ + RCC->CR |= (uint32_t)0x00000001; + + /* Reset CFGR register */ + RCC->CFGR = 0x00000000; + + /* Reset HSEON, CSSON and PLLON bits */ + RCC->CR &= (uint32_t)0xFEF6FFFF; + + /* Reset PLLCFGR register */ + RCC->PLLCFGR = 0x24003010; + + /* Reset HSEBYP bit */ + RCC->CR &= (uint32_t)0xFFFBFFFF; + + /* Disable all interrupts */ + RCC->CIR = 0x00000000; + +#ifdef DATA_IN_ExtSRAM + SystemInit_ExtMemCtl(); +#endif /* DATA_IN_ExtSRAM */ + + /* Configure the System clock source, PLL Multiplier and Divider factors, + AHB/APBx prescalers and Flash settings ----------------------------------*/ + SetSysClock(); + + /* Configure the Vector Table location add offset address ------------------*/ +#ifdef VECT_TAB_SRAM + SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */ +#else + SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */ +#endif +} + +/** + * @brief Update SystemCoreClock variable according to Clock Register Values. + * The SystemCoreClock variable contains the core clock (HCLK), it can + * be used by the user application to setup the SysTick timer or configure + * other parameters. + * + * @note Each time the core clock (HCLK) changes, this function must be called + * to update SystemCoreClock variable value. Otherwise, any configuration + * based on this variable will be incorrect. + * + * @note - The system frequency computed by this function is not the real + * frequency in the chip. It is calculated based on the predefined + * constant and the selected clock source: + * + * - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*) + * + * - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**) + * + * - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**) + * or HSI_VALUE(*) multiplied/divided by the PLL factors. + * + * (*) HSI_VALUE is a constant defined in stm32f4xx.h file (default value + * 16 MHz) but the real value may vary depending on the variations + * in voltage and temperature. + * + * (**) HSE_VALUE is a constant defined in stm32f4xx.h file (default value + * 25 MHz), user has to ensure that HSE_VALUE is same as the real + * frequency of the crystal used. Otherwise, this function may + * have wrong result. + * + * - The result of this function could be not correct when using fractional + * value for HSE crystal. + * + * @param None + * @retval None + */ +void SystemCoreClockUpdate(void) +{ + uint32_t tmp = 0, pllvco = 0, pllp = 2, pllsource = 0, pllm = 2; + + /* Get SYSCLK source -------------------------------------------------------*/ + tmp = RCC->CFGR & RCC_CFGR_SWS; + + switch (tmp) + { + case 0x00: /* HSI used as system clock source */ + SystemCoreClock = HSI_VALUE; + break; + case 0x04: /* HSE used as system clock source */ + SystemCoreClock = HSE_VALUE; + break; + case 0x08: /* PLL used as system clock source */ + + /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N + SYSCLK = PLL_VCO / PLL_P + */ + pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) >> 22; + pllm = RCC->PLLCFGR & RCC_PLLCFGR_PLLM; + + if (pllsource != 0) + { + /* HSE used as PLL clock source */ + pllvco = (HSE_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6); + } + else + { + /* HSI used as PLL clock source */ + pllvco = (HSI_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6); + } + + pllp = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLP) >>16) + 1 ) *2; + SystemCoreClock = pllvco/pllp; + break; + default: + SystemCoreClock = HSI_VALUE; + break; + } + /* Compute HCLK frequency --------------------------------------------------*/ + /* Get HCLK prescaler */ + tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)]; + /* HCLK frequency */ + SystemCoreClock >>= tmp; +} + +/** + * @brief Configures the System clock source, PLL Multiplier and Divider factors, + * AHB/APBx prescalers and Flash settings + * @Note This function should be called only once the RCC clock configuration + * is reset to the default reset state (done in SystemInit() function). + * @param None + * @retval None + */ +static void SetSysClock(void) +{ +/******************************************************************************/ +/* PLL (clocked by HSE) used as System clock source */ +/******************************************************************************/ + __IO uint32_t StartUpCounter = 0, HSEStatus = 0; + + /* Enable HSE */ + RCC->CR |= ((uint32_t)RCC_CR_HSEON); + + /* Wait till HSE is ready and if Time out is reached exit */ + do + { + HSEStatus = RCC->CR & RCC_CR_HSERDY; + StartUpCounter++; + } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); + + if ((RCC->CR & RCC_CR_HSERDY) != RESET) + { + HSEStatus = (uint32_t)0x01; + } + else + { + HSEStatus = (uint32_t)0x00; + } + + if (HSEStatus == (uint32_t)0x01) + { + /* Select regulator voltage output Scale 1 mode, System frequency up to 168 MHz */ + RCC->APB1ENR |= RCC_APB1ENR_PWREN; + PWR->CR |= PWR_CR_VOS; + + /* HCLK = SYSCLK / 1*/ + RCC->CFGR |= RCC_CFGR_HPRE_DIV1; + + /* PCLK2 = HCLK / 2*/ + RCC->CFGR |= RCC_CFGR_PPRE2_DIV2; + + /* PCLK1 = HCLK / 4*/ + RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; + + /* Configure the main PLL */ + RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) | + (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24); + + /* Enable the main PLL */ + RCC->CR |= RCC_CR_PLLON; + + /* Wait till the main PLL is ready */ + while((RCC->CR & RCC_CR_PLLRDY) == 0) + { + } + + /* Configure Flash prefetch, Instruction cache, Data cache and wait state */ + FLASH->ACR = FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS; + + /* Select the main PLL as system clock source */ + RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); + RCC->CFGR |= RCC_CFGR_SW_PLL; + + /* Wait till the main PLL is used as system clock source */ + while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL); + { + } + } + else + { /* If HSE fails to start-up, the application will have wrong clock + configuration. User can add here some code to deal with this error */ + } + +} + +/** + * @brief Setup the external memory controller. Called in startup_stm32f4xx.s + * before jump to __main + * @param None + * @retval None + */ +#ifdef DATA_IN_ExtSRAM +/** + * @brief Setup the external memory controller. + * Called in startup_stm32f4xx.s before jump to main. + * This function configures the external SRAM mounted on STM324xG_EVAL board + * This SRAM will be used as program data memory (including heap and stack). + * @param None + * @retval None + */ +void SystemInit_ExtMemCtl(void) +{ +/*-- GPIOs Configuration -----------------------------------------------------*/ +/* + +-------------------+--------------------+------------------+------------------+ + + SRAM pins assignment + + +-------------------+--------------------+------------------+------------------+ + | PD0 <-> FSMC_D2 | PE0 <-> FSMC_NBL0 | PF0 <-> FSMC_A0 | PG0 <-> FSMC_A10 | + | PD1 <-> FSMC_D3 | PE1 <-> FSMC_NBL1 | PF1 <-> FSMC_A1 | PG1 <-> FSMC_A11 | + | PD4 <-> FSMC_NOE | PE3 <-> FSMC_A19 | PF2 <-> FSMC_A2 | PG2 <-> FSMC_A12 | + | PD5 <-> FSMC_NWE | PE4 <-> FSMC_A20 | PF3 <-> FSMC_A3 | PG3 <-> FSMC_A13 | + | PD8 <-> FSMC_D13 | PE7 <-> FSMC_D4 | PF4 <-> FSMC_A4 | PG4 <-> FSMC_A14 | + | PD9 <-> FSMC_D14 | PE8 <-> FSMC_D5 | PF5 <-> FSMC_A5 | PG5 <-> FSMC_A15 | + | PD10 <-> FSMC_D15 | PE9 <-> FSMC_D6 | PF12 <-> FSMC_A6 | PG9 <-> FSMC_NE2 | + | PD11 <-> FSMC_A16 | PE10 <-> FSMC_D7 | PF13 <-> FSMC_A7 |------------------+ + | PD12 <-> FSMC_A17 | PE11 <-> FSMC_D8 | PF14 <-> FSMC_A8 | + | PD13 <-> FSMC_A18 | PE12 <-> FSMC_D9 | PF15 <-> FSMC_A9 | + | PD14 <-> FSMC_D0 | PE13 <-> FSMC_D10 |------------------+ + | PD15 <-> FSMC_D1 | PE14 <-> FSMC_D11 | + | | PE15 <-> FSMC_D12 | + +-------------------+--------------------+ +*/ + /* Enable GPIOD, GPIOE, GPIOF and GPIOG interface clock */ + RCC->AHB1ENR = 0x00000078; + + /* Connect PDx pins to FSMC Alternate function */ + GPIOD->AFR[0] = 0x00cc00cc; + GPIOD->AFR[1] = 0xcc0ccccc; + /* Configure PDx pins in Alternate function mode */ + GPIOD->MODER = 0xaaaa0a0a; + /* Configure PDx pins speed to 100 MHz */ + GPIOD->OSPEEDR = 0xffff0f0f; + /* Configure PDx pins Output type to push-pull */ + GPIOD->OTYPER = 0x00000000; + /* No pull-up, pull-down for PDx pins */ + GPIOD->PUPDR = 0x00000000; + + /* Connect PEx pins to FSMC Alternate function */ + GPIOE->AFR[0] = 0xc00cc0cc; + GPIOE->AFR[1] = 0xcccccccc; + /* Configure PEx pins in Alternate function mode */ + GPIOE->MODER = 0xaaaa828a; + /* Configure PEx pins speed to 100 MHz */ + GPIOE->OSPEEDR = 0xffffc3cf; + /* Configure PEx pins Output type to push-pull */ + GPIOE->OTYPER = 0x00000000; + /* No pull-up, pull-down for PEx pins */ + GPIOE->PUPDR = 0x00000000; + + /* Connect PFx pins to FSMC Alternate function */ + GPIOF->AFR[0] = 0x00cccccc; + GPIOF->AFR[1] = 0xcccc0000; + /* Configure PFx pins in Alternate function mode */ + GPIOF->MODER = 0xaa000aaa; + /* Configure PFx pins speed to 100 MHz */ + GPIOF->OSPEEDR = 0xff000fff; + /* Configure PFx pins Output type to push-pull */ + GPIOF->OTYPER = 0x00000000; + /* No pull-up, pull-down for PFx pins */ + GPIOF->PUPDR = 0x00000000; + + /* Connect PGx pins to FSMC Alternate function */ + GPIOG->AFR[0] = 0x00cccccc; + GPIOG->AFR[1] = 0x000000c0; + /* Configure PGx pins in Alternate function mode */ + GPIOG->MODER = 0x00080aaa; + /* Configure PGx pins speed to 100 MHz */ + GPIOG->OSPEEDR = 0x000c0fff; + /* Configure PGx pins Output type to push-pull */ + GPIOG->OTYPER = 0x00000000; + /* No pull-up, pull-down for PGx pins */ + GPIOG->PUPDR = 0x00000000; + +/*-- FSMC Configuration ------------------------------------------------------*/ + /* Enable the FSMC interface clock */ + RCC->AHB3ENR = 0x00000001; + + /* Configure and enable Bank1_SRAM2 */ + FSMC_Bank1->BTCR[2] = 0x00001015; + FSMC_Bank1->BTCR[3] = 0x00010603; + FSMC_Bank1E->BWTR[2] = 0x0fffffff; +/* + Bank1_SRAM2 is configured as follow: + + p.FSMC_AddressSetupTime = 3; + p.FSMC_AddressHoldTime = 0; + p.FSMC_DataSetupTime = 6; + p.FSMC_BusTurnAroundDuration = 1; + p.FSMC_CLKDivision = 0; + p.FSMC_DataLatency = 0; + p.FSMC_AccessMode = FSMC_AccessMode_A; + + FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM2; + FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable; + FSMC_NORSRAMInitStructure.FSMC_MemoryType = FSMC_MemoryType_PSRAM; + FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b; + FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable; + FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait = FSMC_AsynchronousWait_Disable; + FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low; + FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable; + FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState; + FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable; + FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable; + FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable; + FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable; + FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &p; + FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &p; +*/ + +} +#endif /* DATA_IN_ExtSRAM */ + + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ +/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/ diff --git a/examples/test-cmd/uart.c b/examples/test-cmd/uart.c new file mode 100644 index 0000000..5500782 --- /dev/null +++ b/examples/test-cmd/uart.c @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2015, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +#if defined(__ARM_EABI__) +#include "stm32f4xx.h" +#elif defined(__AVR__) +#include +#include +#include +#include +#endif + +#include +#include +#include + +#include +#include +#include + +#include "uart.h" + +#if defined(__ARM_EABI__) || defined(__AVR__) +/* rx & tx buffers */ +static char rx_buf[64]; +static struct ucg_cirbuf rx_cirbuf; +static char tx_buf[64]; +static struct ucg_cirbuf tx_cirbuf; +/* generic uart struct */ +struct ucg_uart main_uart; +#endif + +#if defined(__ARM_EABI__) + +#include +#include + +static struct ucg_stm32_uart stm32_uart_data = { + .uart = USART2, + .rcc_uart = RCC_APB1Periph_USART2, + .rcc_gpio = RCC_AHB1Periph_GPIOA, + .gpio = GPIOA, + .gpio_af = GPIO_AF_USART2, + .gpio_pins = GPIO_Pin_2 | GPIO_Pin_3, + .gpio_speed = GPIO_Speed_25MHz, + .irq = USART2_IRQn, + .irq_preempt_prio = 2, + .irq_sub_prio = 0, +}; + +static _ssize_t +uart_write_r(__attribute__((unused)) struct _reent *r, + __attribute__((unused)) int fd, + const void *ptr, size_t len) +{ + size_t i; + const char *buf = ptr; + + for (i = 0; i < len; i++) + ucg_uart_send(&main_uart, buf[i], WAIT); + + return len; +} + +static _ssize_t +uart_read_r(__attribute__((unused)) struct _reent *r, + __attribute__((unused)) int fd, + void *ptr, size_t len) +{ + int c; + char *buf = ptr; + + (void)len; + c = ucg_uart_recv(&main_uart, NOWAIT); + if (c < 0) { + r->_errno = EAGAIN; + return 0; + } + buf[0] = c; + return 1; +} + +void USART2_IRQHandler(void) +{ + if ((USART2->SR & USART_FLAG_TXE)) + ucg_uart_tx_intr(&main_uart); + if ((USART2->SR & USART_FLAG_RXNE)) + ucg_uart_rx_intr(&main_uart); +} + +struct ucg_chardev uart_stdin_dev = { + .name = "stdin", + .open_r = NULL, + .close_r = NULL, + .read_r = uart_read_r, + .write_r = NULL, +}; + +struct ucg_chardev uart_stdout_dev = { + .name = "stdout", + .open_r = NULL, + .close_r = NULL, + .read_r = NULL, + .write_r = uart_write_r, +}; + +struct ucg_chardev uart_stderr_dev = { + .name = "stderr", + .open_r = NULL, + .close_r = NULL, + .read_r = NULL, + .write_r = uart_write_r, +}; + +static void uart_register_stdio(void) +{ + ucg_chardev_register(&uart_stdin_dev); + ucg_chardev_register(&uart_stdout_dev); + ucg_chardev_register(&uart_stderr_dev); + + /* Disable buffering */ + setbuf(stdin, NULL); + setbuf(stdout, NULL); + setbuf(stderr, NULL); +} + +#elif defined(__AVR__) + +#include +/* avr-specific uart struct */ +static struct ucg_avr_uart avr_uart_data = { + .reg_udr = &UDR0, + .reg_ucsra = &UCSR0A, + .reg_ucsrb = &UCSR0B, + .reg_ucsrc = &UCSR0C, + .reg_ubrrl = &UBRR0L, + .reg_ubrrh = &UBRR0H, + .bit_udre = UDRE0, + .bit_rxc = RXC0, + .bit_udrie = UDRIE0, + .bit_rxen = RXEN0, + .bit_txen = TXEN0, + .bit_rxcie = RXCIE0, + .bit_u2x = U2X0, +}; + +/* send on stdout */ +static int std_send(char c, FILE *f) +{ + (void)f; + ucg_uart_send(&main_uart, c, WAIT); + return 0; +} + +/* recv on stdin */ +static int std_recv(FILE *f) +{ + int16_t c; + + (void)f; + c = ucg_uart_recv(&main_uart, NOWAIT); + if (c < 0) + return _FDEV_EOF; + + return c; +} + +SIGNAL(USART_RX_vect) +{ + ucg_uart_rx_intr(&main_uart); +} + +SIGNAL(USART_UDRE_vect) +{ + ucg_uart_tx_intr(&main_uart); +} +#endif + +int uart_init(void) +{ + int ret = 0; +#if defined(__ARM_EABI__) || defined(__AVR__) + struct ucg_uart_config conf; + const void *uart_ops; + void *uart_data; + +#if defined(__ARM_EABI__) + uart_ops = &stm32_uart_ops; + uart_data = &stm32_uart_data; +#elif defined(__AVR__) + uart_ops = &avr_uart_ops; + uart_data = &avr_uart_data; +#endif + + ret = ucg_uart_init(&main_uart, uart_ops, uart_data, + &rx_cirbuf, rx_buf, sizeof(rx_buf), + &tx_cirbuf, tx_buf, sizeof(tx_buf)); + if (ret < 0) + return ret; + + ucg_uart_getconf(&main_uart, &conf); + conf.baudrate = 57600; + ret = ucg_uart_setconf(&main_uart, &conf); + if (ret < 0) + return ret; + +#if defined(__ARM_EABI__) + uart_register_stdio(); +#elif defined(__AVR__) + fdevopen(std_send, std_recv); +#endif +#endif + return ret; +} diff --git a/examples/test-cmd/uart.h b/examples/test-cmd/uart.h new file mode 100644 index 0000000..ca1a139 --- /dev/null +++ b/examples/test-cmd/uart.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2015, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +#ifndef UART_H_ +#define UART_H_ + +#include + +int uart_init(void); + +#endif diff --git a/examples/test-mk/Makefile b/examples/test-mk/Makefile new file mode 100644 index 0000000..f278945 --- /dev/null +++ b/examples/test-mk/Makefile @@ -0,0 +1,58 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + +UCGINE ?= $(abspath ../..) + +# output path with trailing slash +O ?= build/ + +CFLAGS = -g -O2 -Wall +CFLAGS += -Idir + +cflags-$(O)titi.o := -DTEST + +obj-y-$(O)target.o := dir/titi.c dir/toto.c + +obj-y-$(O)obj.o := $(O)target.o + +ar-y-$(O)toto.a := $(O)obj.o +exe-y-$(O)prog := main.c $(O)obj.o + +copy-y-$(O)install/ := $(O)toto.a $(O)prog +copy-y-$(O)install/2/toto.a := $(O)toto.a +slink-y-$(O)install/ := dir/toto.h + +subdir-y := test1 test2 +TOTO ?= 1 +mkflags-test1 := TOTO=$(TOTO) + +include $(UCGINE)/mk/ucgine.mk + +all: _ucgine_all + +clean: _ucgine_clean + +.PHONY: clean all diff --git a/examples/test-mk/dir/titi.c b/examples/test-mk/dir/titi.c new file mode 100644 index 0000000..05cc3a4 --- /dev/null +++ b/examples/test-mk/dir/titi.c @@ -0,0 +1,4 @@ +int titi(void) +{ + return 0; +} diff --git a/examples/test-mk/dir/toto.c b/examples/test-mk/dir/toto.c new file mode 100644 index 0000000..8b533ec --- /dev/null +++ b/examples/test-mk/dir/toto.c @@ -0,0 +1,4 @@ +int toto(void) +{ + return 0; +} diff --git a/examples/test-mk/dir/toto.h b/examples/test-mk/dir/toto.h new file mode 100644 index 0000000..466fbd2 --- /dev/null +++ b/examples/test-mk/dir/toto.h @@ -0,0 +1 @@ +int toto(void); diff --git a/examples/test-mk/main.c b/examples/test-mk/main.c new file mode 100644 index 0000000..31dbf45 --- /dev/null +++ b/examples/test-mk/main.c @@ -0,0 +1,4 @@ +int main(void) +{ + return 0; +} diff --git a/examples/test-mk/test1/Makefile b/examples/test-mk/test1/Makefile new file mode 100644 index 0000000..7386ae6 --- /dev/null +++ b/examples/test-mk/test1/Makefile @@ -0,0 +1,43 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + +UCGINE ?= $(abspath ../../..) + +# output path with trailing slash +O = build/ + +CFLAGS = -g -O2 -Wall -DTOTO=$(TOTO) +CFLAGS += -Idir + +exe-y-$(O)prog := main.c + +include $(UCGINE)/mk/ucgine.mk + +all: _ucgine_all + +clean: _ucgine_clean + +.PHONY: clean all diff --git a/examples/test-mk/test1/main.c b/examples/test-mk/test1/main.c new file mode 100644 index 0000000..31dbf45 --- /dev/null +++ b/examples/test-mk/test1/main.c @@ -0,0 +1,4 @@ +int main(void) +{ + return 0; +} diff --git a/examples/test-mk/test2/Makefile b/examples/test-mk/test2/Makefile new file mode 100644 index 0000000..e874674 --- /dev/null +++ b/examples/test-mk/test2/Makefile @@ -0,0 +1,43 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + +UCGINE ?= $(abspath ../../..) + +# output path with trailing slash +O = build/ + +CFLAGS = -g -O2 -Wall +CFLAGS += -Idir + +exe-y-$(O)prog := main.c + +include $(UCGINE)/mk/ucgine.mk + +all: _ucgine_all + +clean: _ucgine_clean + +.PHONY: clean all diff --git a/examples/test-mk/test2/main.c b/examples/test-mk/test2/main.c new file mode 100644 index 0000000..31dbf45 --- /dev/null +++ b/examples/test-mk/test2/main.c @@ -0,0 +1,4 @@ +int main(void) +{ + return 0; +} diff --git a/examples/test-uart/Makefile b/examples/test-uart/Makefile new file mode 100644 index 0000000..780187f --- /dev/null +++ b/examples/test-uart/Makefile @@ -0,0 +1,114 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + +UCGINE ?= $(abspath ../..) +include $(UCGINE)/mk/ucgine-pre.mk + +O ?= $(CURDIR)/build +PROG = $(O)/test-uart + +CROSS = arm-none-eabi- + +CFLAGS = -g -O2 -Wall +CFLAGS += -I. + +ifeq ($(CROSS),avr-) +MCU = atmega328p +F_CPU = 8000000UL +AVRDUDE_PORT = /dev/ttyUSB0 +AVRDUDE_PROG = arduino +AVRDUDE_BAUD = 57600 +CFLAGS += -I$(UCGINE)/arch/avr/include +CFLAGS += -I$(UCGINE)/arch/avr/uart/include +CFLAGS += -mmcu=$(MCU) +CFLAGS += -DF_CPU=$(F_CPU) +LDFLAGS += -mmcu=$(MCU) +# uart +exe-y-$(PROG) += $(UCGINE)/arch/avr/uart/ucg_avr_uart.c +endif + +ifeq ($(CROSS),arm-none-eabi-) +STLINK ?= /home/zer0/projects/stm32/stlink +STM_COMMON ?= /home/zer0/projects/stm32/stm32_discovery_arm_gcc/STM32F4-Discovery_FW_V1.1.0 +CFLAGS += -DUSE_STDPERIPH_DRIVER -Tstm32_flash.ld +CFLAGS += -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork +CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16 +CFLAGS += -I$(STM_COMMON)/Utilities/STM32F4-Discovery +CFLAGS += -I$(STM_COMMON)/Libraries/CMSIS/Include +CFLAGS += -I$(STM_COMMON)/Libraries/CMSIS/ST/STM32F4xx/Include +CFLAGS += -I$(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/inc +CFLAGS += -I$(UCGINE)/lib/gloss/include +CFLAGS += -I$(UCGINE)/arch/stm32/include +CFLAGS += -I$(UCGINE)/arch/stm32/uart/include +LDFLAGS = -Tstm32_flash.ld --specs=rdimon.specs -lc +LDFLAGS += -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork +LDFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16 +# stm32 standard periph lib +exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_exti.c +exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_gpio.c +exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_rcc.c +exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_usart.c +exe-y-$(PROG) += $(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/src/misc.c +# startup file +exe-y-$(PROG) += $(STM_COMMON)/Libraries/CMSIS/ST/STM32F4xx/Source/Templates/TrueSTUDIO/startup_stm32f4xx.s +# local files +exe-y-$(PROG) += system_stm32f4xx.c +# gloss +exe-y-$(PROG) += $(UCGINE)/lib/gloss/ucg_gloss_stubs.c +exe-y-$(PROG) += $(UCGINE)/lib/gloss/ucg_gloss_chardev.c +# uart +exe-y-$(PROG) += $(UCGINE)/arch/stm32/uart/ucg_stm32_uart.c +endif + +# cirbuf +CFLAGS += -I$(UCGINE)/lib/cirbuf/include +exe-y-$(PROG) += $(UCGINE)/lib/cirbuf/ucg_cirbuf.c +# uart +CFLAGS += -I$(UCGINE)/lib/uart/include +exe-y-$(PROG) += $(UCGINE)/lib/uart/ucg_uart.c + +exe-y-$(PROG) += uart.c main.c + +objcopy-hex-y-$(PROG).hex := $(PROG) +objcopy-bin-y-$(PROG).bin := $(PROG) + +include $(UCGINE)/mk/ucgine-post.mk + +.PHONY: all +all: $(all-targets) + +.PHONY: clean +clean: _ucgine_clean + +.PHONY: burn +burn: all +ifeq ($(CROSS),arm-none-eabi-) + $(STLINK)/st-flash write $(PROG).bin 0x8000000 +endif +ifeq ($(CROSS),avr-) + avrdude -e -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROG) \ + -b $(AVRDUDE_BAUD) -U flash:w:$(PROG):e +endif diff --git a/examples/test-uart/main.c b/examples/test-uart/main.c new file mode 100644 index 0000000..ff82841 --- /dev/null +++ b/examples/test-uart/main.c @@ -0,0 +1,121 @@ +/* + * Copyright 2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 "uart.h" + +#if defined(__ARM_EABI__) +#include + +static void led_on(void) +{ + GPIOD->ODR |= (1 << 13); +} + +static void led_off(void) +{ + GPIOD->ODR &= (~(1 << 13)); +} + +static void target_init(void) +{ + /* enable the clock to GPIOD, and stall instruction pipeline as per + * errata 2.1.13 "Delay after an RCC peripheral clock enabling" */ + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); + __asm("dsb"); + + /* set pin 13 to be general purpose output */ + GPIOD->MODER = (1 << 26); +} +#elif defined(__AVR__) +#include + +static void led_on(void) +{ + PORTB |= (1 << 5); +} + +static void led_off(void) +{ + PORTB &= (~(1 << 5)); +} + +static void target_init(void) +{ + DDRB = (1 << 5); +} +#endif + +static void big_print(void) +{ + printf( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\n" + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\n" + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\n" + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\n"); +} + +int main(void) +{ + unsigned i; + char c; + int ret; + + target_init(); + + /* toggle the pin */ + for (i = 0; i < 3; i++) { + led_on(); + ucg_delay_ms(500); + led_off(); + ucg_delay_ms(500); + } + + uart_init(); + ucg_irq_unlock(); + + big_print(); + + while (1) { + ret = fread(&c, 1, 1, stdin); + if (ret == 1) { + if (c == 'x') + big_print(); + else + fwrite(&c, 1, 1, stdout); + } else if (ret == 0) + clearerr(stdin); + } + + return 0; +} diff --git a/examples/test-uart/stm32_flash.ld b/examples/test-uart/stm32_flash.ld new file mode 100755 index 0000000..350c05b --- /dev/null +++ b/examples/test-uart/stm32_flash.ld @@ -0,0 +1,172 @@ +/* +***************************************************************************** +** +** File : stm32_flash.ld +** +** Abstract : Linker script for STM32F407VG Device with +** 1024KByte FLASH, 192KByte RAM +** +** Set heap size, stack size and stack location according +** to application requirements. +** +** Set memory bank area and size if external memory is used. +** +** Target : STMicroelectronics STM32 +** +** Environment : Atollic TrueSTUDIO(R) +** +** Distribution: The file is distributed “as is,” without any warranty +** of any kind. +** +** (c)Copyright Atollic AB. +** You may use this file as-is or modify it according to the needs of your +** project. Distribution of this file (unmodified or modified) is not +** permitted. Atollic AB permit registered Atollic TrueSTUDIO(R) users the +** rights to distribute the assembled, compiled & linked contents of this +** file as part of an application binary file, provided that it is built +** using the Atollic TrueSTUDIO(R) toolchain. +** +***************************************************************************** +*/ + +/* Entry Point */ +ENTRY(Reset_Handler) + +/* Highest address of the user mode stack */ +_estack = 0x20020000; /* end of 128K RAM on AHB bus*/ + +/* Generate a link error if heap and stack don't fit into RAM */ +_Min_Heap_Size = 0; /* required amount of heap */ +_Min_Stack_Size = 0x400; /* required amount of stack */ + +/* Specify the memory areas */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K + MEMORY_B1 (rx) : ORIGIN = 0x60000000, LENGTH = 0K +} + +/* Define output sections */ +SECTIONS +{ + /* The startup code goes first into FLASH */ + .isr_vector : + { + . = ALIGN(4); + KEEP(*(.isr_vector)) /* Startup code */ + . = ALIGN(4); + } >FLASH + + /* The program code and other data goes into FLASH */ + .text : + { + . = ALIGN(4); + *(.text) /* .text sections (code) */ + *(.text*) /* .text* sections (code) */ + *(.rodata) /* .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + *(.glue_7) /* glue arm to thumb code */ + *(.glue_7t) /* glue thumb to arm code */ + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + . = ALIGN(4); + _etext = .; /* define a global symbols at end of code */ + _exit = .; + } >FLASH + + + .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH + .ARM : { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } >FLASH + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array*)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } >FLASH + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array*)) + PROVIDE_HIDDEN (__init_array_end = .); + } >FLASH + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(.fini_array*)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE_HIDDEN (__fini_array_end = .); + } >FLASH + + /* used by the startup to initialize data */ + _sidata = .; + + /* Initialized data sections goes into RAM, load LMA copy after code */ + .data : AT ( _sidata ) + { + . = ALIGN(4); + _sdata = .; /* create a global symbol at data start */ + *(.data) /* .data sections */ + *(.data*) /* .data* sections */ + + . = ALIGN(4); + _edata = .; /* define a global symbol at data end */ + } >RAM + + /* Uninitialized data section */ + . = ALIGN(4); + .bss : + { + /* This is used by the startup in order to initialize the .bss secion */ + _sbss = .; /* define a global symbol at bss start */ + __bss_start__ = _sbss; + *(.bss) + *(.bss*) + *(COMMON) + + . = ALIGN(4); + _ebss = .; /* define a global symbol at bss end */ + __bss_end__ = _ebss; + } >RAM + + /* User_heap_stack section, used to check that there is enough RAM left */ + ._user_heap_stack : + { + . = ALIGN(4); + PROVIDE ( end = . ); + PROVIDE ( _end = . ); + PROVIDE ( __end__ = . ); + . = . + _Min_Heap_Size; + . = . + _Min_Stack_Size; + . = ALIGN(4); + } >RAM + + /* MEMORY_bank1 section, code must be located here explicitly */ + /* Example: extern int foo(void) __attribute__ ((section (".mb1text"))); */ + .memory_b1_text : + { + *(.mb1text) /* .mb1text sections (code) */ + *(.mb1text*) /* .mb1text* sections (code) */ + *(.mb1rodata) /* read-only data (constants) */ + *(.mb1rodata*) + } >MEMORY_B1 + + /* Remove information from the standard libraries */ + /DISCARD/ : + { + libc.a ( * ) + libm.a ( * ) + libgcc.a ( * ) + } + + .ARM.attributes 0 : { *(.ARM.attributes) } +} diff --git a/examples/test-uart/stm32f4xx_conf.h b/examples/test-uart/stm32f4xx_conf.h new file mode 100644 index 0000000..74447a8 --- /dev/null +++ b/examples/test-uart/stm32f4xx_conf.h @@ -0,0 +1,94 @@ +/** + ****************************************************************************** + * @file stm32f4xx_conf.h + * @author MCD Application Team + * @version V1.0.0 + * @date 19-September-2011 + * @brief Library configuration file. + ****************************************************************************** + * @attention + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2011 STMicroelectronics

+ ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F4xx_CONF_H +#define __STM32F4xx_CONF_H + +#if defined (HSE_VALUE) +/* Redefine the HSE value; it's equal to 8 MHz on the STM32F4-DISCOVERY Kit */ + #undef HSE_VALUE + #define HSE_VALUE ((uint32_t)8000000) +#endif /* HSE_VALUE */ + +/* Includes ------------------------------------------------------------------*/ +/* Uncomment the line below to enable peripheral header file inclusion */ +#include "stm32f4xx_adc.h" +#include "stm32f4xx_can.h" +#include "stm32f4xx_crc.h" +#include "stm32f4xx_cryp.h" +#include "stm32f4xx_dac.h" +#include "stm32f4xx_dbgmcu.h" +#include "stm32f4xx_dcmi.h" +#include "stm32f4xx_dma.h" +#include "stm32f4xx_exti.h" +#include "stm32f4xx_flash.h" +#include "stm32f4xx_fsmc.h" +#include "stm32f4xx_hash.h" +#include "stm32f4xx_gpio.h" +#include "stm32f4xx_i2c.h" +#include "stm32f4xx_iwdg.h" +#include "stm32f4xx_pwr.h" +#include "stm32f4xx_rcc.h" +#include "stm32f4xx_rng.h" +#include "stm32f4xx_rtc.h" +#include "stm32f4xx_sdio.h" +#include "stm32f4xx_spi.h" +#include "stm32f4xx_syscfg.h" +#include "stm32f4xx_tim.h" +#include "stm32f4xx_usart.h" +#include "stm32f4xx_wwdg.h" +#include "misc.h" /* High level functions for NVIC and SysTick (add-on to CMSIS functions) */ + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ + +/* If an external clock source is used, then the value of the following define + should be set to the value of the external clock source, else, if no external + clock is used, keep this define commented */ +/*#define I2S_EXTERNAL_CLOCK_VAL 12288000 */ /* Value of the external clock in Hz */ + + +/* Uncomment the line below to expanse the "assert_param" macro in the + Standard Peripheral Library drivers code */ +/* #define USE_FULL_ASSERT 1 */ + +/* Exported macro ------------------------------------------------------------*/ +#ifdef USE_FULL_ASSERT + +/** + * @brief The assert_param macro is used for function's parameters check. + * @param expr: If expr is false, it calls assert_failed function + * which reports the name of the source file and the source + * line number of the call that failed. + * If expr is true, it returns no value. + * @retval None + */ + #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__)) +/* Exported functions ------------------------------------------------------- */ + void assert_failed(uint8_t* file, uint32_t line); +#else + #define assert_param(expr) ((void)0) +#endif /* USE_FULL_ASSERT */ + +#endif /* __STM32F4xx_CONF_H */ + +/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/ diff --git a/examples/test-uart/system_stm32f4xx.c b/examples/test-uart/system_stm32f4xx.c new file mode 100644 index 0000000..98e8e04 --- /dev/null +++ b/examples/test-uart/system_stm32f4xx.c @@ -0,0 +1,553 @@ +/** + ****************************************************************************** + * @file system_stm32f4xx.c + * @author MCD Application Team + * @version V1.0.0 + * @date 30-September-2011 + * @brief CMSIS Cortex-M4 Device Peripheral Access Layer System Source File. + * This file contains the system clock configuration for STM32F4xx devices, + * and is generated by the clock configuration tool + * stm32f4xx_Clock_Configuration_V1.0.0.xls + * + * 1. This file provides two functions and one global variable to be called from + * user application: + * - SystemInit(): Setups the system clock (System clock source, PLL Multiplier + * and Divider factors, AHB/APBx prescalers and Flash settings), + * depending on the configuration made in the clock xls tool. + * This function is called at startup just after reset and + * before branch to main program. This call is made inside + * the "startup_stm32f4xx.s" file. + * + * - SystemCoreClock variable: Contains the core clock (HCLK), it can be used + * by the user application to setup the SysTick + * timer or configure other parameters. + * + * - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must + * be called whenever the core clock is changed + * during program execution. + * + * 2. After each device reset the HSI (16 MHz) is used as system clock source. + * Then SystemInit() function is called, in "startup_stm32f4xx.s" file, to + * configure the system clock before to branch to main program. + * + * 3. If the system clock source selected by user fails to startup, the SystemInit() + * function will do nothing and HSI still used as system clock source. User can + * add some code to deal with this issue inside the SetSysClock() function. + * + * 4. The default value of HSE crystal is set to 25MHz, refer to "HSE_VALUE" define + * in "stm32f4xx.h" file. When HSE is used as system clock source, directly or + * through PLL, and you are using different crystal you have to adapt the HSE + * value to your own configuration. + * + * 5. This file configures the system clock as follows: + *============================================================================= + *============================================================================= + * Supported STM32F4xx device revision | Rev A + *----------------------------------------------------------------------------- + * System Clock source | PLL (HSE) + *----------------------------------------------------------------------------- + * SYSCLK(Hz) | 168000000 + *----------------------------------------------------------------------------- + * HCLK(Hz) | 168000000 + *----------------------------------------------------------------------------- + * AHB Prescaler | 1 + *----------------------------------------------------------------------------- + * APB1 Prescaler | 4 + *----------------------------------------------------------------------------- + * APB2 Prescaler | 2 + *----------------------------------------------------------------------------- + * HSE Frequency(Hz) | 25000000 + *----------------------------------------------------------------------------- + * PLL_M | 25 + *----------------------------------------------------------------------------- + * PLL_N | 336 + *----------------------------------------------------------------------------- + * PLL_P | 2 + *----------------------------------------------------------------------------- + * PLL_Q | 7 + *----------------------------------------------------------------------------- + * PLLI2S_N | NA + *----------------------------------------------------------------------------- + * PLLI2S_R | NA + *----------------------------------------------------------------------------- + * I2S input clock | NA + *----------------------------------------------------------------------------- + * VDD(V) | 3.3 + *----------------------------------------------------------------------------- + * Main regulator output voltage | Scale1 mode + *----------------------------------------------------------------------------- + * Flash Latency(WS) | 5 + *----------------------------------------------------------------------------- + * Prefetch Buffer | OFF + *----------------------------------------------------------------------------- + * Instruction cache | ON + *----------------------------------------------------------------------------- + * Data cache | ON + *----------------------------------------------------------------------------- + * Require 48MHz for USB OTG FS, | Enabled + * SDIO and RNG clock | + *----------------------------------------------------------------------------- + *============================================================================= + ****************************************************************************** + * @attention + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2011 STMicroelectronics

+ ****************************************************************************** + */ + +/** @addtogroup CMSIS + * @{ + */ + +/** @addtogroup stm32f4xx_system + * @{ + */ + +/** @addtogroup STM32F4xx_System_Private_Includes + * @{ + */ + +#include "stm32f4xx.h" + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Private_TypesDefinitions + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Private_Defines + * @{ + */ + +/************************* Miscellaneous Configuration ************************/ +/*!< Uncomment the following line if you need to use external SRAM mounted + on STM324xG_EVAL board as data memory */ +/* #define DATA_IN_ExtSRAM */ + +/*!< Uncomment the following line if you need to relocate your vector Table in + Internal SRAM. */ +/* #define VECT_TAB_SRAM */ +#define VECT_TAB_OFFSET 0x00 /*!< Vector Table base offset field. + This value must be a multiple of 0x200. */ +/******************************************************************************/ + +/************************* PLL Parameters *************************************/ +/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */ +#define PLL_M 25 +#define PLL_N 336 + +/* SYSCLK = PLL_VCO / PLL_P */ +#define PLL_P 2 + +/* USB OTG FS, SDIO and RNG Clock = PLL_VCO / PLLQ */ +#define PLL_Q 7 + +/******************************************************************************/ + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Private_Variables + * @{ + */ + + uint32_t SystemCoreClock = 168000000; + + __I uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9}; + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Private_FunctionPrototypes + * @{ + */ + +static void SetSysClock(void); +#ifdef DATA_IN_ExtSRAM + static void SystemInit_ExtMemCtl(void); +#endif /* DATA_IN_ExtSRAM */ + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Private_Functions + * @{ + */ + +/** + * @brief Setup the microcontroller system + * Initialize the Embedded Flash Interface, the PLL and update the + * SystemFrequency variable. + * @param None + * @retval None + */ +void SystemInit(void) +{ + /* FPU settings ------------------------------------------------------------*/ + #if (__FPU_PRESENT == 1) && (__FPU_USED == 1) + SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */ + #endif + + /* Reset the RCC clock configuration to the default reset state ------------*/ + /* Set HSION bit */ + RCC->CR |= (uint32_t)0x00000001; + + /* Reset CFGR register */ + RCC->CFGR = 0x00000000; + + /* Reset HSEON, CSSON and PLLON bits */ + RCC->CR &= (uint32_t)0xFEF6FFFF; + + /* Reset PLLCFGR register */ + RCC->PLLCFGR = 0x24003010; + + /* Reset HSEBYP bit */ + RCC->CR &= (uint32_t)0xFFFBFFFF; + + /* Disable all interrupts */ + RCC->CIR = 0x00000000; + +#ifdef DATA_IN_ExtSRAM + SystemInit_ExtMemCtl(); +#endif /* DATA_IN_ExtSRAM */ + + /* Configure the System clock source, PLL Multiplier and Divider factors, + AHB/APBx prescalers and Flash settings ----------------------------------*/ + SetSysClock(); + + /* Configure the Vector Table location add offset address ------------------*/ +#ifdef VECT_TAB_SRAM + SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */ +#else + SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */ +#endif +} + +/** + * @brief Update SystemCoreClock variable according to Clock Register Values. + * The SystemCoreClock variable contains the core clock (HCLK), it can + * be used by the user application to setup the SysTick timer or configure + * other parameters. + * + * @note Each time the core clock (HCLK) changes, this function must be called + * to update SystemCoreClock variable value. Otherwise, any configuration + * based on this variable will be incorrect. + * + * @note - The system frequency computed by this function is not the real + * frequency in the chip. It is calculated based on the predefined + * constant and the selected clock source: + * + * - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*) + * + * - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**) + * + * - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**) + * or HSI_VALUE(*) multiplied/divided by the PLL factors. + * + * (*) HSI_VALUE is a constant defined in stm32f4xx.h file (default value + * 16 MHz) but the real value may vary depending on the variations + * in voltage and temperature. + * + * (**) HSE_VALUE is a constant defined in stm32f4xx.h file (default value + * 25 MHz), user has to ensure that HSE_VALUE is same as the real + * frequency of the crystal used. Otherwise, this function may + * have wrong result. + * + * - The result of this function could be not correct when using fractional + * value for HSE crystal. + * + * @param None + * @retval None + */ +void SystemCoreClockUpdate(void) +{ + uint32_t tmp = 0, pllvco = 0, pllp = 2, pllsource = 0, pllm = 2; + + /* Get SYSCLK source -------------------------------------------------------*/ + tmp = RCC->CFGR & RCC_CFGR_SWS; + + switch (tmp) + { + case 0x00: /* HSI used as system clock source */ + SystemCoreClock = HSI_VALUE; + break; + case 0x04: /* HSE used as system clock source */ + SystemCoreClock = HSE_VALUE; + break; + case 0x08: /* PLL used as system clock source */ + + /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N + SYSCLK = PLL_VCO / PLL_P + */ + pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) >> 22; + pllm = RCC->PLLCFGR & RCC_PLLCFGR_PLLM; + + if (pllsource != 0) + { + /* HSE used as PLL clock source */ + pllvco = (HSE_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6); + } + else + { + /* HSI used as PLL clock source */ + pllvco = (HSI_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6); + } + + pllp = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLP) >>16) + 1 ) *2; + SystemCoreClock = pllvco/pllp; + break; + default: + SystemCoreClock = HSI_VALUE; + break; + } + /* Compute HCLK frequency --------------------------------------------------*/ + /* Get HCLK prescaler */ + tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)]; + /* HCLK frequency */ + SystemCoreClock >>= tmp; +} + +/** + * @brief Configures the System clock source, PLL Multiplier and Divider factors, + * AHB/APBx prescalers and Flash settings + * @Note This function should be called only once the RCC clock configuration + * is reset to the default reset state (done in SystemInit() function). + * @param None + * @retval None + */ +static void SetSysClock(void) +{ +/******************************************************************************/ +/* PLL (clocked by HSE) used as System clock source */ +/******************************************************************************/ + __IO uint32_t StartUpCounter = 0, HSEStatus = 0; + + /* Enable HSE */ + RCC->CR |= ((uint32_t)RCC_CR_HSEON); + + /* Wait till HSE is ready and if Time out is reached exit */ + do + { + HSEStatus = RCC->CR & RCC_CR_HSERDY; + StartUpCounter++; + } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); + + if ((RCC->CR & RCC_CR_HSERDY) != RESET) + { + HSEStatus = (uint32_t)0x01; + } + else + { + HSEStatus = (uint32_t)0x00; + } + + if (HSEStatus == (uint32_t)0x01) + { + /* Select regulator voltage output Scale 1 mode, System frequency up to 168 MHz */ + RCC->APB1ENR |= RCC_APB1ENR_PWREN; + PWR->CR |= PWR_CR_VOS; + + /* HCLK = SYSCLK / 1*/ + RCC->CFGR |= RCC_CFGR_HPRE_DIV1; + + /* PCLK2 = HCLK / 2*/ + RCC->CFGR |= RCC_CFGR_PPRE2_DIV2; + + /* PCLK1 = HCLK / 4*/ + RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; + + /* Configure the main PLL */ + RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) | + (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24); + + /* Enable the main PLL */ + RCC->CR |= RCC_CR_PLLON; + + /* Wait till the main PLL is ready */ + while((RCC->CR & RCC_CR_PLLRDY) == 0) + { + } + + /* Configure Flash prefetch, Instruction cache, Data cache and wait state */ + FLASH->ACR = FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS; + + /* Select the main PLL as system clock source */ + RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); + RCC->CFGR |= RCC_CFGR_SW_PLL; + + /* Wait till the main PLL is used as system clock source */ + while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL); + { + } + } + else + { /* If HSE fails to start-up, the application will have wrong clock + configuration. User can add here some code to deal with this error */ + } + +} + +/** + * @brief Setup the external memory controller. Called in startup_stm32f4xx.s + * before jump to __main + * @param None + * @retval None + */ +#ifdef DATA_IN_ExtSRAM +/** + * @brief Setup the external memory controller. + * Called in startup_stm32f4xx.s before jump to main. + * This function configures the external SRAM mounted on STM324xG_EVAL board + * This SRAM will be used as program data memory (including heap and stack). + * @param None + * @retval None + */ +void SystemInit_ExtMemCtl(void) +{ +/*-- GPIOs Configuration -----------------------------------------------------*/ +/* + +-------------------+--------------------+------------------+------------------+ + + SRAM pins assignment + + +-------------------+--------------------+------------------+------------------+ + | PD0 <-> FSMC_D2 | PE0 <-> FSMC_NBL0 | PF0 <-> FSMC_A0 | PG0 <-> FSMC_A10 | + | PD1 <-> FSMC_D3 | PE1 <-> FSMC_NBL1 | PF1 <-> FSMC_A1 | PG1 <-> FSMC_A11 | + | PD4 <-> FSMC_NOE | PE3 <-> FSMC_A19 | PF2 <-> FSMC_A2 | PG2 <-> FSMC_A12 | + | PD5 <-> FSMC_NWE | PE4 <-> FSMC_A20 | PF3 <-> FSMC_A3 | PG3 <-> FSMC_A13 | + | PD8 <-> FSMC_D13 | PE7 <-> FSMC_D4 | PF4 <-> FSMC_A4 | PG4 <-> FSMC_A14 | + | PD9 <-> FSMC_D14 | PE8 <-> FSMC_D5 | PF5 <-> FSMC_A5 | PG5 <-> FSMC_A15 | + | PD10 <-> FSMC_D15 | PE9 <-> FSMC_D6 | PF12 <-> FSMC_A6 | PG9 <-> FSMC_NE2 | + | PD11 <-> FSMC_A16 | PE10 <-> FSMC_D7 | PF13 <-> FSMC_A7 |------------------+ + | PD12 <-> FSMC_A17 | PE11 <-> FSMC_D8 | PF14 <-> FSMC_A8 | + | PD13 <-> FSMC_A18 | PE12 <-> FSMC_D9 | PF15 <-> FSMC_A9 | + | PD14 <-> FSMC_D0 | PE13 <-> FSMC_D10 |------------------+ + | PD15 <-> FSMC_D1 | PE14 <-> FSMC_D11 | + | | PE15 <-> FSMC_D12 | + +-------------------+--------------------+ +*/ + /* Enable GPIOD, GPIOE, GPIOF and GPIOG interface clock */ + RCC->AHB1ENR = 0x00000078; + + /* Connect PDx pins to FSMC Alternate function */ + GPIOD->AFR[0] = 0x00cc00cc; + GPIOD->AFR[1] = 0xcc0ccccc; + /* Configure PDx pins in Alternate function mode */ + GPIOD->MODER = 0xaaaa0a0a; + /* Configure PDx pins speed to 100 MHz */ + GPIOD->OSPEEDR = 0xffff0f0f; + /* Configure PDx pins Output type to push-pull */ + GPIOD->OTYPER = 0x00000000; + /* No pull-up, pull-down for PDx pins */ + GPIOD->PUPDR = 0x00000000; + + /* Connect PEx pins to FSMC Alternate function */ + GPIOE->AFR[0] = 0xc00cc0cc; + GPIOE->AFR[1] = 0xcccccccc; + /* Configure PEx pins in Alternate function mode */ + GPIOE->MODER = 0xaaaa828a; + /* Configure PEx pins speed to 100 MHz */ + GPIOE->OSPEEDR = 0xffffc3cf; + /* Configure PEx pins Output type to push-pull */ + GPIOE->OTYPER = 0x00000000; + /* No pull-up, pull-down for PEx pins */ + GPIOE->PUPDR = 0x00000000; + + /* Connect PFx pins to FSMC Alternate function */ + GPIOF->AFR[0] = 0x00cccccc; + GPIOF->AFR[1] = 0xcccc0000; + /* Configure PFx pins in Alternate function mode */ + GPIOF->MODER = 0xaa000aaa; + /* Configure PFx pins speed to 100 MHz */ + GPIOF->OSPEEDR = 0xff000fff; + /* Configure PFx pins Output type to push-pull */ + GPIOF->OTYPER = 0x00000000; + /* No pull-up, pull-down for PFx pins */ + GPIOF->PUPDR = 0x00000000; + + /* Connect PGx pins to FSMC Alternate function */ + GPIOG->AFR[0] = 0x00cccccc; + GPIOG->AFR[1] = 0x000000c0; + /* Configure PGx pins in Alternate function mode */ + GPIOG->MODER = 0x00080aaa; + /* Configure PGx pins speed to 100 MHz */ + GPIOG->OSPEEDR = 0x000c0fff; + /* Configure PGx pins Output type to push-pull */ + GPIOG->OTYPER = 0x00000000; + /* No pull-up, pull-down for PGx pins */ + GPIOG->PUPDR = 0x00000000; + +/*-- FSMC Configuration ------------------------------------------------------*/ + /* Enable the FSMC interface clock */ + RCC->AHB3ENR = 0x00000001; + + /* Configure and enable Bank1_SRAM2 */ + FSMC_Bank1->BTCR[2] = 0x00001015; + FSMC_Bank1->BTCR[3] = 0x00010603; + FSMC_Bank1E->BWTR[2] = 0x0fffffff; +/* + Bank1_SRAM2 is configured as follow: + + p.FSMC_AddressSetupTime = 3; + p.FSMC_AddressHoldTime = 0; + p.FSMC_DataSetupTime = 6; + p.FSMC_BusTurnAroundDuration = 1; + p.FSMC_CLKDivision = 0; + p.FSMC_DataLatency = 0; + p.FSMC_AccessMode = FSMC_AccessMode_A; + + FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM2; + FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable; + FSMC_NORSRAMInitStructure.FSMC_MemoryType = FSMC_MemoryType_PSRAM; + FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b; + FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable; + FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait = FSMC_AsynchronousWait_Disable; + FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low; + FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable; + FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState; + FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable; + FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable; + FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable; + FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable; + FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &p; + FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &p; +*/ + +} +#endif /* DATA_IN_ExtSRAM */ + + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ +/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/ diff --git a/examples/test-uart/uart.c b/examples/test-uart/uart.c new file mode 100644 index 0000000..1567135 --- /dev/null +++ b/examples/test-uart/uart.c @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2015, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +#if defined(__ARM_EABI__) +#include "stm32f4xx.h" +#elif defined(__AVR__) +#include +#include +#include +#include +#endif + +#include +#include +#include + +#include +#include +#include + +#include "uart.h" + +/* rx & tx buffers */ +static char rx_buf[64]; +static struct ucg_cirbuf rx_cirbuf; +static char tx_buf[64]; +static struct ucg_cirbuf tx_cirbuf; +/* generic uart struct */ +struct ucg_uart main_uart; + +#if defined(__ARM_EABI__) + +#include +#include + +static struct ucg_stm32_uart stm32_uart_data = { + .uart = USART2, + .rcc_uart = RCC_APB1Periph_USART2, + .rcc_gpio = RCC_AHB1Periph_GPIOA, + .gpio = GPIOA, + .gpio_af = GPIO_AF_USART2, + .gpio_pins = GPIO_Pin_2 | GPIO_Pin_3, + .gpio_speed = GPIO_Speed_25MHz, + .irq = USART2_IRQn, + .irq_preempt_prio = 2, + .irq_sub_prio = 0, +}; + +static _ssize_t +uart_write_r(__attribute__((unused)) struct _reent *r, + __attribute__((unused)) int fd, + const void *ptr, size_t len) +{ + size_t i; + const char *buf = ptr; + + for (i = 0; i < len; i++) + ucg_uart_send(&main_uart, buf[i], WAIT); + + return len; +} + +static _ssize_t +uart_read_r(__attribute__((unused)) struct _reent *r, + __attribute__((unused)) int fd, + void *ptr, size_t len) +{ + int c; + char *buf = ptr; + + c = ucg_uart_recv(&main_uart, NOWAIT); + if (c < 0) { + r->_errno = EAGAIN; + return 0; + } + buf[0] = c; + return 1; +} + +void USART2_IRQHandler(void) +{ + if ((USART2->SR & USART_FLAG_TXE)) + ucg_uart_tx_intr(&main_uart); + if ((USART2->SR & USART_FLAG_RXNE)) + ucg_uart_rx_intr(&main_uart); +} + +struct ucg_chardev uart_stdin_dev = { + .name = "stdin", + .open_r = NULL, + .close_r = NULL, + .read_r = uart_read_r, + .write_r = NULL, +}; + +struct ucg_chardev uart_stdout_dev = { + .name = "stdout", + .open_r = NULL, + .close_r = NULL, + .read_r = NULL, + .write_r = uart_write_r, +}; + +struct ucg_chardev uart_stderr_dev = { + .name = "stderr", + .open_r = NULL, + .close_r = NULL, + .read_r = NULL, + .write_r = uart_write_r, +}; + +static void uart_register_stdio(void) +{ + ucg_chardev_register(&uart_stdin_dev); + ucg_chardev_register(&uart_stdout_dev); + ucg_chardev_register(&uart_stderr_dev); + + /* Disable buffering */ + setbuf(stdin, NULL); + setbuf(stdout, NULL); + setbuf(stderr, NULL); +} + +#elif defined(__AVR__) + +#include +/* avr-specific uart struct */ +static struct ucg_avr_uart avr_uart_data = { + .reg_udr = &UDR0, + .reg_ucsra = &UCSR0A, + .reg_ucsrb = &UCSR0B, + .reg_ucsrc = &UCSR0C, + .reg_ubrrl = &UBRR0L, + .reg_ubrrh = &UBRR0H, + .bit_udre = UDRE0, + .bit_rxc = RXC0, + .bit_udrie = UDRIE0, + .bit_rxen = RXEN0, + .bit_txen = TXEN0, + .bit_rxcie = RXCIE0, + .bit_u2x = U2X0, +}; + +/* send on stdout */ +static int std_send(char c, FILE *f) +{ + (void)f; + ucg_uart_send(&main_uart, c, WAIT); + return 0; +} + +/* recv on stdin */ +static int std_recv(FILE *f) +{ + int16_t c; + + (void)f; + c = ucg_uart_recv(&main_uart, NOWAIT); + if (c < 0) + return _FDEV_EOF; + + return c; +} + +SIGNAL(USART_RX_vect) +{ + ucg_uart_rx_intr(&main_uart); +} + +SIGNAL(USART_UDRE_vect) +{ + ucg_uart_tx_intr(&main_uart); +} +#endif + +int uart_init(void) +{ + int ret; + struct ucg_uart_config conf; + const void *uart_ops; + void *uart_data; + +#if defined(__ARM_EABI__) + uart_ops = &stm32_uart_ops; + uart_data = &stm32_uart_data; +#else + uart_ops = &avr_uart_ops; + uart_data = &avr_uart_data; +#endif + + ret = ucg_uart_init(&main_uart, uart_ops, uart_data, + &rx_cirbuf, rx_buf, sizeof(rx_buf), + &tx_cirbuf, tx_buf, sizeof(tx_buf)); + if (ret < 0) + return ret; + + ucg_uart_getconf(&main_uart, &conf); + conf.baudrate = 57600; + ret = ucg_uart_setconf(&main_uart, &conf); + if (ret < 0) + return ret; + +#if defined(__ARM_EABI__) + uart_register_stdio(); +#elif defined(__AVR__) + fdevopen(std_send, std_recv); +#endif + return ret; +} diff --git a/examples/test-uart/uart.h b/examples/test-uart/uart.h new file mode 100644 index 0000000..ca1a139 --- /dev/null +++ b/examples/test-uart/uart.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2015, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +#ifndef UART_H_ +#define UART_H_ + +#include + +int uart_init(void); + +#endif diff --git a/lib/callout/include/ucg_callout.h b/lib/callout/include/ucg_callout.h new file mode 100644 index 0000000..34bb29d --- /dev/null +++ b/lib/callout/include/ucg_callout.h @@ -0,0 +1,361 @@ +/* + * Copyright (c) <2014-2015>, Olivier Matz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +/* Inspired from Intel DPDK rte_timer library */ +/*- + * Copyright (c) <2010>, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - 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. + * + * - Neither the name of Intel Corporation nor the names of its + * 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. + */ + +#ifndef UCG_CALLOUT_H_ +#define UCG_CALLOUT_H_ + +#include + +#define UCG_CALLOUT_STATS +/* #define UCG_CALLOUT_DEBUG */ + +/** + * This module provides a timer service. The manager function + * ucg_callout_manage() can be called from an interrupt or from a + * standard function (usually a main-loop). In the latter case, no + * preemption is possible. + * + * Each timer has a priority: the timers with higher priorities are + * scheduled before the others. This feature is mostly useful when the + * manager is called from an interrupt. Indeed, the callback function of + * a timer with a high priority cannot be preempted by a timer with a + * lower priority. + * + * The module locks interrupts when doing critical operations, ensuring that + * critical data are accessed atomically. + * + * State of timers: + * - stopped: initial state after ucg_callout_init() + * - scheduled: after a call to ucg_callout_schedule(), the timer is in the + * scheduled list of the callout manager + * - expired: after a call to ucg_callout_manage(), if the expire time of a + * timer is reached, it is moved in a local list and its state is + * changed to "expired". + * - before starting the callback, the timer goes in state "running". + * + * Once running, the associated timer is not touched anymore by + * ucg_callout_manage(). As a result, the timer MUST be either reloaded + * or stopped (and potentially freed). + */ + +/** + * Maximum number of nested preemptions. + */ +#define UCG_CALLOUT_MAX_RECURSION 5 + +#ifdef UCG_CALLOUT_STATS +/** + * The structure that stores the timer statistics, mostly useful for debug + * purposes. + */ +struct ucg_callout_debug_stats { + uint32_t schedule; /**< nb of calls to ucg_callout_(re)schedule() */ + uint32_t stop; /**< nb of calls to ucg_callout_stop() */ + uint32_t manage; /**< nb of calls to ucg_callout_manage() */ + uint32_t max_recursion; /** manage() skipped due to max recursion */ + uint32_t delayed; /** task delayed a bit due to low prio */ + uint32_t hard_delayed; /** task recheduled later due to low priority */ + + uint8_t cur_scheduled; /**< current number of scheduled timers */ + uint8_t cur_expired; /**< current number of expired timers */ + uint8_t cur_running; /**< current number of running timers */ +}; +#endif + +struct ucg_callout; +struct ucg_callout_mgr; + +/** + * The type of a callout callback function. + */ +typedef void (ucg_callout_cb_t)(struct ucg_callout_mgr *cm, + struct ucg_callout *tim, void *arg); + +/** + * A callout structure, storing all data associated to a timer. + */ +struct ucg_callout { + LIST_ENTRY(ucg_callout) next; /**< next/prev in list */ + +#define UCG_CALLOUT_STATE_STOPPED 0 /**< not scheduled */ +#define UCG_CALLOUT_STATE_SCHEDULED 1 /**< in the scheduled list */ +#define UCG_CALLOUT_STATE_EXPIRED 2 /**< expired, will be executed soon */ +#define UCG_CALLOUT_STATE_RUNNING 3 /**< being executed */ + uint8_t state; /**< stopped, scheduled, expired */ + uint8_t priority; /**< the priority of the timer */ + uint16_t expire; /**< time when timer should expire */ + + ucg_callout_cb_t *f; /**< callback function pointer */ + void *arg; /**< argument given to the cb function. */ +}; + +/* define the callout list */ +LIST_HEAD(ucg_callout_list, ucg_callout); + +/* static initializer for a timer structure */ +#define UCG_CALLOUT_INITIALIZER { } + +/** + * Type of the function used by a callout manager to get a time reference + */ +typedef uint16_t (ucg_callout_get_time_t)(void); + +/** + * An instance of callout manager. It is possible to have several + * managers. A callout is attached to one manager at a time. + */ +struct ucg_callout_mgr { + ucg_callout_get_time_t *get_time; /**< func to get the time reference */ + uint16_t prev_time; /**< time of previous call */ + uint8_t cur_priority; /** priority of running event */ + uint8_t nb_recursion; /** number of recursion */ + struct ucg_callout_list sched_list; /**< list of scheduled timers */ + +#ifdef UCG_CALLOUT_STATS + struct ucg_callout_debug_stats stats; /**< stats */ +#endif +}; + +/** + * Initialize a callout manager + * + * The callout manager must be initialized before ucg_callout_add() or + * ucg_callout_manage() can be called. + * + * @param cm + * Pointer to the uninitialized callout manager structure. + * @param get_time + * Pointer to a function that returns a time reference (unsigned 16 bits). + */ +void ucg_callout_mgr_init(struct ucg_callout_mgr *cm, + ucg_callout_get_time_t *get_time); + +/** + * Initialize a callout structure and set callback function + * + * Before doing any operation on the callout structure, it has to be + * initialized with this function. It is possible to reinitialize a + * timer that has been previously scheduled, but it must be stopped. + * + * @param tim + * The timer to initialize. + * @param priority + * The priority of the callout (high value means higher priority) + * @param f + * The callback function of the timer. + * @param arg + * The user argument of the callback function. + */ +void ucg_callout_init(struct ucg_callout *tim, ucg_callout_cb_t f, void *arg, + uint8_t priority); + +/** + * Schedule a callout + * + * The ucg_callout_schedule() function adds the timer in the scheduled + * list. After the specified amount of ticks are elapsed, the callback + * function of the timer previously given to ucg_callout_init() will be + * invoked with its argument. + * + * The given "tick" value is relative to the current time, and is 16 bits + * wide. As it internally uses signed 16 bits comparison, the max value for + * ticks is 32767. + * + * @param cm + * The callout manager where the timer should be scheduled + * @param tim + * The timer handle + * @param ticks + * The number of ticks before the callback function is called, relative to now + * (the reference is given by the get_time() function of the callout manager). + * @return + * 0 on success, negative on error + */ +int ucg_callout_schedule(struct ucg_callout_mgr *cm, struct ucg_callout *tim, + uint16_t ticks); + +/** + * Reschedule a callout + * + * This function does exactly the same than ucg_callout_schedule() + * except that the given time "ticks" is not relative to the current + * time but to the "expire" field of the timer. + * + * Using this function is advised to avoid drift if you want to have periodic + * timers. + * + * This function should preferably be called from the callback function of + * the timer. Indeed, if the "expire" field should be a known value or it + * can result in an undefined behavior + * + * The given "tick" value is relative to the "expire" field of the + * timer, and is 16 bits wide. As it internally uses signed 16 bits + * comparison, the max value for ticks is 32767. + * + * @param cm + * The callout manager where the timer should be scheduled + * @param tim + * The timer handle + * @param ticks + * The number of ticks before the callback function is called, relative to + * the "expire" value of the timer + * @return + * 0 on success, negative on error + */ +int ucg_callout_reschedule(struct ucg_callout_mgr *cm, struct ucg_callout *tim, + uint16_t ticks); + +/** + * Stop a timer. + * + * The ucg_callout_stop() function stops a timer associated with the + * timer handle tim. + * + * If the timer is scheduled or expired, it is removed from the list: + * the callback function won't be invoked. If the timer is stopped or + * running the function does nothing. + * + * If a timer structure is dynamically allocated, invoking + * ucg_callout_stop() is needed before freeing the structure, even if + * the freeing occurs in a callback. Indeed, this function can be called + * safely from a timer callback. If it succeeds, the timer is not + * referenced anymore by the callout manager. + * + * @param cm + * The callout manager where the timer is or was scheduled + * @param tim + * The timer + * @return + * 0 on success, negative on error + */ +void ucg_callout_stop(struct ucg_callout_mgr *cm, struct ucg_callout *tim); + +/** + * Return the state of a timer + * + * @param tim + * The timer + * @return + * - UCG_CALLOUT_STATE_STOPPED: the timer is stopped + * - UCG_CALLOUT_STATE_SCHEDULED: the timer is scheduled + * - UCG_CALLOUT_STATE_EXPIRED: the timer was moved in a local list before + * execution + */ +static inline uint8_t ucg_callout_state(struct ucg_callout *tim) +{ + return tim->state; +} + +/** + * Manage the timer list and execute callback functions. + * + * This function must be called periodically, either from a loop of from + * an interrupt. It browses the list of scheduled timers and runs all + * timers that are expired. + * + * This function must be called at least every 16384 reference ticks of + * cm->get_time(), but calling it more often is recommanded to avoid + * delaying task abusively. + * + * The function must be called with IRQ allowed. + */ +void ucg_callout_manage(struct ucg_callout_mgr *cm); + +/** + * Dump statistics about timers. + */ +void ucg_callout_dump_stats(struct ucg_callout_mgr *cm); + +/** + * Set the current priority level + * + * Prevent callout with a priority lower than "new_prio" to be executed. + * If the current priority of the callout manager is already lower higher + * than "new_prio", the function won't change the running priority. + * + * The returned value should be stored by the caller and restored with + * ucg_callout_mgr_restore_prio(), preferably in the same function. + * + * @param cm + * The callout manager + * @param new_prio + * The new running priority + * + * @return + * The value of the running priority before the call og this function + */ +uint8_t ucg_callout_mgr_set_prio(struct ucg_callout_mgr *cm, uint8_t new_prio); + +/** + * Restore the current priority level + * + * Used after a call to ucg_callout_mgr_set_prio(). + * + * @param cm + * The callout manager + * @param old_prio + * The old running priority + */ +void ucg_callout_mgr_restore_prio(struct ucg_callout_mgr *cm, uint8_t old_prio); + +#endif /* UCG_CALLOUT_H_ */ diff --git a/lib/callout/ucg_callout.c b/lib/callout/ucg_callout.c new file mode 100644 index 0000000..8e51240 --- /dev/null +++ b/lib/callout/ucg_callout.c @@ -0,0 +1,412 @@ +/* + * Copyright (c) <2014-2015>, Olivier Matz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +/* Inspired from Intel DPDK rte_timer library */ +/*- + * Copyright (c) <2010>, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - 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. + * + * - Neither the name of Intel Corporation nor the names of its + * 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. + */ + +#include +#include +#include +#include +#include + +#include +#include + +/* allow to browse a list while modifying the current element */ +#define _LIST_FOREACH_SAFE(cur, next, head, field) \ + for ((cur) = LIST_FIRST((head)), \ + (next) = ((cur) ? LIST_NEXT((cur), field) : NULL); \ + (cur); \ + (cur) = (next), \ + (next) = ((cur) ? LIST_NEXT((cur), field) : NULL)) + +#ifdef UCG_CALLOUT_STATS +/* called with irq locked */ +#define CALLOUT_STAT_ADD(cm, field, x) do { \ + cm->stats.field += x; \ + } while(0) +#else +#define CALLOUT_STAT_ADD(cm, field, x) do { } while(0) +#endif + +#ifdef UCG_CALLOUT_DEBUG +#define callout_dprintf(fmt, ...) \ + printf("%s(): " fmt, __FUNCTION__, __VA_ARGS__) +#else +#define callout_dprintf(...) do { } while (0) +#endif + +/* Initialize a callout manager */ +void +ucg_callout_mgr_init(struct ucg_callout_mgr *cm, + ucg_callout_get_time_t *get_time) +{ + memset(cm, 0, sizeof(*cm)); + cm->get_time = get_time; + LIST_INIT(&cm->sched_list); +} + +/* Initialize the timer handle tim for use */ +void +ucg_callout_init(struct ucg_callout *tim, ucg_callout_cb_t f, void *arg, + uint8_t priority) +{ + memset(tim, 0, sizeof(*tim)); + tim->f = f; + tim->arg = arg; + tim->priority = priority; +} + +/* + * Add a timer in the scheduled list (timer must not already be in a list). The + * timers are sorted in the list according the expire time (the closer timers + * first). + * + * called with irq locked + */ +static void +callout_add_in_sched_list(struct ucg_callout_mgr *cm, struct ucg_callout *tim) +{ + struct ucg_callout *t, *prev_t; + + callout_dprintf("cm=%p tim=%p\r\n", cm, tim); + tim->state = UCG_CALLOUT_STATE_SCHEDULED; + + /* list is empty */ + if (LIST_EMPTY(&cm->sched_list)) { + LIST_INSERT_HEAD(&cm->sched_list, tim, next); + return; + } + + /* 'tim' expires before first entry */ + t = LIST_FIRST(&cm->sched_list); + if ((int16_t)(tim->expire - t->expire) <= 0) { + LIST_INSERT_HEAD(&cm->sched_list, tim, next); + return; + } + + /* find an element that will expire after 'tim' */ + LIST_FOREACH(t, &cm->sched_list, next) { + if ((int16_t)(tim->expire - t->expire) <= 0) { + LIST_INSERT_BEFORE(t, tim, next); + return; + } + prev_t = t; + } + + /* not found, insert at the end of the list */ + LIST_INSERT_AFTER(prev_t, tim, next); +} + +/* + * Add a timer in the local expired list (timer must not already be in a + * list). The timers are sorted in the list according to the priority (high + * priority first). + * + * called with irq locked + */ +static void +callout_add_in_expired_list(struct ucg_callout_mgr *cm, + struct ucg_callout_list *expired_list, struct ucg_callout *tim) +{ + struct ucg_callout *t, *prev_t; + + (void)cm; /* avoid warning if debug is disabled */ + + callout_dprintf("cm=%p tim=%p\r\n", cm, tim); + tim->state = UCG_CALLOUT_STATE_EXPIRED; + + /* list is empty */ + if (LIST_EMPTY(expired_list)) { + LIST_INSERT_HEAD(expired_list, tim, next); + return; + } + + /* 'tim' has a higher prio */ + t = LIST_FIRST(expired_list); + if (tim->priority >= t->priority) { + LIST_INSERT_HEAD(expired_list, tim, next); + return; + } + + /* find an element that will expire after 'tim' */ + LIST_FOREACH(t, expired_list, next) { + if (tim->priority >= t->priority) { + LIST_INSERT_BEFORE(t, tim, next); + return; + } + prev_t = t; + } + + /* not found, insert at the end of the list */ + LIST_INSERT_AFTER(prev_t, tim, next); +} + +/* + * del from list (timer must be in a list) + */ +static void +callout_del(struct ucg_callout_mgr *cm, struct ucg_callout *tim) +{ + (void)cm; /* avoid warning if debug is disabled */ + callout_dprintf("cm=%p tim=%p\r\n", cm, tim); + LIST_REMOVE(tim, next); +} + +/* Reset and start the timer associated with the timer handle tim */ +static int +__callout_schedule(struct ucg_callout_mgr *cm, struct ucg_callout *tim, + uint16_t expire) +{ + ucg_irqflags_t flags; + + callout_dprintf("cm=%p tim=%p expire=%d\r\n", + cm, tim, expire); + + flags = ucg_irq_lock_save(); + CALLOUT_STAT_ADD(cm, schedule, 1); + + /* remove it from list */ + if (tim->state != UCG_CALLOUT_STATE_STOPPED) { + /* stats */ + if (tim->state == UCG_CALLOUT_STATE_SCHEDULED) + CALLOUT_STAT_ADD(cm, cur_scheduled, -1); + else if (tim->state == UCG_CALLOUT_STATE_EXPIRED) + CALLOUT_STAT_ADD(cm, cur_expired, -1); + if (tim->state == UCG_CALLOUT_STATE_RUNNING) + CALLOUT_STAT_ADD(cm, cur_running, -1); + + callout_del(cm, tim); + } + + tim->expire = expire; + CALLOUT_STAT_ADD(cm, cur_scheduled, 1); + callout_add_in_sched_list(cm, tim); + ucg_irq_unlock_restore(flags); + + return 0; +} + +/* Reset and start the timer associated with the timer handle tim */ +int +ucg_callout_schedule(struct ucg_callout_mgr *cm, struct ucg_callout *tim, + uint16_t ticks) +{ + return __callout_schedule(cm, tim, cm->get_time() + ticks); +} + +/* Reset and start the timer associated with the timer handle tim */ +int +ucg_callout_reschedule(struct ucg_callout_mgr *cm, struct ucg_callout *tim, + uint16_t ticks) +{ + return __callout_schedule(cm, tim, tim->expire + ticks); +} + +/* Stop the timer associated with the timer handle tim */ +void +ucg_callout_stop(struct ucg_callout_mgr *cm, struct ucg_callout *tim) +{ + ucg_irqflags_t flags; + + callout_dprintf("cm=%p tim=%p\r\n", cm, tim); + + flags = ucg_irq_lock_save(); + if (tim->state != UCG_CALLOUT_STATE_STOPPED) { + + /* stats */ + if (tim->state == UCG_CALLOUT_STATE_SCHEDULED) + CALLOUT_STAT_ADD(cm, cur_scheduled, -1); + else if (tim->state == UCG_CALLOUT_STATE_EXPIRED) + CALLOUT_STAT_ADD(cm, cur_expired, -1); + if (tim->state == UCG_CALLOUT_STATE_RUNNING) + CALLOUT_STAT_ADD(cm, cur_running, -1); + CALLOUT_STAT_ADD(cm, stop, 1); + + /* remove it from list */ + callout_del(cm, tim); + tim->state = UCG_CALLOUT_STATE_STOPPED; + } + ucg_irq_unlock_restore(flags); +} + +/* must be called periodically, run all timer that expired */ +void ucg_callout_manage(struct ucg_callout_mgr *cm) +{ + struct ucg_callout_list expired_list; + struct ucg_callout_list reschedule_list; + struct ucg_callout *tim, *tim_next; + uint16_t cur_time; + uint8_t old_prio; + int16_t diff; + + CALLOUT_STAT_ADD(cm, manage, 1); + callout_dprintf("cm=%p\r\n", cm); + + /* maximize the number of self-recursions */ + if (cm->nb_recursion >= UCG_CALLOUT_MAX_RECURSION) { + CALLOUT_STAT_ADD(cm, max_recursion, 1); + return; + } + + ucg_irq_lock(); + cm->nb_recursion++; + LIST_INIT(&expired_list); + LIST_INIT(&reschedule_list); + cur_time = cm->get_time(); + old_prio = cm->cur_priority; + + /* move all expired timers in a local list */ + _LIST_FOREACH_SAFE(tim, tim_next, &cm->sched_list, next) { + + diff = cur_time - tim->expire; + + /* check the expiration time (tasks are sorted) */ + if (diff < 0) + break; + + callout_dprintf("cm=%p diff=%d\r\n", cm, diff); + + /* check the priority, if it's too low, inc stats */ + if (tim->priority <= cm->cur_priority) { + if (diff < 16484) + CALLOUT_STAT_ADD(cm, delayed, 1); + else { + /* reschedule to avoid an overflow */ + CALLOUT_STAT_ADD(cm, hard_delayed, 1); + LIST_REMOVE(tim, next); + tim->expire = cur_time; + LIST_INSERT_HEAD(&reschedule_list, tim, next); + } + continue; + } + + LIST_REMOVE(tim, next); + callout_add_in_expired_list(cm, &expired_list, tim); + CALLOUT_STAT_ADD(cm, cur_scheduled, -1); + CALLOUT_STAT_ADD(cm, cur_expired, 1); + } + + /* reschedule hard_delayed timers, this does not happen usually */ + while (!LIST_EMPTY(&reschedule_list)) { + tim = LIST_FIRST(&reschedule_list); + LIST_REMOVE(tim, next); + callout_add_in_sched_list(cm, tim); + } + + /* for each timer of 'expired' list, execute callback */ + while (!LIST_EMPTY(&expired_list)) { + tim = LIST_FIRST(&expired_list); + LIST_REMOVE(tim, next); + + /* execute callback function */ + CALLOUT_STAT_ADD(cm, cur_expired, -1); + CALLOUT_STAT_ADD(cm, cur_running, 1); + tim->state = UCG_CALLOUT_STATE_RUNNING; + cm->cur_priority = tim->priority; + ucg_irq_unlock(); + tim->f(cm, tim, tim->arg); + ucg_irq_lock(); + } + + cm->cur_priority = old_prio; + cm->nb_recursion--; + ucg_irq_unlock(); +} + +/* set the current priority level */ +uint8_t ucg_callout_mgr_set_prio(struct ucg_callout_mgr *cm, uint8_t new_prio) +{ + uint8_t old_prio; + + old_prio = cm->cur_priority; + if (new_prio <= old_prio) + return old_prio; + + cm->cur_priority = new_prio; + return old_prio; +} + +/* restore the current priority level */ +void ucg_callout_mgr_restore_prio(struct ucg_callout_mgr *cm, uint8_t old_prio) +{ + cm->cur_priority = old_prio; +} + +/* dump statistics about timers */ +void ucg_callout_dump_stats(struct ucg_callout_mgr *cm) +{ +#ifdef UCG_CALLOUT_STATS + printf("Timer statistics:\r\n"); + printf(" schedule = %"PRIu32"\r\n", cm->stats.schedule); + printf(" stop = %"PRIu32"\r\n", cm->stats.stop); + printf(" manage = %"PRIu32"\r\n", cm->stats.manage); + printf(" max_recursion = %"PRIu32"\r\n", cm->stats.max_recursion); + printf(" delayed = %"PRIu32"\r\n", cm->stats.delayed); + printf(" hard_delayed = %"PRIu32"\r\n", cm->stats.hard_delayed); + + printf(" cur_scheduled = %u\r\n", cm->stats.cur_scheduled); + printf(" cur_expired = %u\r\n", cm->stats.cur_expired); + printf(" cur_running = %u\r\n", cm->stats.cur_running); +#else + printf("No timer statistics, UCG_CALLOUT_STATS is disabled\r\n"); +#endif +} diff --git a/lib/cirbuf/include/ucg_cirbuf.h b/lib/cirbuf/include/ucg_cirbuf.h new file mode 100644 index 0000000..c6bed51 --- /dev/null +++ b/lib/cirbuf/include/ucg_cirbuf.h @@ -0,0 +1,412 @@ +/* + * Copyright 2009-2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +/*- + * Copyright (c) <2010>, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - 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. + * + * - Neither the name of Intel Corporation nor the names of its + * 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. + */ + +#ifndef UCG_CIRBUF_H_ +#define UCG_CIRBUF_H_ + +#include + +/** + * A circular buffer. + */ +struct ucg_cirbuf { + unsigned maxlen; /**< Total length of the fifo (number of elements). */ + unsigned start; /**< Index of the first element. */ + unsigned len; /**< Current len of fifo. */ + char *buf; /**< Pointer to the data buffer. */ +}; + +/** + * Initialize a circular buffer. + * + * @param cbuf + * A pointer to an uninitialized circular buffer structure. + * @param buf + * The buffer used to store the data. + * @param start + * The index of head at initialization. + * @param maxlen + * The size of the buffer. + */ +void ucg_cirbuf_init(struct ucg_cirbuf *cbuf, char *buf, unsigned start, + unsigned maxlen); + +/** + * Check if the circular buffer is full. + * + * @param cbuf + * The circular buffer pointer. + * @return + * 1 if the circular buffer is full, else 0. + */ +static inline int ucg_cirbuf_is_full(const struct ucg_cirbuf *cbuf) +{ + return cbuf->len == cbuf->maxlen; +} + +/** + * Check if the circular buffer is empty. + * + * @param cbuf + * The circular buffer pointer. + * @return + * 1 if the circular buffer is empty, else 0. + */ +static inline int ucg_cirbuf_is_empty(const struct ucg_cirbuf *cbuf) +{ + return cbuf->len == 0; +} + +/** + * Get the length of data in the circular buffer. + * + * @param cbuf + * The circular buffer pointer. + * @return + * The current length of data in the circular buffer. + */ +static inline unsigned ucg_cirbuf_get_len(const struct ucg_cirbuf *cbuf) +{ + return cbuf->len; +} + +/** + * Get the size of the circular buffer. + * + * @param cbuf + * The circular buffer pointer. + * @return + * Return the maximum size of the circular buffer (used + free elements) + */ +static inline unsigned ucg_cirbuf_get_maxlen(const struct ucg_cirbuf *cbuf) +{ + return cbuf->maxlen; +} + +/** + * Get the lenght of free space in the circular buffer. + * + * @param cbuf + * The circular buffer pointer. + * @return + * Return the length of free space. + */ +static inline unsigned ucg_cirbuf_get_freelen(const struct ucg_cirbuf *cbuf) +{ + return cbuf->maxlen - cbuf->len; +} + +/** + * Iterator for a circular buffer + * + * cirbuf: struct cirbuf pointer + * i: an integer internally used in the macro + * elt: char that takes the value for each iteration + */ +#define UCG_CIRBUF_FOREACH(cirbuf, i, elt) \ + for (i = 0, elt = (cirbuf)->buf[(cirbuf)->start]; \ + i < ((cirbuf)->len); \ + i ++, elt = (cirbuf)->buf[((cirbuf)->start + i) % \ + ((cirbuf)->maxlen)]) + +/** + * Add a character at the head of the circular buffer. + * + * @param cbuf + * The circular buffer pointer. + * @param c + * The character to add. + * @return + * Return 0 on success, or a negative value on error. + */ +int ucg_cirbuf_add_head_safe(struct ucg_cirbuf *cbuf, char c); + +/** + * Add a character at the head of the circular buffer. + * + * The function does not check that there is enough free space + * in the buffer, so it has to be done by the caller. If it's + * not the case, undefined behavior will occur. + * + * @param cbuf + * The circular buffer pointer. + * @param c + * The character to add. + */ +void ucg_cirbuf_add_head(struct ucg_cirbuf *cbuf, char c); + +/** + * Add a character at the tail of the circular buffer. + * + * @param cbuf + * The circular buffer pointer. + * @param c + * The character to add. + * @return + * Return 0 on success, or a negative value on error. + */ +int ucg_cirbuf_add_tail_safe(struct ucg_cirbuf *cbuf, char c); + +/** + * Add a character at the tail of the circular buffer. + * + * The function does not check that there is enough free space + * in the buffer, so it has to be done by the caller. If it's + * not the case, undefined behavior will occur. + * + * @param cbuf + * The circular buffer pointer. + * @param c + * The character to add. + */ +void ucg_cirbuf_add_tail(struct ucg_cirbuf *cbuf, char c); + +/** + * Remove a char at the head of the circular buffer. + * + * @param cbuf + * The circular buffer pointer. + * @return + * Return 0 on success, or a negative value on error. + */ +int ucg_cirbuf_del_head_safe(struct ucg_cirbuf *cbuf); + +/** + * Remove a char at the head of the circular buffer. + * + * The function does not check that there is enough elements + * in the buffer, so it has to be done by the caller. If it's + * not the case, undefined behavior will occur. + * + * @param cbuf + * The circular buffer pointer. + */ +void ucg_cirbuf_del_head(struct ucg_cirbuf *cbuf); + +/** + * Remove a char at the tail of the circular buffer. + * + * @param cbuf + * The circular buffer pointer. + * @return + * Return 0 on success, or a negative value on error. + */ +int ucg_cirbuf_del_tail_safe(struct ucg_cirbuf *cbuf); + +/** + * Remove a char at the tail of the circular buffer. + * + * The function does not check that there is enough elements + * in the buffer, so it has to be done by the caller. If it's + * not the case, undefined behavior will occur. + * + * @param cbuf + * The circular buffer pointer. + */ +void ucg_cirbuf_del_tail(struct ucg_cirbuf *cbuf); + +/** + * Return the element at the tail of the circular buffer. + * + * The circular buffer must not be empty or an undefined character + * will be returned. + * + * @param cbuf + * The circular buffer pointer. + * @return + * The character at the tail of the circular buffer. + */ +char ucg_cirbuf_get_head(const struct ucg_cirbuf *cbuf); + +/** + * Return the element at the tail of the circular buffer. + * + * The circular buffer must not be empty or an undefined character + * will be returned. + * + * @param cbuf + * The circular buffer pointer. + * @return + * The character at the tail of the circular buffer. + */ +char ucg_cirbuf_get_tail(const struct ucg_cirbuf *cbuf); + +/** + * Add a buffer at the head of the circular buffer. + * + * Add 'n' bytes of buffer pointed by 'buf' ad the head of th + * circular buffer. + * + * @param cbuf + * The circular buffer pointer. + * @param buf + * The pointer to the buffer. + * @param n + * Number of bytes to add. + * @return + * Return 0 on success, or a negative value on error. + */ +int ucg_cirbuf_add_buf_head(struct ucg_cirbuf *cbuf, const char *buf, + unsigned n); + +/** + * Add a buffer at the tail of the circular buffer. + * + * Add 'n' bytes of buffer pointed by 'buf' ad the tail of th + * circular buffer. + * + * @param cbuf + * The circular buffer pointer. + * @param buf + * The pointer to the buffer. + * @param n + * Number of bytes to add. + * @return + * Return 0 on success, or a negative value on error. + */ +int ucg_cirbuf_add_buf_tail(struct ucg_cirbuf *cbuf, const char *buf, + unsigned n); + +/** + * Remove chars at the head of the circular buffer. + * + * @param cbuf + * The circular buffer pointer. + * @param n + * Number of bytes to remove. + * @return + * Return 0 on success, or a negative value on error. + */ +int ucg_cirbuf_del_buf_head(struct ucg_cirbuf *cbuf, unsigned n); + +/** + * Remove chars at the tail of the circular buffer. + * + * @param cbuf + * The circular buffer pointer. + * @param n + * Number of bytes to remove. + * @return + * Return 0 on success, or a negative value on error. + */ +int ucg_cirbuf_del_buf_tail(struct ucg_cirbuf *cbuf, unsigned n); + +/** + * Copy multiple bytes from the head of the circular buffer. + * + * Copy a maximum of 'n' characters from the head of the circular buffer + * into a flat buffer pointed by 'buf'. If the circular buffer is + * smaller than n, less bytes are copied. + * + * @param cbuf + * The circular buffer pointer. + * @param buf + * The pointer to the buffer. + * @param n + * Maximum number of bytes to copy. + * @return + * Return the number of copied chars. + */ +int ucg_cirbuf_get_buf_head(const struct ucg_cirbuf *cbuf, char *buf, + unsigned n); + +/** + * Copy multiple bytes from the tail of the circular buffer. + * + * Copy a maximum of 'n' characters from the tail of the circular buffer + * into a flat buffer pointed by 'buf'. If the circular buffer is + * smaller than n, less bytes are copied. + * + * @param cbuf + * The circular buffer pointer. + * @param buf + * The pointer to the buffer. + * @param n + * Maximum number of bytes to copy. + * @return + * Return the number of copied chars. + */ +int ucg_cirbuf_get_buf_tail(const struct ucg_cirbuf *cbuf, char *buf, + unsigned n); + +/** + * Set the start of the data to the index 0 of the internal buffer. + * + * After a call to this function, it is possible for the caller to + * use cbuf->buf as a linear (read-only) buffer. + * + * @param cbuf + * The circular buffer pointer. + */ +void ucg_cirbuf_align_left(struct ucg_cirbuf *cbuf); + +/** + * Set the end of the data to the last index of the internal buffer. + * + * After a call to this function, it is possible for the caller to + * use cbuf->buf as a linear (read-only) buffer. + * + * @param cbuf + * The circular buffer pointer. + */ +void ucg_cirbuf_align_right(struct ucg_cirbuf *cbuf); + +#endif /* CIRBUF_H_ */ diff --git a/lib/cirbuf/ucg_cirbuf.c b/lib/cirbuf/ucg_cirbuf.c new file mode 100644 index 0000000..05efb82 --- /dev/null +++ b/lib/cirbuf/ucg_cirbuf.c @@ -0,0 +1,624 @@ +/* + * Copyright (c) 2009-2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +/*- + * Copyright (c) <2010>, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - 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. + * + * - Neither the name of Intel Corporation nor the names of its + * 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. + */ + +#include +#include + +#include + +/* init a circular buffer */ +void +ucg_cirbuf_init(struct ucg_cirbuf *cbuf, char *buf, unsigned start, + unsigned maxlen) +{ + cbuf->maxlen = maxlen; + cbuf->len = 0; + cbuf->start = start; + cbuf->buf = buf; +} + +/* return the index of the tail (contains an empty element) */ +static unsigned +ucg_cirbuf_get_end(const struct ucg_cirbuf *cbuf) +{ + unsigned end; + + end = cbuf->start + cbuf->len; + if (end >= cbuf->maxlen) + end -= cbuf->maxlen; + + return end; +} + +/* multiple add at head */ +int +ucg_cirbuf_add_buf_head(struct ucg_cirbuf *cbuf, const char *buf, unsigned n) +{ + unsigned remain = n; + int copy_start; + unsigned copy_len; + + if (remain == 0 || remain > ucg_cirbuf_get_freelen(cbuf)) + return -EINVAL; + + copy_start = cbuf->start - remain; + if (copy_start < 0) + copy_start += cbuf->maxlen; + cbuf->start = copy_start; + cbuf->len += remain; + copy_len = remain; + if ((unsigned)copy_start + copy_len >= cbuf->maxlen) + copy_len = cbuf->maxlen - copy_start; + if (copy_len > 0) { + memcpy(cbuf->buf + copy_start, buf, copy_len); + buf += copy_len; + remain -= copy_len; + } + if (remain == 0) + return n; + + copy_len = remain; + memcpy(cbuf->buf, buf, copy_len); + + return n; +} + +/* multiple add at tail */ +int +ucg_cirbuf_add_buf_tail(struct ucg_cirbuf *cbuf, const char *buf, unsigned n) +{ + unsigned remain = n; + unsigned copy_start, copy_len; + + if (remain == 0 || remain > ucg_cirbuf_get_freelen(cbuf)) + return -EINVAL; + + copy_start = ucg_cirbuf_get_end(cbuf); + cbuf->len += remain; + copy_len = remain; + if (copy_start + copy_len >= cbuf->maxlen) + copy_len = cbuf->maxlen - copy_start; + if (copy_len > 0) { + memcpy(cbuf->buf + copy_start, buf, copy_len); + buf += copy_len; + remain -= copy_len; + } + if (remain == 0) + return n; + + copy_len = remain; + memcpy(cbuf->buf, buf, copy_len); + + return n; +} + +/* single add at head */ +static inline void +__cirbuf_add_head(struct ucg_cirbuf *cbuf, char c) +{ + unsigned start = cbuf->start; + + if (start == 0) + start = cbuf->maxlen - 1; + else + start = start - 1; + cbuf->buf[start] = c; + cbuf->start = start; + cbuf->len++; +} + +/* single add at head, checking if full first */ +int +ucg_cirbuf_add_head_safe(struct ucg_cirbuf *cbuf, char c) +{ + if (!ucg_cirbuf_is_full(cbuf)) { + __cirbuf_add_head(cbuf, c); + return 0; + } + return -EINVAL; +} + +/* single add at head */ +void +ucg_cirbuf_add_head(struct ucg_cirbuf *cbuf, char c) +{ + __cirbuf_add_head(cbuf, c); +} + +/* single add at tail */ +static inline void +__cirbuf_add_tail(struct ucg_cirbuf *cbuf, char c) +{ + unsigned end = ucg_cirbuf_get_end(cbuf); + + cbuf->buf[end] = c; + cbuf->len++; +} + +/* single add at tail, checking if full first */ +int +ucg_cirbuf_add_tail_safe(struct ucg_cirbuf *cbuf, char c) +{ + if (!ucg_cirbuf_is_full(cbuf)) { + __cirbuf_add_tail(cbuf, c); + return 0; + } + return -EINVAL; +} + +/* single add at tail */ +void +ucg_cirbuf_add_tail(struct ucg_cirbuf *cbuf, char c) +{ + __cirbuf_add_tail(cbuf, c); +} + +/* multiple delete at head */ +int +ucg_cirbuf_del_buf_head(struct ucg_cirbuf *cbuf, unsigned size) +{ + if (size == 0 || size > ucg_cirbuf_get_len(cbuf)) + return -EINVAL; + + cbuf->len -= size; + if (ucg_cirbuf_is_empty(cbuf)) { + cbuf->start += size - 1; + cbuf->start %= cbuf->maxlen; + } + else { + cbuf->start += size; + cbuf->start %= cbuf->maxlen; + } + return 0; +} + +/* multiple delete at tail */ +int +ucg_cirbuf_del_buf_tail(struct ucg_cirbuf *cbuf, unsigned size) +{ + if (size == 0 || size > ucg_cirbuf_get_len(cbuf)) + return -EINVAL; + + cbuf->len -= size; + return 0; +} + +/* single del at head */ +static inline void +__cirbuf_del_head(struct ucg_cirbuf *cbuf) +{ + cbuf->len --; + if (!ucg_cirbuf_is_empty(cbuf)) { + cbuf->start ++; + cbuf->start %= cbuf->maxlen; + } +} + +/* single del at head, checking if empty first */ +int +ucg_cirbuf_del_head_safe(struct ucg_cirbuf *cbuf) +{ + if (cbuf && !ucg_cirbuf_is_empty(cbuf)) { + __cirbuf_del_head(cbuf); + return 0; + } + return -EINVAL; +} + +/* single del at head */ +void +ucg_cirbuf_del_head(struct ucg_cirbuf *cbuf) +{ + __cirbuf_del_head(cbuf); +} + +/* single del at tail */ +static inline void +__cirbuf_del_tail(struct ucg_cirbuf *cbuf) +{ + cbuf->len--; +} + +/* single del at tail, checking if empty first */ +int +ucg_cirbuf_del_tail_safe(struct ucg_cirbuf *cbuf) +{ + if (cbuf && !ucg_cirbuf_is_empty(cbuf)) { + __cirbuf_del_tail(cbuf); + return 0; + } + return -EINVAL; +} + +/* single del at tail */ +void +ucg_cirbuf_del_tail(struct ucg_cirbuf *cbuf) +{ + __cirbuf_del_tail(cbuf); +} + +/* convert to buffer */ +int +ucg_cirbuf_get_buf_head(const struct ucg_cirbuf *cbuf, char *buf, unsigned n) +{ + unsigned remain = n; + unsigned cirbuf_len, copy_start, copy_len; + + cirbuf_len = ucg_cirbuf_get_len(cbuf); + if (remain >= cirbuf_len) + remain = cirbuf_len; + + if (remain == 0) + return 0; + + copy_start = cbuf->start; + copy_len = remain; + if (copy_start + copy_len >= cbuf->maxlen) + copy_len = cbuf->maxlen - copy_start; + if (copy_len > 0) { + memcpy(buf, cbuf->buf + copy_start, copy_len); + buf += copy_len; + remain -= copy_len; + } + if (remain == 0) + return n; + + copy_len = remain; + memcpy(buf, cbuf->buf, copy_len); + + return n; +} + +/* convert to buffer */ +int +ucg_cirbuf_get_buf_tail(const struct ucg_cirbuf *cbuf, char *buf, unsigned n) +{ + unsigned remain = n; + int copy_start; + unsigned cirbuf_len, copy_len; + + cirbuf_len = ucg_cirbuf_get_len(cbuf); + if (remain >= cirbuf_len) + remain = cirbuf_len; + + if (remain == 0) + return 0; + + copy_start = ucg_cirbuf_get_end(cbuf) - remain; + if (copy_start < 0) + copy_start += cbuf->maxlen; + copy_len = remain; + if ((unsigned)copy_start + copy_len >= cbuf->maxlen) + copy_len = cbuf->maxlen - copy_start; + if (copy_len > 0) { + memcpy(buf, cbuf->buf + copy_start, copy_len); + buf += copy_len; + remain -= copy_len; + } + if (remain == 0) + return n; + + copy_len = remain; + memcpy(buf, cbuf->buf, copy_len); + + return n; +} + +/* get head */ +char +ucg_cirbuf_get_head(const struct ucg_cirbuf *cbuf) +{ + return cbuf->buf[cbuf->start]; +} + +/* get tail */ +char +ucg_cirbuf_get_tail(const struct ucg_cirbuf *cbuf) +{ + unsigned end; + + /* should not happen */ + if (cbuf->len == 0) + return 0; + + end = cbuf->start + cbuf->len - 1; + if (end >= cbuf->maxlen) + end -= cbuf->maxlen; + return cbuf->buf[end]; +} + +static void +__ucg_cirbuf_shift(struct ucg_cirbuf *cbuf, unsigned n) +{ + char tmp, tmp2; + unsigned start, cur, min; + + start = 0; + cur = 0; + tmp = cbuf->buf[0]; + min = cbuf->maxlen; + + while (1) { + cur = cur + n; + if (cur >= cbuf->maxlen) + cur -= cbuf->maxlen; + tmp2 = cbuf->buf[cur]; + cbuf->buf[cur] = tmp; + tmp = tmp2; + if (cur == start) { + if ((cur + 1) == min) + break; + cur++; + tmp = cbuf->buf[cur]; + start = cur; + } else if (cur < min) { + min = cur; + } + } + + cbuf->start += n; + if (cbuf->start >= cbuf->maxlen) + cbuf->start -= cbuf->maxlen; +} + +void ucg_cirbuf_align_left(struct ucg_cirbuf *cbuf) +{ + __ucg_cirbuf_shift(cbuf, cbuf->maxlen - cbuf->start); +} + +void ucg_cirbuf_align_right(struct ucg_cirbuf *cbuf) +{ + unsigned end = ucg_cirbuf_get_end(cbuf); + __ucg_cirbuf_shift(cbuf, cbuf->maxlen - end); +} + +#ifdef TEST_CIRBUF +#include +#include + +void dump_it(struct ucg_cirbuf * cbuf) +{ + unsigned i; + int idx; + char e; + + printf("sta=%2.2d len=%2.2d/%2.2d { ", + cbuf->start, + ucg_cirbuf_get_len(cbuf), + ucg_cirbuf_get_maxlen(cbuf)); + + for (i = 0; i < ucg_cirbuf_get_maxlen(cbuf); i++) { + idx = i - cbuf->start; + if (idx < 0) + idx += cbuf->maxlen; + if (idx < (int)ucg_cirbuf_get_len(cbuf)) + printf("%2.2x, ", cbuf->buf[i] & 0xFF); + else + printf("XX, "); + } + printf("} -> "); + + printf("[ "); + UCG_CIRBUF_FOREACH(cbuf, i, e) { + printf("%2.2x, ", e & 0xFF); + } + printf("]\n"); +} + +int main(void) +{ + unsigned i; + struct ucg_cirbuf my_fifo; + char fifo_buf[16]; + + char buf1[] = { 0x10, 0x11, 0x12 }; + char buf2[] = { 0x20, 0x21, 0x22, 0x23 }; + + char tmp_buf[16]; + char ref_buf[] = { 0x20, 0x21, 0x22, 0x23, 0x01, 0x10, 0x11, 0x12 }; + + /* Test 1 */ + + printf("Test 1\n"); + + ucg_cirbuf_init(&my_fifo, fifo_buf, 0, 4); + assert(ucg_cirbuf_is_empty(&my_fifo)); + assert(!ucg_cirbuf_is_full(&my_fifo)); + dump_it(&my_fifo); + + ucg_cirbuf_add_tail(&my_fifo, 1); + assert(ucg_cirbuf_get_head(&my_fifo) == 1); + assert(ucg_cirbuf_get_tail(&my_fifo) == 1); + dump_it(&my_fifo); + + + ucg_cirbuf_add_tail(&my_fifo, 2); + assert(!ucg_cirbuf_is_empty(&my_fifo)); + assert(!ucg_cirbuf_is_full(&my_fifo)); + dump_it(&my_fifo); + + ucg_cirbuf_add_tail(&my_fifo, 3); + assert(ucg_cirbuf_get_head(&my_fifo) == 1); + assert(ucg_cirbuf_get_tail(&my_fifo) == 3); + dump_it(&my_fifo); + + ucg_cirbuf_add_tail(&my_fifo, 4); + assert(!ucg_cirbuf_is_empty(&my_fifo)); + assert(ucg_cirbuf_is_full(&my_fifo)); + dump_it(&my_fifo); + + ucg_cirbuf_del_tail(&my_fifo); + dump_it(&my_fifo); + assert(ucg_cirbuf_get_tail(&my_fifo) == 3); + assert(ucg_cirbuf_get_head(&my_fifo) == 1); + + ucg_cirbuf_del_head(&my_fifo); + assert(ucg_cirbuf_get_tail(&my_fifo) == 3); + assert(ucg_cirbuf_get_head(&my_fifo) == 2); + dump_it(&my_fifo); + + ucg_cirbuf_del_head(&my_fifo); + assert(ucg_cirbuf_get_tail(&my_fifo) == 3); + assert(ucg_cirbuf_get_head(&my_fifo) == 3); + dump_it(&my_fifo); + + ucg_cirbuf_del_head(&my_fifo); + assert(ucg_cirbuf_is_empty(&my_fifo)); + dump_it(&my_fifo); + + + /* Test 2 */ + + printf("Test 2\n"); + + ucg_cirbuf_init(&my_fifo, fifo_buf, 2, 4); + dump_it(&my_fifo); + + ucg_cirbuf_add_head(&my_fifo, 4); + assert(ucg_cirbuf_get_head(&my_fifo) == 4); + assert(ucg_cirbuf_get_tail(&my_fifo) == 4); + dump_it(&my_fifo); + + + ucg_cirbuf_add_head(&my_fifo, 3); + assert(!ucg_cirbuf_is_empty(&my_fifo)); + assert(!ucg_cirbuf_is_full(&my_fifo)); + dump_it(&my_fifo); + + ucg_cirbuf_add_head(&my_fifo, 2); + assert(ucg_cirbuf_get_head(&my_fifo) == 2); + assert(ucg_cirbuf_get_tail(&my_fifo) == 4); + dump_it(&my_fifo); + + ucg_cirbuf_add_head(&my_fifo, 1); + assert(!ucg_cirbuf_is_empty(&my_fifo)); + assert(ucg_cirbuf_is_full(&my_fifo)); + dump_it(&my_fifo); + + + /* Test 3 */ + + printf("Test 3\n"); + + for (i = 0; i < 16; i++) { + ucg_cirbuf_init(&my_fifo, fifo_buf, i, 16); + dump_it(&my_fifo); + ucg_cirbuf_add_buf_head(&my_fifo, buf1, sizeof(buf1)); + dump_it(&my_fifo); + ucg_cirbuf_add_head(&my_fifo, 1); + dump_it(&my_fifo); + ucg_cirbuf_add_buf_head(&my_fifo, buf2, sizeof(buf2)); + dump_it(&my_fifo); + ucg_cirbuf_get_buf_head(&my_fifo, tmp_buf, sizeof(tmp_buf)); + assert(memcmp(tmp_buf, ref_buf, sizeof(ref_buf)) == 0); + } + + /* Test 4 */ + + printf("Test 4\n"); + + for (i = 0; i < 16; i++) { + ucg_cirbuf_init(&my_fifo, fifo_buf, i, 16); + dump_it(&my_fifo); + ucg_cirbuf_add_buf_tail(&my_fifo, buf2, sizeof(buf2)); + dump_it(&my_fifo); + ucg_cirbuf_add_tail(&my_fifo, 1); + dump_it(&my_fifo); + ucg_cirbuf_add_buf_tail(&my_fifo, buf1, sizeof(buf1)); + dump_it(&my_fifo); + ucg_cirbuf_get_buf_tail(&my_fifo, tmp_buf, sizeof(tmp_buf)); + assert(memcmp(tmp_buf, ref_buf, sizeof(ref_buf)) == 0); + + printf("align left\n"); + ucg_cirbuf_align_left(&my_fifo); + dump_it(&my_fifo); + ucg_cirbuf_get_buf_tail(&my_fifo, tmp_buf, sizeof(tmp_buf)); + assert(memcmp(tmp_buf, ref_buf, sizeof(ref_buf)) == 0); + assert(my_fifo.start == 0); + + printf("align right\n"); + ucg_cirbuf_align_right(&my_fifo); + dump_it(&my_fifo); + ucg_cirbuf_get_buf_tail(&my_fifo, tmp_buf, sizeof(tmp_buf)); + assert(memcmp(tmp_buf, ref_buf, sizeof(ref_buf)) == 0); + assert(my_fifo.start + my_fifo.len == my_fifo.maxlen); + } + + /* Test 5 */ + + printf("Test 5\n"); + + ucg_cirbuf_init(&my_fifo, fifo_buf, 10, 16); + dump_it(&my_fifo); + i = 0; + while (ucg_cirbuf_add_tail_safe(&my_fifo, i) == 0) + i++; + dump_it(&my_fifo); + ucg_cirbuf_del_buf_tail(&my_fifo, 10); + dump_it(&my_fifo); + assert(ucg_cirbuf_get_len(&my_fifo) == 6); + assert(ucg_cirbuf_del_buf_tail(&my_fifo, 10) != 0); + assert(ucg_cirbuf_get_tail(&my_fifo) == 5); + assert(ucg_cirbuf_get_head(&my_fifo) == 0); + + return 0; +} +#endif diff --git a/lib/cmd/include/ucg_cmd.h b/lib/cmd/include/ucg_cmd.h new file mode 100644 index 0000000..c9e80ec --- /dev/null +++ b/lib/cmd/include/ucg_cmd.h @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2009-2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +/*- + * Copyright (c) <2010>, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - 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. + * + * - Neither the name of Intel Corporation nor the names of its + * 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. + */ + +#ifndef UCG_CMD_H_ +#define UCG_CMD_H_ + +#include +#include +#ifdef UCG_CMD_HAVE_TERMIOS +#include +#endif + +#include "ucg_cmd_parse.h" +#include "ucg_cmd_rdline.h" + +/** + * A command line structure. + */ +struct ucg_cmd { + ucg_cmd_ctx_t *ctx; /**< The list of commands for this context. */ + struct ucg_rdline rdl; /**< The associated rdline structure. */ + char prompt[UCG_RDLINE_PROMPT_SIZE]; /**< The command line prompt. */ +#ifdef UCG_CMD_HAVE_TERMIOS + struct termios oldterm; /**< The old termios info */ +#endif + void *opaque; /**< A user opaque pointer. */ +}; + +/** + * Allocate and initialize a new command line structure + * + * Allocate and initialize a new command line structure, using the + * specified context, prompt and input/output streams. + * + * Once unused, the command line structure should be freed using + * ucg_cmd_free(). + * + * @param ctx + * The command line context. + * @param prompt + * The command line prompt. The string is copied in the ucg_cmd + * structure. The prompt must be smaller than UCG_RDLINE_PROMPT_SIZE. + * @param f_in + * The input stream. + * @param f_out + * The output stream. + * @return + * The newly allocated command line structure. + */ +struct ucg_cmd *ucg_cmd_new(ucg_cmd_ctx_t *ctx, + const char *prompt, FILE *f_in, FILE *f_out); + +/** + * Initialize a new command line structure + * + * Initialize a command line structure, using the specified prompt and + * specified input/output streams. + * + * @param cl + * The uninitialize command line structure + * @param ctx + * The command line context + * @param prompt + * The command line prompt. The string is copied in the ucg_cmd + * structure. The prompt must be smaller than UCG_RDLINE_PROMPT_SIZE. + * @param f_in + * The input stream + * @param f_out + * The output stream + */ +void ucg_cmd_init(struct ucg_cmd *cl, ucg_cmd_ctx_t *ctx, + const char *prompt, FILE *f_in, FILE *f_out); + +/** + * Set the prompt of the given command line. + */ +void ucg_cmd_set_prompt(struct ucg_cmd *cl, const char *prompt); + +/** + * Free a previously allocated command line + * + * The structure pointed by cl is freed. The streams f_in and f_out are + * closed, except if it's stdin, stdout, or stderr. Note: the user + * should call ucg_cmd_quit() before calling this function. + * + * @param cl + * The command line pointer. + */ +void ucg_cmd_free(struct ucg_cmd *cl); + +/** + * Parse a file use the given cmd context + * + * The output file descriptor (used for instance by cmd_printf) is + * given as a parameter. It can be -1 if no output is required. + */ +struct ucg_cmd *ucg_cmd_file_new(ucg_cmd_ctx_t *ctx, + const char *prompt, const char *path, FILE *f_out); + +/** + * Print data on the output of the command line + * + * This function is a wrapper to rdline_printf(). + * + * @param cl + * The command line pointer. + * @param fmt + * The format string, followed by variable arguments. + * @return + * On success, return the number of characters printed (not including + * the trailing '\0'). On error, a negative value is returned. + */ +int ucg_cmd_printf(struct ucg_cmd *cl, const char *fmt, ...); + +/** + * Push an input buffer to the command line. + * + * Typically, this function is called by ucg_cmd_interact() to send the + * input characters to the command line process. It can also be called + * by a user, for instance when new data is received from an input + * socket. + * + * @param cl + * The command line pointer. + * @param buf + * The address of the input buffer. + * @param size + * The len of the input buffer. + * @return + * The function returns the number of processed characters, or a + * negative value on error (ex: EOF reached or command line exited). + */ +int ucg_cmd_in(struct ucg_cmd *cl, const char *buf, int size); + +/* flags for ucg_cmd_interact */ +#define UCG_CMD_F_IGNORE_EOF 0x0001 /**< ignore eof when polling */ + +/** + * Start command line on configured file descriptor. This function + * loops until the user explicitelly call ucg_cmd_quit(), or if the + * input fd reaches EOF. + * + * @param cl + * The command line pointer + * @param flags + * Any flags from UCG_CMD_F_* + * - UCG_CMD_F_IGNORE_EOF: on eof, clear error and continue polling + */ +void ucg_cmd_interact(struct ucg_cmd *cl, unsigned flags); + +/** + * Stop a running command line. + * + * Actually it will call ucg_rdline_quit() on the associated rdline. + * + * @param cl + * The command line pointer. + */ +void ucg_cmd_quit(struct ucg_cmd *cl); + +#endif /* UCG_CMD_H_ */ diff --git a/lib/cmd/include/ucg_cmd_parse.h b/lib/cmd/include/ucg_cmd_parse.h new file mode 100644 index 0000000..37d93c1 --- /dev/null +++ b/lib/cmd/include/ucg_cmd_parse.h @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2009-2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +/*- + * Copyright (c) <2010>, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - 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. + * + * - Neither the name of Intel Corporation nor the names of its + * 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. + */ + +#ifndef UCG_CMD_PARSE_H_ +#define UCG_CMD_PARSE_H_ + +#include + +#ifndef offsetof +#define offsetof(type, field) ((size_t) &( ((type *)0)->field) ) +#endif + +// XXX config +#define UCG_CMD_MAX_TOKEN_SIZE 32 /* including '\0' */ +#define UCG_CMD_MAX_DSTBUF_SIZE 128 + +/** + * A token header. + * + * Stores a pointer to the ops struct, and the offset: the place to + * write the parsed result in the destination structure. + */ +struct ucg_cmd_tk_hdr { + struct ucg_cmd_tk_ops *ops; + unsigned int offset; +}; +typedef struct ucg_cmd_tk_hdr ucg_cmd_tk_hdr_t; + +/** + * Operations on token. + */ +struct ucg_cmd_tk_ops { + /** + * parse() converts a buffer containing a token into its parsed + * value. Ex: an integer string is converted into its integer + * value. The result is stored in "result" whose size is + * "res_size". It returns 0 on success and a negative value on + * error. + */ + int (*parse)(ucg_cmd_tk_hdr_t *tk, const char *line, + void *result, unsigned int res_size); + + /** + * complete_start() prepares a completion operation. The + * "opaque" arg is an opaque pointer that will be given to + * complete_iterate() function. It can be used to store private + * data for this completion. For each complete_start() call, the + * user must call complete_end() at the end of iterations (if + * defined) in case some data have to be freed. + * + * Return a negative value if completion is not possible, or 0 + * on success. + */ + int (*complete_start)(ucg_cmd_tk_hdr_t *tk, const char *line, + void **opaque); + + /** + * complete_iterate() copy in "dst" buffer the next possible + * completion for this token. Return 0 on success (final + * completion = completion until the end of the token), 1 if + * it's an intermediate completion (token not fully completed), + * or a negative value on error (or when there is no more + * completion). Refer to ucg_cmd_complete_string_iterate() for + * an example. + */ + int (*complete_iterate)(ucg_cmd_tk_hdr_t *tk, void **opaque, + char *dst, unsigned int dst_size); + + /** + * complete_end() is called when the iteration on this token is + * finished, this function should free all things allocated + * during complete_start(). + */ + void (*complete_end)(ucg_cmd_tk_hdr_t *tk, void **opaque); + + /** + * help() fills the dstbuf with the help for the token. It returns + * -1 on error and 0 on success. + */ + int (*help)(ucg_cmd_tk_hdr_t *tk, char *dst, unsigned int dst_size); +}; + +struct ucg_cmd; + +/** + * Store a command, defined by a list of tokens and a callback function. + */ +struct ucg_cmd_inst { + /** callback function when the commend is parsed */ + void (*f)(void *parsed_result, struct ucg_cmd *cl, void *opaque); + void *data; /**< opaque pointer given as is to the callback */ + char *help_str; /**< help for this command */ + ucg_cmd_tk_hdr_t *tokens[]; /**< list of tokens */ +}; +typedef struct ucg_cmd_inst ucg_cmd_inst_t; + +/** + * A context is a list of commands. + */ +struct ucg_cmd_ctx { + const char *name; /**< The name of the context */ + const ucg_cmd_inst_t *insts[]; /**< List of commands */ +}; +typedef struct ucg_cmd_ctx ucg_cmd_ctx_t; + +/* return status for parsing */ +#define UCG_CMD_PARSE_SUCCESS 0 +#define UCG_CMD_PARSE_EMPTY -1 +#define UCG_CMD_PARSE_NOMATCH -2 +#define UCG_CMD_PARSE_AMBIGUOUS -3 +#define UCG_CMD_PARSE_UNTERMINATED_QUOTE -4 + +/** + * Try to parse a buffer according to the specified context. The + * argument linebuf must end with "\n\0". + * + * The function returns: + * - UCG_CMD_PARSE_SUCCESS (0) on success + * - UCG_CMD_PARSE_EMPTY if there is nothing to parse + * - UCG_CMD_PARSE_NOMATCH if line does not match any command + * - UCG_CMD_PARSE_AMBIGUOUS if several commands match + * - UCG_CMD_PARSE_UNTERMINATED_QUOTE if a quote is used incorrectly + */ +int ucg_cmd_parse(struct ucg_cmd *cl, const char *linebuf, void *opaque); + +/* return status for completion */ +#define UCG_CMD_COMPLETE_APPEND 0 +#define UCG_CMD_COMPLETE_NONE -1 +#define UCG_CMD_COMPLETE_MANY -2 + +/** + * ucg_cmd_complete() tries to complete the buffer given as a parameter. + * + * It returns: + * - UCG_CMD_COMPLETE_APPEND (0) on success, when a completion is + * done (one possible choice). In this case, the chars are + * appended in dst buffer. + * - UCG_CMD_COMPLETE_NONE: error, no possible completion + * - UCG_CMD_COMPLETE_MANY: error, many possble completions, need to call + * ucg_cmd_help() function to see all the possibilities. + */ +int ucg_cmd_complete(struct ucg_cmd *cl, const char *buf, char *dst, + unsigned int size); + +/** + * Display a contextual help. + * + * @param cl + * The command line pointer + * @param line + * The current line buffer. + */ +void ucg_cmd_help(struct ucg_cmd *cl, const char *line); + +/** + * Check if the character ends a token + * + * @param c + * The character. + * @return + * True if (c == '\0' || iscomment(c) || isblank(c) || + * isendofline(c)) + */ +int ucg_cmd_isendoftoken(char c); + +/** + * Quote a string and escape original quotes + * + * @param dst + * The destination buffer. + * @param dstlen + * The length of the destination buffer. + * @param src + * The source buffer. + * @return + * Return 0 on success, a negative value on error. + */ +int ucg_cmd_quote_token(char *dst, unsigned dstlen, const char *src); + +/** + * Get one token + * + * The function removes quotes (if any) and stop copying when the end of + * token is reached. The destination buffer is '\0'-terminated. + * + * @param dst + * The destination buffer. + * @param dstlen + * The length of the destination buffer. + * @param src + * The source buffer. + * @return + * Return the number of "consumed" bytes from the source buffer, or + * a negative value on error + */ +int ucg_cmd_get_token(char *dst, unsigned dstlen, const char *src); + +#endif /* UCG_CMD_PARSE_H_ */ diff --git a/lib/cmd/include/ucg_cmd_parse_etheraddr.h b/lib/cmd/include/ucg_cmd_parse_etheraddr.h new file mode 100644 index 0000000..88c3329 --- /dev/null +++ b/lib/cmd/include/ucg_cmd_parse_etheraddr.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2009-2015, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +/*- + * Copyright (c) <2010>, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - 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. + * + * - Neither the name of Intel Corporation nor the names of its + * 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. + */ + +#ifndef _PARSE_ETHERADDR_H_ +#define _PARSE_ETHERADDR_H_ + +#include "ucg_cmd_parse.h" + +struct ucg_cmd_tk_etheraddr_data { + uint8_t flags; +}; + +struct ucg_cmd_tk_etheraddr { + struct ucg_cmd_tk_hdr hdr; +}; +typedef struct ucg_cmd_tk_etheraddr ucg_cmd_tk_etheraddr_t; + +extern struct ucg_cmd_tk_ops ucg_cmd_tk_etheraddr_ops; + +#define UCG_CMD_TK_ETHERADDR(structure, field) \ +{ \ + .hdr = { \ + .ops = &cmd_token_etheraddr_ops, \ + .offset = offsetof(structure, field), \ + }, \ +} + + +#endif /* _PARSE_ETHERADDR_H_ */ diff --git a/lib/cmd/include/ucg_cmd_parse_file.h b/lib/cmd/include/ucg_cmd_parse_file.h new file mode 100644 index 0000000..937c7a0 --- /dev/null +++ b/lib/cmd/include/ucg_cmd_parse_file.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2009-2015, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +#ifndef UCG_CMD_PARSE_FILE_H_ +#define UCG_CMD_PARSE_FILE_H_ + +#include "ucg_cmd_parse.h" + +/* size of a parsed file */ +#define UCG_FILENAME_SIZE UCG_CMD_MAX_TOKEN_SIZE + +typedef char ucg_cmd_filename_t[UCG_FILENAME_SIZE]; + +#define PARSE_FILE_F_CREATE 0x01 /* file does not necessarilly exist */ +#define PARSE_FILE_F_DIRECTORY 0x02 /* must be a directory */ +struct ucg_cmd_tk_file_data { + int flags; +}; + +struct ucg_cmd_tk_file { + struct ucg_cmd_tk_hdr hdr; + struct ucg_cmd_tk_file_data file_data; +}; +typedef struct ucg_cmd_tk_file ucg_cmd_tk_file_t; + +extern struct ucg_cmd_tk_ops ucg_cmd_tk_file_ops; + +#define UCG_CMD_TK_FILE(structure, field, node_flags) \ +{ \ + .hdr = { \ + .ops = &ucg_cmd_tk_file_ops, \ + .offset = offsetof(structure, field), \ + }, \ + .file_data = { \ + .flags = node_flags, \ + }, \ +} + +#endif /* UCG_CMD_PARSE_FILE_H_ */ diff --git a/lib/cmd/include/ucg_cmd_parse_ipaddr.h b/lib/cmd/include/ucg_cmd_parse_ipaddr.h new file mode 100644 index 0000000..8eee078 --- /dev/null +++ b/lib/cmd/include/ucg_cmd_parse_ipaddr.h @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2009-2015, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +/*- + * Copyright (c) <2010>, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - 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. + * + * - Neither the name of Intel Corporation nor the names of its + * 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. + */ + +#ifndef UCG_CMD_PARSE_IPADDR_H_ +#define UCG_CMD_PARSE_IPADDR_H_ + +#include "ucg_cmd_parse.h" + +#define UCG_CMD_IPADDR_V4 0x01 +#define UCG_CMD_IPADDR_V6 0x02 +#define UCG_CMD_IPADDR_NETWORK 0x04 + +struct ucg_cmd_ipaddr { + uint8_t family; + union { + struct in_addr ipv4; + struct in6_addr ipv6; + } addr; + unsigned int prefixlen; /* in case of network only */ +}; +typedef struct ucg_cmd_ipaddr ucg_cmd_ipaddr_t; + +struct ucg_cmd_tk_ipaddr_data { + uint8_t flags; +}; + +struct ucg_cmd_tk_ipaddr { + struct ucg_cmd_tk_hdr hdr; + struct ucg_cmd_tk_ipaddr_data ipaddr_data; +}; +typedef struct ucg_cmd_tk_ipaddr ucg_cmd_tk_ipaddr_t; + +extern struct ucg_cmd_tk_ops ucg_cmd_tk_ipaddr_ops; + +#define UCG_CMD_TK_IPADDR(structure, field) \ +{ \ + .hdr = { \ + .ops = &ucg_cmd_tk_ipaddr_ops, \ + .offset = offsetof(structure, field), \ + }, \ + .ipaddr_data = { \ + .flags = UCG_CMD_IPADDR_V4 | \ + UCG_CMD_IPADDR_V6, \ + }, \ +} + +#define UCG_CMD_TK_IPV4(structure, field) \ +{ \ + .hdr = { \ + .ops = &ucg_cmd_tk_ipaddr_ops, \ + .offset = offsetof(structure, field), \ + }, \ + .ipaddr_data = { \ + .flags = UCG_CMD_IPADDR_V4, \ + }, \ +} + +#define UCG_CMD_TK_IPV6(structure, field) \ +{ \ + .hdr = { \ + .ops = &ucg_cmd_tk_ipaddr_ops, \ + .offset = offsetof(structure, field), \ + }, \ + .ipaddr_data = { \ + .flags = UCG_CMD_IPADDR_V6, \ + }, \ +} + +#define UCG_CMD_TK_IPNET(structure, field) \ +{ \ + .hdr = { \ + .ops = &ucg_cmd_tk_ipaddr_ops, \ + .offset = offsetof(structure, field), \ + }, \ + .ipaddr_data = { \ + .flags = UCG_CMD_IPADDR_V4 | \ + UCG_CMD_IPADDR_V6 | \ + UCG_CMD_IPADDR_NETWORK, \ + }, \ +} + +#define UCG_CMD_TK_IPV4NET(structure, field) \ +{ \ + .hdr = { \ + .ops = &ucg_cmd_tk_ipaddr_ops, \ + .offset = offsetof(structure, field), \ + }, \ + .ipaddr_data = { \ + .flags = UCG_CMD_IPADDR_V4 | \ + UCG_CMD_IPADDR_NETWORK, \ + }, \ +} + +#define UCG_CMD_TK_IPV6NET(structure, field) \ +{ \ + .hdr = { \ + .ops = &ucg_cmd_tk_ipaddr_ops, \ + .offset = offsetof(structure, field), \ + }, \ + .ipaddr_data = { \ + .flags = UCG_CMD_IPADDR_V4 | \ + UCG_CMD_IPADDR_NETWORK, \ + }, \ +} + +#endif /* UCG_CMD_PARSE_IPADDR_H_ */ diff --git a/lib/cmd/include/ucg_cmd_parse_num.h b/lib/cmd/include/ucg_cmd_parse_num.h new file mode 100644 index 0000000..d5292d1 --- /dev/null +++ b/lib/cmd/include/ucg_cmd_parse_num.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2009-2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +/*- + * Copyright (c) <2010>, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - 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. + * + * - Neither the name of Intel Corporation nor the names of its + * 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. + */ + +#ifndef UCG_CMD_PARSE_NUM_H_ +#define UCG_CMD_PARSE_NUM_H_ + +#include "ucg_cmd_parse.h" + +enum ucg_cmd_numtype { + UINT8 = 0, + UINT16, + UINT32, + UINT64, + INT8, + INT16, + INT32, + INT64 +#ifndef NO_PARSE_FLOAT + ,FLOAT +#endif +}; + +struct ucg_cmd_tk_num_data { + enum ucg_cmd_numtype type; +}; + +struct ucg_cmd_tk_num { + struct ucg_cmd_tk_hdr hdr; + struct ucg_cmd_tk_num_data num_data; +}; +typedef struct ucg_cmd_tk_num ucg_cmd_tk_num_t; + +extern struct ucg_cmd_tk_ops ucg_cmd_tk_num_ops; + +#define UCG_CMD_TK_NUM(structure, field, numtype) \ +{ \ + .hdr = { \ + .ops = &ucg_cmd_tk_num_ops, \ + .offset = offsetof(structure, field), \ + }, \ + .num_data = { \ + .type = numtype, \ + }, \ +} + +#endif /* UCG_CMD_PARSE_NUM_H_ */ diff --git a/lib/cmd/include/ucg_cmd_parse_string.h b/lib/cmd/include/ucg_cmd_parse_string.h new file mode 100644 index 0000000..a127fab --- /dev/null +++ b/lib/cmd/include/ucg_cmd_parse_string.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2009-2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +/*- + * Copyright (c) <2010>, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - 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. + * + * - Neither the name of Intel Corporation nor the names of its + * 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. + */ + +#ifndef UCG_CMD_PARSE_STRING_H_ +#define UCG_CMD_PARSE_STRING_H_ + +#include "ucg_cmd_parse.h" + +/* size of a parsed string */ +#define UCG_STR_TOKEN_SIZE UCG_CMD_MAX_TOKEN_SIZE + +typedef char ucg_cmd_fixed_string_t[UCG_STR_TOKEN_SIZE]; + +struct ucg_cmd_tk_string_data { + const char *str; +}; + +struct ucg_cmd_tk_string { + struct ucg_cmd_tk_hdr hdr; + struct ucg_cmd_tk_string_data string_data; +}; +typedef struct ucg_cmd_tk_string ucg_cmd_tk_string_t; + +extern struct ucg_cmd_tk_ops ucg_cmd_tk_string_ops; + +#define UCG_CMD_TK_STRING(structure, field, string) \ +{ \ + .hdr = { \ + .ops = &ucg_cmd_tk_string_ops, \ + .offset = offsetof(structure, field), \ + }, \ + .string_data = { \ + .str = string, \ + }, \ +} + +#endif /* UCG_CMD_PARSE_STRING_H_ */ diff --git a/lib/cmd/include/ucg_cmd_rdline.h b/lib/cmd/include/ucg_cmd_rdline.h new file mode 100644 index 0000000..564835b --- /dev/null +++ b/lib/cmd/include/ucg_cmd_rdline.h @@ -0,0 +1,452 @@ +/* + * Copyright (c) 2009-2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +/*- + * Copyright (c) <2010>, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - 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. + * + * - Neither the name of Intel Corporation nor the names of its + * 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. + */ + +#ifndef CMD_RDLINE_H_ +#define CMD_RDLINE_H_ + +/** + * This file is a small equivalent to the GNU readline library, it was + * originally designed for very small systems (an 8-bits Atmel AVR + * microcontroller), but the library can now run on many emmbedded + * systems with or without OS. + * + * Obviously, it does not support as many things as the GNU readline, + * but at least it supports some interresting features like a kill + * buffer and a command history. + * + * It also have a feature that does not have the GNU readline (as far + * as I know): it is possible to have several instances of readline + * running at the same time, even on a monothread program, since it + * works with callbacks. + */ + +#include +#include + +#include +#include + +/* configuration */ +#define UCG_RDLINE_BUF_SIZE 32 +#define UCG_RDLINE_PROMPT_SIZE 16 +#define UCG_RDLINE_VT100_BUF_SIZE 8 +#define UCG_RDLINE_HISTORY_BUF_SIZE 64 +#define UCG_RDLINE_MAX_LINES 23 /* pager */ + +enum ucg_rdline_status { + UCG_RDLINE_STOPPED, + UCG_RDLINE_RUNNING, + UCG_RDLINE_EXITED +}; + +struct ucg_rdline; + +/** + * type of callback given to ucg_rdline_help() to display the content of + * the help. The first argument is the rdline pointer. The other args + * are buffer and size. + */ +typedef int (ucg_rdline_printf_t)(struct ucg_rdline *rdl, + const char *fmt, ...); + +/** + * type of callback invoked when a command is parsed. "rdl" is a pointer + * to the ucg_rdline structure, "line" is a pointer to the current line + * string ('\0' terminated). + */ +typedef void (ucg_rdline_validate_t)(struct ucg_rdline *rdl, + const char *line); + +/** + * type of callback invoked when a completion is requested. "rdl" is a + * pointer to the ucg_rdline structure, "line" is a pointer to the + * current line string ('\0' terminated). The characters to append + * are written to dstbuf. + * Return 0 on success: dstbuf contains the characters to append to + * the current line ('\0' terminated) + * Else return a negative value if no completion is performed. + */ +typedef int (ucg_rdline_complete_t)(struct ucg_rdline *rdl, const char *line, + char *dstbuf, unsigned int dstsize); + +/** + * callback invoked when a the help is requested. "rdl" is a pointer to + * the ucg_rdline structure, "line" is a pointer to the current line string + * ('\0' terminated). + */ +typedef void (ucg_rdline_help_t)(struct ucg_rdline *rdl, const char *line); + +typedef void (ucg_rdline_pager_cb_t)(struct ucg_rdline *, void *); + +struct ucg_rdline { + enum ucg_rdline_status status; + FILE *f_in; + FILE *f_out; + + /* rdline bufs */ + struct ucg_cirbuf left; + struct ucg_cirbuf right; + char left_buf[UCG_RDLINE_BUF_SIZE+1]; /* reserve 1 char for the \0 */ + char right_buf[UCG_RDLINE_BUF_SIZE]; + + char prompt[UCG_RDLINE_PROMPT_SIZE]; + +#ifndef UCG_CMD_NO_RDLINE_KILL_BUF + char kill_buf[UCG_RDLINE_BUF_SIZE]; + unsigned int kill_size; +#endif + +#ifndef UCG_CMD_NO_RDLINE_HISTORY + /* history */ + struct ucg_cirbuf history; + char history_buf[UCG_RDLINE_HISTORY_BUF_SIZE]; + int history_cur_line; +#endif + + /* callbacks and func pointers */ + ucg_rdline_validate_t *validate; + ucg_rdline_complete_t *complete; + ucg_rdline_help_t *help; + + /* vt100 parser */ + struct ucg_cmd_vt100 vt100; + + /* opaque pointer */ + void *opaque; + +#ifndef UCG_CMD_NO_PAGER + char *pager_buf; /* buffer used to store paged data */ + int pager_len; /* total len of buffer */ + int pager_off; /* offset of next data */ + int pager_lines; /* number of lines displayed */ + ucg_rdline_pager_cb_t *pager_cb; /* callback once paging is finished */ + void *pager_arg; /* argument of callback */ + int pager_ret; /* saved return value */ +#endif +}; + +/** + * Init fields for a struct ucg_rdline. + * + * @param rdl A pointer to an uninitialized struct ucg_rdline + * @param fd_in + * Input file descriptor + * @param fd_out + * Output file descriptor + * @param validate + * A pointer to the function to execute when the user validates the + * buffer. + * @param complete + * A pointer to the function to execute when the user completes the + * buffer. + * @param help + * A pointer to the function to execute when the user ask for + * contextual help. + */ +void ucg_rdline_init(struct ucg_rdline *rdl, + FILE *f_in, FILE *f_out, + ucg_rdline_validate_t *validate, + ucg_rdline_complete_t *complete, + ucg_rdline_help_t *help); + + +/** + * Init the current buffer, and display a prompt. + * + * Also set the rdline status to "running", overriding previous + * ucg_rdline_stop() or ucg_rdline_quit(). + * + * @param rdl + * A pointer to an initialized struct ucg_rdline + * @param prompt + * A string containing the prompt, or NULL to keep existing one + */ +void ucg_rdline_newline(struct ucg_rdline *rdl, const char *prompt); + +/** + * Ignore all subsequent received chars. + * + * @param rdl + * A pointer to a struct ucg_rdline + */ +void ucg_rdline_stop(struct ucg_rdline *rdl); + +/** + * Exit from running rdline loop + * + * Same than ucg_rdline_stop() except that next calls to ucg_rdline_char_in() + * will return UCG_RDLINE_RES_EXITED. Hence, any running rdline() function is + * interrupted. + * + * @param rdl + * A pointer to a struct ucg_rdline + */ +void ucg_rdline_quit(struct ucg_rdline *rdl); + +/** + * Restart after a call to ucg_rdline_stop() or ucg_rdline_quit() + * + * @param rdl + * A pointer to a struct ucg_rdline + */ +void ucg_rdline_restart(struct ucg_rdline *rdl); + +/** + * Redisplay the current buffer + * + * @param rdl + * A pointer to a struct ucg_rdline + */ +void ucg_rdline_redisplay(struct ucg_rdline *rdl); + +/* return status for ucg_rdline_char_in() */ +#define UCG_RDLINE_RES_SUCCESS 0 +#define UCG_RDLINE_RES_VALIDATED 1 +#define UCG_RDLINE_RES_COMPLETED 2 +#define UCG_RDLINE_RES_NOT_RUNNING -1 +#define UCG_RDLINE_RES_EOF -2 +#define UCG_RDLINE_RES_EXITED -3 +#define UCG_RDLINE_RES_CANNOT_COMPLETE -3 + +/** + * Append a char to the readline buffer. + * + * @param rdl + * A pointer to a struct ucg_rdline + * @param c + * The character to append + * @return + * - UCG_RDLINE_RES_VALIDATED when the line has been validated. + * - UCG_RDLINE_RES_NOT_RUNNING if it is not running. + * - UCG_RDLINE_RES_EOF if EOF (ctrl-d on an empty line). + * - UCG_RDLINE_RES_EXITED if user called ucg_rdline_quit() + * - Else return UCG_RDLINE_RES_SUCCESS. + */ +int ucg_rdline_char_in(struct ucg_rdline *rdl, char c); + +#define UCG_RDLINE_F_IGNORE_EOF 0x0001 /**< ignore eof when polling */ + +/** + * Read (and edit) a line + * + * @param rdl + * A pointer to a struct ucg_rdline + * @param prompt + * The prompt string + * @param flags + * Any flags from UCG_RDLINE_F_* + * - UCG_RDLINE_F_IGNORE_EOF: on eof, clear error and continue polling + * @return + * - UCG_RDLINE_RES_VALIDATED when the line has been validated. + * - UCG_RDLINE_RES_NOT_RUNNING if it is not running. + * - UCG_RDLINE_RES_EOF if EOF (ctrl-d on an empty line). + * - UCG_RDLINE_RES_EXITED if user called ucg_rdline_quit() + */ +int ucg_rdline(struct ucg_rdline *rdl, const char *prompt, unsigned flags); + +/** + * write a buffer on rdline file descriptor + * + * @param rdl + * The rdline descriptor + * @param buf + * Pointer to the buffer + * @param count + * Number of bytes to write + * @return + * On success, the number of bytes written is returned (zero + * indicates nothing was written). On error, -1 is returned, and + * errno is set appropriately + */ +ssize_t ucg_rdline_write(struct ucg_rdline *rdl, void *buf, size_t count); + +/** + * write on rdline file descriptor according to a format string + * + * @param rdl + * The rdline descriptor + * @param fmt + * The format strings + * @return + * On success, return the number of characters printed (not including + * the trailing '\0'). On error, a negative value is returned. + */ +int ucg_rdline_printf(struct ucg_rdline *rdl, const char *fmt, ...); + +/** + * write on rdline file descriptor according to a format string + * + * @param rdl + * The rdline descriptor + * @param fmt + * The format strings + * @param ap + * Variable argument list + * @return + * Upon successful return, these functions return the number of + * characters printed (not including the trailing '\0' used to end + * output to strings). On error, a negative value is returned. + */ +int ucg_rdline_vprintf(struct ucg_rdline *rdl, const char *fmt, va_list ap); + +/** + * Return the current buffer, terminated by '\0'. + * + * @param rdl + * A pointer to a struct ucg_rdline + * @return + * The rdline buffer + */ +const char *ucg_rdline_get_buffer(struct ucg_rdline *rdl); + +/** + * Add the buffer to history. + * + * @param rdl + * A pointer to a struct ucg_rdline + * @param buf + * A buffer that is terminated by '\0' + * @return + * - 0 on success + * - negative on error + */ +int ucg_rdline_add_history(struct ucg_rdline *rdl, const char *buf); + +/** + * Clear current history + * + * @param rdl + * A pointer to a struct ucg_rdline + */ +void ucg_rdline_clear_history(struct ucg_rdline *rdl); + +/** + * Get the i-th history item + * + * @param rdl + * A pointer to a struct ucg_rdline + * @param i + * The index of the history item + * @return + * The i-th string of history, or NULL on error. + */ +const char *ucg_rdline_get_history_item(struct ucg_rdline *rdl, unsigned int i); + +#ifndef UCG_CMD_NO_PAGER +/** + * Write data asynchronously (using pager if needed) + * + * If there is enough place to print data on the current page, it is + * printed synchronously. Else, a temporary buffer is allocated and + * the data is stored in it. When the main rdline is called again, the + * pager is flushed before parsing any other commands. + * + * @param rdl + * The rdline descriptor + * @param buf + * Buffer to be sent + * @param len + * Length of buffer to be sent + * @return + * On success, the number of bytes written is returned (zero + * indicates nothing was written). On error, -1 is returned, and + * errno is set appropriately + */ +ssize_t ucg_rdline_pager_write(struct ucg_rdline *rdl, void *buf, size_t len); + +/** + * Print data asynchronously (using pager if needed) + * + * If there is enough place to print data on the current page, it is + * printed synchronously. Else, a temporary buffer is allocated and + * the data is stored in it. When the main rdline is called again, the + * pager is flushed before parsing any other commands. + * + * @param rdl + * The rdline descriptor + * @param fmt + * The format strings + * @return + * Upon successful return, these functions return the number of + * characters printed (not including the trailing '\0' used to end + * output to strings). On error, a negative value is returned. + */ +int ucg_rdline_pager_printf(struct ucg_rdline *rdl, const char *fmt, ...); + +/** + * Set the callback for the pager + * + * If there is some data in the pager to be printed, set a callback + * function that will be called when all the data will be printed. If + * the pager is empty, don't do anything and return -1. + * @param rdl + * The rdline descriptor + * @return + * - 0 if there is some data in the pager buffer and the callback + * is loaded + * - -1 if there is no data in pager buffer (in this case the callback + * is not called) + */ +int ucg_rdline_pager_set_cb(struct ucg_rdline *rdl, + ucg_rdline_pager_cb_t *cb, void *arg); +#endif + +#endif /* UCG_CMD_RDLINE_H_ */ diff --git a/lib/cmd/include/ucg_cmd_socket.h b/lib/cmd/include/ucg_cmd_socket.h new file mode 100644 index 0000000..e4fa918 --- /dev/null +++ b/lib/cmd/include/ucg_cmd_socket.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2009-2015, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +/*- + * Copyright (c) <2010>, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - 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. + * + * - Neither the name of Intel Corporation nor the names of its + * 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. + */ + +#ifndef UCG_CMD_SOCKET_H_ +#define UCG_CMD_SOCKET_H_ + +#ifdef UCG_CMD_HAVE_SOCKET + +#include + +/** + * Helper to create a tcpv4 socket + */ +int ucg_cmd_tcpv4_listen(in_addr_t addr, uint16_t port); + +/** + * Helper to create a tcpv6 socket + */ +int ucg_cmd_tcpv6_listen(const struct in6_addr *addr6, uint16_t port); + +/** + * Helper to create a unix socket + */ +int ucg_cmd_unix_listen(const char *filename); + +/** + * Helper to call accept() and create a new cmd instance + */ +struct ucg_cmd *ucg_cmd_accept(ucg_cmd_ctx_t *ctx, + const char *prompt, int s); +#endif + +#endif /* UCG_CMD_SOCKET_H_ */ diff --git a/lib/cmd/include/ucg_cmd_termios.h b/lib/cmd/include/ucg_cmd_termios.h new file mode 100644 index 0000000..c882444 --- /dev/null +++ b/lib/cmd/include/ucg_cmd_termios.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2009-2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +/*- + * Copyright (c) <2010>, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - 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. + * + * - Neither the name of Intel Corporation nor the names of its + * 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. + */ + +#ifndef UCG_CMD_TERMIOS_H_ +#define UCG_CMD_TERMIOS_H_ + +struct ucg_cmd; + +/** + * Set the pty in raw mode + * + * Save the previous configuration in the command line structure. + * It is restored with ucg_termios_restore(). The function does + * nothing if the platform does not support termios. + */ +int ucg_cmd_termios_raw(struct ucg_cmd *cl); + +/** + * Restore saved termios settings + */ +int ucg_cmd_termios_restore(struct ucg_cmd *cl); + +#endif /* UCG_CMD_H_ */ diff --git a/lib/cmd/include/ucg_cmd_vt100.h b/lib/cmd/include/ucg_cmd_vt100.h new file mode 100644 index 0000000..a2a10b8 --- /dev/null +++ b/lib/cmd/include/ucg_cmd_vt100.h @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2009-2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +/*- + * Copyright (c) <2010>, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - 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. + * + * - Neither the name of Intel Corporation nor the names of its + * 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. + */ + +#ifndef UCG_CMD_VT100_H_ +#define UCG_CMD_VT100_H_ + +#define ucg_vt100_bell "\007" +#define ucg_vt100_bs "\010" +#define ucg_vt100_bs_clear "\010 \010" +#define ucg_vt100_tab "\011" +#define ucg_vt100_crnl "\012\015" +#define ucg_vt100_clear_right "\033[0K" +#define ucg_vt100_clear_left "\033[1K" +#define ucg_vt100_clear_down "\033[0J" +#define ucg_vt100_clear_up "\033[1J" +#define ucg_vt100_clear_line "\033[2K" +#define ucg_vt100_clear_screen "\033[2J" +#define ucg_vt100_up_arr "\033\133\101" +#define ucg_vt100_down_arr "\033\133\102" +#define ucg_vt100_right_arr "\033\133\103" +#define ucg_vt100_left_arr "\033\133\104" +#define ucg_vt100_multi_right "\033\133%uC" +#define ucg_vt100_multi_left "\033\133%uD" +#define ucg_vt100_suppr "\033\133\063\176" +#define ucg_vt100_home "\033M\033E" +#define ucg_vt100_word_left "\033\142" +#define ucg_vt100_word_right "\033\146" + +/* Result of parsing : it must be synchronized with + * ucg_cmd_vt100_commands[] in vt100.c */ +#define UCG_CMD_KEY_UP_ARR 0 +#define UCG_CMD_KEY_DOWN_ARR 1 +#define UCG_CMD_KEY_RIGHT_ARR 2 +#define UCG_CMD_KEY_LEFT_ARR 3 +#define UCG_CMD_KEY_BKSPACE 4 +#define UCG_CMD_KEY_RETURN 5 +#define UCG_CMD_KEY_CTRL_A 6 +#define UCG_CMD_KEY_CTRL_E 7 +#define UCG_CMD_KEY_CTRL_K 8 +#define UCG_CMD_KEY_CTRL_Y 9 +#define UCG_CMD_KEY_CTRL_C 10 +#define UCG_CMD_KEY_CTRL_F 11 +#define UCG_CMD_KEY_CTRL_B 12 +#define UCG_CMD_KEY_SUPPR 13 +#define UCG_CMD_KEY_TAB 14 +#define UCG_CMD_KEY_CTRL_D 15 +#define UCG_CMD_KEY_CTRL_L 16 +#define UCG_CMD_KEY_RETURN2 17 +#define UCG_CMD_KEY_META_BKSPACE 18 +#define UCG_CMD_KEY_WLEFT 19 +#define UCG_CMD_KEY_WRIGHT 20 +#define UCG_CMD_KEY_HELP 21 +#define UCG_CMD_KEY_CTRL_W 22 +#define UCG_CMD_KEY_CTRL_P 23 +#define UCG_CMD_KEY_CTRL_N 24 +#define UCG_CMD_KEY_META_D 25 + +extern const char *ucg_cmd_vt100_commands[]; + +enum ucg_cmd_vt100_parser_state { + UCG_CMD_VT100_INIT, + UCG_CMD_VT100_ESCAPE, + UCG_CMD_VT100_ESCAPE_CSI +}; + +#define UCG_CMD_VT100_BUF_SIZE 8 +struct ucg_cmd_vt100 { + uint8_t bufpos; + char buf[UCG_CMD_VT100_BUF_SIZE]; + enum ucg_cmd_vt100_parser_state state; +}; + +/** + * Init + */ +void ucg_vt100_init(struct ucg_cmd_vt100 *vt); + +#define UCG_VT100_STD_CHAR -1 +#define UCG_VT100_NOT_COMPLETE -2 +/** + * Input a new character. + * Return UCG_VT100_STD_CHAR if the character is not part of a control sequence + * Return UCG_VT100_NOT_COMPLETE if c is not the last char of a control sequence + * Else return the index in ucg_vt100_commands[] + */ +int ucg_vt100_parser(struct ucg_cmd_vt100 *vt, char c); + +#endif /* UCG_CMD_VT100_H_ */ diff --git a/lib/cmd/ucg_cmd.c b/lib/cmd/ucg_cmd.c new file mode 100644 index 0000000..61b9a70 --- /dev/null +++ b/lib/cmd/ucg_cmd.c @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2009-2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +/*- + * Copyright (c) <2010>, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - 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. + * + * - Neither the name of Intel Corporation nor the names of its + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ucg_cmd_parse.h" +#include "ucg_cmd_rdline.h" +#include "ucg_cmd.h" + +static void +default_valid_buffer(struct ucg_rdline *rdl, const char *line) +{ + struct ucg_cmd *cl = rdl->opaque; + int ret; + + ret = ucg_cmd_parse(cl, line, cl); + if (ret == UCG_CMD_PARSE_AMBIGUOUS) + ucg_cmd_printf(cl, "Ambiguous command\n"); + else if (ret == UCG_CMD_PARSE_NOMATCH) + ucg_cmd_printf(cl, "Bad arguments\n"); + else if (ret == UCG_CMD_PARSE_UNTERMINATED_QUOTE) + ucg_cmd_printf(cl, "Unterminated quote\n"); +} + +static int +default_complete_buffer(struct ucg_rdline *rdl, const char *line, + char *dstbuf, unsigned int dstsize) +{ + int ret; + struct ucg_cmd *cl = rdl->opaque; + ret = ucg_cmd_complete(cl, line, dstbuf, dstsize); + if (ret == UCG_CMD_COMPLETE_APPEND) + return 0; + return -1; +} + + +static void +default_help(struct ucg_rdline *rdl, const char *line) +{ + struct ucg_cmd *cl = rdl->opaque; + + ucg_cmd_help(cl, line); +} + +/* ---- Some rdline wrappers ---- */ + +void +ucg_cmd_set_prompt(struct ucg_cmd *cl, const char *prompt) +{ + snprintf(cl->prompt, sizeof(cl->prompt), "%s", prompt); +} + +void +ucg_cmd_init(struct ucg_cmd *cl, ucg_cmd_ctx_t *ctx, + const char *prompt, FILE *f_in, FILE *f_out) +{ + /* init cmd structure */ + memset(cl, 0, sizeof(struct ucg_cmd)); + cl->ctx = ctx; + + /* init embedded rdline */ + ucg_rdline_init(&cl->rdl, f_in, f_out, + default_valid_buffer, + default_complete_buffer, + default_help); + + cl->rdl.opaque = cl; + ucg_cmd_set_prompt(cl, prompt); + ucg_rdline_newline(&cl->rdl, cl->prompt); +} + +struct ucg_cmd * +ucg_cmd_new(ucg_cmd_ctx_t *ctx, const char *prompt, + FILE *f_in, FILE *f_out) +{ + struct ucg_cmd *cl; + + cl = malloc(sizeof(struct ucg_cmd)); + if (cl == NULL) + return NULL; + + ucg_cmd_init(cl, ctx, prompt, f_in, f_out); + return cl; +} + +struct ucg_cmd * +ucg_cmd_file_new(ucg_cmd_ctx_t *ctx, const char *prompt, + const char *path, FILE *f_out) +{ +#if UCG_CMD_HAVE_FILE + FILE *f_in; + + f_in = fopen(path, "r"); + if (f_in == NULL) + return NULL; + return (ucg_cmd_new(ctx, prompt, f_in, f_out)); +#else + (void)ctx; + (void)prompt; + (void)path; + (void)f_out; + return NULL; +#endif +} + +void +ucg_cmd_free(struct ucg_cmd *cl) +{ + struct ucg_rdline *rdl = &cl->rdl; + + if (rdl->f_in != stdin) + fclose(rdl->f_in); + if (rdl->f_out != rdl->f_in && rdl->f_out != stdout && + rdl->f_out != stderr) + fclose(rdl->f_out); + free(cl); +} + +int +ucg_cmd_printf(struct ucg_cmd *cl, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = ucg_rdline_vprintf(&cl->rdl, fmt, ap); + va_end(ap); + + return ret; +} + +/* Push an input buffer in the command line. Typically, this function + * is called by ucg_cmd_interact() to send the input characters to the + * cmd process. It can also be called by a user callback function, + * when a buffer is received from the input socket. + * + * The function returns the number of processed characters, or a + * negative value on error (EOF reached or command line exited. */ +int +ucg_cmd_in(struct ucg_cmd *cl, const char *buf, int size) +{ + int ret = 0; + int i; + + for (i = 0; i < size; i++) { + ret = ucg_rdline_char_in(&cl->rdl, buf[i]); + + if (ret == UCG_RDLINE_RES_VALIDATED && + cl->rdl.status == UCG_RDLINE_STOPPED) + break; + + if (ret == UCG_RDLINE_RES_VALIDATED) + ucg_rdline_newline(&cl->rdl, cl->prompt); + else if (ret == UCG_RDLINE_RES_EOF) + return -1; + else if (ret == UCG_RDLINE_RES_EXITED) + return -1; + } + return i; +} + +/* Interrupt a running command line (exits from ucg_cmd_interact) */ +void +ucg_cmd_quit(struct ucg_cmd *cl) +{ + ucg_rdline_quit(&cl->rdl); +} + +/* loop until the user explicitelly call ucg_cmd_quit(), or if the input + * fd reaches EOF. */ +void +ucg_cmd_interact(struct ucg_cmd *cl, unsigned flags) +{ + int ret; + char c; + + c = -1; + while (1) { + ret = fread(&c, 1, 1, cl->rdl.f_in); + + if (ret == 0) { + if (flags & UCG_CMD_F_IGNORE_EOF) { + clearerr(cl->rdl.f_in); + continue; + } + break; + } + + if (ucg_cmd_in(cl, &c, 1) < 0) + break; + } +} diff --git a/lib/cmd/ucg_cmd_parse.c b/lib/cmd/ucg_cmd_parse.c new file mode 100644 index 0000000..587a250 --- /dev/null +++ b/lib/cmd/ucg_cmd_parse.c @@ -0,0 +1,694 @@ +/* + * Copyright (c) 2009-2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +/*- + * Copyright (c) <2010>, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - 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. + * + * - Neither the name of Intel Corporation nor the names of its + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include "ucg_cmd_parse.h" +#include "ucg_cmd.h" + +//#define debug_printf printf +#define debug_printf(args...) do {} while(0) + +/* used internally for ucg_cmd_help() and ucg_cmd_complete() */ +struct cmd_preparse { + int nb_valid_tok; /* number of valid tokens in the buffer */ + void *opaque; /* pointer to opaque data */ + char comp_tok_buf[UCG_CMD_MAX_TOKEN_SIZE]; /* token to complete */ + size_t comp_tok_len; /* length of the token to complete */ + size_t comp_tok_offset; /* offset of token to complete in the line buf */ +}; + +/* isblank() needs _XOPEN_SOURCE >= 600 || _ISOC99_SOURCE, so use our + * own. */ +static int +isblank2(char c) +{ + if (c == ' ' || c == '\t' ) + return 1; + return 0; +} + +static int +isendofline(char c) +{ + if (c == '\n' || c == '\r' ) + return 1; + return 0; +} + +static int +iscomment(char c) +{ + if (c == '#') + return 1; + return 0; +} + +int +ucg_cmd_isendoftoken(char c) +{ + if (!c || iscomment(c) || isblank2(c) || isendofline(c)) + return 1; + return 0; +} + +static unsigned int +nb_common_chars(const char * s1, const char * s2) +{ + unsigned int i=0; + + while (*s1==*s2 && *s1 && *s2) { + s1++; + s2++; + i++; + } + return i; +} + +/* quote a string and escape original quotes */ +int ucg_cmd_quote_token(char *dst, unsigned dstlen, const char *src) +{ + unsigned s = 0, d = 0; + + /* the 2 quotes + '\0' */ + if (dstlen < 3) + return -EMSGSIZE; + + dst[d++] = '"'; + while (src[s] != '\0') { + if (d >= (dstlen-2)) + return -EMSGSIZE; + + if (src[s] == '"') + dst[d++] = '\\'; + if (src[s] == '\\' && src[s+1] == '"') + dst[d++] = '\\'; + + dst[d++] = src[s++]; + } + + if (d >= (dstlen-2)) + return -EMSGSIZE; + dst[d++] = '"'; + dst[d++] = '\0'; + return 0; +} + +/* Remove quotes and stop when we reach the end of token. Return the + * number of "eaten" bytes from the source buffer, or a negative value + * on error */ +int ucg_cmd_get_token(char *dst, unsigned dstlen, const char *src) +{ + unsigned s = 0, d = 0; + int quoted = 0; + + /* skip spaces */ + while (isblank2(src[s])) + s++; + + /* empty token */ + if (ucg_cmd_isendoftoken(src[s])) + return -EINVAL; + + /* copy token and remove quotes */ + while (src[s] != '\0') { + if (d >= dstlen) + return -EMSGSIZE; + + if (ucg_cmd_isendoftoken(src[s]) && quoted == 0) + break; + + if (src[s] == '\\' && src[s+1] == '"') { + dst[d++] = '"'; + s += 2; + continue; + } + if (src[s] == '\\' && src[s+1] == '\\') { + dst[d++] = '\\'; + s += 2; + continue; + } + if (src[s] == '"') { + s++; + quoted = !quoted; + continue; + } + dst[d++] = src[s++]; + } + + /* not enough room in dst buffer */ + if (d >= (dstlen-1)) + return -EMSGSIZE; + + /* end of string during quote */ + if (quoted) + return -EINVAL; + + dst[d++] = '\0'; + return s; +} + +/* return the nth token from src and copy it in dst. Return the offset + * of the token in src, or a negative value on error. Note: the index + * of the first token is 0. */ +static int cmd_get_nth_token(char *dst, unsigned dstlen, int n, + const char *src) +{ + int ret = 0, offset = 0; + + do { + offset += ret; + + /* skip spaces */ + while (isblank2(src[offset])) + offset++; + + /* get the token starting at offset */ + ret = ucg_cmd_get_token(dst, dstlen, src + offset); + if (ret < 0) + return ret; + + } while (n--); + + return offset; +} + +/* + * try to match the buffer with an instruction (only the first + * nb_match_token tokens if != 0). + * + * Return 0 if we match all the tokens, else the number of matched + * tokens, or a negative value on error. +*/ +static int +match_inst(const ucg_cmd_inst_t *inst, const char *linebuf, + unsigned int nb_match_token, void *resbuf, unsigned resbuf_size) +{ + unsigned int token_num = 0; + ucg_cmd_tk_hdr_t *token; + int n = 0, res; + char token_str[UCG_CMD_MAX_TOKEN_SIZE]; + + token = inst->tokens[token_num]; + + /* check if we match all tokens of inst */ + while (token) { + + /* we matched enough tokens, return success */ + if (nb_match_token != 0 && token_num >= nb_match_token) + return 0; + + debug_printf("TK\n"); + + /* copy token and remove quotes */ + n = ucg_cmd_get_token(token_str, sizeof(token_str), linebuf); + if (n < 0) + break; + + /* parse this token */ + if (resbuf == NULL) + res = token->ops->parse(token, token_str, NULL, 0); + else { + unsigned rb_sz; + void *rb = (char *)resbuf + token->offset; + + /* not enough room to store result */ + if (token->offset > resbuf_size) + return -ENOBUFS; + + rb_sz = resbuf_size - token->offset; + res = token->ops->parse(token, token_str, rb, rb_sz); + } + + /* does not match this token */ + if (res < 0) + break; + + debug_printf("TK parsed (len=%d)\n", n); + linebuf += n; + token_num ++; + token = inst->tokens[token_num]; + } + + /* does not match */ + if (token_num == 0) + return -ENOENT; + + /* we don't match all the tokens */ + if (token) + return token_num; + + /* are there are some tokens more */ + while (isblank2(*linebuf)) + linebuf++; + + /* end of buf, we match all inst */ + if (*linebuf == '\0' || isendofline(*linebuf) || iscomment(*linebuf)) + return 0; + + /* garbage after inst */ + return token_num; +} + + +/* Check if a line buffer is valid and can be parsed or completed. The + * parsing stops when \n or \0 is reached. The also function checks + * that tokens are correctly quoted. The number of tokens in the + * buffer is returned. */ +static int validate_linebuf(const char *linebuf) +{ + int quoted = 0, comment = 0; + int i = 0, nbtok = 0, token = 0; + + while (linebuf[i] != '\0') { + if (isendofline(linebuf[i]) && quoted == 0) + break; + if (comment == 1) { + i++; + continue; + } + if (iscomment(linebuf[i]) && quoted == 0) { + comment = 1; + i ++; + continue; + } + + /* end of token */ + if (isblank2(linebuf[i]) && quoted == 0) + token = 0; + /* new token */ + if (!isblank2(linebuf[i]) && token == 0) { + token = 1; + nbtok++; + } + + if (linebuf[i] == '\\' && linebuf[i+1] == '"') { + i += 2; + continue; + } + if (linebuf[i] == '\\' && linebuf[i+1] == '\\') { + i += 2; + continue; + } + if (linebuf[i] == '"') { + i++; + quoted = !quoted; + continue; + } + i++; + } + if (quoted) + return UCG_CMD_PARSE_UNTERMINATED_QUOTE; + return nbtok; +} + +/* Try to parse a buffer according to the specified context. The + * argument linebuf must end with \n or \0. */ +int +ucg_cmd_parse(struct ucg_cmd *cl, const char *linebuf, + void *opaque) +{ + ucg_cmd_ctx_t *ctx = cl->ctx; + const ucg_cmd_inst_t **pinst; + const ucg_cmd_inst_t *inst; + char result_buf[UCG_CMD_MAX_DSTBUF_SIZE]; + void (*f)(void *, struct ucg_cmd *, void *) = NULL; + void *data = NULL; + int ret; + + ret = validate_linebuf(linebuf); + if (ret < 0) + return ret; + if (ret == 0) + return UCG_CMD_PARSE_EMPTY; + + + /* parse it !! */ + for (pinst = &ctx->insts[0]; *pinst != NULL; pinst++) { + inst = *pinst; + debug_printf("INST\n"); + + /* fully parsed */ + ret = match_inst(inst, linebuf, 0, result_buf, + sizeof(result_buf)); + + if (ret != 0) + continue; + + debug_printf("INST fully parsed\n"); + + /* if end of buf -> there is no garbage after inst */ + if (f != NULL) { + /* more than 1 inst matches */ + debug_printf("Ambiguous cmd\n"); + return UCG_CMD_PARSE_AMBIGUOUS; + } + f = inst->f; + data = inst->data; + } + + /* call func */ + if (f == NULL) + return UCG_CMD_PARSE_NOMATCH; + + f(result_buf, opaque, data); + return UCG_CMD_PARSE_SUCCESS; +} + +/* called by ucg_cmd_help() and ucg_cmd_complete() to preparse the + * line buffer (the operations done are common to these functions) */ +static int cmd_preparse(struct cmd_preparse *preparse, const char *buf) +{ + int ret, len, nb_tok; + + /* count the number of tokens in the line buffer */ + ret = validate_linebuf(buf); + if (ret < 0) + return ret; + nb_tok = ret; + + /* if last token is not complete, decrement nb_valid_tok */ + len = strlen(buf); + if (nb_tok == 0 || isblank2(buf[len - 1])) { + preparse->nb_valid_tok = nb_tok; + preparse->comp_tok_offset = len; + preparse->comp_tok_buf[0] = '\0'; + preparse->comp_tok_len = 0; + } + else { + preparse->nb_valid_tok = nb_tok - 1; + + /* get the incomplete token (can be empty) and return its + * offset in the buffer */ + preparse->comp_tok_offset = + cmd_get_nth_token(preparse->comp_tok_buf, + sizeof(preparse->comp_tok_buf), + preparse->nb_valid_tok, + buf); + preparse->comp_tok_len = strlen(preparse->comp_tok_buf); + } + + return 0; +} + +/* Display a contextual help in the command line. The contextual help + * depends on the buffer given. */ +void ucg_cmd_help(struct ucg_cmd *cl, const char *buf) +{ + ucg_cmd_ctx_t *ctx = cl->ctx; + ucg_cmd_tk_hdr_t *token; + const ucg_cmd_inst_t **pinst; + const ucg_cmd_inst_t *inst; + struct cmd_preparse preparse; + char tmpbuf[UCG_CMD_MAX_DSTBUF_SIZE]; + char *help_str; + size_t n; + int iterate; + + cmd_preparse(&preparse, buf); + + debug_printf("display contextual help\n"); + + for (pinst = &ctx->insts[0]; *pinst != NULL; pinst++) { + inst = *pinst; + + /* get instruction static help string */ + help_str = inst->help_str; + if (help_str == NULL) + help_str = "No help"; + + /* match the beginning of the command */ + if (preparse.nb_valid_tok != 0 && + match_inst(inst, buf, preparse.nb_valid_tok, + NULL, 0) != 0) + continue; + + token = inst->tokens[preparse.nb_valid_tok]; + + /* end of inst */ + if (token == NULL) { + ucg_cmd_printf(cl, "%-20s %s\n", "", help_str); + continue; + } + + /* token matches, but no completion */ + if (token->ops->complete_start == NULL || + token->ops->complete_iterate == NULL) + iterate = 0; + else + iterate = 1; + + /* store the incomplete token in tmpbuf */ + n = preparse.comp_tok_len + 1; + if (n > sizeof(tmpbuf)) + n = sizeof(tmpbuf); + snprintf(tmpbuf, sizeof(tmpbuf), "%s", preparse.comp_tok_buf); + + if (iterate == 1 && + token->ops->complete_start(token, tmpbuf, + &preparse.opaque) < 0) { + /* cancel iteration, complete_start() returned + * a negative value, meaning no completion */ + iterate = 0; + if (token->ops->complete_end != NULL) + token->ops->complete_end(token, + &preparse.opaque); + } + + debug_printf(" iterate = %d\n", iterate); + + if (iterate == 0) { + /* get token dynamic help string */ + if ((token->ops->help == NULL) || + (token->ops->help(token, tmpbuf, + sizeof(tmpbuf)) < 0)) + snprintf(tmpbuf, sizeof(tmpbuf), "unknown"); + + ucg_cmd_printf(cl, "%-20s %s\n", tmpbuf, help_str); + continue; + } + + /* iterate over all possible completion for this inst */ + while (token->ops->complete_iterate(token, + &preparse.opaque, + tmpbuf, + sizeof(tmpbuf)) >= 0) { + + + debug_printf(" choice <%s>\n", tmpbuf); + + /* get the token and add it in help buffer */ + ucg_cmd_printf(cl, "%-20s %s\n", tmpbuf, + iterate != 0? help_str : "''"); + + /* don't display help next time */ + iterate = 0; + } + + /* no more completion, go to next inst */ + if (token->ops->complete_end != NULL) + token->ops->complete_end(token, &preparse.opaque); + } +} + +/* try to complete the buffer given as a parameter */ +int +ucg_cmd_complete(struct ucg_cmd *cl, const char *buf, + char *dst, unsigned int dstsize) +{ + ucg_cmd_ctx_t *ctx = cl->ctx; + ucg_cmd_tk_hdr_t *token; + const ucg_cmd_inst_t **pinst; + const ucg_cmd_inst_t *inst; + struct cmd_preparse preparse; + int nb_match = 0; + int nb_completion = 0; + char completion_buf[UCG_CMD_MAX_TOKEN_SIZE]; + char tmpbuf[UCG_CMD_MAX_TOKEN_SIZE]; + int ret; + size_t n, completion_len = UCG_CMD_MAX_TOKEN_SIZE; + + debug_printf("%s called\n", __FUNCTION__); + + /* fill the preparse structure that contains infos that will + * help us to complete the buffer */ + ret = cmd_preparse(&preparse, buf); + if (ret < 0) + return UCG_CMD_COMPLETE_NONE; + + /* try to complete ! */ + for (pinst = &ctx->insts[0]; *pinst != NULL; pinst++) { + inst = *pinst; + + debug_printf("INST\n"); + + /* try to match the first tokens */ + if (preparse.nb_valid_tok != 0 && + match_inst(inst, buf, preparse.nb_valid_tok, + NULL, 0) != 0) + continue; + + nb_match ++; + token = inst->tokens[preparse.nb_valid_tok]; + + /* non completable */ + if (token == NULL || + token->ops->complete_start == NULL || + token->ops->complete_iterate == NULL) + continue; + + /* store the incomplete token in tmpbuf */ + n = preparse.comp_tok_len + 1; + if (n > sizeof(tmpbuf)) + n = sizeof(tmpbuf); + snprintf(tmpbuf, n, "%s", preparse.comp_tok_buf); + + /* non completable */ + if (token->ops->complete_start(token, tmpbuf, + &preparse.opaque) < 0) { + if (token->ops->complete_end != NULL) + token->ops->complete_end(token, + &preparse.opaque); + continue; + } + + /* all possible completion for this token */ + while (1) { + + ret = token->ops->complete_iterate(token, + &preparse.opaque, + tmpbuf, + sizeof(tmpbuf)-1); + + if (ret < 0) + break; + + debug_printf("Completion %s\n", tmpbuf); + + /* we kept at least the room for one char */ + if (ret == 0) + strcat(tmpbuf, " "); + + debug_printf(" choice <%s>\n", tmpbuf); + + /* does the completion match the beginning of + * the word ? */ + if (strncmp(preparse.comp_tok_buf, tmpbuf, + preparse.comp_tok_len)) + continue; + + /* first one, save the buffer */ + if (nb_completion == 0) { + completion_len = snprintf(completion_buf, + sizeof(completion_buf), + "%s", tmpbuf); + } + else { + n = nb_common_chars(completion_buf, tmpbuf); + if (n < completion_len) + completion_len = n; + } + nb_completion ++; + + /* we cannot add any char, just display help */ + if (completion_len == preparse.comp_tok_len) + break; + } + if (token->ops->complete_end != NULL) + token->ops->complete_end(token, &preparse.opaque); + + if (completion_len == preparse.comp_tok_len) + break; + } + + debug_printf("nb_completion=%d, completion_len=%d\n", + (int)nb_completion, (int)completion_len); + + /* one choice, append chars and return */ + if (nb_completion == 1) { + snprintf(dst, dstsize, "%s", + completion_buf + preparse.comp_tok_len); + return UCG_CMD_COMPLETE_APPEND; + } + + /* many choices, but starting with same chars: append chars + * and return */ + if (nb_completion != 0 && completion_len > preparse.comp_tok_len) { + if (completion_len >= dstsize) + completion_len = dstsize - 1; + strncpy(dst, completion_buf + preparse.comp_tok_len, + completion_len - preparse.comp_tok_len); + dst[completion_len - preparse.comp_tok_len] = '\0'; + return UCG_CMD_COMPLETE_APPEND; + } + + /* no match, nothing to do */ + if (nb_match == 0 || nb_completion == 0) + return UCG_CMD_COMPLETE_NONE; + + return UCG_CMD_COMPLETE_MANY; +} + diff --git a/lib/cmd/ucg_cmd_parse_etheraddr.c b/lib/cmd/ucg_cmd_parse_etheraddr.c new file mode 100644 index 0000000..fd31084 --- /dev/null +++ b/lib/cmd/ucg_cmd_parse_etheraddr.c @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2009-2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +/*- + * Copyright (c) <2010>, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - 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. + * + * - Neither the name of Intel Corporation nor the names of its + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ucg_cmd_parse.h" +#include "ucg_cmd_parse_etheraddr.h" + +#define ETHER_ADDRSTRLEN 18 + +#ifdef __linux__ +#define ea_oct ether_addr_octet +#else +#define ea_oct octet +#endif + +static struct ether_addr * +my_ether_aton(const char *a) +{ + int i; + static struct ether_addr ether_addr; + unsigned int o0, o1, o2, o3, o4, o5; + + i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o0, &o1, &o2, &o3, &o4, &o5); + + if (i != ETHER_ADDR_LEN) + return NULL; + + ether_addr.ea_oct[0] = (uint8_t)o0; + ether_addr.ea_oct[1] = (uint8_t)o1; + ether_addr.ea_oct[2] = (uint8_t)o2; + ether_addr.ea_oct[3] = (uint8_t)o3; + ether_addr.ea_oct[4] = (uint8_t)o4; + ether_addr.ea_oct[5] = (uint8_t)o5; + + return (struct ether_addr *)ðer_addr; +} + +static int +cmd_parse_etheraddr( + __attribute__((unused)) ucg_cmd_tk_hdr_t *tk, + const char *buf, void *res, unsigned ressize) +{ + unsigned int token_len = 0; + char ether_str[ETHER_ADDRSTRLEN]; + struct ether_addr *tmp; + + if (res && ressize < sizeof(struct ether_addr)) + return -1; + + /* if token is too big... */ + token_len = snprintf(ether_str, sizeof(ether_str), "%s", buf); + if (token_len >= sizeof(ether_str)) + return -1; + + tmp = my_ether_aton(ether_str); + if (tmp == NULL) + return -1; + + if (res != NULL) + memcpy(res, tmp, sizeof(struct ether_addr)); + return 0; +} + +static int +cmd_help_etheraddr( + __attribute__((unused)) ucg_cmd_tk_hdr_t *tk, + char *dstbuf, unsigned int size) +{ + snprintf(dstbuf, size, ""); + return 0; +} + +struct ucg_cmd_tk_ops ucg_cmd_tk_etheraddr_ops = { + .parse = cmd_parse_etheraddr, + .complete_start = NULL, + .complete_iterate = NULL, + .complete_end = NULL, + .help = cmd_help_etheraddr, +}; diff --git a/lib/cmd/ucg_cmd_parse_file.c b/lib/cmd/ucg_cmd_parse_file.c new file mode 100644 index 0000000..44917af --- /dev/null +++ b/lib/cmd/ucg_cmd_parse_file.c @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2009-2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 +#include + +#include "ucg_cmd_parse.h" +#include "ucg_cmd_parse_file.h" + +struct cmd_complete_file_callback { + char *token; + DIR *dir; +}; + +static int +cmd_parse_file(ucg_cmd_tk_hdr_t *tk, const char *buf, + void *res, unsigned ressize) +{ + struct ucg_cmd_tk_file *tk2 = (struct ucg_cmd_tk_file *)tk; + struct ucg_cmd_tk_file_data *sd = &tk2->file_data;; + unsigned int token_len; + struct stat st; + int flags, ret; + char *tmp, *dname; + + if (res && ressize < UCG_FILENAME_SIZE) + return -1; + + token_len = strlen(buf); + if (token_len >= (UCG_FILENAME_SIZE - 1) || token_len == 0) + return -1; + + flags = sd->flags; + + if (flags & PARSE_FILE_F_CREATE) { + /* the directory must exist */ + tmp = strdup(buf); + dname = dirname(tmp); + ret = stat(dname, &st); + if (ret != 0) + ret = lstat(dname, &st); + free(tmp); + if (ret != 0) + return -1; + if (!S_ISDIR(st.st_mode)) + return -1; + } + else { + ret = stat(buf, &st); + if (ret != 0) + return -1; + if (flags & PARSE_FILE_F_DIRECTORY) + if (!S_ISDIR(st.st_mode)) + return -1; + } + + /* we already checked that token_len is < FILENAME_SIZE-1 */ + if (res) + strcpy(res, buf); + + return 0; +} + +/* + * This function is quite similar to dirname(3) except that: + * - it allocates the returned string and don't modify the argument + * - the result of dirname2() is not exactly the same than dirname() + * path dirname2 + * "/usr/lib" "/usr" + * "/usr/" "/usr" + * "/usr" "/" + * "usr" "." or "" if allow_empty == 1 + * "" "." or "" if allow_empty == 1 + * "/" "/" + * "." "." or "" if allow_empty == 1 + * "./" "." + * ".." "." or "" if allow_empty == 1 + * "../" ".." + */ +static char * +dirname2(const char *path, int allow_empty) +{ + char *s; + char *last_slash; + int len; + + len = strlen(path); + if (len == 0 && allow_empty == 0) { + s = strdup("."); + return s; + } + + s = strdup(path); + last_slash = strrchr(s, '/'); + if (last_slash == NULL) { + if (allow_empty) + s[0] = '\0'; + else + strcpy(s, "."); + } + else if (last_slash == s) + s[1] = '\0'; + else { + *last_slash = '\0'; + } + + return s; +} + +static int +cmd_complete_file_start(ucg_cmd_tk_hdr_t *tk, + const char *tokstr, void **opaque) +{ + char *dname; + struct cmd_complete_file_callback *cb; + + (void)tk; + *opaque = NULL; + cb = malloc(sizeof(*cb)); + if (cb == NULL) + return -1; + memset(cb, 0, sizeof(*cb)); + *opaque = cb; + + cb->token = strdup(tokstr); + /* we need to copy again tokstr because dirname() alters the string */ + dname = dirname2(tokstr, 0); + cb->dir = opendir(dname); + free(dname); + + if (cb->dir == NULL) + return -1; + + return 0; +} + +static int +cmd_complete_file_iterate(ucg_cmd_tk_hdr_t *tk, void **opaque, + char *dstbuf, unsigned int size) +{ + struct ucg_cmd_tk_file *tk2 = (struct ucg_cmd_tk_file *)tk; + struct ucg_cmd_tk_file_data *sd = &tk2->file_data; + struct cmd_complete_file_callback *cb; + struct dirent *de; + struct stat st; + char *dname; + int len; + int need_join_slash = 1; + int flags = sd->flags; + + cb = *opaque; + /* read next dir name, skipping "." and ".." */ + while (1) { + de = readdir(cb->dir); + if (de == NULL) + return -1; + if (strcmp(de->d_name, ".") == 0 || + strcmp(de->d_name, "..") == 0) + continue; + + + /* do we need a / to join dirname and basename ? */ + dname = dirname2(cb->token, 1); + len = strlen(dname); + if (len == 0 || dname[len - 1] == '/') + need_join_slash = 0; + + /* keep one byte for potential '/' */ + len = snprintf(dstbuf, size-1, "%s%s%s", dname, + need_join_slash ? "/" : "", de->d_name); + free(dname); + if (len < 0 || len >= (int)size - 1) + continue; + + /* append '/' if it's a directory */ + if (stat(dstbuf, &st) != 0) { + if (lstat(dstbuf, &st) != 0) + return -1; + } + if (S_ISDIR(st.st_mode)) { + strcat(dstbuf, "/"); + return 1; /* intermediate completion */ + } + /* skip non-directories */ + else if (flags & PARSE_FILE_F_DIRECTORY) + continue; + break; + } + + return 0; +} + +static void +cmd_complete_file_end(ucg_cmd_tk_hdr_t *tk, void **opaque) +{ + struct cmd_complete_file_callback *cb; + + (void)tk; + cb = *opaque; + + if (cb == NULL) + return; + + if (cb->dir) + closedir(cb->dir); + if (cb->token) + free(cb->token); + + free(cb); +} + + +static int +cmd_help_file(ucg_cmd_tk_hdr_t *tk, char *dstbuf, + unsigned int size) +{ + struct ucg_cmd_tk_file *tk2 = (struct ucg_cmd_tk_file *)tk; + struct ucg_cmd_tk_file_data *sd = &tk2->file_data;; + int flags; + + flags = sd->flags; + if (flags & PARSE_FILE_F_DIRECTORY) + snprintf(dstbuf, size, ""); + else + snprintf(dstbuf, size, ""); + + return 0; +} + +struct ucg_cmd_tk_ops ucg_cmd_tk_file_ops = { + .parse = cmd_parse_file, + .complete_start = cmd_complete_file_start, + .complete_iterate = cmd_complete_file_iterate, + .complete_end = cmd_complete_file_end, + .help = cmd_help_file, +}; diff --git a/lib/cmd/ucg_cmd_parse_ipaddr.c b/lib/cmd/ucg_cmd_parse_ipaddr.c new file mode 100644 index 0000000..55aa7c1 --- /dev/null +++ b/lib/cmd/ucg_cmd_parse_ipaddr.c @@ -0,0 +1,384 @@ +/* + * Copyright (c) 2009-2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +/*- + * Copyright (c) <2010>, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - 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. + * + * - Neither the name of Intel Corporation nor the names of its + * 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 inet_ntop() functions: + * + * Copyright (c) 1996 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + + +#include +#include +#include +#include +#include +#include +#include +#ifndef __linux__ +#include +#endif + +#include +#include + +#define INADDRSZ 4 +#define IN6ADDRSZ 16 + +/* + * WARNING: Don't even consider trying to compile this on a system where + * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. + */ + +static int inet_pton4(const char *src, unsigned char *dst); +static int inet_pton6(const char *src, unsigned char *dst); + +/* int + * inet_pton(af, src, dst) + * convert from presentation format (which usually means ASCII printable) + * to network format (which is usually some kind of binary format). + * return: + * 1 if the address was valid for the specified address family + * 0 if the address wasn't valid (`dst' is untouched in this case) + * -1 if some other error occurred (`dst' is untouched in this case, too) + * author: + * Paul Vixie, 1996. + */ +static int +my_inet_pton(int af, const char *src, void *dst) +{ + switch (af) { + case AF_INET: + return (inet_pton4(src, dst)); + case AF_INET6: + return (inet_pton6(src, dst)); + default: + errno = EAFNOSUPPORT; + return (-1); + } + /* NOTREACHED */ +} + +/* int + * inet_pton4(src, dst) + * like inet_aton() but without all the hexadecimal and shorthand. + * return: + * 1 if `src' is a valid dotted quad, else 0. + * notice: + * does not touch `dst' unless it's returning 1. + * author: + * Paul Vixie, 1996. + */ +static int +inet_pton4(const char *src, unsigned char *dst) +{ + static const char digits[] = "0123456789"; + int saw_digit, octets, ch; + unsigned char tmp[INADDRSZ], *tp; + + saw_digit = 0; + octets = 0; + *(tp = tmp) = 0; + while ((ch = *src++) != '\0') { + const char *pch; + + if ((pch = strchr(digits, ch)) != NULL) { + unsigned int new = *tp * 10 + (pch - digits); + + if (new > 255) + return (0); + if (! saw_digit) { + if (++octets > 4) + return (0); + saw_digit = 1; + } + *tp = (unsigned char)new; + } else if (ch == '.' && saw_digit) { + if (octets == 4) + return (0); + *++tp = 0; + saw_digit = 0; + } else + return (0); + } + if (octets < 4) + return (0); + + memcpy(dst, tmp, INADDRSZ); + return (1); +} + +/* int + * inet_pton6(src, dst) + * convert presentation level address to network order binary form. + * return: + * 1 if `src' is a valid [RFC1884 2.2] address, else 0. + * notice: + * (1) does not touch `dst' unless it's returning 1. + * (2) :: in a full address is silently ignored. + * credit: + * inspired by Mark Andrews. + * author: + * Paul Vixie, 1996. + */ +static int +inet_pton6(const char *src, unsigned char *dst) +{ + static const char xdigits_l[] = "0123456789abcdef", + xdigits_u[] = "0123456789ABCDEF"; + unsigned char tmp[IN6ADDRSZ], *tp, *endp, *colonp; + const char *xdigits, *curtok; + int ch, saw_xdigit, count_xdigit; + unsigned int val; + + memset((tp = tmp), '\0', IN6ADDRSZ); + endp = tp + IN6ADDRSZ; + colonp = NULL; + /* Leading :: requires some special handling. */ + if (*src == ':') + if (*++src != ':') + return (0); + curtok = src; + saw_xdigit = count_xdigit = 0; + val = 0; + while ((ch = *src++) != '\0') { + const char *pch; + + if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) + pch = strchr((xdigits = xdigits_u), ch); + if (pch != NULL) { + if (count_xdigit >= 4) + return (0); + val <<= 4; + val |= (pch - xdigits); + if (val > 0xffff) + return (0); + saw_xdigit = 1; + count_xdigit++; + continue; + } + if (ch == ':') { + curtok = src; + if (!saw_xdigit) { + if (colonp) + return (0); + colonp = tp; + continue; + } else if (*src == '\0') { + return (0); + } + if (tp + sizeof(int16_t) > endp) + return (0); + *tp++ = (unsigned char) ((val >> 8) & 0xff); + *tp++ = (unsigned char) (val & 0xff); + saw_xdigit = 0; + count_xdigit = 0; + val = 0; + continue; + } + if (ch == '.' && ((tp + INADDRSZ) <= endp) && + inet_pton4(curtok, tp) > 0) { + tp += INADDRSZ; + saw_xdigit = 0; + count_xdigit = 0; + break; /* '\0' was seen by inet_pton4(). */ + } + return (0); + } + if (saw_xdigit) { + if (tp + sizeof(int16_t) > endp) + return (0); + *tp++ = (unsigned char) ((val >> 8) & 0xff); + *tp++ = (unsigned char) (val & 0xff); + } + if (colonp != NULL) { + /* + * Since some memmove()'s erroneously fail to handle + * overlapping regions, we'll do the shift by hand. + */ + const int n = tp - colonp; + int i; + + for (i = 1; i <= n; i++) { + endp[- i] = colonp[n - i]; + colonp[n - i] = 0; + } + tp = endp; + } + if (tp != endp) + return (0); + memcpy(dst, tmp, IN6ADDRSZ); + return (1); +} + +static int +cmd_parse_ipaddr(ucg_cmd_tk_hdr_t *tk, const char *buf, + void *res, unsigned ressize) +{ + struct ucg_cmd_tk_ipaddr *tk2 = + (struct ucg_cmd_tk_ipaddr *)tk; + unsigned int token_len = 0; + char ip_str[INET6_ADDRSTRLEN+4]; /* '+4' is for prefixlen (if any) */ + ucg_cmd_ipaddr_t ipaddr; + char *prefix, *prefix_end; + long prefixlen; + + if (res && ressize < sizeof(ucg_cmd_ipaddr_t)) + return -1; + + memset(&ipaddr, 0, sizeof(ipaddr)); + + /* if token is too big... */ + token_len = snprintf(ip_str, sizeof(ip_str), "%s", buf); + if (token_len >= sizeof(ip_str)) + return -1; + + /* convert the network prefix */ + if (tk2->ipaddr_data.flags & UCG_CMD_IPADDR_NETWORK) { + prefix = strrchr(ip_str, '/'); + if (prefix == NULL) + return -1; + *prefix = '\0'; + prefix ++; + errno = 0; + prefixlen = strtol(prefix, &prefix_end, 10); + if (errno || (*prefix_end != '\0') ) + return -1; + ipaddr.prefixlen = prefixlen; + } + else { + ipaddr.prefixlen = 0; + } + + /* convert the IP addr */ + if ((tk2->ipaddr_data.flags & UCG_CMD_IPADDR_V4) && + my_inet_pton(AF_INET, ip_str, &ipaddr.addr.ipv4) == 1) { + ipaddr.family = AF_INET; + if (res != NULL) + memcpy(res, &ipaddr, sizeof(ipaddr)); + return 0; + } + if ((tk2->ipaddr_data.flags & UCG_CMD_IPADDR_V6) && + my_inet_pton(AF_INET6, ip_str, &ipaddr.addr.ipv6) == 1) { + ipaddr.family = AF_INET6; + if (res != NULL) + memcpy(res, &ipaddr, sizeof(ipaddr)); + return 0; + } + return -1; + +} + +static int +cmd_help_ipaddr(ucg_cmd_tk_hdr_t *tk, char *dstbuf, + unsigned int size) +{ + struct ucg_cmd_tk_ipaddr *tk2 = + (struct ucg_cmd_tk_ipaddr *)tk; + + switch (tk2->ipaddr_data.flags) { + case UCG_CMD_IPADDR_V4: + snprintf(dstbuf, size, ""); + break; + case UCG_CMD_IPADDR_V6: + snprintf(dstbuf, size, ""); + break; + case UCG_CMD_IPADDR_V4 | UCG_CMD_IPADDR_V6: + snprintf(dstbuf, size, ""); + break; + case UCG_CMD_IPADDR_NETWORK | UCG_CMD_IPADDR_V4: + snprintf(dstbuf, size, ""); + break; + case UCG_CMD_IPADDR_NETWORK | UCG_CMD_IPADDR_V6: + snprintf(dstbuf, size, ""); + break; + case UCG_CMD_IPADDR_NETWORK | UCG_CMD_IPADDR_V4 | + UCG_CMD_IPADDR_V6: + snprintf(dstbuf, size, ""); + break; + default: + snprintf(dstbuf, size, ""); + break; + } + return 0; +} + +struct ucg_cmd_tk_ops cmd_token_ipaddr_ops = { + .parse = cmd_parse_ipaddr, + .complete_start = NULL, + .complete_iterate = NULL, + .complete_end = NULL, + .help = cmd_help_ipaddr, +}; + diff --git a/lib/cmd/ucg_cmd_parse_num.c b/lib/cmd/ucg_cmd_parse_num.c new file mode 100644 index 0000000..7de21c2 --- /dev/null +++ b/lib/cmd/ucg_cmd_parse_num.c @@ -0,0 +1,532 @@ +/* + * Copyright (c) 2009-2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +/*- + * Copyright (c) <2010>, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - 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. + * + * - Neither the name of Intel Corporation nor the names of its + * 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. + */ + +#include +#include +#include +#include +#include + +#include "ucg_cmd_parse.h" +#include "ucg_cmd_parse_num.h" + +//#define debug_printf(args...) printf(args) +#define debug_printf(args...) do {} while(0) + +enum num_parse_state_t { + START, + DEC_NEG, + BIN, + HEX, + FLOAT_POS, + FLOAT_NEG, + + ERROR, + + FIRST_OK, /* not used */ + ZERO_OK, + HEX_OK, + OCTAL_OK, + BIN_OK, + DEC_NEG_OK, + DEC_POS_OK, + FLOAT_POS_OK, + FLOAT_NEG_OK +}; + +/* Keep it sync with enum in .h */ +static const char * num_help[] = { + "", "", "", "", + "", "", "", "", +#ifdef UCG_CMD_HAVE_FLOAT + "", +#endif +}; + +static int +add_to_res(unsigned int c, uint64_t *res, unsigned int base) +{ + /* overflow */ + if ( (UINT64_MAX - c) / base < *res ) { + return -1; + } + + *res = (uint64_t) (*res * base + c); + return 0; +} + +static int +check_res_size(struct ucg_cmd_tk_num_data *nd, unsigned ressize) +{ + switch (nd->type) { + case INT8: + case UINT8: + if (ressize < sizeof(int8_t)) + return -1; + break; + case INT16: + case UINT16: + if (ressize < sizeof(int16_t)) + return -1; + break; + case INT32: + case UINT32: + if (ressize < sizeof(int32_t)) + return -1; + break; + case INT64: + case UINT64: + if (ressize < sizeof(int64_t)) + return -1; + break; +#ifdef UCG_CMD_HAVE_FLOAT + case FLOAT: + if (ressize < sizeof(float)) + return -1; + break; +#endif + default: + return -1; + } + return 0; +} + +/* parse an int or a float */ +static int +cmd_parse_num(ucg_cmd_tk_hdr_t *tk, const char *srcbuf, + void *res, unsigned ressize) +{ + struct ucg_cmd_tk_num_data nd; + enum num_parse_state_t st = START; + const char * buf = srcbuf; + char c = *buf; + uint64_t res1 = 0; +#ifdef UCG_CMD_HAVE_FLOAT + uint64_t res2 = 0, res3 = 1; +#endif + + memcpy(&nd, &((struct ucg_cmd_tk_num *)tk)->num_data, sizeof(nd)); + + /* check that we have enough room in res */ + if (res) { + if (check_res_size(&nd, ressize) < 0) + return -1; + } + + while (st != ERROR && c != '\0') { + debug_printf("%c %x -> ", c, c); + switch (st) { + case START: + if (c == '-') { + st = DEC_NEG; + } + else if (c == '0') { + st = ZERO_OK; + } +#ifdef UCG_CMD_HAVE_FLOAT + else if (c == '.') { + st = FLOAT_POS; + res1 = 0; + } +#endif + else if (c >= '1' && c <= '9') { + if (add_to_res(c - '0', &res1, 10) < 0) + st = ERROR; + else + st = DEC_POS_OK; + } + else { + st = ERROR; + } + break; + + case ZERO_OK: + if (c == 'x') { + st = HEX; + } + else if (c == 'b') { + st = BIN; + } +#ifdef UCG_CMD_HAVE_FLOAT + else if (c == '.') { + st = FLOAT_POS; + res1 = 0; + } +#endif + else if (c >= '0' && c <= '7') { + if (add_to_res(c - '0', &res1, 10) < 0) + st = ERROR; + else + st = OCTAL_OK; + } + else { + st = ERROR; + } + break; + + case DEC_NEG: + if (c >= '0' && c <= '9') { + if (add_to_res(c - '0', &res1, 10) < 0) + st = ERROR; + else + st = DEC_NEG_OK; + } +#ifdef UCG_CMD_HAVE_FLOAT + else if (c == '.') { + res1 = 0; + st = FLOAT_NEG; + } +#endif + else { + st = ERROR; + } + break; + + case DEC_NEG_OK: + if (c >= '0' && c <= '9') { + if (add_to_res(c - '0', &res1, 10) < 0) + st = ERROR; + } +#ifdef UCG_CMD_HAVE_FLOAT + else if (c == '.') { + st = FLOAT_NEG; + } +#endif + else { + st = ERROR; + } + break; + + case DEC_POS_OK: + if (c >= '0' && c <= '9') { + if (add_to_res(c - '0', &res1, 10) < 0) + st = ERROR; + } +#ifdef UCG_CMD_HAVE_FLOAT + else if (c == '.') { + st = FLOAT_POS; + } +#endif + else { + st = ERROR; + } + break; + + case HEX: + st = HEX_OK; + /* no break */ + case HEX_OK: + if (c >= '0' && c <= '9') { + if (add_to_res(c - '0', &res1, 16) < 0) + st = ERROR; + } + else if (c >= 'a' && c <= 'f') { + if (add_to_res(c - 'a' + 10, &res1, 16) < 0) + st = ERROR; + } + else if (c >= 'A' && c <= 'F') { + if (add_to_res(c - 'A' + 10, &res1, 16) < 0) + st = ERROR; + } + else { + st = ERROR; + } + break; + + + case OCTAL_OK: + if (c >= '0' && c <= '7') { + if (add_to_res(c - '0', &res1, 8) < 0) + st = ERROR; + } + else { + st = ERROR; + } + break; + + case BIN: + st = BIN_OK; + /* no break */ + case BIN_OK: + if (c >= '0' && c <= '1') { + if (add_to_res(c - '0', &res1, 2) < 0) + st = ERROR; + } + else { + st = ERROR; + } + break; + +#ifdef UCG_CMD_HAVE_FLOAT + case FLOAT_POS: + if (c >= '0' && c <= '9') { + if (add_to_res(c - '0', &res2, 10) < 0) + st = ERROR; + else + st = FLOAT_POS_OK; + res3 = 10; + } + else { + st = ERROR; + } + break; + + case FLOAT_NEG: + if (c >= '0' && c <= '9') { + if (add_to_res(c - '0', &res2, 10) < 0) + st = ERROR; + else + st = FLOAT_NEG_OK; + res3 = 10; + } + else { + st = ERROR; + } + break; + + case FLOAT_POS_OK: + if (c >= '0' && c <= '9') { + if (add_to_res(c - '0', &res2, 10) < 0) + st = ERROR; + if (add_to_res(0, &res3, 10) < 0) + st = ERROR; + } + else { + st = ERROR; + } + break; + + case FLOAT_NEG_OK: + if (c >= '0' && c <= '9') { + if (add_to_res(c - '0', &res2, 10) < 0) + st = ERROR; + if (add_to_res(0, &res3, 10) < 0) + st = ERROR; + } + else { + st = ERROR; + } + break; +#endif + + default: + debug_printf("not impl "); + + } + +#ifdef UCG_CMD_HAVE_FLOAT + debug_printf("(%"PRIu32") (%"PRIu32") (%"PRIu32")\n", + res1, res2, res3); +#else + debug_printf("(%"PRIu32")\n", res1); +#endif + + buf ++; + c = *buf; + + /* token too long */ + if (buf-srcbuf > 127) + return -1; + } + + switch (st) { + case ZERO_OK: + case DEC_POS_OK: + case HEX_OK: + case OCTAL_OK: + case BIN_OK: + if ( nd.type == INT8 && res1 <= INT8_MAX ) { + if (res) + *(int8_t *)res = (int8_t) res1; + return 0; + } + else if ( nd.type == INT16 && res1 <= INT16_MAX ) { + if (res) + *(int16_t *)res = (int16_t) res1; + return 0; + } + else if ( nd.type == INT32 && res1 <= INT32_MAX ) { + if (res) + *(int32_t *)res = (int32_t) res1; + return 0; + } + else if ( nd.type == UINT8 && res1 <= UINT8_MAX ) { + if (res) + *(uint8_t *)res = (uint8_t) res1; + return 0; + } + else if (nd.type == UINT16 && res1 <= UINT16_MAX ) { + if (res) + *(uint16_t *)res = (uint16_t) res1; + return 0; + } + else if ( nd.type == UINT32 ) { + if (res) + *(uint32_t *)res = (uint32_t) res1; + return 0; + } + else if ( nd.type == UINT64 ) { + if (res) + *(uint64_t *)res = res1; + return 0; + } +#ifdef UCG_CMD_HAVE_FLOAT + else if ( nd.type == FLOAT ) { + if (res) + *(float *)res = (float)res1; + return 0; + } +#endif + else { + return -1; + } + break; + + case DEC_NEG_OK: + if ( nd.type == INT8 && res1 <= INT8_MAX + 1 ) { + if (res) + *(int8_t *)res = (int8_t) (-res1); + return 0; + } + else if ( nd.type == INT16 && res1 <= (uint16_t)INT16_MAX + 1 ) { + if (res) + *(int16_t *)res = (int16_t) (-res1); + return 0; + } + else if ( nd.type == INT32 && res1 <= (uint32_t)INT32_MAX + 1 ) { + if (res) + *(int32_t *)res = (int32_t) (-res1); + return 0; + } +#ifdef UCG_CMD_HAVE_FLOAT + else if ( nd.type == FLOAT ) { + if (res) + *(float *)res = - (float)res1; + return 0; + } +#endif + else { + return -1; + } + break; + +#ifdef UCG_CMD_HAVE_FLOAT + case FLOAT_POS: + case FLOAT_POS_OK: + if ( nd.type == FLOAT ) { + if (res) + *(float *)res = (float)res1 + + ((float)res2 / (float)res3); + return 0; + + } + else { + return -1; + } + break; + + case FLOAT_NEG: + case FLOAT_NEG_OK: + if ( nd.type == FLOAT ) { + if (res) + *(float *)res = - ((float)res1 + +((float)res2 / (float)res3)); + return 0; + + } + else { + return -1; + } + break; +#endif + default: + debug_printf("error\n"); + return -1; + } +} + + +/* parse an int or a float */ +static int +cmd_help_num(ucg_cmd_tk_hdr_t *tk, char *dstbuf, + unsigned int size) +{ + struct ucg_cmd_tk_num_data nd; + + memcpy(&nd, &((struct ucg_cmd_tk_num *)tk)->num_data, sizeof(nd)); + + /* should not happen.... don't so this test */ + /* if (nd.type >= (sizeof(num_help)/sizeof(const char *))) */ + /* return -1; */ + + strncpy(dstbuf, num_help[nd.type], size); + dstbuf[size-1] = '\0'; + return 0; +} + + +struct ucg_cmd_tk_ops ucg_cmd_tk_num_ops = { + .parse = cmd_parse_num, + .complete_start = NULL, + .complete_iterate = NULL, + .complete_end = NULL, + .help = cmd_help_num, +}; diff --git a/lib/cmd/ucg_cmd_parse_string.c b/lib/cmd/ucg_cmd_parse_string.c new file mode 100644 index 0000000..35778c8 --- /dev/null +++ b/lib/cmd/ucg_cmd_parse_string.c @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2009-2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +/*- + * Copyright (c) <2010>, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - 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. + * + * - Neither the name of Intel Corporation nor the names of its + * 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. + */ + +#include +#include +#include +#include + +#include "ucg_cmd_parse.h" +#include "ucg_cmd_parse_string.h" + +#define MULTISTRING_HELP "Mul-choice STRING" +#define ANYSTRING_HELP "Any STRING" +#define FIXEDSTRING_HELP "Fixed STRING" + +static unsigned int +get_token_len(const char *s) +{ + char c; + unsigned int i=0; + + c = s[i]; + while (c!='#' && c!='\0') { + i++; + c = s[i]; + } + return i; +} + +static const char * +get_next_token(const char *s) +{ + unsigned int i; + i = get_token_len(s); + if (s[i] == '#') + return s+i+1; + return NULL; +} + +static int +parse_fixed_string(struct ucg_cmd_tk_string_data *sd, + const char *buf, unsigned token_len) +{ + unsigned int conf_token_len; + const char *str; + + str = sd->str; + for (str = sd->str; str != NULL ; str = get_next_token(str)) { + + conf_token_len = get_token_len(str); + + /* if token from config is too big... */ + if (conf_token_len >= UCG_STR_TOKEN_SIZE - 1) + continue; + + /* compare conf token and user token */ + if (token_len == conf_token_len && + strncmp(buf, str, token_len) == 0) + return 0; + } + + return -1; +} + +static int +cmd_parse_string(ucg_cmd_tk_hdr_t *tk, + const char *buf, void *res, unsigned ressize) +{ + struct ucg_cmd_tk_string *tk2 = + (struct ucg_cmd_tk_string *)tk; + struct ucg_cmd_tk_string_data *sd = &tk2->string_data;; + unsigned int token_len; + + if (res && ressize < UCG_STR_TOKEN_SIZE) + return -1; + + token_len = strlen(buf); + + if (token_len >= (UCG_STR_TOKEN_SIZE - 1) || token_len == 0) + return -1; + + /* fixed string */ + if (sd->str) { + if (parse_fixed_string(sd, buf, token_len) < 0) + return -1; + } + + /* we already checked that token_len is < STR_TOKEN_SIZE-1 */ + if (res) + strcpy(res, buf); + + return 0; +} + +static int +cmd_complete_string_start(ucg_cmd_tk_hdr_t *tk, + __attribute__((unused)) const char *tokstr, + void **opaque) +{ + struct ucg_cmd_tk_string *tk2 = + (struct ucg_cmd_tk_string *)tk; + struct ucg_cmd_tk_string_data *sd = &tk2->string_data;; + const char *str; + + str = sd->str; + *opaque = (void *)str; + if (str == NULL) + return -1; /* no completion */ + return 0; +} + +static int +cmd_complete_string_iterate(ucg_cmd_tk_hdr_t *tk, void **opaque, + char *dstbuf, unsigned int size) +{ + const char *s; + unsigned int len; + + (void)tk; + s = *opaque; + if (s == NULL) + return -1; + *opaque = (void *)get_next_token(s); + + len = get_token_len(s); + if (len > size - 1) + return -1; + + memcpy(dstbuf, s, len); + dstbuf[len] = '\0'; + return 0; +} + +static int +cmd_help_string(ucg_cmd_tk_hdr_t *tk, char *dstbuf, + unsigned int size) +{ + (void)tk; + snprintf(dstbuf, size, ""); + return 0; +} + +struct ucg_cmd_tk_ops ucg_cmd_tk_string_ops = { + .parse = cmd_parse_string, + .complete_start = cmd_complete_string_start, + .complete_iterate = cmd_complete_string_iterate, + .complete_end = NULL, + .help = cmd_help_string, +}; diff --git a/lib/cmd/ucg_cmd_rdline.c b/lib/cmd/ucg_cmd_rdline.c new file mode 100644 index 0000000..25e65f0 --- /dev/null +++ b/lib/cmd/ucg_cmd_rdline.c @@ -0,0 +1,934 @@ +/* + * Copyright (c) 2009-2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +/*- + * Copyright (c) <2010>, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - 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. + * + * - Neither the name of Intel Corporation nor the names of its + * 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. + */ + +#ifndef UCG_CMD_NO_PAGER +#define _GNU_SOURCE /* for vasprintf */ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ucg_cmd_rdline.h" +#include "ucg_cmd_parse.h" + +#ifndef UCG_CMD_NO_RDLINE_HISTORY +static void rdline_remove_old_history_item(struct ucg_rdline *rdl); +static void rdline_remove_first_history_item(struct ucg_rdline *rdl); +static unsigned int rdline_get_history_size(struct ucg_rdline *rdl); +#endif /* !UCG_CMD_NO_RDLINE_HISTORY */ + +#ifndef UCG_CMD_NO_PAGER +static int rdline_pager_next_page(struct ucg_rdline *rdl); +static void rdline_pager_reset(struct ucg_rdline *rdl); +#endif /* !UCG_CMD_NO_PAGER */ + + +/* isblank() needs _XOPEN_SOURCE >= 600 || _ISOC99_SOURCE, so use our + * own. */ +static int +isblank2(char c) +{ + if (c == ' ' || + c == '\t' ) + return 1; + return 0; +} + +void +ucg_rdline_init(struct ucg_rdline *rdl, + FILE *f_in, FILE *f_out, + ucg_rdline_validate_t *validate, + ucg_rdline_complete_t *complete, + ucg_rdline_help_t *help) +{ + memset(rdl, 0, sizeof(*rdl)); + rdl->f_in = f_in; + rdl->f_out = f_out; + rdl->validate = validate; + rdl->complete = complete; + rdl->help = help; + rdl->status = UCG_RDLINE_STOPPED; + + /* Disable buffering */ + setbuf(f_in, NULL); + setbuf(f_out, NULL); + +#ifndef UCG_CMD_NO_RDLINE_HISTORY + ucg_cirbuf_init(&rdl->history, rdl->history_buf, 0, + UCG_RDLINE_HISTORY_BUF_SIZE); +#endif /* !UCG_CMD_NO_RDLINE_HISTORY */ +} + +void +ucg_rdline_newline(struct ucg_rdline *rdl, const char *prompt) +{ + ucg_vt100_init(&rdl->vt100); + ucg_cirbuf_init(&rdl->left, rdl->left_buf, 0, UCG_RDLINE_BUF_SIZE); + ucg_cirbuf_init(&rdl->right, rdl->right_buf, 0, UCG_RDLINE_BUF_SIZE); + + /* if pointer is the same or NULL, don't copy it */ + if (prompt != NULL && prompt != rdl->prompt) + snprintf(rdl->prompt, sizeof(rdl->prompt), "%s", prompt); + + ucg_rdline_printf(rdl, "%s", rdl->prompt); + rdl->status = UCG_RDLINE_RUNNING; + +#ifndef UCG_CMD_NO_RDLINE_HISTORY + rdl->history_cur_line = -1; +#endif /* !UCG_CMD_NO_RDLINE_HISTORY */ +} + +void +ucg_rdline_stop(struct ucg_rdline *rdl) +{ + rdl->status = UCG_RDLINE_STOPPED; +} + +void +ucg_rdline_quit(struct ucg_rdline *rdl) +{ + rdl->status = UCG_RDLINE_EXITED; +} + +void +ucg_rdline_restart(struct ucg_rdline *rdl) +{ + rdl->status = UCG_RDLINE_RUNNING; +} + +const char * +ucg_rdline_get_buffer(struct ucg_rdline *rdl) +{ + unsigned int len_l, len_r; + ucg_cirbuf_align_left(&rdl->left); + ucg_cirbuf_align_left(&rdl->right); + + len_l = ucg_cirbuf_get_len(&rdl->left); + len_r = ucg_cirbuf_get_len(&rdl->right); + memcpy(rdl->left_buf+len_l, rdl->right_buf, len_r); + + rdl->left_buf[len_l + len_r] = '\0'; + return rdl->left_buf; +} + +static void +display_right_buffer(struct ucg_rdline *rdl, int force) +{ + unsigned int i; + char tmp; + + if (!force && ucg_cirbuf_is_empty(&rdl->right)) + return; + + ucg_rdline_printf(rdl, ucg_vt100_clear_right); + UCG_CIRBUF_FOREACH(&rdl->right, i, tmp) { + ucg_rdline_printf(rdl, "%c", tmp); + } + if (!ucg_cirbuf_is_empty(&rdl->right)) + ucg_rdline_printf(rdl, ucg_vt100_multi_left, + ucg_cirbuf_get_len(&rdl->right)); +} + +void +ucg_rdline_redisplay(struct ucg_rdline *rdl) +{ + unsigned int i; + char tmp; + + ucg_rdline_printf(rdl, ucg_vt100_home); + ucg_rdline_printf(rdl, "%s", rdl->prompt); + UCG_CIRBUF_FOREACH(&rdl->left, i, tmp) { + ucg_rdline_printf(rdl, "%c", tmp); + } + display_right_buffer(rdl, 1); +} + +static int +rdline_parse_char(struct ucg_rdline *rdl, char c) +{ + unsigned int i; + int cmd; + char tmp; +#ifndef UCG_CMD_NO_RDLINE_HISTORY + const char *history; +#endif + + cmd = ucg_vt100_parser(&rdl->vt100, c); + if (cmd == UCG_VT100_NOT_COMPLETE) + return UCG_RDLINE_RES_SUCCESS; + +#ifndef UCG_CMD_NO_PAGER + /* display asynchrounous printf if any */ + if (rdl->pager_buf != NULL) { + + /* user ask to exit pager, or last page is displayed*/ + if ((cmd == UCG_VT100_STD_CHAR && c == 'q') || + rdline_pager_next_page(rdl) == 0) { + int ret; + + ret = rdl->pager_ret; + rdline_pager_reset(rdl); + if (rdl->pager_cb != NULL) { + rdl->pager_cb(rdl, rdl->pager_arg); + rdl->pager_cb = NULL; + } + /* maybe the pager was reloaded in the + * callback */ + if (rdl->pager_buf != NULL) + return UCG_RDLINE_RES_SUCCESS; + + /* else, redisplay prompt and return the saved status */ + ucg_rdline_redisplay(rdl); + return ret; + } + + /* Some pages remain, lines were displayed in + * rdline_pager_next_page() */ + return UCG_RDLINE_RES_SUCCESS; + } +#endif + + /* process control chars */ + if (cmd != UCG_VT100_STD_CHAR) { + switch (cmd) { + case UCG_CMD_KEY_CTRL_B: + case UCG_CMD_KEY_LEFT_ARR: + if (ucg_cirbuf_is_empty(&rdl->left)) + break; + tmp = ucg_cirbuf_get_tail(&rdl->left); + ucg_cirbuf_del_tail(&rdl->left); + ucg_cirbuf_add_head(&rdl->right, tmp); + ucg_rdline_printf(rdl, ucg_vt100_left_arr); + break; + + case UCG_CMD_KEY_CTRL_F: + case UCG_CMD_KEY_RIGHT_ARR: + if (ucg_cirbuf_is_empty(&rdl->right)) + break; + tmp = ucg_cirbuf_get_head(&rdl->right); + ucg_cirbuf_del_head(&rdl->right); + ucg_cirbuf_add_tail(&rdl->left, tmp); + ucg_rdline_printf(rdl, ucg_vt100_right_arr); + break; + + case UCG_CMD_KEY_WLEFT: + while (! ucg_cirbuf_is_empty(&rdl->left) && + (tmp = ucg_cirbuf_get_tail(&rdl->left)) && + isblank2(tmp)) { + ucg_rdline_printf(rdl, ucg_vt100_left_arr); + ucg_cirbuf_del_tail(&rdl->left); + ucg_cirbuf_add_head(&rdl->right, tmp); + } + while (! ucg_cirbuf_is_empty(&rdl->left) && + (tmp = ucg_cirbuf_get_tail(&rdl->left)) && + !isblank2(tmp)) { + ucg_rdline_printf(rdl, ucg_vt100_left_arr); + ucg_cirbuf_del_tail(&rdl->left); + ucg_cirbuf_add_head(&rdl->right, tmp); + } + break; + + case UCG_CMD_KEY_WRIGHT: + while (! ucg_cirbuf_is_empty(&rdl->right) && + (tmp = ucg_cirbuf_get_head(&rdl->right)) && + isblank2(tmp)) { + ucg_rdline_printf(rdl, ucg_vt100_right_arr); + ucg_cirbuf_del_head(&rdl->right); + ucg_cirbuf_add_tail(&rdl->left, tmp); + } + while (! ucg_cirbuf_is_empty(&rdl->right) && + (tmp = ucg_cirbuf_get_head(&rdl->right)) && + !isblank2(tmp)) { + ucg_rdline_printf(rdl, ucg_vt100_right_arr); + ucg_cirbuf_del_head(&rdl->right); + ucg_cirbuf_add_tail(&rdl->left, tmp); + } + break; + + case UCG_CMD_KEY_BKSPACE: + if(!ucg_cirbuf_del_tail_safe(&rdl->left)) { + ucg_rdline_printf(rdl, ucg_vt100_bs); + display_right_buffer(rdl, 1); + } + break; + + case UCG_CMD_KEY_META_BKSPACE: + case UCG_CMD_KEY_CTRL_W: + while (! ucg_cirbuf_is_empty(&rdl->left) && + isblank2(ucg_cirbuf_get_tail(&rdl->left))) { + ucg_rdline_printf(rdl, ucg_vt100_bs); + ucg_cirbuf_del_tail(&rdl->left); + } + while (! ucg_cirbuf_is_empty(&rdl->left) && + !isblank2(ucg_cirbuf_get_tail(&rdl->left))) { + ucg_rdline_printf(rdl, ucg_vt100_bs); + ucg_cirbuf_del_tail(&rdl->left); + } + display_right_buffer(rdl, 1); + break; + + case UCG_CMD_KEY_META_D: + while (! ucg_cirbuf_is_empty(&rdl->right) && + isblank2(ucg_cirbuf_get_head(&rdl->right))) + ucg_cirbuf_del_head(&rdl->right); + while (! ucg_cirbuf_is_empty(&rdl->right) && + !isblank2(ucg_cirbuf_get_head(&rdl->right))) + ucg_cirbuf_del_head(&rdl->right); + display_right_buffer(rdl, 1); + break; + + case UCG_CMD_KEY_SUPPR: + case UCG_CMD_KEY_CTRL_D: + if (cmd == UCG_CMD_KEY_CTRL_D && + ucg_cirbuf_is_empty(&rdl->left) && + ucg_cirbuf_is_empty(&rdl->right)) { + return UCG_RDLINE_RES_EOF; + } + if (!ucg_cirbuf_del_head_safe(&rdl->right)) { + display_right_buffer(rdl, 1); + } + break; + + case UCG_CMD_KEY_CTRL_A: + if (ucg_cirbuf_is_empty(&rdl->left)) + break; + ucg_rdline_printf(rdl, ucg_vt100_multi_left, + ucg_cirbuf_get_len(&rdl->left)); + while (! ucg_cirbuf_is_empty(&rdl->left)) { + tmp = ucg_cirbuf_get_tail(&rdl->left); + ucg_cirbuf_del_tail(&rdl->left); + ucg_cirbuf_add_head(&rdl->right, tmp); + } + break; + + case UCG_CMD_KEY_CTRL_E: + if (ucg_cirbuf_is_empty(&rdl->right)) + break; + ucg_rdline_printf(rdl, ucg_vt100_multi_right, + ucg_cirbuf_get_len(&rdl->right)); + while (! ucg_cirbuf_is_empty(&rdl->right)) { + tmp = ucg_cirbuf_get_head(&rdl->right); + ucg_cirbuf_del_head(&rdl->right); + ucg_cirbuf_add_tail(&rdl->left, tmp); + } + break; + +#ifndef UCG_CMD_NO_RDLINE_KILL_BUF + case UCG_CMD_KEY_CTRL_K: + ucg_cirbuf_get_buf_head(&rdl->right, rdl->kill_buf, + UCG_RDLINE_BUF_SIZE); + rdl->kill_size = ucg_cirbuf_get_len(&rdl->right); + ucg_cirbuf_del_buf_head(&rdl->right, rdl->kill_size); + ucg_rdline_printf(rdl, ucg_vt100_clear_right); + break; + + case UCG_CMD_KEY_CTRL_Y: + i=0; + while (ucg_cirbuf_get_len(&rdl->right) + + ucg_cirbuf_get_len(&rdl->left) < + UCG_RDLINE_BUF_SIZE && + i < rdl->kill_size) { + ucg_cirbuf_add_tail(&rdl->left, rdl->kill_buf[i]); + ucg_rdline_printf(rdl, "%c", rdl->kill_buf[i]); + i++; + } + display_right_buffer(rdl, 0); + break; +#endif /* !UCG_CMD_NO_RDLINE_KILL_BUF */ + + case UCG_CMD_KEY_CTRL_C: + ucg_rdline_printf(rdl, "\r\n"); + ucg_rdline_newline(rdl, rdl->prompt); + break; + + case UCG_CMD_KEY_CTRL_L: + ucg_rdline_redisplay(rdl); + break; + + case UCG_CMD_KEY_HELP: { + if (rdl->help == NULL) + break; + + ucg_cirbuf_align_left(&rdl->left); + rdl->left_buf[ucg_cirbuf_get_len(&rdl->left)] = '\0'; + ucg_rdline_printf(rdl, "\r\n"); + rdl->help(rdl, rdl->left_buf); +#ifndef UCG_CMD_NO_PAGER + if (rdl->pager_buf != NULL) + return UCG_RDLINE_RES_SUCCESS; + else + rdline_pager_reset(rdl); +#endif + ucg_rdline_redisplay(rdl); + break; + } + + case UCG_CMD_KEY_TAB: { + char tmp_buf[UCG_CMD_MAX_TOKEN_SIZE]; + int ret; + unsigned int tmp_size; + + if (rdl->complete == NULL) + break; + + ucg_cirbuf_align_left(&rdl->left); + rdl->left_buf[ucg_cirbuf_get_len(&rdl->left)] = '\0'; + + /* try to complete the complete() */ + ret = rdl->complete(rdl, rdl->left_buf, + tmp_buf, sizeof(tmp_buf)); + + /* no completion or error */ + if (ret < 0) { + ucg_cirbuf_align_left(&rdl->left); + rdl->left_buf[ucg_cirbuf_get_len(&rdl->left)] = '\0'; + ucg_rdline_printf(rdl, "\r\n"); + rdl->help(rdl, rdl->left_buf); +#ifndef UCG_CMD_NO_PAGER + if (rdl->pager_buf != NULL) + return UCG_RDLINE_RES_SUCCESS; + else + rdline_pager_reset(rdl); +#endif + ucg_rdline_redisplay(rdl); + break; + } + + tmp_size = strlen(tmp_buf); + /* add chars */ + i = 0; + while(ucg_cirbuf_get_len(&rdl->right) + + ucg_cirbuf_get_len(&rdl->left) < + UCG_RDLINE_BUF_SIZE && + i < tmp_size) { + ucg_cirbuf_add_tail(&rdl->left, tmp_buf[i]); + ucg_rdline_printf(rdl, "%c", tmp_buf[i]); + i++; + } + display_right_buffer(rdl, 1); + break; + } + + case UCG_CMD_KEY_RETURN: + case UCG_CMD_KEY_RETURN2: { + char tmp; + while (!ucg_cirbuf_is_empty(&rdl->right) && + (tmp = ucg_cirbuf_get_head(&rdl->right))) { + ucg_cirbuf_del_head(&rdl->right); + ucg_cirbuf_add_tail(&rdl->left, tmp); + } + ucg_cirbuf_align_left(&rdl->left); + rdl->left_buf[ucg_cirbuf_get_len(&rdl->left)] = '\0'; + ucg_rdline_printf(rdl, "\r\n"); +#ifndef UCG_CMD_NO_RDLINE_HISTORY + if (rdl->history_cur_line != -1) + rdline_remove_first_history_item(rdl); +#endif + + if (rdl->validate) + rdl->validate(rdl, rdl->left_buf); +#ifndef UCG_CMD_NO_PAGER + /* user may have stopped rdline */ + if (rdl->status == UCG_RDLINE_EXITED) { + rdline_pager_reset(rdl); + return UCG_RDLINE_RES_EXITED; + } + /* there is something in pager buffer, save + * return value that will be return once + * paging is finished */ + if (rdl->pager_buf != NULL) { + rdl->pager_ret = UCG_RDLINE_RES_VALIDATED; + return UCG_RDLINE_RES_SUCCESS; + } + + rdline_pager_reset(rdl); +#else + if (rdl->status == UCG_RDLINE_EXITED) + return UCG_RDLINE_RES_EXITED; +#endif + return UCG_RDLINE_RES_VALIDATED; + } +#ifndef UCG_CMD_NO_RDLINE_HISTORY + case UCG_CMD_KEY_UP_ARR: + case UCG_CMD_KEY_CTRL_P: + if (rdl->history_cur_line == 0) { + rdline_remove_first_history_item(rdl); + } + if (rdl->history_cur_line <= 0) { + ucg_rdline_add_history(rdl, + ucg_rdline_get_buffer(rdl)); + rdl->history_cur_line = 0; + } + + history = ucg_rdline_get_history_item(rdl, + rdl->history_cur_line + 1); + if (history == NULL) + break; + + rdl->history_cur_line++; + ucg_vt100_init(&rdl->vt100); + ucg_cirbuf_init(&rdl->left, rdl->left_buf, 0, + UCG_RDLINE_BUF_SIZE); + ucg_cirbuf_init(&rdl->right, rdl->right_buf, 0, + UCG_RDLINE_BUF_SIZE); + ucg_cirbuf_add_buf_tail(&rdl->left, history, + strlen(history)); + ucg_rdline_redisplay(rdl); + break; + + case UCG_CMD_KEY_DOWN_ARR: + case UCG_CMD_KEY_CTRL_N: + if (rdl->history_cur_line - 1 < 0) + break; + + rdl->history_cur_line--; + history = ucg_rdline_get_history_item(rdl, + rdl->history_cur_line); + if (history == NULL) + break; + + ucg_vt100_init(&rdl->vt100); + ucg_cirbuf_init(&rdl->left, rdl->left_buf, 0, + UCG_RDLINE_BUF_SIZE); + ucg_cirbuf_init(&rdl->right, rdl->right_buf, 0, + UCG_RDLINE_BUF_SIZE); + ucg_cirbuf_add_buf_tail(&rdl->left, history, + strlen(history)); + ucg_rdline_redisplay(rdl); + + break; +#endif /* !UCG_CMD_NO_RDLINE_HISTORY */ + + default: + break; + } + + return UCG_RDLINE_RES_SUCCESS; + } + + if (!isprint((int)c)) + return UCG_RDLINE_RES_SUCCESS; + + /* standard chars */ + if (ucg_cirbuf_get_len(&rdl->left) + + ucg_cirbuf_get_len(&rdl->right) >= UCG_RDLINE_BUF_SIZE) + return UCG_RDLINE_RES_SUCCESS; + + if (ucg_cirbuf_add_tail_safe(&rdl->left, c)) + return UCG_RDLINE_RES_SUCCESS; + + ucg_rdline_printf(rdl, "%c", c); + display_right_buffer(rdl, 0); + + return UCG_RDLINE_RES_SUCCESS; +} + +int +ucg_rdline_char_in(struct ucg_rdline *rdl, char c) +{ + int ret, same = 0; + const char *history, *buffer; + + if (rdl->status == UCG_RDLINE_EXITED) + return UCG_RDLINE_RES_EXITED; + if (rdl->status != UCG_RDLINE_RUNNING) + return UCG_RDLINE_RES_NOT_RUNNING; + + ret = rdline_parse_char(rdl, c); + + /* add line to history */ + if (ret == UCG_RDLINE_RES_VALIDATED) { + buffer = ucg_rdline_get_buffer(rdl); + history = ucg_rdline_get_history_item(rdl, 0); + if (history) + same = !strcmp(buffer, history); + + if (strlen(buffer) >= 1 && same == 0) + ucg_rdline_add_history(rdl, buffer); + } + + return ret; +} + +int +ucg_rdline(struct ucg_rdline *rdl, const char *prompt, unsigned flags) +{ + char c; + int ret = UCG_RDLINE_RES_NOT_RUNNING; + + ucg_rdline_newline(rdl, prompt); + while (1) { + if (fread(&c, 1, 1, rdl->f_in) == 0) { + if (flags & UCG_RDLINE_F_IGNORE_EOF) { + clearerr(rdl->f_in); + continue; + } + break; + } + ret = ucg_rdline_char_in(rdl, c); + if (ret != UCG_RDLINE_RES_SUCCESS) + break; + } + + return ret; +} + +/* HISTORY */ + +#ifndef UCG_CMD_NO_RDLINE_HISTORY +static void +rdline_remove_old_history_item(struct ucg_rdline *rdl) +{ + char tmp; + + while (! ucg_cirbuf_is_empty(&rdl->history) ) { + tmp = ucg_cirbuf_get_head(&rdl->history); + ucg_cirbuf_del_head(&rdl->history); + if (!tmp) + break; + } +} + +static void +rdline_remove_first_history_item(struct ucg_rdline *rdl) +{ + char tmp; + + if ( ucg_cirbuf_is_empty(&rdl->history) ) { + return; + } + else { + ucg_cirbuf_del_tail(&rdl->history); + } + + while (! ucg_cirbuf_is_empty(&rdl->history) ) { + tmp = ucg_cirbuf_get_tail(&rdl->history); + if (!tmp) + break; + ucg_cirbuf_del_tail(&rdl->history); + } +} + +static unsigned int +rdline_get_history_size(struct ucg_rdline *rdl) +{ + unsigned int i, tmp, ret=0; + + UCG_CIRBUF_FOREACH(&rdl->history, i, tmp) { + if (tmp == 0) + ret ++; + } + + return ret; +} + +const char * +ucg_rdline_get_history_item(struct ucg_rdline *rdl, unsigned int idx) +{ + unsigned int len, i, tmp; + + len = rdline_get_history_size(rdl); + if (idx >= len) + return NULL; + + ucg_cirbuf_align_left(&rdl->history); + + UCG_CIRBUF_FOREACH(&rdl->history, i, tmp) { + if (idx == len - 1) { + return rdl->history_buf + i; + } + if (tmp == 0) + len --; + } + + return NULL; +} + +int +ucg_rdline_add_history(struct ucg_rdline *rdl, const char *buf) +{ + unsigned int len; + + len = strlen(buf); + if (len >= UCG_RDLINE_HISTORY_BUF_SIZE) + return -1; + + while (len >= ucg_cirbuf_get_freelen(&rdl->history)) { + rdline_remove_old_history_item(rdl); + } + + ucg_cirbuf_add_buf_tail(&rdl->history, buf, len); + ucg_cirbuf_add_tail(&rdl->history, 0); + + return 0; +} + +void +ucg_rdline_clear_history(struct ucg_rdline *rdl) +{ + ucg_cirbuf_init(&rdl->history, rdl->history_buf, 0, + UCG_RDLINE_HISTORY_BUF_SIZE); +} + +#else /* !UCG_CMD_NO_RDLINE_HISTORY */ + +int ucg_rdline_add_history(struct ucg_rdline *rdl, const char *buf) +{ + return -1; +} + +void ucg_rdline_clear_history(struct ucg_rdline *rdl) +{ + return; +} + +char *ucg_rdline_get_history_item(struct ucg_rdline *rdl, unsigned int i) +{ + return NULL; +} + + +#endif /* !UCG_CMD_NO_RDLINE_HISTORY */ + + +ssize_t +ucg_rdline_write(struct ucg_rdline *rdl, void *buf, size_t count) +{ + return fwrite(buf, 1, count, rdl->f_out); +} + +int +ucg_rdline_vprintf(struct ucg_rdline *rdl, const char *fmt, va_list ap) +{ + if (rdl->f_out == NULL) + return -1; + + return vfprintf(rdl->f_out, fmt, ap); +} + +int +ucg_rdline_printf(struct ucg_rdline *rdl, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = ucg_rdline_vprintf(rdl, fmt, ap); + va_end(ap); + + return ret; +} + +#ifndef UCG_CMD_NO_PAGER +/* reset pager state */ +void +rdline_pager_reset(struct ucg_rdline *rdl) +{ + if (rdl->pager_buf) { + free(rdl->pager_buf); + rdl->pager_buf = NULL; + } + rdl->pager_lines = 0; + rdl->pager_len = 0; + rdl->pager_off = 0; + rdl->pager_cb = NULL; + rdl->pager_arg = NULL; + rdl->pager_ret = UCG_RDLINE_RES_SUCCESS; +} + +/* Return the offset of the i-th occurence of char c in string s. If + * there is less than i occurences, return -1 and fill i with the + * count. */ +static int +strnchr(const char *s, char c, int *i) +{ + int n = 0; + const char *orig = s; + + while (*s) { + if (*s == c) + n++; + if (*i == n) + return s - orig; + s++; + } + *i = n; + return -1; +} + +/* display a page of data from pager, return 0 if all is displayed */ +static int +rdline_pager_next_page(struct ucg_rdline *rdl) +{ + int lines = UCG_RDLINE_MAX_LINES; + int displen; + char *s; + + s = rdl->pager_buf; + if (s == NULL) + return 0; + + ucg_rdline_printf(rdl, ucg_vt100_home); + ucg_rdline_printf(rdl, ucg_vt100_clear_right); + + s += rdl->pager_off; + + /* we know that s is 0-terminated */ + displen = strnchr(s, '\n', &lines); + rdl->pager_lines = lines; + + /* we can display all the data */ + if (displen == -1) { + fwrite(s, 1, rdl->pager_len, rdl->f_out); + free(rdl->pager_buf); + rdl->pager_buf = NULL; + return 0; + } + + displen = displen + 1; /* include \n */ + fwrite(s, 1, displen, rdl->f_out); + rdl->pager_off += displen; + rdl->pager_len -= displen; + + ucg_rdline_printf(rdl, "--- press a key to continue ---"); + return -1; +} + +/* push data in pager */ +ssize_t +ucg_rdline_pager_write(struct ucg_rdline *rdl, void *buf, size_t len) +{ + char *s = buf; + + /* display as many lines as we can */ + if (rdl->pager_lines < UCG_RDLINE_MAX_LINES) { + int lines = UCG_RDLINE_MAX_LINES - rdl->pager_lines; + int displen; + + /* we know that s is 0-terminated */ + displen = strnchr(s, '\n', &lines); + rdl->pager_lines += lines; + + /* we can display all the data */ + if (displen == -1) { + fwrite(s, 1, len, rdl->f_out); + return 0; + } + displen = displen + 1; /* include \n */ + fwrite(s, 1, displen, rdl->f_out); + s += displen; + len -= displen; + } + + if (rdl->pager_buf == NULL) { + ucg_rdline_printf(rdl, "--- press a key to continue ---"); + } + rdl->pager_buf = realloc(rdl->pager_buf, rdl->pager_len + len); + if (rdl->pager_buf == NULL) { + rdline_pager_reset(rdl); + return -1; + } + + memcpy(rdl->pager_buf + rdl->pager_len, s, len); + rdl->pager_len += len; + return 0; +} + +/* Print data asynchronously (using pager if needed) */ +int +ucg_rdline_pager_printf(struct ucg_rdline *rdl, const char *fmt, ...) +{ + int n; + char *buf = NULL; + va_list ap; + + if (rdl->f_out == NULL) + return -1; + + va_start(ap, fmt); + n = vasprintf(&buf, fmt, ap); + va_end(ap); + + if (n > 0) + ucg_rdline_pager_write(rdl, buf, n); + free(buf); + return n; +} + +int ucg_rdline_pager_set_cb(struct ucg_rdline *rdl, + ucg_rdline_pager_cb_t *cb, void *arg) +{ + if (rdl->pager_buf == NULL) + return -1; + + rdl->pager_cb = cb; + rdl->pager_arg = arg; + return 0; +} +#endif /* !UCG_CMD_NO_PAGER */ diff --git a/lib/cmd/ucg_cmd_socket.c b/lib/cmd/ucg_cmd_socket.c new file mode 100644 index 0000000..84bc226 --- /dev/null +++ b/lib/cmd/ucg_cmd_socket.c @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2009-2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +/*- + * Copyright (c) <2010>, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - 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. + * + * - Neither the name of Intel Corporation nor the names of its + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef UCG_CMD_HAVE_TERMIOS +#include +#endif + +#ifdef UCG_CMD_HAVE_SOCKET +#include +#include +#include +#include +#endif + +#include "ucg_cmd_parse.h" +#include "ucg_cmd_rdline.h" +#include "ucg_cmd_socket.h" +#include "ucg_cmd.h" + +#ifdef UCG_CMD_HAVE_SOCKET +int +ucg_cmd_tcpv4_listen(in_addr_t addr, uint16_t port) +{ + int s; + struct sockaddr_in sin_ci; + int optval = 1; + + s = socket(PF_INET, SOCK_STREAM, 0); + if (s < 0) + return s; + + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, + &optval, sizeof(optval)) == -1) + goto end; + + memset(&sin_ci, 0, sizeof(sin_ci)); + sin_ci.sin_family = AF_INET; + sin_ci.sin_addr.s_addr = addr; + sin_ci.sin_port = htons(port); +#ifndef __linux__ + sin_ci.sin_len = sizeof(sin_ci); +#endif + if (bind(s, (struct sockaddr *)&sin_ci, sizeof(sin_ci)) < 0) + goto end; + + if (listen(s, 1) < 0) + goto end; + + return s; + end: + close(s); + return -1; +} + +int +ucg_cmd_tcpv6_listen(const struct in6_addr *addr6, uint16_t port) +{ + int s; + struct sockaddr_in6 sin6_ci; + + s = socket(PF_INET6, SOCK_STREAM, 0); + if (s < 0) + return s; + + bzero(&sin6_ci, sizeof(sin6_ci)); + sin6_ci.sin6_family = AF_INET6; +#ifndef __linux__ + sin6_ci.sin6_len = sizeof(sin6_ci); +#endif + memcpy(&sin6_ci.sin6_addr, addr6, sizeof(sin6_ci.sin6_addr)); + sin6_ci.sin6_port = htons(port); + if (bind(s, (struct sockaddr *) &sin6_ci, sizeof(sin6_ci)) < 0) + goto end; + + if (listen(s, 1) < 0) + goto end; + + return s; + end: + close(s); + return -1; +} + +int +ucg_cmd_unix_listen(const char *filename) +{ + int s; + struct sockaddr_un servAddr; + + s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s < 0) + return s; + + bzero(&servAddr, sizeof(servAddr)); + servAddr.sun_family = AF_UNIX; + memcpy(servAddr.sun_path, filename , strlen(filename)); + + unlink(filename); + if(bind(s, (struct sockaddr *) &servAddr, sizeof(servAddr)) < 0) + goto end; + + if (listen(s, 1) < 0) + goto end; + + return s; + end: + close(s); + return -1; +} + +struct ucg_cmd * +ucg_cmd_accept(ucg_cmd_ctx_t *ctx, const char *prompt, int s) +{ + FILE *f; + int s2; + struct sockaddr sin; + socklen_t sinlen; + + sinlen = sizeof(struct sockaddr); + + if ((s2 = accept(s, &sin, &sinlen)) < 0) + return NULL; + + f = fdopen(s2, "r+"); + if (f == NULL) { + close(s2); + return NULL; + } + return (ucg_cmd_new(ctx, prompt, f, f)); +} +#endif diff --git a/lib/cmd/ucg_cmd_termios.c b/lib/cmd/ucg_cmd_termios.c new file mode 100644 index 0000000..bb4b1ea --- /dev/null +++ b/lib/cmd/ucg_cmd_termios.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 +#ifdef UCG_CMD_HAVE_TERMIOS +#include +#endif + +#include "ucg_cmd.h" +#include "ucg_cmd_termios.h" + +int ucg_cmd_termios_raw(struct ucg_cmd *cl) +{ + int ret = 0; + +#ifdef UCG_CMD_HAVE_TERMIOS + struct termios oldterm, term; + + ret = tcgetattr(0, &oldterm); + if (ret < 0) + return ret; + + memcpy(&term, &oldterm, sizeof(term)); + term.c_lflag &= ~(ICANON | ECHO | ISIG); + ret = tcsetattr(0, TCSANOW, &term); + if (ret < 0) + return ret; + + memcpy(&cl->oldterm, &oldterm, sizeof(term)); +#endif + + (void)cl; /* silent compiler */ + + return ret; +} + +int ucg_cmd_termios_restore(struct ucg_cmd *cl) +{ + int ret = 0; + (void)cl; /* silent compiler */ + +#ifdef UCG_CMD_HAVE_TERMIOS + ret = tcsetattr(fileno(stdin), TCSANOW, &cl->oldterm); +#endif + return ret; +} + diff --git a/lib/cmd/ucg_cmd_vt100.c b/lib/cmd/ucg_cmd_vt100.c new file mode 100644 index 0000000..66d8184 --- /dev/null +++ b/lib/cmd/ucg_cmd_vt100.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2009-2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +/*- + * Copyright (c) <2010>, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - 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. + * + * - Neither the name of Intel Corporation nor the names of its + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include "ucg_cmd_vt100.h" + +const char *ucg_cmd_vt100_commands[] = { + ucg_vt100_up_arr, + ucg_vt100_down_arr, + ucg_vt100_right_arr, + ucg_vt100_left_arr, + "\177", + "\n", + "\001", + "\005", + "\013", + "\031", + "\003", + "\006", + "\002", + ucg_vt100_suppr, + ucg_vt100_tab, + "\004", + "\014", + "\r", + "\033\177", + ucg_vt100_word_left, + ucg_vt100_word_right, + "?", + "\027", + "\020", + "\016", + "\033\144", +}; + +void +ucg_vt100_init(struct ucg_cmd_vt100 *vt) +{ + vt->state = UCG_CMD_VT100_INIT; +} + + +static int +match_command(char *buf, unsigned int size) +{ + const char *cmd; + unsigned int i = 0; + + for (i = 0; i < sizeof(ucg_cmd_vt100_commands) / + sizeof(const char *); i++) { + cmd = *(ucg_cmd_vt100_commands + i); + + if (size == strlen(cmd) && + !strncmp(buf, cmd, strlen(cmd))) { + return i; + } + } + + return -1; +} + +int +ucg_vt100_parser(struct ucg_cmd_vt100 *vt, char ch) +{ + unsigned int size; + uint8_t c = (uint8_t) ch; + + if (vt->bufpos > UCG_CMD_VT100_BUF_SIZE) { + vt->state = UCG_CMD_VT100_INIT; + vt->bufpos = 0; + } + + vt->buf[vt->bufpos++] = c; + size = vt->bufpos; + + switch (vt->state) { + case UCG_CMD_VT100_INIT: + if (c == 033) { + vt->state = UCG_CMD_VT100_ESCAPE; + } + else { + vt->bufpos = 0; + goto match_command; + } + break; + + case UCG_CMD_VT100_ESCAPE: + if (c == 0133) { + vt->state = UCG_CMD_VT100_ESCAPE_CSI; + } + else if (c >= 060 && c <= 0177) { + vt->bufpos = 0; + vt->state = UCG_CMD_VT100_INIT; + goto match_command; + } + break; + + case UCG_CMD_VT100_ESCAPE_CSI: + if (c >= 0100 && c <= 0176) { + vt->bufpos = 0; + vt->state = UCG_CMD_VT100_INIT; + goto match_command; + } + break; + + default: + vt->bufpos = 0; + break; + } + + return -2; + + match_command: + return match_command(vt->buf, size); +} diff --git a/lib/gloss/include/ucg_gloss_chardev.h b/lib/gloss/include/ucg_gloss_chardev.h new file mode 100644 index 0000000..92e5f8d --- /dev/null +++ b/lib/gloss/include/ucg_gloss_chardev.h @@ -0,0 +1,132 @@ +/* + * Copyright 2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +#ifndef UCG_GLOSS_DEVICE_H_ +#define UCG_GLOSS_DEVICE_H_ + +#include +#include + +/** + * Maximum number of devices stored in cache. + * + * Instead of only storing the devices in a list, the first ones are + * stored in a table indexed by the file descriptor. This avoids a + * linear browsing on lookup. + */ +#define GLOSS_DEVICE_CACHE_MAX 4 + +/** + * Structure describing a device + * + * It can be registered using ucg_chardev_register(), and allows to + * redirect file operations to any channel (uart, spi, ...). + */ +struct ucg_chardev { + /* filled by ucg_chardev_register() */ + TAILQ_ENTRY(ucg_chardev) next; + int fd; + + /* filled by user before registration */ + const char *name; + int (*open_r)(struct _reent *r, const char *path, int flags, int mode); + int (*close_r)(struct _reent *r, int fd); + _ssize_t (*read_r)(struct _reent *r, int fd, void *ptr, size_t len); + _ssize_t (*write_r)(struct _reent *r, int fd, const void *ptr, size_t len); +}; + +/** + * The list of devices. + */ +TAILQ_HEAD(ucg_chardev_list, ucg_chardev); +struct ucg_chardev_list ucg_chardev_list; + +/** + * Table of first ucg_chardev pointers, initialized to NULL. + */ +struct ucg_chardev *ucg_chardev_cache[GLOSS_DEVICE_CACHE_MAX]; + +/** + * Register a device + * + * Register device operations. The structure is appended to the device + * list, and its name and fd fields are modified. Therefore, a structure + * cannot be registered twice. + * + * The first three devices to be registered are stdin, stdout and stderr. + * + * @param dev + * The filled device structure + * @return + * 0 on success, negative on error + */ +int ucg_chardev_register(struct ucg_chardev *dev); + +/** + * Return the device associated to the given file descriptor. + * + * @param fd + * The file descriptor. + * @return + * The device matching the file descriptor. + */ +static inline const struct ucg_chardev * +ucg_chardev_lookup_from_fd(int fd) +{ + const struct ucg_chardev *dev; + + if (fd >= 0 && fd < GLOSS_DEVICE_CACHE_MAX) + return ucg_chardev_cache[fd]; + + TAILQ_FOREACH(dev, &ucg_chardev_list, next) { + if (dev->fd == fd) + return dev; + } + return NULL; +} + +/** + * Return the device associated to the given name. + * + * @param name + * The name of the file. + * @return + * The device matching the file name. + */ +static inline const struct ucg_chardev * +ucg_chardev_lookup_from_name(const char *name) +{ + const struct ucg_chardev *dev; + + TAILQ_FOREACH(dev, &ucg_chardev_list, next) { + if (strcmp(name, dev->name) == 0) + return dev; + } + return NULL; +} + +#endif /* UCG_GLOSS_DEVICE_H_ */ diff --git a/lib/gloss/ucg_gloss_chardev.c b/lib/gloss/ucg_gloss_chardev.c new file mode 100644 index 0000000..972c987 --- /dev/null +++ b/lib/gloss/ucg_gloss_chardev.c @@ -0,0 +1,53 @@ +/* + * Copyright 2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 + +struct ucg_chardev_list ucg_chardev_list = + TAILQ_HEAD_INITIALIZER(ucg_chardev_list); + +static int max_fd = 0; + +/* register a device for file operations */ +int ucg_chardev_register(struct ucg_chardev *dev) +{ + if (dev->name == NULL) + return -EINVAL; + + if (ucg_chardev_lookup_from_name(dev->name) != NULL) + return -EEXIST; + + TAILQ_INSERT_TAIL(&ucg_chardev_list, dev, next); + + dev->fd = max_fd ++; + if (dev->fd < GLOSS_DEVICE_CACHE_MAX) + ucg_chardev_cache[dev->fd] = dev; + return 0; +} diff --git a/lib/gloss/ucg_gloss_stubs.c b/lib/gloss/ucg_gloss_stubs.c new file mode 100644 index 0000000..03d9ed6 --- /dev/null +++ b/lib/gloss/ucg_gloss_stubs.c @@ -0,0 +1,301 @@ +/* + * Copyright 2015, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 + +/* provide a dummy environment */ +char *__env[1] = { 0 }; +char **environ = __env; + +/* provide a definition of errno if not already provided */ +int errno; + +/* defined by the linker, used by _sbrk */ +extern char _ebss; + +/* open a device, return its file descriptor */ +int +_open_r(struct _reent *reent, const char *file, int flags, int mode) +{ + const struct ucg_chardev *dev; + + dev = ucg_chardev_lookup_from_name(file); + if (dev == NULL) { + reent->_errno = ENODEV; + return -1; + } + + if (dev->open_r) + dev->open_r(reent, file, flags, mode); + + return dev->fd; +} + +/* close a device */ +int +_close_r(struct _reent *reent, int fd) +{ + const struct ucg_chardev *dev; + + dev = ucg_chardev_lookup_from_fd(fd); + if (dev == NULL) { + reent->_errno = EBADF; + return -1; + } + + /* call the close() handler if defined */ + if (dev->close_r) + return dev->close_r(reent, fd); + + return 0; +} + +/* read a device. */ +_ssize_t +_read_r(struct _reent *reent, int fd, void *ptr, size_t len) +{ + const struct ucg_chardev *dev; + + dev = ucg_chardev_lookup_from_fd(fd); + if (dev == NULL) { + reent->_errno = EBADF; + return -1; + } + + if (dev->read_r) + return dev->read_r(reent, fd, ptr, len); + + reent->_errno = ENODEV; + return -1; +} + +/* write to a device */ +_ssize_t +_write_r(struct _reent *reent, int fd, const void *ptr, size_t len) +{ + const struct ucg_chardev *dev; + + dev = ucg_chardev_lookup_from_fd(fd); + if (dev == NULL) { + reent->_errno = EBADF; + return -1; + } + + if (dev->write_r) + return dev->write_r(reent, fd, ptr, len); + + reent->_errno = ENODEV; + return -1; +} + +/* exit: print error code and loop forever */ +void +_exit(int rc) +{ + printf("exit %d\n", rc); + while (1); +} + +/* set owner: not implemented */ +int +_chown_r(struct _reent *reent, + __attribute__((unused)) const char *path, + __attribute__((unused)) uid_t owner, + __attribute__((unused)) gid_t group) +{ + reent->_errno = ENOSYS; + return -1; +} + +/* execve: not implemented */ +int +_execve_r(struct _reent *reent, + __attribute__((unused)) const char *name, + __attribute__((unused)) char *const *argv, + __attribute__((unused)) char *const *env) +{ + reent->_errno = ENOSYS; + return -1; +} + +/* fork: not implemented */ +int +_fork_r(struct _reent *reent) +{ + reent->_errno = ENOSYS; + return -1; +} + +/* fstat: not implemented, all devices are character devices */ +int +_fstat_r(__attribute__((unused)) struct _reent *reent, + __attribute__((unused)) int fildes, + struct stat *st) +{ + st->st_mode = S_IFCHR; + return 0; +} + +/* getpid: not implemented */ +int +_getpid_r(struct _reent *reent) +{ + reent->_errno = ENOSYS; + return -1; +} + +struct timeval; + +/* gettimeofday: not implemented */ +int +_gettimeofday_r(struct _reent *reent, + __attribute__((unused)) struct timeval *ptimeval, + __attribute__((unused)) void *ptimezone) +{ + reent->_errno = ENOSYS; + return -1; +} + +/* isatty; not implemented */ +int +_isatty_r(struct _reent *reent, __attribute__((unused)) int file) +{ + reent->_errno = ENOTTY; + return 0; +} + +/* kill a process: not implemented */ +int +_kill_r(struct _reent *reent, + __attribute__((unused)) int pid, + __attribute__((unused)) int sig) +{ + reent->_errno = ENOSYS; + return -1; +} + +/* link: not implemented */ +int +_link_r(struct _reent *reent, + __attribute__((unused)) const char *existing, + __attribute__((unused)) const char *new) +{ + reent->_errno = EMLINK; + return -1; +} + +/* lseek: not implemented */ +_off_t +_lseek_r(__attribute__((unused)) struct _reent *reent, + __attribute__((unused)) int file, + __attribute__((unused)) _off_t ptr, + __attribute__((unused)) int dir) +{ + return 0; +} + +/* readlink: not implemented */ +int +_readlink_r(struct _reent *reent, + __attribute__((unused)) const char *path, + __attribute__((unused)) char *buf, + __attribute__((unused)) size_t bufsize) +{ + reent->_errno = ENOSYS; + return -1; +} + +/* simple sbrk */ +void * +_sbrk_r(__attribute__((unused)) struct _reent *reent, ptrdiff_t incr) +{ + static char *heap_end = NULL; + char *prev_heap_end; + + if (heap_end == NULL) + heap_end = &_ebss; + + prev_heap_end = heap_end; + heap_end += incr; + return (caddr_t)prev_heap_end; +} + +/* stat: not implemented, all devices are character devices */ +int +_stat_r(__attribute__((unused)) struct _reent *reent, + __attribute__((unused)) const char *file, + struct stat *st) +{ + st->st_mode = S_IFCHR; + return 0; +} + +/* symlink: not implemented */ +int +_symlink_r(struct _reent *reent, + __attribute__((unused)) const char *path1, + __attribute__((unused)) const char *path2) +{ + reent->_errno = ENOSYS; + return -1; +} + +/* times: not timplemented */ +clock_t +_times_r(struct _reent *reent, + __attribute__((unused)) struct tms *buf) +{ + reent->_errno = ENOSYS; + return -1; +} + +/* unlink: not implemented */ +int +_unlink_r(struct _reent *reent, + __attribute__((unused)) const char *name) +{ + reent->_errno = EMLINK; + return -1; +} + +/* wait: not implemented */ +int +_wait_r(struct _reent *reent, __attribute__((unused)) int *status) +{ + reent->_errno = ENOSYS; + return -1; +} + diff --git a/lib/uart/include/ucg_uart.h b/lib/uart/include/ucg_uart.h new file mode 100644 index 0000000..ae50e78 --- /dev/null +++ b/lib/uart/include/ucg_uart.h @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2005-2015, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +#ifndef UCG_UART_H_ +#define UCG_UART_H_ + +#include + +/** + * UART interface, using interrupts. Supports 5 to 8 bits characters + * (not 9 bits). + */ + +/** + * Callback type of tx/rx event. + */ +typedef void (ucg_uart_cb_t)(char); + +struct ucg_uart; + +/** + * Configuration of the uart + */ +struct ucg_uart_config { + uint8_t enable : 1, /**< enable or disable the uart */ + parity : 2, /**< none, odd or even */ + stop_bits : 1, /**< 1 or 2 bits at the end of the frame */ + reserved : 4; /**< nothing for now */ + uint8_t nbits; /**< number of bits in frame, 5, 6, 7 or 8 */ + uint32_t baudrate; /**< speed of uart */ +}; + +/** + * Per-arch uart driver operations + */ +struct ucg_uart_driver_ops { + void (*disable_tx_irq)(struct ucg_uart *uart); /**< Disable tx intrpt */ + void (*enable_tx_irq)(struct ucg_uart *uart); /**< Enable tx intrpt */ + uint8_t (*tx_ready)(struct ucg_uart *uart); /**< Test if tx register empty */ + uint8_t (*rx_ready)(struct ucg_uart *uart); /**< Test if rx register full */ + void (*set_udr)(struct ucg_uart *uart, char c); /**< Set uart data register */ + char (*get_udr)(struct ucg_uart *uart); /**< Get uart data register */ + int (*set_conf)(struct ucg_uart *uart, + const struct ucg_uart_config *conf); /**< Set configuration */ +}; + +/** + * Generic Uart structure + */ +struct ucg_uart { + struct ucg_uart_config conf; /**< Current configuration */ + ucg_uart_cb_t *rx_cb; /**< RX callback */ + ucg_uart_cb_t *tx_cb; /**< TX callback */ + struct ucg_cirbuf *rx_fifo; /**< RX fifo */ + struct ucg_cirbuf *tx_fifo; /**< TX fifo */ + const struct ucg_uart_driver_ops *ops; /**< Pointer to arch uart ops */ + void *driver_data; /**< Opaque arch uart data */ +}; + +/** + * Uart wait directive, used in rx/tx functions. + */ +enum ucg_uart_wait { + WAIT, /**< Loop until operation can be done */ + NOWAIT, /**< Return if operation can't be done now */ +}; + +/** + * Handle TX register empty interrupt + * + * @param uart + * The uart structure. + */ +void ucg_uart_tx_intr(struct ucg_uart *uart); + +/** + * Handle RX register empty interrupt + * + * @param uart + * The uart structure. + */ +void ucg_uart_rx_intr(struct ucg_uart *uart); + +/** + * Initialize uart + * + * Initialize the per-arch and generic uart structure, the rx and tx + * fifos, and configure the uart with the default configuration. + * + * @param uart + * The uart structure. + * @param ops + * A pointer to the per-arch uart operations. + * @param driver_data + * A pointer to a per-arch uart data structure (ex: struct ucg_stm32_uart *) + * @param rx_cbuf + * A pointer to an uninitialized cirbuf, used for rx fifo + * @param rx_buf + * The buffer used by the rx fifo + * @param rx_bufsize + * The length of rx_buf in bytes + * @param tx_cbuf + * A pointer to an uninitialized cirbuf, used for tx fifo + * @param tx_buf + * The buffer used by the tx fifo + * @param tx_bufsize + * The length of tx_buf in bytes + * @return + * 0 on success, negative on error. + */ +int ucg_uart_init(struct ucg_uart *uart, + const struct ucg_uart_driver_ops *ops, void *driver_data, + struct ucg_cirbuf *rx_cbuf, char *rx_buf, unsigned rx_bufsize, + struct ucg_cirbuf *tx_cbuf, char *tx_buf, unsigned tx_bufsize); + +/** + * Configure the uart with the given configuration. + * + * The function ucg_uart_init() must have been called first. + * + * @param uart + * The uart structure. + * @param conf + * The configuration to be applied. + * @return + * 0 on success, negative on error. + */ +int ucg_uart_setconf(struct ucg_uart *uart, const struct ucg_uart_config *conf); + +/** + * Get the current configuration of the uart. + * + * @param uart + * The uart structure. + * @param conf + * The pointer to be filled with the current running configuration. + */ +void ucg_uart_getconf(struct ucg_uart *uart, struct ucg_uart_config *conf); + +/** + * Receive a character. + * + * Receive the next character, taken from the fifo if any. If NOWAIT is + * given, the function returns an error if the fifo is empty. If WAIT is + * given, the function loops until a new character can be returned. + * + * The function can be called from an interrupt, but it should be used + * with care if WAIT is given, as it will loop until a character is + * received. + * + * @param uart + * The uart structure. + * @param wait + * The wait directive (WAIT or NOWAIT) + * @return + * Return the character on success (>= 0), or a negative value on error. + */ +int ucg_uart_recv(struct ucg_uart *uart, enum ucg_uart_wait wait); + +/** + * Send a character. + * + * Put a new character in the TX fifo. If NOWAIT is given, the function + * returns an error if the fifo is full. If WAIT is given, the function + * loops until some room is available in the tx fifo. + * + * The function can be called from an interrupt, but it should be used + * with care if WAIT is given, as it will wait that the current + * transmission is finished before returning. + * + * @param uart + * The uart structure. + * @param wait + * The wait directive (WAIT or NOWAIT) + * @return + * Return the transmitted character on success (>= 0), or a negative + * value on error. + */ +int ucg_uart_send(struct ucg_uart *uart, char c, enum ucg_uart_wait wait); + +/** + * Flush the TX fifo + * + * Loop until the uart TX fifo is empty. The function can be called from + * an interrupt, but it should be used with care as it will wait that the + * transmission of all bytes of the fifo is finished before returning. + * + * @param uart + * The uart structure. + */ +void ucg_uart_flush(struct ucg_uart *uart); + +/** + * Register a TX callback function + * + * Register a function that will be called after a character is written + * in the UART dta register. This function is called with interrupts + * locked, and the user is free to access or modify the tx fifo if + * required. Note that the character that has just been transmitted is + * not in the tx fifo when the callback is invoked. + * + * @param uart + * The uart structure. + * @param f + * The function to call on tx event, can be NULL to disable. + * @param arg + * The opaque argument given to the function. + */ +void ucg_uart_register_tx_cb(struct ucg_uart *uart, ucg_uart_cb_t *f); + +/** + * Register a TX callback function + * + * Register a function that will be called after a character is read + * from the UART dta register. This function is called with interrupts + * locked, and the user is free to access or modify the rx fifo if + * required. Note that the character that has just been received is + * present in the rx fifo when the callback is invoked. + * + * @param uart + * The uart structure. + * @param f + * The function to call on tx event, can be NULL to disable. + * @param arg + * The opaque argument given to the function. + */ +void ucg_uart_register_rx_cb(struct ucg_uart *uart, ucg_uart_cb_t *f); + +#endif /* UCG_UART_H_ */ diff --git a/lib/uart/ucg_uart.c b/lib/uart/ucg_uart.c new file mode 100644 index 0000000..68d4d2f --- /dev/null +++ b/lib/uart/ucg_uart.c @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2005-2015, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 + +/* Send the next char, or disable tx interruptions if tx fifo is + * empty. Must be called with intrp locked and uart data register + * empty. */ +static void ucg_uart_send_next_char(struct ucg_uart *uart) +{ + char c; + + if (ucg_cirbuf_is_empty(uart->tx_fifo)) { + uart->ops->disable_tx_irq(uart); + return; + } + + c = ucg_cirbuf_get_tail(uart->tx_fifo); + ucg_cirbuf_del_tail(uart->tx_fifo); + uart->ops->set_udr(uart, c); + if (uart->tx_cb != NULL) + uart->tx_cb(c); + + uart->ops->enable_tx_irq(uart); + +} + +/* Receive char and put it in the fifo. If the rx fifo is full, the char + * is dropped. Must be called with intrp locked and uart data register + * empty. */ +static void ucg_uart_recv_next_char(struct ucg_uart *uart) +{ + char c; + + c = uart->ops->get_udr(uart); + + if (!ucg_cirbuf_is_full(uart->rx_fifo)) + ucg_cirbuf_add_head(uart->rx_fifo, c); + + if (uart->rx_cb != NULL) + uart->rx_cb(c); +} + +/* called by user to handle the "tx register empty" interrupt */ +void ucg_uart_tx_intr(struct ucg_uart *uart) +{ + ucg_uart_send_next_char(uart); +} + +/* called by user to handle the "rx register non empty" interrupt */ +void ucg_uart_rx_intr(struct ucg_uart *uart) +{ + ucg_uart_recv_next_char(uart); +} + + +/* get a char from the receive fifo */ +static int ucg_uart_recv_nowait(struct ucg_uart *uart) +{ + char c = 0; + ucg_irqflags_t flags; + + flags = ucg_irq_lock_save(); + if (ucg_cirbuf_is_empty(uart->rx_fifo)) { + ucg_irq_unlock_restore(flags); + return -1; + } + + c = ucg_cirbuf_get_tail(uart->rx_fifo); + ucg_cirbuf_del_tail(uart->rx_fifo); + ucg_irq_unlock_restore(flags); + + return (int)c; +} + +/* get a char from the receive fifo */ +int ucg_uart_recv(struct ucg_uart *uart, enum ucg_uart_wait wait) +{ + int c = 0; + + c = ucg_uart_recv_nowait(uart); + if (c >= 0) + return c; + + if (wait == NOWAIT) + return -1; + + if (ucg_irq_locked()) { + /* if irq are masked we have to poll uart register */ + while (!uart->ops->rx_ready(uart)) + ; + + /* then receive the data, and return it */ + ucg_uart_recv_next_char(uart); + c = ucg_uart_recv_nowait(uart); + } else { + while ((c = ucg_uart_recv_nowait(uart)) == -1) + ; + } + + return c; +} + +/* send a char, or put it in the fifo if uart is not ready. Return -1 + * if fifo is full */ +static int ucg_uart_send_nowait(struct ucg_uart *uart, char c) +{ + ucg_irqflags_t flags; + + flags = ucg_irq_lock_save(); + + if (ucg_cirbuf_is_full(uart->tx_fifo)) { + ucg_irq_unlock_restore(flags); + return -1; + } + + /* uart is ready to send */ + if (ucg_cirbuf_is_empty(uart->tx_fifo) && uart->ops->tx_ready(uart)) { + uart->ops->set_udr(uart, c); + if (uart->tx_cb != NULL) + uart->tx_cb(c); + uart->ops->enable_tx_irq(uart); + + } + else { /* not ready, put char in fifo */ + ucg_cirbuf_add_head(uart->tx_fifo, c); + } + + ucg_irq_unlock_restore(flags); + return 0; +} + +/* send a byte */ +int ucg_uart_send(struct ucg_uart *uart, char c, enum ucg_uart_wait wait) +{ + /* try to send the char */ + if (ucg_uart_send_nowait(uart, c) == 0) + return 0; + + if (wait == NOWAIT) + return -1; + + if (ucg_irq_locked()) { + /* if irq are masked we have to poll uart register */ + while (!uart->ops->tx_ready(uart)) + ; + + /* then send a data to free a room in the fifo */ + ucg_uart_send_next_char(uart); + ucg_cirbuf_add_head(uart->tx_fifo, c); + } else { + /* if irq are not locked, we can loop to emit */ + while (ucg_uart_send_nowait(uart, c) != 0) + ; + } + return 0; +} + +/* flush the tx fifo */ +void ucg_uart_flush(struct ucg_uart *uart) +{ + ucg_irqflags_t flags; + + if (ucg_irq_locked()) { + /* poll uart register, and send next byte until tx fifo + * is empty */ + while (1) { + if (ucg_cirbuf_is_empty(uart->tx_fifo)) + break; + while (!uart->ops->tx_ready(uart)) + ; + ucg_uart_send_next_char(uart); + } + } else { + /* just wait that tx fifo is empty */ + while (1) { + flags = ucg_irq_lock_save(); + if (ucg_cirbuf_is_empty(uart->tx_fifo)) { + ucg_irq_unlock_restore(flags); + break; + } + ucg_irq_unlock_restore(flags); + } + } +} + +/* read the current running configuration */ +void ucg_uart_getconf(struct ucg_uart *uart, struct ucg_uart_config *conf) +{ + memcpy(conf, &uart->conf, sizeof(*conf)); +} + +/* set a new uart config */ +int ucg_uart_setconf(struct ucg_uart *uart, const struct ucg_uart_config *conf) +{ + int ret; + + ret = uart->ops->set_conf(uart, conf); + if (ret < 0) + return ret; + + memcpy(&uart->conf, conf, sizeof(*conf)); + return 0; +} + +/* register the function that will be executed at each byte transmission */ +void ucg_uart_register_tx_cb(struct ucg_uart *uart, void (*f)(char)) +{ + ucg_irqflags_t flags; + + flags = ucg_irq_lock_save(); + uart->tx_cb = f; + ucg_irq_unlock_restore(flags); +} + +/* register the function that will be executed at each byte reception */ +void ucg_uart_register_rx_cb(struct ucg_uart *uart, void (*f)(char)) +{ + ucg_irqflags_t flags; + + flags = ucg_irq_lock_save(); + uart->rx_cb = f; + ucg_irq_unlock_restore(flags); +} + +/* init uart and fifos, call the per-arch initialization and set a default + * configuration (9600 bauds) */ +int ucg_uart_init(struct ucg_uart *uart, + const struct ucg_uart_driver_ops *ops, void *driver_data, + struct ucg_cirbuf *rx_cbuf, char *rx_buf, unsigned rx_bufsize, + struct ucg_cirbuf *tx_cbuf, char *tx_buf, unsigned tx_bufsize) +{ + int ret; + + const struct ucg_uart_config def_conf = { + .enable = 1, + .parity = 0, + .stop_bits = 0, + .reserved = 0, + .nbits = 8, + .baudrate = 9600, + }; + + memset(uart, 0, sizeof(*uart)); + ucg_cirbuf_init(rx_cbuf, rx_buf, 0, rx_bufsize); + ucg_cirbuf_init(tx_cbuf, tx_buf, 0, tx_bufsize); + uart->rx_fifo = rx_cbuf; + uart->tx_fifo = tx_cbuf; + uart->ops = ops; + uart->driver_data = driver_data; + ret = ucg_uart_setconf(uart, &def_conf); + + return ret; +} diff --git a/mk/ucgine-ar-rules.mk b/mk/ucgine-ar-rules.mk new file mode 100644 index 0000000..4ad30cb --- /dev/null +++ b/mk/ucgine-ar-rules.mk @@ -0,0 +1,55 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. +# + +# dump some infos if debug is enabled +ifeq ($(D),1) +$(call disp_list,------ all-ar,$(all-ar)) +$(foreach ar,$(all-ar),\ + $(info,out-$(ar): $(out-$(ar))) \ + $(call disp_list,pre-$(ar),$(pre-$(ar))) \ +) +endif + +# include dependencies and commands files if they exist +$(foreach ar,$(all-ar),\ + $(eval -include $(call depfile,$(ar))) \ + $(eval -include $(call cmdfile,$(ar))) \ +) + +# remove duplicates +filtered-all-ar := $(sort $(all-ar)) + +# link several objects files into one shared object +$(filtered-all-ar): $$(pre-$$@) $$(wildcard $$(dep-$$@)) FORCE + @[ -d $(dir $@) ] || mkdir -p $(dir $@) + @$(call display_deps,$(pre-$(@)),$@,\ + $(call ar_cmd,$(pre-$(@)),$@),$?) + @$(if $(call check_deps,$@,$(call ar_cmd,$(pre-$(@)),$@),$?),\ + $(call ar_print_cmd,$(pre-$(@)),$@) && \ + $(call ar_cmd,$(pre-$(@)),$@) && \ + $(call save_cmd,$(call ar_cmd,$(pre-$(@)),$@),$@) && \ + $(call create_empty_depfile,$@)) diff --git a/mk/ucgine-ar-vars.mk b/mk/ucgine-ar-vars.mk new file mode 100644 index 0000000..7d2fab7 --- /dev/null +++ b/mk/ucgine-ar-vars.mk @@ -0,0 +1,75 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. +# + +# ar-y-$(ar) is provided by the user +# $(ar) is the path of the static library, and the variable contains +# the list of sources. Several ar-y-$(ar) can be present. + +# list all ar builds requested by user +all-ar := $(patsubst ar-y-%,%,$(filter ar-y-%,$(.VARIABLES))) + +# add them to the list of targets +all-targets += $(all-ar) + +# for each ar, create the following variables: +# out-$(ar) = output path of the arcutable +# pre-$(ar) = list of prerequisites for this arcutable +# Some source files need intermediate objects, we define these variables +# for them too, and add them in a list: $(all-iobj). +# Last, we add the generated files in $(all-clean-file). +$(foreach ar,$(all-ar),\ + $(eval out-$(ar) := $(dir $(ar))) \ + $(eval pre-$(ar) := ) \ + $(foreach src,$(ar-y-$(ar)), \ + $(if $(call is_cc_source,$(src)), \ + $(eval iobj := $(call src2iobj,$(src),$(out-$(ar)))) \ + $(eval pre-$(iobj) := $(src)) \ + $(eval all-iobj += $(iobj)) \ + $(eval all-clean-file += $(iobj)) \ + $(eval pre-$(ar) += $(iobj)) \ + , \ + $(if $(call is_obj_source,$(src)),\ + $(eval pre-$(ar) += $(src)) \ + , \ + $(error "unsupported source format: $(src)"))) \ + )\ + $(eval all-clean-file += $(ar)) \ +) + +# link several *.o files into a static libary +# $1: sources (*.o) +# $2: dst (xyz.a) +ar_cmd = ar crsD $(2) $(1) + +# print line used to ar object files +ifeq ($(V),1) +ar_print_cmd = echo $(call protect_quote,$(call ar_cmd,$1,$2)) +else +ar_print_cmd = echo " AR $(2)" +endif + +all-clean-file += $(all-ar) diff --git a/mk/ucgine-clean-rules.mk b/mk/ucgine-clean-rules.mk new file mode 100644 index 0000000..6bdf867 --- /dev/null +++ b/mk/ucgine-clean-rules.mk @@ -0,0 +1,33 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. +# + +.PHONY: _ucgine_clean +_ucgine_clean: $(all-clean-target) FORCE + @$(call clean_print_cmd,$(all-clean-file) $(call depfile,$(all-clean-file)) \ + $(call cmdfile,$(all-clean-file))) && \ + $(call clean_cmd,$(all-clean-file) $(call depfile,$(all-clean-file)) \ + $(call cmdfile,$(all-clean-file))) diff --git a/mk/ucgine-clean-vars.mk b/mk/ucgine-clean-vars.mk new file mode 100644 index 0000000..53f732e --- /dev/null +++ b/mk/ucgine-clean-vars.mk @@ -0,0 +1,37 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. +# + +# remove files +# $1: files +clean_cmd = rm -rf $(1) + +# print line used to clean files +ifeq ($(V),1) +clean_print_cmd = echo $(call protect_quote,$(call clean_cmd,$1)) +else +clean_print_cmd = echo " CLEAN $(CURDIR)" +endif diff --git a/mk/ucgine-copy-rules.mk b/mk/ucgine-copy-rules.mk new file mode 100644 index 0000000..6602a9e --- /dev/null +++ b/mk/ucgine-copy-rules.mk @@ -0,0 +1,55 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. +# + +# dump some infos if debug is enabled +ifeq ($(D),1) +$(call disp_list,------ all-copy,$(all-copy)) +$(foreach copy,$(all-copy),\ + $(info,out-$(copy): $(out-$(copy))) \ + $(call disp_list,pre-$(copy),$(pre-$(copy))) \ +) +endif + +# include dependencies and commands files if they exist +$(foreach copy,$(all-copy),\ + $(eval -include $(call depfile,$(copy))) \ + $(eval -include $(call cmdfile,$(copy))) \ +) + +# remove duplicates +filtered-all-copy := $(sort $(all-copy)) + +# convert format of executable +$(filtered-all-copy): $$(pre-$$@) $$(wildcard $$(dep-$$@)) FORCE + @[ -d $(dir $@) ] || mkdir -p $(dir $@) + @$(call display_deps,$(pre-$(@)),$@,\ + $(call copy_cmd,$(pre-$(@)),$@),$?) + @$(if $(call check_deps,$@,$(call copy_cmd,$(pre-$(@)),$@),$?),\ + $(call copy_print_cmd,$(pre-$(@)),$@) && \ + $(call copy_cmd,$(pre-$(@)),$@) && \ + $(call save_cmd,$(call copy_cmd,$(pre-$(@)),$@),$@) && \ + $(call create_empty_depfile,$@)) diff --git a/mk/ucgine-copy-vars.mk b/mk/ucgine-copy-vars.mk new file mode 100644 index 0000000..677274e --- /dev/null +++ b/mk/ucgine-copy-vars.mk @@ -0,0 +1,74 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. +# + +# copy a file +# copy-y-$(copy) is provided by the user +# $(copy) is the path of the directory containing the destination +# files, and the variable contains the path of the files to copy. Several +# copy-y-$(copy) can be present. + +# list all path requested by user +_all-copy := $(patsubst copy-y-%,%,$(filter copy-y-%,$(.VARIABLES))) +all-copy := + +# for each copy, create the following variables: +# out-$(copy) = output path of the executable +# pre-$(copy) = list of prerequisites for this executable +# We also add the files in $(all-copy). +$(foreach copy,$(_all-copy),\ + $(if $(notdir $(copy)), \ + $(if $(call compare,$(words $(copy-y-$(copy))),1), \ + $(error "only one source file is allowed in copy-y-$(copy)")) \ + $(eval dst := $(dir $(copy))$(notdir $(copy-y-$(copy)))) \ + $(eval out-$(copy) := $(dir $(copy))) \ + $(eval pre-$(copy) := $(copy-y-$(copy))) \ + $(eval all-copy += $(dst)) \ + , \ + $(foreach src,$(copy-y-$(copy)),\ + $(eval dst := $(copy)$(notdir $(src))) \ + $(eval out-$(copy) := $(copy)) \ + $(eval pre-$(dst) := $(src)) \ + $(eval all-copy += $(dst)) \ + ) \ + ) \ +) + +# add them to the list of targets and clean +all-targets += $(all-copy) +all-clean-file += $(all-copy) + +# convert format of executable from elf to ihex +# $1: source executable (elf) +# $2: destination file +copy_cmd = $(CP) $(1) $(2) + +# print line used to convert executable format +ifeq ($(V),1) +copy_print_cmd = echo $(call protect_quote,$(call copy_cmd,$1,$2)) +else +copy_print_cmd = echo " COPY $(2)" +endif diff --git a/mk/ucgine-exe-rules.mk b/mk/ucgine-exe-rules.mk new file mode 100644 index 0000000..a1a5c42 --- /dev/null +++ b/mk/ucgine-exe-rules.mk @@ -0,0 +1,55 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. +# + +# dump some infos if debug is enabled +ifeq ($(D),1) +$(call disp_list,------ all-exe,$(all-exe)) +$(foreach exe,$(all-exe),\ + $(info,out-$(exe): $(out-$(exe))) \ + $(call disp_list,pre-$(exe),$(pre-$(exe))) \ +) +endif + +# include dependencies and commands files if they exist +$(foreach exe,$(all-exe),\ + $(eval -include $(call depfile,$(exe))) \ + $(eval -include $(call cmdfile,$(exe))) \ +) + +# remove duplicates +filtered-all-exe := $(sort $(all-exe)) + +# link several objects files into one executable +$(filtered-all-exe): $$(pre-$$@) $$(wildcard $$(dep-$$@)) FORCE + @[ -d $(dir $@) ] || mkdir -p $(dir $@) + @$(call display_deps,$(pre-$(@)),$@,\ + $(call link_cmd,$(pre-$(@)),$@),$?) + @$(if $(call check_deps,$@,$(call link_cmd,$(pre-$(@)),$@),$?),\ + $(call link_print_cmd,$(pre-$(@)),$@) && \ + $(call link_cmd,$(pre-$(@)),$@) && \ + $(call save_cmd,$(call link_cmd,$(pre-$(@)),$@),$@) && \ + $(call create_empty_depfile,$@)) diff --git a/mk/ucgine-exe-vars.mk b/mk/ucgine-exe-vars.mk new file mode 100644 index 0000000..f980be4 --- /dev/null +++ b/mk/ucgine-exe-vars.mk @@ -0,0 +1,79 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. +# + +# exe-y-$(exe) is provided by the user +# $(exe) is the path of the binary, and the variable contains +# the list of sources. Several exe-y-$(exe) can be present. + +# list all exe builds requested by user +all-exe := $(patsubst exe-y-%,%,$(filter exe-y-%,$(.VARIABLES))) + +# add them to the list of targets +all-targets += $(all-exe) + +# for each exe, create the following variables: +# out-$(exe) = output path of the executable +# pre-$(exe) = list of prerequisites for this executable +# Some source files need intermediate objects, we define these variables +# for them too, and add them in a list: $(all-iobj). +# Last, we add the generated files in $(all-clean-file). +$(foreach exe,$(all-exe),\ + $(eval out-$(exe) := $(dir $(exe))) \ + $(eval pre-$(exe) := ) \ + $(foreach src,$(exe-y-$(exe)), \ + $(if $(call is_cc_source,$(src)), \ + $(eval iobj := $(call src2iobj,$(src),$(out-$(exe)))) \ + $(eval pre-$(iobj) := $(src)) \ + $(eval all-iobj += $(iobj)) \ + $(eval all-clean-file += $(iobj)) \ + $(eval pre-$(exe) += $(iobj)) \ + , \ + $(if $(call is_obj_source,$(src)),\ + $(eval pre-$(exe) += $(src)) \ + , \ + $(if $(call is_alib_source,$(src)),\ + $(eval pre-$(exe) += $(src)) \ + , \ + $(error "unsupported source format: $(src)")))) \ + )\ + $(eval all-clean-file += $(exe)) \ +) + +# link several *.o files into a exeary +# $1: sources (*.o) (*.a) +# $2: dst (xyz.o too) +link_cmd = $(CC) $(LDFLAGS) $(ldflags-$(2)) -o $(2) $(filter %.o,$(1)) \ + $(filter %.a,$(1)) $(LDLIBS) $(ldlibs-$(2)) + +# print line used to link object files +ifeq ($(V),1) +link_print_cmd = echo $(call protect_quote,$(call link_cmd,$1,$2)) +else +link_print_cmd = echo " EXE $(2)" +endif + +all-clean-file += $(all-exe) diff --git a/mk/ucgine-obj-rules.mk b/mk/ucgine-obj-rules.mk new file mode 100644 index 0000000..af208e4 --- /dev/null +++ b/mk/ucgine-obj-rules.mk @@ -0,0 +1,83 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. +# + +# dump some infos if debug is enabled +ifeq ($(D),1) +$(call disp_list,------ all-obj,$(all-obj)) +$(foreach obj,$(all-obj),\ + $(info,out-$(obj): $(out-$(obj))) \ + $(call disp_list,pre-$(obj),$(pre-$(obj))) \ +) +$(call disp_list,------ all-iobj,$(all-iobj)) +$(foreach iobj,$(all-iobj),\ + $(call disp_list,pre-$(iobj),$(pre-$(iobj))) \ +) +endif + +# if a generated file has the same name than a user target, +# generate an error +conflicts := $(filter $(all-iobj),$(all-targets)) +$(if $(conflicts), \ + $(error Intermediate file has the same names than user targets:\ + $(conflicts))) + +# include dependencies and commands files if they exist +$(foreach obj,$(all-obj),\ + $(eval -include $(call depfile,$(obj))) \ + $(eval -include $(call cmdfile,$(obj))) \ +) +$(foreach iobj,$(all-iobj),\ + $(eval -include $(call depfile,$(iobj))) \ + $(eval -include $(call cmdfile,$(iobj))) \ +) + +# remove duplicates +filtered-all-iobj := $(sort $(all-iobj)) + +# convert source files to intermediate object file +$(filtered-all-iobj): $$(pre-$$@) $$(wildcard $$(dep-$$@)) FORCE + @[ -d $(dir $@) ] || mkdir -p $(dir $@) + @$(call display_deps,$(pre-$(@)),$@,$(call compile_cmd,$(pre-$(@)),$@),$?) + @$(if $(call check_deps,$@,$(call compile_cmd,$(pre-$(@)),$@),$?),\ + $(call compile_print_cmd,$(pre-$(@)),$@) && \ + $(call compile_cmd,$(pre-$(@)),$@) && \ + $(call save_cmd,$(call compile_cmd,$(pre-$(@)),$@),$@) && \ + $(call obj-fixdep,$@)) + +# remove duplicates +filtered-all-obj := $(sort $(all-obj)) + +# combine several objects files to one +$(filtered-all-obj): $$(pre-$$@) $$(wildcard $$(dep-$$@)) FORCE + @[ -d $(dir $@) ] || mkdir -p $(dir $@) + @$(call display_deps,$(pre-$(@)),$@,\ + $(call combine_cmd,$(pre-$(@)),$@),$?) + @$(if $(call check_deps,$@,$(call combine_cmd,$(pre-$(@)),$@),$?),\ + $(call combine_print_cmd,$(pre-$(@)),$@) && \ + $(call combine_cmd,$(pre-$(@)),$@) && \ + $(call save_cmd,$(call combine_cmd,$(pre-$(@)),$@),$@) && \ + $(call create_empty_depfile,$@)) diff --git a/mk/ucgine-obj-vars.mk b/mk/ucgine-obj-vars.mk new file mode 100644 index 0000000..8bd9101 --- /dev/null +++ b/mk/ucgine-obj-vars.mk @@ -0,0 +1,124 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. +# + +# obj-y-$(obj) is provided by the user +# $(obj) is the path of the object, and the variable contains +# the list of sources. Several obj-y-$(obj) can be present. + +# list all object builds requested by user +all-obj := $(patsubst obj-y-%,%,$(filter obj-y-%,$(.VARIABLES))) + +# add them to the list of targets +all-targets += $(all-obj) + +# convert source path to intermediate object path, and filter +# objects from sources +# $1: list of source paths +# $2: output directory (including trailing slash) +# return: list of intermediate object paths +src2iobj = $(addprefix $(filter-out ./,$(2)),$(notdir $(strip \ + $(patsubst %.c,%.o,\ + $(patsubst %.s,%.o,\ + $(filter-out %.o,$(1))))))) + +# return the file if it matches a extension that is built with cc +# $1: source file +is_cc_source = $(filter %.c %.s %S,$(1)) + +# return the file if it's already an object file: in this case no +# intermediate object is needed +# $1: source file +is_obj_source = $(filter %.o,$(1)) + +# return the file if it's a static library +# $1: source file +is_alib_source = $(filter %.a,$(1)) + +# for each obj, create the following variables: +# out-$(obj) = output path of the object +# pre-$(obj) = list of prerequisites for this object +# Some source files need intermediate objects, we define these variables +# for them too, and add them in a list: $(all-iobj). +# Last, we add the generated files in $(all-clean-file). +$(foreach obj,$(all-obj),\ + $(eval out-$(obj) := $(dir $(obj))) \ + $(eval pre-$(obj) := ) \ + $(foreach src,$(obj-y-$(obj)), \ + $(if $(call is_cc_source,$(src)), \ + $(eval iobj := $(call src2iobj,$(src),$(out-$(obj)))) \ + $(eval pre-$(iobj) := $(src)) \ + $(eval all-iobj += $(iobj)) \ + $(eval all-clean-file += $(iobj)) \ + $(eval pre-$(obj) += $(iobj)) \ + , \ + $(if $(call is_obj_source,$(src)),\ + $(eval pre-$(obj) += $(src)) \ + , \ + $(error "unsupported source format: $(src)"))) \ + )\ + $(eval all-clean-file += $(obj)) \ +) + +# fix the format of .o.d.tmp (generated by gcc) to a .o.d that defines +# dependencies as makefile variables +# $1: object file (.o) +obj-fixdep = if [ -f $(call file2tmpdep,$(1)) ]; then\ + echo -n "dep-$(1) = " > $(call depfile,$(1)) && \ + sed 's,^[^ ][^:]*: ,,' $(call file2tmpdep,$(1)) >> $(call depfile,$(1)) && \ + rm -f $(call file2tmpdep,$(1)); \ + else \ + $(call create_empty_depfile,$(1)); \ + fi + +# compile a file +# $1: sources +# $2: dst +compile_cmd = $(CC) -Wp,-MD,$(call file2tmpdep,$(2)) \ + $(CPPFLAGS) $(cppflags-$(2)) \ + $(CFLAGS) $(cflags-$(2)) \ + -c -o $2 $1 + +# print line used to compile a file +ifeq ($(V),1) +compile_print_cmd = echo $(call protect_quote,$(call compile_cmd,$1,$2)) +else +compile_print_cmd = echo " CC $(2)" +endif + +# combine several *.o files into one +# $1: sources (*.o) +# $2: dst (xyz.o too) +combine_cmd = $(LD) -r $(1) -o $(2) + +# print line used to combine object files +ifeq ($(V),1) +combine_print_cmd = echo $(call protect_quote,$(call combine_cmd,$1,$2)) +else +combine_print_cmd = echo " LD $(2)" +endif + +all-clean-file += $(all-obj) diff --git a/mk/ucgine-objcopy-rules.mk b/mk/ucgine-objcopy-rules.mk new file mode 100644 index 0000000..86a7cb6 --- /dev/null +++ b/mk/ucgine-objcopy-rules.mk @@ -0,0 +1,74 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. +# + +# dump some infos if debug is enabled +ifeq ($(D),1) +$(call disp_list,------ all-objcopy-hex,$(all-objcopy-hex)) +$(foreach objcopy,$(all-objcopy-hex),\ + $(info,out-$(objcopy): $(out-$(objcopy))) \ + $(call disp_list,pre-$(objcopy),$(pre-$(objcopy))) \ +) +$(call disp_list,------ all-objcopy-bin,$(all-objcopy-bin)) +$(foreach objcopy,$(all-objcopy-bin),\ + $(info,out-$(objcopy): $(out-$(objcopy))) \ + $(call disp_list,pre-$(objcopy),$(pre-$(objcopy))) \ +) +endif + +# include dependencies and commands files if they exist +$(foreach objcopy,$(all-objcopy-hex) $(all-objcopy-bin),\ + $(eval -include $(call depfile,$(objcopy))) \ + $(eval -include $(call cmdfile,$(objcopy))) \ +) + +# remove duplicates +filtered-all-objcopy-hex := $(sort $(all-objcopy-hex)) + +# convert format of executable +$(filtered-all-objcopy-hex): $$(pre-$$@) $$(wildcard $$(dep-$$@)) FORCE + @[ -d $(dir $@) ] || mkdir -p $(dir $@) + @$(call display_deps,$(pre-$(@)),$@,\ + $(call objcopy_hex_cmd,$(pre-$(@)),$@),$?) + @$(if $(call check_deps,$@,$(call objcopy_hex_cmd,$(pre-$(@)),$@),$?),\ + $(call objcopy_print_cmd,$(pre-$(@)),$@) && \ + $(call objcopy_hex_cmd,$(pre-$(@)),$@) && \ + $(call save_cmd,$(call objcopy_hex_cmd,$(pre-$(@)),$@),$@) && \ + $(call create_empty_depfile,$@)) + +# remove duplicates +filtered-all-objcopy-bin := $(sort $(all-objcopy-bin)) + +# convert format of executable +$(filtered-all-objcopy-bin): $$(pre-$$@) $$(wildcard $$(dep-$$@)) FORCE + @[ -d $(dir $@) ] || mkdir -p $(dir $@) + @$(call display_deps,$(pre-$(@)),$@,\ + $(call objcopy_bin_cmd,$(pre-$(@)),$@),$?) + @$(if $(call check_deps,$@,$(call objcopy_bin_cmd,$(pre-$(@)),$@),$?),\ + $(call objcopy_print_cmd,$(pre-$(@)),$@) && \ + $(call objcopy_bin_cmd,$(pre-$(@)),$@) && \ + $(call save_cmd,$(call objcopy_bin_cmd,$(pre-$(@)),$@),$@) && \ + $(call create_empty_depfile,$@)) diff --git a/mk/ucgine-objcopy-vars.mk b/mk/ucgine-objcopy-vars.mk new file mode 100644 index 0000000..d515d02 --- /dev/null +++ b/mk/ucgine-objcopy-vars.mk @@ -0,0 +1,89 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. +# + +# objcopy changes the format of a binary +# objcopy-hex-y-$(objcopy), objcopy-bin-y-$(objcopy) are provided by the user +# $(objcopy) is the path of the binary, and the variable contains +# the path to the elf. Several objcopy-y-$(objcopy) can be present. + +# list all executable builds requested by user +all-objcopy-hex := $(patsubst objcopy-hex-y-%,%,$(filter objcopy-hex-y-%,$(.VARIABLES))) +all-objcopy-bin := $(patsubst objcopy-bin-y-%,%,$(filter objcopy-bin-y-%,$(.VARIABLES))) + +# add them to the list of targets +all-targets += $(all-objcopy-hex) $(all-objcopy-bin) + +# for each objcopy, create the following variables: +# out-$(objcopy) = output path of the executable +# pre-$(objcopy) = list of prerequisites for this executable +# We also add the generated files in $(all-clean-file). +$(foreach objcopy,$(all-objcopy-hex),\ + $(if $(call compare,$(words $(objcopy-hex-y-$(objcopy))),1),\ + $(error "only one source file is allowed in objcopy-hex-y-$(objcopy)")) \ + $(eval out-$(objcopy) := $(dir $(objcopy))) \ + $(eval pre-$(objcopy) := $(objcopy-hex-y-$(objcopy))) \ + $(eval all-clean-file += $(objcopy)) \ +) + +# for each objcopy, create the following variables: +# out-$(objcopy) = output path of the executable +# pre-$(objcopy) = list of prerequisites for this executable +# We also add the generated files in $(all-clean-file). +$(foreach objcopy,$(all-objcopy-bin),\ + $(if $(call compare,$(words $(objcopy-bin-y-$(objcopy))),1),\ + $(error "only one source file is allowed in objcopy-bin-y-$(objcopy)")) \ + $(eval out-$(objcopy) := $(dir $(objcopy))) \ + $(eval pre-$(objcopy) := $(objcopy-bin-y-$(objcopy))) \ + $(eval all-clean-file += $(objcopy)) \ +) + +# convert format of executable from elf to ihex +# $1: source executable (elf) +# $2: destination file +objcopy_hex_cmd = $(OBJCOPY) -O ihex $(1) $(2) + +# print line used to convert executable format +ifeq ($(V),1) +objcopy_print_cmd = echo $(call protect_quote,$(call objcopy_hex_cmd,$1,$2)) +else +objcopy_print_cmd = echo " OBJCOPY $(2)" +endif + +# convert format of executable from elf to binary +# $1: source executable (elf) +# $2: destination file +objcopy_bin_cmd = $(OBJCOPY) -O binary $(1) $(2) + +# print line used to convert executable format +ifeq ($(V),1) +objcopy_print_cmd = echo $(call protect_quote,$(call objcopy_bin_cmd,$1,$2)) +else +objcopy_print_cmd = echo " OBJCOPY $(2)" +endif + +# XXX dup ? +all-clean-file += $(all-objcopy-hex) $(all-objcopy-bin) diff --git a/mk/ucgine-post.mk b/mk/ucgine-post.mk new file mode 100644 index 0000000..3d78dd6 --- /dev/null +++ b/mk/ucgine-post.mk @@ -0,0 +1,108 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. +# + +# ---- variables that must be defined: +# +# UCGINE: path to ucgine root +# +# ---- variable that can be defined anywhere +# +# CROSS: prefix of the toolchain +# CP, LN, GAWK, GREP: coreutils tools +# CC, CPP, AR, LD, OBJCOPY, OBJDUMP, STRIP: compilers/binutils +# +# ---- variable that can be defined by Makefile: +# +# obj-y-$(path) +# exe-y-$(path) +# ar-y-$(path) +# shlib-y-$(path) +# copy-y-$(path) +# slink-y-$(path) +# objcopy-y-$(path) +# subdir-y +# +# CPPFLAGS, CFLAGS, LDFLAGS, LDLIBS: global flags +# cflags-$(path), cppflags-$(path), ldflags-$(path), ldlibs-$(path): per +# file flags +# mkflags-$(path): flags for subdirectories +# +# ---- variables that can be defined on the command line: +# +# EXTRA_CPPFLAGS, EXTRA_CFLAGS, EXTRA_LDFLAGS, EXTRA_LDLIBS: global +# extra flags +# extra-cflags-$(path), extra-cppflags-$(path): per object extra flags + +ifeq ($(UCGINE),) +$(error UCGINE environment variable is not defined) +endif + +# list of targets asked by user +all-targets := +# list of files generated +all-clean-file := + +# usual internal variables: +# out-$(file) = output path of a generated file +# pre-$(file) = list of files needed to generate $(file) +# all-type = list of targets for this type + +include $(UCGINE)/mk/ucgine-obj-vars.mk +include $(UCGINE)/mk/ucgine-exe-vars.mk +include $(UCGINE)/mk/ucgine-ar-vars.mk +include $(UCGINE)/mk/ucgine-shlib-vars.mk +include $(UCGINE)/mk/ucgine-copy-vars.mk +include $(UCGINE)/mk/ucgine-slink-vars.mk +include $(UCGINE)/mk/ucgine-objcopy-vars.mk +include $(UCGINE)/mk/ucgine-subdir-vars.mk +# must stay at the end +include $(UCGINE)/mk/ucgine-clean-vars.mk + +# dump the list of targets +ifeq ($(D),1) +$(call disp_list,------ all-targets,$(all-targets)) +endif + +# first rule (default) +.PHONY: _ucgine_all +_ucgine_all: $(all-targets) + +# the includes below require second expansion +.SECONDEXPANSION: + +include $(UCGINE)/mk/ucgine-obj-rules.mk +include $(UCGINE)/mk/ucgine-exe-rules.mk +include $(UCGINE)/mk/ucgine-ar-rules.mk +include $(UCGINE)/mk/ucgine-shlib-rules.mk +include $(UCGINE)/mk/ucgine-copy-rules.mk +include $(UCGINE)/mk/ucgine-slink-rules.mk +include $(UCGINE)/mk/ucgine-objcopy-rules.mk +include $(UCGINE)/mk/ucgine-subdir-rules.mk +include $(UCGINE)/mk/ucgine-clean-rules.mk + +.PHONY: FORCE +FORCE: diff --git a/mk/ucgine-pre.mk b/mk/ucgine-pre.mk new file mode 100644 index 0000000..5ab0221 --- /dev/null +++ b/mk/ucgine-pre.mk @@ -0,0 +1,49 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. +# + +# ---- variables that must be defined: +# +# UCGINE: path to ucgine root +# UCGINE_ARCH: architecture (ex: avr, stm32, ...) +# + +ifeq ($(UCGINE),) +$(error UCGINE environment variable is not defined) +endif + +ifeq ($(UCGINE_ARCH),) +$(error UCGINE_ARCH environment variable is not defined) +endif + +MAKEFLAGS += --no-print-directory + +include $(UCGINE)/mk/ucgine-tools.mk + +include $(UCGINE)/arch/$(UCGINE_ARCH)/mk/ucgine-arch.mk + +include $(UCGINE)/mk/ucgine-vars.mk + diff --git a/mk/ucgine-shlib-rules.mk b/mk/ucgine-shlib-rules.mk new file mode 100644 index 0000000..1a131cc --- /dev/null +++ b/mk/ucgine-shlib-rules.mk @@ -0,0 +1,55 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. +# + +# dump some infos if debug is enabled +ifeq ($(D),1) +$(call disp_list,------ all-shlib,$(all-shlib)) +$(foreach shlib,$(all-shlib),\ + $(info,out-$(shlib): $(out-$(shlib))) \ + $(call disp_list,pre-$(shlib),$(pre-$(shlib))) \ +) +endif + +# include dependencies and commands files if they exist +$(foreach shlib,$(all-shlib),\ + $(eval -include $(call depfile,$(shlib))) \ + $(eval -include $(call cmdfile,$(shlib))) \ +) + +# remove duplicates +filtered-all-shlib := $(sort $(all-shlib)) + +# link several objects files into one shared object +$(filtered-all-shlib): $$(pre-$$@) $$(wildcard $$(dep-$$@)) FORCE + @[ -d $(dir $@) ] || mkdir -p $(dir $@) + @$(call display_deps,$(pre-$(@)),$@,\ + $(call shlib_cmd,$(pre-$(@)),$@),$?) + @$(if $(call check_deps,$@,$(call shlib_cmd,$(pre-$(@)),$@),$?),\ + $(call shlib_print_cmd,$(pre-$(@)),$@) && \ + $(call shlib_cmd,$(pre-$(@)),$@) && \ + $(call save_cmd,$(call shlib_cmd,$(pre-$(@)),$@),$@) && \ + $(call create_empty_depfile,$@)) diff --git a/mk/ucgine-shlib-vars.mk b/mk/ucgine-shlib-vars.mk new file mode 100644 index 0000000..7906510 --- /dev/null +++ b/mk/ucgine-shlib-vars.mk @@ -0,0 +1,76 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. +# + +# shlib-y-$(shlib) is provided by the user +# $(shlib) is the path of the shared library, and the variable +# contains the list of sources. Several shlib-y-$(shlib) can be +# present. + +# list all shlib builds requested by user +all-shlib := $(patsubst shlib-y-%,%,$(filter shlib-y-%,$(.VARIABLES))) + +# add them to the list of targets +all-targets += $(all-shlib) + +# for each shlib, create the following variables: +# out-$(shlib) = output path of the shlibcutable +# pre-$(shlib) = list of prerequisites for this shlibcutable +# Some source files need intermediate objects, we define these variables +# for them too, and add them in a list: $(all-iobj). +# Last, we add the generated files in $(all-clean-file). +$(foreach shlib,$(all-shlib),\ + $(eval out-$(shlib) := $(dir $(shlib))) \ + $(eval pre-$(shlib) := ) \ + $(foreach src,$(shlib-y-$(shlib)), \ + $(if $(call is_cc_source,$(src)), \ + $(eval iobj := $(call src2iobj,$(src),$(out-$(shlib)))) \ + $(eval pre-$(iobj) := $(src)) \ + $(eval all-iobj += $(iobj)) \ + $(eval all-clean-file += $(iobj)) \ + $(eval pre-$(shlib) += $(iobj)) \ + , \ + $(if $(call is_obj_source,$(src)),\ + $(eval pre-$(shlib) += $(src)) \ + , \ + $(error "unsupported source format: $(src)"))) \ + )\ + $(eval all-clean-file += $(shlib)) \ +) + +# link several *.o files into a shared libary +# $1: sources (*.o) +# $2: dst (xyz.so) +shlib_cmd = $(CC) $(LDFLAGS) $(ldflags-$(2)) -shared -o $(2) $(1) + +# print line used to shlib object files +ifeq ($(V),1) +shlib_print_cmd = echo $(call protect_quote,$(call shlib_cmd,$1,$2)) +else +shlib_print_cmd = echo " SHLIB $(2)" +endif + +all-clean-file += $(all-shlib) diff --git a/mk/ucgine-slink-rules.mk b/mk/ucgine-slink-rules.mk new file mode 100644 index 0000000..bea3aad --- /dev/null +++ b/mk/ucgine-slink-rules.mk @@ -0,0 +1,55 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. +# + +# dump some infos if debug is enabled +ifeq ($(D),1) +$(call disp_list,------ all-slink,$(all-slink)) +$(foreach slink,$(all-slink),\ + $(info,out-$(slink): $(out-$(slink))) \ + $(call disp_list,pre-$(slink),$(pre-$(slink))) \ +) +endif + +# include dependencies and commands files if they exist +$(foreach slink,$(all-slink),\ + $(eval -include $(call depfile,$(slink))) \ + $(eval -include $(call cmdfile,$(slink))) \ +) + +# remove duplicates +filtered-all-slink := $(sort $(all-slink)) + +# convert format of executable +$(filtered-all-slink): $$(pre-$$@) $$(wildcard $$(dep-$$@)) FORCE + @[ -d $(dir $@) ] || mkdir -p $(dir $@) + @$(call display_deps,$(pre-$(@)),$@,\ + $(call slink_cmd,$(pre-$(@)),$@),$?) + @$(if $(call check_deps,$@,$(call slink_cmd,$(pre-$(@)),$@),$?),\ + $(call slink_print_cmd,$(pre-$(@)),$@) && \ + $(call slink_cmd,$(pre-$(@)),$@) && \ + $(call save_cmd,$(call slink_cmd,$(pre-$(@)),$@),$@) && \ + $(call create_empty_depfile,$@)) diff --git a/mk/ucgine-slink-vars.mk b/mk/ucgine-slink-vars.mk new file mode 100644 index 0000000..428cb25 --- /dev/null +++ b/mk/ucgine-slink-vars.mk @@ -0,0 +1,74 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. +# + +# create a symbolic link of a file +# slink-y-$(slink) is provided by the user +# $(slink) is the path of the directory containing the destination +# files, and the variable contains the path of the files to linked. Several +# slink-y-$(slink) can be present. + +# list all path requested by user +_all-slink := $(patsubst slink-y-%,%,$(filter slink-y-%,$(.VARIABLES))) +all-slink := + +# for each slink, create the following variables: +# out-$(slink) = output path of the executable +# pre-$(slink) = list of prerequisites for this executable +# We also add the files in $(all-slink). +$(foreach slink,$(_all-slink),\ + $(if $(notdir $(slink)), \ + $(if $(call compare,$(words $(slink-y-$(slink))),1), \ + $(error "only one source file is allowed in slink-y-$(slink)")) \ + $(eval dst := $(dir $(slink))$(notdir $(slink-y-$(slink)))) \ + $(eval out-$(slink) := $(dir $(slink))) \ + $(eval pre-$(slink) := $(slink-y-$(slink))) \ + $(eval all-slink += $(dst)) \ + , \ + $(foreach src,$(slink-y-$(slink)),\ + $(eval dst := $(slink)$(notdir $(src))) \ + $(eval out-$(slink) := $(slink)) \ + $(eval pre-$(dst) := $(src)) \ + $(eval all-slink += $(dst)) \ + ) \ + ) \ +) + +# add them to the list of targets and clean +all-targets += $(all-slink) +all-clean-file += $(all-slink) + +# convert format of executable from elf to ihex +# $1: source executable (elf) +# $2: destination file +slink_cmd = $(LN) -nsf $(abspath $(1)) $(2) + +# print line used to convert executable format +ifeq ($(V),1) +slink_print_cmd = echo $(call protect_quote,$(call slink_cmd,$1,$2)) +else +slink_print_cmd = echo " SLINK $(2)" +endif diff --git a/mk/ucgine-subdir-rules.mk b/mk/ucgine-subdir-rules.mk new file mode 100644 index 0000000..aae82db --- /dev/null +++ b/mk/ucgine-subdir-rules.mk @@ -0,0 +1,30 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. +# + +.PHONY: $(subdir-y) +$(subdir-y): FORCE + $(Q)$(MAKE) -C $(@) $(mkflags-$(@)) $(MAKECMDGOALS) diff --git a/mk/ucgine-subdir-vars.mk b/mk/ucgine-subdir-vars.mk new file mode 100644 index 0000000..14e4ee1 --- /dev/null +++ b/mk/ucgine-subdir-vars.mk @@ -0,0 +1,33 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. +# + +# subdir-y is provided by the user +# it contains the list of directory to build + +# add them to the list of targets +all-targets += $(subdir-y) +all-clean-target += $(subdir-y) diff --git a/mk/ucgine-tools.mk b/mk/ucgine-tools.mk new file mode 100644 index 0000000..484262b --- /dev/null +++ b/mk/ucgine-tools.mk @@ -0,0 +1,159 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. +# + +empty:= +space:= $(empty) $(empty) +indent:= $(space)$(space) + +# define a newline char, useful for debugging with $(info) +define newline + + +endef + +# $(prefix shell commands with $(Q) to silent them, except if V=1 +Q=@ +ifeq ("$(V)-$(origin V)", "1-command line") +Q= +endif + +# set variable $1 to $2 if the variable has an implicit value or +# is not defined +# $1 variable name +# $2 new variable content +set_default = $(if \ + $(call not,$(or \ + $(compare $(origin $(1)),default), \ + $(compare $(origin $(1)),undefined) \ + )),\ + $(eval $(1) = $(2)) \ +) + +# display a list +# $1 title +# $2 list +disp_list = $(info $(1)$(newline)\ + $(addsuffix $(newline),$(addprefix $(space),$(2)))) + +# add a dot in front of the file name +# $1 list of paths +# return: full paths with files prefixed by a dot +dotfile = $(strip $(foreach f,$(1),\ + $(join $(dir $f),.$(notdir $f)))) + +# convert source/obj files into dot-dep filename +# $1 list of paths +# return: full paths with files prefixed by a dot and suffixed with .d +depfile = $(strip $(call dotfile,$(addsuffix .d,$(1)))) + +# convert source/obj files into dot-dep filename +# $1 list of paths +# return: full paths with files prefixed by a dot and suffixed with .d.tmp +file2tmpdep = $(strip $(call dotfile,$(addsuffix .d.tmp,$(1)))) + +# convert source/obj files into dot-cmd filename +# $1 list of paths +# return: full paths with files prefixed by a dot and suffixed with .cmd +cmdfile = $(strip $(call dotfile,$(addsuffix .cmd,$(1)))) + +# add a \ before each quote +protect_quote = $(subst ','\'',$(1)) +#'# editor syntax highlight fix + +# return an non-empty string if $1 is empty, and vice versa +# $1 a string +not = $(if $1,,true) + +# return 1 if parameter is a non-empty string, else 0 +boolean = $(if $1,1,0) + +# return an empty string if string are equal +compare = $(strip $(subst $(1),,$(2)) $(subst $(2),,$(1))) + +# return a non-empty string if a file does not exist +# $1: file +file_missing = $(call compare,$(wildcard $1),$1) + +# return a non-empty string if cmdline changed +# $1: file to be built +# $2: the command to build it +cmdline_changed = $(call compare,$(strip $(cmd-$(1))),$(strip $(2))) + +# return an non-empty string if the .d file does not exist +# $1: the dep file (.d) +depfile_missing = $(call compare,$(wildcard $(1)),$(1)) + +# return a non-empty string if, according to dep-xyz variable, a file +# needed to build $1 does not exist. In this case we need to rebuild +# the file and the .d file. +# $1: file to be built +dep-missing = $(call compare,$(wildcard $(dep-$(1))),$(dep-$(1))) + +# return an empty string if no prereq is newer than target +# $1: list of prerequisites newer than target ($?) +dep-newer = $(strip $(filter-out FORCE,$(1))) + +# display why a file should be re-built +# $1: source files +# $2: dst file +# $3: build command +# $4: all prerequisites newer than target ($?) +ifeq ($(D),1) +display_deps = \ + echo -n "$1 -> $2 " ; \ + echo -n "file_missing=$(call boolean,$(call file_missing,$(2))) " ; \ + echo -n "cmdline_changed=$(call boolean,$(call cmdline_changed,$(2),$(3))) " ; \ + echo -n "depfile_missing=$(call boolean,$(call depfile_missing,$(call depfile,$(2)))) " ; \ + echo -n "dep-missing=$(call boolean,$(call dep-missing,$(2))) " ; \ + echo "dep-newer=$(call boolean,$(call dep-newer,$(4)))" +else +display_deps= +endif + +# return an empty string if a file should be rebuilt +# $1: dst file +# $2: build command +# $3: all prerequisites newer than target ($?) +check_deps = \ + $(or $(call file_missing,$(1)),\ + $(call cmdline_changed,$(1),$(2)),\ + $(call depfile_missing,$(call depfile,$(1))),\ + $(call dep-missing,$(1)),\ + $(call dep-newer,$(3))) + +# create a depfile (.d) with no additional deps +# $1: object file (.o) +create_empty_depfile = echo "dep-$(1) =" > $(call depfile,$(1)) + +# save a command in a file +# $1: command to build the file +# $2: name of the file +save_cmd = echo "cmd-$(2) = $(call protect_quote,$(1))" > $(call cmdfile,$(2)) + +# remove the FORCE target from the list of all prerequisites $+ +# no arguments, use $+ +prereq = $(filter-out FORCE,$(+)) diff --git a/mk/ucgine-vars.mk b/mk/ucgine-vars.mk new file mode 100644 index 0000000..c07764e --- /dev/null +++ b/mk/ucgine-vars.mk @@ -0,0 +1,49 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. +# + +CROSS ?= $(ARCH_CROSS) + +# core tools +CP ?= cp +LN ?= ln +GAWK ?= gawk +GREP ?= grep +# compiler and binutils, set_default overrides mk implicit value +# but not command line or standard variables +$(call set_default,CC,$(CROSS)gcc) +$(call set_default,CPP,$(CROSS)cpp) +$(call set_default,AR,$(CROSS)ar) +$(call set_default,LD,$(CROSS)ld) +$(call set_default,OBJCOPY,$(CROSS)objcopy) +$(call set_default,OBJDUMP,$(CROSS)objdump) +$(call set_default,STRIP,$(CROSS)strip) +HOSTCC ?= cc + +CFLAGS += $(EXTRA_CFLAGS) +CPPFLAGS += $(EXTRA_CPPFLAGS) +LDFLAGS += $(EXTRA_LDFLAGS) +LDLIBS += $(EXTRA_LDLIBS) diff --git a/tools/cfzy/Makefile b/tools/cfzy/Makefile new file mode 100644 index 0000000..4114bda --- /dev/null +++ b/tools/cfzy/Makefile @@ -0,0 +1,71 @@ +# +# Copyright 2015, Olivier MATZ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + +UCGINE ?= $(abspath ../..) + +# XXX we should not need UCGINE_ARCH +UCGINE_ARCH = posix +include $(UCGINE)/mk/ucgine-pre.mk + +O ?= $(CURDIR)/build + +CFLAGS = -g -O2 -Wall +CFLAGS += -Ilibconfizery + +src := libconfizery/cfzy_expr.c +src += libconfizery/cfzy_list.c +src += libconfizery/cfzy_htable.c +src += libconfizery/cfzy_string.c +src += libconfizery/cfzy_log.c +src += libconfizery/cfzy_confnode.c +src += libconfizery/cfzy_conftree.c +src += libconfizery/cfzy_dotconfig.c +src += libconfizery/cfzy_c_hdr.c +src += libconfizery/cfzy_file.c +src += libconfizery/cfzy_conftree_parser.c +src += libconfizery/cfzy_confnode_choice.c +src += libconfizery/cfzy_confnode_choiceconfig.c +src += libconfizery/cfzy_confnode_comment.c +src += libconfizery/cfzy_confnode_config.c +src += libconfizery/cfzy_confnode_if.c +src += libconfizery/cfzy_confnode_intconfig.c +src += libconfizery/cfzy_confnode_menu.c +src += libconfizery/cfzy_confnode_menuconfig.c +src += libconfizery/cfzy_confnode_root.c +src += libconfizery/cfzy_confnode_strconfig.c +ar-y-$(O)/libconfizery/libconfizery.a := $(src) + +src := cfzy-basic/main.c +src += $(O)/libconfizery/libconfizery.a +exe-y-$(O)/cfzy-basic/cfzy-basic := $(src) + +include $(UCGINE)/mk/ucgine-post.mk + +.PHONY: all +all: $(all-targets) + +.PHONY: clean +clean: _ucgine_clean diff --git a/tools/cfzy/build/cfzy-basic/cfzy-basic b/tools/cfzy/build/cfzy-basic/cfzy-basic new file mode 100755 index 0000000..9402548 Binary files /dev/null and b/tools/cfzy/build/cfzy-basic/cfzy-basic differ diff --git a/tools/cfzy/cfzy-basic/Makefile b/tools/cfzy/cfzy-basic/Makefile new file mode 100644 index 0000000..c3ac5f0 --- /dev/null +++ b/tools/cfzy/cfzy-basic/Makefile @@ -0,0 +1,11 @@ +include $(TOPDIR)/confizery.vars.mk + +PROG = cfzy-basic + +CFLAGS += -I$(SRCDIR)/libconfizery + +LDLIBS = $(BUILDDIR)/libconfizery/libconfizery.a + +SRCS = main.c + +include $(TOPDIR)/confizery.prog.mk diff --git a/tools/cfzy/cfzy-basic/main.c b/tools/cfzy/cfzy-basic/main.c new file mode 100644 index 0000000..653ef40 --- /dev/null +++ b/tools/cfzy/cfzy-basic/main.c @@ -0,0 +1,422 @@ +/* + * Copyright (c) 2013, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 +#include + +#define LOG(level, fmt, args...) \ + CFZY_LOG("cfzy-basic", level, fmt, ##args) + +/* Parsed arguments */ +struct cfzy_arguments { + char *input_conftree; + char *input_dotconfig; + char *output_dotconfig; + char *output_c_hdr; + char *output_multi_c_hdr; +}; + +static void +usage(const char *prog, int code) +{ + fprintf(stderr, + "\nUsage : \n"); + fprintf(stderr, + " %s [options]\n", prog); + fprintf(stderr, + "-h, --help\n" + " show help\n"); + fprintf(stderr, + "-d, --debug-level\n" + " set debug level (0: no log, 4 noisy)\n"); + fprintf(stderr, + "-c, --input-conftree\n" + " path to input configuration tree file " + "(mandatory)\n"); + fprintf(stderr, + "-i, --input-dotconfig\n" + " path to input dotconfig file\n"); + fprintf(stderr, + "-o, --output-dotconfig\n" + " path to output dotconfig file\n"); + fprintf(stderr, + "-O, --output-c-hdr\n" + " path to output C header file\n"); + fprintf(stderr, + "-m, --output-multi-c-hdr\n" + " path to output multiple C header directory\n"); + exit(code); +} + +static void +free_args(struct cfzy_arguments *cfzy_args) +{ + if (cfzy_args->input_conftree != NULL) + free(cfzy_args->input_conftree); + if (cfzy_args->input_dotconfig != NULL) + free(cfzy_args->input_dotconfig); + if (cfzy_args->output_dotconfig != NULL) + free(cfzy_args->output_dotconfig); + if (cfzy_args->output_c_hdr != NULL) + free(cfzy_args->output_c_hdr); + if (cfzy_args->output_multi_c_hdr != NULL) + free(cfzy_args->output_multi_c_hdr); +} + +static void +parse_args(struct cfzy_arguments *cfzy_args, int argc, char **argv) +{ + int ch; + const char * prog = argv[0]; + int option_index; + int level; + static struct option lgopts[] = { + {"help", 0, 0, 'h'}, + {"debug-level", 0, 0, 'd'}, + {"input-conftree", 0, 0, 'c'}, + {"input-dotconfig", 0, 0, 'i'}, + {"output-dotconfig", 0, 0, 'o'}, + {"output-c-hdr", 0, 0, 'O'}, + {"output-multi-c-hdr", 0, 0, 'm'}, + {NULL, 0, 0, 0} + }; + + while ((ch = getopt_long(argc, argv, + "h" /* help */ + "d:" /* debug-level */ + "c:" /* input-conftree */ + "i:" /* input-dotconfig */ + "o:" /* output-dotconfig */ + "O:" /* output-c-hdr */ + "m:" /* output-multi-c-hdr */ + , + lgopts, &option_index)) != -1) { + switch (ch) { + case 'h': /* help */ + free_args(cfzy_args); + usage(prog, 0); /* will exit */ + break; + + case 'd': /* debug-level */ + level = atoi(optarg); + if (level < 0 || level > CFZY_LOG_DEBUG) { + LOG(ERR, "invalid log level\n"); + free_args(cfzy_args); + cfzy_log_exit(); + exit(1); + } + cfzy_log_set_default_level(level); + break; + + case 'c': /* input-conftree */ + cfzy_args->input_conftree = strdup(optarg); + if (cfzy_args->input_conftree == NULL) { + LOG(ERR, "Cannot alloc input conftree str\n"); + free_args(cfzy_args); + cfzy_log_exit(); + exit(1); + } + break; + + case 'i': /* input-dotconfig */ + cfzy_args->input_dotconfig = strdup(optarg); + if (cfzy_args->input_dotconfig == NULL) { + LOG(ERR, "Cannot alloc input dotconfig str\n"); + free_args(cfzy_args); + cfzy_log_exit(); + exit(1); + } + break; + + case 'o': /* output-dotconfig */ + cfzy_args->output_dotconfig = strdup(optarg); + if (cfzy_args->output_dotconfig == NULL) { + LOG(ERR, "Cannot alloc output dotconfig str\n"); + free_args(cfzy_args); + cfzy_log_exit(); + exit(1); + } + break; + + case 'O': /* output-c-hdr */ + cfzy_args->output_c_hdr = strdup(optarg); + if (cfzy_args->output_c_hdr == NULL) { + LOG(ERR, "Cannot alloc output c_hdr str\n"); + free_args(cfzy_args); + cfzy_log_exit(); + exit(1); + } + break; + + case 'm': /* output-multi-c-hdr */ + cfzy_args->output_multi_c_hdr = strdup(optarg); + if (cfzy_args->output_multi_c_hdr == NULL) { + LOG(ERR, "Cannot alloc output multi_c_hdr str\n"); + free_args(cfzy_args); + cfzy_log_exit(); + exit(1); + } + break; + + default: + LOG(ERR, "invalid option\n"); + free_args(cfzy_args); + cfzy_log_exit(); + usage(prog, 1); /* will exit */ + } + } + + /* conftree file is mandatory */ + if (cfzy_args->input_conftree == NULL) { + LOG(ERR, "input conftree is mandatory\n"); + free_args(cfzy_args); + cfzy_log_exit(); + usage(prog, 1); /* will exit */ + } + + /* check that at least one output argument is given */ + if (cfzy_args->output_dotconfig == NULL && + cfzy_args->output_c_hdr == NULL && + cfzy_args->output_multi_c_hdr == NULL) { + LOG(ERR, "at least one output argument is needed\n"); + free_args(cfzy_args); + cfzy_log_exit(); + usage(prog, 1); /* will exit */ + } + + argc -= optind; + argv += optind; + /* XXX parse mode */ +} + +/* + * configuration tree + * + * -i input .config: + * default is none + * + * -o output .config + * defaut is none + * + * ?? output autoconf.h + * + * output config/xxx.h + * + * -v verbose + * + * oldconfig: + * prompt for options that have no user value + * + * silentoldconfig + * set all options that have no user value to default + * + * config + * ? + * + * defconfig + * ? + * + * randconfig + * set all options to a random value (only for booleans, other + * values are set to default) + * + * allyesconfig + * set all options to y (booleans only) + * + * allnoconfig + * set all options to n (booleans only) + * + * - ouvrir le fichier conftree + * - si -i, ouvrir .config + * - effectue les operations + * - si -o et/ou ... output + */ + +static int +do_configuration(struct cfzy_arguments *cfzy_args) +{ + struct cfzy_confnode *n; + struct cfzy_conftree *conftree; + char buf[BUFSIZ]; + char *s; + int ret; + + /* parse configuration tree */ + conftree = cfzy_conftree_new(cfzy_args->input_conftree); + if (conftree == NULL) { + LOG(ERR, "Cannot parse configuration tree\n"); + return -1; + } + + /* parse .config if provided */ + if (cfzy_args->input_dotconfig != NULL && + cfzy_dotconfig_read(conftree, cfzy_args->input_dotconfig) < 0) { + cfzy_conftree_free(conftree); + LOG(ERR, "Cannot parse dotconfig <%s>\n", + cfzy_args->input_dotconfig); + return -1; + } + +#if 0 + /* evaluate effective values of conftree */ + if (cfzy_conftree_evaluate(conftree) < 0) { + cfzy_conftree_free(conftree); + LOG(ERR, "Cannot evaluate configuration tree\n"); + return -1; + } +#endif + + memset(buf, 0, sizeof(buf)); + + /* browse all nodes following priority list and prompt user */ + TAILQ_FOREACH(n, &conftree->prio_list, prio_next) { + + if (n->flags & CFZY_F_INVISIBLE) + continue; + if (n->flags & CFZY_F_NO_NAME) + continue; + + /* node has a user value or user value cannot be set, + * just display it */ + if ((n->user_value != NULL) || (n->flags & CFZY_F_NO_SET_VALUE)) { + /* XXX we should have a difference between + * NO_VALUE (menu for instance) and + * NO_SET_VALUE */ + cfzy_confnode_evaluate(n); + printf("%s=%s\n", cfzy_confnode_name(n), + n->effective_value); + continue; + } + + /* node cannot be enabled due to deps */ + ret = cfzy_confnode_check_deps(n); + if (ret == 0) { + cfzy_confnode_evaluate(n); + printf("%s=%s\n", cfzy_confnode_name(n), + n->effective_value); + continue; + } + if (ret < 0) { + LOG(ERR, "Cannot check deps\n"); + return -1; + } + + /* ok, now prompt user */ + + cfzy_confnode_dump(n, stdout, CFZY_DUMP_MODE_STANDARD); + + while (1) { + printf("Enter user value for %s > ", + cfzy_confnode_name(n)); + fflush(stdout); + + fgets(buf, sizeof(buf) - 1, stdin); + s = buf; + strsep(&s, "\r\n"); + + /* empty buf means default value */ + if (strcmp(buf, "") == 0) + break; + + /* invalid value, prompt again */ + if (cfzy_confnode_set_uservalue(n, buf) < 0) { + printf("invalid value\n"); + continue; + } + + break; + } + + cfzy_confnode_evaluate(n); + printf("%s=%s\n", cfzy_confnode_name(n), + n->effective_value); + } + + /* output .config if provided */ + if (cfzy_args->output_dotconfig != NULL && + cfzy_dotconfig_write(conftree, cfzy_args->output_dotconfig) < 0) { + cfzy_conftree_free(conftree); + LOG(ERR, "Cannot output dotconfig <%s>\n", + cfzy_args->output_dotconfig); + return -1; + } + + /* output C header if provided */ + if (cfzy_args->output_c_hdr != NULL && + cfzy_c_hdr_write(conftree, cfzy_args->output_c_hdr) < 0) { + cfzy_conftree_free(conftree); + LOG(ERR, "Cannot output C header <%s>\n", + cfzy_args->output_c_hdr); + return -1; + } + + /* output multiple C headers if provided */ + if (cfzy_args->output_multi_c_hdr != NULL && + cfzy_multi_c_hdr_write(conftree, cfzy_args->output_multi_c_hdr) < 0) { + cfzy_conftree_free(conftree); + LOG(ERR, "Cannot output multiple C headers <%s>\n", + cfzy_args->output_multi_c_hdr); + return -1; + } + + cfzy_conftree_free(conftree); + return 0; +} + +int main(int argc, char **argv) +{ + int ret = EXIT_SUCCESS; + struct cfzy_arguments cfzy_args; + + /* XXX a cfzy_init() would be better */ + cfzy_log_init(); + + memset(&cfzy_args, 0, sizeof(cfzy_args)); + + /* argument parsing is always successful (will exit on error) */ + parse_args(&cfzy_args, argc, argv); + + /* do the configuration */ + if (do_configuration(&cfzy_args) < 0) { + LOG(ERR, "Configuration failed"); + ret = EXIT_FAILURE; + } + + free_args(&cfzy_args); + cfzy_log_exit(); + + return ret; +} diff --git a/tools/cfzy/cfzy-test/Makefile b/tools/cfzy/cfzy-test/Makefile new file mode 100644 index 0000000..691a0f5 --- /dev/null +++ b/tools/cfzy/cfzy-test/Makefile @@ -0,0 +1,14 @@ +include $(TOPDIR)/confizery.vars.mk + +PROG = confizery-test + +CFLAGS += -I$(SRCDIR)/libconfizery + +LDLIBS = $(BUILDDIR)/libconfizery/libconfizery.a + +SRCS = main.c +SRCS += test_expr.c +SRCS += test_conftree.c +SRCS += test_dotconfig.c + +include $(TOPDIR)/confizery.prog.mk diff --git a/tools/cfzy/cfzy-test/invalid-configs/circular-dep.cfzy b/tools/cfzy/cfzy-test/invalid-configs/circular-dep.cfzy new file mode 100644 index 0000000..1d8af7f --- /dev/null +++ b/tools/cfzy/cfzy-test/invalid-configs/circular-dep.cfzy @@ -0,0 +1,8 @@ +# circular dependency between FOO and BAR +config FOO + prompt "hello" + default y + requires BAR + +config BAR + default y if FOO diff --git a/tools/cfzy/cfzy-test/invalid-configs/circular-dep2.cfzy b/tools/cfzy/cfzy-test/invalid-configs/circular-dep2.cfzy new file mode 100644 index 0000000..27fc451 --- /dev/null +++ b/tools/cfzy/cfzy-test/invalid-configs/circular-dep2.cfzy @@ -0,0 +1,11 @@ +# circular dependency between FOO, BAR and TOTO +config FOO + prompt "hello" + default y + requires TOTO + +menuconfig BAR + default y + requires FOO + +config TOTO diff --git a/tools/cfzy/cfzy-test/invalid-configs/dotconfig-bad-val b/tools/cfzy/cfzy-test/invalid-configs/dotconfig-bad-val new file mode 100644 index 0000000..17804c7 --- /dev/null +++ b/tools/cfzy/cfzy-test/invalid-configs/dotconfig-bad-val @@ -0,0 +1,17 @@ +# +# -- Menu 1 +# +CONFIG_MENU1_CONFIG1=y +# Hello +# CONFIG_MENU1_CONFIG2 is not set +# CONFIG_MENU1_CONFIG3 is not set +CONFIG_MENU1_CHOICE="MENU1_CHOICE2" +# CONFIG_MENU1_CHOICE1 is not set +CONFIG_MENU1_CHOICE2=y +# CONFIG_MENU1_CHOICE3 is not set +CONFIG_INT_EXAMPLE=a +CONFIG_STR_EXAMPLE="my default value" +# +# -- My menuconfig node +# +# CONFIG_MENUCONFIG1 is not set diff --git a/tools/cfzy/cfzy-test/invalid-configs/dotconfig-bad-val2 b/tools/cfzy/cfzy-test/invalid-configs/dotconfig-bad-val2 new file mode 100644 index 0000000..a1ed3fb --- /dev/null +++ b/tools/cfzy/cfzy-test/invalid-configs/dotconfig-bad-val2 @@ -0,0 +1,16 @@ +# +# -- Menu 1 +# +CONFIG_MENU1_CONFIG1=y +# Hello +# CONFIG_MENU1_CONFIG2 is not set +# CONFIG_MENU1_CONFIG3 is not set +CONFIG_MENU1_CHOICE="MENU1_CHOICE2" +# CONFIG_MENU1_CHOICE1 is not set +CONFIG_MENU1_CHOICE2=y d +# CONFIG_MENU1_CHOICE3 is not set +CONFIG_STR_EXAMPLE="my default value" +# +# -- My menuconfig node +# +# CONFIG_MENUCONFIG1 is not set diff --git a/tools/cfzy/cfzy-test/invalid-configs/dotconfig-bad-val3 b/tools/cfzy/cfzy-test/invalid-configs/dotconfig-bad-val3 new file mode 100644 index 0000000..c7e08fc --- /dev/null +++ b/tools/cfzy/cfzy-test/invalid-configs/dotconfig-bad-val3 @@ -0,0 +1,16 @@ +# +# -- Menu 1 +# +z +CONFIG_MENU1_CONFIG1=y +# Hello +# CONFIG_MENU1_CONFIG2 is not set +# CONFIG_MENU1_CONFIG3 is not set +CONFIG_MENU1_CHOICE="MENU1_CHOICE2" +# CONFIG_MENU1_CHOICE1 is not set +# CONFIG_MENU1_CHOICE3 is not set +CONFIG_STR_EXAMPLE="my default value" +# +# -- My menuconfig node +# +# CONFIG_MENUCONFIG1 is not set diff --git a/tools/cfzy/cfzy-test/invalid-configs/dup-name.cfzy b/tools/cfzy/cfzy-test/invalid-configs/dup-name.cfzy new file mode 100644 index 0000000..6bc2c4d --- /dev/null +++ b/tools/cfzy/cfzy-test/invalid-configs/dup-name.cfzy @@ -0,0 +1,4 @@ +# duplicate config name +config FOO + +intconfig FOO diff --git a/tools/cfzy/cfzy-test/invalid-configs/invalid-attr.cfzy b/tools/cfzy/cfzy-test/invalid-configs/invalid-attr.cfzy new file mode 100644 index 0000000..7ed2e4b --- /dev/null +++ b/tools/cfzy/cfzy-test/invalid-configs/invalid-attr.cfzy @@ -0,0 +1,4 @@ +# invalid attribute +config FOO + prompt "hello" + foo bar diff --git a/tools/cfzy/cfzy-test/invalid-configs/invalid-command.cfzy b/tools/cfzy/cfzy-test/invalid-configs/invalid-command.cfzy new file mode 100644 index 0000000..adb927e --- /dev/null +++ b/tools/cfzy/cfzy-test/invalid-configs/invalid-command.cfzy @@ -0,0 +1,3 @@ +# invalid command +foo bar + prompt "hello" diff --git a/tools/cfzy/cfzy-test/invalid-configs/invalid-expr.cfzy b/tools/cfzy/cfzy-test/invalid-configs/invalid-expr.cfzy new file mode 100644 index 0000000..6bf8fad --- /dev/null +++ b/tools/cfzy/cfzy-test/invalid-configs/invalid-expr.cfzy @@ -0,0 +1,4 @@ +# invalid expression +config FOO + prompt "hello" + default y if ( invalid expression xx diff --git a/tools/cfzy/cfzy-test/invalid-configs/invalid-value.cfzy b/tools/cfzy/cfzy-test/invalid-configs/invalid-value.cfzy new file mode 100644 index 0000000..20a4ff5 --- /dev/null +++ b/tools/cfzy/cfzy-test/invalid-configs/invalid-value.cfzy @@ -0,0 +1,4 @@ +# invalid default value +config FOO + prompt "hello" + default bar diff --git a/tools/cfzy/cfzy-test/invalid-configs/node-not-closed.cfzy b/tools/cfzy/cfzy-test/invalid-configs/node-not-closed.cfzy new file mode 100644 index 0000000..53c5d4c --- /dev/null +++ b/tools/cfzy/cfzy-test/invalid-configs/node-not-closed.cfzy @@ -0,0 +1,6 @@ +# menuconfig node not closed +menuconfig FOO + +config BAR + +# missing endmenuconfig here diff --git a/tools/cfzy/cfzy-test/main.c b/tools/cfzy/cfzy-test/main.c new file mode 100644 index 0000000..9a49c27 --- /dev/null +++ b/tools/cfzy/cfzy-test/main.c @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2013, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 "test_expr.h" +#include "test_conftree.h" +#include "test_dotconfig.h" + +int main(int argc, char **argv) +{ + int ret = 0; + + (void)argc; + + /* XXX a cfzy_init() would be better */ + cfzy_log_init(); + + printf("=== test expression parser ===\n"); + if (test_expr() < 0) { + printf("failed\n"); + ret = 1; + } + else + printf("success\n"); + + printf("=== test conf tree ===\n"); + if (test_conftree(argv[0]) < 0) { + printf("failed\n"); + ret = 1; + } + else + printf("success\n"); + + printf("=== test .config ===\n"); + if (test_dotconfig(argv[0]) < 0) { + printf("failed\n"); + ret = 1; + } + else + printf("success\n"); + + cfzy_log_exit(); + + if (ret == 0) + printf("\n=== SUCCES ===\n"); + else + printf("\n=== FAILED ===\n"); + + return ret; +} diff --git a/tools/cfzy/cfzy-test/test-configs/conftree.cfzy b/tools/cfzy/cfzy-test/test-configs/conftree.cfzy new file mode 100644 index 0000000..3f65270 --- /dev/null +++ b/tools/cfzy/cfzy-test/test-configs/conftree.cfzy @@ -0,0 +1,98 @@ +# +# comments are prefixed by # +# + +# a menu node is a node that has no value but contains several +# other nodes +menu MENU1 + prompt "Menu 1" + # comments can also be added inside a node + +# a config node is the most basic node, storing a boolean value +config MENU1_CONFIG1 + prompt "A config example" + default y + +# a comment node is a node that has no value but it will display +# a prompt in the GUI +comment "Hello" + +# a node can have several default values: each expression is evaluated +# in the same order until one matches. If no expression matches, the +# default value of the node is used +config MENU1_CONFIG2 + prompt "Another config example" + default y if !MENU1_CONFIG1 + default n + +# environment variables can be used anywhere in a conftree file: they +# are evaluated and replaced by their value before parsing the file. +config MENU1_CONFIG3 + prompt "A config example" + ---help--- + The content of the PATH variable is $(PATH) + +# a choice node cntains several choiceconfig nodes that are exclusive +# each other +choice MENU1_CHOICE + prompt "This is menu 1 choice 1" + default MENU1_CHOICE2 if MENU1_CONFIG1 + default MENU1_CHOICE3 + ---help--- + help of menu1_choice: this is a choice between + several values. + +choiceconfig MENU1_CHOICE1 + prompt "choice 1" + +choiceconfig MENU1_CHOICE2 + prompt "choice 2" + +choiceconfig MENU1_CHOICE3 + prompt "choice 3" + +endchoice # this closes the "choice" node + +# an intconfig node stores an integer value +intconfig INT_EXAMPLE + prompt "integer example" + default 12000000 + ---help--- + This is the help of the integer node + +# a strconfig stores a string +strconfig STR_EXAMPLE + prompt "A strconfig example" + default "my default value" + +endmenu + +# A menuconfig node is a menu node that can be enabled or disabled. +# The children nodes are available only if the node is enabled. +menuconfig MENUCONFIG1 + prompt "My menuconfig node" + # If a line is too long, it can be splitted with a backslash + default y if !MENU1_CONFIG1 && \ + MENU1_CHOICE1 + ---help--- + Help for the menuconfig node + +# the "requires" attribute sets a list of expressions that must be +# evaluated to True to enable the node. +config MENUCONFIG1_CONFIG1 + prompt "again, a config" + requires MENU1_CONFIG1 + requires !MENU1_CHOICE3 + +if MENU1_CHOICE1 || MENU1_CHOICE2 + +config MENUCONFIG1_CONFIG2 + prompt "still another config" + +# source another sub conftree file: the path can be relative to this file +# or absolute. The "./" is not mandatory here. +source "./subconftree.cfzy" + +endif + +endmenuconfig diff --git a/tools/cfzy/cfzy-test/test-configs/dotconfig b/tools/cfzy/cfzy-test/test-configs/dotconfig new file mode 100644 index 0000000..190e54b --- /dev/null +++ b/tools/cfzy/cfzy-test/test-configs/dotconfig @@ -0,0 +1,25 @@ +# +# -- Menu 1 +# +CONFIG_MENU1_CONFIG1=y + +# unknown node name (should be successful anyway) +CONFIG_MENU1_CONFIG1XAZXZXZ=y + +# Hello +# CONFIG_MENU1_CONFIG2 is not set +# CONFIG_MENU1_CONFIG3 is not set +CONFIG_MENU1_CHOICE="MENU1_CHOICE2" +# CONFIG_MENU1_CHOICE1 is not set +CONFIG_MENU1_CHOICE2=y +# CONFIG_MENU1_CHOICE3 is not set +CONFIG_STR_EXAMPLE="my default value" +# +# -- My menuconfig node +# +# CONFIG_MENUCONFIG1 is not set + +# same option several times (should be ok too) +CONFIG_INT_EXAMPLE=1234 +CONFIG_INT_EXAMPLE=1234 +CONFIG_INT_EXAMPLE=1234 diff --git a/tools/cfzy/cfzy-test/test-configs/subconftree.cfzy b/tools/cfzy/cfzy-test/test-configs/subconftree.cfzy new file mode 100644 index 0000000..2cc99de --- /dev/null +++ b/tools/cfzy/cfzy-test/test-configs/subconftree.cfzy @@ -0,0 +1,2 @@ +config FOO + prompt "yet another boolean config" diff --git a/tools/cfzy/cfzy-test/test_conftree.c b/tools/cfzy/cfzy-test/test_conftree.c new file mode 100644 index 0000000..3bf3d27 --- /dev/null +++ b/tools/cfzy/cfzy-test/test_conftree.c @@ -0,0 +1,454 @@ +/* + * Copyright (c) 2013, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 +#include +#include +#include + +#include "test_conftree.h" + +#define LOG(level, fmt, args...) \ + CFZY_LOG("test_conftree", level, fmt, ##args) + +#define SUCCESS 0 +#define CANNOT_PARSE_CONFTREE -1 +#define CANNOT_DUMP_CONFTREE -2 +#define CANNOT_FIND_SYMBOL -3 +#define CANNOT_EVAL_CONFTREE -4 +#define CANNOT_GET_VALUE -5 +#define CANNOT_SET_VALUE -6 +#define INVALID_VALUE -7 +#define CANNOT_FILTER_CONFTREE -8 +#define CANNOT_SAVE_CONFTREE_VALUES -9 + +/* check that a node is set to the expected value */ +static int test_one_confnode(const struct cfzy_conftree *conftree, + const char *name, int expected_value) +{ + int val; + + val = cfzy_confnode_get_boolvalue(name, conftree); + if (val < 0) { + LOG(DEBUG, "cannot get node value\n"); + return CANNOT_GET_VALUE; + } + if (val != expected_value) { + LOG(DEBUG, "invalid val for %s: val=%d, expect=%d\n", + name, val, expected_value); + return INVALID_VALUE; + } + + return SUCCESS; +} + +static int test_one_conftree(const char *name) +{ + struct cfzy_list_head *node_list = NULL; + struct cfzy_list_elt *e; + struct cfzy_conftree *conftree; + struct cfzy_confnode *n; + int ret; + + conftree = cfzy_conftree_new(name); + if (conftree == NULL) + return CANNOT_PARSE_CONFTREE; + + if (cfzy_conftree_evaluate(conftree) < 0) { + LOG(DEBUG, "cannot eval conftree\n"); + cfzy_conftree_free(conftree); + return CANNOT_EVAL_CONFTREE; + } + + if (cfzy_conftree_dump(conftree, "/tmp/conftree.dump") < 0) { + cfzy_conftree_free(conftree); + return CANNOT_DUMP_CONFTREE; + } + + /* node value should be 1 (default) */ + ret = test_one_confnode(conftree, "MENU1_CONFIG1", 1); + if (ret < 0) { + cfzy_conftree_free(conftree); + return ret; + } + + /* node value should be 0 (default) */ + ret = test_one_confnode(conftree, "MENU1_CONFIG2", 0); + if (ret < 0) { + cfzy_conftree_free(conftree); + return ret; + } + + /* lookup for this node */ + n = cfzy_htable_lookup(conftree->nodes_htable, "MENU1_CONFIG1"); + if (n == NULL) { + LOG(DEBUG, "cannot find node\n"); + cfzy_conftree_free(conftree); + return CANNOT_FIND_SYMBOL; + } + + /* try to assign an invalid value: should fail */ + if (cfzy_confnode_set_uservalue(n, "dummy") == 0) { + LOG(DEBUG, "set_uservalue returned 0 but should not\n"); + cfzy_conftree_free(conftree); + return CANNOT_SET_VALUE; + } + + /* set thiis node to NO */ + if (cfzy_confnode_set_uservalue(n, "n") < 0) { + LOG(DEBUG, "cannot set node value\n"); + cfzy_conftree_free(conftree); + return CANNOT_SET_VALUE; + } + + /* node value should be 1 because we did not evaluate the tree */ + ret = test_one_confnode(conftree, "MENU1_CONFIG1", 1); + if (ret < 0) { + cfzy_conftree_free(conftree); + return ret; + } + + if (cfzy_conftree_evaluate(conftree) < 0) { + LOG(DEBUG, "cannot eval conftree\n"); + cfzy_conftree_free(conftree); + return CANNOT_EVAL_CONFTREE; + } + + if (cfzy_conftree_dump(conftree, "/tmp/conftree2.dump") < 0) { + cfzy_conftree_free(conftree); + return CANNOT_DUMP_CONFTREE; + } + + if (cfzy_conftree_save_values(conftree) < 0) { + cfzy_conftree_free(conftree); + return CANNOT_SAVE_CONFTREE_VALUES; + } + + if (cfzy_conftree_dump(conftree, "/tmp/conftree3.dump") < 0) { + cfzy_conftree_free(conftree); + return CANNOT_DUMP_CONFTREE; + } + + /* node value should now be 0 */ + ret = test_one_confnode(conftree, "MENU1_CONFIG1", 0); + if (ret < 0) { + cfzy_conftree_free(conftree); + return ret; + } + + /* node value should now be 1 (depends on !MENU1_CONFIG1) */ + ret = test_one_confnode(conftree, "MENU1_CONFIG2", 1); + if (ret < 0) { + cfzy_conftree_free(conftree); + return ret; + } + + /* test "choice" nodes */ + + /* node value should be 0 (default) */ + ret = test_one_confnode(conftree, "MENU1_CHOICE1", 0); + if (ret < 0) { + cfzy_conftree_free(conftree); + return ret; + } + + /* node value should be 0 (default) */ + ret = test_one_confnode(conftree, "MENU1_CHOICE2", 0); + if (ret < 0) { + cfzy_conftree_free(conftree); + return ret; + } + + /* node value should be 1 (default) */ + ret = test_one_confnode(conftree, "MENU1_CHOICE3", 1); + if (ret < 0) { + cfzy_conftree_free(conftree); + return ret; + } + + /* lookup for this node */ + n = cfzy_htable_lookup(conftree->nodes_htable, "MENU1_CHOICE1"); + if (n == NULL) { + LOG(DEBUG, "cannot find node\n"); + cfzy_conftree_free(conftree); + return CANNOT_FIND_SYMBOL; + } + + /* set this node to Y */ + if (cfzy_confnode_set_uservalue(n, "y") < 0) { + LOG(DEBUG, "cannot set node value\n"); + cfzy_conftree_free(conftree); + return CANNOT_SET_VALUE; + } + + if (cfzy_conftree_evaluate(conftree) < 0) { + LOG(DEBUG, "cannot eval conftree\n"); + cfzy_conftree_free(conftree); + return CANNOT_EVAL_CONFTREE; + } + + if (cfzy_conftree_dump(conftree, "/tmp/conftree.dump") < 0) { + cfzy_conftree_free(conftree); + return CANNOT_DUMP_CONFTREE; + } + + /* node value should be 1 */ + ret = test_one_confnode(conftree, "MENU1_CHOICE1", 1); + if (ret < 0) { + cfzy_conftree_free(conftree); + return ret; + } + + /* node value should be 0 */ + ret = test_one_confnode(conftree, "MENU1_CHOICE2", 0); + if (ret < 0) { + cfzy_conftree_free(conftree); + return ret; + } + + /* node value should be 0 */ + ret = test_one_confnode(conftree, "MENU1_CHOICE3", 0); + if (ret < 0) { + cfzy_conftree_free(conftree); + return ret; + } + + /* check the "menuconfig" node value, should be 1 (default) */ + ret = test_one_confnode(conftree, "MENUCONFIG1", 1); + if (ret < 0) { + cfzy_conftree_free(conftree); + return ret; + } + + /* check which node were changed by the user */ + node_list = cfzy_conftree_filter(conftree, CFZY_FILTER_F_USR_CHANGED); + if (node_list == NULL) { + cfzy_conftree_free(conftree); + return CANNOT_FILTER_CONFTREE; + } + TAILQ_FOREACH(e, node_list, next) { + n = e->ptr; + printf("node %s\n", cfzy_confnode_name(n)); + } + cfzy_list_free(node_list, NULL); + + /* check which effective value changed */ + node_list = cfzy_conftree_filter(conftree, CFZY_FILTER_F_EFF_CHANGED); + if (node_list == NULL) { + cfzy_conftree_free(conftree); + return CANNOT_FILTER_CONFTREE; + } + TAILQ_FOREACH(e, node_list, next) { + n = e->ptr; + printf("node2 %s\n", cfzy_confnode_name(n)); + } + cfzy_list_free(node_list, NULL); + + cfzy_conftree_free(conftree); + return 0; +} + +static int test_one_invalid_conftree(const char *name) +{ + struct cfzy_conftree *conftree; + + conftree = cfzy_conftree_new(name); + if (conftree == NULL) + return CANNOT_PARSE_CONFTREE; + + cfzy_conftree_free(conftree); + return 0; +} + +static void print_error(const char *in, int err) +{ + printf("Unexpected return value when parsing: <%s>\n", in); + switch (err) { + case SUCCESS: + printf("Test returned success, but should not\n"); + break; + case CANNOT_PARSE_CONFTREE: + printf("Cannot parse configuration tree\n"); + break; + case CANNOT_DUMP_CONFTREE: + printf("Cannot dump configuration tree\n"); + break; + case CANNOT_EVAL_CONFTREE: + printf("Cannot evalulate configuration tree\n"); + break; + case CANNOT_FIND_SYMBOL: + printf("Cannot find symbol\n"); + break; + case CANNOT_GET_VALUE: + printf("Cannot get node value\n"); + break; + case CANNOT_SET_VALUE: + printf("Cannot set node value\n"); + break; + case INVALID_VALUE: + printf("invalid value\n"); + break; + case CANNOT_FILTER_CONFTREE: + printf("Cannot filter configuration tree\n"); + break; + case CANNOT_SAVE_CONFTREE_VALUES: + printf("Cannot save configuration tree values\n"); + break; + default: + printf("Invalid error %d\n", err); + break; + } +} + +int test_conftree(const char *progname) +{ + char filename[PATH_MAX]; + char *progdir; + int err; + + cfzy_log_set_default_level(CFZY_LOG_DEBUG); + + /* progdir is the directory where confizery-test is located */ + progdir = strdup(progname); + dirname(progdir); + + /* unexistant file */ + snprintf(filename, sizeof(filename), "foobar"); + err = test_one_invalid_conftree(filename); + if (err != CANNOT_PARSE_CONFTREE) { + print_error(filename, err); + goto fail; + } + + /* invalid command */ + snprintf(filename, sizeof(filename), + "%s/../../src/confizery-test/invalid-configs/" + "invalid-command.cfzy", progdir); + err = test_one_invalid_conftree(filename); + if (err != CANNOT_PARSE_CONFTREE) { + print_error(filename, err); + goto fail; + } + + /* invalid attribute */ + snprintf(filename, sizeof(filename), + "%s/../../src/confizery-test/invalid-configs/" + "invalid-attribute.cfzy", progdir); + err = test_one_invalid_conftree(filename); + if (err != CANNOT_PARSE_CONFTREE) { + print_error(filename, err); + goto fail; + } + + /* circular dependency */ + snprintf(filename, sizeof(filename), + "%s/../../src/confizery-test/invalid-configs/" + "circular-dep.cfzy", progdir); + err = test_one_invalid_conftree(filename); + if (err != CANNOT_PARSE_CONFTREE) { + print_error(filename, err); + goto fail; + } + + /* circular dependency */ + snprintf(filename, sizeof(filename), + "%s/../../src/confizery-test/invalid-configs/" + "circular-dep2.cfzy", progdir); + err = test_one_invalid_conftree(filename); + if (err != CANNOT_PARSE_CONFTREE) { + print_error(filename, err); + goto fail; + } + + /* node not closed */ + snprintf(filename, sizeof(filename), + "%s/../../src/confizery-test/invalid-configs/" + "node-not-closed.cfzy", progdir); + err = test_one_invalid_conftree(filename); + if (err != CANNOT_PARSE_CONFTREE) { + print_error(filename, err); + goto fail; + } + + /* duplicated config name */ + snprintf(filename, sizeof(filename), + "%s/../../src/confizery-test/invalid-configs/" + "dup-name.cfzy", progdir); + err = test_one_invalid_conftree(filename); + if (err != CANNOT_PARSE_CONFTREE) { + print_error(filename, err); + goto fail; + } + + /* invalid default value */ + snprintf(filename, sizeof(filename), + "%s/../../src/confizery-test/invalid-configs/" + "invalid-value.cfzy", progdir); + err = test_one_invalid_conftree(filename); + if (err != CANNOT_PARSE_CONFTREE) { + print_error(filename, err); + goto fail; + } + + /* invalid expression */ + snprintf(filename, sizeof(filename), + "%s/../../src/confizery-test/invalid-configs/" + "invalid-expr.cfzy", progdir); + err = test_one_invalid_conftree(filename); + if (err != CANNOT_PARSE_CONFTREE) { + print_error(filename, err); + goto fail; + } + + /* valid config */ + + /* an example config */ + snprintf(filename, sizeof(filename), + "%s/../../src/confizery-test/test-configs/" + "conftree.cfzy", progdir); + err = test_one_conftree(filename); + if (err != SUCCESS) { + print_error(filename, err); + goto fail; + } + + free(progdir); + return 0; + + fail: + free(progdir); + return -1; +} diff --git a/tools/cfzy/cfzy-test/test_conftree.h b/tools/cfzy/cfzy-test/test_conftree.h new file mode 100644 index 0000000..2445af6 --- /dev/null +++ b/tools/cfzy/cfzy-test/test_conftree.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +#ifndef _TEST_CONFTREE_H_ +#define _TEST_CONFTREE_H_ + +/* test cfzy_conftree module */ +int test_conftree(const char *progname); + +#endif diff --git a/tools/cfzy/cfzy-test/test_dotconfig.c b/tools/cfzy/cfzy-test/test_dotconfig.c new file mode 100644 index 0000000..edf57f6 --- /dev/null +++ b/tools/cfzy/cfzy-test/test_dotconfig.c @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2013, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 +#include +#include +#include + +#include "test_dotconfig.h" + +#define LOG(level, fmt, args...) \ + CFZY_LOG("test_dotconfig", level, fmt, ##args) + +#define SUCCESS 0 +#define CANNOT_PARSE_CONFTREE -1 +#define CANNOT_EVAL_CONFTREE -2 +#define CANNOT_GENERATE_DOTCONFIG -3 +#define CANNOT_GENERATE_C_HDR -4 +#define CANNOT_READ_DOTCONFIG -5 + +static void print_error(const char *in, int err) +{ + printf("Unexpected return value when parsing: <%s>\n", in); + switch (err) { + case SUCCESS: + printf("Test returned success, but should not\n"); + break; + case CANNOT_PARSE_CONFTREE: + printf("Cannot parse configuration tree\n"); + break; + case CANNOT_EVAL_CONFTREE: + printf("Cannot evalulate configuration tree\n"); + break; + case CANNOT_GENERATE_DOTCONFIG: + printf("Cannot generate dotconfig file\n"); + break; + case CANNOT_GENERATE_C_HDR: + printf("Cannot generate C header file\n"); + break; + case CANNOT_READ_DOTCONFIG: + printf("Cannot read dotconfig file\n"); + break; + default: + printf("Invalid error %d\n", err); + break; + } +} + +static int test_one_dotconfig(struct cfzy_conftree *conftree, + const char *filename, int expected) +{ + int ret, err; + + printf("-- Test dotconfig <%s>\n", filename); + + /* XXX we should reset config */ + + ret = cfzy_dotconfig_read(conftree, filename); + if (ret < 0) + err = CANNOT_READ_DOTCONFIG; + else + err = SUCCESS; + + /* this is the expected result, return success */ + if (err == expected) + return 0; + + print_error(filename, err); + return -1; +} + +int test_dotconfig(const char *progname) +{ + struct cfzy_conftree *conftree = NULL; + int ret; + char filename[PATH_MAX]; + char *progdir; + + cfzy_log_set_default_level(CFZY_LOG_DEBUG); + + /* progdir is the directory where confizery-test is located */ + progdir = strdup(progname); + dirname(progdir); + + /* an example config */ + snprintf(filename, sizeof(filename), + "%s/../../src/confizery-test/test-configs/" + "conftree.cfzy", progdir); + + conftree = cfzy_conftree_new(filename); + if (conftree == NULL) { + LOG(DEBUG, "cannot parse configuration tree\n"); + goto fail; + } + + if (cfzy_conftree_evaluate(conftree) < 0) { + LOG(DEBUG, "cannot eval conftree\n"); + goto fail; + } + + /* write the dotconfig file */ + ret = cfzy_dotconfig_write(conftree, "/tmp/dotconfig"); + if (ret < 0) { + LOG(DEBUG, "cannot write dotconfig file\n"); + goto fail; + } + + /* write the autoconf.h file */ + ret = cfzy_c_hdr_write(conftree, "/tmp/autoconf.h"); + if (ret < 0) { + LOG(DEBUG, "cannot write autoconf.h file\n"); + goto fail; + } + + /* write the config/foo/bar.h files */ + ret = cfzy_multi_c_hdr_write(conftree, "/tmp/config"); + if (ret < 0) { + LOG(DEBUG, "cannot write multiple headers file\n"); + goto fail; + } + + /* test a valid config */ + if (test_one_dotconfig(conftree, "/tmp/dotconfig", SUCCESS) < 0) + goto fail; + + snprintf(filename, sizeof(filename), + "%s/../../src/confizery-test/test-configs/" + "dotconfig", progdir); + if (test_one_dotconfig(conftree, filename, SUCCESS) < 0) + goto fail; + + /* invalid configs */ + + snprintf(filename, sizeof(filename), + "%s/../../src/confizery-test/invalid-configs/" + "dotconfig-bad-val", progdir); + if (test_one_dotconfig(conftree, filename, CANNOT_READ_DOTCONFIG) < 0) + goto fail; + + snprintf(filename, sizeof(filename), + "%s/../../src/confizery-test/invalid-configs/" + "dotconfig-bad-val2", progdir); + if (test_one_dotconfig(conftree, filename, CANNOT_READ_DOTCONFIG) < 0) + goto fail; + + snprintf(filename, sizeof(filename), + "%s/../../src/confizery-test/invalid-configs/" + "dotconfig-bad-val3", progdir); + if (test_one_dotconfig(conftree, filename, CANNOT_READ_DOTCONFIG) < 0) + goto fail; + + cfzy_conftree_free(conftree); + free(progdir); + return 0; + + fail: + if (conftree != NULL) + cfzy_conftree_free(conftree); + free(progdir); + return -1; +} diff --git a/tools/cfzy/cfzy-test/test_dotconfig.h b/tools/cfzy/cfzy-test/test_dotconfig.h new file mode 100644 index 0000000..f2f0df9 --- /dev/null +++ b/tools/cfzy/cfzy-test/test_dotconfig.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +#ifndef _TEST_DOTCONFIG_H_ +#define _TEST_DOTCONFIG_H_ + +/* test cfzy_dotconfig module */ +int test_dotconfig(const char *progname); + +#endif diff --git a/tools/cfzy/cfzy-test/test_expr.c b/tools/cfzy/cfzy-test/test_expr.c new file mode 100644 index 0000000..ec0af55 --- /dev/null +++ b/tools/cfzy/cfzy-test/test_expr.c @@ -0,0 +1,379 @@ +/* + * Copyright (c) 2013, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 "test_expr.h" + +#define SUCCESS 0 +#define CANNOT_PARSE -1 +#define CANNOT_CONVERT_TO_STR -2 +#define IN_AND_OUT_DIFFERS -3 +#define CANNOT_EVAL -4 +#define CANNOT_GET_VARS -5 + +/* all variables starting with "A" are evaluated to 1, the other are + * evaluated to 0. The variable names "unknown" returns an error + * (variable not found). */ +static int getvalue(const char *varname, void *arg) +{ + (void)arg; + + if (!strcmp(varname, "unknown")) + return -1; + + if (varname[0] == 'A') + return 1; + return 0; +} + +static int test_one_expr(const char *in) +{ + char out[256]; + struct cfzy_expr *exp; + int ret; + + /* valid expression */ + exp = cfzy_expr_parse(in); + if (exp == NULL) + return CANNOT_PARSE; + + cfzy_expr_graph_dump("test.graph", exp); + ret = cfzy_expr_to_str(exp, out, sizeof(out)); + if (ret < 0) { + cfzy_expr_free(exp); + return CANNOT_CONVERT_TO_STR; + } + + if (strncmp(in, out, sizeof(out))) { + cfzy_expr_free(exp); + return IN_AND_OUT_DIFFERS; + } + + ret = cfzy_expr_eval(exp, getvalue, NULL); + if (ret < 0) { + cfzy_expr_free(exp); + return CANNOT_EVAL; + } + + cfzy_expr_free(exp); + return ret; +} + +static void print_error(const char *in, int err) +{ + printf("Unexpected return value when parsing: <%s>\n", in); + switch(err) { + case 0: + case 1: + printf("Test returned %d, but should not\n", err); + break; + case CANNOT_PARSE: + printf("Cannot parse expression\n"); + break; + case CANNOT_CONVERT_TO_STR: + printf("Cannot convert expression to string\n"); + break; + case IN_AND_OUT_DIFFERS: + printf("Input and output expressions differ\n"); + break; + case CANNOT_EVAL: + printf("Cannot evaluate expression\n"); + break; + case CANNOT_GET_VARS: + printf("Cannot get variable list\n"); + break; + default: + printf("Invalid error %d\n", err); + break; + } +} + +/* must be called with an expression containing only A and B as vars */ +static int test_vars(const char *in) +{ + struct cfzy_expr *exp; + struct cfzy_list_elt *e; + struct cfzy_list_head *list; + int ret = 0, count = 0; + + /* valid expression */ + exp = cfzy_expr_parse(in); + if (exp == NULL) + return CANNOT_PARSE; + + list = cfzy_expr_get_vars(exp); + if (list == NULL) { + cfzy_expr_free(exp); + return CANNOT_GET_VARS; + } + + /* list must contain 2 elements: A and B */ + TAILQ_FOREACH(e, list, next) { + count++; + if (!strcmp(e->ptr, "A")) + continue; + if (!strcmp(e->ptr, "B")) + continue; + ret = CANNOT_GET_VARS; + } + if (count != 2) + ret = CANNOT_GET_VARS; + + cfzy_list_free(list, free); + cfzy_expr_free(exp); + return ret; +} + +int test_expr(void) +{ + int ret = 0, err; + const char *in; + + in = "true && true"; + err = test_one_expr(in); + if (err != 1) { + print_error(in, err); + ret = -1; + } + + in = "(true || false) && false"; + err = test_one_expr(in); + if (err != 0) { + print_error(in, err); + ret = -1; + } + + in = "true || (false && false)"; + err = test_one_expr(in); + if (err != 1) { + print_error(in, err); + ret = -1; + } + + /* && has priority */ + in = "true || false && false"; + err = test_one_expr(in); + if (err != 1) { + print_error(in, err); + ret = -1; + } + + /* && has priority */ + in = "false || true && false && true || true && false"; + err = test_one_expr(in); + if (err != 0) { + print_error(in, err); + ret = -1; + } + + /* '==' has priority */ + in = "false == true && false"; + err = test_one_expr(in); + if (err != 0) { + print_error(in, err); + ret = -1; + } + + in = "A"; + err = test_one_expr(in); + if (err != 1) { + print_error(in, err); + ret = -1; + } + + in = "B"; + err = test_one_expr(in); + if (err != 0) { + print_error(in, err); + ret = -1; + } + + in = "true && (true)"; + err = test_one_expr(in); + if (err != 1) { + print_error(in, err); + ret = -1; + } + + in = "(!(A1 && !(B1 || A2 && B2 || A_aa)) || " + "(x && !(D && !A) && !(D || E))) == true"; + err = test_one_expr(in); + if (err != 1) { + print_error(in, err); + ret = -1; + } + + /* invalid expression */ + in = " "; + err = test_one_expr(in); + if (err != CANNOT_PARSE) { + print_error(in, err); + ret = -1; + } + + /* invalid expression */ + in = "("; + err = test_one_expr(in); + if (err != CANNOT_PARSE) { + print_error(in, err); + ret = -1; + } + + /* invalid expression */ + in = ")"; + err = test_one_expr(in); + if (err != CANNOT_PARSE) { + print_error(in, err); + ret = -1; + } + + /* invalid expression */ + in = "( )"; + err = test_one_expr(in); + if (err != CANNOT_PARSE) { + print_error(in, err); + ret = -1; + } + + /* invalid expression */ + in = "A &&"; + err = test_one_expr(in); + if (err != CANNOT_PARSE) { + print_error(in, err); + ret = -1; + } + /* invalid expression */ + in = "A && && B"; + err = test_one_expr(in); + if (err != CANNOT_PARSE) { + print_error(in, err); + ret = -1; + } + + /* invalid expression */ + in = "A B"; + err = test_one_expr(in); + if (err != CANNOT_PARSE) { + print_error(in, err); + ret = -1; + } + + /* invalid expression */ + in = "A && B || C D"; + err = test_one_expr(in); + if (err != CANNOT_PARSE) { + print_error(in, err); + ret = -1; + } + + /* invalid expression */ + in = "A (B)"; + err = test_one_expr(in); + if (err != CANNOT_PARSE) { + print_error(in, err); + ret = -1; + } + + /* invalid expression */ + in = "&& A B"; + err = test_one_expr(in); + if (err != CANNOT_PARSE) { + print_error(in, err); + ret = -1; + } + + /* invalid expression */ + in = "! A B"; + err = test_one_expr(in); + if (err != CANNOT_PARSE) { + print_error(in, err); + ret = -1; + } + + /* invalid expression */ + in = "A && !|| D"; + err = test_one_expr(in); + if (err != CANNOT_PARSE) { + print_error(in, err); + ret = -1; + } + + /* invalid expression */ + in = "A && !"; + err = test_one_expr(in); + if (err != CANNOT_PARSE) { + print_error(in, err); + ret = -1; + } + + /* invalid expression, variable must start with a letter */ + in = "A && _B"; + err = test_one_expr(in); + if (err != CANNOT_PARSE) { + print_error(in, err); + ret = -1; + } + + /* invalid expression, variable must start with a letter and + * numbers are not allowed (at least today) */ + in = "A == 4"; + err = test_one_expr(in); + if (err != CANNOT_PARSE) { + print_error(in, err); + ret = -1; + } + + /* valid expression, but unknown variable */ + in = "unknown"; + err = test_one_expr(in); + if (err != CANNOT_EVAL) { + print_error(in, err); + ret = -1; + } + + /* valid expression, but won't display like this because + * spaces are stripped */ + in = " true||(false && \n false)"; + err = test_one_expr(in); + if (err != IN_AND_OUT_DIFFERS) { + print_error(in, err); + ret = -1; + } + + err = test_vars("A && B || true && A == !B"); + if (err != 0) { + print_error(in, err); + ret = -1; + } + + return ret; +} diff --git a/tools/cfzy/cfzy-test/test_expr.h b/tools/cfzy/cfzy-test/test_expr.h new file mode 100644 index 0000000..cfce881 --- /dev/null +++ b/tools/cfzy/cfzy-test/test_expr.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +#ifndef _TEST_EXPR_H_ +#define _TEST_EXPR_H_ + +/* test cfzy_expr module */ +int test_expr(void); + +#endif diff --git a/tools/cfzy/libconfizery/Makefile b/tools/cfzy/libconfizery/Makefile new file mode 100644 index 0000000..9343477 --- /dev/null +++ b/tools/cfzy/libconfizery/Makefile @@ -0,0 +1,29 @@ +include $(TOPDIR)/confizery.vars.mk + +LIB = libconfizery + +#INSTALL_HEADERS = cmdline.h + +SRCS = cfzy_expr.c +SRCS += cfzy_list.c +SRCS += cfzy_htable.c +SRCS += cfzy_string.c +SRCS += cfzy_log.c +SRCS += cfzy_confnode.c +SRCS += cfzy_conftree.c +SRCS += cfzy_dotconfig.c +SRCS += cfzy_c_hdr.c +SRCS += cfzy_file.c +SRCS += cfzy_conftree_parser.c +SRCS += cfzy_confnode_choice.c +SRCS += cfzy_confnode_choiceconfig.c +SRCS += cfzy_confnode_comment.c +SRCS += cfzy_confnode_config.c +SRCS += cfzy_confnode_if.c +SRCS += cfzy_confnode_intconfig.c +SRCS += cfzy_confnode_menu.c +SRCS += cfzy_confnode_menuconfig.c +SRCS += cfzy_confnode_root.c +SRCS += cfzy_confnode_strconfig.c + +include $(TOPDIR)/confizery.lib.mk diff --git a/tools/cfzy/libconfizery/cfzy_c_hdr.c b/tools/cfzy/libconfizery/cfzy_c_hdr.c new file mode 100644 index 0000000..bb75900 --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_c_hdr.c @@ -0,0 +1,342 @@ +/* + * Copyright (c) 2013, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 +#include + +#include "cfzy_log.h" +#include "cfzy_file.h" +#include "cfzy_conftree.h" +#include "cfzy_confnode.h" + +#define LOG(level, fmt, args...) \ + CFZY_LOG("c_hdr", level, fmt, ##args) + +/* does not clean directories on error, if path exist, assume it's a + * directory and ignore error */ +int cfzy_recursive_mkdir(const char *path) +{ + char tmp[PATH_MAX]; + char *p = NULL; + int n, ret; + + n = snprintf(tmp, sizeof(tmp),"%s", path); + if (n <= 0 || n >= PATH_MAX) + return -1; + + /* ignore all trailing '/' */ + while (n > 0 && tmp[--n] == '/') + tmp[n] = '\0'; + + /* create all intermediate directories */ + for (p = tmp + 1; *p; p++) { + if (*p != '/') + continue; + + *p = '\0'; + if (mkdir(tmp, S_IRWXU) < 0 && errno != EEXIST) + return -1; + + *p = '/'; + } + + /* and last one */ + ret = mkdir(tmp, S_IRWXU); + if (ret < 0 && errno == EEXIST) + return 0; + + return ret; +} + +/* + * Get the file name from a config name. Replace first underscore + * only: + * CONFIG_A_B -> config/a/b.h + * CONFIG_A__B___C -> config/a/_b/__c.h + */ +static char *configname_to_filename(const char *name) +{ + char *filename, *s; + int len; + + /* allocate room for dst buffer */ + len = strlen(name); + filename = malloc(len + 3); + if (filename == NULL) + return NULL; + strncpy(filename, name, len + 1); + + for (s = filename; *s != '\0'; s++) { + /* '/' is not allowed in variable name */ + if (*s == '/') { + free(filename); + return NULL; + } + + /* convert the first '_' in '/' */ + if (*s == '_') { + *s = '/'; + while (s[1] == '/') + s++; + continue; + } + + if (isalpha(*s)) + *s = tolower(*s); + } + *s++ = '.'; + *s++ = 'h'; + *s = '\0'; + + return filename; +} + +static int __cfzy_multi_c_hdr_write(const struct cfzy_confnode *n) +{ + const struct cfzy_confnode *c; + FILE *f = NULL; + char *filename = NULL; + char *dname_buf = NULL, *dname_ptr = NULL; + char oldval[512]; + char newval[512]; + int ret, val; + + /* only dump the node if it has a name and a value */ + if (n->name != NULL && n->effective_value != NULL) { + /* get the name of the file (full path) */ + filename = configname_to_filename(n->name); + if (filename == NULL) + goto fail; + + /* get directory name from file name */ + dname_buf = strdup(filename); + if (dname_buf == NULL) + goto fail; + dname_ptr = dirname(dname_buf); + + /* create dirs */ + if (cfzy_recursive_mkdir(dname_ptr) < 0) + goto fail; + + /* get old value */ + memset(oldval, 0, sizeof(oldval)); + f = fopen(filename, "r"); + if (f != NULL) { + ret = fread(oldval, 1, sizeof(oldval) - 1, f); + if (ret < 0) { + LOG(ERR, "cannot read <%s>\n", filename); + goto fail; + } + fclose(f); + f = NULL; + } + + /* if config node is a bool, we have NULL and n will + * output the same val (is not st) */ + if (n->flags & CFZY_F_VAL_IS_BOOL) { + val = cfzy_confnode_str2bool(n, n->effective_value); + if (val < 0) + return -1; + + /* bool is false -> not set */ + if (val == 0) { + if (snprintf(newval, sizeof(newval), + "/* %s is not set */", + cfzy_confnode_name(n)) < 0) + goto fail; + } + else { + /* bool is true */ + if (snprintf(newval, sizeof(newval), + "/* %s = %s */", cfzy_confnode_name(n), + n->effective_value) < 0) + goto fail; + } + } + else { + if (snprintf(newval, sizeof(newval), + "/* %s = %s */", cfzy_confnode_name(n), + n->effective_value) < 0) + goto fail; + } + + /* if the value is different, write the new value */ + if (strncmp(newval, oldval, sizeof(newval)) != 0) { + f = fopen(filename, "w"); + if (f == NULL) { + LOG(ERR, "cannot open file <%s>\n", filename); + goto fail; + } + + if (fprintf(f, "%s", newval) < 0) { + LOG(ERR, "cannot write in <%s>\n", + filename); + goto fail; + } + fclose(f); + f = NULL; + } + + free(dname_buf); + free(filename); + } + + /* dump all the children of root node */ + TAILQ_FOREACH(c, &n->children, child_next) { + if (__cfzy_multi_c_hdr_write(c) < 0) + break; + } + + return 0; + + fail: + if (f != NULL) + fclose(f); + if (dname_buf != NULL) + free(dname_buf); + if (filename != NULL) + free(filename); + + return -1; +} + +/* write config/foo/bar.h */ +int cfzy_multi_c_hdr_write(const struct cfzy_conftree *conftree, + const char *basedir) +{ + char old_cwd_buf[PATH_MAX]; + char *old_cwd = NULL; + const struct cfzy_confnode *c; + int ret = 0; + + LOG(INFO, "multi C headers write <%s>\n", basedir); + + if (cfzy_recursive_mkdir(basedir) < 0) + return -1; + + old_cwd = getcwd(old_cwd_buf, sizeof(old_cwd_buf)); + if (old_cwd == NULL) { + LOG(ERR, "getcwd failed: %s\n", strerror(errno)); + return -1; + } + + if (chdir(basedir) < 0) { + LOG(ERR, "chdir failed: %s\n", strerror(errno)); + return -1; + } + + /* dump all the children of root node */ + TAILQ_FOREACH(c, &conftree->root->children, child_next) { + ret = __cfzy_multi_c_hdr_write(c); + if (ret < 0) + break; + } + + if (chdir(old_cwd) < 0) + LOG(ERR, "chdir back to <%s> failed: %s\n", + old_cwd, strerror(errno)); + + return ret; +} + +/* write config/autoconf.h */ +int cfzy_c_hdr_write(const struct cfzy_conftree *conftree, + const char *filename) +{ + FILE *tmp_f, *f; + const struct cfzy_confnode *c; + int ret = 0; + + LOG(INFO, "open old C header <%s>\n", filename); + + /* open old file in read mode */ + + f = fopen(filename, "r"); + if (f == NULL && errno != ENOENT) { + printf("cannot open file <%s>: %s\n", + filename, strerror(errno)); + return -1; + } + + /* old file exists */ + if (f != NULL) { + + /* write C header in temp file */ + tmp_f = tmpfile(); + if (tmp_f == NULL) { + printf("cannot open temp file\n"); + return -1; + } + + /* dump all the children of root node */ + TAILQ_FOREACH(c, &conftree->root->children, child_next) { + ret = cfzy_confnode_c_hdr_write(c, tmp_f); + if (ret < 0) + break; + } + + /* files are the same, nothing to do */ + if (cfzy_file_compare(f, tmp_f) == 0) { + LOG(INFO, "already up to date: <%s>\n", filename); + fclose(tmp_f); + fclose(f); + return 0; + } + + fclose(tmp_f); + fclose(f); + } + + /* reopen the file in write mode */ + + LOG(INFO, "write new C header <%s>\n", filename); + + f = fopen(filename, "w"); + if (f == NULL) { + printf("cannot open file <%s>\n", filename); + return -1; + } + + /* dump all the children of root node */ + TAILQ_FOREACH(c, &conftree->root->children, child_next) { + ret = cfzy_confnode_c_hdr_write(c, f); + if (ret < 0) + break; + } + + fclose(f); + + return ret; +} diff --git a/tools/cfzy/libconfizery/cfzy_c_hdr.h b/tools/cfzy/libconfizery/cfzy_c_hdr.h new file mode 100644 index 0000000..841108d --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_c_hdr.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2013, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +/** + * @file + * Confizery C header generation + * + * This module provides the public API to generate the C header files + * from the configuration tree. + */ + +#ifndef _CFZY_C_HDR_H_ +#define _CFZY_C_HDR_H_ + +/** + * Write configuration in a C header file + * + * Write a C header (autoconf.h like) in a file. This single file will + * contain a list of #define (one per option) and is desgined to be + * included by a C application using this config. + * + * If the file already exists and has the same content, nothing is + * written in it. + * + * @param conftree + * the configuration tree + * @param filename + * name of the output file + * @return + * 0 on success, negative on error + */ +int cfzy_c_hdr_write(const struct cfzy_conftree *conftree, + const char *filename); + +/** + * Write the configuration in multiple header files + * + * Write a C header (autoconf.h like) in a file. Each option is + * written in one single file. If one file already exists and has the + * same content, nothing is written in it. + * + * These files are not desgined to be + * included by a C application as each option is just included as a C + * comment. The goal of these files is to handle the dependencies + * properly in the build system. + * + * @param conftree + * the configuration tree + * @param filename + * name of the output file + * @return + * 0 on success, negative on error + */ +int cfzy_multi_c_hdr_write(const struct cfzy_conftree *conftree, + const char *basedir); + +#endif diff --git a/tools/cfzy/libconfizery/cfzy_confnode.c b/tools/cfzy/libconfizery/cfzy_confnode.c new file mode 100644 index 0000000..fc6e6c9 --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_confnode.c @@ -0,0 +1,859 @@ +/* + * Copyright (c) 2013, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 "cfzy_log.h" +#include "cfzy_list.h" +#include "cfzy_htable.h" +#include "cfzy_string.h" +#include "cfzy_expr.h" +#include "cfzy_confnode.h" +#include "cfzy_conftree.h" + +#define LOG(level, fmt, args...) \ + CFZY_LOG("confnode", level, fmt, ##args) + +/* alloc a new confnode */ +struct cfzy_confnode *cfzy_confnode_alloc(struct cfzy_conftree *conftree) +{ + struct cfzy_confnode *n; + + n = malloc(sizeof(struct cfzy_confnode)); + if (n == NULL) + return NULL; + + memset(n, 0, sizeof(*n)); + TAILQ_INIT(&n->children); + + n->conftree = conftree; + + n->expr_deps = cfzy_list_alloc(); + if (n->expr_deps == NULL) { + free(n); + return NULL; + } + + n->default_val_list = cfzy_list_alloc(); + if (n->default_val_list == NULL) { + cfzy_list_free(n->expr_deps, NULL); + free(n); + return NULL; + } + + return n; +} + +/* Free the node given as argument, and all its associated data */ +void cfzy_confnode_free(struct cfzy_confnode *n) +{ + struct cfzy_confnode *c; + struct cfzy_list_elt *e; + struct cfzy_confnode_defvalue *defval; + + /* free the children */ + while ((c = TAILQ_FIRST(&n->children)) != NULL) { + TAILQ_REMOVE(&n->children, c, child_next); + cfzy_confnode_free(c); + } + + /* do the specific free first */ + if (n->ops && n->ops->free) + n->ops->free(n); + + /* free the deps expression list */ + cfzy_list_free(n->expr_deps, (void *)cfzy_expr_free); + + /* free the deps expression list (manually free each element + * instead of providing a function) */ + while ((e = TAILQ_FIRST(n->default_val_list)) != NULL) { + TAILQ_REMOVE(n->default_val_list, e, next); + defval = e->ptr; + cfzy_expr_free(defval->expr); + free(defval->val); + free(defval); + free(e); + } + cfzy_list_free(n->default_val_list, NULL); + + if (n->node_deps != NULL) + cfzy_list_free(n->node_deps, NULL); + + /* free the name, help, prompt, ... */ + if (n->name) + free(n->name); + if (n->prompt) + free(n->prompt); + if (n->help) + free(n->help); + if (n->default_value) + free(n->default_value); + if (n->user_value) + free(n->user_value); + if (n->effective_value) + free(n->effective_value); + if (n->old_user_value) + free(n->old_user_value); + if (n->old_effective_value) + free(n->old_effective_value); + if (n->filename) + free(n->filename); + + free(n); +} + +/* return the list of nodes required to enable this one */ +struct cfzy_list_head * +cfzy_confnode_get_deplist(const struct cfzy_confnode *n) +{ + struct cfzy_list_head *node_list = NULL, *var_list= NULL; + struct cfzy_list_elt *e, *e2; + struct cfzy_expr *expr; + struct cfzy_confnode *c; + const char *varname; + const struct cfzy_confnode_defvalue *defval; + const struct cfzy_conftree *conftree; + + node_list = cfzy_list_alloc(); + if (node_list == NULL) { + LOG(ERR, "cannot allocate node list\n"); + return NULL; + } + + /* get associated configuration tree */ + conftree = n->conftree; + + /* list of dependencies (expression list) */ + TAILQ_FOREACH(e, n->expr_deps, next) { + + expr = e->ptr; + + /* get list of variables for this expression */ + var_list = cfzy_expr_get_vars(expr); + if (var_list == NULL) { + LOG(ERR, "cannot allocate get list\n"); + goto fail; + } + + /* list of variables in this expression */ + TAILQ_FOREACH(e2, var_list, next) { + + varname = e2->ptr; + + c = cfzy_htable_lookup(conftree->nodes_htable, varname); + if (c == NULL) { + LOG(ERR, "cannot find node <%s>\n", varname); + goto fail; + } + + if (cfzy_list_elt_is_in_list(node_list, c)) + continue; + + if (cfzy_list_add_tail(node_list, c) < 0) { + LOG(ERR, "cannot add node in list\n"); + goto fail; + } + } + + cfzy_list_free(var_list, free); + var_list = NULL; + } + + /* list of conditional default values depending on an expression */ + TAILQ_FOREACH(e, n->default_val_list, next) { + + defval = e->ptr; + expr = defval->expr; + + /* get list of variables for this expression */ + var_list = cfzy_expr_get_vars(expr); + if (var_list == NULL) { + LOG(ERR, "cannot allocate get list\n"); + goto fail; + } + + /* list of variables in this expression */ + TAILQ_FOREACH(e2, var_list, next) { + + varname = e2->ptr; + + c = cfzy_htable_lookup(conftree->nodes_htable, varname); + if (c == NULL) { + LOG(ERR, "cannot find node <%s>\n", varname); + goto fail; + } + + if (cfzy_list_elt_is_in_list(node_list, c)) + continue; + + if (cfzy_list_add_tail(node_list, c) < 0) { + LOG(ERR, "cannot add node in list\n"); + goto fail; + } + } + + cfzy_list_free(var_list, free); + var_list = NULL; + } + + /* parent */ + if (n->parent != NULL && !cfzy_list_elt_is_in_list(node_list, n->parent)) { + + if (cfzy_list_add_tail(node_list, n->parent) < 0) { + LOG(ERR, "cannot add node in list\n"); + goto fail; + } + } + + return node_list; + + fail: + if (node_list != NULL) + cfzy_list_free(node_list, NULL); + if (var_list != NULL) + cfzy_list_free(var_list, free); + + return NULL; +} + +/* Add a conditional default value to a node */ +int cfzy_confnode_add_defval(struct cfzy_confnode *n, const char *val, + const char *expr_buf) +{ + struct cfzy_confnode_defvalue *defval; + + defval = malloc(sizeof(*defval)); + if (defval == NULL) + return -1; + + memset(defval, 0, sizeof(*defval)); + defval->expr = cfzy_expr_parse(expr_buf); + if (defval->expr == NULL) { + free(defval); + return -1; + } + + defval->val = strdup(val); + if (defval->val == NULL) { + cfzy_expr_free(defval->expr); + free(defval); + return -1; + } + + if (cfzy_list_add_tail(n->default_val_list, defval) < 0) { + free(defval->val); + cfzy_expr_free(defval->expr); + free(defval); + return -1; + } + + return 0; +} + +/* Parse a line structure (and its associated line buffer) to match an + * attribute of a confnode. Return 0 if the line matches, else return + * -1 if we cannot match a valid attribute. */ +enum cfzy_parse_return +cfzy_confnode_add_attr(struct cfzy_confnode *n, + const struct cfzy_token_list *tklist) +{ + enum cfzy_parse_return ret; + struct cfzy_token *first_tok, *tok; + const char *linebuf = tklist->linebuf; + + first_tok = TAILQ_FIRST(&tklist->list); + + /* specific attribute */ + if (n->ops->add_attr != NULL) { + ret = n->ops->add_attr(n, tklist, linebuf); + if (ret != NO_MATCH) + return ret; + } + + /* syntax is: "prompt content" */ + if (tklist->n_token == 2 && + !strcmp(first_tok->str, "prompt")) { + + if (n->flags & CFZY_F_NO_SET_PROMPT) { + LOG(ERR, "node does not support 'prompt' attr\n"); + return ERROR; + } + + tok = TAILQ_NEXT(first_tok, next); + if (n->prompt != NULL) { + free(n->prompt); + n->prompt = NULL; + } + n->prompt = strdup(tok->str); + if (n->prompt == NULL) { + LOG(ERR, "cannot allocate prompt\n"); + return ERROR; + } + return SUCCESS; + } + + /* syntax is: "requires EXPRESSION" */ + if (tklist->n_token > 1 && + !strcmp(first_tok->str, "requires")) { + struct cfzy_expr *exp; + + if (n->flags & CFZY_F_NO_SET_DEPS) { + LOG(ERR, "node does not support 'requires' attr\n"); + return ERROR; + } + + tok = TAILQ_NEXT(first_tok, next); + exp = cfzy_expr_parse(linebuf + tok->offset); + if (exp == NULL) { + LOG(ERR, "cannot parse expression\n"); + return ERROR; + } + + cfzy_list_add_tail(n->expr_deps, exp); + return SUCCESS; + } + + /* syntax is: "default VALUE" */ + if (tklist->n_token == 2 && + !strcmp(first_tok->str, "default")) { + + if (n->flags & CFZY_F_NO_SET_DEFAULT) { + LOG(ERR, "node does not support 'default' attr\n"); + return ERROR; + } + + tok = TAILQ_NEXT(first_tok, next); + if (cfzy_confnode_str2bool(n, tok->str) < 0) { + LOG(ERR, "invalid value for this node\n"); + return ERROR; + } + + if (n->default_value != NULL) + free(n->default_value); + n->default_value = strdup(tok->str); + if (n->default_value == NULL) { + LOG(ERR, "cannot add default value\n"); + return ERROR; + } + + return SUCCESS; + } + + /* syntax is: "default VALUE if EXPR" */ + if (tklist->n_token >= 4 && + !strcmp(first_tok->str, "default")) { + struct cfzy_token *tok2; + + tok = TAILQ_NEXT(first_tok, next); /* points to value */ + tok2 = TAILQ_NEXT(tok, next); /* points to "if" */ + + if (strcmp(tok2->str, "if")) + return NO_MATCH; + + if (n->flags & CFZY_F_NO_SET_DEFAULT) { + LOG(ERR, "node does not support 'default' attr\n"); + return ERROR; + } + + tok2 = TAILQ_NEXT(tok2, next); /* points to expression */ + + if (cfzy_confnode_str2bool(n, tok->str) < 0) { + LOG(ERR, "invalid value for this node\n"); + return ERROR; + } + + if (cfzy_confnode_add_defval(n, tok->str, + linebuf + tok2->offset) < 0) { + LOG(ERR, "cannot add default value\n"); + return ERROR; + } + + return SUCCESS; + } + + return NO_MATCH; +} + +/* write config value in file f in a dotconfig-like manner. Return 0 + * on success. */ +int cfzy_confnode_dotconfig_write(const struct cfzy_confnode *n, FILE *f) +{ + int val; + const struct cfzy_confnode *c; + + if (n->ops->dotconfig_write) + return n->ops->dotconfig_write(n, f); + + if (n->flags & CFZY_F_DOTCONF_SHOW_TITLE) { + if (fprintf(f, + "#\n" + "# -- %s\n" + "#\n", n->prompt) < 0) + return -1; + } + + /* only dump the children if effective value is not defined + * (some nodes like "comment", "if", "menu", ... can have a + * NULL effective value) */ + if (n->effective_value == NULL) + goto dump_children; + + /* when a boolean is unset, use the "is not set" syntax */ + if (n->flags & CFZY_F_VAL_IS_BOOL) { + val = cfzy_confnode_str2bool(n, n->effective_value); + if (val < 0) + return -1; + + if (val == 0) { + if (fprintf(f, "# CONFIG_%s is not set\n", + cfzy_confnode_name(n)) < 0) + return -1; + return 0; + } + } + + /* common case: dump the value, with or without quotes */ + if (n->flags & CFZY_F_QUOTE_VAL) { + char *quoted_value = cfzy_string_quote(n->effective_value); + + if (quoted_value == NULL) + return -1; + + if (fprintf(f, "CONFIG_%s=%s\n", cfzy_confnode_name(n), + quoted_value) < 0) { + free(quoted_value); + return -1; + } + free(quoted_value); + } + else { + if (fprintf(f, "CONFIG_%s=%s\n", cfzy_confnode_name(n), + n->effective_value) < 0) + return -1; + } + + dump_children: + TAILQ_FOREACH(c, &n->children, child_next) { + if (cfzy_confnode_dotconfig_write(c, f) < 0) + return -1; + } + return 0; +} + +/* write config value in file f in a dotconfig-like manner. Return 0 + * on success. */ +int cfzy_confnode_c_hdr_write(const struct cfzy_confnode *n, FILE *f) +{ + int val; + const struct cfzy_confnode *c; + + if (n->ops->c_hdr_write) + return n->ops->c_hdr_write(n, f); + + /* if no value, don't dump the value of the node, just the children */ + if (n->effective_value == NULL) + goto dump_children; + + if (n->flags & CFZY_F_VAL_IS_BOOL) { + val = cfzy_confnode_str2bool(n, n->effective_value); + if (val < 0) + return -1; + + /* bool is false -> undef */ + if (val == 0) { + if (fprintf(f, "#undef CONFIG_%s\n", + cfzy_confnode_name(n)) < 0) + return -1; + return 0; + } + + /* bool is true -> #define to 1 */ + if (fprintf(f, "#define CONFIG_%s 1\n", + cfzy_confnode_name(n)) < 0) + return -1; + } + /* common case: dump the value, with or without quotes */ + else if (n->flags & CFZY_F_QUOTE_VAL) { + char *quoted_value = cfzy_string_quote(n->effective_value); + + if (quoted_value == NULL) + return -1; + + if (fprintf(f, "#define CONFIG_%s %s\n", + cfzy_confnode_name(n), quoted_value) < 0) { + free(quoted_value); + return -1; + } + free(quoted_value); + } + else { + if (fprintf(f, "#define CONFIG_%s %s\n", + cfzy_confnode_name(n), n->effective_value) < 0) + return -1; + } + + dump_children: + TAILQ_FOREACH(c, &n->children, child_next) { + if (cfzy_confnode_c_hdr_write(c, f) < 0) + return -1; + } + return 0; +} + +/* Close the node beeing parsed */ +int cfzy_confnode_close(struct cfzy_confnode *n) +{ + if (n->ops->close) + return n->ops->close(n); + + return 0; +} + +/* Return the boolean value of the node given the string value */ +int cfzy_confnode_str2bool(const struct cfzy_confnode *n, + const char *value) +{ + if (n->ops->str2bool) + return n->ops->str2bool(n, value); + + /* if there is no specific function, any non-NULL value + * returns 1 */ + if (value == NULL) + return 0; + + return 1; +} + +/* Return a string identifying the node type ("config", "menuconfig", + * "choice", ...) */ +const char *cfzy_confnode_get_type_str(const struct cfzy_confnode *n) +{ + if (n->ops->get_type_str) + return n->ops->get_type_str(n); + + return "unknown"; +} + +static int __get_path(const struct cfzy_confnode *n, char *buf, + int len, int first) +{ + int ret = 0, off; + + if (n->parent == NULL) + return snprintf(buf, len, "/"); + + ret = __get_path(n->parent, buf, len, 0); + if (ret < 0) + return ret; + off = ret; + if (off >= len) + return -1; + + if (n->flags & CFZY_F_INVISIBLE) + return ret; + + /* a visible node must have a name */ + if (n->name == NULL) + return -1; + + ret = snprintf(buf + off, len - off, "%s%s", n->name, + first ? "" : "/"); + if (ret >= len - off) + return -1; + + return ret + off; +} + +/* Dump path in buffer. Invisible nodes like 'if' are ignored. */ +int cfzy_confnode_path(const struct cfzy_confnode *n, char *buf, int len) +{ + int ret; + + ret = __get_path(n, buf, len, 1); + if (ret < 0) + return ret; + return 0; +} + +/* dump a config file in a file */ +int cfzy_confnode_dump(const struct cfzy_confnode *n, FILE *f, int mode) +{ + const struct cfzy_confnode *c; + struct cfzy_confnode_defvalue *defval; + struct cfzy_list_elt *e; + struct cfzy_expr *expr; + char buf[512]; + + if (fprintf(f, "%s\n", cfzy_confnode_get_type_str(n)) < 0) + return -1; + if (fprintf(f, " name: %s\n", cfzy_confnode_name(n)) < 0) + return -1; + if (fprintf(f, " prompt: %s\n", n->prompt) < 0) + return -1; + if (cfzy_confnode_path(n, buf, sizeof(buf)) == 0) { + if (fprintf(f, " path: %s\n", buf) < 0) + return -1; + } + + if (mode == CFZY_DUMP_MODE_MINIMAL) + return 0; + + /* explicit dependencies */ + TAILQ_FOREACH(e, n->expr_deps, next) { + expr = e->ptr; + if (cfzy_expr_to_str(expr, buf, sizeof(buf)) < 0) + return -1; + if (fprintf(f, " requires: %s\n", buf) < 0) + return -1; + } + + /* XXX display valid values */ + + /* default values */ + TAILQ_FOREACH(e, n->default_val_list, next) { + defval = e->ptr; + + if (cfzy_expr_to_str(defval->expr, buf, sizeof(buf)) < 0) + return -1; + + if (fprintf(f, " default: %s if %s\n", defval->val, buf) < 0) + return -1; + } + if (fprintf(f, " node_default %s\n", n->default_value) < 0) + return -1; + if (fprintf(f, " user_value %s\n", n->user_value) < 0) + return -1; + if (fprintf(f, " effective_value %s\n", n->effective_value) < 0) + return -1; + + if (mode == CFZY_DUMP_MODE_STANDARD) + return 0; + + if (fprintf(f, " old_user_value %s\n", n->old_user_value) < 0) + return -1; + if (fprintf(f, " old_effective_value %s\n", n->old_effective_value) < 0) + return -1; + if (fprintf(f, " parsed at %s:%d\n", n->filename, n->linenum) < 0) + return -1; + + if (fprintf(f, "\n") < 0) + return -1; + + if (mode == CFZY_DUMP_MODE_FULL) + return 0; + + /* dump children */ + TAILQ_FOREACH(c, &n->children, child_next) { + if (cfzy_confnode_dump(c, f, CFZY_DUMP_MODE_FULL_RECURSIVE) < 0) + return -1; + } + + /* close node if it's a directory */ + if (n->flags & CFZY_F_IS_DIR) { + if (fprintf(f, "end%s\n\n", cfzy_confnode_get_type_str(n)) < 0) + return -1; + } + + return 0; +} + +/* set the uservalue of a node */ +int cfzy_confnode_set_uservalue(struct cfzy_confnode *n, const char *value) +{ + char *newvalue = NULL; + + if (n->ops->set_uservalue) + return n->ops->set_uservalue(n, value); + + /* check if value is valid */ + if (cfzy_confnode_str2bool(n, value) < 0) + return -1; + + if (value != NULL) { + newvalue = strdup(value); + if (newvalue == NULL) { + LOG(ERR, "cannot allocate new value\n"); + return -1; + } + } + + if (n->user_value != NULL) + free(n->user_value); + + n->user_value = newvalue; + return 0; +} + +/* Get the boolean value of a node, given its name, used as a callback + * by cfzy_expr_parse() when parsing an expression for + * cfzy_confnode_evaluate() */ +int cfzy_confnode_get_boolvalue(const char *name, + const struct cfzy_conftree *conftree) +{ + const struct cfzy_confnode *n; + + n = cfzy_htable_lookup(conftree->nodes_htable, name); + if (n == NULL) + return -1; + + return cfzy_confnode_str2bool(n, n->effective_value); +} + +/* return 0 if a parent node is disabled or if a prerequisite + * expression is false, else return 1. On error, return -1. */ +int cfzy_confnode_check_deps(struct cfzy_confnode *n) +{ + struct cfzy_list_elt *e; + struct cfzy_expr *expr; + struct cfzy_confnode *parent; + int (*get_boolvalue)(const char *, const struct cfzy_conftree *); + int (*get_boolvalue_casted)(const char *, void *); + int ret; + + /* we use an intermediate variable to have a compiler warning + * if the prototype of cfzy_confnode_get_boolvalue changes. We + * know that that these types are compatible here. */ + get_boolvalue = cfzy_confnode_get_boolvalue; + get_boolvalue_casted = (void *)get_boolvalue; + + /* check the dep list first */ + TAILQ_FOREACH(e, n->expr_deps, next) { + expr = e->ptr; + + ret = cfzy_expr_eval(expr, get_boolvalue_casted, n->conftree); + if (ret < 0) + return -1; + + /* the expression is evaluated to 0, we cannot enable + * this node */ + if (ret == 0) { + n->effective_value = NULL; + LOG(DEBUG, "disable node <%s> (expr dep)\n", + cfzy_confnode_name(n)); + return 0; + } + } + + /* check the parent nodes */ + for (parent = n->parent; parent != NULL; parent = parent->parent) { + ret = cfzy_confnode_str2bool(parent, parent->effective_value); + if (ret < 0) + return -1; + + /* a parent node is evaluated to 0, we cannot enable + * this node */ + if (ret == 0) { + n->effective_value = NULL; + LOG(DEBUG, "disable node <%s> (parent dep)\n", + cfzy_confnode_name(n)); + return 0; + } + } + + return 1; +} + +/* Evaluate the effective value of a node, assuming all prerequisites + * are already evaluated. */ +int cfzy_confnode_evaluate(struct cfzy_confnode *n) +{ + struct cfzy_list_elt *e; + struct cfzy_confnode_defvalue *defval; + int (*get_boolvalue)(const char *, const struct cfzy_conftree *); + int (*get_boolvalue_casted)(const char *, void *); + int ret; + + LOG(DEBUG, "evaluating node <%s>\n", cfzy_confnode_name(n)); + + /* we use an intermediate variable to have a compiler warning + * if the prototype of cfzy_confnode_get_boolvalue changes. We + * know that that these types are compatible here. */ + get_boolvalue = cfzy_confnode_get_boolvalue; + get_boolvalue_casted = (void *)get_boolvalue; + + /* remove previous effective value */ + if (n->effective_value != NULL) { + free(n->effective_value); + n->effective_value = NULL; + } + + /* check prerequisites and parents */ + ret = cfzy_confnode_check_deps(n); + if (ret == 0) { + /* we cannot enable this node due to dependencies */ + n->effective_value = NULL; + LOG(DEBUG, "disable node <%s> (expr dep)\n", + cfzy_confnode_name(n)); + return 0; + } + /* error */ + if (ret < 0) + return ret; + + /* user value has the priority */ + if (n->user_value != NULL) { + n->effective_value = strdup(n->user_value); + if (n->effective_value == NULL) + return -1; + + LOG(DEBUG, "set node <%s> to user value <%s>\n", + cfzy_confnode_name(n), n->effective_value); + return 0; + } + + /* assign a default value */ + TAILQ_FOREACH(e, n->default_val_list, next) { + defval = e->ptr; + + ret = cfzy_expr_eval(defval->expr, get_boolvalue_casted, + n->conftree); + if (ret < 0) + return -1; + + /* the expression is evaluated to 1, use this default + * value for this node */ + if (ret == 1) { + n->effective_value = strdup(defval->val); + if (n->effective_value == NULL) + return -1; + LOG(DEBUG, "set node <%s> to default_list value <%s>\n", + cfzy_confnode_name(n), n->effective_value); + return 0; + } + } + + if (n->default_value != NULL) + n->effective_value = strdup(n->default_value); + + LOG(DEBUG, "set node <%s> to default value <%s>\n", + cfzy_confnode_name(n), n->effective_value); + + return 0; +} diff --git a/tools/cfzy/libconfizery/cfzy_confnode.h b/tools/cfzy/libconfizery/cfzy_confnode.h new file mode 100644 index 0000000..0d2f7da --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_confnode.h @@ -0,0 +1,564 @@ +/* + * Copyright (c) 2013, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +/** + * @file + * Confizery configuration node + * + * This module provides functions related to configuration nodes. A + * configuration node is a structure that stores a part of the + * information of the configuration tree. For instance, a "config" or + * "menuconfig" option is stored in a configuration node structure. + * + * It contains among other fields the name of the node (CONFIG_XYZ), + * the prompt and the help. Each node references its parent and a list + * of children as some nodes can contains some other nodes (menu, if, + * choice, ...). It also contains the default values, the user value, + * and the effective value of the node. Some nodes have no values (if, + * comment, ...). + */ + +#ifndef _CFZY_CONFNODE_H_ +#define _CFZY_CONFNODE_H_ + +#include + +#include "cfzy_confnode_ops.h" +#include "cfzy_conftree_parser.h" + +/* confnode flags */ +#define CFZY_F_IS_DIR 0x0001 /**< node can contain other nodes */ +#define CFZY_F_NO_SET_DEPS 0x0002 /**< does not support the "requires" attribute */ +#define CFZY_F_NO_SET_PROMPT 0x0004 /**< does not support the "prompt" attribute */ +#define CFZY_F_NO_SET_DEFAULT 0x0008 /**< does not support the "default" attribute */ +#define CFZY_F_IS_ROOT 0x0010 /**< the root node */ +#define CFZY_F_VAL_IS_BOOL 0x0020 /**< value is a boolean */ +#define CFZY_F_INVISIBLE 0x0040 /**< never seen from cmdline (like the IF node) */ +#define CFZY_F_NO_NAME 0x0080 /**< node has no name (if, comments, ...) */ +#define CFZY_F_NO_SET_VALUE 0x0100 /**< user cannot set value (if, comments, ...) */ +#define CFZY_F_QUOTE_VAL 0x0200 /**< quote value */ +#define CFZY_F_DOTCONF_SHOW_TITLE 0x0400 /**< show title as a comment in dotconfig file */ + +struct cfzy_conftree; + +/** + * Structure storing a conditional default value + */ +struct cfzy_confnode_defvalue { + struct cfzy_expr *expr; /**< expression (condition) */ + char *val; /**< value */ +}; + +/** + * List of cfzy_node, used to chain children together + */ +TAILQ_HEAD(cfzy_confnode_list, cfzy_confnode); + +/** + * This structure defines a configuration node in the configration + * tree, for instance a "menu" entry, or a "bool" entry. + */ +struct cfzy_confnode { + /* pointer to other nodes in conf tree */ + struct cfzy_confnode *parent; /**< pointer to parent */ + struct cfzy_confnode_list children; /**< list of children */ + TAILQ_ENTRY(cfzy_confnode) child_next; /**< next in children list */ + struct cfzy_conftree *conftree; /**< back pointer to conftree */ + + /* node attributes */ + char *name; /**< node name (dynamic alloc) */ + char *prompt; /**< node prompt (dynamic alloc) */ + char *help; /**< node help (dynamic alloc) */ + unsigned flags; /**< node flags */ + struct cfzy_list_head *expr_deps; /**< list of prereq expressions */ + + /* where it was parsed */ + char *filename; /**< name of conftree file */ + int linenum; /**< line where node was declared */ + + /* specific to a node type */ + const struct cfzy_confnode_ops *ops; /**< specific ops on node */ + void *private; + + /* values */ + char *default_value; /**< default value for this node */ + struct cfzy_list_head *default_val_list; /* conditional default vals */ + char *user_value; /**< value wanted by the user */ + char *effective_value; /**< real value, taking care of deps */ + char *old_user_value; /**< saved user value */ + char *old_effective_value; /**< saved effective value */ + + /* allow to chain this node in a priority ordered list */ + TAILQ_ENTRY(cfzy_confnode) prio_next; /**< next in ordered list */ + int in_prio_list; /**< true if present in prio list */ + + struct cfzy_list_head *node_deps; /**< list of nodes that must be + * evaluated before this one */ +}; + +/** + * Get the name of a node + * + * @param n + * configuration node + * @return + * pointer to the node name (hosted in the node structure) + */ +static inline const char *cfzy_confnode_name(const struct cfzy_confnode *n) +{ + if (n->name == NULL) + return "no-name"; + return n->name; +} + +/** + * Allocate a new node + * + * Allocate a new empty node, with no specific ops. + * + * @param conftree + * configuration tree + * @return + * the allocated configuration node, or NULL on error + */ +struct cfzy_confnode *cfzy_confnode_alloc(struct cfzy_conftree *conftree); + +/** + * Free the node given as argument, and all its associated data + * + * This function will free the children of the nodes, then the node + * itself, first by calling the specific function n->ops->free() if + * defined, then by doing the generic operations. + * + * @param n + * configuration node + */ +void cfzy_confnode_free(struct cfzy_confnode *n); + +/** + * Get the prerequisites for this node + * + * Return the list of nodes required to evaluate the value of the + * given node. Indeed, each node has a 'expr_deps' field storing a + * list of expressions and a list of default value associated to + * expressions. This function returns the list of all nodes referenced + * in these expressions and all the ancestor nodes. + * + * The returned list must be freed by the user with + * cfzy_list_free(list, NULL). + * + * @param n + * configuration node + * @return + * list of nodes, or NULL on error + */ +struct cfzy_list_head * +cfzy_confnode_get_deplist(const struct cfzy_confnode *n); + +/** + * Add a conditional default value to a node + * + * @param n + * configuration node + * @param val + * value to be used as default value + * @param expr_buf + * condition string to enable the default value + * @return + * 0 on success, negative on error + */ +int cfzy_confnode_add_defval(struct cfzy_confnode *n, const char *val, + const char *expr_buf); + +/** + * Parse a line of configuration tree file, expecting an attribute + * + * This function first tries to match specific node attributes, using + * the specific operation n->ops->add_attr() if registered. Then it + * tries to match generic attributes (prompt, requires, default). + * + * This function is internal to the confizery library (called by the + * configuration tree parser). + * + * @param n + * configuration node + * @param tklist + * list of tokens for this line + * @return + * SUCCESS if it matches an attribute of this node, NO_MATCH if the line + * is not an attribute for this node, ERROR on error + */ +enum cfzy_parse_return +cfzy_confnode_add_attr(struct cfzy_confnode *n, + const struct cfzy_token_list *tklist); + +/** + * Write .config-like configuration in a file + * + * Write in a file the configuration values of this node and its + * descendants. The format of this file is like .config: it can be + * sourced by a shell or a Makefile. + * + * The function calls the specific node operation + * n->ops->dotconfig_write() if defined. Else, it uses the defaut + * behavior, which varies depending on node flags. + * + * This function is internal and is called by the public function + * cfzy_dotconfig_write(). + * + * @param n + * configuration node + * @param f + * open file with write permissions where config will be written + * @return + * 0 on success, negative on error + */ +int cfzy_confnode_dotconfig_write(const struct cfzy_confnode *n, FILE *f); + +/** + * Write the configuration in a C header file + * + * Write in a file the configuration values of this node and its + * descendants. The format of this file is a C header, so it can be + * included by a C application. + * + * The function calls the specific node operation + * n->ops->c_hdr_write() if defined. Else, it uses the defaut + * behavior, which varies depending on node flags. + * + * This function is internal and is called by the public function + * cfzy_c_hdr_write(). + * + * @param n + * configuration node + * @param f + * open file with write permissions where config will be written + * @return + * 0 on success, negative on error + */ +int cfzy_confnode_c_hdr_write(const struct cfzy_confnode *n, FILE *f); + +/** + * Return a string identifying the node type + * + * The function calls the specific node operation + * n->ops->get_type_str(). + * + * The returned string points to static memory and contains the type + * of the node (ex: "config", "menuconfig", "choice", ...) + * + * @param n + * configuration node + * @return + * pointer to a node type string + */ +const char *cfzy_confnode_get_type_str(const struct cfzy_confnode *n); + +/** + * Return the boolean value of the node given the string value + * + * Convert the string value given as parameter to a boolean value. It + * calls the specific node operation n->ops->str2bool() if + * defined. Else, it uses the defaut behavior: return 0 for value=NULL + * or 1 for any other value. + * + * The specific function (if defined) can also return a negative value + * to indicate that the given value is not valid for this node. + * + * @param n + * configuration node + * @param value + * string containing the value + * @return + * the boolean value (0 or 1) if the string value is valid, + * else -1 + */ +int cfzy_confnode_str2bool(const struct cfzy_confnode *n, const char *value); + +/** + * Get the path of the node + * + * @param n + * configuration node + * @param buf + * buffer where the path should be written + * @param len + * length of the buffer + * @return + * 0 on success, negative on error + */ +int cfzy_confnode_path(const struct cfzy_confnode *n, char *buf, int len); + +/* dump modes */ +#define CFZY_DUMP_MODE_MINIMAL 0x0 +#define CFZY_DUMP_MODE_STANDARD 0x1 +#define CFZY_DUMP_MODE_FULL 0x2 +#define CFZY_DUMP_MODE_FULL_RECURSIVE 0x3 + +/** + * Dump a human-readable configuration in a file + * + * Write in a file the structure fields of this node and its + * descendants. + * + * @param n + * configuration node + * @param f + * open file with write permissions where configuration will be written + * @param mode + * behavior of dump (CFZY_DUMP_MODE_MINIMAL, CFZY_DUMP_MODE_STANDARD, ...) + * @return + * 0 on success, negative on error + */ +int cfzy_confnode_dump(const struct cfzy_confnode *n, FILE *f, int mode); + +/** + * Check prerequisites for a node + * + * @param n + * configuration node to be checked + * @return + * 0 if a parent node is disabled or if a prerequisite expression is + * false, 1 if node can be enable. On error, return -1. + */ +int cfzy_confnode_check_deps(struct cfzy_confnode *n); + +/** + * Evaluate the effective value of a node + * + * Set the new value of n->effective_value, assuming all prerequisites + * are already evaluated. This function is used as a callback by + * cfzy_expr_parse() when parsing an expression for + * cfzy_confnode_evaluate(). + * + * @return n + * configuration node + * @return + * 0 on success, negative on error + */ +int cfzy_confnode_evaluate(struct cfzy_confnode *n); + +/** + * Get the boolean value of a node, given its name + * + * Return the effective value of a node (assuming it has been + * evaluated previously). + * + * @param name + * configuration node name + * @param conftree + * configuration tree + * @return + * the boolean value (0 or 1) if the string value is valid, + * else -1 + */ +int cfzy_confnode_get_boolvalue(const char *name, + const struct cfzy_conftree *conftree); + +/** + * Set user value of a configuration node + * + * To set the user value of a node, the user must not access to the + * field directly but use this function. Indeed, a node may want to do + * a specific operation (like a check, or modifying children nodes) + * stored in n->ops->set_uservalue(). + * + * @return n + * configuration node + * @param value + * value to be set as user value + * @return + * 0 on success, negative on error + */ +int cfzy_confnode_set_uservalue(struct cfzy_confnode *n, const char *value); + +/** + * Create a new root node + * + * @param conftree + * configuration tree + * @return + * a pointer to the configuration node, or NULL on error + */ +struct cfzy_confnode *cfzy_confnode_root_new(struct cfzy_conftree *conftree); + +/** + * Parse a line of configuration tree file, expecting new choice node + * + * @param n + * configuration node + * @param tklist + * list of tokens for this line + * @return + * SUCCESS if it matches an attribute of this node, NO_MATCH if the line + * is not an attribute for this node, ERROR on error + */ +enum cfzy_parse_return +cfzy_confnode_choice_new(struct cfzy_confnode *n, + const struct cfzy_token_list *tklist); +/** + * Parse a line of configuration tree file, expecting new choiceconfig node + * + * @param n + * configuration node + * @param tklist + * list of tokens for this line + * @return + * SUCCESS if it matches an attribute of this node, NO_MATCH if the line + * is not an attribute for this node, ERROR on error + */ +enum cfzy_parse_return +cfzy_confnode_choiceconfig_new(struct cfzy_confnode *n, + const struct cfzy_token_list *tklist); + +/** + * Parse a line of configuration tree file, expecting new comment node + * + * @param n + * configuration node + * @param tklist + * list of tokens for this line + * @return + * SUCCESS if it matches an attribute of this node, NO_MATCH if the line + * is not an attribute for this node, ERROR on error + */ +enum cfzy_parse_return +cfzy_confnode_comment_new(struct cfzy_confnode *n, + const struct cfzy_token_list *tklist); + +/** + * Parse a line of configuration tree file, expecting new config node + * + * @param n + * configuration node + * @param tklist + * list of tokens for this line + * @return + * SUCCESS if it matches an attribute of this node, NO_MATCH if the line + * is not an attribute for this node, ERROR on error + */ +enum cfzy_parse_return +cfzy_confnode_config_new(struct cfzy_confnode *n, + const struct cfzy_token_list *tklist); + +/** + * Parse a line of configuration tree file, expecting new if node + * + * @param n + * configuration node + * @param tklist + * list of tokens for this line + * @return + * SUCCESS if it matches an attribute of this node, NO_MATCH if the line + * is not an attribute for this node, ERROR on error + */ +enum cfzy_parse_return +cfzy_confnode_if_new(struct cfzy_confnode *n, + const struct cfzy_token_list *tklist); + +/** + * Parse a line of configuration tree file, expecting new intconfig node + * + * @param n + * configuration node + * @param tklist + * list of tokens for this line + * @return + * SUCCESS if it matches an attribute of this node, NO_MATCH if the line + * is not an attribute for this node, ERROR on error + */ +enum cfzy_parse_return +cfzy_confnode_intconfig_new(struct cfzy_confnode *n, + const struct cfzy_token_list *tklist); + +/** + * Parse a line of configuration tree file, expecting new menu node + * + * @param n + * configuration node + * @param tklist + * list of tokens for this line + * @return + * SUCCESS if it matches an attribute of this node, NO_MATCH if the line + * is not an attribute for this node, ERROR on error + */ +enum cfzy_parse_return +cfzy_confnode_menu_new(struct cfzy_confnode *n, + const struct cfzy_token_list *tklist); + +/** + * Parse a line of configuration tree file, expecting new menuconfig node + * + * @param n + * configuration node + * @param tklist + * list of tokens for this line + * @return + * SUCCESS if it matches an attribute of this node, NO_MATCH if the line + * is not an attribute for this node, ERROR on error + */ +enum cfzy_parse_return +cfzy_confnode_menuconfig_new(struct cfzy_confnode *n, + const struct cfzy_token_list *tklist); + +/** + * Parse a line of configuration tree file, expecting new strconfig node + * + * @param n + * configuration node + * @param tklist + * list of tokens for this line + * @return + * SUCCESS if it matches an attribute of this node, NO_MATCH if the line + * is not an attribute for this node, ERROR on error + */ +enum cfzy_parse_return +cfzy_confnode_strconfig_new(struct cfzy_confnode *n, + const struct cfzy_token_list *tklist); + +/** + * Close the node beeing parsed + * + * Some nodes can contains other nodes (menu, menuconfig, ...). When + * the parser has finished to parse the descendants of the node, it + * calls this function to close the node. This can trigger some + * specific operations (like checks) if the node type registers a + * n->ops->close(). If no specific function is registered, it does + * nothing. + * + * This function is internal to the confizery library (called by the + * configuration tree parser). + * + * @param n + * configuration node + * @return + * 0 on success, negative on error + */ +int cfzy_confnode_close(struct cfzy_confnode *n); + + +#endif /* _CFZY_CONFNODE_H_ */ diff --git a/tools/cfzy/libconfizery/cfzy_confnode_choice.c b/tools/cfzy/libconfizery/cfzy_confnode_choice.c new file mode 100644 index 0000000..b564748 --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_confnode_choice.c @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2010, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 "cfzy_log.h" +#include "cfzy_list.h" +#include "cfzy_expr.h" +#include "cfzy_confnode.h" + +#define LOG(level, fmt, args...) \ + CFZY_LOG("confnode_choice", level, fmt, ##args) + +/* Return a string identifying the node type ("config", "menuconfig", + * "choice", ...) */ +static const char *choice_get_type_str(const struct cfzy_confnode *n) +{ + (void)n; + return "choice"; +} + +static int choice_str2bool(const struct cfzy_confnode *n, const char *value) +{ + struct cfzy_confnode *c; + + /* NULL means "disabled" */ + if (value == NULL) + return 0; + + /* if we have no children, it's because we are creating the + * node, so accept all values, we will check them later in + * choice_close() */ + if (TAILQ_EMPTY(&n->children)) + return 1; + + /* else, the value must be the name of a children */ + TAILQ_FOREACH(c, &n->children, child_next) { + if (!strcmp(value, c->name)) + return 1; + } + + /* invalid value */ + return -1; +} + +static int choice_close(struct cfzy_confnode *n) +{ + struct cfzy_confnode_defvalue *defval; + struct cfzy_list_elt *e; + struct cfzy_confnode *c; + char exprbuf[512]; + int ret; + + /* browse all default values and call choice_str2bool() on + * them to check that they are correct */ + TAILQ_FOREACH(e, n->default_val_list, next) { + defval = e->ptr; + + if (choice_str2bool(n, defval->val) < 0) { + LOG(ERR, "invalid default value <%s> for node <%s>\n", + defval->val, n->name); + return -1; + } + } + if (choice_str2bool(n, n->default_value) < 0) { + LOG(ERR, "invalid default value <%s> for node <%s>\n", + n->default_value, n->name); + return -1; + } + + /* for each child node */ + TAILQ_FOREACH(c, &n->children, child_next) { + + /* set default value */ + if (!strcmp(n->default_value, c->name)) + c->default_value = strdup("y"); + else + c->default_value = strdup("n"); + + if (c->default_value == NULL) { + LOG(ERR, "cannot allocate default value>\n"); + return -1; + } + + /* set conditional default values for child nodes with + * the same condition */ + TAILQ_FOREACH(e, n->default_val_list, next) { + defval = e->ptr; + + if (cfzy_expr_to_str(defval->expr, exprbuf, + sizeof(exprbuf)) < 0) { + LOG(ERR, "cannot convert expression to str\n"); + return -1; + } + + if (!strcmp(defval->val, c->name)) + ret = cfzy_confnode_add_defval(c, "y", exprbuf); + else + ret = cfzy_confnode_add_defval(c, "n", exprbuf); + + if (ret < 0) { + LOG(ERR, "cannot add conditional default\n"); + return -1; + } + } + + } + + return 0; +} + +static int choice_set_uservalue(struct cfzy_confnode *n, const char *value) +{ + struct cfzy_confnode *c; + char *newvalue; + + /* if value is NULL, unset this node and all its children, + * falling back to default value */ + if (value == NULL) { + /* this node */ + if (n->user_value != NULL) + free(n->user_value); + n->user_value = NULL; + + /* children */ + TAILQ_FOREACH(c, &n->children, child_next) { + if (c->user_value != NULL) + free(c->user_value); + c->user_value = NULL; + } + return 0; + } + + /* if not NULL, the value must be the name of a children */ + TAILQ_FOREACH(c, &n->children, child_next) { + if (!strcmp(value, c->name)) + break; + } + if (c == NULL) + return -1; + + + /* this node */ + if (n->user_value != NULL) + free(n->user_value); + n->user_value = strdup(c->name); + if (n->user_value == NULL) { + LOG(ERR, "cannot allocate value\n"); + return -1; + } + + /* children */ + TAILQ_FOREACH(c, &n->children, child_next) { + if (!strcmp(value, c->name)) + newvalue = strdup("y"); + else + newvalue = strdup("n"); + + if (newvalue == NULL) { + LOG(ERR, "cannot allocate value\n"); + return -1; + } + + if (c->user_value != NULL) + free(c->user_value); + c->user_value = newvalue; + } + + return 0; +} + +static struct cfzy_confnode_ops choice_ops = { + .free = NULL, + .add_attr = NULL, + .close = choice_close, + .dotconfig_write = NULL, + .c_hdr_write = NULL, + .str2bool = choice_str2bool, + .get_type_str = choice_get_type_str, + .set_uservalue = choice_set_uservalue, +}; + +/* Create a new node */ +enum cfzy_parse_return cfzy_confnode_choice_new(struct cfzy_confnode *n, + const struct cfzy_token_list *tklist) +{ + struct cfzy_token *tok; + tok = TAILQ_FIRST(&tklist->list); + + if (strcmp(tok->str, "choice") || tklist->n_token != 2) + return NO_MATCH; + + tok = TAILQ_NEXT(tok, next); + n->ops = &choice_ops; + n->flags = CFZY_F_IS_DIR | CFZY_F_QUOTE_VAL; + + n->name = strdup(tok->str); + if (n->name == NULL) + return ERROR; + + return SUCCESS; +} diff --git a/tools/cfzy/libconfizery/cfzy_confnode_choiceconfig.c b/tools/cfzy/libconfizery/cfzy_confnode_choiceconfig.c new file mode 100644 index 0000000..ea01c17 --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_confnode_choiceconfig.c @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2010, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 "cfzy_log.h" +#include "cfzy_expr.h" +#include "cfzy_confnode.h" + +#define LOG(level, fmt, args...) \ + CFZY_LOG("confnode_choiceconfig", level, fmt, ##args) + +/* Return a string identifying the node type ("config", "menuconfig", + * "choice", ...) */ +static const char *choiceconfig_get_type_str(const struct cfzy_confnode *n) +{ + (void)n; + return "choiceconfig"; +} + +static int choiceconfig_str2bool(const struct cfzy_confnode *n, + const char *value) +{ + (void)n; + + /* NULL or "n" means disabled */ + if (value == NULL || !strcmp("n", value)) + return 0; + + if (!strcmp("y", value)) + return 1; + + return -1; +} + +static int choiceconfig_set_uservalue(struct cfzy_confnode *n, + const char *value) +{ + struct cfzy_confnode *c; + + if (value == NULL) + return cfzy_confnode_set_uservalue(n->parent, NULL); + + if (!strcmp(value, "y")) + return cfzy_confnode_set_uservalue(n->parent, n->name); + + if (!strcmp(value, "n")) { + /* node is already set to "n", do nothing so we won't + * change the selected choice */ + if (!strcmp(n->user_value, "n")) + return 0; + + /* else just set to 'y' the first brother that we find */ + TAILQ_FOREACH(c, &n->children, child_next) { + if (c != n) + break; + } + + /* no brother found */ + if (c == NULL) + return -1; + + return cfzy_confnode_set_uservalue(c, "y"); + } + + return -1; +} + +static struct cfzy_confnode_ops choiceconfig_ops = { + .free = NULL, + .add_attr = NULL, + .close = NULL, + .str2bool = choiceconfig_str2bool, + .dotconfig_write = NULL, + .c_hdr_write = NULL, + .get_type_str = choiceconfig_get_type_str, + .set_uservalue = choiceconfig_set_uservalue, +}; + +/* Create a new node */ +enum cfzy_parse_return +cfzy_confnode_choiceconfig_new(struct cfzy_confnode *n, + const struct cfzy_token_list *tklist) +{ + struct cfzy_token *tok; + + tok = TAILQ_FIRST(&tklist->list); + + if (strcmp(tok->str, "choiceconfig") || tklist->n_token != 2) + return NO_MATCH; + + tok = TAILQ_NEXT(tok, next); + n->ops = &choiceconfig_ops; + n->flags = CFZY_F_NO_SET_DEFAULT | CFZY_F_VAL_IS_BOOL; + + n->name = strdup(tok->str); + if (n->name == NULL) + return ERROR; + + return SUCCESS; +} diff --git a/tools/cfzy/libconfizery/cfzy_confnode_comment.c b/tools/cfzy/libconfizery/cfzy_confnode_comment.c new file mode 100644 index 0000000..4e1e915 --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_confnode_comment.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2010, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 "cfzy_log.h" +#include "cfzy_confnode.h" + +#define LOG(level, fmt, args...) \ + CFZY_LOG("comment", level, fmt, ##args) + +/* write config value in file f in a dotconfig-like manner. Return 0 + * on success. */ +static int comment_dotconfig_write(const struct cfzy_confnode *n, FILE *f) +{ + if (fprintf(f, "# %s\n", n->prompt) < 0) + return -1; + + /* this node has no children, no need to dump them */ + return 0; +} + +/* Return a string identifying the node type ("config", "menuconfig", + * "choice", ...) */ +static const char *comment_get_type_str(const struct cfzy_confnode *n) +{ + (void)n; + return "comment"; +} + +static struct cfzy_confnode_ops comment_ops = { + .free = NULL, + .add_attr = NULL, + .close = NULL, + .str2bool = NULL, + .dotconfig_write = comment_dotconfig_write, + .c_hdr_write = NULL, + .get_type_str = comment_get_type_str, + .set_uservalue = NULL, +}; + +/* Create a new node */ +enum cfzy_parse_return cfzy_confnode_comment_new(struct cfzy_confnode *n, + const struct cfzy_token_list *tklist) +{ + struct cfzy_token *tok; + tok = TAILQ_FIRST(&tklist->list); + + if (strcmp(tok->str, "comment") || tklist->n_token != 2) + return NO_MATCH; + + tok = TAILQ_NEXT(tok, next); + /* comment string is stored in prompt field, not in name */ + if (n->prompt != NULL) { + free(n->prompt); + n->prompt = NULL; + } + n->prompt = strdup(tok->str); + if (n->prompt == NULL) { + LOG(ERR, "cannot allocate prompt"); + return ERROR; + } + n->ops = &comment_ops; + n->flags = CFZY_F_NO_SET_DEFAULT | + CFZY_F_NO_SET_PROMPT | + CFZY_F_NO_NAME | + CFZY_F_NO_SET_VALUE; + + return SUCCESS; +} diff --git a/tools/cfzy/libconfizery/cfzy_confnode_config.c b/tools/cfzy/libconfizery/cfzy_confnode_config.c new file mode 100644 index 0000000..7b802ef --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_confnode_config.c @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2010, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 "cfzy_confnode.h" + +/* Return a string identifying the node type ("config", "menuconfig", + * "choice", ...) */ +static const char *config_get_type_str(const struct cfzy_confnode *n) +{ + (void)n; + return "config"; +} + +static int config_str2bool(const struct cfzy_confnode *n, + const char *value) +{ + (void)n; + + /* NULL or "n" means disabled */ + if (value == NULL || !strcmp("n", value)) + return 0; + + if (!strcmp("y", value)) + return 1; + + return -1; +} + +static struct cfzy_confnode_ops config_ops = { + .free = NULL, + .add_attr = NULL, + .close = NULL, + .str2bool = config_str2bool, + .dotconfig_write = NULL, + .c_hdr_write = NULL, + .get_type_str = config_get_type_str, + .set_uservalue = NULL, +}; + +/* Create a new node */ +enum cfzy_parse_return cfzy_confnode_config_new(struct cfzy_confnode *n, + const struct cfzy_token_list *tklist) +{ + struct cfzy_token *tok; + tok = TAILQ_FIRST(&tklist->list); + + if (strcmp(tok->str, "config") || tklist->n_token != 2) + return NO_MATCH; + + tok = TAILQ_NEXT(tok, next); + n->ops = &config_ops; + n->flags = CFZY_F_VAL_IS_BOOL; + + n->name = strdup(tok->str); + if (n->name == NULL) + return ERROR; + + n->default_value = strdup("n"); + if (n->default_value == NULL) { + free(n->name); + n->name = NULL; + return ERROR; + } + + return SUCCESS; +} diff --git a/tools/cfzy/libconfizery/cfzy_confnode_if.c b/tools/cfzy/libconfizery/cfzy_confnode_if.c new file mode 100644 index 0000000..2fd68b3 --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_confnode_if.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2010, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 "cfzy_list.h" +#include "cfzy_expr.h" +#include "cfzy_confnode.h" + +/* write config value in file f in a dotconfig-like manner. Return -1 + * on error. */ +static int if_dotconfig_write(const struct cfzy_confnode *n, FILE *f) +{ + const struct cfzy_confnode *c; + int val; + + val = cfzy_confnode_str2bool(n, n->effective_value); + if (val < 0) + return -1; + + /* condition is wrong, nothing to dump */ + if (val == 0) + return 0; + + /* else, just dump children */ + TAILQ_FOREACH(c, &n->children, child_next) { + if (cfzy_confnode_dotconfig_write(c, f) < 0) + return -1; + } + return 0; +} + +/* Return a string identifying the node type ("config", "menuconfig", + * "choice", ...) */ +static const char *if_get_type_str(const struct cfzy_confnode *n) +{ + (void)n; + return "if"; +} + +/* evaluate the expression stored in the node */ +static int if_str2bool(const struct cfzy_confnode *n, const char *value) +{ + struct cfzy_list_elt *e; + struct cfzy_expr *expr; + int (*get_boolvalue)(const char *, const struct cfzy_conftree *); + int (*get_boolvalue_casted)(const char *, void *); + + (void)n; + + /* should not happen */ + if (value != NULL) + return -1; + + e = TAILQ_FIRST(n->expr_deps); + if (e == NULL) + return -1; + expr = e->ptr; + + /* we use an intermediate variable to have a compiler warning + * if the prototype of cfzy_confnode_get_boolvalue changes. We + * know that that these types are compatible here. */ + get_boolvalue = cfzy_confnode_get_boolvalue; + get_boolvalue_casted = (void *)get_boolvalue; + + return cfzy_expr_eval(expr, get_boolvalue_casted, n->conftree); +} + +static struct cfzy_confnode_ops if_ops = { + .free = NULL, + .add_attr = NULL, + .close = NULL, + .str2bool = if_str2bool, + .dotconfig_write = if_dotconfig_write, + .c_hdr_write = NULL, + .get_type_str = if_get_type_str, + .set_uservalue = NULL, +}; + +/* Create a new node */ +enum cfzy_parse_return cfzy_confnode_if_new(struct cfzy_confnode *n, + const struct cfzy_token_list *tklist) +{ + struct cfzy_token *tok; + struct cfzy_expr *exp; + + tok = TAILQ_FIRST(&tklist->list); + + if (strcmp(tok->str, "if") || tklist->n_token < 2) + return NO_MATCH; + + tok = TAILQ_NEXT(tok, next); /* points to expression */ + exp = cfzy_expr_parse(tklist->linebuf + tok->offset); + if (exp == NULL) + return ERROR; + + if (cfzy_list_add_tail(n->expr_deps, exp) < 0) { + cfzy_expr_free(exp); + return ERROR; + } + + n->ops = &if_ops; + n->flags = CFZY_F_INVISIBLE | + CFZY_F_NO_NAME | + CFZY_F_NO_SET_DEPS | + CFZY_F_NO_SET_PROMPT | + CFZY_F_NO_SET_DEFAULT | + CFZY_F_NO_SET_VALUE | + CFZY_F_IS_DIR; + + return SUCCESS; +} diff --git a/tools/cfzy/libconfizery/cfzy_confnode_intconfig.c b/tools/cfzy/libconfizery/cfzy_confnode_intconfig.c new file mode 100644 index 0000000..affd57f --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_confnode_intconfig.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2010, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 "cfzy_confnode.h" + +/* Return a string identifying the node type ("config", "menuconfig", + * "choice", ...) */ +static const char *intconfig_get_type_str(const struct cfzy_confnode *n) +{ + (void)n; + return "intconfig"; +} + +static int intconfig_str2bool(const struct cfzy_confnode *n, + const char *value) +{ + char *end; + int64_t val; + + (void)n; + + /* NULL means disabled */ + if (value == NULL) + return 0; + + val = strtoll(value, &end, 0); + if (*end != 0) + return -1; + + /* XXX check range attr here */ + + return val != 0; +} + +static struct cfzy_confnode_ops intconfig_ops = { + .free = NULL, + .add_attr = NULL, /* XXX range, displayhex */ + .close = NULL, + .str2bool = intconfig_str2bool, + .dotconfig_write = NULL, + .c_hdr_write = NULL, + .get_type_str = intconfig_get_type_str, + .set_uservalue = NULL, +}; + +/* Create a new node */ +enum cfzy_parse_return cfzy_confnode_intconfig_new(struct cfzy_confnode *n, + const struct cfzy_token_list *tklist) +{ + struct cfzy_token *tok; + tok = TAILQ_FIRST(&tklist->list); + + if (strcmp(tok->str, "intconfig") || tklist->n_token != 2) + return NO_MATCH; + + tok = TAILQ_NEXT(tok, next); + n->ops = &intconfig_ops; + n->flags = 0; + + n->name = strdup(tok->str); + if (n->name == NULL) + return ERROR; + + n->default_value = strdup("0"); + if (n->default_value == NULL) { + free(n->name); + n->name = NULL; + return ERROR; + } + + return SUCCESS; +} diff --git a/tools/cfzy/libconfizery/cfzy_confnode_menu.c b/tools/cfzy/libconfizery/cfzy_confnode_menu.c new file mode 100644 index 0000000..1d8c6ce --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_confnode_menu.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2010, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 "cfzy_confnode.h" + +/* write config value in file f in a dotconfig-like manner. Return 0 + * on success. */ +static int menu_dotconfig_write(const struct cfzy_confnode *n, FILE *f) +{ + const struct cfzy_confnode *c; + + if (fprintf(f, + "#\n" + "# -- %s\n" + "#\n", n->prompt) < 0) + return -1; + + /* dump children */ + TAILQ_FOREACH(c, &n->children, child_next) { + if (cfzy_confnode_dotconfig_write(c, f) < 0) + return -1; + } + + return 0; +} + +/* this node is always enabled whatever the value */ +static int menu_str2bool(const struct cfzy_confnode *n, + const char *value) +{ + (void)n; + + /* should not happen */ + if (value != NULL) + return -1; + + return 1; +} + +/* Return a string identifying the node type ("config", "menuconfig", + * "choice", ...) */ +static const char *menu_get_type_str(const struct cfzy_confnode *n) +{ + (void)n; + return "menu"; +} + +static struct cfzy_confnode_ops menu_ops = { + .free = NULL, + .add_attr = NULL, + .close = NULL, + .str2bool = menu_str2bool, + .dotconfig_write = menu_dotconfig_write, + .c_hdr_write = NULL, + .get_type_str = menu_get_type_str, + .set_uservalue = NULL, +}; + +/* Create a new node */ +enum cfzy_parse_return cfzy_confnode_menu_new(struct cfzy_confnode *n, + const struct cfzy_token_list *tklist) +{ + struct cfzy_token *tok; + tok = TAILQ_FIRST(&tklist->list); + + if (strcmp(tok->str, "menu") || tklist->n_token != 2) + return NO_MATCH; + + tok = TAILQ_NEXT(tok, next); + n->ops = &menu_ops; + n->flags = CFZY_F_IS_DIR | + CFZY_F_NO_SET_DEPS | + CFZY_F_NO_SET_DEFAULT | + CFZY_F_NO_SET_VALUE | + CFZY_F_DOTCONF_SHOW_TITLE; + + n->name = strdup(tok->str); + if (n->name == NULL) + return ERROR; + + return SUCCESS; +} diff --git a/tools/cfzy/libconfizery/cfzy_confnode_menuconfig.c b/tools/cfzy/libconfizery/cfzy_confnode_menuconfig.c new file mode 100644 index 0000000..73af69e --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_confnode_menuconfig.c @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2010, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 "cfzy_confnode.h" + +/* Return a string identifying the node type ("config", "menuconfig", + * "choice", ...) */ +static const char *menuconfig_get_type_str(const struct cfzy_confnode *n) +{ + (void)n; + return "menuconfig"; +} + +static int menuconfig_str2bool(const struct cfzy_confnode *n, + const char *value) +{ + (void)n; + + if (!strcmp("y", value)) + return 1; + + if (!strcmp("n", value)) + return 0; + + return -1; +} + +static struct cfzy_confnode_ops menuconfig_ops = { + .free = NULL, + .add_attr = NULL, + .close = NULL, + .str2bool = menuconfig_str2bool, + .dotconfig_write = NULL, + .c_hdr_write = NULL, + .get_type_str = menuconfig_get_type_str, + .set_uservalue = NULL, +}; + +/* Create a new node */ +enum cfzy_parse_return +cfzy_confnode_menuconfig_new(struct cfzy_confnode *n, + const struct cfzy_token_list *tklist) +{ + struct cfzy_token *tok; + tok = TAILQ_FIRST(&tklist->list); + + if (strcmp(tok->str, "menuconfig") || tklist->n_token != 2) + return NO_MATCH; + + tok = TAILQ_NEXT(tok, next); + n->ops = &menuconfig_ops; + n->flags = CFZY_F_IS_DIR | CFZY_F_DOTCONF_SHOW_TITLE | + CFZY_F_VAL_IS_BOOL; + + n->name = strdup(tok->str); + if (n->name == NULL) + return ERROR; + + n->default_value = strdup("n"); + if (n->default_value == NULL) { + free(n->name); + n->name = NULL; + return ERROR; + } + + return SUCCESS; +} diff --git a/tools/cfzy/libconfizery/cfzy_confnode_ops.h b/tools/cfzy/libconfizery/cfzy_confnode_ops.h new file mode 100644 index 0000000..68ce24e --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_confnode_ops.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2013, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +/** + * @file + * Confizery configuration node specific operations + * + * This file describes specific node operations, called internally by + * cfzy_confnode module. + */ + +#ifndef _CFZY_CONFNODE_OPS_H_ +#define _CFZY_CONFNODE_OPS_H_ + +#include + +#include "cfzy_conftree_parser.h" + +struct cfzy_confnode; +struct cfzy_token_list; + +/** + * Node operation to free the specific node data. Called by + * cfzy_confnode_free(). + */ +typedef void (confnode_free_t)(struct cfzy_confnode *n); + +/** + * Specific node operation to parse a conftree line, expecting a new + * attribute. Called by cfzy_confnode_add_attr(). + */ +typedef enum cfzy_parse_return +(confnode_add_attr_t)(struct cfzy_confnode *n, + const struct cfzy_token_list *tklist, + const char *linebuf); + +/** + * Specific node operation to close a node beeing parsed. Called by + * cfzy_confnode_close(). + */ +typedef int (confnode_close_t)(struct cfzy_confnode *n); + +/** + * Specific node operation that writes the configuration in a + * file. Called by cfzy_confnode_dotconfig_write(). + */ +typedef int (confnode_dotconfig_write_t)(const struct cfzy_confnode *n, FILE *f); + +/** + * Specific node operation that writes the configuration in a C header + * file. Called by cfzy_confnode_c_hdr_write(). + */ +typedef int (confnode_c_hdr_write_t)(const struct cfzy_confnode *n, FILE *f); + +/** + * Specific node operation that returns the boolean value of the node + * given the string value. Called by cfzy_confnode_str2bool(). + */ +typedef int (confnode_str2bool_t)(const struct cfzy_confnode *n, + const char *strvalue); + +/** + * Specific node operation that returns a string identifying the node + * type. Called by cfzy_confnode_get_type_str(). + */ +typedef const char *(confnode_get_type_str_t)(const struct cfzy_confnode *n); + +/** + * Specific node operation to set user value of a configuration + * node. Called by cfzy_confnode_set_uservalue(). + */ +typedef int (confnode_set_uservalue_t)(struct cfzy_confnode *n, + const char *strvalue); + +/* all node-specific operations */ +struct cfzy_confnode_ops { + confnode_free_t *free; /**< free specific node data */ + confnode_add_attr_t *add_attr; /**< try to parse specific attr */ + confnode_close_t *close; /**< close node beeing parsed */ + confnode_dotconfig_write_t *dotconfig_write; /**< write in dotconfig */ + confnode_c_hdr_write_t *c_hdr_write; /**< write in C header file */ + confnode_str2bool_t *str2bool; /**< check strvalue and + return boolvalue */ + confnode_get_type_str_t *get_type_str; /**< return type string */ + confnode_set_uservalue_t *set_uservalue; /**< check and set user val */ +}; + +#endif /* _CFZY_CONFNODE_OPS_H_ */ diff --git a/tools/cfzy/libconfizery/cfzy_confnode_root.c b/tools/cfzy/libconfizery/cfzy_confnode_root.c new file mode 100644 index 0000000..86feb82 --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_confnode_root.c @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2010, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 "cfzy_confnode.h" + +/* this node is always enabled whatever the value */ +static int root_str2bool(const struct cfzy_confnode *n, + const char *value) +{ + (void)n; + + /* should not happen */ + if (value != NULL) + return -1; + + return 1; +} + +static struct cfzy_confnode_ops root_ops = { + .free = NULL, + .add_attr = NULL, + .close = NULL, + .str2bool = root_str2bool, + .c_hdr_write = NULL, + .dotconfig_write = NULL, + .get_type_str = NULL, + .set_uservalue = NULL, +}; + +/* Create a new root node */ +struct cfzy_confnode *cfzy_confnode_root_new(struct cfzy_conftree *conftree) +{ + struct cfzy_confnode *n; + + n = cfzy_confnode_alloc(conftree); + if (n == NULL) + return NULL; + + n->ops = &root_ops; + n->flags = CFZY_F_IS_ROOT | + CFZY_F_INVISIBLE | + CFZY_F_NO_SET_VALUE | + CFZY_F_NO_NAME | + CFZY_F_NO_SET_DEFAULT | + CFZY_F_NO_SET_DEPS; + + n->name = strdup("root"); + if (n->name == NULL) + return NULL; + + return n; +} diff --git a/tools/cfzy/libconfizery/cfzy_confnode_strconfig.c b/tools/cfzy/libconfizery/cfzy_confnode_strconfig.c new file mode 100644 index 0000000..1d0ea31 --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_confnode_strconfig.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2010, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 "cfzy_confnode.h" + +/* Return a string identifying the node type ("config", "menuconfig", + * "choice", ...) */ +static const char *strconfig_get_type_str(const struct cfzy_confnode *n) +{ + (void)n; + return "strconfig"; +} + +static int strconfig_str2bool(const struct cfzy_confnode *n, + const char *value) +{ + (void)n; + + if (value == NULL || !strcmp("", value)) + return 0; + + return 1; +} + +static struct cfzy_confnode_ops strconfig_ops = { + .free = NULL, + .add_attr = NULL, + .close = NULL, + .str2bool = strconfig_str2bool, + .dotconfig_write = NULL, + .c_hdr_write = NULL, + .get_type_str = strconfig_get_type_str, + .set_uservalue = NULL, +}; + +/* Create a new node */ +enum cfzy_parse_return +cfzy_confnode_strconfig_new(struct cfzy_confnode *n, + const struct cfzy_token_list *tklist) +{ + struct cfzy_token *tok; + tok = TAILQ_FIRST(&tklist->list); + + if (strcmp(tok->str, "strconfig") || tklist->n_token != 2) + return NO_MATCH; + + tok = TAILQ_NEXT(tok, next); + n->ops = &strconfig_ops; + n->flags = CFZY_F_QUOTE_VAL; + + n->name = strdup(tok->str); + if (n->name == NULL) + return ERROR; + + n->default_value = strdup(""); + if (n->default_value == NULL) { + free(n->name); + n->name = NULL; + return ERROR; + } + + return SUCCESS; +} diff --git a/tools/cfzy/libconfizery/cfzy_conftree.c b/tools/cfzy/libconfizery/cfzy_conftree.c new file mode 100644 index 0000000..b4c7777 --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_conftree.c @@ -0,0 +1,368 @@ +/* + * Copyright (c) 2013, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 "cfzy_log.h" +#include "cfzy_list.h" +#include "cfzy_htable.h" +#include "cfzy_confnode.h" +#include "cfzy_conftree.h" +#include "cfzy_conftree_parser.h" + +#define LOG(level, fmt, args...) \ + CFZY_LOG("conftree", level, fmt, ##args) + +/* Parse a configuration tree and add all the nodes in the + * hashtable. Return the number of nodes or -1 on error. */ +static int conftree_fill_htable(struct cfzy_conftree *conftree, + struct cfzy_confnode *n) +{ + struct cfzy_confnode *c; + int ret, count; + + if (n->name != NULL) { + if (cfzy_htable_lookup(conftree->nodes_htable, n->name) != NULL) { + LOG(ERR, "duplicate symbol <%s>\n", n->name); + return -1; + } + + if (cfzy_htable_add(conftree->nodes_htable, n->name, n) < 0) + return -1; + } + count = 1; /* count the nodes recursively */ + + TAILQ_FOREACH(c, &n->children, child_next) { + ret = conftree_fill_htable(conftree, c); + if (ret < 0) + return -1; + count += ret; + } + + return count; +} + +/* Parse a configuration tree and add all the nodes in the hashtable */ +static int conftree_generate_deplist(struct cfzy_confnode *n) +{ + struct cfzy_confnode *c; + + n->node_deps = cfzy_confnode_get_deplist(n); + if (n->node_deps == NULL) + return -1; + + TAILQ_FOREACH(c, &n->children, child_next) { + if (conftree_generate_deplist(c) < 0) + return -1; + } + + return 0; +} + +/* Parse a configuration tree and add all the nodes in the + * hashtable. Return the number of nodes in the prio_list. */ +static int conftree_fill_prio_list(struct cfzy_conftree *conftree, + struct cfzy_confnode *n) +{ + struct cfzy_list_elt *e; + struct cfzy_confnode *c; + int ok = 1; + int count = 0, ret; + + if (n->in_prio_list == 1) { + count = 1; + } + else { + /* if the node is not in prio_list, check if all + * prerequisites are in prio list, and add in the + * list */ + TAILQ_FOREACH(e, n->node_deps, next) { + c = e->ptr; + if (c->in_prio_list == 0) { + ok = 0; + break; + } + } + if (ok == 1) { + TAILQ_INSERT_TAIL(&conftree->prio_list, n, prio_next); + count = 1; + n->in_prio_list = 1; + } + } + + /* process children */ + TAILQ_FOREACH(c, &n->children, child_next) { + ret = conftree_fill_prio_list(conftree, c); + if (ret < 0) + return -1; + count += ret; + } + + return count; +} + +/* create a new configuration tree from filename */ +struct cfzy_conftree *cfzy_conftree_new(const char *filename) +{ + struct cfzy_conftree *conftree; + int node_count, prio_count, prev; + + LOG(INFO, "Open conftree <%s>\n", filename); + + conftree = malloc(sizeof(*conftree)); + if (conftree == NULL) + return NULL; + memset(conftree, 0, sizeof(*conftree)); + + conftree->path = strdup(filename); + if (conftree->path == NULL) { + cfzy_conftree_free(conftree); + return NULL; + } + + conftree->root = cfzy_confnode_root_new(conftree); + if (conftree->root == NULL) { + cfzy_conftree_free(conftree); + return NULL; + } + + /* parse the configuration tree file */ + if (cfzy_conftree_parse(filename, conftree) < 0) { + LOG(ERR, "cannot parse configuration tree\n"); + cfzy_conftree_free(conftree); + return NULL; + } + + conftree->nodes_htable = cfzy_htable_alloc(); + if (conftree->nodes_htable == NULL) { + LOG(ERR, "cannot allocate conftree htable\n"); + cfzy_conftree_free(conftree); + return NULL; + } + + /* fill a htable indexed by node name */ + node_count = conftree_fill_htable(conftree, conftree->root); + if (node_count < 0) { + LOG(ERR, "cannot fill conftree htable\n"); + cfzy_conftree_free(conftree); + return NULL; + } + conftree->count = node_count; + + /* for each node, generate the list of prerequisite nodes */ + if (conftree_generate_deplist(conftree->root) < 0) { + LOG(ERR, "cannot generate dep list\n"); + cfzy_conftree_free(conftree); + return NULL; + } + + /* generate a list ordered by node priority: if Node1 depends + * on Node2, then Node1 is located after Node2 in the list. */ + TAILQ_INIT(&conftree->prio_list); + prev = 0; + while (1) { + prio_count = conftree_fill_prio_list(conftree, conftree->root); + if (prio_count < 0) { + LOG(ERR, "cannot generate prio list\n"); + cfzy_conftree_free(conftree); + return NULL; + } + if (prio_count == node_count) + break; + if (prio_count == prev) { + LOG(ERR, "circular dependency in conf tree\n"); + cfzy_conftree_free(conftree); + return NULL; + } + prev = prio_count; + } + + return conftree; +} + +/* free a configuration tree */ +void cfzy_conftree_free(struct cfzy_conftree *conftree) +{ + if (conftree->path != NULL) + free(conftree->path); + if (conftree->nodes_htable != NULL) + cfzy_htable_free(conftree->nodes_htable, NULL); + if (conftree->root != NULL) + cfzy_confnode_free(conftree->root); + free(conftree); +} + +int cfzy_conftree_dump(const struct cfzy_conftree *conftree, + const char *filename) +{ + FILE *f; + + f = fopen(filename, "w"); + if (f == NULL) { + LOG(ERR, "Cannot open <%s>: %s\n", filename, strerror(errno)); + return -1; + } + + if (cfzy_confnode_dump(conftree->root, f, + CFZY_DUMP_MODE_FULL_RECURSIVE) < 0) { + LOG(ERR, "Cannot dump confnode tree\n"); + fclose(f); + return -1; + } + + fclose(f); + return 0; +} + +int cfzy_conftree_evaluate(struct cfzy_conftree *conftree) +{ + struct cfzy_confnode *n; + + TAILQ_FOREACH(n, &conftree->prio_list, prio_next) { + if (cfzy_confnode_evaluate(n) < 0) { + LOG(ERR, "Cannot evaluate conftree\n"); + return -1; + } + LOG(DEBUG, "eval %s -> %s\n", n->name, n->effective_value); + } + + return 0; +} + +/* same than strcmp() but allow string to be NULL */ +static int strcmp2(const char *s1, const char *s2) +{ + if (s1 == NULL && s2 == NULL) + return 0; + if (s1 == NULL) + return -1; + if (s2 == NULL) + return 1; + return strcmp(s1, s2); +} + +static int __filter(struct cfzy_confnode *n, struct cfzy_list_head *node_list, + int flags) +{ + int add_it = 0; + struct cfzy_confnode *c; + + if ((flags & CFZY_FILTER_F_USR_UNSET) && n->user_value == NULL) + add_it = 1; + else if ((flags & CFZY_FILTER_F_EFF_UNSET) && n->effective_value == NULL) + add_it = 1; + else if ((flags & CFZY_FILTER_F_USR_CHANGED) && + strcmp2(n->user_value, n->old_user_value)) + add_it = 1; + else if ((flags & CFZY_FILTER_F_EFF_CHANGED) && + strcmp2(n->effective_value, n->old_effective_value)) + add_it = 1; + + if (add_it == 1) { + if (cfzy_list_add_tail(node_list, n) < 0) { + LOG(ERR, "cannot add node in list\n"); + return -1; + } + } + + /* children nodes */ + TAILQ_FOREACH(c, &n->children, child_next) { + if (__filter(c, node_list, flags) < 0) + return -1; + } + + return 0; +} + +/* Get a list of nodes matching conditions (for instance the nodes + * that have no user value) */ +struct cfzy_list_head * +cfzy_conftree_filter(struct cfzy_conftree *conftree, int flags) +{ + struct cfzy_list_head *node_list = NULL; + + node_list = cfzy_list_alloc(); + if (node_list == NULL) { + LOG(ERR, "cannot allocate node list\n"); + return NULL; + } + + if (__filter(conftree->root, node_list, flags) < 0) { + LOG(ERR, "cannot filter nodes\n"); + cfzy_list_free(node_list, NULL); + return NULL; + } + + return node_list; +} + +static int __save_values(struct cfzy_confnode *n) +{ + struct cfzy_confnode *c; + + /* duplicate user value */ + if (n->old_user_value != NULL) { + free(n->old_user_value); + n->old_user_value = NULL; + } + if (n->user_value != NULL) { + n->old_user_value = strdup(n->user_value); + if (n->old_user_value == NULL) { + LOG(ERR, "cannot duplicate value\n"); + return -1; + } + } + + /* duplicate effective value */ + if (n->old_effective_value != NULL) { + free(n->old_effective_value); + n->old_effective_value = NULL; + } + if (n->effective_value != NULL) { + n->old_effective_value = strdup(n->effective_value); + if (n->old_effective_value == NULL) { + LOG(ERR, "cannot duplicate value\n"); + return -1; + } + } + + /* do the same on children */ + TAILQ_FOREACH(c, &n->children, child_next) { + if (__save_values(c) < 0) + return -1; + } + + return 0; +} + +int cfzy_conftree_save_values(struct cfzy_conftree *conftree) +{ + return __save_values(conftree->root); +} diff --git a/tools/cfzy/libconfizery/cfzy_conftree.h b/tools/cfzy/libconfizery/cfzy_conftree.h new file mode 100644 index 0000000..ea77c1e --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_conftree.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2013, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +/** + * @file + * Confizery configuration tree + * + * This module defines a structure cfzy_conftree that stores the full + * configuration tree (composed of cfzy_confnode structures), and some + * meta data like the number of nodes in the tree, a hash table used + * to do a node lookup by its name, the path of the configuration tree + * file, ... + */ + +#ifndef _CFZY_CONFTREE_H_ +#define _CFZY_CONFTREE_H_ + +#include "cfzy_confnode.h" + +struct cfzy_htable; + +/** + * Structure storing the config tree + */ +struct cfzy_conftree { + char *path; /**< path of conftree file */ + struct cfzy_confnode *root; /**< pointer to root node */ + int count; /**< number of nodes */ + struct cfzy_htable *nodes_htable; /**< htable of nodes, indexed by name */ + struct cfzy_confnode_list prio_list; /**< ordered list of nodes */ +}; + +/** + * Create a new configuration tree + * + * Parse a configuration tree and allocate a new conftree + * structure. The structure must be freed with cfzy_conftree_free(). + * + * @param filename + * name of the configuration tree file + * @return + * the allocated conftree structure, or NULL on error + */ +struct cfzy_conftree *cfzy_conftree_new(const char *filename); + +/** + * Free a configuration tree structure + * + * Free a configuration tree structure previously allocated with + * cfzy_conftree_new(). + * + * @param conftree + * the configuration tree structure to be freed + */ +void cfzy_conftree_free(struct cfzy_conftree *conftree); + +/** + * Dump a configuration tree structure in a file + * + * @param conftree + * the configuration tree structure to be freed + * @param filename + * file where conf should be dumped + * @return + * 0 on success, negative on error + */ +int cfzy_conftree_dump(const struct cfzy_conftree *conftree, + const char *filename); + +/** + * Evaluate a configuration tree structure + * + * Parse all nodes following priority list and set the effective_value + * field by calling cfzy_confnode_evaluate() which takes care of + * default values, user values, and dependencies. + * + * @param conftree + * the configuration tree structure + * @return + * 0 on success, negative on error + */ +int cfzy_conftree_evaluate(struct cfzy_conftree *conftree); + +/** + * Save values of configuration tree + * + * For each node, copy the effective_value field in + * old_effective_value and the user_value field in old_user_value. + * These values can be used later with cfzy_conftree_filter() to + * compare the old values with the new ones. + * + * @param conftree + * the configuration tree structure + * @return + * 0 on success, negative on error + */ +int cfzy_conftree_save_values(struct cfzy_conftree *conftree); + +/* flags to be used with cfzy_conftree_filter() */ +#define CFZY_FILTER_F_USR_UNSET 0x01 +#define CFZY_FILTER_F_EFF_UNSET 0x02 +#define CFZY_FILTER_F_USR_CHANGED 0x04 +#define CFZY_FILTER_F_EFF_CHANGED 0x08 + +/** + * Get a list of nodes matching conditions + * + * Return the list of nodes in configuration tree that match + * conditions. Example: cfzy_conftree_filter(contree, + * CFZY_FILTER_F_USR_UNSET) will return all nodes that have no user + * value (set to NULL). + * + * @param conftree + * the configuration tree structure + * @param flags + * list of conditions (CFZY_FILTER_F_*) + * @return + * 0 on success, negative on error + */ +struct cfzy_list_head * +cfzy_conftree_filter(struct cfzy_conftree *conftree, int flags); + +#endif /* _CFZY_CONFTREE_H_ */ diff --git a/tools/cfzy/libconfizery/cfzy_conftree_parser.c b/tools/cfzy/libconfizery/cfzy_conftree_parser.c new file mode 100644 index 0000000..a282e83 --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_conftree_parser.c @@ -0,0 +1,790 @@ +/* + * Copyright (c) 2013, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 + +#include "cfzy_log.h" +#include "cfzy_string.h" +#include "cfzy_expr.h" +#include "cfzy_htable.h" +#include "cfzy_conftree.h" +#include "cfzy_confnode.h" +#include "cfzy_conftree_parser.h" + +#define LOG(level, fmt, args...) \ + CFZY_LOG("conftree_parser", level, fmt, ##args) + +/* + * Structure that stores the state of the configuration tree parser. + */ +struct conftree_parse_status { + const char *filename; /* name of file beeing parsed */ + FILE *f; /* pointer to file beeing parsed */ + int linenum; /* current line number in file */ + char *curline; /* pointer to current line, or + NULL if no current line */ + struct cfzy_token_list *tklist; /* pointer to current token + list of NULL if no current line */ + struct cfzy_confnode *parent; /* current parent node */ +}; + +static int __cfzy_conftree_parse(const char *filename, + struct cfzy_confnode **pparent); + +/* return true if line is empty or contains only spaces/comments */ +static int line_is_empty(const char *buf) +{ + while (*buf != '\0' && *buf != '#') { + if (!isspace(*buf)) + return 0; + buf++; + } + return 1; +} + +/* + * Add a token in the tklist structure given as argument. + * + * 'buf': pointer to the beginning of line buffer. + * 'offset': offset in the line where start parsing + * + * Return the number of consumed bytes if a token is found + * Return 0 if there is no more token + * Return -1 on error + */ +static int +append_token(struct cfzy_token_list *tklist, const char *buf, int offset) +{ + struct cfzy_token *tok; + const char *s; + unsigned len, space_len = 0; + char *retbuf; + + /* skip spaces */ + s = buf + offset; + while (*s != '\0' && isspace(*s)) + s++; + if (*s == '\0' || *s == '#') + return 0; + + tok = malloc(sizeof(struct cfzy_token)); + if (tok == NULL) { + LOG(ERR, "not enough memory\n"); + return -1; + } + + space_len = s - buf - offset; + tok->offset = s - buf; + + /* if it's a quote, unquote the string */ + if (*s == '"' || *s == '\'') { + retbuf = cfzy_string_unquote(s, &len); + if (retbuf == NULL) + goto free; + } + /* else wait a space */ + else { + retbuf = strdup(s); + if (retbuf == NULL) { + LOG(ERR, "not enough memory\n"); + goto free; + } + + for (len = 0; + s[len] != '\0' && !isspace(s[len]) && s[len] != '#'; + len ++) + ; + + retbuf[len] = '\0'; + } + + /* fill token structure and append it in tklist */ + tok->str = retbuf; + TAILQ_INSERT_TAIL(&tklist->list, tok, next); + tklist->n_token ++; + + return len + space_len; + + free: + free(tok); + return -1; +} + +/* free a token list previously allocated with tokenize() */ +static void free_token_list(struct cfzy_token_list *tklist) +{ + struct cfzy_token *tok; + + while ((tok = TAILQ_FIRST(&tklist->list)) != NULL) { + TAILQ_REMOVE(&tklist->list, tok, next); + free(tok->str); + free(tok); + } + free(tklist); +} + +/* + * Parse the line contained in buffer 'linebuf'. This function fills a + * tklist structure, composed of several tokens. + * Return the tklist pointer on success or NULL on error. + */ +static struct cfzy_token_list *tokenize(const char *linebuf) +{ + struct cfzy_token_list *tklist; + int ret, offset = 0; + + tklist = malloc(sizeof(struct cfzy_token_list)); + if (tklist == NULL) { + LOG(ERR, "not enough memory\n"); + return NULL; + } + + memset(tklist, 0, sizeof(struct cfzy_token_list)); + TAILQ_INIT(&tklist->list); + + /* read tokens from buf and append it to tklist structure */ + do { + ret = append_token(tklist, linebuf, offset); + if (ret < 0) { + free_token_list(tklist); + return NULL; + } + offset += ret; + } while (ret != 0); + + tklist->linebuf = linebuf; + return tklist; +} + +/* duplicate buf, replacing variables like $(VAR) by their values */ +char *replace_variables(const char *buf) +{ + char *s, *buf_dup, *var; + const char *start; + char *out = NULL; + char c; + + buf_dup = strdup(buf); + if (buf_dup == NULL) { + LOG(ERR, "not enough memory\n"); + return NULL; + } + + s = buf_dup; + + for ( ; s[0] != '\0' ; s++) { + + start = s; + + /* wait a "$(" */ + for ( ; s[0] != '\0' && (s[0] != '$' || s[1] != '('); s++); + c = s[0]; + s[0] = '\0'; + if (cfzy_string_asprintf(&out, "%s", start) < 0) + goto fail; + + if (c == '\0') + break; + + s += 2; + start = s; + + for ( ; s[0] != '\0' && s[0] != ')'; s++); + c = s[0]; + if (c == '\0') + goto fail; + + s[0] = '\0'; + var = getenv(start); + + if (var != NULL) { + if (cfzy_string_asprintf(&out, var) < 0) + goto fail; + } + + } + + free(buf_dup); + return out; + + fail: + free(buf_dup); + if (out != NULL) + free(out); + return NULL; +} + +/* + * Fill state structure with current line. If state->curline is + * already set, the function does nothing, else it will read a new + * line in the file and tokenize it. Return 0 on success, and -1 on + * error. If state->curline is still NULL after a call to this + * function, we reached the end of the file. + */ +static int get_curline(struct conftree_parse_status *state) +{ + char buf[BUFSIZ]; + int line_len; + char *line; + + /* nothing to do */ + if (state->curline != NULL) + return 0; + + state->curline = fgets(buf, sizeof(buf), state->f); + + /* end of file */ + if (state->curline == NULL) { + LOG(DEBUG, "EOF\n"); + return 0; + } + + state->linenum ++; + line_len = strlen(state->curline); + + /* if line ends with '\', get another line */ + while (line_len >= 2 && + state->curline[line_len - 1] == '\n' && + state->curline[line_len - 2] == '\\') { + if (line_len >= (int)sizeof(buf) - 1) + break; + + line = fgets(buf + line_len - 2, + sizeof(buf) - line_len - 2, state->f); + if (line == NULL) /* EOF */ + break; + + line_len = strlen(state->curline); + state->linenum ++; + } + + state->curline = replace_variables(buf); + if (state->curline == NULL) { + LOG(ERR, "Cannot eval variables\n"); + return -1; + } + + state->tklist = tokenize(state->curline); + if (state->tklist == NULL) { + LOG(ERR, "Cannot parse line\n"); + free(state->curline); + return -1; + } + + return 0; +} + +/* + * This function is called when we processed the current line. It will + * free allocated memory (token list) and set state->curline to NULL. + */ +static void eat_curline(struct conftree_parse_status *state) +{ + if (state->curline != NULL) { + free(state->curline); + state->curline = NULL; + } + if (state->tklist != NULL) { + free_token_list(state->tklist); + state->tklist = NULL; + } +} + +/* Try to parse node help. Return NO_MATCH if the current line does + * not match the 'help' token, SUCCESS if help is successfully parsed, + * or ERROR on error. */ +static enum cfzy_parse_return +parse_help(struct cfzy_confnode *n, struct conftree_parse_status *state) +{ + struct cfzy_token_list *tklist; + struct cfzy_token *tok; + char *buf; + + if (get_curline(state) < 0) + return ERROR; + + buf = state->curline; + + /* EOF */ + if (buf == NULL) + return NO_MATCH; + + /* we want 1 token */ + tklist = state->tklist; + if (tklist->n_token != 1) + return NO_MATCH; + + /* we want a "help" token */ + tok = TAILQ_FIRST(&tklist->list); + if (strcmp(tok->str, "help") && strcmp(tok->str, "---help---")) + return NO_MATCH; + + LOG(INFO, "parse help\n"); + eat_curline(state); + + /* parse help content */ + while (1) { + + /* read next line */ + if (get_curline(state) < 0) + return ERROR; + + buf = state->curline; + + /* EOF */ + if (buf == NULL) + return SUCCESS; + + /* skip empty lines */ + if (line_is_empty(buf)) { + eat_curline(state); + continue; + } + + /* if line does not start with space, this is this end + of help parsing */ + if (!isspace(*buf)) + return SUCCESS; + + /* append string in buffer, stripping starting spaces */ + tok = TAILQ_FIRST(&state->tklist->list); + if (cfzy_string_asprintf(&n->help, "%s", + buf + tok->offset) < 0) + return ERROR; + + eat_curline(state); + } +} + +/* Try to parse the "source" command. Return NO_MATCH if the current + * line does not match the "source" token, SUCCESS if the file is + * successfully sourced, or ERROR on error. */ +static enum cfzy_parse_return source_file(struct conftree_parse_status *state) +{ + struct cfzy_token_list *tklist; + struct cfzy_token *tok; + + if (get_curline(state) < 0) + return ERROR; + + /* EOF */ + if (state->curline == NULL) + return NO_MATCH; + + tklist = state->tklist; + tok = TAILQ_FIRST(&tklist->list); + + /* syntax is: source FILE_NAME */ + if (tklist->n_token != 2 || strcmp(tok->str, "source")) + return NO_MATCH; + + tok = TAILQ_NEXT(tok, next); + LOG(INFO, "source <%s>\n", tok->str); + + /* parse new file */ + if (__cfzy_conftree_parse(tok->str, &state->parent) < 0) { + LOG(ERR, "error in sourced file\n"); + return ERROR; + } + + eat_curline(state); + return SUCCESS; +} + +/* Try to create a new node command ("config", "menuconfig", "choice", + * "if", ...). Return NO_MATCH if the current line is not a new node + * command, SUCCESS if a new node is created, or ERROR on error. This + * function does not parse the attributes of the node but only the + * first line. */ +static enum cfzy_parse_return +new_node(struct conftree_parse_status *state, struct cfzy_confnode **nodep) +{ + struct cfzy_token_list *tklist; + enum cfzy_parse_return ret; + + if (get_curline(state) < 0) + return ERROR; + + /* EOF */ + if (state->curline == NULL) + return NO_MATCH; + + tklist = state->tklist; + + *nodep = cfzy_confnode_alloc(state->parent->conftree); + if (*nodep == NULL) { + LOG(ERR, "cannot allocate node\n"); + return ERROR; + } + (*nodep)->parent = state->parent; + + ret = cfzy_confnode_choice_new(*nodep, tklist); + if (ret != NO_MATCH) + return ret; + ret = cfzy_confnode_choiceconfig_new(*nodep, tklist); + if (ret != NO_MATCH) + return ret; + ret = cfzy_confnode_comment_new(*nodep, tklist); + if (ret != NO_MATCH) + return ret; + ret = cfzy_confnode_config_new(*nodep, tklist); + if (ret != NO_MATCH) + return ret; + ret = cfzy_confnode_if_new(*nodep, tklist); + if (ret != NO_MATCH) + return ret; + ret = cfzy_confnode_intconfig_new(*nodep, tklist); + if (ret != NO_MATCH) + return ret; + ret = cfzy_confnode_menu_new(*nodep, tklist); + if (ret != NO_MATCH) + return ret; + ret = cfzy_confnode_menuconfig_new(*nodep, tklist); + if (ret != NO_MATCH) + return ret; + ret = cfzy_confnode_strconfig_new(*nodep, tklist); + if (ret != NO_MATCH) + return ret; + + cfzy_confnode_free(*nodep); + return NO_MATCH; +} + +/* Try to create a new node with all its attributes and help. Return + * NO_MATCH if the current line is not a new node command, SUCCESS if + * a new node is created, or ERROR on error. */ +static enum cfzy_parse_return +parse_confnode(struct conftree_parse_status *state) +{ + char *buf; + struct cfzy_confnode *n; + enum cfzy_parse_return ret; + struct cfzy_token_list *tklist; + + if (get_curline(state) < 0) + return ERROR; + + buf = state->curline; + + /* EOF */ + if (buf == NULL) + return NO_MATCH; + + /* line must not start with a space */ + if (isspace(*buf)) + return NO_MATCH; + + /* if it's a new node command */ + ret = new_node(state, &n); + if (ret == ERROR || ret == NO_MATCH) + return ret; + + /* save parsing infos */ + n->filename = strdup(state->filename); + if (n->filename == NULL) { + cfzy_confnode_free(n); + return ERROR; + } + n->linenum = state->linenum; + + TAILQ_INSERT_TAIL(&state->parent->children, n, child_next); + eat_curline(state); + + /* parse node attributes */ + while (1) { + + /* fill state structure with current or next line */ + if (get_curline(state) < 0) + return ERROR; + + buf = state->curline; + tklist = state->tklist; + + /* EOF */ + if (buf == NULL) + return SUCCESS; + + /* skip empty lines */ + if (line_is_empty(buf)) { + eat_curline(state); + continue; + } + + /* if line does not start with space, this node is + * parsed, break */ + if (!isspace(*buf)) + break; + + /* check if it's a node attribute */ + ret = cfzy_confnode_add_attr(n, tklist); + if (ret == ERROR) + return ERROR; + if (ret == SUCCESS) { + eat_curline(state); + continue; + } + + /* check if it's a help indication */ + ret = parse_help(n, state); + if (ret == ERROR) + return ERROR; + if (ret == SUCCESS) + break; + + /* the attribute is invalid */ + LOG(ERR, "invalid node attribute\n"); + return ERROR; + } + + /* enter in a new node */ + if (n->flags & CFZY_F_IS_DIR) + state->parent = n; + + return SUCCESS; +} + +/* Try to match the command that closes the current parent node, if + * it's a directory. Return NO_MATCH if the line does not match this + * command, SUCCESS if the parent node is closed (in this case, + * state->parent is updated), or ERROR on error. */ +static enum cfzy_parse_return +close_dir(struct conftree_parse_status *state) +{ + struct cfzy_token_list *tklist; + struct cfzy_token *tok; + struct cfzy_confnode *n; + + n = state->parent; + if ((n->flags & CFZY_F_IS_DIR) == 0) + return NO_MATCH; + + /* fill state structure with current or next line */ + if (get_curline(state) < 0) + return ERROR; + + /* EOF */ + if (state->curline == NULL) + return NO_MATCH; + + tklist = state->tklist; + if (tklist->n_token != 1) + return NO_MATCH; + + /* token must be "endchoice", "endmenu", "endif", ... */ + tok = TAILQ_FIRST(&tklist->list); + if (strncmp(tok->str, "end", 3)) + return NO_MATCH; + if (strcmp(tok->str + 3, cfzy_confnode_get_type_str(n))) + return NO_MATCH; + if (cfzy_confnode_close(n) < 0) { + LOG(ERR, "cannot close node\n"); + return ERROR; + } + + state->parent = n->parent; + return SUCCESS; +} + +/* + * Parse the configuration tree by reading the file stored in the + * 'state' structure. Return SUCCESS or ERROR. + */ +static enum cfzy_parse_return parse_conftree(struct conftree_parse_status *state) +{ + enum cfzy_parse_return ret; + char *buf; + + /* browse all lines of the file */ + while (1) { + + /* fill state structure with current or next line */ + if (get_curline(state) < 0) + return ERROR; + + buf = state->curline; + + /* EOF */ + if (buf == NULL) + break; + + LOG(DEBUG, "parent=%s: %s", + cfzy_confnode_name(state->parent), buf); + + /* skip empty / comments lines */ + if (line_is_empty(buf)) { + eat_curline(state); + continue; + } + + /* if the line does not start with a space, syntax error */ + if (isspace(*buf)) { + LOG(ERR, "line starts with space\n"); + return ERROR; + } + + /* if it's a source command, parse the new file */ + ret = source_file(state); + if (ret == ERROR) + return ERROR; + if (ret == SUCCESS) + continue; + + /* if the command closes a menu, choice, ... */ + ret = close_dir(state); + if (ret == ERROR) + return ERROR; + if (ret == SUCCESS) { + eat_curline(state); + continue; + } + + /* parse new node */ + ret = parse_confnode(state); + if (ret == ERROR) + return ERROR; + if (ret == SUCCESS) + continue; + + /* invalid command */ + LOG(ERR, "invalid command\n"); + return ERROR; + } + + return SUCCESS; +} + +/* + * Parse the configuration tree by reading "filename", knowing that + * the current parent node is pointed by pparent (which can be + * modified at the end of the function). This function will fill open + * the file, and fill a state structure for the parser, then call + * parse_conftree(). Return 0 on success and -1 on error. + */ +static int +__cfzy_conftree_parse(const char *filename, struct cfzy_confnode **pparent) +{ + struct conftree_parse_status state; + FILE *f = NULL; + char old_cwd_buf[PATH_MAX]; + char *cwd_buf = NULL, *cwd_ptr = NULL, *old_cwd = NULL; + int ret; + + memset(&state, 0, sizeof(state)); + + old_cwd = getcwd(old_cwd_buf, sizeof(old_cwd_buf)); + if (old_cwd == NULL) { + LOG(ERR, "getcwd failed: %s\n", strerror(errno)); + goto fail; + } + + f = fopen(filename, "r"); + if (f == NULL) { + LOG(ERR, "cannot find file <%s>\n", filename); + goto fail; + } + + cwd_buf = strdup(filename); + if (cwd_buf == NULL) { + LOG(ERR, "not enough memory\n"); + goto fail; + } + cwd_ptr = dirname(cwd_buf); + ret = chdir(cwd_ptr); + if (ret < 0) { + LOG(ERR, "chdir failed: %s\n", strerror(errno)); + goto fail; + } + + state.filename = filename; + state.f = f; + state.linenum = 0; + state.parent = *pparent; + state.curline = NULL; + + /* browse all lines of the file */ + ret = parse_conftree(&state); + if (ret != SUCCESS) { + /* node will be freed by caller (cfzy_conftree_new) */ + LOG(ERR, "parse error at %s:%d\n", filename, state.linenum); + LOG(ERR, ">>> %s", state.curline); + goto fail; + } + + eat_curline(&state); /* will free token_list */ + fclose(f); + free(cwd_buf); + if (chdir(old_cwd) < 0) { + LOG(ERR, "chdir back to <%s> failed: %s\n", + old_cwd, strerror(errno)); + return -1; + } + + *pparent = state.parent; + return 0; + + fail: + if (state.f != NULL) + eat_curline(&state); /* will free token_list */ + if (f != NULL) + fclose(f); + if (cwd_buf != NULL) + free(cwd_buf); + if (old_cwd != NULL) + if (chdir(old_cwd) < 0) + LOG(ERR, "chdir back to <%s> failed: %s\n", + old_cwd, strerror(errno)); + + return -1; +} + +/* External api for parsing a new configuration tree file. Return 0 on + * success and -1 on error. */ +int cfzy_conftree_parse(const char *filename, struct cfzy_conftree *conftree) +{ + int ret; + struct cfzy_confnode *parent = conftree->root; + + ret = __cfzy_conftree_parse(filename, &parent); + if (ret < 0) + return ret; + + if (parent != conftree->root) { + LOG(ERR, "Node <%s> not closed properly\n", + cfzy_confnode_name(parent)); + return -1; + } + + return 0; +} diff --git a/tools/cfzy/libconfizery/cfzy_conftree_parser.h b/tools/cfzy/libconfizery/cfzy_conftree_parser.h new file mode 100644 index 0000000..8e17fbf --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_conftree_parser.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2013, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +/** + * @file + * Confizery configuration tree parser + * + * This modules provides an API to parse the configuration tree file + * (equivalent to Kconfig file in linux terminology) which describes + * the structure of the tree, the option list, the dependencies, the + * default values... + */ + + +#ifndef _CFZY_CONFTREE_PARSER_H_ +#define _CFZY_CONFTREE_PARSER_H_ + +#include + +struct cfzy_conftree; + +/** + * Return value for some parsing functions. + */ +enum cfzy_parse_return { + SUCCESS, + NO_MATCH, + ERROR, +}; + +/** + * This is the structure defining a token. It is used by the + * configuration tree parser. A token is either a word or a string + * surrounded by double quotes. + */ +struct cfzy_token { + TAILQ_ENTRY(cfzy_token) next; /**< pointer to next token */ + char *str; /**< string */ + unsigned offset; /**< offset in line */ +}; + +/** + * A list of token, and a pointer to the initial line buffer + */ +struct cfzy_token_list { + TAILQ_HEAD(, cfzy_token) list; /**< list of tokens */ + unsigned n_token; /**< number of tokens */ + const char *linebuf; /**< the line buffer */ +}; + +/** + * Parse configuration tree file and fill the conftree + * + * This function is called by cfzy_conftree_new() to fill the newly + * allocated tree. + * + * @param filename + * path to configuration tree file + * @param conftree + * an empty configuration tree + * @return + * 0 on success, or negative on error + */ +int cfzy_conftree_parse(const char *filename, struct cfzy_conftree *conftree); + +#endif /* _CFZY_CONFTREE_PARSER_H_ */ diff --git a/tools/cfzy/libconfizery/cfzy_dotconfig.c b/tools/cfzy/libconfizery/cfzy_dotconfig.c new file mode 100644 index 0000000..4e86934 --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_dotconfig.c @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2013, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 "cfzy_log.h" +#include "cfzy_htable.h" +#include "cfzy_string.h" +#include "cfzy_file.h" +#include "cfzy_conftree.h" +#include "cfzy_confnode.h" + +#define LOG(level, fmt, args...) \ + CFZY_LOG("dotconfig", level, fmt, ##args) + +/* Allocate and return string containing the value of the + * option. Buffer must point to the option (after the '=' in the + * .config), and it can be quoted. The string must be freed by the + * user. */ +static char *dotconfig_get_optval(const char *buf) +{ + const char *s; + unsigned i, len; + char *retbuf; + + /* skip spaces */ + s = buf; + while (*s != '\0' && isspace(*s)) + s++; + if (*s == '\0' || *s == '#') + return 0; + + /* if it's a quote, unquote the string */ + if (*s == '"' || *s == '\'') { + retbuf = cfzy_string_unquote(s, &len); + } + else { + retbuf = strdup(s); + if (retbuf == NULL) + return NULL; + + for (i = 0; s[i] != '\0' && s[i] != '\n' && s[i] != '#'; i ++) + ; + + retbuf[i] = '\0'; + } + return retbuf; +} + +/* parse a line of .config: fill the uservalue of nodes in the + * conftree structure */ +static int dotconfig_parse_line(struct cfzy_conftree *conftree, const char *line) +{ + struct cfzy_confnode *n; + char buf[BUFSIZ]; + char *nodename, *s; + char *optval; + + /* copy line in a writable buffer */ + buf[sizeof(buf) - 1] = '\0'; + strncpy(buf, line, sizeof(buf) - 1); + s = buf; + + /* skip spaces */ + while (*s != '\0' && isspace(*s)) + s++; + + /* nothing to do, empty line */ + if (*s == '\0') + return 0; + + /* skip the first "#", expecting "is not set" */ + if (*s == '#') { + s++; + while (*s != '\0' && isspace(*s)) + s++; + + /* nothing to do, it's a comment */ + if (*s == '\0') + return 0; + + /* get node name, ignore invalid names */ + if (strncmp("CONFIG_", s, strlen("CONFIG_"))) + return 0; + nodename = s + strlen("CONFIG_"); + s = nodename; + + /* skip spaces */ + while (*s != '\0' && !isspace(*s)) + s++; + + /* nothing to do, it's a comment */ + if (*s == '\0') + return 0; + + *s = '\0'; + s ++; + + /* should end with "is not set", else it's a comment */ + if (strcmp(s, "is not set\n")) + return 0; + + optval = strdup("n"); + if (optval == NULL) + return -1; + } + else { + /* get node name, ignore invalid names */ + if (strncmp("CONFIG_", s, strlen("CONFIG_"))) + return -1; + nodename = s + strlen("CONFIG_"); + + while (*s != '\0' && *s != '=') + s++; + /* invalid line */ + if (*s != '=') + return -1; + + *s = '\0'; + optval = dotconfig_get_optval(s + 1); + if (optval == NULL) + return -1; + } + + /* find the node in the conf htable */ + n = cfzy_htable_lookup(conftree->nodes_htable, nodename); + if (n == NULL) { + LOG(NOTICE, "ignore unknown symbol <%s>\n", nodename); + free(optval); + return 0; + } + + /* check that value is valid */ + if (cfzy_confnode_str2bool(n, optval) < 0) { + LOG(ERR, "invalid option value %s=<%s>\n", nodename, optval); + free(optval); + return -1; + } + + /* set value in node */ + LOG(DEBUG, "%s=<%s>\n", nodename, optval); + if (cfzy_confnode_set_uservalue(n, optval) < 0) { + LOG(ERR, "cannot set value %s=<%s>\n", nodename, optval); + free(optval); + return -1; + } + + free(optval); + return 0; +} + +/* parse .config */ +int cfzy_dotconfig_read(struct cfzy_conftree *conftree, const char *filename) +{ + FILE *f; + char buf[BUFSIZ]; + int err = 0; + + LOG(INFO, "dotconfig read <%s>\n", filename); + + f = fopen(filename, "r"); + if (f == NULL) { + LOG(ERR, "cannot find file <%s>\n", filename); + return -1; + } + while (fgets(buf, sizeof(buf), f) != NULL) { + + if (dotconfig_parse_line(conftree, buf) < 0) { + err = -1; + break; + } + } + fclose(f); + + return err; +} + +/* write .config */ +int cfzy_dotconfig_write(const struct cfzy_conftree *conftree, + const char *filename) +{ + FILE *f, *tmp_f; + const struct cfzy_confnode *c; + int ret = 0; + + LOG(INFO, "open old dotconfig <%s>\n", filename); + + /* open old file in read mode */ + + f = fopen(filename, "r"); + if (f == NULL && errno != ENOENT) { + printf("cannot open file <%s>: %s\n", + filename, strerror(errno)); + return -1; + } + + /* old file exists */ + if (f != NULL) { + + /* write C header in temp file */ + tmp_f = tmpfile(); + if (tmp_f == NULL) { + printf("cannot open temp file\n"); + return -1; + } + + /* dump all the children of root node */ + TAILQ_FOREACH(c, &conftree->root->children, child_next) { + ret = cfzy_confnode_dotconfig_write(c, tmp_f); + if (ret < 0) + break; + } + + /* files are the same, nothing to do */ + if (cfzy_file_compare(f, tmp_f) == 0) { + LOG(INFO, "already up to date: <%s>\n", filename); + fclose(tmp_f); + fclose(f); + return 0; + } + + fclose(tmp_f); + fclose(f); + } + + /* reopen the file in write mode */ + + LOG(INFO, "write new C header <%s>\n", filename); + + f = fopen(filename, "w"); + if (f == NULL) { + LOG(ERR, "cannot open file <%s>\n", filename); + return -1; + } + + /* dump all the children of root node */ + TAILQ_FOREACH(c, &conftree->root->children, child_next) { + ret = cfzy_confnode_dotconfig_write(c, f); + if (ret < 0) + break; + } + + fclose(f); + + return ret; +} diff --git a/tools/cfzy/libconfizery/cfzy_dotconfig.h b/tools/cfzy/libconfizery/cfzy_dotconfig.h new file mode 100644 index 0000000..53dd8dd --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_dotconfig.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2013, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +/** + * @file + * Confizery dotconfig parsing/generation + * + * This modules provides an API to parse or generate a .config file + * from a configuration tree. + */ + +#ifndef _CFZY_DOTCONFIG_H_ +#define _CFZY_DOTCONFIG_H_ + +/** + * Write configuration in a .config file + * + * Write the configuration values (.config like) in a file. This file + * is designed to be sourced in a shell script or in a makefile. + * + * If the file already exists and has the same content, nothing is + * written in it. + * + * @param conftree + * the configuration tree + * @param filename + * name of the output file + * @return + * 0 on success, negative on error + */ +int cfzy_dotconfig_write(const struct cfzy_conftree *conftree, + const char *filename); + +/** + * Read and parse a dotconfig configuration file + * + * Read the configuration values (.config like) from a file, and set + * the values in the configuration tree. + * + * @param conftree + * the configuration tree + * @param filename + * name of the output file + * @return + * 0 on success, negative on error + */ +int cfzy_dotconfig_read(struct cfzy_conftree *conftree, const char *filename); + +#endif diff --git a/tools/cfzy/libconfizery/cfzy_expr.c b/tools/cfzy/libconfizery/cfzy_expr.c new file mode 100644 index 0000000..d911e22 --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_expr.c @@ -0,0 +1,571 @@ +/* + * Copyright (c) 2013, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 "cfzy_list.h" +#include "cfzy_expr.h" + +/* #define DEBUG */ + +#ifdef DEBUG +#define debug_printf(args...) fprintf(stderr, args) +#else +#define debug_printf(args...) do { } while(0) +#endif + +/* + * Return the operator (or the variable name) of the root node of the + * given expression. + */ +static const char *op_print(const struct cfzy_expr *exp) +{ + switch (exp->optype) { + case OP_OR: + return "||"; + case OP_AND: + return "&&"; + case OP_EQUAL: + return "=="; + case OP_OBRACKET: + return "("; + case OP_NOT: + return "!"; + case OP_CBRACKET: + return ")"; + case OP_VAR: + return exp->varname; + default: + return NULL; + } +} + +/* recursive function called by cfzy_expr_graph_dump() */ +static void expr_dump_node(FILE *f, const struct cfzy_expr *exp, int level) +{ + if (exp == NULL) + return; + + fprintf(f, "vertice %d %s:%p\n", level, op_print(exp), exp); + + if (exp->left) { + fprintf(f, "edge %p %p\n", exp, exp->left); + expr_dump_node(f, exp->left, level + 1); + } + + if (exp->right) { + fprintf(f, "edge %p %p\n", exp, exp->right); + expr_dump_node(f, exp->right, level + 1); + } +} + +/* dump an expression graph in a file */ +int cfzy_expr_graph_dump(const char *filename, const struct cfzy_expr *exp) +{ + FILE *f; + + f = fopen(filename, "w"); + if (f == NULL) + return -1; + expr_dump_node(f, exp, 0); + fclose(f); + return 0; +} + +/* return true if it's a binary operator */ +static int expr_is_bin_op(const struct cfzy_expr *exp) +{ + if (exp->optype == OP_AND || + exp->optype == OP_OR || + exp->optype == OP_EQUAL) + return 1; + return 0; +} + +/* + * Dump the expression 'exp' as a string into the buffer 'buf' of + * length 'len'. The string is nul-terminated. Return the number of + * written bytes on success (not including \0), else return -1. + */ +int cfzy_expr_to_str(const struct cfzy_expr *exp, char *buf, int len) +{ + int n, orig_len; + + orig_len = len; + + /* dump left expression if it's a binary operator (in case of + * unary operator, we only use the right child) */ + if (expr_is_bin_op(exp)) { + n = cfzy_expr_to_str(exp->left, buf, len); + if (n == -1 || n >= len || len <= 0) + return -1; + buf += n; + len -= n; + } + + /* dump the operator */ + if (expr_is_bin_op(exp)) + n = snprintf(buf, len, " %s ", op_print(exp)); + else + n = snprintf(buf, len, "%s", op_print(exp)); + if (n == -1 || n >= len || len <= 0) + return -1; + buf += n; + len -= n; + + /* dump the right expression except if it's a variable */ + if (exp->optype != OP_VAR) { + n = cfzy_expr_to_str(exp->right, buf, len); + if (n == -1 || n >= len || len <= 0) + return -1; + buf += n; + len -= n; + } + + /* close the bracket if needed */ + if (exp->optype == OP_OBRACKET) { + n = snprintf(buf, len, ")"); + if (n == -1 || n >= len || len <= 0) + return -1; + buf += n; + len -= n; + } + + return orig_len - len; +} + +/* + * Evaluate an expression. Return -1 on error, else the value of the + * expression (greater or equal to 0). + */ +int cfzy_expr_eval(const struct cfzy_expr *exp, + int (*getvalue)(const char *, void *), void *opaque_arg) +{ + switch (exp->optype) { + case OP_OR: + return cfzy_expr_eval(exp->left, getvalue, opaque_arg) || + cfzy_expr_eval(exp->right, getvalue, opaque_arg); + case OP_AND: + return cfzy_expr_eval(exp->left, getvalue, opaque_arg) && + cfzy_expr_eval(exp->right, getvalue, opaque_arg); + case OP_EQUAL: + return cfzy_expr_eval(exp->left, getvalue, opaque_arg) == + cfzy_expr_eval(exp->right, getvalue, opaque_arg); + case OP_OBRACKET: + return cfzy_expr_eval(exp->right, getvalue, opaque_arg); + case OP_NOT: + return !cfzy_expr_eval(exp->right, getvalue, opaque_arg); + case OP_VAR: + /* constants names (used for testing purposes) */ + if (!strcmp("true", exp->varname)) + return 1; + if (!strcmp("false", exp->varname)) + return 0; + if (getvalue == NULL) + return -1; + return getvalue(exp->varname, opaque_arg); + default: + debug_printf("%s(): bad operator\n", __FUNCTION__); + return -1; + } +} + +/* The argument 'buf' is expression string that must start with an + * opening bracket '('. This function returns the len of the + * expression contained until we get the closing bracket corresponding + * to buf[0]. Return -1 on error. */ +static int get_brac_len(const char *buf) +{ + const char *s = buf; + int i = 1; + + if (*s != '(') + return -1; + s++; + + while(*s != '\0' && i != 0) { + if (*s == ')') + i--; + if (*s == '(') + i++; + s++; + } + if (i != 0) + return -1; + + return s-buf; +} + +/* + * Allocate a new expression exp corresponding to the next characters + * in "buf". On success, a new expression exp is returned and the + * number of eaten characters is set in "eatlen". If the end of the + * string is reached, the expression exp contains the OP_EOF + * operator. On error, NULL is returned. + */ + static struct cfzy_expr *get_next_op(const char *buf, unsigned *eatlen) +{ + struct cfzy_expr *exp; + const char *s; + unsigned len; + + s = buf; + exp = malloc(sizeof(struct cfzy_expr)); + if (exp == NULL) + return NULL; + memset(exp, 0, sizeof(*exp)); + + switch (s[0]) { + case '#': + exp->optype = OP_EOF; + *eatlen = s - buf + 1; + return exp; + case '\0': + exp->optype = OP_EOF; + *eatlen = s - buf + 1; + return exp; + case '(': + exp->optype = OP_OBRACKET; + *eatlen = s - buf + 1; + return exp; + case ')': + exp->optype = OP_CBRACKET; + *eatlen = s - buf + 1; + return exp; + case '!': + exp->optype = OP_NOT; + *eatlen = s - buf + 1; + return exp; + case '&': + exp->optype = OP_AND; + if (s[1] != '&') + goto fail; + *eatlen = s - buf + 2; + return exp; + case '|': + exp->optype = OP_OR; + if (s[1] != '|') + goto fail; + *eatlen = s - buf + 2; + return exp; + case '=': + if (s[1] != '=') + goto fail; + exp->optype = OP_EQUAL; + *eatlen = s - buf + 2; + return exp; + default: + /* a variable name must start with a letter */ + if (isalpha(s[0])) + break; + goto fail; + } + + /* It's a variable, get name */ + while (isalnum(s[0]) || s[0] == '_') + s++; + + exp->optype = OP_VAR; + *eatlen = s - buf; + + /* alloc string for variable name */ + len = s - buf; + exp->varname = malloc(len+1); + memcpy(exp->varname, buf, len); + exp->varname[len] = '\0'; + return exp; + + fail: + free(exp); + return NULL; +} + +/* + * Parse the expression contained in s (which is a modifiable + * string). Return 0 on success, in this case the expression tree is + * returned in exp_ptr. On error, return -1. + */ +static int expr_parse(char *s, struct cfzy_expr **exp_ptr) +{ + struct cfzy_expr *top = NULL, *exp, *tmp; + int len; + unsigned eatlen; + + debug_printf("parse <%s>\n", s); + + if (*s == '\0') + return -1; + + while (1) { + + /* skip spaces */ + while (isspace(*s)) + s++; + + /* get next operator/operand */ + exp = get_next_op(s, &eatlen); + if (exp == NULL) { + debug_printf("Parse error\n"); + goto fail; + } + + if (exp->optype == OP_EOF) { + free(exp); + exp = NULL; + break; + } + + debug_printf("New node: %s\n", op_print(exp)); + switch (exp->optype) { + + case OP_OR: + case OP_AND: + case OP_EQUAL: + /* if it is the first node, error */ + if (top == NULL) + goto fail; + + /* the rightest leaf must be a variable */ + tmp = top; + while (tmp->right) + tmp = tmp->right; + if (tmp->optype != OP_VAR) + goto fail; + + /* if new node priority is lower than root + * node, it becomes the new root */ + if (exp->optype < top->optype) { + exp->left = top; + top = exp; + break; + } + + /* exp is placed at the "rightest" leaf. Check + * that our parent is not a variable */ + tmp = top; + while (tmp->right && exp->optype > tmp->right->optype) + tmp = tmp->right; + + if (tmp->right == NULL) + goto fail; + + exp->left = tmp->right; + tmp->right = exp; + break; + + case OP_OBRACKET: + /* if operator is an opening bracket, + * recursively call ourself with the + * sub-expression */ + len = get_brac_len(s); + if (len < 0) { + debug_printf("Cannot find closing bracket\n"); + goto fail; + } + s[len-1] = '\0'; + if (expr_parse(s+1, &exp->right) < 0) + goto fail; + if (exp->right == NULL) + goto fail; + s[len-1] = ')'; + s += len - 1; + + /* if it is the first node */ + if (top == NULL) { + top = exp; + break; + } + + /* exp is placed at the "rightest" leaf. Check + * that our parent is not a variable */ + tmp = top; + while (tmp->right) + tmp = tmp->right; + if (tmp->optype == OP_VAR) + goto fail; + tmp->right = exp; + break; + + case OP_NOT: + case OP_VAR: + /* if it is the first node */ + if (top == NULL) { + top = exp; + break; + } + + /* exp is placed at the "rightest" leaf. Check + * that our parent is not a variable */ + tmp = top; + while (tmp->right) + tmp = tmp->right; + if (tmp->optype == OP_VAR) + goto fail; + tmp->right = exp; + break; + + default: + /* should not happen */ + debug_printf("Parse error\n"); + goto fail; + } + + s += eatlen; + } + + if (top == NULL) + goto fail; + + /* the rightest leaf must be a variable */ + tmp = top; + while (tmp->right) + tmp = tmp->right; + if (tmp->optype != OP_VAR) { + debug_printf("Missing operand\n"); + goto fail; + } + + *exp_ptr = top; + return 0; + + fail: + if (top != NULL) + cfzy_expr_free(top); + if (exp != NULL) + cfzy_expr_free(exp); + + return -1; +} + +/* + * Parse the expression contained in buf. Return an expression tree or + * NULL on error. + */ +struct cfzy_expr *cfzy_expr_parse(const char *buf) +{ + struct cfzy_expr *top; + char *s; + int ret; + + s = strdup(buf); + if (s == NULL) + return NULL; + + ret = expr_parse(s, &top); + free(s); + if (ret < 0) + return NULL; + return top; +} + +/* Free an expression tree */ +void cfzy_expr_free(struct cfzy_expr *exp) +{ + if (exp == NULL) + return; + + if (exp->left) { + cfzy_expr_free(exp->left); + exp->left = NULL; + } + + if (exp->right) { + cfzy_expr_free(exp->right); + exp->right = NULL; + } + + if (exp->varname != NULL) + free(exp->varname); + + free(exp); +} + +/* recursive function called by cfzy_expr_get_vars() that browse the + * expr tree to fill the list */ +static int +expr_get_vars(const struct cfzy_expr *exp, struct cfzy_list_head *list) +{ + struct cfzy_list_elt *e; + char *name; + + if (exp->left) { + if (expr_get_vars(exp->left, list) < 0) + return -1; + } + + if (exp->right) { + if (expr_get_vars(exp->right, list) < 0) + return -1; + } + + if (exp->optype != OP_VAR) + return 0; + + /* ignore true and false constants */ + if (!strcmp(exp->varname, "true")) + return 0; + if (!strcmp(exp->varname, "false")) + return 0; + + /* nothing to do if var is already in list */ + TAILQ_FOREACH(e, list, next) { + if (!strcmp(e->ptr, exp->varname)) + return 0; + } + + name = strdup(exp->varname); + if (name == NULL) + return -1; + + if (cfzy_list_add_tail(list, name) < 0) { + free(name); + return -1; + } + + return 0; +} + +/* Return a list containing all variables on which expression depend */ +struct cfzy_list_head *cfzy_expr_get_vars(const struct cfzy_expr *exp) +{ + struct cfzy_list_head *list; + + list = cfzy_list_alloc(); + if (list == NULL) + return NULL; + + if (expr_get_vars(exp, list) < 0) { + cfzy_list_free(list, free); + return NULL; + } + + return list; +} diff --git a/tools/cfzy/libconfizery/cfzy_expr.h b/tools/cfzy/libconfizery/cfzy_expr.h new file mode 100644 index 0000000..9265955 --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_expr.h @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2013, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +#ifndef _CFZY_EXPR_H_ +#define _CFZY_EXPR_H_ + +/** + * @file + * Confizery Expression + * + * This module provides functions to parse an expression string and + * convert it in a logical expression tree, that can be evaluated, + * according to the variables defined in the configuration tree. + * + * It is used in configuration tree file, for instance in the "if" + * node or the "default" attribute. + */ + +#include + +#include "cfzy_list.h" + +/** + * Type of operand in expression tree (from low to high priority) + */ +enum cfzy_expr_op { + OP_OR, + OP_AND, + OP_EQUAL, + OP_OBRACKET, + OP_CBRACKET, + OP_NOT, + OP_VAR, + OP_EOF, +}; + +/** + * Structure describing an expression tree + * + * An expression tree is a binary tree. Each node contains an + * operator, or a variable name. If it's a unary operator, only the + * right child is used, else both left and right children are set. + */ +struct cfzy_expr { + TAILQ_ENTRY(expr) next; /**< next entry in list */ + enum cfzy_expr_op optype; /**< operator type */ + char *varname; /**< var name, only valid if type is OP_VAR */ + struct cfzy_expr *left; /**< left child */ + struct cfzy_expr *right; /**< right child */ +}; + +/** + * Parse a string and convert it in an expression tree + * + * @param buf + * String containing an expression + * @return + * Expression tree, or NULL on error + */ +struct cfzy_expr *cfzy_expr_parse(const char *buf); + +/** + * Free an expression tree previously allocated with cfzy_expr_parse() + * + * @param exp + * Expression to free + */ +void cfzy_expr_free(struct cfzy_expr *exp); + +/** + * Write an expression tree in a string buffer + * + * Dump the expression 'exp' as a string into the buffer 'buf' of + * length 'len'. The output string is always nul-terminated. + * + * @param exp + * Expression tree to dump + * @param buf + * Destination buffer + * @param len + * Len of the destination buffer + * @return + * Number of written bytes on success (not including '\0'), else + * return -1. + */ +int cfzy_expr_to_str(const struct cfzy_expr *exp, char *buf, int len); + +/** + * Evaluate an expression tree + * + * Evaluate the given expression tree according to variables defined + * in conftree. + * + * @param exp + * Expression tree to evaluate + * @param getvalue + * Function used to evaluate a variable. This function should + * return the value of the variable (positive), or a negative value + * on error (variable invalid or not found). + * This argument can be NULL, in this case only "true" and "false" are + * evaluated. + * @param opaque_arg + * Second argument given as-is to getvalue() function. + * @return + * The value of the expression (greater or equal to 0), or -1 on + * error + */ +int cfzy_expr_eval(const struct cfzy_expr *exp, + int (*getvalue)(const char *, void *), void *opaque_arg); + +/** + * Dump the expression graph in a file + * + * Useful for debug purpose only. The output can be parsed with + * cfzy_expr_graph.py. + * + * @param filename + * Name of file + * @param exp + * Expression tree to dump + * @return + * 0 on succes, -1 on error + */ +int cfzy_expr_graph_dump(const char *filename, const struct cfzy_expr *exp); + +/** + * Return a list containing all variables on which expression depend + * + * Allocate a list head, and for each variable of the expression, + * allocate an element and add it in the list. Each element points to + * a string which is allocated and should be freed by the user. Each + * variable is present only once in the list. + * + * @param exp + * Expression tree + * @return + * List containing the variables (it can be empty), or NULL on error. + */ +struct cfzy_list_head *cfzy_expr_get_vars(const struct cfzy_expr *exp); + +#endif /* _CFZY_EXPR_H_ */ diff --git a/tools/cfzy/libconfizery/cfzy_expr_graph.py b/tools/cfzy/libconfizery/cfzy_expr_graph.py new file mode 100644 index 0000000..b56bbd8 --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_expr_graph.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python + +import matplotlib.pyplot as plt +import networkx as nx +import sys, re, random + +G = nx.Graph() +labels = {} +levels = {} +init_pos = {} +maxlevel = 0 + +# always keep the same seed +random.seed(1234) + +# parse lines, new vertice is: +# vertice LEVEL OPERATOR:VERTICE_NAME +# new edge is: +# edge OPERATOR:VERTICE_NAME OPERATOR:VERTICE_NAME +while True: + l = sys.stdin.readline() + if l == "": + break + m = re.match("^vertice ([0-9]+) ([^\s]+)$", l) + if m: + level = m.groups()[0] + level = int(level) + operator, name = m.groups()[1].split(":") + print "vert: operator = %s, level = %d, name = %s"%(operator, level, name) + labels[name] = operator + init_pos[name] = (random.random(), -level) + levels[name] = level + if level > maxlevel: + maxlevel = level + continue + m = re.match("^edge ([^\s]+) ([^\s]+)$", l) + if m: + vertice1, vertice2 = m.groups() + G.add_edge(vertice1, vertice2) + continue + +pos = nx.spring_layout(G, pos=init_pos) + +# root node is blue and bigger +rootnode = filter(lambda x:levels[x] == 0, levels.keys()) +nx.draw_networkx_nodes(G, pos, node_color='b', node_size=2000, nodelist=rootnode) + +# graph other nodes +for level in range(1, maxlevel + 1): + othernodes = filter(lambda x:levels[x] == level, levels.keys()) + color = (1., float(level)/maxlevel, float(level)/maxlevel) + color = [color] * len(othernodes) + nx.draw_networkx_nodes(G, pos, node_color=color, node_size=1000, nodelist=othernodes) + +nx.draw_networkx_edges(G, pos) +nx.draw_networkx_labels(G, pos, labels, font_size=20, font_family='sans-serif') + +plt.axis('off') +plt.savefig("weighted_graph.png") # save as png +plt.show() # display diff --git a/tools/cfzy/libconfizery/cfzy_file.c b/tools/cfzy/libconfizery/cfzy_file.c new file mode 100644 index 0000000..4d80081 --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_file.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2013, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 "cfzy_log.h" + +#define LOG(level, fmt, args...) \ + CFZY_LOG("file", level, fmt, ##args) + +/* 0 = same file, 1 = different files, -1 = error + * the function does not close the files */ +int cfzy_file_compare(FILE *f1, FILE *f2) +{ + char buf1[BUFSIZ], buf2[BUFSIZ]; + size_t ret1, ret2; + + rewind(f1); + rewind(f2); + + while (1) { + ret1 = fread(buf1, 1, sizeof(buf1), f1); + if (ret1 == 0 && ferror(f1)) + return -1; + + ret2 = fread(buf2, 1, sizeof(buf2), f2); + if (ret2 == 0 && ferror(f2)) + return -1; + + if (ret1 != ret2) + return 1; + + if (ret1 == 0) /* implies ret2 == 0 too */ + return 0; + + if (memcmp(buf1, buf2, ret1)) + return 1; + } + + /* never reached */ + return 0; +} diff --git a/tools/cfzy/libconfizery/cfzy_file.h b/tools/cfzy/libconfizery/cfzy_file.h new file mode 100644 index 0000000..c9ed80b --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_file.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2013, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +/** + * @file + * File-related tools + */ + +#ifndef CFZY_FILE_H +#define CFZY_FILE_H + +#include + +/** + * Compare 2 files + * + * The function rewind to the beginning of the files, then compares + * them. The file position indicator is undefined after the execution + * of the function, and the files are not closed. + * + * @param f1 + * pointer to first stream + * @param f2 + * pointer to second stream + * @return + * 0 if files are the same, 1 if files are different, -1 on error + */ +int cfzy_file_compare(FILE *f1, FILE *f2); + +#endif /* CFZY_FILE */ diff --git a/tools/cfzy/libconfizery/cfzy_htable.c b/tools/cfzy/libconfizery/cfzy_htable.c new file mode 100644 index 0000000..0718f8a --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_htable.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2013, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 "cfzy_htable.h" + +/* return the hash from name */ +static uint32_t hash_name(const char *name) +{ + uint32_t h = 0; + const unsigned char *c; + + /* XXX add murmurhash ? */ + for (c = (const unsigned char *)name; *c; c++) + h += ((((uint32_t)*c) << 4) + (((uint32_t)*c) >> 4)) * 11; + + return h & CFZY_HTABLE_MASK; +} + +/* return the hlist_elt associated to the given name. If not found, + * return NULL. */ +static struct cfzy_hlist_elt * +__cfzy_htable_lookup(const struct cfzy_htable *htable, const char *name) +{ + struct cfzy_hlist_elt *e; + uint32_t h = hash_name(name); + + LIST_FOREACH(e, &htable->buckets[h], next) { + if (!strcmp(name, e->name)) + break; + } + return e; +} + +/* return the object associated to the given name. If not found, + * return NULL. */ +void *cfzy_htable_lookup(const struct cfzy_htable *htable, const char *name) +{ + struct cfzy_hlist_elt *e = __cfzy_htable_lookup(htable, name); + if (e == NULL) + return NULL; + return e->ptr; +} + +/* add an entry in the htable, allocating a new hlist_elt */ +int cfzy_htable_add(struct cfzy_htable *htable, const char *name, + void *ptr) +{ + struct cfzy_hlist_elt *e; + uint32_t h; + + /* The ptr should not be NULL, else we won't differentiate a + * lookup matching a NULL element and a non-matching one */ + if (ptr == NULL) + return -1; + + e = malloc(sizeof(struct cfzy_hlist_elt)); + if (e == NULL) + return -1; + + e->name = strdup(name); + if (e->name == NULL) { + free(e); + return -1; + } + + e->ptr = ptr; + h = hash_name(name); + LIST_INSERT_HEAD(&htable->buckets[h], e, next); + + return 0; +} + +/* delete and free an hlist_elt from the htable */ +static int __cfzy_htable_del(struct cfzy_hlist_elt *e) +{ + LIST_REMOVE(e, next); + free(e->name); + free(e); + return 0; +} + +/* delete an entry from the htable, freeing the hlist_elt */ +int cfzy_htable_del(struct cfzy_htable *htable, const char *name) +{ + struct cfzy_hlist_elt *e = __cfzy_htable_lookup(htable, name); + + if (e == NULL) + return -1; + + return __cfzy_htable_del(e); +} + +/* allocate and initialize a new htable */ +struct cfzy_htable *cfzy_htable_alloc(void) +{ + struct cfzy_htable *htable; + unsigned i; + + htable = malloc(sizeof(struct cfzy_htable)); + if (htable == NULL) + return NULL; + + for (i = 0; i < CFZY_HTABLE_SIZE; i++) { + LIST_INIT(&htable->buckets[i]); + } + return htable; +} + +/* empty the htable, freeing hlist_elt structures, and free the htable */ +void cfzy_htable_free(struct cfzy_htable *htable, void (*free_fct)(void *)) +{ + struct cfzy_hlist_elt *e; + unsigned i; + + for (i = 0; i < CFZY_HTABLE_SIZE; i++) { + while ((e = LIST_FIRST(&htable->buckets[i])) != NULL) { + if (free_fct != NULL) + free_fct(e->ptr); + + __cfzy_htable_del(e); + } + } + free(htable); +} diff --git a/tools/cfzy/libconfizery/cfzy_htable.h b/tools/cfzy/libconfizery/cfzy_htable.h new file mode 100644 index 0000000..21e9de3 --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_htable.h @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2013, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +#ifndef _CFZY_HTABLE_H_ +#define _CFZY_HTABLE_H_ + +/** + * @file + * Confizery Hash Table + * + * This module is a simple hash table implementation, based on LIST + * from sys/queue.h --see queue(3)-- and with a constant number of + * buckets. It handles the allocation of chaining structure so any + * kind of element can be added/deleted. Each element is referenced by + * a string (key). + */ + +#include + +/* we use a constant size of htable, it simplifies the code and it is + * enough for our purpose */ + +#define CFZY_HTABLE_ORDER 10 +#define CFZY_HTABLE_SIZE (1 << CFZY_HTABLE_ORDER) +#define CFZY_HTABLE_MASK (CFZY_HTABLE_SIZE - 1) + +/** + * A hash table bucket + */ +LIST_HEAD(cfzy_hlist_head, cfzy_hlist_elt); + +/** + * A hash table element, used to chain objects together and store the + * name (key). This structure is allocated by cfzy_htable_add(). + */ +struct cfzy_hlist_elt { + LIST_ENTRY(cfzy_hlist_elt) next; + char *name; + void *ptr; +}; + +/** + * A hash table structure + */ +struct cfzy_htable { + struct cfzy_hlist_head buckets[CFZY_HTABLE_SIZE]; +}; + +/** + * Lookup for an element matching the given key + * + * @param htable + * Hash table pointer + * @param name + * String identifying the element (key) + * @return + * Pointer to the element, or NULL on error + */ +void *cfzy_htable_lookup(const struct cfzy_htable *htable, const char *name); + + +/** + * Add an element in the hash table + * + * Allocate a new cfzy_hlist_elt structure, initialize it to point to + * the given pointer "ptr", then queue the structure in the + * appropriate hash table bucket. + * + * @param htable + * Hash table pointer + * @param name + * String identifying the element (key) + * @param ptr + * Pointer to the element (must not be NULL) + * @return + * 0 on success, -1 on error + */ +int cfzy_htable_add(struct cfzy_htable *htable, const char *name, + void *ptr); + +/** + * Remove an element from the hash table + * + * Unchain the structure from the hash table bucket, and free the + * cfzy_hlist_elt structure. + * + * @param htable + * Hash table pointer + * @param name + * String identifying the element (key) + * @return + * 0 on success, -1 on error (element not found) + */ +int cfzy_htable_del(struct cfzy_htable *htable, const char *name); + +/** + * Allocate and initialize a new empty htable + * + * @return + * Hash table pointer or NULL on error + */ +struct cfzy_htable *cfzy_htable_alloc(void); + +/** + * Free a htable + * + * Empty the htable, freeing all elements (cfzy_hlist_elt structures, + * and its pointer, using the provided free_fct), then free the + * cfzy_htable. + * + * @param htable + * Hash table pointer + * @param free_fct + * Function to free an element + */ +void cfzy_htable_free(struct cfzy_htable *htable, void (*free_fct)(void *)); + +#endif /* _CFZY_HTABLE_H_ */ diff --git a/tools/cfzy/libconfizery/cfzy_list.c b/tools/cfzy/libconfizery/cfzy_list.c new file mode 100644 index 0000000..f7a7019 --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_list.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2013, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 "cfzy_list.h" + +/* allocate a list_elt and insert at the tail of the list */ +int cfzy_list_add_tail(struct cfzy_list_head *list, void *ptr) +{ + struct cfzy_list_elt *e; + + e = malloc(sizeof(struct cfzy_list_elt)); + if (e == NULL) + return -1; + + e->ptr = ptr; + TAILQ_INSERT_TAIL(list, e, next); + return 0; +} + +/* allocate a list_elt and insert at the tail of the list */ +int cfzy_list_add_head(struct cfzy_list_head *list, void *ptr) +{ + struct cfzy_list_elt *e; + + e = malloc(sizeof(struct cfzy_list_elt)); + if (e == NULL) + return -1; + + e->ptr = ptr; + TAILQ_INSERT_HEAD(list, e, next); + return 0; +} + +/* remove from the list and free the list_elt */ +void cfzy_list_del(struct cfzy_list_head *list, struct cfzy_list_elt *e) +{ + TAILQ_REMOVE(list, e, next); + free(e); +} + +/* check if an elt is in list */ +int cfzy_list_elt_is_in_list(const struct cfzy_list_head *list, const void *ptr) +{ + struct cfzy_list_elt *e; + + TAILQ_FOREACH(e, list, next) { + if (e->ptr == ptr) + return 1; + } + return 0; +} + +/* allocate a new list_head */ +struct cfzy_list_head *cfzy_list_alloc(void) +{ + struct cfzy_list_head *list; + + list = malloc(sizeof(struct cfzy_list_head)); + if (list == NULL) + return NULL; + + TAILQ_INIT(list); + return list; +} + +/* empty the list, freeing the list_elt, and free the list_head */ +void cfzy_list_free(struct cfzy_list_head *list, void (*free_fct)(void *)) +{ + struct cfzy_list_elt *e; + + while ((e = TAILQ_FIRST(list)) != NULL) { + TAILQ_REMOVE(list, e, next); + if (free_fct != NULL) + free_fct(e->ptr); + free(e); + } + free(list); +} diff --git a/tools/cfzy/libconfizery/cfzy_list.h b/tools/cfzy/libconfizery/cfzy_list.h new file mode 100644 index 0000000..48b8d52 --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_list.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2013, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +#ifndef _CFZY_LIST_H_ +#define _CFZY_LIST_H_ + +/** + * @file + * Confizery List + * + * This module is a list implementation, based on TAILQ from + * sys/queue.h -see queue(3)-. It handles the allocation of chaining + * structure so any kind of element can be chained. + * + * To be really useful, this API must be used in conjonction with the + * TAILQ_* macros. For instance, to browse the list: + * + * struct cfzy_list_elt *e; + * TAILQ_FOREACH(e, list_head, next) { + * obj = e->ptr; + * ... + * } + */ + +#include + +/** + * A list head + */ +TAILQ_HEAD(cfzy_list_head, cfzy_list_elt); + +/** + * A list element, used to chain objects together. This structure is + * allocated by cfzy_list_add(). + */ +struct cfzy_list_elt { + TAILQ_ENTRY(cfzy_list_elt) next; + void *ptr; +}; + +/** + * Insert an element at the tail of the list + * + * Allocate a cfzy_list_elt structure, set its ptr field and insert it + * at the tail of the list. + * + * @param list + * List head to insert the element + * @param ptr + * Pointer to the object that will be referenced by list_elt->ptr + * @return + * 0 on succes, and negative on error + */ +int cfzy_list_add_tail(struct cfzy_list_head *list, void *ptr); + +/** + * Insert an element at the head of the list + * + * Allocate a cfzy_list_elt structure, set its ptr field and insert it + * at the head of the list. + * + * @param list + * List head to insert the element + * @param ptr + * Pointer to the object that will be referenced by list_elt->ptr + * @return + * 0 on succes, and negative on error + */ +int cfzy_list_add_head(struct cfzy_list_head *list, void *ptr); + +/** + * Remove an element from the list + * + * Unchain the cfzy_list_elt structure and free it. + * + * @param list + * List head to insert the element + * @param elt + * List element + */ +void cfzy_list_del(struct cfzy_list_head *list, struct cfzy_list_elt *elt); + +/** + * Check if an element is in the list + * + * Browse the list and check if there is already an cfzy_list_elt + * structure referencing ptr. + * + * @param list + * List head + * @param ptr + * Pointer to the element + * @return + * 1 if the element is in list, else 0 + */ +int cfzy_list_elt_is_in_list(const struct cfzy_list_head *list, const void *ptr); + +/** + * Allocate a new list + * + * @return + * Pointer to the new list head, or NULL on error + */ +struct cfzy_list_head *cfzy_list_alloc(void); + +/** + * Free a list + * + * Free all cfzy_list_elt of the list using the free_fct if provided, + * then free the cfzy_list_head structure. + * + * @param list + * List head pointer + * @param free_fct + * Function to free an element + */ +void cfzy_list_free(struct cfzy_list_head *list, void (*free_fct)(void *)); + +#endif /* _CFZY_LIST_H_ */ diff --git a/tools/cfzy/libconfizery/cfzy_log.c b/tools/cfzy/libconfizery/cfzy_log.c new file mode 100644 index 0000000..ef143b8 --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_log.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2013, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this log of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this log of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 "cfzy_log.h" +#include "cfzy_htable.h" + +static struct cfzy_htable *level_htable = NULL; +static int default_level = CFZY_LOG_NOTICE; + +/* initialize log susbsystem */ +int cfzy_log_init(void) +{ + level_htable = cfzy_htable_alloc(); + if (level_htable == NULL) + return -1; + return 0; +} + +/* free all structures associated to log subsystem */ +void cfzy_log_exit(void) +{ + cfzy_htable_free(level_htable, free); +} + +/* set default log level (for log types that are not registered) */ +int cfzy_log_set_default_level(int level) +{ + if (level != -1 && (level < CFZY_LOG_ERR || level > CFZY_LOG_DEBUG)) + return -1; + default_level = level; + return 0; +} + +/* set log level for a specific log type: the string is added in the + * hash table */ +int cfzy_log_set_level(const char *logtype, int level) +{ + int *l; + + if (level != -1 && (level < CFZY_LOG_ERR || level > CFZY_LOG_DEBUG)) + return -1; + l = cfzy_htable_lookup(level_htable, logtype); + + /* if log is already registered, it's easy */ + if (l != NULL) { + *l = level; + return 0; + } + + /* else allocate a new (int *) and store it in the htable */ + l = malloc(sizeof(*l)); + if (l == NULL) + return -1; + + *l = level; + cfzy_htable_add(level_htable, logtype, l); + return 0; +} + +/* print a log if given level is <= to configured log level */ +int cfzy_log_printf(const char *logtype, int level, const char *fmt, ...) +{ + int *l; + va_list ap; + int ret; + + l = cfzy_htable_lookup(level_htable, logtype); + + if (l == NULL && level > default_level) + return 0; + else if (l != NULL && level > *l) + return 0; + + va_start(ap, fmt); + ret = vfprintf(stderr, fmt, ap); + va_end(ap); + + return ret; +} diff --git a/tools/cfzy/libconfizery/cfzy_log.h b/tools/cfzy/libconfizery/cfzy_log.h new file mode 100644 index 0000000..a4b0f91 --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_log.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2013, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this log of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this log of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +#ifndef _CFZY_LOG_H_ +#define _CFZY_LOG_H_ + +/** + * @file + * Confizery Log + * + * This module provides logging helpers. XXX explain + */ + +/* log levels */ +#define CFZY_LOG_ERR 0 +#define CFZY_LOG_WARNING 1 +#define CFZY_LOG_NOTICE 2 +#define CFZY_LOG_INFO 3 +#define CFZY_LOG_DEBUG 4 + +/** + * Initialize log subsystem + * + * @return + * 0 on succes, -1 on error + */ +int cfzy_log_init(void); + +/** + * Uninitialize log subsystem + */ +void cfzy_log_exit(void); + +/** + * Set default log level + * + * @param level + * Level of log, -1 means no log at all + * @return + * 0 on succes, -1 on error + */ +int cfzy_log_set_default_level(int level); + +/** + * Set log level for a specific log type + * + * @param logtype + * String describing the log type + * @param level + * Level of log, -1 means no log at all + * @return + * 0 on succes, -1 on error + */ +int cfzy_log_set_level(const char *logtype, int level); + +/** + * Print a log if given level is <= to configured log level + * + * The log is displayed on standard error. + * + * @param logtype + * String describing the log type + * @param level + * Level at which the text must be displayed + * @param fmt + * Format string, followed by optional arguments, like in printf. + * @return + * The number of written characters (0 if nothing is diplayed), + * or -1 on error. + */ +int cfzy_log_printf(const char *logtype, int level, const char *fmt, ...); + +#if 0 +/** + * Print a log if given level is <= to configured log level + */ +#define CFZY_LOG(logtype, level, fmt, args...) \ + cfzy_log_printf(logtype, CFZY_LOG_##level, fmt, \ + ## args) +#else +#define CFZY_LOG(logtype, level, fmt, args...) \ + cfzy_log_printf(logtype, CFZY_LOG_##level, "%s():%d " fmt, \ + __func__, __LINE__, ## args) +#endif + +#endif /* _CFZY_LOG_H_ */ diff --git a/tools/cfzy/libconfizery/cfzy_string.c b/tools/cfzy/libconfizery/cfzy_string.c new file mode 100644 index 0000000..ebdafbf --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_string.c @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2013, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 "cfzy_log.h" + +#define LOG(level, fmt, args...) \ + CFZY_LOG("string", level, fmt, ##args) + +/* Acts as a asprintf, except that it appends data after buf (if not + * NULL), reallocating memory if necessary. */ +int cfzy_string_asprintf(char **buf, const char *fmt, ...) +{ + va_list ap; + char dummy; + int offset = 0, buflen, ret; + + if (*buf != NULL) + offset = strlen(*buf); + + va_start(ap, fmt); + ret = vsnprintf(&dummy, 1, fmt, ap); + va_end(ap); + if (ret < 0) { + LOG(ERR, "first vsnprintf failed\n"); + return ret; + } + + buflen = offset + ret + 1; + *buf = realloc(*buf, buflen); + if (*buf == NULL) { + LOG(ERR, "asprintf: not enough memory\n"); + return -1; + } + + va_start(ap, fmt); + ret = vsnprintf(*buf + offset, buflen - offset, fmt, ap); + va_end(ap); + + if (ret >= buflen || ret < 0) { + free(*buf); + *buf = NULL; + LOG(ERR, "second vsnprintf failed\n"); + return -1; + } + + return ret; +} + +/* + * If the buffer is surrounded by quotes (simple or double), this + * function will allocate a new string and remove the quotes properly, + * also deleting the backslash in occurences of \\, \" or \'. If the + * buffer does not start by a quote, the function just return a + * duplicate of the input string. On sucess, the eatlen is updated to + * the number of consumed bytes (including quotes if any, but not + * including \0). On error, return NULL, in this case *eatlen is + * undefined. + */ +char *cfzy_string_unquote(const char *buf, unsigned *eatlen) +{ + char delim; + char *out, *s2; + const char *s; + + s = buf; + out = strdup(s); + if (out == NULL) { + LOG(ERR, "not enough memory\n"); + return NULL; + } + + if (s[0] == '"') + delim = '"'; + else if (s[0] == '\'') + delim = '\''; + else { + *eatlen = strlen(s); + return out; + } + s++; + + s2 = out; + while (*s != delim) { + if (*s == '\0') { + free(out); + return NULL; + } + if (*s == '\\' && *(s+1) == delim) { + *s2 = delim; + s += 2; + s2 ++; + continue; + } + if (*s == '\\' && *(s+1) == '\\') { + *s2 = '\\'; + s += 2; + s2 ++; + continue; + } + *s2 = *s; + s ++; + s2 ++; + } + *s2 = '\0'; + + *eatlen = s - buf + 1; + return out; +} + +/* quote a string and escape original quotes */ +char *cfzy_string_quote(const char *src) +{ + int s, d; + char *dst; + + /* get dst buf len */ + for (s = 0, d = 0; src[s] != '\0'; s++, d++) { + if (src[s] == '"') + d++; + if (src[s] == '\\' && src[s+1] == '"') + d++; + } + + dst = malloc(d + 3); /* 3 for the 2 quotes and the \0 */ + if (dst == NULL) + return NULL; + + dst[0] = '"'; + for (s = 0, d = 1; src[s] != '\0'; s++, d++) { + if (src[s] == '"') + dst[d++] = '\\'; + if (src[s] == '\\' && src[s+1] == '"') + dst[d++] = '\\'; + + dst[d] = src[s]; + } + + dst[d++] = '"'; + dst[d++] = '\0'; + + return dst; +} + diff --git a/tools/cfzy/libconfizery/cfzy_string.h b/tools/cfzy/libconfizery/cfzy_string.h new file mode 100644 index 0000000..6514be0 --- /dev/null +++ b/tools/cfzy/libconfizery/cfzy_string.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2013, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +/** + * @file + * String-related tools + */ + +#ifndef CFZY_STRING_H +#define CFZY_STRING_H + +/** + * Append a formatted string in an allocated buffer + * + * This function acts as a asprintf, with some differences: + * - *but can already contain a string, in this case, the new string + * is appended. + * - *strp is set to NULL on error, like in BSD's asprintf + * + * @param strp + * Pointer to a (char *), must not be NULL. If *strp is NULL a new + * buffer is allocated to store the string. Else, if *strp is not + * NULL, it must point to a valid string in a buffer that was + * previously allocated using malloc. + * @param fmt + * Format string, followed by other arguments, like in printf + * @return + * The number of bytes printed. If memory allocation wasn't + * possible, or some other error occurs, these functions will return + * -1, and the contents of strp is set to NULL. + **/ +int cfzy_string_asprintf(char **buf, const char *fmt, ...); + +/** + * Remove quotes on a string + * + * If the buffer is surrounded by quotes (simple or double), this + * function will allocate a new string and remove the quotes properly, + * also deleting the backslash in occurences of \\, \" or \'. If the + * buffer does not start by a quote, the function just return a + * duplicate of the input string. + * + * On sucess, the value pointer by eatlen is updated to the len of + * consumed bytes in the input buffer, including quotes if any, but + * not including \0. On error *eatlen is undefined. + * + * @param buf + * string buffer + * @param eatlen + * pointer to an int where the number of consumed bytes is written + * @return + * the unquoted string (allocated), or NULL on error + */ +char *cfzy_string_unquote(const char *buf, unsigned *eatlen); + +/** + * Quote a string with double quotes + * + * Allocate a new string which is a copy of the first one, surrounded + * by quotes and quotes of initial string are escaped with a + * backslash. + * + * @param src + * input string + * @return + * the quoted string (allocated), or NULL on error + */ +char *cfzy_string_quote(const char *src); + +#endif /* CFZY_STRING */