/* ----------------------------------------------------------------------------
 * ergoDOX : controller specific exports
 * ----------------------------------------------------------------------------
 * Copyright (c) 2012 Ben Blazak <benblazak.dev@gmail.com>
 * Released under The MIT License (MIT) (see "license.md")
 * Project located at <https://github.com/benblazak/ergodox-firmware>
 * ------------------------------------------------------------------------- */

#pragma once

#define USB_DEBUG_HID

#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <util/twi.h>

// --------------------------------------------------------------------

/*
 * DRIVE_ROWS and DRIVE_COLUMNS
 * - Select which pins will drive (alternate between hi-Z and drive
 *   low) and which will be inputs
 *
 * Notes
 * - You must set exactly one of each 'TEENSY' macro, and of each
 *   'MCP23018' macro
 * - If you are using internal diodes (inside the key switches)... then
 *   i don't know what to tell you.  You will set one chip to drive
 *   rows, and the other to drive columns, but i don't have a key
 *   switch to check which at the moment, and i couldn't seem to find
 *   it online.
 * - If the diode cathode is towards the square solder pad, set
 *   #define  TEENSY__DRIVE_COLUMNS    1
 *   #define  MCP23018__DRIVE_COLUMNS  1
 * - If the diode cathode is towards the circular solder pad, set
 *   #define  TEENSY__DRIVE_ROWS       1
 *   #define  MCP23018__DRIVE_ROWS     1
 */
#define  TEENSY__DRIVE_ROWS     	0
#define  MCP23018__DRIVE_ROWS   	0
#define  TEENSY__DRIVE_COLUMNS  	1
#define  MCP23018__DRIVE_COLUMNS	1

#define KB_ROWS   	6 	// must match real life
#define KB_COLUMNS	14	// must match real life

// --------------------------------------------------------------------

uint8_t kb_init(void);
uint8_t kb_update_matrix(bool matrix[KB_ROWS][KB_COLUMNS]);

// --------------------------------------------------------------------

#define MCP23018_TWI_ADDRESS 0b0100000

uint8_t mcp23018_init(void);
uint8_t mcp23018_update_matrix( bool matrix[KB_ROWS][KB_COLUMNS] );

// --------------------------------------------------------------------

uint8_t teensy_init(void);
uint8_t teensy_update_matrix( bool matrix[KB_ROWS][KB_COLUMNS] );

// --------------------------------------------------------------------

#define TWI_FREQ 400000

void    twi_init  (void);
uint8_t twi_start (void);
void    twi_stop  (void);
uint8_t twi_send  (uint8_t data);
uint8_t twi_read  (uint8_t * data);

// --------------------------------------------------------------------

void usb_init(void);         	// initialize everything
uint8_t usb_configured(void);	// is the USB port configured

int8_t usb_keyboard_press(uint8_t key, uint8_t modifier);
int8_t usb_keyboard_send(void);
extern uint8_t keyboard_modifier_keys;
extern uint8_t keyboard_keys[6];

extern uint16_t consumer_key;

// Everything below this point is only intended for usb_serial.c

#define EP_TYPE_CONTROL        	0x00
#define EP_TYPE_BULK_IN        	0x81
#define EP_TYPE_BULK_OUT       	0x80
#define EP_TYPE_INTERRUPT_IN   	0xC1
#define EP_TYPE_INTERRUPT_OUT  	0xC0
#define EP_TYPE_ISOCHRONOUS_IN 	0x41
#define EP_TYPE_ISOCHRONOUS_OUT	0x40

#define EP_SINGLE_BUFFER	0x02
#define EP_DOUBLE_BUFFER	0x06

#define EP_SIZE(s)	((s) == 64 ? 0x30 : \
                    ((s) == 32 ? 0x20 : \
                    ((s) == 16 ? 0x10 : \
                     0x00)))

#define MAX_ENDPOINT	4

#define LSB(n) (n & 255)
#define MSB(n) ((n >> 8) & 255)

#define HW_CONFIG() 	(UHWCON = 0x01)
#define PLL_CONFIG()	(PLLCSR = 0x12)
#define USB_CONFIG()	(USBCON = ((1<<USBE)|(1<<OTGPADE)))
#define USB_FREEZE()	(USBCON = ((1<<USBE)|(1<<FRZCLK)))

// standard control endpoint request types
#define GET_STATUS       	0
#define CLEAR_FEATURE    	1
#define SET_FEATURE      	3
#define SET_ADDRESS      	5
#define GET_DESCRIPTOR   	6
#define GET_CONFIGURATION	8
#define SET_CONFIGURATION	9
#define GET_INTERFACE    	10
#define SET_INTERFACE    	11
// HID (human interface device)
#define HID_GET_REPORT  	1
#define HID_GET_IDLE    	2
#define HID_GET_PROTOCOL	3
#define HID_SET_REPORT  	9
#define HID_SET_IDLE    	10
#define HID_SET_PROTOCOL	11
// CDC (communication class device)
#define CDC_SET_LINE_CODING       	0x20
#define CDC_GET_LINE_CODING       	0x21
#define CDC_SET_CONTROL_LINE_STATE	0x22

// --------------------------------------------------------------------

/* mapping from spatial position to matrix position
 * - spatial position: where the key is spatially, relative to other
 *   keys both on the keyboard and in the layout
 * - matrix position: the coordinate in the matrix to which a key is
 *   scanned by the update functions
 *
 * - location numbers are in the format `row##column`, where both 'row'
 *   and 'column' are single digit hex numbers corresponding to the
 *   matrix position (which also corresponds to the row and column pin
 *   labels used in the teensy and mcp23018 files)
 *
 * - coordinates
 *   - optional keys
 *     k15, k16 (left hand thumb group)
 *     k17, k18 (right hand thumb group)
 *   - unused keys
 *     k36, k00 (left hand)
 *     k37, k0D (right hand)
 *
 * --- other info -----------------------------------------------------
 *           rows x columns = positions;  used,  unused
 * per hand:    6 x  7      = 42;         40,    2
 *    total:    6 x 14      = 84;         80,    4
 *
 * left hand  : rows 0..5, cols 0..6
 * right hand : rows 0..5, cols 7..D
 * --------------------------------------------------------------------
 */
#define KB_MATRIX_LAYER(          \
    /* for unused positions */      \
    na,            \
                \
    /* left hand, spatial positions */    \
    k50,k51,k52,k53,k54,k55,k56,      \
    k40,k41,k42,k43,k44,k45,k46,      \
    k30,k31,k32,k33,k34,k35,      \
    k20,k21,k22,k23,k24,k25,k26,      \
    k10,k11,k12,k13,k14,        \
                            k05,k06,    \
                        k15,k16,k04,    \
            k03,k02,k01,    \
                \
    /* right hand, spatial positions */    \
        k57,k58,k59,k5A,k5B,k5C,k5D,    \
        k47,k48,k49,k4A,k4B,k4C,k4D,    \
            k38,k39,k3A,k3B,k3C,k3D,    \
        k27,k28,k29,k2A,k2B,k2C,k2D,    \
                k19,k1A,k1B,k1C,k1D,    \
    k07,k08,          \
    k09,k17,k18,          \
    k0C,k0B,k0A )          \
                \
/* matrix positions */            \
{{  na,k01,k02,k03,k04,k05,k06, k07,k08,k09,k0A,k0B,k0C, na },  \
 { k10,k11,k12,k13,k14,k15,k16, k17,k18,k19,k1A,k1B,k1C,k1D },  \
 { k20,k21,k22,k23,k24,k25,k26, k27,k28,k29,k2A,k2B,k2C,k2D },  \
 { k30,k31,k32,k33,k34,k35, na,  na,k38,k39,k3A,k3B,k3C,k3D },  \
 { k40,k41,k42,k43,k44,k45,k46, k47,k48,k49,k4A,k4B,k4C,k4D },  \
 { k50,k51,k52,k53,k54,k55,k56, k57,k58,k59,k5A,k5B,k5C,k5D }}

// -----------------------------------------------------------------
// debug
// -----------------------------------------------------------------

extern volatile uint8_t debug_flush_timer;

void usb_debug_free_memory(void);
void usb_debug_print(const char *s);
void usb_debug_printf(const char *fmt, ...);
int8_t usb_debug_putchar(char c);
void usb_debug_flush_output(void);

#ifdef KBD_DEBUG
#define debug_print(s)          	usb_debug_print(PSTR(s))
#define debug_printf(s, args...)	usb_debug_printf(PSTR(s), args)
#else
#define debug_print(s)
#define debug_printf(...)
#endif
