From 8df722823eb3371a8fd9082b78e51077bfd277df Mon Sep 17 00:00:00 2001 From: Olivier Matz Date: Mon, 28 Sep 2015 19:03:55 +0200 Subject: [PATCH 1/1] initial revision --- arch/avr/include/errno.h | 177 ++++ arch/avr/include/fcntl.h | 1 + arch/avr/include/stdio.h | 40 + arch/avr/include/sys/queue.h | 844 ++++++++++++++++ arch/avr/include/sys/types.h | 33 + arch/avr/include/ucg_delay.h | 43 + arch/avr/include/ucg_irq.h | 64 ++ arch/avr/include/ucg_reent_intr.h | 34 + arch/avr/include/unistd.h | 1 + arch/avr/mk/ucgine-arch.mk | 35 + arch/avr/uart/include/ucg_avr_uart.h | 55 ++ arch/avr/uart/ucg_avr_uart.c | 109 ++ arch/posix/include/ucg_delay.h | 43 + arch/posix/include/ucg_irq.h | 58 ++ arch/posix/mk/ucgine-arch.mk | 30 + arch/stm32/include/ucg_delay.h | 46 + arch/stm32/include/ucg_irq.h | 87 ++ arch/stm32/include/ucg_reent_intr.h | 81 ++ arch/stm32/mk/ucgine-arch.mk | 39 + arch/stm32/uart/include/ucg_stm32_uart.h | 51 + arch/stm32/uart/ucg_stm32_uart.c | 157 +++ arch/stm32/ucg_reent_intr.c | 192 ++++ examples/Makefile | 49 + examples/spi-flash-client/Makefile | 77 ++ examples/spi-flash-client/main.c | 129 +++ examples/spi-flash-client/uart.c | 116 +++ examples/spi-flash-client/uart.h | 35 + examples/spi-flash/Makefile | 84 ++ examples/spi-flash/main.c | 324 ++++++ examples/spi-flash/stm32_flash.ld | 172 ++++ examples/spi-flash/stm32f4xx_conf.h | 94 ++ examples/spi-flash/system_stm32f4xx.c | 553 +++++++++++ examples/spi-flash/uart.c | 148 +++ examples/spi-flash/uart.h | 37 + examples/test-callout/Makefile | 138 +++ examples/test-callout/main.c | 221 +++++ examples/test-callout/stm32_flash.ld | 172 ++++ examples/test-callout/stm32f4xx_conf.h | 94 ++ examples/test-callout/system_stm32f4xx.c | 553 +++++++++++ examples/test-cmd/Makefile | 129 +++ examples/test-cmd/commands.c | 160 +++ examples/test-cmd/commands.h | 35 + examples/test-cmd/main.c | 133 +++ examples/test-cmd/stm32_flash.ld | 172 ++++ examples/test-cmd/stm32f4xx_conf.h | 94 ++ examples/test-cmd/system_stm32f4xx.c | 553 +++++++++++ examples/test-cmd/uart.c | 238 +++++ examples/test-cmd/uart.h | 35 + examples/test-mk/Makefile | 58 ++ examples/test-mk/dir/titi.c | 4 + examples/test-mk/dir/toto.c | 4 + examples/test-mk/dir/toto.h | 1 + examples/test-mk/main.c | 4 + examples/test-mk/test1/Makefile | 43 + examples/test-mk/test1/main.c | 4 + examples/test-mk/test2/Makefile | 43 + examples/test-mk/test2/main.c | 4 + examples/test-uart/Makefile | 114 +++ examples/test-uart/main.c | 121 +++ examples/test-uart/stm32_flash.ld | 172 ++++ examples/test-uart/stm32f4xx_conf.h | 94 ++ examples/test-uart/system_stm32f4xx.c | 553 +++++++++++ examples/test-uart/uart.c | 233 +++++ examples/test-uart/uart.h | 35 + lib/callout/include/ucg_callout.h | 361 +++++++ lib/callout/ucg_callout.c | 412 ++++++++ lib/cirbuf/include/ucg_cirbuf.h | 412 ++++++++ lib/cirbuf/ucg_cirbuf.c | 624 ++++++++++++ lib/cmd/include/ucg_cmd.h | 219 ++++ lib/cmd/include/ucg_cmd_parse.h | 256 +++++ lib/cmd/include/ucg_cmd_parse_etheraddr.h | 87 ++ lib/cmd/include/ucg_cmd_parse_file.h | 63 ++ lib/cmd/include/ucg_cmd_parse_ipaddr.h | 164 +++ lib/cmd/include/ucg_cmd_parse_num.h | 104 ++ lib/cmd/include/ucg_cmd_parse_string.h | 95 ++ lib/cmd/include/ucg_cmd_rdline.h | 452 +++++++++ lib/cmd/include/ucg_cmd_socket.h | 91 ++ lib/cmd/include/ucg_cmd_termios.h | 81 ++ lib/cmd/include/ucg_cmd_vt100.h | 146 +++ lib/cmd/ucg_cmd.c | 257 +++++ lib/cmd/ucg_cmd_parse.c | 694 +++++++++++++ lib/cmd/ucg_cmd_parse_etheraddr.c | 144 +++ lib/cmd/ucg_cmd_parse_file.c | 268 +++++ lib/cmd/ucg_cmd_parse_ipaddr.c | 384 +++++++ lib/cmd/ucg_cmd_parse_num.c | 532 ++++++++++ lib/cmd/ucg_cmd_parse_string.c | 207 ++++ lib/cmd/ucg_cmd_rdline.c | 934 ++++++++++++++++++ lib/cmd/ucg_cmd_socket.c | 196 ++++ lib/cmd/ucg_cmd_termios.c | 72 ++ lib/cmd/ucg_cmd_vt100.c | 179 ++++ lib/gloss/include/ucg_gloss_chardev.h | 132 +++ lib/gloss/ucg_gloss_chardev.c | 53 + lib/gloss/ucg_gloss_stubs.c | 301 ++++++ lib/uart/include/ucg_uart.h | 253 +++++ lib/uart/ucg_uart.c | 282 ++++++ mk/ucgine-ar-rules.mk | 55 ++ mk/ucgine-ar-vars.mk | 75 ++ mk/ucgine-clean-rules.mk | 33 + mk/ucgine-clean-vars.mk | 37 + mk/ucgine-copy-rules.mk | 55 ++ mk/ucgine-copy-vars.mk | 74 ++ mk/ucgine-exe-rules.mk | 55 ++ mk/ucgine-exe-vars.mk | 79 ++ mk/ucgine-obj-rules.mk | 83 ++ mk/ucgine-obj-vars.mk | 124 +++ mk/ucgine-objcopy-rules.mk | 74 ++ mk/ucgine-objcopy-vars.mk | 89 ++ mk/ucgine-post.mk | 108 ++ mk/ucgine-pre.mk | 49 + mk/ucgine-shlib-rules.mk | 55 ++ mk/ucgine-shlib-vars.mk | 76 ++ mk/ucgine-slink-rules.mk | 55 ++ mk/ucgine-slink-vars.mk | 74 ++ mk/ucgine-subdir-rules.mk | 30 + mk/ucgine-subdir-vars.mk | 33 + mk/ucgine-tools.mk | 159 +++ mk/ucgine-vars.mk | 49 + tools/cfzy/Makefile | 71 ++ tools/cfzy/build/cfzy-basic/cfzy-basic | Bin 0 -> 219248 bytes tools/cfzy/cfzy-basic/Makefile | 11 + tools/cfzy/cfzy-basic/main.c | 422 ++++++++ tools/cfzy/cfzy-test/Makefile | 14 + .../invalid-configs/circular-dep.cfzy | 8 + .../invalid-configs/circular-dep2.cfzy | 11 + .../invalid-configs/dotconfig-bad-val | 17 + .../invalid-configs/dotconfig-bad-val2 | 16 + .../invalid-configs/dotconfig-bad-val3 | 16 + .../cfzy-test/invalid-configs/dup-name.cfzy | 4 + .../invalid-configs/invalid-attr.cfzy | 4 + .../invalid-configs/invalid-command.cfzy | 3 + .../invalid-configs/invalid-expr.cfzy | 4 + .../invalid-configs/invalid-value.cfzy | 4 + .../invalid-configs/node-not-closed.cfzy | 6 + tools/cfzy/cfzy-test/main.c | 79 ++ .../cfzy/cfzy-test/test-configs/conftree.cfzy | 98 ++ tools/cfzy/cfzy-test/test-configs/dotconfig | 25 + .../cfzy-test/test-configs/subconftree.cfzy | 2 + tools/cfzy/cfzy-test/test_conftree.c | 454 +++++++++ tools/cfzy/cfzy-test/test_conftree.h | 34 + tools/cfzy/cfzy-test/test_dotconfig.c | 194 ++++ tools/cfzy/cfzy-test/test_dotconfig.h | 34 + tools/cfzy/cfzy-test/test_expr.c | 379 +++++++ tools/cfzy/cfzy-test/test_expr.h | 34 + tools/cfzy/libconfizery/Makefile | 29 + tools/cfzy/libconfizery/cfzy_c_hdr.c | 342 +++++++ tools/cfzy/libconfizery/cfzy_c_hdr.h | 81 ++ tools/cfzy/libconfizery/cfzy_confnode.c | 859 ++++++++++++++++ tools/cfzy/libconfizery/cfzy_confnode.h | 564 +++++++++++ .../cfzy/libconfizery/cfzy_confnode_choice.c | 231 +++++ .../libconfizery/cfzy_confnode_choiceconfig.c | 130 +++ .../cfzy/libconfizery/cfzy_confnode_comment.c | 99 ++ .../cfzy/libconfizery/cfzy_confnode_config.c | 97 ++ tools/cfzy/libconfizery/cfzy_confnode_if.c | 141 +++ .../libconfizery/cfzy_confnode_intconfig.c | 104 ++ tools/cfzy/libconfizery/cfzy_confnode_menu.c | 113 +++ .../libconfizery/cfzy_confnode_menuconfig.c | 98 ++ tools/cfzy/libconfizery/cfzy_confnode_ops.h | 112 +++ tools/cfzy/libconfizery/cfzy_confnode_root.c | 83 ++ .../libconfizery/cfzy_confnode_strconfig.c | 94 ++ tools/cfzy/libconfizery/cfzy_conftree.c | 368 +++++++ tools/cfzy/libconfizery/cfzy_conftree.h | 147 +++ .../cfzy/libconfizery/cfzy_conftree_parser.c | 790 +++++++++++++++ .../cfzy/libconfizery/cfzy_conftree_parser.h | 90 ++ tools/cfzy/libconfizery/cfzy_dotconfig.c | 274 +++++ tools/cfzy/libconfizery/cfzy_dotconfig.h | 73 ++ tools/cfzy/libconfizery/cfzy_expr.c | 571 +++++++++++ tools/cfzy/libconfizery/cfzy_expr.h | 165 ++++ tools/cfzy/libconfizery/cfzy_expr_graph.py | 60 ++ tools/cfzy/libconfizery/cfzy_file.c | 68 ++ tools/cfzy/libconfizery/cfzy_file.h | 54 + tools/cfzy/libconfizery/cfzy_htable.c | 154 +++ tools/cfzy/libconfizery/cfzy_htable.h | 142 +++ tools/cfzy/libconfizery/cfzy_list.c | 104 ++ tools/cfzy/libconfizery/cfzy_list.h | 143 +++ tools/cfzy/libconfizery/cfzy_log.c | 107 ++ tools/cfzy/libconfizery/cfzy_log.h | 110 +++ tools/cfzy/libconfizery/cfzy_string.c | 172 ++++ tools/cfzy/libconfizery/cfzy_string.h | 94 ++ 178 files changed, 27035 insertions(+) create mode 100644 arch/avr/include/errno.h create mode 100644 arch/avr/include/fcntl.h create mode 100644 arch/avr/include/stdio.h create mode 100644 arch/avr/include/sys/queue.h create mode 100644 arch/avr/include/sys/types.h create mode 100644 arch/avr/include/ucg_delay.h create mode 100644 arch/avr/include/ucg_irq.h create mode 100644 arch/avr/include/ucg_reent_intr.h create mode 100644 arch/avr/include/unistd.h create mode 100644 arch/avr/mk/ucgine-arch.mk create mode 100644 arch/avr/uart/include/ucg_avr_uart.h create mode 100644 arch/avr/uart/ucg_avr_uart.c create mode 100644 arch/posix/include/ucg_delay.h create mode 100644 arch/posix/include/ucg_irq.h create mode 100644 arch/posix/mk/ucgine-arch.mk create mode 100644 arch/stm32/include/ucg_delay.h create mode 100644 arch/stm32/include/ucg_irq.h create mode 100644 arch/stm32/include/ucg_reent_intr.h create mode 100644 arch/stm32/mk/ucgine-arch.mk create mode 100644 arch/stm32/uart/include/ucg_stm32_uart.h create mode 100644 arch/stm32/uart/ucg_stm32_uart.c create mode 100644 arch/stm32/ucg_reent_intr.c create mode 100644 examples/Makefile create mode 100644 examples/spi-flash-client/Makefile create mode 100644 examples/spi-flash-client/main.c create mode 100644 examples/spi-flash-client/uart.c create mode 100644 examples/spi-flash-client/uart.h create mode 100644 examples/spi-flash/Makefile create mode 100644 examples/spi-flash/main.c create mode 100755 examples/spi-flash/stm32_flash.ld create mode 100644 examples/spi-flash/stm32f4xx_conf.h create mode 100644 examples/spi-flash/system_stm32f4xx.c create mode 100644 examples/spi-flash/uart.c create mode 100644 examples/spi-flash/uart.h create mode 100644 examples/test-callout/Makefile create mode 100644 examples/test-callout/main.c create mode 100755 examples/test-callout/stm32_flash.ld create mode 100644 examples/test-callout/stm32f4xx_conf.h create mode 100644 examples/test-callout/system_stm32f4xx.c create mode 100644 examples/test-cmd/Makefile create mode 100644 examples/test-cmd/commands.c create mode 100644 examples/test-cmd/commands.h create mode 100644 examples/test-cmd/main.c create mode 100755 examples/test-cmd/stm32_flash.ld create mode 100644 examples/test-cmd/stm32f4xx_conf.h create mode 100644 examples/test-cmd/system_stm32f4xx.c create mode 100644 examples/test-cmd/uart.c create mode 100644 examples/test-cmd/uart.h create mode 100644 examples/test-mk/Makefile create mode 100644 examples/test-mk/dir/titi.c create mode 100644 examples/test-mk/dir/toto.c create mode 100644 examples/test-mk/dir/toto.h create mode 100644 examples/test-mk/main.c create mode 100644 examples/test-mk/test1/Makefile create mode 100644 examples/test-mk/test1/main.c create mode 100644 examples/test-mk/test2/Makefile create mode 100644 examples/test-mk/test2/main.c create mode 100644 examples/test-uart/Makefile create mode 100644 examples/test-uart/main.c create mode 100755 examples/test-uart/stm32_flash.ld create mode 100644 examples/test-uart/stm32f4xx_conf.h create mode 100644 examples/test-uart/system_stm32f4xx.c create mode 100644 examples/test-uart/uart.c create mode 100644 examples/test-uart/uart.h create mode 100644 lib/callout/include/ucg_callout.h create mode 100644 lib/callout/ucg_callout.c create mode 100644 lib/cirbuf/include/ucg_cirbuf.h create mode 100644 lib/cirbuf/ucg_cirbuf.c create mode 100644 lib/cmd/include/ucg_cmd.h create mode 100644 lib/cmd/include/ucg_cmd_parse.h create mode 100644 lib/cmd/include/ucg_cmd_parse_etheraddr.h create mode 100644 lib/cmd/include/ucg_cmd_parse_file.h create mode 100644 lib/cmd/include/ucg_cmd_parse_ipaddr.h create mode 100644 lib/cmd/include/ucg_cmd_parse_num.h create mode 100644 lib/cmd/include/ucg_cmd_parse_string.h create mode 100644 lib/cmd/include/ucg_cmd_rdline.h create mode 100644 lib/cmd/include/ucg_cmd_socket.h create mode 100644 lib/cmd/include/ucg_cmd_termios.h create mode 100644 lib/cmd/include/ucg_cmd_vt100.h create mode 100644 lib/cmd/ucg_cmd.c create mode 100644 lib/cmd/ucg_cmd_parse.c create mode 100644 lib/cmd/ucg_cmd_parse_etheraddr.c create mode 100644 lib/cmd/ucg_cmd_parse_file.c create mode 100644 lib/cmd/ucg_cmd_parse_ipaddr.c create mode 100644 lib/cmd/ucg_cmd_parse_num.c create mode 100644 lib/cmd/ucg_cmd_parse_string.c create mode 100644 lib/cmd/ucg_cmd_rdline.c create mode 100644 lib/cmd/ucg_cmd_socket.c create mode 100644 lib/cmd/ucg_cmd_termios.c create mode 100644 lib/cmd/ucg_cmd_vt100.c create mode 100644 lib/gloss/include/ucg_gloss_chardev.h create mode 100644 lib/gloss/ucg_gloss_chardev.c create mode 100644 lib/gloss/ucg_gloss_stubs.c create mode 100644 lib/uart/include/ucg_uart.h create mode 100644 lib/uart/ucg_uart.c create mode 100644 mk/ucgine-ar-rules.mk create mode 100644 mk/ucgine-ar-vars.mk create mode 100644 mk/ucgine-clean-rules.mk create mode 100644 mk/ucgine-clean-vars.mk create mode 100644 mk/ucgine-copy-rules.mk create mode 100644 mk/ucgine-copy-vars.mk create mode 100644 mk/ucgine-exe-rules.mk create mode 100644 mk/ucgine-exe-vars.mk create mode 100644 mk/ucgine-obj-rules.mk create mode 100644 mk/ucgine-obj-vars.mk create mode 100644 mk/ucgine-objcopy-rules.mk create mode 100644 mk/ucgine-objcopy-vars.mk create mode 100644 mk/ucgine-post.mk create mode 100644 mk/ucgine-pre.mk create mode 100644 mk/ucgine-shlib-rules.mk create mode 100644 mk/ucgine-shlib-vars.mk create mode 100644 mk/ucgine-slink-rules.mk create mode 100644 mk/ucgine-slink-vars.mk create mode 100644 mk/ucgine-subdir-rules.mk create mode 100644 mk/ucgine-subdir-vars.mk create mode 100644 mk/ucgine-tools.mk create mode 100644 mk/ucgine-vars.mk create mode 100644 tools/cfzy/Makefile create mode 100755 tools/cfzy/build/cfzy-basic/cfzy-basic create mode 100644 tools/cfzy/cfzy-basic/Makefile create mode 100644 tools/cfzy/cfzy-basic/main.c create mode 100644 tools/cfzy/cfzy-test/Makefile create mode 100644 tools/cfzy/cfzy-test/invalid-configs/circular-dep.cfzy create mode 100644 tools/cfzy/cfzy-test/invalid-configs/circular-dep2.cfzy create mode 100644 tools/cfzy/cfzy-test/invalid-configs/dotconfig-bad-val create mode 100644 tools/cfzy/cfzy-test/invalid-configs/dotconfig-bad-val2 create mode 100644 tools/cfzy/cfzy-test/invalid-configs/dotconfig-bad-val3 create mode 100644 tools/cfzy/cfzy-test/invalid-configs/dup-name.cfzy create mode 100644 tools/cfzy/cfzy-test/invalid-configs/invalid-attr.cfzy create mode 100644 tools/cfzy/cfzy-test/invalid-configs/invalid-command.cfzy create mode 100644 tools/cfzy/cfzy-test/invalid-configs/invalid-expr.cfzy create mode 100644 tools/cfzy/cfzy-test/invalid-configs/invalid-value.cfzy create mode 100644 tools/cfzy/cfzy-test/invalid-configs/node-not-closed.cfzy create mode 100644 tools/cfzy/cfzy-test/main.c create mode 100644 tools/cfzy/cfzy-test/test-configs/conftree.cfzy create mode 100644 tools/cfzy/cfzy-test/test-configs/dotconfig create mode 100644 tools/cfzy/cfzy-test/test-configs/subconftree.cfzy create mode 100644 tools/cfzy/cfzy-test/test_conftree.c create mode 100644 tools/cfzy/cfzy-test/test_conftree.h create mode 100644 tools/cfzy/cfzy-test/test_dotconfig.c create mode 100644 tools/cfzy/cfzy-test/test_dotconfig.h create mode 100644 tools/cfzy/cfzy-test/test_expr.c create mode 100644 tools/cfzy/cfzy-test/test_expr.h create mode 100644 tools/cfzy/libconfizery/Makefile create mode 100644 tools/cfzy/libconfizery/cfzy_c_hdr.c create mode 100644 tools/cfzy/libconfizery/cfzy_c_hdr.h create mode 100644 tools/cfzy/libconfizery/cfzy_confnode.c create mode 100644 tools/cfzy/libconfizery/cfzy_confnode.h create mode 100644 tools/cfzy/libconfizery/cfzy_confnode_choice.c create mode 100644 tools/cfzy/libconfizery/cfzy_confnode_choiceconfig.c create mode 100644 tools/cfzy/libconfizery/cfzy_confnode_comment.c create mode 100644 tools/cfzy/libconfizery/cfzy_confnode_config.c create mode 100644 tools/cfzy/libconfizery/cfzy_confnode_if.c create mode 100644 tools/cfzy/libconfizery/cfzy_confnode_intconfig.c create mode 100644 tools/cfzy/libconfizery/cfzy_confnode_menu.c create mode 100644 tools/cfzy/libconfizery/cfzy_confnode_menuconfig.c create mode 100644 tools/cfzy/libconfizery/cfzy_confnode_ops.h create mode 100644 tools/cfzy/libconfizery/cfzy_confnode_root.c create mode 100644 tools/cfzy/libconfizery/cfzy_confnode_strconfig.c create mode 100644 tools/cfzy/libconfizery/cfzy_conftree.c create mode 100644 tools/cfzy/libconfizery/cfzy_conftree.h create mode 100644 tools/cfzy/libconfizery/cfzy_conftree_parser.c create mode 100644 tools/cfzy/libconfizery/cfzy_conftree_parser.h create mode 100644 tools/cfzy/libconfizery/cfzy_dotconfig.c create mode 100644 tools/cfzy/libconfizery/cfzy_dotconfig.h create mode 100644 tools/cfzy/libconfizery/cfzy_expr.c create mode 100644 tools/cfzy/libconfizery/cfzy_expr.h create mode 100644 tools/cfzy/libconfizery/cfzy_expr_graph.py create mode 100644 tools/cfzy/libconfizery/cfzy_file.c create mode 100644 tools/cfzy/libconfizery/cfzy_file.h create mode 100644 tools/cfzy/libconfizery/cfzy_htable.c create mode 100644 tools/cfzy/libconfizery/cfzy_htable.h create mode 100644 tools/cfzy/libconfizery/cfzy_list.c create mode 100644 tools/cfzy/libconfizery/cfzy_list.h create mode 100644 tools/cfzy/libconfizery/cfzy_log.c create mode 100644 tools/cfzy/libconfizery/cfzy_log.h create mode 100644 tools/cfzy/libconfizery/cfzy_string.c create mode 100644 tools/cfzy/libconfizery/cfzy_string.h 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 0000000000000000000000000000000000000000..9402548415a29bd136355021a8cdb077bc51a1c6 GIT binary patch literal 219248 zcmd443w#ts);`=bNnnydW>D04A2GPW1x>tdK+qYIKnH_F0Yw25$c1EuBqlQu6g8R& zW*nkfZ;QUJ%U)h(cimm~)x}9v5-t+(il{5ft{~oe92L9)-kAUMR8{v(LgMcGegE(G z`&yamI(6#QsZ-~iI#pfWxwWu(irr?@%*(D_qA}NVj7NavucjTiJW1sFQ<(YKPt0k zs%dTkU_J7>PL;2Fv`oOw)HPIOHC?4h+W*+2G;|4vc;JH_rn zj(HCgtw8+7Qy~k$NXFa?^l~Z0a~Ibvyy${+ z7gvm1TvO|B8rPI}(YT8)IH$4hobv?##A6qyUOrpYFy-M!j%|;`KeEK~FK=jk_~Vvk zMVn7(I+E>=HCCUIlf<$y_-D#LkmA;wZ6oc3osECvSbplNQ&t{$U`5sJ1+MX5gid+l zw}*}ac))8npaT^K2Z5h62>jGR;1>@9zi1Hn6m)DLe$obkKQ;)R?m^%UgTNme1b$!; z_>4jLxq1-%wS&NK8wCFSAn?(Hz%LktpEZNvR}KQdeh~QbLEu?~v^!%E{L2Typ9{Df zb^2e|0Wgq0ga?6d9R!{}2;4ge{DeW^(+A<7JPyQX(jfRh9R%(g1pd+>^#2C<2<;@T z=g(!h(=d<7>ncU3W4K2_WKx8L_&fUHM-+T-KlnZc-_;MU!OxY`tzD#5RW0^6R%?|_ zH9oDXuD-HXYxFhLmM^K-N=tE9;jfo~`sG@^-`A)uxxS*NL2Ia7uy}D@If^Q3L=9iv z;>Cb2tEsIZKC)WXqDtVGn}`jILEE>azN%(%r3M!ASx0YG`}BHouTtI(>JHPrYjwJP+a0&GYUV7qQvWrHAuUXTYQpitKU910CG zSn`^K>-M~JF66k{{~!Iw zdPzssOvXGEr)L8OR7o@Ew_|H+hI6L+Ie>)S_lpNGO(tnRmG_@ilS;a`vJ$SA65v*-gi41K;HFIrT7M`L32`B4 z>vAN((<~^)C;^_H0MAT-4@-c%6W|zV>&i}mXIM~8%SnJc6X1CXa8q|!>`8!+NWj+< z;E8^;BmsVG0{)x?IAbRBDocPLX9AH{C%}(SfY&F$>GRF2DFL2o0+BW+z)wtowk{DZ1b8?BentYkCjmYx0lqB(er5uE zM*=)60lq5%J~{!uHvxWD0^CS||0Dr^AOW780M}bSap<9x!vAt>dTWm_#rPepQg7Mf z*qnfb+t`KY0VOv20{l8p_u!8CY9dAU#&C^3n|Zo`$PSSo#XL^EBm1hsfKRrwK=zMgH4|kf%9D>P7ww=4rB#GLiq7d75gZMCA7|pT@jL ze>?Lup-8jH-^e^oCsHr+ z4b0PIB4r}~bLMF(krI(#$oxsndqnN7{E%MWtr-?*dB0rgVnnpwu z`3cO^Bq9fXVE@l!-p%}8kw2Sxnm}ZS$d6*4svqeQ`IDHZ%173T{4vZ^)gx;}K8<;* zc%(z*?aWiPBh4cJ?Ssfur6ctsf6r>SR@H&LxV*Dw2Y;BU2fu_udyJQ%C&i&N_1=Ok z;S_lzybEvKtiTfWE4q`18xYgniIk7ZidAN(Fo555!JZ+w8JS`NpYfgm)y#hB4@ z*zLR{ANR4W=R7w8D>P}^uiTn(DntbP(SsoG|GPK*7zlP&-{jG{<}CMUkx$rSXwvCK z{Q@KvYL5R-Z}@2obsbSRO4P3&25N5{HPhKT7gG0ze`TR(6a5~dx39=@8F#`~at{^< zKQh+PghP}51`@dkyJ!N&aPZf1IN5ne5qJ;w8lM6=k_Ljn3$m$s@upnP)&oHIhVQkS zI^5*ZqGtnlK5=v6xY^FuEdrPEvFvag*vdWF{S(O0I3D95x%&i`ZCs63yTO2FdA`(^%lijPW)=1J9PoLd zl^dHy((7`0G~>wtm}Zg22WjNg$vH59k|>OLf~;{naDB<1A|FCN<~j+WgJ3{pLwG zJml6i|A^VBXw;%7^S7p42axSc(NcHMqh#z-&7Tsz7xgS+pR+=2$Nb2Rs1xUBHf;5W z;Fm%>#n{S;Xm~P6b|+JJ-bUx4abG^@hAn=)!Abe{7#9fW@P7f?bq0Db>UQTXMTI}e zqD?Hyw~ESGbeAl8j72l8qO~k)ltrspbg5OOEkn^9S#$%7yjGEiMU!OFRV~bKr_Bh*qg0kG6PAm=}n2)~H|5OzG(EH5kkdGdk@77y)`zJ*% zgn|maG490_qK6uD^w7O@mfg=oqd~IBS@0FadE7S?@_(&G2`V}RKj0>mudnU#NA#Ab zsoiK}kH7Qa7MuS`W9V^$gTU_qcD8Yeu7@r>4}Q1{w?s7jm;l*Y_jIyjHS?FlVl0OO z)%YLL{kN<<_iCLQO*y7+pGVnTPSPJPkilO_!vL4Fs*Zm%Je~TX63JDww)*|1) zheg{T+=U_^}At>nz8jDItqWB33jHTVlYz=ZqkFF8N)#!wR>x>AX>TY z6Y5X^eJak%D@OR>DaM`d`?SbWy3;q~;*)N@SZ@zx9UxL@(!;>e+h=9L!+eGa z$dL&!(qTD*2EryC;gFm0Os0{;Ufs5Zj9UF3f9gZOaBDaJIr^cr8zHUWVLfyUy1do+ zbQW5HAjavTl4?D)u0ynXWj0wE24_YY#`a;JAa@6y_Y0wJmeTo(Gne-&J$TqV8~(8< zcyOlFBxm3jknsjz%H8e_z7E%D`~!oB$^KgJ_$|)6ZbRY0LfsSG?7vtKP0!I=xBKb~ zTR#o&S^;`tU#mwe2!2rPEbP(CzZ>(fV%s4!UjB7g6YR|x`&BG9{<&K`w#_n15Oxaw zg8lMy;jZ-ZFLHa7Eq9S@%n}N@yjcP)No(oJIe@~xt3-i`qK2^E~R>~rIOr(#%dzK=QV#3i$S^eRVCP; zcUDpGdnj4xR$85ZRP+)xPQenl9=aMMYd3a*)uPY}=m4zkG(9vGYKxNA9{*cK!C6_Z z=-YAoDGC*2Rg?MQdPt0H3WBlN4puMO;8_F-rh%KH&@)+NRu7%ddFZlenHhiR!4j-9 zXQHomJ#@WG58bR8O_ve#W{1)JIRpcD^OQ*U7=K0Dmz3`WJ(MKsgS#j6;MI=k--U%k z48OQSHr&I8(UYNo-7H{;?+8dSt`h`TWaY{143+iBtVd;=WHwi|&e=~wPFE12#e(2) z1zBz3e0MW2l$@o9;8=J2 zM?~wCztck}1K=y;AQXjeMoXU=9taEV6a}|B1K*SV;8sT3Z-F4B{SszYX1~3h+2tF& z;U|I9S=NkMs_AAi?c59t!c>}#@Fwz46vA%w(8Mg^jEyfqW_svSGTS;68Yg<8hnky# zSRBfk4)YI;1iM9{DNu_%V=~$SH?kkA`}X6w$PPi?1N!WTGth&4VqbhxW<|kXXCRxE zWnYHNzHBcJ9${b3k$vg%hTlT{$hlxk$)_k7@rEA1J>EHx`+kI2prk5!nSUV7I+-LR$fu9=eD!Y5fEdfs$2Dad0m~j9-y= z$|tx9kk+04RRRG}J(OcyGuxtgT5nm3*xQFGN8aI$n}Eg_p1~}lBju) zRnrmY9b;U8nz=nZrh~eRcy{*8qTpBEF2H3_AACc2zDxE?&o>`d4(DfN z6hnL*<0Ri?_UWC^L^&Lv!#AcV^lUQ-U;%Ed1RnhRa3Newx{RMN)!*bWR((q8Tx?u0 zm6&^s70Ao!;|<{#lr05+kXr)qJyWZTLsgl@p>tq8@OVAGQ;LK8Vmn$6+kD4JzbdFJ zJqYEyS`OQNWhi#8EEVcG+tqzP6upysOnUGiUq0$=eH6Eta_q1hpm@VNemgz1j01h7 zmT?%|V#*7{kX0C{)>TEJLRa@nk{v3@ZY9~6B%ADPeFFsHBJ+wu^C7U9Z@6*qCm_{Z zmA=)7lB z-mA{mFQIgztx&oiy28~wp~l&|2e9TFZB^h#+}Sl}>kGI;#~serkl?To!X1Oa9~SJ% z1~<;Y?+Fj?kL^GvcrP;EZ~(tjI!(7=()<@_So$ZVgK|?kiK3=V0`~)3@xFCn-hXEC zjdVEQ-y&agz^x)Ss4^Ayg z#!A51dI~D)p(i@1eDGv9agpiv6=6z8H5nI9gdN9rf`PU>sFSU!qDp>Fmsr{c_iy+U zB+t=t7Vdy2IvayFcjMvH;S>%q?tZn68zxYiRDHf$y~+>KS?n~z1Xh>&zH?0>ia zxaf`X_y$1El)gon5b00G!mDAnGv*4XBH9psMGwAWxaj;jUvBa3wc4W$gYU{#ttMBB zCgI_lE;rsb5E!7fwC7a57H zu?o~8rMR=^^{XMs$?ksg*F$#;kz+B|y4@Lg1ocBxA$e!u1&%~$vNYBs%miN+#wwQ1 z&eo_XPmunreWd?*BrbhMGL*|J9CKDClFl2_h4@WmubsQ~!Q*YkuuQwY-HD_=E zs7C_$F`Qtx-+mV77wA70wNn4Z3NvB9HoT>(Tra9LU4;djGr<0^?GhEQ{xtFoAk-^aDMwYhNLD(-jQ$<~79F?QoH2T$ z2vWBTtL3%}dT+*L5R(V3%*tv4=N?7z{SO6muM~yu%VNNfqH0yNT^2pbA{r5jmaC$_ z$f5_mEkD@&jjEm89;>CQj-pT?s~MG4ht7+%Kyk!8ag~F8dUwN2MQfp;cnT^6sA#lKg@-+ds5c^_xB5VjR4 zi8-jhK_R@Y5Hyto z??sld^1*^E-R2CujC;A743*>vHRq;)SWfPQW^Q;@HVhB1qiHz!$OWhY|49FqXWEC@ zr?Fx?;l_MJ=)amSq8rDC=^mr)eKnhJ_GRm#XXER+8SkMW_$xWyGWJt@4_|o08=x|s z?qsF~-*!c%J>2t1LV*nT?ija&721 z5kW(y9KnBjpMeBp1;M@47ZnF$>lVIqw?8Yg6ZJWt?O|`c!7WWY3R~azoolN66M}p` zgdrzW8E^StDa?Hz9>wdte+$t4uOj@(@pg)tv$vJn%gY-ciqYxR*tyJGJ=%t307XCp zv0`In8@gGJaMENA{TYPLAqq**IRGIYMF-4)RAvGx?=y5>CSj}>X3duDQG_`N5xn6O z!JA;Fb1!)7`T%+$2*-KrWq1~PTFht&hF!+WcLC^aG5g0sFNd;V)jE|Evnc=z!5bb& z0SNj7G7hd6q7iAz^s7w=(V(e0*BTe@Qz|7$Is(5E7- zKaDL|2*AX435O>(mCbnUCK22=8MjkLLBVe#r*j74bS*iJTpI+i)C&SG5YR)S$!JLOqb8=74Pi*k_AHgbcdZ^;*@K(tNa%a0#1*=Sg{?c~KA+rY z@_$HQk*)WxT#GVZqOKlXxeoVwFh_3}B}Le&%`z+8vmS1hw=-^WC=EPvUTpNAAbVkDJf#Q7->U6$hwUx==C zx>{fz&9GtkrYIj1^ca6(G$tz2qp%;~^gH7*b7|PpgNi=48TqD1c`M@G6KFH9$fl#R zX;B{*fT%hie~_LY>Rt^xT!*@h^mj1Ey)6XrO0bgS4CfwGZCLCK+$9m#FcFns{+Fms z054QFZF+>UlZ1uNtwE9RJd^2+=$XPFCGfpyV7|R!F4#LeZik%K#N}kPA&^sD^oFkl zN_01rL&o3G&)^#^`)u65Kv#VkdP_IW9^C%{zn+87*$EST0(QTICHoMHNcRwWLdk}Y zCAfd{N1)(&RxPSJ{2sFxuIM;x{Hcwn2R|+je#k+%QH_7!aa5iN_8~jUubGwu-h{!rvJl#)ukTH?n{I&Hfy!g@70Pcsfv`vJv*)0| zc>SM3dz=BWev!LP%@T&)CN$#=@Tt79_#8?RWbz%(z*B(0D=-?Kbp|ch8-4^J*>Ddu zNIn8I7ELlMWt#JRBX+d+81G=`v$ZGqoHxwvSXs|~n@4N9lHKIi{^IY2XxpA)Q|AoMB%`7 z9srH5ZGHHBp3xjCj6wyexX&98yoHrEyx|W_DsgcIL12!kd2RG= zgdL@BI^_JQkDANsF%ePG({^1BLb*_bA4p~gGIIK+?HFD%BA=Phxt2D3KH2&yp5kJ- zp7MsjHS2U8=+l8z)Kl)Ks-wSuRr6!;YvZ0gp=7?%LTtg0v4cdJ%@r!v^fNK2!pS$I z9ms1u4Se10keAx2RR;bLf?Vt_?^N?|_!0}R63B6}G!RcbJJ-=NYOE3Rz=Na6P^h5N zCqADj3VzOQRe5A-_RLOqD+l_^uE{NsPZF5Hax>FI8rTW${+6;j4#YQ{zm#};j9nNl zD1=%{dSEBkIwxB51!3uus0&30kaxBo0wL^) z7q{1=EsqU8GBd&*SzxkWcm|l;B}-t z8Lop6h=)QCdN#hG8uVzJ(DFbL#6_)MBbXhZNC< z2T`GTkaEbV6l?oyu5-3+25NCIW_DTAhxf3C1mfhOdi%9Q5nDm1Cr4feP|;7I69!b$@fmu1gAKI3pIn;^^z4xgw7m{dZ^p!uyFGGDBJaOT;QcUaM=k|(f@(LZ z(r&xf0Uqzu5|9+WDKZ(T(ANZeFA9D=d!{-hw-=J=((xnh9m1lqACbnM4I;wWi{bsF zkb^LGZnZH8My^~X0 z?x!s=Y`Kiapcrq7eqwieQ+E>wuSe*daV2U-mIER8)5tDpLHE5VltoX0M&vpa32u@t zvGjl&YwRWJV(=3=AE-)C5x;$t+tW4t4h%J4K;cdz6Uf_!23riiQ;Tw|>7>}EHuK)9`N!o@U z@I|`cyeguC3%4entG6gD|J#Zm3+pw372Mo3n%iz*NuCcfLV&mAjqq(0Y8=Jcl4dZ5 z^L-K?X2w~#IiXSk__oeRQs1p-nCPqnGm{>w!%7d^lQDAG(%m6w1U?u1?l=Ab%I!^v zDL3ooG2;cuT6otjzMA%#Hn9WNtC#OI>_97WZWx>834l}$4B6BC!@0hR#liPrXSgr* zU1^*OnPS0vu%OzOjE8(jNGzml; zeaG2D4=F6>#$4pRc!cP7w%UP% z=Vv*^nB%!&J3SJDES{W!lhF=3Qylyj`woGU%+9e zJw~^X&hjW8^3}ltrw8BlhJOu`o%Ji>E-wK4u(r#Y^cYD9Ul|?GRZVSv8HUH`x+wH_ zDi|HpD`K15*~U}l6pXX=UEG=CG37#Obj=b}g>>jfDnS)e4jRCZu7Qe0PR5;J=p~Dx zrF|G`K*$u~W{AM*wXl?vP;3_yt2pi-M7;VrrUm*q1c99$?CM@nHX}jWrd+3ygpm#c zl-2LPlX}pJdeXB%&M!j<(s-PK#{|JW#@hACiz`eL z5f0iJqGHo=%GA}4j_D7|UlL1(RTMv_FLb)g0oJi(7!LIi-G~p?Rft39TpKh@ufg3F z>&>{Orq(7w-~F6GYFaO2Q6Q12~-2q2Kq z#RLvBj=?bNy<8q6hEQ8H9u4(*&W}fLSz`Q?{e!y;93futIp=z8$vFe7Mebuh4|*-? zq~F6l&H^qECT2O;=iB_BOw9IUy@UPw+#beLVrEjnNsf?0Wl6cI;~+5OYP3 zQFLS+mOOEc|8vA+m8ffsW)Cod9xLc!>j4^@D@ZjJvN0W@4VrIV!cIXgQizWYdI!3O z!*2=a8T#?_$=T#*`3tPHqt~p2maw_eYiu9{6olZtVLpoPED6HZJqA|?H@b;QkI{>; z2pbf9zl8Cc*ej5R!=tXZeUj&<+^=?!`5dED_JvOduwAzs@q@Po2b&R^^>RbyW3-8< zE5VJo_!zmaI5w8%|2dK)^Yr!`kN8U`9l=h+t#h7b61}J>G>=>RdyFSgP12E-e`Zx4 z7q2`@Rz8`BsPR+)$E<^&jd@wO^%_}{DgR$3k6$Bl5Kg&QvH3@^DNME-ebJ_Jf{fQmA3H`NS9D}N-j)cNK!DCacd%eGH2nia zf@kVMUluk%sdTczWK(F^hZIF)QK=}nYx0A&0pUR;nCv`2tUYlZFM@E~ zs7_^n8_|Jypk%Ds5|8w$;t7#{?#|qUk?+wHZ#csuxDy2BexWcSGo9XiB^j%E8m*+f*!dll&Ya z<7_;L6=XH(?S+8iXmxubRGzM{y)c_Mcyfy=nJ46k-{cLqz+gKyy0LHjj7V`v$Fb+_ z)13t`G6c=-nQ45=Pzc_o@EfvN>Yd|k<<_&2!cICj99x9gtp*57DuY7s2TEjk} z>Pvk~lM5RKg`)L&SxpD?dDdb*vkG$@r2)o>Y4hRvI1ltYH^6EN72&|8kZ*Aay~fU; z#rxm;jB5cHjr<;Dm7T)TbdLo_Cnq>P)ZpSi8#i;%8y$9IEiUIi&YVaX6#T>!J3+a4qbrr>wFA-_hFKl#}^QG!x=!`IgNZHWaVrd zK^j(RGfUh3ZoU2HOyeq15j)GqCmW^l%@S$M_#P=6JVi7pS}Udtv%&ta5CpYu7yE^B zP_GyL`YmaP@HW&V!07CuVVnRQ5sRxi3QrGQ#_0@7aqNSxFWaz*)uA&uRv650t*S26 zw+u#z-T07^UXk1flkZzNTj!|mrit!0oh1hjya%p8Nn|{JFdr5}P+>7q8a#YeORj^+k5O6zYDrMU<3@C4{xj{?h_crORaHP=y-=4f~W#1!u# zoPy;Dxs3K6#Qu0F-b4p?pqsLe8LI)rV;;Zo0|*dl>z0oy+ZKyk3_z>MzhAvI3G15JXEFPb6ObHH%B-DJ=BzK9AMBX zHdUN~cR)#%_-GO^k|g7mRJ`7YgXV$1Ksj*i+!>g}L5E~ZjLqOfRL-A+2kEGd2FmdC zFcW$S*Eth@NtsD7zf4iaF~lGqG2-nrAUEOF8<$c1HOB;DQ865?P!yclUXfKVUca~j zWI1rXUrDRn4>qu6l_6b&4xbaqf}g>jKYi41Fpmi)AA`+P55)$a@G>NsLoLk1E37DG z`@-Ki0~ewVZ}?~U?Oa2{$vIm44a6Q19oEA56b7D&(WQ-w_syjjYjt7rq_Il}*Lpg# zk$15C7YonlNnPlU+TEt^7{4G{c}&{_7Is)jeS^!N6YWINdI4E&C!X-$2jcdUi4iuygd%U4`q0_ILB8y0$t58)jFgicM))E{em-hYZK{Pj zJ|=Qd&Ik_>V&3b19S@#5Q-mj`ZsUaj-*MeFAl{iyAOV;cfb0!({_SjL<35xpx@Mzo zE2)|NobUy5?fD*B1>U@b=hTDqvh-pi(0-}Qj_7Q2p2HBY{ha#f{~2b@bjvh+R|_Sl zvF3=S7x?8l`1X-W@U35;DwkpNA!xNlxd~Lv>B_&BQpkbCspok>vP$m4dOq&d3D!BM z`%=gyZ2GlG`yuwXf@->s{mnUA*bg9lh#bUF#SK%8eqo&hHZbdEPTb;C>n0=x1oNB=RKtWEp(!?#TDBSG>C&hef7Y{uIv>_-PJIiFoRR z*cyvM!=(V=5PipL4SqoW<1Vw ziw7a=KQPQgRWaS5zO}c{euS?_y#yFfaLwc*{%iIMhXJ^|>&r!>0XkNAF1#`i0sS7` z$0`+`R6LiG`vbBCZf1xjy}3WYcEq`F`VTYG736VHCvXxJlkMPuk(>~5_~7`+A+#mz zNYZx(-W82s4?q18o*@`nqJ95fycE^O5=?Wc#@!%*m)-<}zw5+3SBR}F$LU-&V{TR# zMZOT^!H@A%5CWdBs*`H~>@ZXX(dAElSD6QxKsm*@kd%Xe5>1&L{e}1t2RDuGM{BJ; z;u)&%apO(dw(pp(T9c+L`bRNt-te;kb(Yb0%Ram>Sq%^LnejCyB5X^&E1SopjnhM2Y|R_K)@(hl zx>M1uQ;r!|NUjNB>#b3QM=HD+W$M+9~Jz&uuNdgtfS;-T+j|IXv-R$lER-VmD zp4p&*xZGt_Klvl_yd5nh%2Uh|SLgAYIl`_5si(}*A(}ncn`pBu}3-!L@ zcikjL6+Rbl~x;n`8LBWi%;#rs;6ibVS1YYUKPJ8%_OJR<0iGuuK5E2yq2CC<)K`9p<)A@lLmX)FQ0`6<^BGl?JVU+a z37UT-Sb*cf_gm2EOu8NDtLS_5Gx`3g)SomoTNFIp%@+Q*`qLcOA95eppT+A2(w`yZ z52HPzi^>7b8c=^+e+5=Q{n1;FBsx|t{AOQy2>syW@bBb1o|Ze=Z!7>Sko;&BIZ@S6Nk9nSf=ARzOj`L4ChzOg@W4{NRfBxCGzhdfH z?stiKfky8i%@Ni_Brs-&e~5Ga6x*R)rUo3^WsW}<1dxW*i$lAjhO(Cgf08cxbhw)> z{%`d``?KXw&PYzwhk1V)NFNSL=)dtN+MoZuKFl;eR>ATleEOx@13uu>-iBnlIYxXS z6;zFl?A03T>U>*0$pGgg$lo=0GyGS+@K-!HEbQ&0zVfEErDH-Zs3S_^T)^+<@g9v zeiMDdPTx>9$#HeH^-q+X?3tX6%yV1lk9^1}KV%bpg9}LLPs>fNACe8>cTs$f^o)O4%QHmYtWk6*Bh`=YlUc4+bdVAa_b{+vgky;?i`K9w) z=C6zA4>5mvJpUE*m&EhmGk=bi#}UsnWJbn&!XCbcQ1DjHt>_)Y1Oo_MtAwdgp_@eT z;9``EXECCHXObvD-w63UK4HEu4wYwip9a~8Z5Euh#1_lj-DeWOM>P_#-F!=N1mF5> z1|02(WeV2EHzBV6(C}eIu}5M=eLz|``%Jca+1uNFWVjdn>A?{(tzU9cDP3%a=tF*u27f8^sXrDlpxIj=;m@rtka zf?wlfQo+}# zNupCwsSnYO==J2BqhkCL-Qd?l@7u@^R6VlEfiG;Nqc+~qbl$~NZw0~k@c4MlW-q=1 zqTAj;y#M;K_ee>+V~YN%SP&B?ZB~W7LPHy!_mX(qEy^ z8K^7s_@0PRX=mVD>|sJX`Q;GUpT4?_nBWu-xKkJ-?_Arzy>zrUbj;@H{hSX#F&6Dm z-;a4+3^SD)qajWGWQa+cdNq1(jUU!=nHwy9g$h7lr){wGRRR`>)0=NmO%YZ>JprDp z2Mw|55Zf!q=_WT`JD^~egALcn}%;awr|TDfi~inJwhwgnxJ#mMM~`t?U-uCqzrFcxbc@|kYme6?G+RN%J6tKo@JEHI&5b1EhH{jv z#<;zj=V>vZ2muS6>(AvAUPmF%!1Bmo5sq>0HqL@>;2ThS5Z|nl{sfO5o-1k}JEiJS zp{C(kC(pUa*@V-d@ip5OVZSr*ITi!n<{!>-iszQjyEtyhkMZ5)yo=w>!Tls>;7{^y zg|l_ND8JRVqSxcXt>$cf3>5I{3$|J2!Qn@Ta2$f0)K@Y5UtwiF#6MwtqszHce2)_w zuWm6*;ap{rtv5Op_(d(fHk^z(HTx?2E!iXJ`4hV;3}kIte`Q8Ux_m3 z4={U%Gw^`y5)L{%2_iyRez z{KF!nL0i~IKY2IaU3iwrvjD1b6W=jy`AqPCJp@LkrXY<};Rl_N^F8Id`nRCOGX_tniSB3I zsecq1%J=PPI`nURy{qxq1SQ@dSt`o8|L9I+gQAUG#4^DL<$e}KihgMRdxQWZ?}=vs zvVF9RQiiHt^#b%`NdJ^0?^1HXZMK5aTi>W#1@H9ujSaew0e8z+(*hmO^~k_@Td|5= zf|=0aYL_~jCBAfsuN>mdQY#3Nv)mU6?^7R5?@mSUr+NsCfm|zs8kg^QJvbTUaU^0+ zoF2zJ-w`z=-H++Fu-_QE%E69~6dW*vPgMr(kp*k;WmbMN5gC9YSBQLQC~;5IFRpj_ z9Z{DK#la^g#b$^9i!V0|HQg*$5zXi{Cb~}?-E^DAK8yjYrd$2R=hQF_a&;FUPsFF< zeR+Hc5|3E$sAF8TK>8E)9mv*C!Hy41-{gwDL4kMunw$QoWC7WCq8>lt`4YcH>4?58 z_0-&NT?OoTYv%q7m=y2W`4-}PP`L-ek#qfy9wS+A*=XCiFPXN#qkUv^dvXbV08Gxm z=e7@Ha3gx?ltbjw6+KP1qvBc@76>i|tPewIjBD9buE^$Z#$mPXaSnv4aa};sKtHkB zLYY|h%Wd#P{lw_X_l{*C`i9g8Hzh3}Nf|eyMGzD3OWh&h`;GPJwSd8w`<6J@7vepm z#aWp+y*V#;yRc)kzt8pW??z&y!|&YouIO<4$QQUd#0HYHe96VZGCY?{exW!ximt*^ zH4**fZjZh~2Xw+C=6?M?p?L}GhndEW^q%NE&e?8On`U^fM$<_6@+VI8?)(+-Fy7B2 z@(J6?QI0Wxl!MH>A;|tn;iQ1=lutSv?1g}%3k@l>Jjhk z;`YC?j$A}SvNNOO)%aqWO6ALh@z6wPH2^#QNNCrtviR=eho-o7?B5*aY$NFOB0c;z~!b1w+W0Zwr=5!@MHGyBQY7ag z`Hc(DD(qLULBNkG!-adSBc4atqAJKONv=(-XS|qFG+3WyZ{p)9i~b9v)|;G#Hn0&s z6~CQ=u+fL`4InJkFR~aEyy1x!UQZm)xXi+u0xa%Ns_%gqXP{e<5soLKBDO*hktApF zUEVdwPyC5*Bu!@pSfLY_pBKS-$nZ~4%20#v?atzRa?@?m<1PN%uE18?RvV7N!gYD` zcK9yAcODite>=~){uZ16U2ooD-zlEv*gXF~oa?W$Z}rCJ0o@*b)r=PiEok$Sh@D?D zDIW77kiecns^_c6S00FE6!;J)S7kf`UzhU`wm0PW>Ac|uDDHG~IMi_&H5?ZsWJK6D z4#J>chyN1J7su_@_Y~UeY{6apyaA>s58iEVUvBfcCe91K;!ndq)r3)q$87ixG{c|C zvGy5s#&5UuCi7joTOlkVQJ!Aks*Mt#6pOk^r}?{i!RLKr^m2S!=Q&&3De(96W9`#y zIL1G=`MXj6J?&TEMa1n}lO6M-cy~~1zF8ZEuOXFhM?(VmoV5RzO?Chg?EyGyYqCqM ze>Dky>O0LGAD9i!gA{;5Y~(-1%X#CY{*UR!q&0|lh7;n2FE`WK zff_Jy3mdmRxU{AISr`Dt2h-8k`A+sdnMTt05$g2tM1 zZFFPym)jQV^^Iv-VXd#S!R>Fv&(Z~p{gv*jx&{)w#I4oT z0#Z{U;T8=Oy0~tUdvWE`%Ef6%6-i#wwCaipQ%VaWswE5EL`s%DYDLHsE> zchv&?rMU{Nx^i*7R#Caozi6BwipKGW?8cP?7k}EVQe`XZe1z02((rfYSbh|sd|Y)! zgIT)7zt~qZPJ$q}an)npD!S$r0N&=s@=Xiw?J{ro|$rCuigABeD3Te3u-GC z`05%gx;1gSmH>W)u!SS&rD@~pSiipn2P7bo-2Ewt?rGy@B+wC3{xKT)knsZioj=6{ z<+CKAe!^J&7!`q552;w>@RtZH%PAcxbkOam?ez;98Y>5o?vLZgyF77p<0S&OAKjy5 zVSesbkKvb> z!VcXM_s8%=Z%Y%gWjMz>`Nea2&f52m=F(dVwKt#m7X@nU>R?}N{-S^zhTzbblzAC}mtotbtfyo-BuW7?T% z+L`YB8JABfnp%n(g(HE_DmFTvbpxVz_In6p+(mshk!i*~+RaIGdWKm3^Rfiz`Zx zLM3oubRpLYH`I1?BWC3^t+KWPu!U8Nd#e|>#jgU|gF zIW#{(|CO)mPnUnQmi>^3@$h00dgkC-6#LPW{=c%R9@^Q67|C)3Cf*=)l}1=(t$vvi zP&{7pRSV^=sO;mH%yEI7OkERIOa{#MO+2&TwUx_c%++uFDry=REX34j>47_&y#n!i z5pkIG2Ec<1=nN&Mi3dwepoKUjAquo6sRTVc3SadKe{vVjS4!i5Q*VGOLSGrU3XjB& zmMR0Nl)B8{zU3mAg)!GRnize{!L=|>!Jk0sTfU&8qLiv=*6vRqRzm9Rqe3y|hrhbK zxJGDdlT}~X_l*Tjiag2?^iDMvyTUMbHX###L~O|dcrDVW~}y6F9yR$5V=@{ zKmBq0@v*3~wh}>kCAFlFK@Wz9Kd6kjcK|Tw7&r`ErCQf`vXEGwJ3_ z|5f2%QZJB%ekVwvAD*cYR-fV?Vg~*)wgpzhvCNX6YKMP5Ta+E`Xx(uB<1WE(oTL&u zgvfOgSwmsp3x9%pfjJ{k2}?^Qwuxow8~fC+j5vME&F-1C`bVEE|toV zh|!lmyo)7~co@h(u&o59=3EPDbmJsx)k+jXerQV0MN)`~MZQXmBg;)5%<;2CiFw7U z4`G_+HH&KN8YiS#6l5o1*0O76sreYVR@fcEY7WQ?za?^_Q3_q z(XysI@ptDrBPA?V`r-{zYO=?mx3ad*zo^>1q;iQ^sG6R;R19E)#bM!$DRIw}FWsV? z9yrGFBET@6BX*{QwZ=MsLwTh|L}t~v!2enWO5Ej(>#!cCCyNtQMSVg@Y!t}d5+oK4 z$&K7CF*_(>l?@HpIf@6hX6YrD(4krc%;2fKZV6X&^ra@I>M$K347Q<$lNz0^Sxy;X zNx7iG2VJPa)=6Xig7Sn;NDiQqQUcP+B-A&X7V`_I{F;S+Uqb!M&G;I^!6q5SRKXy$ zzOrF)e0M?9M1M=gjuq8IgQb^}rpVw&mRk!YO;9R@@+$<%9=)}!vY;L_Qbnn+?)u7F zqKKQC8!dW_K+63MmYK)tb3(E7v#(OAwwOr$HYW*DJE2s=(ye|m1c@b$lDTH`URqvV zR|C~rF7VB9=&vj)tg*()lyje;SJ@?0M2J6b#qsg&Pq$BfY{B$vA51Q-#DXFo+0*>v z+Y|lq5$G{CsHovaDO606Pt#;4OT{n=d5KP!_6-~*5(R_ZM=CE25X;*ELIul+$TOtdu%JxsK{ z3q4GI<``Ef&i`s)u5+CZwy8?m!wwY9QT(R7=sc z!$>ocRPFgxG!N-&q$NnhNUNF0v%zghv+*$RFw*K@gO60y!;}2ANHcNr zz(BeUX%2j4CeH7aAU%w<8L5s(Jv~UQ@J*z z)kxPNU5gXc2GTjtqMuHrc$Ih?(p^XoAT^Nk8K=H27UL1G!`tzo4r%xW@J;xOpg#h6 zq&m`VNXw8qUIHITS0n9U{$;d>G!yAIq&m{QNDm{;JO<@BPn&~ON2((|jI@bxysFfJ zG!N-oq-&AxW&Ty@%dx;mnvJv#sg5*nC+ZQ7v;*lJq-&9GL%NscID4m!L^;w-q^ptU zF#kIEMmh&+6VkOvS0fE0U59iR(rrlF-oSXX{2!=y9O}P`dPohV9Y~wr!gw(MHuU6p z(A^DrBW>Cfi|ssbCS+C!=%-OF;ME*zD~+lO&L zT8*@f`S;L2q-{vIA>D@5K&rnFxtxgc{t*2^n)ea(jd=s(igY#7ypx34QXJy4-FT&D z!-q^x9yZj`VH@fq{1p6KdwVQ)wnP!X1LJF2mcq}z8M5FLz6{rtjLd1y^kt4_?b4Gj zKKuMEJ{T1=3y_wyU@Z6?-J9WBY4>Jiwj|BWaN`6oKo$6x6Nts$Af3q>uDk4$Gcwzg z3NzfRlD!$(D^t7~IW0q`W~}oh?X#t2&(EeD{E^ z8}+q<4A)f|4go&}aJC24wl2!+0>F0wewqcXmt>ab;GqAwT_Qq$PC`+)Dl>jvVZ&cMH$ z`f?Zh(VjdxBYPF~W#tfWMqbO%f{c=+PUuV?Ku{Z?^i>;;hZWujy=KfUAvOSd?WDIV zsUX9>lKR(@GAkp8oKq^$7+_fHp|OuynA3Kk{?Cyn)CabzKP@9?VZZ8ny8SAErX*0D znr^?QA68Mi{rCN_l-wu>&a>O@j>VQ!ZdX!nLZ88ROR~KzZ8HS76}-Nf;m9Xx7l}6uK}6x-bv74+E|Pz8B+1xgX^>gg(#6SeI-+aPp&5P3$_Nkxc$fbbqcC{h5F5m1#vI=X;9t=z73#}h#l9U{LiEx_itBc zxRXwWuc7qem-TdxEYwx_Xx1x6y-XZ-T8H}tzJ$FlPY~D5^sPD93hyBLk9r-b7hpXQ zZ?Vs$5Zy~eQ+{+mKb^ATMf@j$e*pNa27Z`dK~t@4A?o@{w&a(ML|Cri}9;H z{l}*?V~yJ$NQ^`D^pcFV?xKu!?rV{HQC@^%cot|vd>*2l9tT~=W3kvr{}<#$|N1@X ztzC;46ZhovsCjW(#yX7e!~JHcDe0Um6K6I+q-!!KPZ>YX<5~!7o$E+HZb$HM_RCDm zq1^<4R}VbOE&&gAvkcmtuPh8P1N;??jaVW+eFpVbqaJ-iLOu9zX*t9t{r7>l4tSF& zw>TddRr|}*T!1-^E>txJk574@0*wmeUAe#X)eG>i>?2Vx4tSE@vk#~kV*XeKdW%6X z^DnX3x6}{FTj@Fac@(|kjGi-+G+V~DGx9TboKb>wG9g8F+?4`yJyI_qoL%ycj(spQ z)Bs}|Ft1UVNoS^{DNrH!$N~%BBW=T4vH|y$ON$*{tCm(WHNBA$zhfkdvW8*b19)(o z)Afu^>c@?EmooM4=1KtLByMV|a&SzTy*Pa=?o%c~*g7W_Dd=Iz|_$ZRXM0TyRmyy^* zClDP|0CP!C+B)g zwxq855Bw(VZS?QQ7QpKPKZl6wx&Y}v0dE6*0b%ieFuwDEy!0l8R8Q$+h(^9fVy~nJ z^=>C1&X-uP*yC}Jm|)rO0^qwh^yQ1=G7s=fz%M18%QD<|X=%@;sFP$NdL> z82B7+^J4p30AJUyebIlw*8<+J|5&Fn8X2(rVx2h>dqI0qZ!o>R0OdK|efvv3iUH3C zyvSq@Df?Lgcp2dTX+Q5{`@m1^C*}D#;JW}XCVkNmz>``AugpWydfl5eu~{4$~E#J>;tIh$g!sl<=#E%p)MoUQ1S zg)$!A0{jx--#wWBD~3PK=uk?8!Wj?smXDj2>dYc zg}%qf2~sMz@XZAk$8|CAcLBc;_X*>Q9ig9F_(IuGO#Fv|e;D}Fe+1uL&kFqqe&+UA z>|Elji}3dW&jEal2}DZx_kinwpGi3U|86NhJX<`(R1?WC9_k1k2mF2LrqvC@Z&_}L(p$%@> zTqd?aT$5koDsg6pWAoM*Uy}3hKS8GrbWA&Hu{WWmsp$x7gnoUG@-WKBvmNk(ImcRP za;`cBd%wF-?q@mVaF<=|r(s`g6~YGWTS7CipLTghHcZ*tcOtS}SIk5GOzf@i8QiX# z?MWy0H_R#NmHq4pu7PrS9Q4*=Pkxx9m$;s9lo|xXM4Z%YWeU@=<;3aDTuE@ez5<OL2j61>)| zEfc9Jiyl=#)#o)-CK?_Wh^sBtBT^IYQhAf_CW)q{s?==%NKE!;heOtnsk|xA11j%R z<*NGs=gBrD|NafFRO9-9N*`6}dX;Wf=|5EZp-K;`bV!Em=kY2%OQjd7bc#x^QfZY+ zm#TE7N*_?^qbgmm(yc1}he|(G=|PnaajN!JdX`ErQt1?xUZv71l`d83N|ipK(nnRg zUZq=A`VW{?#DmHL5&$sYl)QP3lzk zxIxNisPa*RlrQN^ZqS@*cY`AID{#&6^n=K(XO@KLsC11=2SgqK=)OkQcd4{Yr30V} zQ2n(MzkhkzAn^Wp{qtpWCA|`r_Al?B@0cg?O?a8gm-K_1<^L{SGhfo{Q0c$JUw56v zH}N`Deoa5PS^n?RHKmeXhf4n)e*f|{WwL&M_K z$odCPkoXUovqK^>?j`;$m_sFg$B7dEBv2UWlK3ZiP5gD^B)<6MO{egt8jt=}B>uPSO?=Y{O#M;$2REAdrqM_H6IOXif8_HfzG>v&{wV&zoff__ zYW|75fi6k^)OSpL(};hZ{$n3n_{wMx4MZF;N&mS07QQmlv>)Z?3y_ogU>fJVAEmz` zX5wqg2;cfq{5OW$6#cylKWIjQ{pv~ncVWLk@>9RqBQWm!QT~hYNiT`-s+IVygUnWv z{^&DJ{5ACw|Lr8v%uzqfV5d?Q9k!u}WQUy(#HVnxt{I9g2EbpgOhj-hveT_oAC z!X*1pKk$>2Q*c^MOL5F6hHc2NQ8WZ*i+jfdL`xpZPwk?F=eLsVDM2)w{fbuBL%n2vjtBJIL5)g2>REO(<~8!0j%_*OS3tY9j#$ ziLa&h0O0uUSA?W4LSv5adGAWiA^Zp4XQrMCl8z(1cc;DtI*u6cv(qZ^>(Fe>#NV7E~ zQ=QgJv|%XErU4f{NrBh$7L z)LPPbV$N}jji1D7OPj#VXj=ymI?_1&j&p4Ph0N--cbPd?WY(ndw4NhJWY(tbXC_x< z)}*BBimg3li+%S$a*&co|ZV2UQ3MjwWC7{P@j*;lOJGD9jQ0hgH zT@YRqAQrDeWSvx|TW_ zyrx|=o}JYkYmmuIo5+&XD_Agzf8a^WsK<*=X_ruZWSlfo97J-u(k4*}Qa7L|ZIYz; ziMW>(T`z&zGvj$^hs~vR!b#~^hvvM53X>p zxzi`IL~xZpiKMg)9M;s*FQJmUQZEL@^h?=LCU_&g6bCDS`V>25!ak=@k<{7e^eKvZ zCM4-jFFc<}y^v)36e@_8`U`NHK9v+*sW-B+PHAUqcK+e2Vdq@}{9}snb1Z+*+6*L) zHc;MXpIrg`)Vp2i-qoTs^$NbU25&82<; zrtH`8J}>nk>6G%`lj_5-y^KuhscpPp@F41zq@In9eR~CQ=A^Q5du0*s%QEWlew)4e z*TAVx9R>#NH5|nH)WyVEO#G(Q-Mn8yI?buCvE5q2+fw)OzK-`Dsju+9p7*O$-{JiY zykC==!MeUNsJk}xLBjoPH=Oz`;mb&~N3;JC4G$Y%2hd0=OB-&4(2f;%LWffvcL6$t zBW1q^bcT5!Av%fu*+kdu(~uoDHEfo=$P)4MChjlFM1Bt2|8@$I+c zXV}ay@hgbZ*UPG+c{?|Hhh2LNpd$%5!z|Y9zh}j=u`D4s9+rq<7Zn|!px864;?Dr5 zK95OnSS4?@)X}`JdII;3)E0QDVT<@xR9EWxskpE1#(idL8F6Z!!@WE8QsVsl1>9$+ z&fxvxS8<<{TFd(-Z{j`=y`qw)I5@y5j(f!~rIX_Lm-scrFT0lFU=JMA{ttU^0$x>h z^^Ko>@6EaA+z^rw2mvw?5|Rr^2niV&B9|!vAq)aaKm{qYfG80s1O!AJ5OG8sl{(L2 zt1W6(oLX&b#j&(jMX6P*RJ6_m{(rx<_qjQNq`rT@_x<`j-^+cTbMD$}t-bcz!`XYE zwb$MU>?wf>0aSyu|G)&@8rl9e+5szZ$ti(G)}lPffkw^7hcM8{*bW(*j*K%j`52ls zgG1k^vZVfO%kKNgSyDNhh~*v!d{PA?IBrD;zyny0Deh!6g`~=R!Jgqhhpr&0ijPMw z6k<9F4!gMo=Vrh*3%yI0gzrGuS&FDds`vn4cOL|+5}i~KcvVW+arq%T)sJwB+lZ*D zjBti~BH{jobFK6uFa_4JCW9GxLc1`i$Ai=j+x3Vb-2Gyt1DL&b zk2PexPZ*)Dr12I}59+u##p6Dx-Z1pMZ`#8VxIA^BR?a#fCpZ6y%-A8E!>pX^e4ICWoEs0}9Af1> z=;M6O<9y~2&WTpforZH6N(m_kmRpvU_beZZBt3O8ba6WdO*P+-aibSs#=t>%F|hed zEtRbJ!2TYg0uE6sMe(^VL<8tO`_y?DyVXPB4sF@>*y{Y{mg*!kJ$m=#pE9?}QfZRh zg9sPig9i2*c-`Q{vtp2kCzsD{au*#SLJd_{q63Ix5yJY2ZLAMDZZaGI<)@BTzxKE~ zp{-ec%WxZs9;M(rX7N~cG{U1nDc~?-!fO}~w}g2bYw;8$^r8_@A>fUk4cvYZqbCC> zg23op035GM$WnSfLfhh@D-rq@A^t?$A?Yy*pnl}17OO>2P4ZNaxF@?Vpm6P*@ z37m((X7I7LR)DF*Xmu%>#+tx&2t2Hr+~Ds_DOx#a81fw;zhq=AN3@?B{4asuAII}) z>u=@!+$S?R$u!W0Q)61|evcaR6NsDR7147D4A#7X)1c~&8 z88PD!;!ib?$5Wjx5B^j>_lwX(``u@_`a-#WkNE@7`2(-{0_Fek1*{4lfvV=H#~^%w z2%kgY1CGFRrRI!5!UL-LD9+(1TKSU&mYPRom5e0wIg(Tj=b*AcutUJ477F-V>b!_z ztJ$Bt34lpi9r$|mJVKJ^ur32qB^FBM5edIRc%bC%QpI`*ZrQ$CI=OBT_l52L7Z}}t*)Mz(& zP%rR-FM*>pk7T9aeqRDTzcT^mi$$vQ)E{-w&ubhfQIV`px?VW3+UAp%)7KD~j|Y8( zer1NheDqvp0?ay+X{D@Jfxq%spw~=UfsyO*C`s?ZCM-`*$g=rd95DiqSNJxLp+|5_ zQ22HMdqe&39RhMQqdSnZ-XpHUaOl>L0XXz#+_bTPP>f#Hyux!-Y#k81LOFq9cj2E` z(#TS zi1B5DS74JG#;;HE>R3JOY-q_Vxn|h0C{kX@EDXj@ew zW`bioS1@QSmkvGjTH74MVrUF~CicVSAcsCD6a34NpU^j(2|6@1hQ1}7;*c@)9pMaz zhQ`nV!nqC&jiK)e7rAqB5216$UztloV@OG$#w{TwAc2_6X;>&Efzd7vjiCeyOmt~z z4A~Nx=3dG;j-o=dTpAif5%n7c7PvGthB_!Jyv(JcF_a>KCYOfBP$w}bp6=4nh?k!! zU|ZwT&=~5fW+1TEMHga)GSnX+ZJkR)V<=Po6@iT|4UM4!bru*dacO7_m8wk$Y;|cz z3iXq~HkXFRP?-d7b7^P{^_ReQmxjhrxnhQQxHL3|28eDuT^brgRqAcvp0WauAe2xv z7=g%G+;}B!KqR#H;TR_}Y-=M>&IqvCRvqz90q`0QIa38s%^YW698R%1^Z+V!O;`+= zp=~TYhX%~h&j?x$4Va-DSd5PIE?IA3DPn%%YzEGDwj&f_+hK7Sg=~ka2=)^Xn+D!5 z5lA9$;3;Sjp1>v&z80Y4RKdI3a{fTL>L`F9q(l2$X9z4^eD>8>da?dm8E3F@DC|+Cu;}|s!LCUewM7A*h8o) zN-i__>^M7(J?SzD5(YKZ>eLS))Vk~oR=k6-8h5r=Ie^ijnX#6uAm#7`i ztvrq?bt~)^lSHWaB&|*GUnrd(%M9W3xQgA?B*sQPuV%sYq7~2$?!SAM2(X>iDE{0# zSqJ$P?FMu2)TEwzIQf3IC&mV_?gO`!lq8?-j2>OQrZ)wSN`faT+ zd834Hp()fd1cd(05FkrT31zo3$I|oLpA{nC4tsw4^VzuTp5ONM{I;*>w|zao?d$n% zU(av*dw$#B^V|NO-}d+Xw!i1M{XM_!sF~1E_xyH5%|$@>{B{RLg>}zwr$|8e{Ip4_xyH-qTNvU{C1`~3jy8p+y0*4_V@g@zvs98J-_Ym`E7sCZx0aN zbkA>BsdJFdQ&xmMv~7B5dmA$;J+%EZg1U#cZ(w$H4{hHv8)){>rNG$^>oAeMF4gX^ z0RXYAO~G=ZL$K_juR=h2=z|9T&k!diT=hK24wq8GRi6U19Ga5DRbLZrg|{J)5gtN{ zblBlq3(uBbJ_;YfPMS(lhV76kIVD^x-9DF(!nIoBhXNakW^5P)F=lKykrLx$14CwP zIGKchGd55L$A(K8MUM?4%Zv?zhF-+Q$A+JiSC0(@QD9W}NnG)<;a>Iw4IsJY?BCP& zqhqtuwg9Wr=rI~d`zLy{U>Xj*wnAx3Kut(n2CAL*JTPIXM1eB;SybBG%;q0F0uS4Ky_J)TRLw@tqFf&vqX135^(eqolqUU zL|2G1bR4}zcNE|ZM=#O64!B5KSKv6AtvY&%j&O~mm*@z`oF?1@v#8W)XCvVg_9E^? zM=#NlXPT_w1m?~L>@Cr~51s{%UZUFs{4!@TlnpE<|LKliqNAKOj$Wc8exsw8=m=lp z=p{PBTOGYbM|hjlk#d@rBkpa^)tEU2PG!0~9KA$GnLG6oUE;vYQ6^o_MU`+3OA0Mr zAeJhMkE>ZptSMQdOC0tj*(vNrkKK|by2N_EcnRfWQsnk6AjtRve@Uzd+u=wrOLU11 z@}kDUIdO6Z>Q|x`1JF?=d0C=MoL)^b5p^C}FVQ8=n&d^CONvyxL=4LkUE-V7ybiquEQm|#6|l77dc#_OI-Xp z;4+6xbcsv&f?I=Jv60#iTbJ$ZkULAqcDPDrJ4te9)@+A42yaFCrv&Py+3`US$a8AR z+I?UQYYr(n-;xQ^GMikZoU zLQKbY?m%VP&PRZgSTnY>6%jJ@${Q;v*cs*BjVo_#Csy|n2&&8gg{vWedtf;tGeBBq zD%4R~0jbvR)eIC%Kut4z#lmNGe~q-sTEjFZz0Tb4{Pr%Dv7 z`yZM3YdU@yvZ%vr0p>kxuBBcNaHPs+b*VQ3>`SxRk*GI?Bb%F&)t*3a#LNDgio6ww z;ZLXVwg}Ck74v8ynUHSoJrGJ~&e92?n0n$bNf#kikY|E&G!y#>FckwKgE)oBAf90* z;WR)i_d06voTTQ>CdV-jeyaIHaP;JB6gZgX=M2k2&mRL!o_loikmp@eW@jPPi?wu{ znPBgskXHk*f-}D#@K1puzW~730IY%=4cqPlEEi$5fRl5pa4Wdg08wa6n-CWv@$ z@N{E#?X9L|2Y0X3@`Fw1YT#_LmX)HVr>9wcF7?Uot)`+-ra+$iFEvTa7lB?>EN*?y z`YU0R+h$^NVBt?Sb1|q`s;h*L>DS0!2@0FOW*YcqE3&Pq~P|(ZheWy1&@LhgYFD8CGi!YAGeyTvAKqH1KJXQs(C2tq72H6 zr7|m_Ovz6y)xf}O4B+t@&!;C9oo4^5n8yi>F#y5R(MA}vSAL!e^g~@U(UoAh(%`6H z=`$vf%fJgJKoz5Z_63%j=y^@sMsjavun5gu2mg#9tLJ=H&v!62m&`3UA+F)|m+tNY_ICe7 zkhOXtYqbz2=l+~+s*Bi@JkOxi>n5gsL>C05rC!W_Wj2Bp?EChC<_2f2iHV*A*psMz zrSBR2rDdjPX<}cLb9AB!uuB<+=FW$gRXX3`tH3l<@N6lht^#$1p$-G}>_ezq4Rsu- z4{NF$Jo_x2DxZ}y{g9vSVX*MwX>3LBF|pZbUe>XLr&+dD`i3Eh-kI4tGWGsSxU}9C z22Z`a;%Gpzrrtd2E}k-*{1zYac1@H_ zMIZI0`EL7ZmbIPcGd{5))O@Qn|KcNNX`)W^fFZDqD&w)NXxAKFrYxNXAAY#Ovs|`l zd|TymdRsc82M;)Zw+dGJWI)Td;ydlehxyrdb|d8^K9@2qvz>R?U}1t|CA|)~@40+Z zzL*WnB_^p00rRJtdGa40)-U*6-HAt~j2@io3qDtobaeTpd`!M%a3O;FG5ea`=Q9XK ze{KYRgtoR2qM*Nuu^sofqE8swF({jJG)_m@fC40!g)CJ)(gK!I$r`+PsI{CvAgMNj)!137+VuVqF6u9 z#YlBEIV!?o}&W;J+ZARXq`09nqEL;h0!UjZ*CP#={I#dVO9Dbo8ajbLsZH z7-5U0RPbqDjIhP#*G|*PU8Pwt-DuGWO)nUkfb?pfF-3%SBEJ|E&}x?Y)YR~LC}UM` zL$rBuuH`;14R^>S*Di2XpgC31&qy^%@l6AzW_Hh*B!r@t-`=>WGkl^n{0t%tJUBwt zIh1&Y`U%Roa~4uKU595w+zAMqdE+LNP73Npty9_1MdpoxY%^~x05Iqd!^PIK0#OU5 z^@>r1^$7~ZLsrKiqY(mh!8H2x)bMDC(zDf`c`<-^m1e3ZsLQ=nFZWX2!Bhw5#mE#- zpnOcOM%)j6ohrNK<$yU;#A9aYtmoaD=b&!FD*evz$Yavc7+a@T`1E{fRuuD zCh%|C&$d-M@}cdA{;SgAu(eBvlWryP0ka);6-h%aJdK>8t1HFAc!??6GZ2qI)qHt| zej2Y-oGQGAqA9}s`AWt2_6KtC`XYVNR*9X5!T)OEyCD~z7@zQ|{i7P3AEPK;_`fv? zYzu&Pk4i-olPcexYpVPk0E7N)xLD5YFnWCrQ6CuOE=K)@s$kRzRL}0|ab%j5XkAp# zs;M5wG32N&1NNUlRYlkse=Tc+XM!TaFEUY=G3w*$5-;kjj9R+JP?z>bMc-ipXCT1C zO5=^e4L*5R4D3W5C2iuDhLyFt$FQQUl!gj4AMw_Z>wD=?1Vs&Dd@n-N;+udKwT%QO z)=$&%5P<9k?=*>7(P|&ll-5k1Mpkr=VOolMKgBSy^~%JfV6RVvFGJUPX;_s4|l-Y=ZZV%Ycfzs{ZOKAr` z1x4DyO(tqe2vNUNTfL}XFly;mLzQ;$gbA=}c7e}VY+s%g6TQ85@S;G+_(TozI3|)~NRCPAXrPfwtG2Y>r|?;t z?zN>CjKWs*Tf@ZX{%XS%Zzjs#}(Y=Su z8cI9TEl z!^AF?dHme4Qt{!!+Dh?sQ(IJgerrjal zGavS)+odo4S0D5XVl3Y-=H0XU;%WaiXK6ndduq30^KLXB(ctv3Mz;+ak~{R#fkaRC zj=;;gcs^+7y%zDL+kFYZpst29uD?v-hJ0_7+mBcvwHld{F{mJGpn?cXe2G_%Zn6EUosJ9)?^~w!%AZ54COk&8YB7$rE{m6kHXX>|8|fv zOVD@!0>EAZj{qnxMT>hDz=&6Hiv&G0`t>8;Y8{U4LwF*%M|=gq4O*r=t9POM`+c5C z;A$|8BxX2M8O{`9l>QBqVG6@3b~t4nBu8xTdyMK_5>*ERvniH!(!ZanllcG!y<@mo zC!A2%azZ_!uP!ajhvcX1;6_Ggk9;j+Sp~-#jy8*Y0NSh@%vc?h$Wr894yDM$lq(5c zb#OwT2R`-^dX))9cNqOHK}Dya=w*WI6|7Zo+Yjrp-(U5u^!Orxv6P;pDm6G8iS0DP z{t>V2M+)>)k{YDNA-PPCh-pDuJg-7XkC&Z;950_kc$6&VbO~}P$5;QOWK}0U$kiAh zTCYHKm9*0_u8X0QNxH#3rtqYcJ~+6Px&^!Do9ft#c)Bbn7RLZ$B$hieRUEAKxN^&Z zlxgH91va1$NihCrp!4*CKuTx@)^-BgQ!SuRMhIy4wSYbuAz)5MSb~&->(CWj!Av9+ zTu%@}dE^3}m2YHFJ1YarGZAdr^BX3?`3$#|P}N)blZ~&TDw~~vY<3S-@w7N^vwLU= zDYC6RRNIM65pH%5na%FD^A=DxjxxZok`k(|AoHU@g=)3L4+S<7#ak(%Y8;vVz|yTc z8z2%BPu7GX3`tT6wd+WbE#C=x2fHMjF!ZND(>;bV5>patZ(+=%e!b&MnD zMudImMudImMudImMudImMuhdb5y^8QAZ*T!NTxe?*qj@Y%<2o9b0d~Q3zF&19qyp0F!s|hofHW)CDWZd+)1qh z<#f20A&{;XBCsZz?%ZK>ZbWh_MQ13U6R<9s?%d%_bw2_dlj+VKE>P!#;gV#!bBE2j z5y@{cV9t$5raO1ooEwo$ckZw`HzJwt+~IP#%)-eg`Db__!UIINoym0P4p*s%!0?on zf|pM#Y|f1cZ(}Cq+=%ec2H2P@loz)Q$q|ox$J?Aw^oSQ!8iWM|cK< z<2!@F8Ons%f+^2-7y;7j_@xVZ6rDOPoxUqhosljMUe2g%$wUV)r*14jeFlS5Pl@^r z250nrB0cyZ^5K@#$E@=&aO=VwQhN}8`V5B1)IXvkt2 z7$OT?@aQubBEB;iBEB;iB1eL)pHU|1Y#N^ckV5Ms5K9%s_a)mtYbw}?*5wX!P)F$$))jy*^;$FeFxXMibFzFeFy-@yLZjOviS94KCYB z20hF|hcg%IsOz6F;;WE!YU)_VUEZHzRz}8h_zY;UTX$Ju>mWU0cUfW6Fx;sgql6jM z!JAFo512pIOh3#_aT%DweB6auXcpdVkEQklDzFRyBi8X8eAh4lYu8Lto<$&stAVp9 zRprt{O`Sk^5PITlaSoE;V+i)FD~*AI?O#YMPYhRivc?0|1~Os^BQ7&ccL^(1qfd@j zt%Rz522oV4lnPu{skJGSQm*pS>`zIP5Y!q;@EQbr(F?-nR4bUWE+#4DUt|(o-WjiC zH<-XG1Zc*~gUttTbOo;(Jd5c8!PD+$)bH7;pO$q#Vj*L0@Pkt|0t~7QGTh*rH68}< z&R*JiT>nN6WbB|?GNN?0!pw4y_gGzHn3G&1eaco9+>Eg@B7 zN^}O8FnwPelW6(p8Y$N!_57ciz?}>{;itUeqZGJhIx;hs0&$+kH3t80igug7XgwuN zp;9v`)Fdm+tn_{pH=MJ;Qk`f-4@ax!Pc{D}NoO`$vR94lMJFTI;Aek+CBsc*)YG2q zf!OzWe+Ps+iM(47X5pksY&K1L`HhD3E0i~WlHLK6UJ6=27WyCe z(!0$|?+vE6rv3ERx1Zkmt?}Eoo-~|Mp5pC(lmjAp#^X%{byL}O$42^T}wYv*R zsP&Y($I#_NPsBD?ryyDSuB-Lz;FlxWT&zdNd_ zuOcY@UOi;G!7|hD6(p6%5Epi7Xvp&dpjhi-u8@wa)7luhKt?fpk)`80KTkE%WuPF_ zNO~8NUeJ;P4!S-1z!a1jBA@^F4Uv&X8vN(Lt7Na8SOv!#e4q=47K2}b)Ws7f{|rM- z0=3{$6JV!2vPDGx{RYoouHf$`z(HqW3sZqpq4UM;Z_@bI*+(U&7#@E7v|zk1!W(Ty zkn-Akj+T39c|BtlEcVIBOKGFb^-u z;}5DEDe7nZMeV62P4fe+1Nvm2&>}lOGC->~7-{CGnm`=_J*!QCsUGq$=!z*b`I5?Q zmEt?*VZdySpP~(OCMUJRLX%qQWQCstTpr+q`YCHAW)fTvm_OD0d?&9~KIL<+jzBeF z@co=)?;r%E8r}qx8{D-v#yD2NYMWGM-~EvJ}>LR>2iU#!)DT zi?s~4tDe8|$v#A`X{-K9XeIY+Qv%diN+8_|GlLb~SOOI?rtUHcTqL@&2>1crsC0D| zie<12+18_Og^aS#Biu(u58pwyJcLNKjR z`mf%VIu0xTL7^Vh6Y89LE{IZsUOF=13{>bId_oSiFJ!LK?@%FF9qBpE2w4cF^~1nk zv<&TuGs22%Sa3_E7-ktxZ*riTkAwEQ;Fd_iosNPTu+Xq^T4hzzY--;|>qaHbruOXw zt30!5!sM#gQQ*81=2dxy%?>lG=<3WXa(}hYOj_+3IXfN*)m#M4*5XRG2vvCo($M&- ziy)3y!bMedUgs6rR^!=Zlf@=mJq3|?MXpj+Q_;8ba;8;zyL}T@RgVXi7xs}>bLE~_ zWRj}73^-o)zN&jrxx7-!tLNj7S8B7Wz6{R}ugHn2%Cr53)>L`6-_R!OEW4Jgq97#3BC=%3BC=%3BC=%3BC=% z3BC=%3BC=%3H}Yi2})dc^idEA{tdwi{tdwi{tdwi{tdwi{tdwi{tdwi{tdwi{tdwi z{tdwi{tdwi{tdwi{tdwi{tdwi{tdwi{tdwi{tdwi{tdwi{tdwi{tdwi{tdwitu_QF zm<_=REgOOpd>eujd>euj{`)rsca%M^u<_!#O@0Bb6jP^f(Qd6k@-+;FlI!^AB&YK) zC3yn>a+6o^FP6N8e```WOI0bgxCc^};2uiJ#T^sjYw?&mc2zEf%eG&;Y7#(nK4M6> zhcG1jOzqlZNRW-0w%%u&O8dPXKN5m6I;GgPr!xu->2|Hil5M{RjiHjbG;K(?N6`W+ z+kWjjF~(Yn%;xR5I&VN~Fr;^hZ<-DF{{!$TvS~I&HqG*-(F!2Zu-HgtD_*!Z8xOwT zG#jqv=b5}sv*976NM^#d6lJB-rW*EbnhnP{&C=#-JB$G7Vk6a?vFo&S+LXd|M!MKk z!=qMGI&G@qI=#1;&e37trrEH6)2vf|9{AHQ#jPuENNp?r^rl&NDo-1gO|$MXLX}Ok z?lc;<^`=?3k=ro!rdfCTUjge)v#xK`tb6=s2-cfs-I;{-rdijwY1TdAUBuO!X5BgD z(VJ#ny=gX;HdS}QKJe&Gv+lx8;L)3AUEiizcM0X_O|$M&%F&x<-IECGO|$MY!g|xJ zdop3YY1VC`eOParb$y#=-P0&@r`|N{4BUY-$za=f0e}=*SAkg0C_Wxdn?!52Y?^h3 zy-W7lAn}w6UB-bU5p?PmYELL#0CbgGE+EM0fxjfygKyKU(?EVX*VdU#qpL*a!7Vzf zBrlt0o#``3CSs9C)|+OXS*LmtxnWf+FA?>oS!d4YK+C3CXD&g#Y1WzdEzz=R)|t4#f{%p&#A2w3XCZhMlMk*LV?}v@}_QOUhzXZG94;!iC9nU$`gft-`C&fVxjZ@24iRaj5$+%jqGL&xuUA#{wa-ZZa_qKZ?m_>aDEg z%t|783Kh%5ahw8V?L~L2rV^J;W8;h9V*)wca-1c$*)=5~W{GK5z}zhH=U?+Da)>Mc z8Y%e zT!ir}$WswdFWnE8qHX{N4#O0f#6I~5?Leq+iB96N)qQFJ4H^#G z2mn_S;MVKKBfxYF05}kNNxhArm((Rp>hjH)wHDFg{xz^iiR<$nLI)7)W1~FlM?y+6 zfY6p-f4{0vcR)7-Rs3NURKxEHy!oCC86|jMUj-xH(={6ZN8mNCL`{*nVD7gc@^1xq z*+QhzrwO-zLYUQmpox`(0DnBTuFusVcNzuYCID3cN=F#BKWpKt9EwDYBJTi~h>;SB zqTnA}EyIUG`m0Tr znf^O%rC(+hpJ#X&_d8!)YoJz&arq8_oVC`yTy)>nuI|l7_fp45VhSuDXa?QxG>w+> zZ*5C{g~%W7m+!TDDVvAAve{uIl|s@Vv?P=niq{QRtk9XTir+R&gTVAJ%_NT)*x;3$ zi?m>BwaR;#22lsL;%iOOOhO<7j~$fcGZ5&sBI;kgDxPCCjqMG zXQM=B8L8|~_*2czY;4kdJWsF@tyuRSyV!f|K#;x1i|jr6)Zsn&S>#Ix??NyiHusk} zaF^C2AZt!9Go8~Al%C{P$b>i8`WREQinsc5Rf9LWLvp4oWVGQgp~1w9EEV!9b9Oc) z-E1VWLXOjtfNxzP<-asc$Fih9G=cdDY&0>X3R&L8-x=yDpx$Drt0_;Y$%AwTSWAx@ zDr0r^;a`o%vWf>AJR4rmFof{;Gd~k5|foxY;JPywWqc(F4Gk~<$9Q@s_sVMuy2e`@6ore!_`%fI;rtLKy{%7npsXl8+AH2x4 zV+|P#lCg%Y1n5~q))Gx8KD=jyhmc~dArxh$wQmifJiI&NPHPBX!HqRUOV`#ABi&d- z=+I}ZAsYeeBgFoVHRK&Ib>5F#7v7MXc=v6@6OOO^Bql(BK0-{W<_NL%pn6A$-3!<| zLTnJS_+yU{8-=*-9wC;dzw*QP)gL-SjBLq?H(Hb_%EcFR~pW_pz6q{wX*Ajmiae@U!|R@M;m8*9il07d9M z0CZGI-dIB(B$2POy_ti*M~>0qoyc?biIn(aemB+#hYz|iz@oT16b(4-l(HN@@Dwk*~V zw|q0=Yio#G!3f$K;`*#1ZsiQHYio#G#m6HT3Nf9opCPX8#6X4#7WX8Ud{^3GZRco4 z@|_cQ7Q%8)7#oAteL03&)t_z08*JYK%%5sL32&f1czBuW&k9T)4O>Ff5}f2vK}vAT z{+U?_h&5pbX!KvIq@;%#ka@cVyN!YEWVM+-jmJ_N&CfY_B{>auzQm;D@MSXTycm-O z?kI7n$o`XwG96JWTSwWctnBZ6bWL(mac{kRH<$pl9A&FB)Ag6K5}Bvp1w5C>_R*@xNJiVte7&h zd{&!G(7vcHJPr+^<`u!B4ClKm)3xMnf0QUm82|(HFCN9GA``!ihVT67Q zYVO;HdNL}<%0AD8_)Qm{b;7o>0$f)Z{6^rj?=gYP5#Tw;WLn7v1Xi>kXSvcRfLXaO z8O|Fi+$uf+n(nQQ5w?~wCy%URPEH0G84p~4GJJDHjC8Am+`)A3jUH18nX+vo;T{Nm zIG((fJ;UH10{$f*{zQZS4e%cr{Bq=Jxxw=>%-(7OFMufvCX9HVdqJ>9TU@r*dDl|9?w zS?D~?jv|(We~}MA+Td3Lzs2DB5YO`QKVtBSc+t2+gOZhE>Pe;n17(6Tu**%8p0%w(E}_B3FFw=L~gw5$DdM*H$6pQLuAc<-=iQwNp7Z;ce)f9`}Ly8l!f!Tv+6)i^ql z#O}wkU;2nexZBP~f$T96-;KAK zUU*anh*2?3vC$MxLRd!1&T>@M281icl9;YIl75MB6^2zEI`b%m&}zCWHk)r5l4g@3 zsg0U!+D!!#!8g{tc9StI#@ex1sDFdD?X;URf1!Gj(Qbl~?oy~{qkamEDE68B4hC!b zY~20E-rOp~RL2@!Yyy)I;K`1?Aj3{Q5XHNMtTDmsI(u5x08Ybo~rPINA>o7b< zE{ob187j3OIXp(RRL63SZ{5cAW9-=$57n|JuI0GlF-npoF7zcaWq6Ee!QW``7@~6L z4v!IS$%A*=@EBoB9=wZ(#|T>skd?PRr99&zYm2pi`|ucftR^rionziU_ctv7rpyyAOSU%7J zidTn8HD96I<&|`?s`1Q=9bZ&~Jo92_F)t4EjDZ~{)OfbTj(b&&XQ#x**nt)V@=EDm z9U${(Ug zO~v#3yLfOTf~oHfkAZ?d5RXVXWOTyFh2A)I|L_>mf_ z;`q>MC%pqJwaReuBZ1fWxm16!aA~uXJ{B&u&Tx(ZC%+%zr9a0f;SG&9DPW_5PX=^K zYx2opeBdx0X~tbmyF>A4%|k@kWwu`5GE! zbi&Sl9#3Gtcq6jR7jFa&y@)feug<7s^e5(vHw4kkP+fMQx|i|0zPkO{v9-C_QROHtbO)%Cf)x^dT6%ELDbafi!Z-4l$MjrSq9 zPD>Zpzd9pb7JJ-L{~{AzU){Q&0o02R;yLQs6;flXpf$zo4r*zsiNM1Y68=rWeYo=81c zL4>5iA?54X(Z}z0qCfbye#%art{+~;`;iQN7joy zky#&l5!o8G@)A)m_C)3^0a_M&B6A7q#h%E#dm@Xs0M?5=ktJ6F=3);UsqL_J+0JCSvvhFGfZT7BJF|w9XmIxyd!9gS zv)H3sqh9P0G+2q_Vh?N4EcR$Nb~S-U&Bn!^2&z*vH2D~sG=pC3N$k(IEQ>vfzQvxz z3P#Y2J&C@>p2W&UVAqR1iB)_&a-k5@u^p}p+Rj|S*aS`RGa$l`VLnl&20Y1B#41uL z^^gh+NEKUwBcDrB)oeK_&bye6sG7}yGn~^9MGa!aTzNF{y%^}k4_mR-b@Qzq?FCPF zeq)Ah%j&V0LIx^D*U8LM9UzRD{5Ds&bBUV91l1~-|Fa~aX-ufQ_=`@XhFOD=nC8ls zgUF||E6V0#3(47fv4y=&&I8DzI#xjOV-;`sTrOT*4Dn2@C*MugMS4<4q`WS7u#%X5 zUT?hJDN77K>TMvj&X(xkOEd}yKK~mg6comMaT(S-Wh5!gOf3r1Y5WUfyosZ>X@M+> zCc>u0M7OgLmIe5HtFa*JM3)6vP`$(ib&3$R0?PxNAzBie0cJ+l2{9ycJHihhaKWXN zd#N{@JRW@fsb((e^p=volqLNIX7Ghlu9vf1k3JeT`=ObF7Ujdv`l&Cl6LI@!3uNEy zkui}-CgAiQ=i(cf>Ihb8uA$@oLVl{bGgbmhq!>rA=FVVJgALaJNU=)Ue%2Vwka)b= zh}UmFNcPvfU+;&DuJ)->bdL$lf}Z$dJ8Nkt%WJ2>*D5VkH76%|y6mJbuR@nXG^r=Z zoqP-yeePyt3H}Pf!WAdLUs;0tNG)eed6F@EO~XsiNvGlqn@=%rV9T^ZSP`GT0&pEj zaynfO5`)q3{1}ih)ybmTCp$*zVDx39+)xxj9^T@-!i?q{jnaEtmuUX&$?P~@GHQ7zmNa+IDhLDqC0(&d6EFtK;)tqefY|+Okg%r z0D~KRVuQ{egsHMVT)(|Fj={=-R~UV`dh)Dk!z?Fh!>JgI0Cy{3Yr2umRPhTlS!c2< zxRJ%4BQ5x5lJuz~viB-S`U(N%5m|*ul%XFHhD5w$B~e`IWqAIqI<4nD=p*@4&2vz< zRX2fjDeA9!I{@*VuK6{fkAbRr2Ed?3&|U_B{m~m^5_4caWd7BN+ZKkXzRE@`su}?N zGVl)~vSuhkZz44KNB}icaci0mS+fAZ?bOH`5`^f4dM$WAW&MIS`>HgczXW>=5Cvw~ z3}Cp!@>&3c$X+u5p|23C84BR;W0AmU0Pr|}Y<10KK<;s%9SwJflrlZ>vEvF69fIKwJB-pJrH0kuD<(`v6r@GdP9*<|a*cRe06I;;^E1hT(cJ4Al%=0E(XN z!ynUvFZ#8?Q(*M>Ccqwal~1}=lxb>@{ciN{zAE9y{&uqR<_29veYo`JwR&VF7%5R! z(S9R@U>8mLXd*#{&MGp&m`Fu{lfWX(*3 zUPq|rL;%;!0dOjSo96=Hr;=U+P`wF&8{D>0%Noe>dDvJTSOZw?S7*Z-DueU*c@RwG zV2;p(IXDmIz1Gp9e*9hS9Btrxj^M@NNDSLO<{a>Kt69$^$76mA-RfQWBMzT zAhqUMgbL;Zcm+T+fa`&qB4fqRjHw3tN;*c5^Mffn;Sqekl%^Jyn+WHyq^@Pw$cz&qU_IXMj*nIX9< z0l>FRg4CKG2%WSLKp}vK2yk&WYZ02xPyjd|&NLlS*%Q2sf+wii8Ajy=;2%Wf52!qL zvd(fVmFxd8Nn`P}%5MY24xWV6YmUS1I>@%_14qS#hNnY*caY~q@|=d-tK=EY$M8|Z zhfBt%RIEZXa=IB^ZSW;{a3&eN zbS!j|i|#g57T%Sb+SY4W*~XaEpgcleS1W~A9k8OuPtsKC_}IVEvGNMT$cLD@UrwO| z`R;V#ky^wyXs<2}49P?@ITiS<6VaH7tT_jvc?i{P0&pLI>Z<{`!JS4mc_yr}TFicm z*{oLdFs-fAVm`(|{y+?TR(P)wat$7%uS5vC|M$@SSN_E({t(f6TAN}DLh7iM=o{kQ+~ua2 zb5T?3jKGyFW37?8m-D_;BQb0Jxs|@q~xmud$J(;Pr%Vi6<1 z)jua7qD}o!1V6lYNLA}spszRRl%%S?NtcLOhsW_XBZNIctro&3t>^=Tm)EzICKF6) zb6Y9tM)iPS;Z`Z#j+8z&LiphGi+xbH&CaQE-NYX(n*Hsjv`LC)-xw`oCQ7|Wj-Md-}fBNG)(MTr^T79XqmyEhRDkdeie$W#^BciAMIlr zKXZGdVOo*F{(D7ky#J04FkBZS$|IT!4g{RS4KWnH?7q^HNOYvZGm(7;zZ&UHXlN<4 zn%p)TgRY{D=V>-+3@PV(0lZ=VEzn2rL5`l{6Log`qHbwd)H|r{^NkS6zZMcWH2;?w zF3G>;pMGI*oJ6VQ{%3WZ%s9bM@7v)M!K$M$o0#xqp_LcK|W?Ly|70va_ zUF?^;$tQOhIG6y9kfqu0iDnY5E9kS)gB(AqVH0nrCY? z-)_TGVvTodsJxHi8u6MA%*s=iM-!%f*Oxmf+iT)AY zOI>Oq70Jqp3FBJ4HOIAhYc_b^lpXe}@t%`q(u4-ju@Z;zjSr*XcqP{~bU=>f8W0%o zxk{$YZ15a6JG`jIbE`P7Ar&$9@51ukzm z74mrnZdMHgkR-3bFH}P))v|a6o=^>P&?$LdP!0SVJ+HtX)xgtOc?CXF4OarkD{w$H zd;}aXcuh7m0>>+m85r*!9~js+p1Xc{1$H-7p9cSLf~xo}Xyi$7+0w@%C>M~I6)aQW-BZcs&bzTbLQ5&g_!xX}! zE+*RQ#7}A34tZ>c2mEm&oK8Jit4saM6p5|0JZZP!#h7XmB3|Il^=+qX1B&FcgPUAB#Iy$w- z6OC-(m+K56#W;{rl+{sx!i8$2I9#T5;yzcDXFH4l>2>EaVm2A-v~>EMICVz4`Gm_a z$wZ$kr;Z+5+ULsg`CK`EpDVZgaWJL*0k_V)A+;Crr+uy>Q@I&f96KV%2vvNpBGc$z zqc_+`8n*-1K39?HUjWuVR}tTFgAw0xgAw0xgAw0xgOL-yLtO2174aQ67}3WKcGRD6 z38L<_&sD^C++buei|%yiH7FZdLOI&!D&jkCFycFIFtUvN`nbV}@3_H8(+h~JeXb(1 zFL#IY2xXo|nLD-5l{-+r*RmIFA`zu8h1OXhPCZb3slX<&ro`vU9o7@!F0(-@^VqGx zkqEl=gFVUwQsl;s?2s`Pe@UzdpU;)sKz{MLawo3CnV>SUUK6lH5;}JF{jx%t4q3 z=A}gHrP=X8kH~Xsb>x=h$Qafpo~|PyGbJ+N4kRHDNo0aq&dQ17s`|+8QLN7P4rp9^5;vX(zVOP83?EmUniGJOQOHkU8;7`T)_hHbBt# zX1^vClIc0Tqe#piP2Ee_kh~cW)Rv!WEvTMj(!Q92n^k_z$`F=3x zg1Lgs!%l)%mV89Df}%Geh&3Zs$+GOS3`>@?Onl^8emXBBTJj=b{$j)SRx->j-a1b< zkET+asKz!!-;QU8KkSM?k4`lw!QlJB}+!s5IM!9rG9 z5GuLBSr^18&nkM|qyPovHxFZtR`%u=Wd+Rt@E~PZLS^}IycOL8t$vD_y``FWFy?QW zzEt%NrvCwgeJZEMB$540t>`2Z^2+G7c+$UWNm{`Cvka?OPAeIIy+Oi;D8D=?p%r+D z@jK!*Oe^pZ6(~nA|9qbUC~qD*h*zJOME@2_^-VGX2lHlbOE;C$XNI5YQ4{D4rmdRE z4bDDai$R&FvI4peogT-)^S~?c0=f=8V)c2;C$Nuubc+i4btXXRNdEtyJuUvO$`P(kU$ja6f@^0N)W944@9CyM7}9@Vnoo69B9v zhLaU|L|vdYFFMD_PLi_3dLtAlTMV8udMxB$Y{*^(LV^`d0nhJom2L#f&w(nv48W@Z z^0yg&wiHuQMS){ zBoSxnc}AGTy(<10gbw%&f@fk$nptx$N|smTi5l-yui>TEh3JVxvWRD_1`j|VS`AVf zW1>sF=|QTn`f%84aM)^qhW)?GY7l?t4)-644x-I>?r`l~kYpwwuDueVu?y5T6K&qP z!$U~XW`J6X((l}DAMsClw!;XpI@CSMhE=+oPszl0?r_}=0DtgM zJAA*u>J&kzmc|=WOTnLa#D4)2lXq_S7@?XY{;xy2-Vy)L0``vhPeHOj_K5#<#BKM8 ze|hJ27i5A*zjL>L#6RWy*dzY`fxNVJ#6M-4cW!538Oo$HZ8l>7NTH<(B=4@ad`UTp zHDwO9<56^-IkjKvv0L)a?bP$=I-zVLMQ&RGp$aa;UlQxV_s;D!kl!3?cRxUh$}`_| zR7qamxt-}Rkxazf9$CM0JG1tA5&69Ut-M6^4z*hfv^ms{pnm6e<~0#*4z)WIuy?54 z8o=J6b{hbDhuU#tjd!RWH*I-`+HD2QcWyRP+hObSjZ2#^AL4%dJWE}C&T9k*-ZPaWZA|r02W&=Z0BC1m}H2D~sG(#8tI!zw$ zbvh4l5(_MSKZogJmY6p%0xLh&OxptAuZvlec)3iEbi#i*t3KC^qC?`$oDY$?Tr+31 zyjrR$-3kmlc;vSalJ)mfYdDWM0`rR=GMA8G=E3aU<=p-nCNUeV-R1258tQQ_f|v)d z!Q4d48%%l6#N|m)lSRg0%J?uYV=!eTW2%au?eO4wImJIcml+*K;w+>2CgB0Lc^Bpy z*~sWHmh@4GnsNBLtx*Yd|v{#6J<{^>?O z3Rr%(#^q0FkuPBBnUH@lyOuwJ^3Mca%b!5`Pa=pp=dDKm3e4B?_W1(6==2qTDY5do z;H6GtFSXxr%@>7OCERz#DmjV8o_LnGOJx~*u|){x%2f6g4l%z(uwXlKLvLGMP%D^` z-qQR8>|hm+o*on7Yr$j{y~$@}HXj$b58gRTDAAU~2%;^A5%l3P&gLlRKAq7Zcts9` ztn*L+A*&JZ`DQx004}yRI)lJukFB9@hHH0Ug%7}8nB)hI=;0S$F~;a_|{?^-&%}w z0*&r^Ug-GNVjSP6pPU7EfJe^@9p74v<6DbymQaqK7dpPR7{|93{(?XVz?oIneV$ zXTmtdmiF&V(5;c}-=M)tTuO@5$Xb+#&}r0coEJKc5}OQ7m&X~Jd<;#R!ORQE)8#X% zjq3s0&UW0BSZe9qNTRA(Y~C;hlKfQj+WayXdGeOdp@%$SxJXO7`1%>Z*> zGJl96I0HL#`mzlazz*K+0ux~dM#=7CyqeOT!~X>r#Egg@^l?$6*%-GO<~!rkdmtL6 zk7gd%Ltv&@vc|Bnb~u}6(vwcaDiM20Lr_;wMHnipvv&kacozKV_-)2((Fz%dL!m5 zS0FK}BgXD-RuZ$*i#u}E5Y~bl+lGSOt@Ye@`cm5Zk^2_+ubT?16m37-d>+2N0H9{va? zT7&ajG|hzn;g4`0o{FH$vJOv04o^k?*H1-0=lEa9sfagB9Xb^$^rj*_xK@?S#kk^5bFB+OAZ=DZ%TQJ!m_X1Tzn2*Vc{-CZXV! zudN;8jFFEZCL(1?p1dl`n-8p(CqYdXJ5?2BTo{*8#k|}jGB82=4QOJg%BIJ!nH3q$ z?_fIkspdJtmtFJ&25<#{RHm!JzHi)yAjOeK^U}vX`_hDdwR7@fov;p8yM&| z*h@Sl8kvDW_cfSf=`S?&T!R@(_hVx~W7h`wunbA>gx(>8qsw=G%miN13_P#WDtw2P zYbHC^U9%{_Dr%YR6tzrtqTb@5`=W~aCOA>w#791-7_t`R#%DO(MoeSNM3Z>AS^Mje zcX)3z9>Z}f)&C0~!?Q3Tcpk$%kk9iNei87G^%&lbxIg4E{04Y_ z$YYpteyqpvU5MM3$1r8?JnS*N4#Vbu#AEnK4Ek^e7LQ?CX!NgS?F#c!l8z0in!QAXFETyTj?*p0hHP)Hb zIG#P0yUrxCmrGCLOiqK1*f={ALMi+We@VJPsDLmru4dW<9x#0bN>3h;d5IBt0`d5% z=7+(X_Z3LhP$Mq@#&rOCB?8FrjN6w872IQ@_0h4cTNsJFJ=U7oM*!%3Fk3#QoCZoS zN-BKWNZ|&QA~~&~$T-|ug|`{nS8*D=%(!PwEv3hrbzDr6hAjE1=DWej(q77zbq(;^ z&UX?$*1kee+xbqW$6EP17XIn@3fd_w@0AD^bT9(3kSM?95SJtNK3Vqb)3O2>=zTD6 zw#>>`P=lu-D_d5eSImZ-ZxMw3ZkQ3CfF+XRsV0zuz*fnfd>W*1jluT-o~6ObukHK- z95S6R`HD?#HdpQ8GiT<(r7iQ|(w2E}KWf4&yuP3Jwb#i0vJt2xUXfzeZx8-?g{Sme zg{t6XkMDN^{&2)s8Mif|8n?A_r0cn@9S7KRTjMg`k9AwS7jfHlTa)9t-31ST=ZDTADx{d{KORflXpf8Mif_L@g_cTwT;{)`|ghd6JFP zcG$XXhwHbt!_x5>B^;caM~c>#I^E+CeYdG&hC5M>(IkD_-V*nRHOIhN|b84+;qKo6Np zmfr|s=v+)f9y0Q{og_=IUS`Pw1zBp6D#K)fd+*f*w$wd_^^SPb{Sb*sPhbHIg1ByC zrJKl>d>4W}#7cJ*yBU7fFNa_Adj;z1h03Wk!s&%d0bMI61)B_#Ahf4g>1K(QE>Em< zv#9?f2+5p&d`uMJQRUE>R=Qpz4en&%GS`^E*_uHnD)~ITOpRd4!wt&JP6}FPCj~9D zliq1yh2+#}}e5 z`lSNYz~LpyTQCqF{>D@Mn=JqR-*~c}tMIfYab>>s=bl=oABVl84tqx(_KtEY_=fqv z(>vne}ut{(~rUaRmLHlPpBzY`dY>u!_YP}z=7Sw%*5c%m}7KI zEB#tB%LnGR8|D+@%r}9#8<=I~^Jg0nfZ?lW zrfk;Eq@UGwXT`M0tWOQo(VPW{y#@?&c5gOM4N^1cft5Q`v(Jy~kpp(>F@rBLTalXn zOu(`9!1@Bg?lOxxo*q~uHbOuQlay4%=_%%y6F5o96Z4CJA*ok^WO2<#@7%pvkjAI1j8$T4}s_?~DOc z9H6!t<}q>RhcP;mxlIQskDbFVe=ESO9iYlNNGt8FByi@zyjikFQcn45ie4cppN&Xo zY^0=|H5l`=WxTJXs6PR#rAbheMM5Pdd;_wUQAruu=Tio(KaYbZizT@{xlsE)?KgRB zv8rr>{ymXL@qM}rar0VypRzB2wWG!N=`sZKw?nrI#PRt)>79Av1{JaL_k-a?FmxBI z%5-KRkS$h~>9nf+6+#LY&58kp#Rz=8wN<5XiorbvT)`zKuuC(DRV6ErRW(@8ZLGUz ziG=?H-zR>gF=Qo1|3CJ=1+c2(X!xAF;c`NF1VVUdfFKV6B_ZLZB!Pf}5?&YXO-LYc z4UmLDNMdrsO9?gsv_2E@Rf_~4U#k_=e%fk5Ev>eNwpz6GTot@ot&b>OhSqS_wx!FgZm&+b3t9;Do$<60Cbv})o zI=(wOpC6@sp4@z%-2DHX+%(`KIC>Q-r{gx^ zK3d@B>y~)Rafgo8tX4)07z(Z0{h}83;knuxml0i-I$8U$o6<)6U^*}R3F7gX; zk>3r)q0ds22Y_@KEb@~9_f5hbRyg(DOPRA#r(CtL7t+E$+}ti1Zf=*HmJbc_A3W4N zg1__Gxz0+1JOLii7^m0rD`_paDhj2?_2IG$v*t79= z!~|-2mu`t)3W3k?&c>Dg*SPGJmdXS! zp-iBQ2Pa(jDMsUuMBu+3KeI=>pO*L#q)oD(KU1d#qtn$E|3&Kji#?PjhMn`z$?AphEed`8paUt7Q$`X-tcNYpp} z*A|dR&_SYp@ZB~oNc<>J1q&3%*Cnhtrn%mo`Zf@z%3oVZmA|%-Dt~PuRsPySs{FNu zRQYQQsq)trQq`|5^n+hpc#(VPKKnR;^sS1s|A=U-zxuTWqzO7$&A+xV4Oe#cYYW&j z|JnkIo9NdTkeq*Q0b%^Lg}(xA`Vfd0c8qmD3X#Yv2ddG)9G+W)15|j>4t_HC3RMNq z+d;q`0^h6A1Tp@+RSJs@@EcZK4f?qnAZZDaqMG$CQebCWLt`#RT{8jG?Xh6pz?U=Z zfc%kQYqgT#cYwz~d(Ti0s%D1FF?oPxLQFAtXp_BEF197NKybD?*NoWOaa^4 z-QZ;1F1`s1FsYNf!Dusj20G|+^s_gJ=seT} z{_H(Z0;9q5r=*wTB*4wRa?|BFCYM6-ba3Gn=)x~UAw#Swlr3+DZf;OQ;2vN4URJBB z1C@yio#(9Eb+E{m86iWud!u!An6rQr$pO`qGhy0 zaaA)2w-MyBZpC74B_uks`J4_A915FJ>t8})%*!}~WwGH}m~7R+ViEPx4kFksW#Fu* zZUVHk8`{X(xPk}{Bf;hPqX=>mUIhdiu*M>*pA#jRN&eZF-pTA9Lv~|vaz9T*r!vuH z$Czc$>xd-+R>sXl`E}^HJLs*4+G(3pgTQ8wfQ$uZLnc^wO_E5U_UK05jF9D#WH1&+TO3K1R;%hjD8E;YN7T zNKQRjv__^AJ z_=^UYFF4t+*@e`030A?Mz2`EhRk8Oq8<)czu+F%pL^cE}$8B&I+NglBWx|!N0{Epm z;Y*L;=)H!#>|@Y=fSj4HVMxjO3y$+^h|QJZ{&l3vZL;h?VRf#Yw0~Xza!tfv7`5RG z34#t5(^HY>6X;NO4+y`C=M%CKwEl?%YApLHq_Bn^2g}&`HWv1gh$EpR{ImC@^#+BZ zQNDc*ly3)hhi^pg`8L+{{}L=Uv3htzZn`wbd@M^@Rr%DLv3`TDuY&Re#H;%{W=MSzfE8-w3qgR8Gk$AAN?`__`4GuVa=S1wP)e4 zzopy)z_$RnAAqyJ3&6A|;Ny*Tie<*%iQOa608>T`5zYi~DLcm+H={;T{TM1UmQdk2 zD9n_U5%{fUoZT%jyWb^-yl$XNSyO%mT_saZJMJ!b*qDNz`a0lO>;*XydDc5n+6|?% zEEt{{o!D~R4@oZk-G+?)WB_!jZ<3G@QZgQJ;h!Wt&aQnmg~-H0VP1CO>qr!IgMUgq ztgT~UN4Q?mBY$2*f)4su9=`5pKPQLtDWW=8@&@#t$I%_6i#}(g8+{RE&zBDHylfHZ zC_N3l<(JZ1z6+J7$zRI(l^pHkP|o-z@x(>C1Gw=_$asS)P}F->1w>Jt>K&!l(D$j9 ztqsPo@_yrhOygzJlUb`2nyW>f10ATTI$owpr|yHiMy5&Gvc*;?kISNFucOgtQUUXg z96+F9OrB6lweL_3N>E^q;tR}C{B(SF5dXpRgERPd33|xiC78~8bLXE)h31XQ>AW?E zuUwnXKX{mw3U?X682B&gPt!*ZfZz`Ql3MK<{L_y;{h74pwb zrYtJVfllg47L#Km==-{6yZ_Fh7+B1<3m* zXx|G&mWFsA3jMOF$r=!OFc5>-xTsrcGh7vnv{LYkTkUgEk3y;+tNYpv&HGDhor|Dd zoGxv3ZW~b85wz4C)S>)#_~_=e0LcJp8}v#$gyJ<2&k>sE_ke1k`M(k^dxbB89(?Ka|6m8 z_y;z?gf1!m4T26P>`5OW zoa#(K{67#L|MVe$po zJV_SR_Xs6*lZ8-n3XEJ*P!>v)VfO)X1l9xC?qHkc@BU zLg-ut)Uo6#C`~`R0ScRw@pl-K2H10;a7ps1sLMd`cFSr_##}jRkbMDgYfB!4Z8Gcz zC|s624GW`e__k52J^6VQG|RpU3O6PH77N+3urK*lEKHDvJCpx{g&bM9FZpdO)S&!kCGw!_JzQJg9EM!-F8^}xMN(#_rFL0i&3RMH8dj<+JHz9}*3HdNPfUP02Y ztb$Mck%3;uwW` z_FoIP4zQv>yt*(3D}ntbaFLJvrN4j;C&}(;+HJ%h;LqNJM@9qiD?{u_2*&P!qGUV? z86Sq?KnP{>2U&CfOZ7Ardm4nI4JLMnG3}e02JHuz19~kGo`SejtD*Eblum5`pwCyZ z%Z#L)hyKN$52GA&P9`S`zmC%Um^}WfiZN38N?C1a#hs$MQKFT@lPSvmck;M)u;J7W))=a7| zlziVnYgSYMJ88}5f$$W>No$V2i>!Ge0FZ3HOd>r9LgMd&psAcp>$LX?{}|vq2oEM4 zx`jIV3E+_ybNHZx2JQD9G^jeb{64f80Tx<|7P2xQ!&jT3$>^+zVDGOYP z7WfH>r7UnETEO|5Qn?8k&4j|p{Xn#rxQtOu;IO3>xD0+6sx|@5s7GBw4*kBXfD&V< z9(n^I6AoCC1A<+Q(!5AyXLCn;9*2%bOMh=cf&zf^?P77K(4n}VvAXKE6uM)&9BA+baM@M^BfeU8Rij7 zZH617&`_G;1E3kTp6YR|hBmnhY=$w+0cOY8>*ER3W_uipa>i9CbI?J7&&2$_`<)n8 z&&2$_`&|f*kI&h&cs+NXv9tO^J^q7tfU9q zfqx@m`S-8wNh+@vDzEl@J+jKHh03epRZ^K(Z-Ai_Z8g9#kO6&*9SFixk(h0FbArzG zSX4Q8{M)C2M^Mx;-+=n=--8eQ=j?(HP-+(V3M_vCFh!4ZfUKJ}2k|{d0bapLJ&GkB zjp+3FlZI2Rz~jhw6^uK)#ScO}7kuj~FjNop(BRuM;M3}X$wW{k6Fo4Q2&!bF2PP9i zl}z-&WFn}Ni5{3t1XVJTQi*&hnSi@NP@rTYWjxkUGLeGGM39n+6nw`yNXbMBCKEwQ zCQ`1&Hja%s<^?I3Oayz_KZ3&A6ig<9J#7@YAqA6(V5%&{QZShaQZkW($wY8~%pWdE z!DJ#h&|VI0T2nBY2oAEJ25xOBm`nsS?B7D+vJ^}vf|N|8phE;HnMlE8B1p+Z3MLal zN+wb;nFvxck%GxYkdlcMOeTVpq-=*%FqsHWv0nk)<5o{hCW1F7%VZ+B50&hJ$wcs0 z1g#$UwadZVP+h0TD@ghk)E1M8>i`#YdVvYLqXVJXDP7XJXn^m{k22}%#sqhmhJPRfr1knvT8H8s*gWz~)8(@~86xP7?MEXl0eM^#>b6jiSLoP-m zKri5s(qwJ4GSpeKhu7Og{R;3x?}u)aMJh>%olk%U$ztCXN>q@+C@=`W=zX#^U~HqR zJb`*Fq5?8}sB4C=cFFKOYjCrRM;8Je?y`M8dJCra(l5~D(p%uiRzC!BdXMb|qg0CZ zmryS~wMUErnUzPBq6@6Q2iA3Y8d!=o8TY~>-%(9t;=$bFQ{CMu6?{U-w;h#zMH_zvLI~A^aNvJ)(MR3ujgt zyez_(a_H?ZoJLu8Uwc&Vv=q_%)ZWDd``W1kVYwP$iQdyv@>1re!AOp@Qc|WvJuDpr z;!@KGuvi@&ZQ%tXOG*mJm1c|HDJj_225fRK&FCzs?LozEbJMVx!rj1j$hPOEVT)eF zfn7FK_6Ax<$$+$d(rm*vHA(g|H;w!0I|lm60J47R_UVI>Pg*xAR(~uj&gp4DlRp@2 zp9QV4CDvq0IR|#roxuoX*)s-9if)EtFj0W$Fbkl79Ax3tNv=W$u3$q<2bbD|``Z1w z7Y@2{mJ|!VOo}ZV07DP^Vyg}I&ylKUUJmDNfk7R!20;?e6_7C#Hz`aUvk^uNWHu56 zL5qP@SR7^K!s2LGEp%`?*3M0vG&lf-F+)IvC3f+^(v-4pXuYxBtQ^T?9M)i`TysVW zkRi#L?lzE+As+72OrnO_pn!#%!MTDQQkbAQ#x8Ms$w5Lfpe0A5c=;RFh9iu zEDnaIdgh$snmMO(1w>2W;A%1im|8oMzZzafCaTA^Av-f1`{yjdnTN z>0QQT*xR{-@K|n-T*s*b#kn)Itp3>pUrWcXhw_alalvv}%mO|XTeICv;^r3o*sI(5 zbI4}I0z)oLACzh$8xVbdQV;4FBCTfaUwJt2Vh9C+rt+e2Rthlf*B#lOtw4ycsw;@E z#JgKxSR?bbEs(gt6?5eRG+kF?oTW8LucC##%FJ|)BS=3>MeuVtqMxf4rP-uAN()qT zj49N-J}+(>Ly$f{4gvFeZX6;7Lw&-jZc-(|vl8cpH*azWaut3*I&X62LMiTCsAqg| z?!_UXH%I9{@kv)I3kChx0z|8Q0?m3`VYS>A$^FxHjROl3AdPrp5 zF(J-bd9IUp4vF_&cgzNFbH!{-`P@+(YTcQWYsQX1K{($T3Eo8$NKbHd!_jV);sk^* zh-A&s;l}_sSE>c+5(gx#9cPrr;u+rP0<&9XSCBMeFEFE3iUD^ZWx}Lq9z=#XF>>^@ zK)PHv-CUWb=_vwPlsk!y<1$I9PjLt{Dq3gNbeIzF>@dCK5HKCiibHfg9oW@y4xD5g z;hqlvQeMOSVJpFI?_2qjVBcUi3Vq}n1}dcTV%9f)pV7}fx6E8Jwrv-Wt zBrT}yATvz{^I{1rp0cx95plgE(?hmGJ_XXk7QzgQ%31UhHw;t-0+?`WRin5d_X#xO z{`G`+3N*iXr$9yADR7@i<%1YAsME^*>Q6N*2!t{h%R#& z4yvC@(AM_xk7#eO4@W7mc%S$oOm!8Zwy)dc`nfy~_oWnrOgJ}fl`#jznF@?KFb?r) zZR2@lKGVycf0e`2GQCQ14hVY+l{0ecCwF$3_~`}FmG-HmPuIwl50g3E^9?tXH}0rR zvBv;H^F{>_WFq?-Wh9Nujz$|9aAxB{nal0Qn1~g>w{!W}8Op~fzdFhx z!!kNKREK3ffCI9hj`4X0X($a|s4|zCWDozYau!*hLLO}6q%&XQf)Ui5e z2t&J^QNvs_3I;aQRRka5Lm&g+^n56uy1}#@I0>IjHk|2S*bdEaU zYOdP>G91JQp^@x4`aUk)6ma=t;tkCIZ7IrUW1-+JD2ys(D_sV8emIgw+`DR+gESNQK@$r{JajF{=~Ti zKeR*h6(4lwnGPd)itGGe9`M7!t8IYJ|8UIWGC(tC!@4}{=c8FV>*u3cD)P}R6?u0` zMF{%50pT1FYUJJEE~Aq7HjXQ*fRTwILtEbKkj9 z+qP+ZLncp8ZvS15eA5kP)&)jo6=bJ-6X}n9_8I24>)Gcn5-!){a^}r~B=shN`vn7d zz2GCC3nD+4&pW%%@lv$f{$tOcW(aTv>Sd-27JHj9!D1id)~T)t)z{5GZn*ooh8tal zZave68*Z8Cc?Kqj$254V;s^{-C)xweKp=JyaD+WFxE?IQEDo|hg-<78cFF@~?|6M? z^WBr3zQjL$rJh!VxDJnWCyuoiJsFiqm4%+E7x92jX{Rj;6vgNnD$PDcOe7TPFO2!`|t36hYf!i zh~fX$Ra>`=Uscgmv)ZbyS+%;puBv8hW#bkrySb?`yQXe+ZS$JS?9FFQiB6d`zP6^W zdGq+{y5{UvHL<2R5>)^W+10C8`{^e$E6dtYQB#)%Z_2@U@Ilv6?6IzXO=Z?vkkD$1 zHP+Ns=RyG_Zi=k|h5*Cci2$#utO6JuDz>GevPqNF)GG=gWw5#uzT_&V(6W=-nkFnl z2fEK#V`XKup`x*=ve8q6(nRYUum^7g7ivv?Od6}&gF+FaZiWADlb-;IN9>q0rtJp!@+SKbetbdnZA}V@ zXIoA{+PB}X61|+P4nTarPDUXgal(un>x$Xe@DyIf1S&R734XSj)~;bvctRI>ByT`>pVRF zP+r0MSB{HORZg>rwu*{lV&p+*(}Fgv{TtVYolG&RT?}<%`<}}d2e!Wi^kS@2dYf2Z zBrZ56MjeEH?cj+Zgz7^OC)OPkGmpV_VoXdt^2nDrm6adJE0_p8CpnX|#g_dYZEd3G z!S`p+pV3h!Ub9Lu_+^6CIzHUhDPkwYG3%JPdwY@ixmAh^i9Oqo;Pu0ucg&qvR<;8~ z7lW(B^el0KlZ6#%gMb+NlDw>vZ7O%on+`=0#FpLm25%L&+k0WKil&P_cF|Gs`xc0~ zy`3X-%fx?gkNrTl9DrjH5N9bq=PN!t#pBTMJ22kgwu?FdXulMqbKbma=YUvs0gD&T`sC)QO2RomOY9D0Pa&WM}Vyqf;*uDMua^C#={}an1J9ozTtCwqZy< z5GV!sr`uX3923K1BAO|N=Zha%RiJl1RQ_mNJ5)X<2_KP!j|F1?jiLsfqF~3Xq9IeH zmWs7|cTAfOVzLrJ%M#)J4o=8`*yTo~H|em`Wd=o15~ev<8` zs-4d8dDvgWF>!m#3Go}dRNUH<1=swEVoRyGt>qxVMSzRlBwn!hUR!wpr1@~GgX8{% zZu$3%nwKQ%nu{druSxmyr-^~FFI3-tj@VLFdJa(kNm1|amJh>lsO-MBowCLA3jLLC z`S*zNS)!yh_Yj03p#RBM^u7nWRRR3tt!T#wy2U_nCw-o%+$(xGDrMSO+A-fbO*9pW zj1!npeSIsYGf&HA_sV8ZL$laRos#Lnt>}73WbH$;_DHuFGHA=KgX3F2%Wk@Y;K(*E~pX>hN&4RoC>DUJm@4wVB%EDHi@N~x&M65rnL^eaCra~;%UzW$uNR;g<>7DO#9wN94k zStVIO+;IkGlz$;x*3~1@7B$DjsZRTGn3UD~=ZjvYmx~(b7) z#MibT$#uppY$)7!{JH~q)AQEDl3jcBueEU5aA^F5zfL)C##4vJi{z^LVsq7@dG9^j zHc4!Dj))b`OU?mUN{Yl8PAoR#=nChHZLr3*J4f5Z=2##%P4sCWf%5$UKLXjko`n^1 z<=kvB@mMUU45&r(LFedg&e2Yh)tVMah6!>Q@~eP#)2BRH%pUG!v*jeopL^2CNyV;h zc9w3Z?;^cB>GX)31irk^ow}=8&H3PJ+##r0AuFnCINk4J?!-?pOc5I<(@gq?Z7-U- zsA6Jx=9jjNow@I*xNQ4D@qu*^c6iQSXINW#V07ACXNnlv?kt=(-)Wn>X?KCRV|&q? zbDfR5-iDQ&)u_If&+2XW9x^x8GHy^I~V&oCAx+pL*P29Ephv)G3b;AI}VJ`p6D!YyEHJe|51(4m-yW2XD%13oP1!|hB>A! z*2q*(Y{BKhp4{P_R?STI8JW6o4nMcdp;n z_B`|#fSpOlqhe-EJZ3p3c8J+Wjz3xr4A|D3H~=`*>QPl zfOoe7)?w?@&8egZ{p%})+mSJxJ~fM+?R4A8EfXmxcJCIAPL(q+54Xm+{y*Y$Xjt?) zDC%OOU#Vz@^&irO%}&;oKzE2cufW6zSf5_pNqmO-pNlVaSj!9Ne`bw!#~+hn`oa#K zJp0z!__ftNvhhL19evy}$`$I+6%L<6u5hhw@5aGD4j*x+AM(s&yWI7|VaJxa1><44 zi&Z0 zzW3~$?7U4&=k4BdRK%)8O1@~$QV3^Rv@@?wv~I6D4r{B}1lz)V@%8OR)y|>0&e~Tx z^9sJc?z{!>In~aVwmdjI>1>+2XyLpCXFn^>EEVB=F}`SB0c^nDu!^GM+buDtT5QA& z`w2LO74JgGT61l4XSw)ci<6lx+Tm3EP4OBWRrGgGh#^PBKditA8^rAp2N#PiSvNv> zy9hU&$)zQ0F`gaGD_ajCcwJlDWlPJ?+yNo+&Wmu*`SYax%fvk`N5l<~f{T}1+Qr`k z$Dnq{#SU)eKM=7;ok`;F7sFY{HTJ=BalgH{OnhtGUhz!eV3|0utqQ)Qwp83}L&X2a z_P{v1z*!a;*Z&=_o%pITWp52ydW?t-6<2tTLNQk z@dw)}U9cPmGBDbPlqfJFO{8YE{jjra$D?IpmQy8i;pn@~nOJs6lm=$lf$?eL__qD~ zo&{}|T{68)oZWt1XWqPZ^ABA$-DwjaYK@6?tyE?3)@=7GjIT3 zhI?-DTuWf0y$kLYfCe8}FFDU%4~Bbq`(AOVB{0T5dTrc)yG1_e1}8ha#J#Xsi9>jP z^|Te3VDBuy3PxjZV0=0ZMaD4^1NFc2YFTFZtE7i>_NHRXzN%4Q;VA$*&+#7+oOesEc#YZa)MH3UU+U^EJdrr6PEmm-8)WUO^YfX5}5ibY2bm7jNSn<2eRoY^kQip1$zaI6g5<1c|% zrk!xk`O)#E)y~Li%Qiq>RrF)gSkyM(3B8$@^TT;!Ex6}a=OqXP5UEZ)8~wRhQv@v= zz#aRSzzJDDkt`2*f=+92r&A4BF(FfgO7Zi@mh)2oX@!x(G8C_85b_<^VvB~>)2rKd zTsLQ-x+(QznSG4~zt1V&2?>KZrq2X?dhDGJl4dKw&qpd^B$=0WTycr}?t2R!Li(_dp3?-NVYlb;Pu@`UnBn5B8Pe_x3Rlyx? zZE}fiks~wYvobO@RsZgM@N66Yv&*X-nsOSN!xoHYEkQ>hZg2Kov(@* zo)U}A`=^LWPUb>6H$<=g2um3EhRJdU5yFd@e;6tKM z(TK9;Z5M&zlVHE0E(53T$Wa?zX~bcl6(d4I_$hn2E2_A+wrEzXLYp=HZS?X$S#Wn*cUv<4B*1kLiY9)MO?iQ_JZG z2MM?4G)%0R+2-WrUR(x`5y5^xcH&&IxfC*DIP=;&37!>O*$fZ=Y>0_rtq$%u1MuLB zysZyn^}liU`cr3yMQs(_XWTE=9faHLRbpL{7?344>_0xY;BEolvjJ}V<%@>)zrk}< z`Jxe|$`@PJ?ZFFApdY;`ooTS#6jM%WD1gwa<0Joz{c$-p<5pZ*LPnY1w-ZJOWfCdh8V? zv6A^t+0L@Lk&AYU|FZLU6)f%e;o-A3zkcm=&Enyfmu63&1~*XQnDt9`EAFLjIAeCI zf~~=KJDTAct`i{KjW#I$QcHfb__EzD>fq3IHwgED?d)G)w*1Y!?6Ub~V#^WG;l%E; z<#1PbbL-Bd0r(Y3`#lTqQY?kfk1?m{IdjShWhQU#Cz!-u`!5^|qMP#Xz>4Aa@?v-d zZ|*#2cy>J=bjxBME_=3I0+aCEASu`^-QeogB6%-9nrH{*SO(>dzdS_XotQu+el%a7 zj_AP8_bnfG`-0Bp_c$XrA%%LT5v!80MN&a_b&EWQfMfb6;qgOxYxq7H&foud%ENtv zk9uhSUyL`fCWjBfn_WTL|gn&g=MOpclOlotUNuRki8@8ThE&uHoa z!$DF5>mI!Lli|390tb(O7EFw>1FHre-O=U@bDmvx=7fp!4&}{T-}WtMc;3-lpMqzN zvcy0LA?sooIxm1QJorRM8yxe-_QGjw#{Sr>vTy%zL8lm+FQT#auZX_;3q;-CGJLSg zmRn;xxUzHJe6i^meDp4wwjghrSkW$eo)81!5o35{HcOP^gZXFdU+;8e=ixcCjgLkJ zp1J?P?&lG}X$em@?mZxHfgP`QZlAXRZk={6|D6m(Qq5TX`pPu_lwarayttxy`1PtC}VM-)(O#R$YbW>wZ ztP+|dCk126XjN?m(&PIr@L)o;a&t}0!ZkU%rm~^Qif*WATwmD;KvcdZQd1YLl8w!_ z4sTaR>nb-{u?z9KIcC*X!z(SsQwy?sZ6!#rm^RcmN$Q5i%8k@DBn8p6l@)7HJN!K^ zl(MN}W2IEj*d>Y)*7Pk^tgF~iY01}PNOyi$M@oh^G3HoZTi=96&|<;+H&NW^N5QE8 zUt3>Sohd(>>zZn+>nhh|;?I(U+6|30^{RIiZB@nU_00`%X{xk9+=k{@R9hNs3J);V z)m!zoYog8Y(oS?^MQt7fy+*s%GA+Q(uhf!L& zRZX48mcVcwm@G#!SAHw49*x#e3V?vuUT29Hf9?v}QcoW_QI0 zkks@5aaGqAcykR*6+K6qz-QowS_RlHT3flXvevkhoPjZGO}%_stfsoTu_9JekE%CR ztN|VHlB+Hg)n$^pOomJ21pGj%##{?VZev9Z1`0Yy1{>vDFwy#|D(K#-0>5p-I*{Lj ze>9EItJ! zQ?X`Ev?3=xLROXwggaIKjicn{{t6@&zNNb6kn<}a+E%iDaM54NCv_QQI zC^GUv#8?Y6s}bgZ6HE=9v_Mi1UE;(dyC`eHvEQ1?%~H=fbLY=C-A=?e2kYPnYJHLIam z(YShT0le=xe#)fG@zt5*7v)MD)mC7LTw4QHk>OZNET_3y2he69w;F4$Bu<*RL6>hq zS{1SS8kzwZsw->Jm#UiUR!5^&_S*UlmD%tr=7ek*5FGuc?B>&R3+WJk%5&l(H#JxN!Wy6&Z@)$XnIaWv;sJIbTYY2wNwK$d3 zLRHJF9lV=~>sT}jZ=0@$xUR;xmYo%NOJ8*}gjr)XR#vMOPRg{gib8c9nN&$(WXEXs zDy^Zcz(pI4sRB`y`;W$8GD-qm%rR2ne9;J{(xOnbWabhpw%)9&vKyS#v5J~n3kYT| zJl8XH;L2*!7;g2jR8($qVYScU`U;D_=|JWdh}{S;v|4Lmm4U$rUu}e>M>5n+MR*Ra8*Oy7A}fdAcjmJfFf5?#B)GXD1|-Jp$Zrv zTuMvZNndHKsa{LdNa`D_$H3<@zg!nl1W58c9m+Aku(>`~xrUMi4*^A7m8Hp`6}NDs zV4Tr-c)J1s_I=K*B{R>NH#-8>X{>C5)eOy)tJ;9Lkcmg79Dqiq4HdDq7FaG9CQ6lC zJ6sh!JxMD-7qb`5v8vFM;&qU$PoB+@Y8b#0Sf(@>)z$O}%kILcVq!xMXLgkz_ zwY9E<5&B!bp#fJ!NDO0@Y7LQuGM2-78b3ueAG6%@2s3-+A{U(1_01Tt<9eoSr>efe zH>WTOkjc7STvcP4FhSA{FWG{KJlmAeR+XBCdibOOGBGEs;RM0dFDlRFqN~sxRt#n} z>{;Y|GBXqh83K{^7EF^Nyl;TXfJtQ-f;6ZFZLvbCr)K8d@a8o&)iS>fSJdL-EyW=h zZLT9Hof}?V7i+|;5&7)!v!^U5#g5TVr7GKl_Y^Szn*u8snE1GCs1Peyd<1qqN)16Bv?pWKFH98(~z>fkvTi7H{&wuLsG%sYW< zT&4W35wxtVfSk9vQF>s!6nJOUB?ef$vTmcR3#oj36Bl_Stg+zgxT@CR>+;|S_05fd zkaH=%8BT5KP0~W~b?Ui66(Vq6db7bOiYcpMupvk`SaOP~m6M~8G%~Ds6|v?fIge@g zioGg}SE(NGJ1roas>MhPhQ&GH5l20Eqyz;w$4P@>giQ#2G(z~3qmE0g%-Ok&gk~jF zG?-{JIaKQeo{=it`JGgYt{<$#g&n8HN75==qldel(+EOikw99i@#w|ac=5?lIvVl( zCVU+ClZmXK^n$>Q8)N)>k(25uJqfg{sWJm_k`ClkGe8?B5R|bSxOn9U-60Cro=gHg zeNhNKm9^0TCASTxN_M3Q;{=i3?Z;pXS>+W^B6U|MQ5y+CKD;Cv?~iDyn#8wfnb;lJ z`Q;Lil_%jzcb+AwmL34&^%1Q#Aqb>lGx!D-XGkc>l){w29h=n-gWrhqfbrqy{ zu#u>5sJO6M9%IUxzN)cewWO|ZP^Vc5^g<3Giwqr-n_RS23WqY_HW*pUI=SVL!4wxJ zSmXgK}jZwPE7#@A)l#kB@B? zGkCavA>N*Xuly*HSJwx(dRn)NFlbtZT@572RX^8^iy1sPpiq+7)(Qq6Ayks}rd{00 zrR(WCU4YQbesH?taX|A5bE}sO>Ky{pG^6mj1YTlLUf$1>$ggS9OPJb7RiaT*K*0xgfkTcvincNe$tR!8}5# zPPz1uA^d3e5oSL~w+b`J43T}LlS>yELI!ub249XNe{P@!g@7PSg(24lX*93Uk?3) z`r}Sq18OWDx}CA*iQDT7lqD#GB+gi`iXEzBqL!k35e542_R##KXe^vq)eG27jh`tz?kpL0*wgF1=(38Dx1BA(#H52_p=?t5GX8sC~6q zetj)a1Y9~qI=RG}+15(pqAz~67PDGB2_ZT&$cmt}i?v!W5;q`ZkQGscT>6J5T&zK@ zLnM(7ixcSJTPs+Hdd+?bgU@Kx3I^ReEYUjr&JZ%lQlkz_I=RGB+SU>V+02TNOUCFc zh_}A5z*`A}Nf5*P2nTV4!N)Z!%%CgGvp!ltE5sm+g8UgaVzCs&-LhH;;(lEs{LqM? z$q7iM5yXe}@egJ)gS8q}!r%r%p{) zVg~=&O?4owj)%5;d!Iw}rF2CPV0ew@RsJOLc$2OP5;5j8$|J1J4SYRL)#ltP6I~ui;!KXDU%9_J(kC5h3)`+EpFzy%1 z7ujznVa93R;Z+2(r6KcT-X*%bu-7mN^0RHkdna83*7np{#C!pFVzs2gmmJ6cv76kd zEw%Fd1Rm3<5QD~^E7?gd*4jj`CGa+l3NgrbwXG#5%J719V+AaXLi#Hs6PdxQ+IImz|F^;jq?-;(-$D&c`OIHZk46f6t6%4K? zl!}d9y2228ye7Bg6-Q6&sALu9`=0ejy_ zoJKsxYhJ#v-A+{xX_8_FAJ?c729FX-*^f)(yDPJS6ET=eC`HJn-G-3Co8kz$G|$l- z84M9hapcnb$(qnRbbieutl6VlvoM3K3$l-Na;d)-ILsjHq6oRP+7L3xx+p>}{lpM5 zIMtaSq1V*kB73qi z6RawC6Uh;6`W1d7uV8atp{=xn4VR?VT)`$s(@mX_NG1p);7y)Qy^n$j0nE@cPH87``Rw$Y?o{;RXN+`dV^xSEYLDoL-Mx56>`Vh@9x`KBwtJMbOZN)0H zD+H`5S}Ha_;#fjvi8z*!r9+(8xe@1e5yW}rxQk3~tTR<>%{&9v)J`62=7~5juV;vt z@#Q59GJEh+mU_1~&JqSqhfS8Z!vy}#{W0@^m775J+yt`c`eZ*wvJck&wwMLINejA| zK^C-|)no-n_?CZW_8VfFVmUPzTG!dmho@w8GV%|mvg$&cDjCUQBwH5-WYsXO8Oz|R zVib$jM~M|2!EL=G<&c)#O@gB$COJhL#JB!dQkC)7sR{gWY63s>`wmB;uMy9H)#NiP z)7;&)NLCM}YHzwizQkZ3LSc+A?&Q))L&)GJjS4f^64#bX_ZmV5e-}r{r7?8S2r-kv zsf1F}aA}Jn^bVO{vq&P%A_+8$ux1x&flC-e)SIhv7gv6)KL=j&F!15hTJt4VxZmQxjvJq#LyY#ude-VwTz z8s6PC@3*Py15FZQkR1cI_4TA;lUB8uL1Q!D+UI8!W=5u!PuokVkE=~z3|>Phr7f4f zW(XO4OQXUJeh}A|OY?gwn}JCg+)60b3776MgbY3yN64kO4WV~9{F+4)X%^{Z&AzAA zh%oq`MwKwgx+wSNQgvr`mGqMok!D@Bxh~VemmhskU4)`upY(Tev~< z3bQT0ZctvUeS#rpYF*r-3%3mk& z4UH;h&=|xwLHvxu%*eFzsd5$d!L|ccyh1-tB+M-ViPcLrg5R%`f4D9Y*oUslAY4i_ zgx>BlsVd8mFqj?3i%Vx4!jI+^NyIDC>F4E(t%cOhLem|Ci{j+sQi&n-%5?!%Z7?JZ zZj0mf(G4Ss8v5_d`FmWPsNrH?=2%DaY}TUsPN*)Ss=GBwF@v3S-w?t+gLe>0IRuxI z`zTd`kip)BQiNQ(fbymU=fi$EA}q%(T8Eu#(Ek_B1+_NI&l99s~npln- zv^^Fxc&nCU1%uo(vR|y_NYQevV32!Ogj_Om_)gbG5sxLB*UAzC>kZ25Vnrn4HCog# zi}-;-arb}IjC^rpD^Xug*F-rln~KK?0tbWlYU}wz-H)ish*-`dvh#crKk7jOkDzN3 zB&Px2#PCujcpA-5;!|aqr>_KuFInXAbGr6?c7i%hQp{jIq0noJJGr#W5HfgJqe>Y3 zPF!0q{m~G5eaA0HB#|5uEyqsHE5hJjjS4f!J*yeWr6Y!rK^9FBb|oFN#ak*2@6*ge z3?9^|u(wNW$$kD-lZ6;$F%qnl%|y-G!q$|*OEn|kRPwhf;Z{6+Fmo6*jC`T#4eFy? zUkAyG$ktH@>ouP`XDHr#5NCJFmn`3!y?QM2M*+Q8c#+m$`3OQ-TS zoIr}t*9ItN@M(<-Gx&2tse$0q)HKBvCNzV2gi?fDI;FoR^vY304KC5VN-ihRwDL{Z z#Y9r2Ta~XP&@d{0ico!Z_r8VZDpKrvt#gRAd(EI&kMneQA-@Eni+vJ=Sb|@ehF$|q zr*7E#5V06!>w|q6Wa0kv1Hl$}N~<0Aiuxf{4cFTENbaI4Ry;whe~n1YsQ9>W$Nb0* z*o48`sSYw32Jax0vI&>&F@y}>8%M~c2Q*8E&{)7P;8pJ4a(aeGNW4GrIIkX%rjiCaXFm~ z-%2=$Mcm_lgJO_-20Jjw*24~{fxfWauh5)AHxRhbpa{~J!vsA`S8yZ-C+a7I${CzQ zDCJ08nr;XgoDoOJrS*o8L4K%8anxcFaG0)7(iPl`!S^*P%piB8JdR7Nb*Etl-JSZk zyv(k@X*htuaR$Z8=NS}9q%SE{b*d%_jV91A3Na&oqBlV%|2DB?hWHi&!-i7{bK(bq z!Mzw{V0f(vTN1CLV!Rh+B zvM__Sgi;c4>1spB;5BiCT)NQ^dgb6ARvVHv1gxP&^;1n!%wQ5d?*jH?a0a22 z^j!L#A?%9%E1G?T!HK$6IfIj2>?56Anr;Xg%g1<4y zJtOI`ZJ zW5>!&Gbq)!`Q#yaUt1@_0^F%v`BuCaiQ2R(_P6q_xeJLQYYRb<6??AhzRvTtD)zVX z^~H~`y`vcyGdLnisSgHX@N|tTW^fFll)oE;DQE)KmYRkPzN}Ft4DO=`{1h)P9np{d zl`!~19HF;Ue&p>U-GeW^;vUU|Y9R*sIZSZ&8tP_?kzgxz}2Ib{Fl&ZK7^m*=QvTnA-+w4WEI-yC58606AT48V`q14E5iJ!$v zaOBGD|DxGP801Icz)lP@Lu4Q6~Tuh*8!FNB7~o~7Vz0H7EwAkzq|WqT(} znXio#VsMj2EoKF-)u=Fo4{Fq61|QR?2!qcMD&b4=he#ln1)g%}WQYd}#AncqS(Ghi z1}N$upePwdU--=%wnKuq$e4sB0}`)W0*brkJ`*tFH%9l6ia7NczT3DcgWuGsC0^!< zCT%N`U#Xjg7&MYcy_~?j+7<-x$gzzBR-W%j;V2EEq47T<$a@{8NwCA@B$Crah|>&m zCIWV3kYAEfahgki(20U?_A&c(ozF!WdVG*kB& z{WXEFYE+294BcaS6@j;FRP<2-pVz1mgGK`1GBBUIDKXtK_*IQ6XH~dcJQAdCzNWh? zXYe773NgsSCCK2GQ8yjByYTA-4%Cdo{_cDMj)!jzwMMUJ_(IJ*#2~+&f@BQ08nQPT z)G~y663DNbfQPa{SieLwjVH`01c28vI8>vSF!)a_Lsx zMVQY64;mCf`ZA25(=DJJEVr9h-y!hF2F2iu8s#5q zsyfY90GO1)hcs#hgO3nOnUqU|d4T=jdeom*YR>o|JEIQuX$r4Cy%%*z?HWbhBVWif+h=U>7gOOMPLuGHyBNh5(z&=tuTe$$XK z*hiZy#GtWYi86xuJehgkcA4yZ34}0Q6m&9PHdz9NXSj#9PKd!CS|9jLamAH;hOuRk zwRc;>==d+WK#EH)hWi=067}d+yO_xN}Jl$pv;Q zKn6O!=1s8bj3*|X*d{O~XGlAV)Uh^Nxg#5_;AxEE46QrWoaP;k7>(iGdgy&Az;2&E z`baWq>V@B~Q2Xl!*RvE?c4*n&i*!^0;Vg?WDgR_+kK0+v3#Hm!Rk9vIBJhT5V z%|625gBn%FAivuQrxFahdyI5)$@J)(r@tb$FKhN82B&I;%9+vI1~rkin5MNUXYhQD z3NgqhZ3&{<_o(NmwFJ>R>SnXe}GGkAzl%4@mwlp$pBr*VW_I&KIVd^L`c3cd@A1V?2Inyh~XgDj5|_yC)5byZgOC5#+n1X_8_F%QUKlL5?2aKMZmRQ_kY$+mjHbnpfCM zaseUu3cMNINL7&RF}Rgb0jp!}B?RAU$OtkQN`unq3d|WSp!6oO;F;85gl-sOFiWFK z7@Vw8iy4e*RG7hS8dc1oIkgHi_?V_z{49ZkNz?=pr$3RfykM$RiJBEmkSc$fT76CT z6=Lunjp9AAHYV#~zE90}$23H(uc523oROVeFzrID*}FuSC}wW#S9iwe1+A7 z!Se{EhLcNwHiTLY0oB3j&xC?{Xk1W5M4s zx_msrKbp7p48uF>=dC@1Es@|T{5JB4-x|+gkX3=NA-(C_+gY1$YHdOc{(?}7kV_{F zA%kygRGC*=)z&+T3hTAUJd%|Kguuws8GD4iJph%LeS(O}<9I*yflk9P8zyLNhHt;f z=;SiX(ZlMv^V{36OTs!9h;77excn5E0^023HjHBny7@7V6-2qb8oKFO)f;F^y6YIn zZjlV%4FEE42eG}T{+-X%p$AEFmI$JoR}esP|LrCuczM4;RYP<(Tg>3kG-?Hd576CT z_`b9kSLy=2G_AtEpi+^3m3jq(H^&ij=@~=FV2E-S*$o%IOh}m248GP!sUzvS((Dwi zMub6EyXSlnku9>2+TkEGyj=4NG5ESM$x|!6%41vBll1^$pIXUk(bt&8` zmv9W%8{rt_af9W7M~meJ)XAjfp#U=~YI*fVI16XhOzYF`X7$j%F4n@7^vCh8R{L2&0?^G+iHGrGfc=hfvF zqF&*s8pt5`5m-qc2Pl?=#Q+o==nk42fcgP}>{)>FcJ~5xcY)rtha=RTks!^|t#0RqoYdmSm7Mmiev1vVp!V%TE1n zX36CQ-fmEj6L_An9lvjyz??rdRL_01g?Ew2S8LHjGf3nGTJ%r{y`cXhU11XW*;c@+?ZWZ-gA?h7hko>-=S4@dyL0{Beb~#tr@n9D4T$!RB&KC$c zqMSm4!f#>pyaD{cJB*CyQQ{$CymlP|ynGYSNiVkqk&3_J<`ok=8T<>Oq=mS^iU9JB zm0m3*(%Y#-dgbp+_F#69T`{wt={OhqIf1Vk)E^17w9kfY0tXn>KmyM+sPP2OGN^M1 zEH$Vy0{PnxK(2a1?J!hcZCXehc1y4`Pb7ZzJAvuFQ2|O^CX8!%OH0soBF%-&E!9l5%?QWz;w)gypARz1~(B3xZmfSYumIi6>^hwKRH6cuo z&=oqK_#%UMgaX#2PS%?xK%7_bStRgMx+eNXA66>0>+boH-sO7z)Cm!XHSjcEc=C-u zdgb8|Rzl>|S1oV`iK;sS)Oq5#Q&ctn_@NG;27fYYn+O zW}N8z1v&ZU#1CvYg;YIHTfyf8Y%|Wopw)Lsv=OEi8{d_ca+1aCJT&Aai&thqd57vE z;%&MMad&^x7V)jMKcXr_vY0^@5=Qflm0kfP(klWd#72ZLi@?la+Hv-4G!FYU4tqO< zhTiVhk?b3_dL_(=zg{GPo!+2n(<4>!gClkZR}m`l5j!`ub;AgQ!!;_*;At*ikxnjE z7(xc!o%-+Qq)>wax?#v`hBK*ZmL^%uU_S_|d!~ z%!^OuVI&yjQ+Q+_>EzOC&Ax;|?pYCX$;iPBwK&`8qNsn8EinY6XMu6H2w^QYIg@`=5yOixy$g_y!2vKwyxCQO4oYeOitPgWR(s z`BLjM(aWIMRvQywB>3TJY-)@{dXJ>rZui0=juzSd3unF8Qe_gig2RF;1)uu0p`*(hR`czC$asOW?%9Mf#&h9 zl5>c`dd8qUe*r&VqImiKOW%LOB%_os>jMfv$ zHo7JnqXrL6f-z!CvBHVia2MASJ7B{gkAbi*e%DGT9hn!nSwbZioY`Ng8%7vpp6~-)47z#w7sw{!v0w8lWsn&paH4e5 z_*}Z;JNjNDcxm7<4=>3eLJXm6&~oZ7gqsqRsLbq>t(f|9?B~~#@Q@HNcq5_IaB}H6L&)F@afDRR7w+L2-7vx+^TZw^om~34 zA!N|qgWntOpa$R34MRU5@K**!kiHZX#KPbhun62=qH*!IgK^QD_XvurLk*kn9rt|}foHleGE zQ9P_Y(Q!s}HOn~r8*y=}RF&Jjq}JGq{neV1yXto>d&;QoA8! zkb6{wRIe}mj2*MJty1Q7speJ6;CD5u#IKMqzfC7kg2mry9!HHC*Y|5qSgatx^^o4n4X_5$o z+*%odONN)vmotb5N2CNA5BHEH`tq|vqFG8;keWdb8L)F?@K&Ny8dE`E3JD6+6=ooV zyEUqW!RrX6H0IJiL&%`JQ-93mJgG)E3=yO+D+#(xllWYlNmx;r&fF@o&fI{h5}YvI zN8P@zyDw(&6kFloFpRfUk!Mc%H7{)C{`%Pi1nx9wy*>bj6>hY2d_( zkv}q(RSo>pG>qh4lC1{*c^O7=$33kJQ@j508J1}%^#b#V!9|2h>_FUblx`SdkmHOi zzevlwmTD45?9X zUP5dFGnqR>fJx5W%-ng9JNI7hy)#La)kHuA2{Kl#NNY0{v>?((3Kg`<_&`Lh9oyPM z>(YQPil|c+im%dd@BRDjbIv~J0=l}|e|p!N`M&)--)n#Sc^>y%t{&o!Y&hxu|3c#O z^*b&kO)wBPPA}Pua7ZQ(vTp^H-f3v-0;iZITXj$$*;>R__5e%!LP4@or^(*M&_46l_g6z5S$;_OO{&cwV@`ZB@JYw zipd|9q13IgAj@4c&O}e+H^cZ{HAw^6z}2pY>@Xmk29|CM1dD z8mJ_l>}4;SI4Ul8idnL}wQew9zwa4{|O9nM`pS*AyKvu@XdVX_j_`fg-|&GvuN*Xf+qmvO_2Fxvt# zm82@a&>W}Kn#g%Ayp@?}hH{}f+*Z`q2&tdgXOZ2TrWONCI7>@9%o6u?sWb<|$6 z_r(NR`rFVV*&oFOU1@=f@i4GgfLhnAHz_N3j`NTuyWK&3WchG`34y`UrN4TtXlYNzg`pGhfR+pt#C)iE)G6(gOjV3v{ zi)=!m%xKycrz0Vg+~N$t@wf}1m+W6SsE6#`4(cWQF+jSCy$4vjHx%S>B%Q&b@Xe6D zWFKLGi*gSyH7rK@5O*}ryBH`%*f1$)T;lY_d+vO*Gs zs|Oe+_p2`S{uu=GEU2#G$uQasns!P(0TZmcjBr-w<6-^22KI*z>SATS6V{J@+QQ0o zfW0b&{L1X~BG8Y!v4UT~F@2GUuj~%P+D104^oj$E5YO?T+T1zdk8p`tae%=xGA(x- zj!#GA$bZV!U>jMsRN5shzUrN|lW#&@sb39pKWfAp5@$bSEs3*gtO9XXg>6in)nVAg z87q4galbYExMi^VAq(V3E07Sxmq&pZ)Nx@aCCjYJ zO`Bw#UCB86lW}$><4kN-nY?f?y;$2iWt82i z>_%@uL(E1>J5Z}N)FQjqLA_)zcThLk?SSMZ+-WAnjQn4^8K9SJ`ywlBS4>~=S5R08sg#-g1RPrCM7qeH?x4iQ z-wY0Ib|SrGiw^1|TLNT*Wa-9G(2x5P9O`!>J=Cg&kgvruB;>jGUb3rkOcFr$^?+

$iB{L^^v7PZ9aQ|rOZ+1{0SsJu;W+^nEsPiyvyu;b*q82afOB!&{k!f(B z0F`zo`w$>oL6*K83X=W%m>>#{OoRIjs5CWM+O&0K>1&}N*{Fx)dCfmU;aR8H^&D8< ze~_A!{RIxqR}4@&%1V-AHYz2^wlcmW}kh(tQD2)tQK5!u?yJTt5HY`iLsUW&!Lyw96 z%z;{VLG_TO!Ib{YcvIqhgEQ1e0}SKkMdaJoJ8=35jb~|`bxYf*`=%o4Jk5o*ZE#wBWa-0pc9!TyMg`f>WA>#z zcGF|O^SF&HeQ1x}2UwyTxtvHg^qAODeG?)4yQ^at*(;pK{-o{1Hu*O&z;sB3$+o~y zJTp0GJ~8HfTYBjD#^$LljEa`hMi@;k3B8f!&jK{4$-M_y*b>V5=A}k{d+mcXZ&pd4 zB>M;s)fqbp#cY~la_qbvithrI`Tmn&zZgP(oRogI6X_!RHx5edRKA5n3K#2To?QU; zh!FC9@!s0GPNbLYJ_l_ld%c5t$-WIxdghYmvo!x^XRD8F)K)T?RE+!>=Ll>}I$J$t zuK_e)U9mIhse_LJrPRx8NTw?jet z^=TD=%zR||w5CiiWa(L(&mLeY@62x_OV3u2rCm-iOEwH`n;+5+0J}hSrpfb;yNpyu z!MQ;o-=EQh@6cgljB`KAp9PQ+mkcv8b{<4{T%u&`oD5%Qy83o8%I**%JK&%mvaEpE zVg&q|fs`%KAsG0vGt^7=m9BI5lRXuX?IJAQ9tx7ZBPPhw7ehg^kHiF7dL|U~TQ(VR zmH{t!T0LYLqU}p8O^1SH8KM9e6AS%zo@SqcMAwDDQ6L7Vb4*k1#B$eG{jSMw)m zk6*cV=}Y=b^g=#L(CG~Kk-Z3z*%q>}(aCMu4ECz191Hv*V_8ngvO30|Um*kfRr>z_ zKv!c0Wqu>e7-jY$%elBg)#e{yfj5&yj_iI{SCM1kJuW9%){)f{uVllf%O=|;vF(|I z&7<9Y-d?gwx5gP>gKZLEU7panKgBte4y^A$wOS#X5c&kgg;7u-?hdygs%vta9 zv$gnyjDb!7eI4j}P+$HZfFE_D)%!H)!=S$WX^mFyOwcW$$1Sn=Ye5G<-v;^ssBdp( zsnvVtBug7%ub%wLFmMX!>7W;Ywt-#<>XRpMr{JfcUAU0UH(C1fzl0orWA`RproS0f z;i_&6=*6H#P~YBN(0c&%)1aTHJx}2)z%z*R2H52tod;3=ULAkGf&M(`F9cl<>bX;S z;5E=apnL1czZ>}bKtBL_cOCi8Mb_g+(0!oSf%@@W348!_6qJ{sefj-3{%+6@fKG$@ z@{a@m7U)x;XB}lFOQ3r|ef?iUo^Q828gfhP$UhGJTcCW;+K=kUtD~*Qi$SjjodkUb z^f^#JzT<%}13d%uygKqX1OFq?+d%)kj{FyZKMMMF(8ub?f8`h(;VkH5px*}df3t(eikgXM;bv-0`=wZ0{&N^p9KAE z9r>q$&w>65bpG+aWn2CMwhg}oXcOcw2Bkgz zqJFyP(r?a?T@|A{s@$_Su5BavcY0Lw6F1D8W;$gl; z_oe(PtY1geSsxkCR}dfH#mBcmH(_1$<7+}(d{Hakr1vGvLwp|}-xd0D`2VX$TSFrl}d|T^vSU+w6<=b1Sx9C`3)0Sr;&ljEgZ|(H!?^~fh z*D=cTWud-2+ldcV@hz5odnEf4^Tu}Kdn0{&)aP3;DbH76`ttPqB>Id0VoP71aj{*$ zgnoEB_BV`+{r#o-k#FCBsU!9IMn_*>VP4`3fM{W0jzK%WO)fO?|n$Odq40^JO{1#~;; zPEeoxw(;_YegH!|Ku16epnE`lG6UeBK>6LbLn!;?f7{<5BLLp=|1*?t2fY*Yeo&vh z20$n1#h`2#U;ZipCD01!F3@X0Zv^$pEP(HVJ`FnO3!=Od^tGU`2VDosczaRy3C}-y zet8fAC)MenC5*SLL3y32-uNSaC*-HF@8DGgKOR10#)sh^z`VvM3g=L+VBVSl4 zqs#}S8nIsTNy0M9Q=snw<@u-o9Go8??eZb3rxscX`tjvqUh(cU`@^I!i1N=tIZyDm zT|3&tzZX}>OkKD^Pq_0liQ^4wzB(lpZ&0gK@x#a>iYiaV0N=>BU z$EmIR)9@wg`CHQPrRv~E29*D4P{*qWQt=biOe%h&`cf)>lDhTIH2Wv3=~R4~QXfr| zU#@2ENyA^^hi=LZidVeihZ_`EFdsiP4xrSaUR4Kgs)N6}4t`n!*F)|w^&cT^Y1y8Hj$6$xoFu6Com-z7+;gtFY=JL-AKTkEPuD4_~ta!=c>%!~$PU9NrCUaD& ze-k=SEmKYGKd9Ib4Bn_lVdp5wKMTAHd;xI%1Vmc=#|p0VHix`nYF2|{(D=Q3z*yi` z?-h0IoF}-B|Io6Gk$=eiS8AoP->3%mS$sKg{mh!qLk9Rs!1W$h<4wTX;|G8%;JV-F zPZ9W`y%|%coofV7tM7inb({y!xAONHEv4=NUT(IyZdd(z5cV6d%b4;B=D$+<2@P$h z=>m)E{--}r&<^l_v9QwD7IeL)uFV+aSm3{bd{>Lb*GgG`4xex3r?1WEqT-r(Jn)w5 zEWX4XRZ4$#S=*nx*5bH?q*{Q_0%v|+5Bwl-)@zgCX?fcQ`KGs7`^PK$(dQD{uL4dx zdw?Ih-pUhyM;-g`62YVj<-eqmS{C118-9H6<3OMVfzcs7nyMS|i{fppf z_4-a7Twhz$@yx8Vezz<8StN0pc3*GtEbx@;BBs|2M#=Ax}GB06yi#qhArO?e7DA6zu31OKbcf@I}CX47>$?+24LGcv?Fy zJl(;6Zng5)!|#noewoT(UbRP+($@mh>ZQNrsN1peax2MteFb;!PESH0`YfU z8Tb2j)(bf6J0E`4#|BI!R!gbt>e$ymtEubN)gQP25#R&B^*%#?^miS#{1ot-uIqzh4zRtzO@!ooyLof$jJk;M2h00Q-$1VA{*Gf2!bV_Vw==nmD&x`|O8nfKLOL zqWSayKM0)l8Wr5Mqv!W(!PETihn=aw`jw*j+yQ(V_(tIR+*I3F*l%QjKO%UV{cqCF zfR$vuo`#)_XXj^i>>MNKxH>;7XYGiX`J4f~5jfY)PQgw7J^LF3Pm8k~b`HS~uLUe$ z48Mcc@00NRYMJPCJkuiieLPU!v~(h_wT^>Jft{_xbvGnw@_Xd8L*n&da`Q>?F^>>@W2s z_vdP%wv#+hTq^iHl{_!g*Cdum-eykDXhrp9^Ixe|z~^cigUB|(d^Q>!g$U92K=hhE zbxbCLDpjJLvnk*l#LZ`x7|$a2@%_1lM_+ z#XP{g{jiSwd>PleUej0~?}q(TfY0ucbWi=PgBHSk8@7Xa5kH`}P!S;PZa zbwj=j{kez(Lk6c*NOwd2Ao{2D0P|UA`cLxu%q?~7+zvY#%wt<%=iWN<57)u3Uv^NqZfiT>9=cbwKw*3^;j zgWne9`BWIY8u;|qj6p_#zmquHaR~SafKM&6@|%F)Z*UYMWVh5?=jULLm9OB~H8M|X zdH2MBIc&QG4ox$ZgL&?Uo3h}&8a6K;kdF(@wpIvL^pMv~-z^7MO{6gT57#xKN z`DPt_t`5FHHY$4Fc+8tO_SNa1%S2x1dEdDL0f@;|td_&jtR6ke?}J46+&cyMa%gWpVC5 zKTP@c7JnV`@Ck!MIYPcFxQ^3b7aoJ1IpmpfJ_~#neout0M$-M|{bpr6Ff{kY* z_7JbpFPFe{|x%= z*MLu#Eza+3dJBm_s@VI%2@mdi1RS%;%W1k{;@*c?wN5be-7+iP)B~9;Cejz z?R6>a%;v3~tKfHg9XmD1m(ea8;P)NC=LW4E*6SG4Ka=-&{uJ^G{rOth`FI__vyeZ8 z`STvc`D7jWUkR?~!$bEB7|Yy8EtUyQ=WVKL<9r%^)nKJotxXIMHxH^Hc+5kM9wcNGTA>T1<32H&&GRCaSrSypjk8 zOU2=_k%>yKHdZQX<%!~85Hz>1XloI(`MfBPR8?W5RIXK<+gnz(MPyo5bw)}nIy%+h z@SFDph0=(4DC8&egX;yMoC~8 z5`X*3jy9$H0tLiotvOPq_LVCld6QrxIc;CrcA-?n)G!`YqE1#5fC`Jh^>D0@(|tvgDkf-8&6 zgrw8-u1Jin?JFrFGeetImV%j?Ur)h>UJrj=&$9+lJ zRY-W>r9jE>$BN-Vbi8MlA`$BjN?Md^O-OK>gbJn<>njHFLo{HzN!PSmCaqy3xXVHW zOIodzO1jxJ2?I5yq^wwU0XROMrVw`SL&#ao?=I_UMH9WMl>^TTHB?Yd~5+7Qo;Jm zFIlsB<2sRw9I4i1))MHVOL~J17sJ}c>$j-jqD_~sU9%~;^o?)q*^mvgYu0Ys5TGKZ z5xp)IYr~>g7~3%z3|1%1(yr&jd~q_9xoFeIwd;b`=GNv8GsVV{cwfg_ny*xfvecCZ z&3ax(feWKvt|URl!SbFul6E$WEE`6Ayc`#=)`m(GwK!&?GYwcP>5gM7UCGNlUmcT0 zbbRN~7(m@;jHBGpkgT=CG6Py^eZa)3`#=a8*=lV1G1lS%lqYJ{I5I5BDOKWFq#wz? zDWBJQ&JD$7qM)!$yQH#P;n%Fz}3yPNOU9Q zj<`n5)!5*!ZT(5ClHWB}^b#$>6lEiu;#anmCJE9zaV?-Xv{D+}<;7v_`T-jdGmLap+c>U==Cw81D~4^Rrw19?(h5Tp-gv=yH?u~dH{|ge zkLSk+{UJ6iaeCc((Da^(s|^JA&wcht|ttR<_CA`JvDYGx^45jO|nMDOGO*1%){1v+>+F$ zS_(#U#UcNsB07$oD>HBCbVet*740h6c&WINQLT4ek*2gW3s}6rE z(ep@jW;gv|Jf+QshYJ(c(Re-M88gdm(rdUAamVb8BGc%o_8zxl_l6ZYm&pz4lbFhw zJ~gW<2~_u#cmP4Lqgsvjnpg^SVQeHNKYDDrjjrz5HU=~C>0{zGu_vu(D?X&8H^@RB zOcbx4D9JDzE|vYcZF2Z!Ovz@O{G#FWWX~iIUUOBf)CR;PpeJgZ;mG=^J90deM#QvB zUM4O%DUx2Ni)z@Mh{c72AiH@T_8~^mWKL&74~Kf|muvM}ZVWNmj8}5yQL};ax`J$S zb(fKClW8m&#idZ%lcguf$ZAFAiW&v)_kK$^IoH#32va0s7 ziq#;0)u4!po80)=pu`|sXXD8llM$twu_+L#{Emqc(aRM_WDue_Rva#o&+XVz$xpJR z&z{Lk&dBP$19V}D7o&c6`U+9im&R}zQkaaCiHRSXf^cDFs1OHufJXnJ@>*3{0f76etiJ?yl=7qCU{+s z7B3M-ZY{a^Z=Ii!l9K1$4N+ZisGrBsb*KKx_ON+tmcHc&arZntU+sI7moTrWwu|ly zk@#y1GsZH%+umAp*|0|-;=j&$?sI?}~Yl>VnewbBn7DiW*_V^q=zx%Ko zg7oj#|9a@Jg&d#P=Q{vqbaLdQ^>xmC{dWnb8_H-zZ#E zpU?m6`$qaR#fsUYz5`@Fr0ZY%d*YqLz z)Agr+e&6BwTl7(-?&FOcTXgM@qIy)AW95%-_`QgOAF&cImH%&unA{3+`uzUH120qm z3DMQ@(?7pwG4nF@zc0FnNA&r9j4wfdRvVX(meC*9hwX5yU{U}4-o~wW8gVCYOQAmP z{KV7e_dBMczZL%L=|Au3yZh6)QO5C0{U$P?|0Pt%;_`?4Co}h0feRr=(nJPSKliUg zZ1HgFpXuSO4ynFBM?Nb0J@{|&28=JOzc1ip+Rh30T5GG%jUSDdlsdbPe)l)6gb=l#Ez(6x4yXk8tD7^J8Og0@AJl=jVg>|Qyu@;Txs=RbKi^mzpRe_9gA&` zXp#G8(GUF-(}&r2h-rG`W{dLedOYUq>oVsa%4qsnlhvQn8yxxQex$oJ$BkZ#^heLH PY5qr7TlCd05&wS!CEm7E literal 0 HcmV?d00001 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 */ -- 2.20.1