diff options
| -rw-r--r-- | firmware/zpu/usrp2p/bootloader/init_bootloader.c | 123 | ||||
| -rw-r--r-- | firmware/zpu/usrp2p/bootloader/spi_bootloader.c | 134 | ||||
| -rw-r--r-- | firmware/zpu/usrp2p/bootloader/u2p2-rom.ld | 190 | ||||
| -rw-r--r-- | firmware/zpu/usrp2p/bootloader/udp_bootloader.c | 200 | 
4 files changed, 0 insertions, 647 deletions
| diff --git a/firmware/zpu/usrp2p/bootloader/init_bootloader.c b/firmware/zpu/usrp2p/bootloader/init_bootloader.c deleted file mode 100644 index d797edce4..000000000 --- a/firmware/zpu/usrp2p/bootloader/init_bootloader.c +++ /dev/null @@ -1,123 +0,0 @@ -// -// Copyright 2010-2011 Ettus Research LLC -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program.  If not, see <http://www.gnu.org/licenses/>. -// - -#include <memory_map.h> -#include <nonstdio.h> -#include <hal_io.h> -#include <xilinx_s3_icap.h> -#include <spi_flash.h> -#include <spi_flash_private.h> -#include <mdelay.h> -#include <ihex.h> -#include <bootloader_utils.h> -#include <string.h> -#include <hal_uart.h> -#include <i2c.h> -#include "usrp2/fw_common.h" - -#define BUTTON_PUSHED ((router_status->irqs & PIC_BUTTON) ? 0 : 1) - -void load_ihex(void) { //simple IHEX parser to load proper records into RAM. loads program when it receives end of record. -	char buf[128]; //input data buffer -	uint8_t ihx[32]; //ihex data buffer - -	ihex_record_t ihex_record; -	ihex_record.data = ihx; - -	while(1) { -		gets(buf); - -		if(!ihex_parse(buf, &ihex_record)) { //RAM data record is valid -			if(ihex_record.type == 1) { //end of record -				puts("OK"); -				//load main firmware -				start_program(); -				puts("ERROR: main image returned! Back in IHEX load mode."); -			} else { -				const uint8_t *destination = (uint8_t *)ihex_record.addr + RAM_BASE; -				memcpy((void *) destination, ihex_record.data, ihex_record.length); -				puts("OK"); -			} -		} else puts("NOK"); -	} -} - -int main(int argc, char *argv[]) { -	hal_disable_ints();	// In case we got here via jmp 0x0 -	output_regs->leds = 0xFF; -	mdelay(100); -	output_regs->leds = 0x00; -	hal_uart_init(); -	spif_init(); -	i2c_init(); //for EEPROM -	puts("USRP2+ bootloader super ultra ZPU edition\n"); -	 -	bool production_image = find_safe_booted_flag(); -	set_safe_booted_flag(0); //haven't booted yet -	 -	if(BUTTON_PUSHED) { //see memory_map.h -		puts("Starting USRP2+ in safe mode."); -		if(is_valid_fw_image(SAFE_FW_IMAGE_LOCATION_ADDR)) { -				set_safe_booted_flag(1); //let the firmware know it's the safe image -				spi_flash_read(SAFE_FW_IMAGE_LOCATION_ADDR, FW_IMAGE_SIZE_BYTES, (void *)RAM_BASE); -				start_program(); -				puts("ERROR: return from main program! This should never happen!"); -				icap_reload_fpga(SAFE_FPGA_IMAGE_LOCATION_ADDR); -			} else { -				puts("ERROR: no safe firmware image available. I am a brick. Feel free to load IHEX to RAM."); -				load_ihex(); -			} -	} -	 -	if(!production_image) { -		puts("Checking for valid production FPGA image..."); -		if(is_valid_fpga_image(PROD_FPGA_IMAGE_LOCATION_ADDR)) { -			puts("Valid production FPGA image found. Attempting to boot."); -			set_safe_booted_flag(1); -			mdelay(300); //so serial output can finish -			icap_reload_fpga(PROD_FPGA_IMAGE_LOCATION_ADDR); -		} -		puts("No valid production FPGA image found.\nAttempting to load production firmware..."); -	} -	if(is_valid_fw_image(PROD_FW_IMAGE_LOCATION_ADDR)) { -		puts("Valid production firmware found. Loading..."); -		spi_flash_read(PROD_FW_IMAGE_LOCATION_ADDR, FW_IMAGE_SIZE_BYTES, (void *)RAM_BASE); -		puts("Finished loading. Starting image."); -		mdelay(300); -		start_program(); -		puts("ERROR: Return from main program! This should never happen!"); -		//if this happens, though, the safest thing to do is reboot the whole FPGA and start over. -		mdelay(300); -		icap_reload_fpga(SAFE_FPGA_IMAGE_LOCATION_ADDR); -		return 1; -	} -	puts("No valid production firmware found. Trying safe firmware..."); -	if(is_valid_fw_image(SAFE_FW_IMAGE_LOCATION_ADDR)) { -		spi_flash_read(SAFE_FW_IMAGE_LOCATION_ADDR, FW_IMAGE_SIZE_BYTES, (void *)RAM_BASE); -		puts("Finished loading. Starting image."); -		mdelay(300); -		start_program(); -		puts("ERROR: return from main program! This should never happen!"); -		mdelay(300); -		icap_reload_fpga(SAFE_FPGA_IMAGE_LOCATION_ADDR); -		return 1; -	} -	puts("ERROR: no safe firmware image available. I am a brick. Feel free to load IHEX to RAM."); -	load_ihex(); - -	return 0; -} diff --git a/firmware/zpu/usrp2p/bootloader/spi_bootloader.c b/firmware/zpu/usrp2p/bootloader/spi_bootloader.c deleted file mode 100644 index 3e66d41cb..000000000 --- a/firmware/zpu/usrp2p/bootloader/spi_bootloader.c +++ /dev/null @@ -1,134 +0,0 @@ -/* -*- c -*- */ -/* - * Copyright 2009-2011 Ettus Research LLC - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program.  If not, see <http://www.gnu.org/licenses/>. - */ - -#include <hal_io.h> -#include <nonstdio.h> -#include <mdelay.h> -#include <spi_flash.h> -#include <quadradio/flashdir.h> -#include <quadradio/simple_binary_format.h> -#include <stdlib.h> - - -void hal_uart_init(void); -void spif_init(void); - -void pic_interrupt_handler() __attribute__ ((interrupt_handler)); - -void pic_interrupt_handler() -{ -  // nop stub -} - -static void -error(int e) -{ -  putstr("ERR"); -  puthex8(e); -  newline(); -} - -static void -load(uint32_t flash_addr, uint32_t ram_addr, uint32_t size) -{ -  spi_flash_read(flash_addr, size, (void *) ram_addr); -} - -static bool -load_from_slot(const struct flashdir *fd, int fw_slot) -{ -    putstr("Loading f/w image "); -    putchar('0' + fw_slot); -    putstr("... "); - -    if (fw_slot >= fd->fw_nslots){ -      error(1); -      return false; -    } - -    int slot = fw_slot + fd->fw_slot0; -    if (fd->slot[slot].start == 0 || fd->slot[slot].start == 0xffff -	|| fd->slot[slot].len == 0 || fd->slot[slot].len == 0xffff){ -      error(2); -      return false; -    } -     -    uint32_t sbf_base = fd->slot[slot].start << spi_flash_log2_sector_size(); -    uint32_t sbf_len  = fd->slot[slot].len << spi_flash_log2_sector_size(); -    uint32_t sbf_offset = 0; - -    struct sbf_header sbf; -    spi_flash_read(sbf_base, sizeof(struct sbf_header), &sbf); -    if (sbf.magic != SBF_MAGIC || sbf.nsections > SBF_MAX_SECTIONS){ -      error(3); -      return false; -    } -    sbf_offset += sizeof(struct sbf_header); - -    unsigned int i; -    for (i = 0; i < sbf.nsections; i++){ -      if (sbf_offset + sbf.sec_desc[i].length > sbf_len){ -	error(4); -	return false; -      } -      load(sbf_offset + sbf_base, -	   sbf.sec_desc[i].target_addr, -	   sbf.sec_desc[i].length); -      sbf_offset += sbf.sec_desc[i].length; -    } -    putstr("Done!"); - -    typedef void (*fptr_t)(void); -    (*(fptr_t) sbf.entry)();		// almost certainly no return - -    return true; -} - -int -main(int argc, char **argv) -{ -  hal_uart_init(); -  spif_init(); - -  sr_leds->leds =  0; -  mdelay(100); -  sr_leds->leds = ~0; -  mdelay(100); -  sr_leds->leds =  0; - -  putstr("\n>>> spi_bootloader <<<\n"); - -  const struct flashdir *fd = get_flashdir(); -  if (fd == 0) -    abort(); - -  while(1){ -    int sw; -    int fw_slot; -     -    sw = readback->switches; -    fw_slot = sw & 0x7; - -    if (!load_from_slot(fd, fw_slot)){ -      if (fw_slot != 0){ -	putstr("Falling back to slot 0\n"); -	load_from_slot(fd, 0); -      } -    } -  } -} diff --git a/firmware/zpu/usrp2p/bootloader/u2p2-rom.ld b/firmware/zpu/usrp2p/bootloader/u2p2-rom.ld deleted file mode 100644 index 4c9eaa8e5..000000000 --- a/firmware/zpu/usrp2p/bootloader/u2p2-rom.ld +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Same as default, but with bss and stack moved to top 2K of main ram - * Copied from qr-rom.ld - */ - -/* Default linker script, for normal executables */ -OUTPUT_FORMAT("elf32-microblaze", "", "") -/*SEARCH_DIR("/home/eb/build/Xilinx_EDK_GNU_10.1i/mb/release/lin/mb/microblaze-xilinx-elf/lib");*/ - - -ENTRY(_start) -_TEXT_START_ADDR = DEFINED(_TEXT_START_ADDR) ? _TEXT_START_ADDR : 0x50; -_HEAP_SIZE = DEFINED(_HEAP_SIZE) ? _HEAP_SIZE : 0x0; -_STACK_SIZE = DEFINED(_STACK_SIZE) ? _STACK_SIZE : 0x400; -_BSS_START_ADDR = DEFINED(_BSS_START_ADDR) ? _BSS_START_ADDR : 0xF800; -SECTIONS -{ -  .vectors.reset 0x0 : { KEEP (*(.vectors.reset)) } = 0 -  .vectors.sw_exception 0x8 : { KEEP (*(.vectors.sw_exception)) } = 0 -  .vectors.interrupt 0x10 : { KEEP (*(.vectors.interrupt)) } = 0 -  .vectors.debug_sw_break 0x18 : { KEEP (*(.vectors.debug_sw_break)) } = 0 -  .vectors.hw_exception 0x20 : { KEEP (*(.vectors.hw_exception)) } = 0 -  . = _TEXT_START_ADDR; -   _ftext  =  .; -  .text : { -    *(.text) -    *(.text.*) -    *(.gnu.linkonce.t.*) -  } -   _etext  =  .; -  .init : { KEEP (*(.init))	} =0 -  .fini : { KEEP (*(.fini))	} =0 -  PROVIDE (__CTOR_LIST__ = .); -  PROVIDE (___CTOR_LIST__ = .); -  .ctors   : -  { -    /* gcc uses crtbegin.o to find the start of -       the constructors, so we make sure it is -       first.  Because this is a wildcard, it -       doesn't matter if the user does not -       actually link against crtbegin.o; the -       linker won't look for a file to match a -       wildcard.  The wildcard also means that it -       doesn't matter which directory crtbegin.o -       is in.  */ -    KEEP (*crtbegin.o(.ctors)) -    /* We don't want to include the .ctor section from -       from the crtend.o file until after the sorted ctors. -       The .ctor section from the crtend file contains the -       end of ctors marker and it must be last */ -    KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) -    KEEP (*(SORT(.ctors.*))) -    KEEP (*(.ctors)) -  } -  PROVIDE (__CTOR_END__ = .); -  PROVIDE (___CTOR_END__ = .); -  PROVIDE (__DTOR_LIST__ = .); -  PROVIDE (___DTOR_LIST__ = .); -   .dtors         : -  { -    KEEP (*crtbegin.o(.dtors)) -    KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) -    KEEP (*(SORT(.dtors.*))) -    KEEP (*(.dtors)) -  } -  PROVIDE (__DTOR_END__ = .); -  PROVIDE (___DTOR_END__ = .); -   . = ALIGN(4); -    _frodata = . ; -  .rodata : { -    *(.rodata) -    *(.rodata.*) -    *(.gnu.linkonce.r.*) -    CONSTRUCTORS; /* Is this needed? */ -  } -   _erodata = .; -  /* Alignments by 8 to ensure that _SDA2_BASE_ on a word boundary */ -  /* Note that .sdata2 and .sbss2 must be contiguous */ -  . = ALIGN(8); -   _ssrw = .; -  .sdata2 : { -    *(.sdata2) -    *(.sdata2.*) -    *(.gnu.linkonce.s2.*) -  } -  . = ALIGN(4); -  .sbss2 : { -    PROVIDE (__sbss2_start = .); -    *(.sbss2) -    *(.sbss2.*) -    *(.gnu.linkonce.sb2.*) -    PROVIDE (__sbss2_end = .); -  } -  . = ALIGN(8); -   _essrw = .; -   _ssrw_size = _essrw - _ssrw; -   PROVIDE (_SDA2_BASE_ = _ssrw + (_ssrw_size / 2 )); -   . = ALIGN(4); -   _fdata = .; -  .data : { -    *(.data) -    *(.gnu.linkonce.d.*) -    CONSTRUCTORS; /* Is this needed? */ -  } -   _edata = . ; -   /* Added to handle pic code */ -  .got : { -    *(.got) -  } -  .got1 : { -    *(.got1) -  } -  .got2 : { -    *(.got2) -  } -  /* Added by Sathya to handle C++ exceptions */ -  .eh_frame : { -    *(.eh_frame) -  } -  .jcr : { -    *(.jcr) -  } -  .gcc_except_table : { -    *(.gcc_except_table) -  } -  /* Alignments by 8 to ensure that _SDA_BASE_ on a word boundary */ -  /* Note that .sdata and .sbss must be contiguous */ -  . = ALIGN(8); -   _ssro = .; -  .sdata : { -    *(.sdata) -    *(.sdata.*) -    *(.gnu.linkonce.s.*) -  } -  . = ALIGN(4); -  .sbss : { -    PROVIDE (__sbss_start = .); -    *(.sbss) -    *(.sbss.*) -    *(.gnu.linkonce.sb.*) -    PROVIDE (__sbss_end = .); -  } -  . = ALIGN(8); -   _essro = .; -   _ssro_size = _essro - _ssro; -  PROVIDE (_SDA_BASE_ = _ssro + (_ssro_size / 2 )); -   . = _BSS_START_ADDR; -   . = ALIGN(4); -   _fbss = .; -  .bss : { -    PROVIDE (__bss_start = .); -    *(.bss) -    *(.bss.*) -    *(.gnu.linkonce.b.*) -    *(COMMON) -    . = ALIGN(4); -    PROVIDE (__bss_end = .); -  } -   . = ALIGN(4); -  .heap : { -     _heap = .; -     _heap_start = .; -     . += _HEAP_SIZE; -     _heap_end = .; -  } -  _end = .; -   . = ALIGN(4); -   . = 0xFFF0; -  .stack : { -  /* -     _stack_end = .; -     . += _STACK_SIZE; -     . = ALIGN(8); -     _stack = .; -     _end = .; -   */ -   _stack_end = .; -   _stack = .; -  } -  .tdata : { -    *(.tdata) -    *(.tdata.*) -    *(.gnu.linkonce.td.*) -  } -  .tbss : { -    *(.tbss) -    *(.tbss.*) -    *(.gnu.linkonce.tb.*) -  } -} diff --git a/firmware/zpu/usrp2p/bootloader/udp_bootloader.c b/firmware/zpu/usrp2p/bootloader/udp_bootloader.c deleted file mode 100644 index 118de2ae9..000000000 --- a/firmware/zpu/usrp2p/bootloader/udp_bootloader.c +++ /dev/null @@ -1,200 +0,0 @@ -// -// Copyright 2010-2011 Ettus Research LLC -// -/* - * Copyright 2007,2008 Free Software Foundation, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program.  If not, see <http://www.gnu.org/licenses/>. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <lwip/ip.h> -#include <lwip/udp.h> -#include "u2_init.h" -#include "memory_map.h" -#include "spi.h" -#include "hal_io.h" -#include "pic.h" -#include <stdbool.h> -#include "ethernet.h" -#include "nonstdio.h" -#include <net/padded_eth_hdr.h> -#include <net_common.h> -#include "memcpy_wa.h" -#include <stddef.h> -#include <stdlib.h> -#include <string.h> -#include "clocks.h" -#include "usrp2/fw_common.h" -#include <i2c.h> -#include <ethertype.h> -#include <arp_cache.h> -#include "udp_fw_update.h" -#include "pkt_ctrl.h" -#include "banal.h" -#include <bootloader_utils.h> -#include <spi_flash.h> -#include <xilinx_s3_icap.h> -#include <mdelay.h> - -#define BUTTON_PUSHED ((router_status->irqs & PIC_BUTTON) ? 0 : 1) - -static void handle_inp_packet(uint32_t *buff, size_t num_lines){ - -  //test if its an ip recovery packet -  typedef struct{ -      padded_eth_hdr_t eth_hdr; -      char code[4]; -      union { -        struct ip_addr ip_addr; -      } data; -  }recovery_packet_t; -  recovery_packet_t *recovery_packet = (recovery_packet_t *)buff; -  if (recovery_packet->eth_hdr.ethertype == 0xbeee && strncmp(recovery_packet->code, "addr", 4) == 0){ -      printf("Got ip recovery packet: "); print_ip_addr(&recovery_packet->data.ip_addr); newline(); -      set_ip_addr(&recovery_packet->data.ip_addr); -      return; -  } - -  //pass it to the slow-path handler -  handle_eth_packet(buff, num_lines); -} - - -//------------------------------------------------------------------ - -/* - * Called when eth phy state changes (w/ interrupts disabled) - */ -void link_changed_callback(int speed){ -    printf("\neth link changed: speed = %d\n", speed); -    if (speed != 0){ -        hal_set_leds(LED_RJ45, LED_RJ45); -        pkt_ctrl_set_routing_mode(PKT_CTRL_ROUTING_MODE_MASTER); -        send_gratuitous_arp(); -    } -    else{ -        hal_set_leds(0x0, LED_RJ45); -        pkt_ctrl_set_routing_mode(PKT_CTRL_ROUTING_MODE_SLAVE); -    } -} - -static void do_the_bootload_thing(void) { - -    bool production_image = find_safe_booted_flag(); -	set_safe_booted_flag(0); //haven't booted yet -	 -	if(BUTTON_PUSHED) { //see memory_map.h -		puts("Starting USRP2+ in safe mode. I am a brick. Feel free to reprogram me via the UDP burner."); -        return; -        //no longer necessary since we can just burn from UDP via the bootloader now -/* -        if(is_valid_fw_image(SAFE_FW_IMAGE_LOCATION_ADDR)) { -				set_safe_booted_flag(1); //let the firmware know it's the safe image -				spi_flash_read(SAFE_FW_IMAGE_LOCATION_ADDR, FW_IMAGE_SIZE_BYTES, (void *)RAM_BASE); -				start_program(); -				puts("ERROR: return from main program! This should never happen!"); -				icap_reload_fpga(SAFE_FPGA_IMAGE_LOCATION_ADDR); -			} else { -				puts("ERROR: no safe firmware image available. I am a brick. Feel free to reprogram me via the UDP burner."); -				return; -			} -*/ -	} -	 -	if(!production_image) { -		puts("Checking for valid production FPGA image..."); -		if(is_valid_fpga_image(PROD_FPGA_IMAGE_LOCATION_ADDR)) { -			puts("Valid production FPGA image found. Attempting to boot."); -			set_safe_booted_flag(1); -			mdelay(300); //so serial output can finish -			icap_reload_fpga(PROD_FPGA_IMAGE_LOCATION_ADDR); -		} -		puts("No valid production FPGA image found.\nAttempting to load production firmware..."); -	} -	if(is_valid_fw_image(PROD_FW_IMAGE_LOCATION_ADDR)) { -		puts("Valid production firmware found. Loading..."); -		spi_flash_read(PROD_FW_IMAGE_LOCATION_ADDR, FW_IMAGE_SIZE_BYTES, (void *)RAM_BASE); -		puts("Finished loading. Starting image."); -		mdelay(300); -		start_program(); -		puts("ERROR: Return from main program! This should never happen!"); -		//if this happens, though, the safest thing to do is reboot the whole FPGA and start over. -		mdelay(300); -		icap_reload_fpga(SAFE_FPGA_IMAGE_LOCATION_ADDR); -		return; -	} -	puts("No valid production firmware found. Trying safe firmware..."); -	if(is_valid_fw_image(SAFE_FW_IMAGE_LOCATION_ADDR)) { -		spi_flash_read(SAFE_FW_IMAGE_LOCATION_ADDR, FW_IMAGE_SIZE_BYTES, (void *)RAM_BASE); -		puts("Finished loading. Starting image."); -		mdelay(300); -		start_program(); -		puts("ERROR: return from main program! This should never happen!"); -		mdelay(300); -		icap_reload_fpga(SAFE_FPGA_IMAGE_LOCATION_ADDR); -		return; -	} -    puts("ERROR: no safe firmware image available. I am a brick. Feel free to reprogram me via the UDP burner."); -} - -int -main(void) -{ -  u2_init(); -  spif_init(); - -  set_default_mac_addr(); -  set_default_ip_addr(); - -  putstr("\nN210 bootloader, UDP edition\n"); -  print_mac_addr(ethernet_mac_addr()); newline(); -  print_ip_addr(get_ip_addr()); newline(); -  printf("FPGA compatibility number: %d\n", USRP2_FPGA_COMPAT_NUM); -  printf("Firmware compatibility number: %d\n", USRP2_FW_COMPAT_NUM); -   -  do_the_bootload_thing(); -   -  //if we reach this point, the bootloader has fallen through, so we initalize the UDP handler for updating - -  //1) register the addresses into the network stack -  //TODO: make this a fixed IP address, don't depend on EEPROM -  register_addrs(ethernet_mac_addr(), get_ip_addr()); -  pkt_ctrl_program_inspector(get_ip_addr(), USRP2_UDP_DSP0_PORT); - -  //2) register callbacks for udp ports we service -  init_udp_listeners(); -  register_udp_listener(USRP2_UDP_UPDATE_PORT, handle_udp_fw_update_packet); -   -  pkt_ctrl_set_routing_mode(PKT_CTRL_ROUTING_MODE_SLAVE); - -  //4) setup ethernet hardware to bring the link up -  ethernet_register_link_changed_callback(link_changed_callback); -  ethernet_init(); - -  while(true){ - -    size_t num_lines; -    void *buff = pkt_ctrl_claim_incoming_buffer(&num_lines); -    if (buff != NULL){ -        handle_inp_packet((uint32_t *)buff, num_lines); -        pkt_ctrl_release_incoming_buffer(); -    } - -    pic_interrupt_handler(); -  } -} | 
