From 0a643be39eb710649560f80c6ce0bc32576813fd Mon Sep 17 00:00:00 2001 From: Andrew Kannan Date: Tue, 7 Apr 2020 00:01:11 -0400 Subject: Update 072 based CannonKeys boards to rely on core QMK support (#8716) * Swap CannonKeys 072 boards over to rely on core QMK backlight support * Rename keymap * Rename via_tsangan back to via --- keyboards/cannonkeys/stm32f072/keyboard.c | 241 -------------------------- keyboards/cannonkeys/stm32f072/keyboard.h | 23 --- keyboards/cannonkeys/stm32f072/led.c | 251 ---------------------------- keyboards/cannonkeys/stm32f072/led_custom.h | 8 - keyboards/cannonkeys/stm32f072/ws2812.c | 151 ----------------- 5 files changed, 674 deletions(-) delete mode 100644 keyboards/cannonkeys/stm32f072/keyboard.c delete mode 100644 keyboards/cannonkeys/stm32f072/keyboard.h delete mode 100644 keyboards/cannonkeys/stm32f072/led.c delete mode 100644 keyboards/cannonkeys/stm32f072/led_custom.h delete mode 100644 keyboards/cannonkeys/stm32f072/ws2812.c (limited to 'keyboards/cannonkeys/stm32f072') diff --git a/keyboards/cannonkeys/stm32f072/keyboard.c b/keyboards/cannonkeys/stm32f072/keyboard.c deleted file mode 100644 index 39f912506d..0000000000 --- a/keyboards/cannonkeys/stm32f072/keyboard.c +++ /dev/null @@ -1,241 +0,0 @@ -#include "keyboard.h" -#include "ch.h" -#include "hal.h" -#include "led_custom.h" -#include "util.h" -#include "quantum.h" - -#include "raw_hid.h" -#include "dynamic_keymap.h" -#include "tmk_core/common/eeprom.h" -#include "version.h" // for QMK_BUILDDATE used in EEPROM magic - -#include "via.h" -#define EEPROM_CUSTOM_BACKLIGHT (VIA_EEPROM_CUSTOM_CONFIG_ADDR) - -backlight_config_t kb_backlight_config = { - .enable = true, - .breathing = true, - .level = BACKLIGHT_LEVELS -}; - -void backlight_config_save(){ - eeprom_update_byte((uint8_t*)EEPROM_CUSTOM_BACKLIGHT, kb_backlight_config.raw); -} - -void backlight_config_load(){ - kb_backlight_config.raw = eeprom_read_byte((uint8_t*)EEPROM_CUSTOM_BACKLIGHT); -} - -// Called from via_init() if VIA_ENABLE -// Called from matrix_init_kb() if not VIA_ENABLE -void via_init_kb(void) -{ - // If the EEPROM has the magic, the data is good. - // OK to load from EEPROM. - if (via_eeprom_is_valid()) { - backlight_config_load(); - } else { - // If the EEPROM has not been saved before, or is out of date, - // save the default values to the EEPROM. Default values - // come from construction of the backlight_config instance. - backlight_config_save(); - - // DO NOT set EEPROM valid here, let caller do this - } -} - -__attribute__ ((weak)) -void matrix_init_board(void); - -void matrix_init_kb(void){ - // If VIA is disabled, we still need to load backlight settings. - // Call via_init_kb() the same way as via_init(), with setting - // EEPROM valid afterwards. -#ifndef VIA_ENABLE - via_init_kb(); - via_eeprom_set_valid(true); -#endif // VIA_ENABLE - backlight_init_ports(); - - matrix_init_board(); -} - -bool process_record_kb(uint16_t keycode, keyrecord_t *record) { - switch (keycode) { - case BL_INC: - if (record->event.pressed) { - kb_backlight_config.level = kb_backlight_config.level + 1; - if(kb_backlight_config.level > BACKLIGHT_LEVELS){ - kb_backlight_config.level = BACKLIGHT_LEVELS; - } - backlight_set(kb_backlight_config.level); - backlight_config_save(); - } - return false; - case BL_TOGG: - if (record->event.pressed) { - kb_backlight_config.enable = !kb_backlight_config.enable; - if(kb_backlight_config.enable){ - backlight_set(kb_backlight_config.level); - } else { - backlight_set(0); - } - backlight_config_save(); - } - return false; - - case BL_DEC: - if (record->event.pressed) { - if(kb_backlight_config.level <= 1){ - kb_backlight_config.level = 0; - } else { - kb_backlight_config.level = kb_backlight_config.level - 1; - } - backlight_set(kb_backlight_config.level); - backlight_config_save(); - } - return false; - case BL_BRTG: - if (record->event.pressed) { - kb_backlight_config.breathing = !kb_backlight_config.breathing; - breathing_toggle(); - backlight_config_save(); - } - return false; - default: - break; - } - - return process_record_user(keycode, record);; -} - -#ifdef VIA_ENABLE - -void backlight_get_value( uint8_t *data ) -{ - uint8_t *value_id = &(data[0]); - uint8_t *value_data = &(data[1]); - switch (*value_id) - { - case id_qmk_backlight_brightness: - { - // level / BACKLIGHT_LEVELS * 255 - value_data[0] = ((uint16_t)kb_backlight_config.level) * 255 / BACKLIGHT_LEVELS; - break; - } - case id_qmk_backlight_effect: - { - value_data[0] = kb_backlight_config.breathing ? 1 : 0; - break; - } - } -} - -void backlight_set_value( uint8_t *data ) -{ - uint8_t *value_id = &(data[0]); - uint8_t *value_data = &(data[1]); - switch (*value_id) - { - case id_qmk_backlight_brightness: - { - // level / 255 * BACKLIGHT_LEVELS - kb_backlight_config.level = ((uint16_t)value_data[0]) * BACKLIGHT_LEVELS / 255; - backlight_set(kb_backlight_config.level); - break; - } - case id_qmk_backlight_effect: - { - if ( value_data[0] == 0 ) { - kb_backlight_config.breathing = false; - breathing_disable(); - } else { - kb_backlight_config.breathing = true; - breathing_enable(); - } - break; - } - } -} - -void raw_hid_receive_kb( uint8_t *data, uint8_t length ) -{ - uint8_t *command_id = &(data[0]); - uint8_t *command_data = &(data[1]); - switch ( *command_id ) - { - case id_lighting_set_value: - { - backlight_set_value(command_data); - break; - } - case id_lighting_get_value: - { - backlight_get_value(command_data); - break; - } - case id_lighting_save: - { - backlight_config_save(); - break; - } - default: - { - // Unhandled message. - *command_id = id_unhandled; - break; - } - } - // DO NOT call raw_hid_send(data,length) here, let caller do this -} -#endif - -// -// In the case of VIA being disabled, we still need to check if -// keyboard level EEPROM memory is valid before loading. -// Thus these are copies of the same functions in VIA, since -// the backlight settings reuse VIA's EEPROM magic/version, -// and the ones in via.c won't be compiled in. -// -// Yes, this is sub-optimal, and is only here for completeness -// (i.e. catering to the 1% of people that want wilba.tech LED bling -// AND want persistent settings BUT DON'T want to use dynamic keymaps/VIA). -// -#ifndef VIA_ENABLE - -bool via_eeprom_is_valid(void) -{ - char *p = QMK_BUILDDATE; // e.g. "2019-11-05-11:29:54" - uint8_t magic0 = ( ( p[2] & 0x0F ) << 4 ) | ( p[3] & 0x0F ); - uint8_t magic1 = ( ( p[5] & 0x0F ) << 4 ) | ( p[6] & 0x0F ); - uint8_t magic2 = ( ( p[8] & 0x0F ) << 4 ) | ( p[9] & 0x0F ); - - return (eeprom_read_byte( (void*)VIA_EEPROM_MAGIC_ADDR+0 ) == magic0 && - eeprom_read_byte( (void*)VIA_EEPROM_MAGIC_ADDR+1 ) == magic1 && - eeprom_read_byte( (void*)VIA_EEPROM_MAGIC_ADDR+2 ) == magic2 ); -} - -// Sets VIA/keyboard level usage of EEPROM to valid/invalid -// Keyboard level code (eg. via_init_kb()) should not call this -void via_eeprom_set_valid(bool valid) -{ - char *p = QMK_BUILDDATE; // e.g. "2019-11-05-11:29:54" - uint8_t magic0 = ( ( p[2] & 0x0F ) << 4 ) | ( p[3] & 0x0F ); - uint8_t magic1 = ( ( p[5] & 0x0F ) << 4 ) | ( p[6] & 0x0F ); - uint8_t magic2 = ( ( p[8] & 0x0F ) << 4 ) | ( p[9] & 0x0F ); - - eeprom_update_byte( (void*)VIA_EEPROM_MAGIC_ADDR+0, valid ? magic0 : 0xFF); - eeprom_update_byte( (void*)VIA_EEPROM_MAGIC_ADDR+1, valid ? magic1 : 0xFF); - eeprom_update_byte( (void*)VIA_EEPROM_MAGIC_ADDR+2, valid ? magic2 : 0xFF); -} - -void via_eeprom_reset(void) -{ - // Set the VIA specific EEPROM state as invalid. - via_eeprom_set_valid(false); - // Set the TMK/QMK EEPROM state as invalid. - eeconfig_disable(); -} - -#endif // VIA_ENABLE diff --git a/keyboards/cannonkeys/stm32f072/keyboard.h b/keyboards/cannonkeys/stm32f072/keyboard.h deleted file mode 100644 index 81dd1b704e..0000000000 --- a/keyboards/cannonkeys/stm32f072/keyboard.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "quantum.h" - -typedef union { - uint8_t raw; - struct { - bool enable :1; - bool breathing : 1; - uint8_t level :6; - }; -} backlight_config_t; - -// Backlighting -extern backlight_config_t kb_backlight_config; -extern bool kb_backlight_breathing; -void backlight_init_ports(void); -void backlight_set(uint8_t level); -bool is_breathing(void); -void breathing_enable(void); -void breathing_disable(void); -void backlight_config_load(void); -void backlight_config_save(void); diff --git a/keyboards/cannonkeys/stm32f072/led.c b/keyboards/cannonkeys/stm32f072/led.c deleted file mode 100644 index 5c7df47da7..0000000000 --- a/keyboards/cannonkeys/stm32f072/led.c +++ /dev/null @@ -1,251 +0,0 @@ -/* -Copyright 2012 Jun Wako - -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 2 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 . -*/ - -#include "hal.h" -#include "led_custom.h" -#include "keyboard.h" -#include "printf.h" - -static void breathing_callback(PWMDriver *pwmp); - -static PWMConfig pwmCFG = { - 0xFFFF, /* PWM clock frequency */ - 256, /* PWM period (in ticks) 1S (1/10kHz=0.1mS 0.1ms*10000 ticks=1S) */ - NULL, /* No Callback */ - { - {PWM_OUTPUT_ACTIVE_HIGH, NULL}, /* Enable Channel 0 */ - {PWM_OUTPUT_DISABLED, NULL}, - {PWM_OUTPUT_DISABLED, NULL}, - {PWM_OUTPUT_DISABLED, NULL} - }, - 0, /* HW dependent part.*/ - 0 -}; - -static PWMConfig pwmCFG_breathing = { - 0xFFFF, /* 10kHz PWM clock frequency */ - 256, /* PWM period (in ticks) 1S (1/10kHz=0.1mS 0.1ms*10000 ticks=1S) */ - breathing_callback, /* Breathing Callback */ - { - {PWM_OUTPUT_ACTIVE_HIGH, NULL}, /* Enable Channel 0 */ - {PWM_OUTPUT_DISABLED, NULL}, - {PWM_OUTPUT_DISABLED, NULL}, - {PWM_OUTPUT_DISABLED, NULL} - }, - 0, /* HW dependent part.*/ - 0 -}; - -// See http://jared.geek.nz/2013/feb/linear-led-pwm -static uint16_t cie_lightness(uint16_t v) { - if (v <= 5243) // if below 8% of max - return v / 9; // same as dividing by 900% - else { - uint32_t y = (((uint32_t) v + 10486) << 8) / (10486 + 0xFFFFUL); // add 16% of max and compare - // to get a useful result with integer division, we shift left in the expression above - // and revert what we've done again after squaring. - y = y * y * y >> 8; - if (y > 0xFFFFUL) // prevent overflow - return 0xFFFFU; - else - return (uint16_t) y; - } -} - - -uint8_t get_backlight_level(void){ - return kb_backlight_config.level; -} - -void backlight_init_ports(void) { - printf("backlight_init_ports()\n"); - palSetPadMode(GPIOA, 6, PAL_MODE_ALTERNATE(1)); - pwmStart(&PWMD3, &pwmCFG); - // pwmEnableChannel(&PWMD3, 0, PWM_FRACTION_TO_WIDTH(&PWMD3, 0xFFFF,cie_lightness(0xFFFF))); - if(kb_backlight_config.enable){ - backlight_set(kb_backlight_config.level); - if(kb_backlight_config.breathing){ - breathing_enable(); - } - } else { - backlight_set(0); - } - -} - -void backlight_set(uint8_t level) { - printf("backlight_set(%d)\n", level); - uint32_t duty = (uint32_t)(cie_lightness(0xFFFF * (uint32_t) level / BACKLIGHT_LEVELS)); - printf("duty: (%d)\n", duty); - if (level == 0) { - // Turn backlight off - pwmDisableChannel(&PWMD3, 0); - } else { - // Turn backlight on - if(!is_breathing()){ - pwmEnableChannel(&PWMD3, 0, PWM_FRACTION_TO_WIDTH(&PWMD3,0xFFFF,duty)); - } - } -} - - -uint8_t backlight_tick = 0; - -void backlight_task(void) { -} - -#define BREATHING_NO_HALT 0 -#define BREATHING_HALT_OFF 1 -#define BREATHING_HALT_ON 2 -#define BREATHING_STEPS 128 - -static uint8_t breathing_period = BREATHING_PERIOD; -static uint8_t breathing_halt = BREATHING_NO_HALT; -static uint16_t breathing_counter = 0; - -bool is_breathing(void) { - return PWMD3.config == &pwmCFG_breathing; -} - -#define breathing_min() do {breathing_counter = 0;} while (0) -#define breathing_max() do {breathing_counter = breathing_period * 256 / 2;} while (0) - - -void breathing_interrupt_enable(void){ - pwmStop(&PWMD3); - pwmStart(&PWMD3, &pwmCFG_breathing); - chSysLockFromISR(); - pwmEnablePeriodicNotification(&PWMD3); - pwmEnableChannelI( - &PWMD3, - 0, - PWM_FRACTION_TO_WIDTH( - &PWMD3, - 0xFFFF, - 0xFFFF - ) - ); - chSysUnlockFromISR(); -} - -void breathing_interrupt_disable(void){ - pwmStop(&PWMD3); - pwmStart(&PWMD3, &pwmCFG); -} - -void breathing_enable(void) -{ - breathing_counter = 0; - breathing_halt = BREATHING_NO_HALT; - breathing_interrupt_enable(); -} - -void breathing_pulse(void) -{ - if (get_backlight_level() == 0) - breathing_min(); - else - breathing_max(); - breathing_halt = BREATHING_HALT_ON; - breathing_interrupt_enable(); -} - -void breathing_disable(void) -{ - printf("breathing_disable()\n"); - breathing_interrupt_disable(); - // Restore backlight level - backlight_set(get_backlight_level()); -} - -void breathing_self_disable(void) -{ - if (get_backlight_level() == 0) - breathing_halt = BREATHING_HALT_OFF; - else - breathing_halt = BREATHING_HALT_ON; -} - -void breathing_toggle(void) { - if (is_breathing()){ - printf("disable breathing\n"); - breathing_disable(); - } else { - printf("enable breathing\n"); - breathing_enable(); - } -} - -void breathing_period_set(uint8_t value) -{ - if (!value) - value = 1; - breathing_period = value; -} - -void breathing_period_default(void) { - breathing_period_set(BREATHING_PERIOD); -} - -void breathing_period_inc(void) -{ - breathing_period_set(breathing_period+1); -} - -void breathing_period_dec(void) -{ - breathing_period_set(breathing_period-1); -} - -/* To generate breathing curve in python: - * from math import sin, pi; [int(sin(x/128.0*pi)**4*255) for x in range(128)] - */ -static const uint8_t breathing_table[BREATHING_STEPS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 17, 20, 24, 28, 32, 36, 41, 46, 51, 57, 63, 70, 76, 83, 91, 98, 106, 113, 121, 129, 138, 146, 154, 162, 170, 178, 185, 193, 200, 207, 213, 220, 225, 231, 235, 240, 244, 247, 250, 252, 253, 254, 255, 254, 253, 252, 250, 247, 244, 240, 235, 231, 225, 220, 213, 207, 200, 193, 185, 178, 170, 162, 154, 146, 138, 129, 121, 113, 106, 98, 91, 83, 76, 70, 63, 57, 51, 46, 41, 36, 32, 28, 24, 20, 17, 15, 12, 10, 8, 6, 5, 4, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - -// Use this before the cie_lightness function. -static inline uint16_t scale_backlight(uint16_t v) { - return v / BACKLIGHT_LEVELS * get_backlight_level(); -} - -static void breathing_callback(PWMDriver *pwmp) -{ - (void)pwmp; - uint16_t interval = (uint16_t) breathing_period * 256 / BREATHING_STEPS; - // resetting after one period to prevent ugly reset at overflow. - breathing_counter = (breathing_counter + 1) % (breathing_period * 256); - uint8_t index = breathing_counter / interval % BREATHING_STEPS; - - if (((breathing_halt == BREATHING_HALT_ON) && (index == BREATHING_STEPS / 2)) || - ((breathing_halt == BREATHING_HALT_OFF) && (index == BREATHING_STEPS - 1))) - { - breathing_interrupt_disable(); - } - - uint32_t duty = cie_lightness(scale_backlight(breathing_table[index] * 256)); - - chSysLockFromISR(); - pwmEnableChannelI( - &PWMD3, - 0, - PWM_FRACTION_TO_WIDTH( - &PWMD3, - 0xFFFF, - duty - ) - ); - chSysUnlockFromISR(); -} diff --git a/keyboards/cannonkeys/stm32f072/led_custom.h b/keyboards/cannonkeys/stm32f072/led_custom.h deleted file mode 100644 index 550d5b84cc..0000000000 --- a/keyboards/cannonkeys/stm32f072/led_custom.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -void backlight_task(void); -void breathing_interrupt_disable(void); -void breathing_interrupt_enable(void); -void breathing_toggle(void); -void breathing_enable(void); -void breathing_disable(void); diff --git a/keyboards/cannonkeys/stm32f072/ws2812.c b/keyboards/cannonkeys/stm32f072/ws2812.c deleted file mode 100644 index b2f2384f2c..0000000000 --- a/keyboards/cannonkeys/stm32f072/ws2812.c +++ /dev/null @@ -1,151 +0,0 @@ -#include "ch.h" -#include "hal.h" - -#include "ws2812.h" - -#define BYTES_FOR_LED_BYTE 4 -#define NB_COLORS 3 -#define BYTES_FOR_LED BYTES_FOR_LED_BYTE*NB_COLORS -#define DATA_SIZE BYTES_FOR_LED*NB_LEDS -#define RESET_SIZE 200 - -// Define the spi your LEDs are plugged to here -#define LEDS_SPI WS2812_SPI -// Define the number of LEDs you wish to control in your LED strip -#define NB_LEDS RGBLED_NUM - -#define LED_SPIRAL 0 - -static uint8_t txbuf[DATA_SIZE + RESET_SIZE]; -static uint8_t get_protocol_eq(uint8_t data, int pos); - -/* - * This lib is meant to be used asynchronously, thus the colors contained in - * the txbuf will be sent in loop, so that the colors are always the ones you - * put in the table (the user thus have less to worry about) - * - * Since the data are sent via DMA, and the call to spiSend is a blocking one, - * the processor ressources are not used to much, if you see your program being - * too slow, simply add a: - * chThdSleepMilliseconds(x); - * after the spiSend, where you increment x untill you are satisfied with your - * program speed, another trick may be to lower this thread priority : your call - */ -static THD_WORKING_AREA(LEDS_THREAD_WA, 128); -static THD_FUNCTION(ledsThread, arg) { - (void) arg; - while(1){ - spiSend(&LEDS_SPI, DATA_SIZE + RESET_SIZE, txbuf); - } -} - -#if LED_SPIRAL -/* - * 'Led spiral' is a simple demo in which we put all the leds to the same - * color, where this color does all the hsv circle in loop. - * If you want to launch the thread that will chage the led colors to the - * appropriate value, simply set LED_SPIRAL to 1. - */ - -static THD_WORKING_AREA(HSVTRANS_WA, 128); -static THD_FUNCTION(hsv_transThread, arg) { - (void) arg; - hsv_color color = {0, 255, 255}; - while(1){ - color.h += 1; - color.h %= 256; - set_leds_color_hsv(color); - chThdSleepMilliseconds(50); - } -} -#endif - -static const SPIConfig spicfg = { - false, - NULL, - GPIOB, - 15, - SPI_CR1_BR_1|SPI_CR1_BR_0 // baudrate : fpclk / 8 => 1tick is 0.32us -}; - -/* - * Function used to initialize the driver. - * - * Starts by shutting off all the LEDs. - * Then gets access on the LED_SPI driver. - * May eventually launch an animation on the LEDs (e.g. a thread setting the - * txbuff values) - */ -void leds_init(void){ - for(int i = 0; i < RESET_SIZE; i++) - txbuf[DATA_SIZE+i] = 0x00; - spiAcquireBus(&LEDS_SPI); /* Acquire ownership of the bus. */ - spiStart(&LEDS_SPI, &spicfg); /* Setup transfer parameters. */ - spiSelect(&LEDS_SPI); /* Slave Select assertion. */ - chThdCreateStatic(LEDS_THREAD_WA, sizeof(LEDS_THREAD_WA),NORMALPRIO, ledsThread, NULL); -#if LED_SPIRAL - chThdCreateStatic(HSVTRANS_WA, sizeof(HSVTRANS_WA), - NORMALPRIO, hsv_transThread, NULL); -#endif -} - -/* - * As the trick here is to use the SPI to send a huge pattern of 0 and 1 to - * the ws2812b protocol, we use this helper function to translate bytes into - * 0s and 1s for the LED (with the appropriate timing). - */ -static uint8_t get_protocol_eq(uint8_t data, int pos){ - uint8_t eq = 0; - if (data & (1 << (2*(3-pos)))) - eq = 0b1110; - else - eq = 0b1000; - if (data & (2 << (2*(3-pos)))) - eq += 0b11100000; - else - eq += 0b10000000; - return eq; -} - -// -///* -// * If you want to set a LED's color in the RGB color space, simply call this -// * function with a hsv_color containing the desired color and the index of the -// * led on the LED strip (starting from 0, the first one being the closest the -// * first plugged to the board) -// * -// * Only set the color of the LEDs through the functions given by this API -// * (unless you really know what you are doing) -// */ -void set_led_color_rgb(LED_TYPE color, int pos){ - for(int j = 0; j < 4; j++) - txbuf[BYTES_FOR_LED*pos + j] = get_protocol_eq(color.g, j); - for(int j = 0; j < 4; j++) - txbuf[BYTES_FOR_LED*pos + BYTES_FOR_LED_BYTE+j] = get_protocol_eq(color.r, j); - for(int j = 0; j < 4; j++) - txbuf[BYTES_FOR_LED*pos + BYTES_FOR_LED_BYTE*2+j] = get_protocol_eq(color.b, j); -} - - -void WS2812_init(void) { - leds_init(); -} - -void ws2812_setleds(LED_TYPE *ledarray, uint16_t number_of_leds) { - uint8_t i = 0; - while (i < number_of_leds) { - set_led_color_rgb(ledarray[i], i); - i++; - } -} - - -void set_leds_color_rgb(LED_TYPE color){ - for(int i = 0; i < NB_LEDS; i++) - set_led_color_rgb(color, i); -} - - -void ws2812_setleds_rgbw(LED_TYPE *ledarray, uint16_t number_of_leds) { - -} -- cgit v1.2.3