/***********************************************************************
 *
 * Copyrigth: Karl Hammar, Aspö Data, LGPL
 *
 */

#include <stdint.h>

#include <libopencm3/stm32/adc.h>
#include <libopencm3/stm32/dma.h>
#include <libopencm3/stm32/rcc.h>

#include <libarm.h>
#include "commit.h"

/* defines */

#define HSE_FREQ  12*MHz
#define HSI_FREQ   8*MHz
#define APB1_FREQ HSE_FREQ
#define APB2_FREQ HSE_FREQ
#define BAUDRATE 1200

#define GPIOPORT_SZ 2 // PAxx, PBxx
#define MSMOUSE_MSG_SZ 3
#define BYTESZ TTY_B7
#define ADC_MID (1024*2)
#define NUARTS 3
#define BUFSZ 100
#define LEFT_BUTTON   GPIO_IL(1,BV( 5))
#define RIGHT_BUTTON  GPIO_IL(1,BV( 6))
#define CENTER_DETECT GPIO_IL(1,BV(12))
#define SELECT_USART3 GPIO_IL(1,BV(13))
#define SELECT_USART1 GPIO_IL(1,BV(14))
#define PULLUP        (BV(5)|BV(6)|BV(12)|BV(13)|BV(14))

/* data types */

struct button_state {
  int lb;
  int rb;
  int cd;
  int uart;
};

/* global variables */
const uint8_t gpioport_sz = GPIOPORT_SZ;
struct gpioport_t gpioport[GPIOPORT_SZ];

/* uart */
uint8_t buffer_area[NUARTS][2][BUFSZ];
struct buf_t bufr[NUARTS];
struct buf_t bufw[NUARTS];
uint32_t usart_addr[NUARTS] = { USART1, USART2, USART3 };

/* adc */
uint8_t adc_ch[]   = { 8, 7 }; // adc hw channel, X and Y
uint16_t adc[sizeof(adc_ch)];
int adc_state = -1;
const uint16_t adc_max = 4095;

/* timer */
const struct timespec timespec_upd = { 0, 31250 * 1000L };
const struct timespec timespec_1200 = { 0, 833333L };

void init(void);
void adc_start_single(uint8_t ch);
int  adc_run(void);
void button_sta(struct button_state *bt_old, struct button_state *bt_nxt);
void button_end(struct button_state *bt_old, struct button_state *bt_nxt);
void mouse_MS(uint8_t msg[MSMOUSE_MSG_SZ], uint8_t lb, uint8_t rb, int8_t X, int8_t Y);
int  mouse_send(uint8_t uart, uint8_t lb, uint8_t rb, int8_t X, int8_t Y);
void uart_run(void);

/* functions */

void init(void) {
  int ix;
  /* init clocks */
  clk_init_f1v_xtl();
  time_init(16, 3, 250);

  /* init io */
  RCC_APB2ENR |=
    RCC_APB2ENR_USART1EN   | RCC_APB2ENR_ADC1EN   | RCC_APB2ENR_IOPCEN |
    RCC_APB2ENR_IOPBEN     | RCC_APB2ENR_IOPAEN   | RCC_APB2ENR_AFIOEN ;
  RCC_APB2RSTR |=
    RCC_APB2RSTR_USART1RST | RCC_APB2RSTR_ADC1RST | RCC_APB2RSTR_IOPCRST |
    RCC_APB2RSTR_IOPBRST   | RCC_APB2RSTR_IOPARST | RCC_APB2RSTR_AFIORST ;
  RCC_APB2RSTR  = 0;

  RCC_APB1ENR  |= RCC_APB1ENR_USART3EN   | RCC_APB1ENR_USART2EN;
  RCC_APB1RSTR |= RCC_APB1RSTR_USART3RST | RCC_APB1RSTR_USART2RST;
  RCC_APB1RSTR  = 0;

  ADC1_CR2 &= ~ADC_CR2_ADON;

  GPIO_CRL(GPIOA) =
    GPIOCNF( CNF_INPUT,  0) | // PA0  -
    GPIOCNF( CNF_INPUT,  1) | // PA1  -
    GPIOCNF( CNF_APP10,  2) | // PA2  usart2tx
    GPIOCNF( CNF_INFLT,  3) | // PA3  usart2rx
    GPIOCNF( CNF_INPUT,  4) | // PA4  -
    GPIOCNF( CNF_INPUT,  5) | // PA5  -
    GPIOCNF( CNF_INANA,  6) | // PA6  X2 Center_Tap_Ref
    GPIOCNF( CNF_INANA,  7) | // PA7  X2 Y_Axis_Output
    0;
  GPIO_CRH(GPIOA) =
    //GPIOCNF( CNF_INPUT,  8) | // PA8  MCO
    GPIOCNF( CNF_APP50,  8) | // PA8  MCO
    GPIOCNF( CNF_APP10,  9) | // PA9  usart1tx
    GPIOCNF( CNF_INFLT, 10) | // PA10 usart1rx
    GPIOCNF( CNF_INPUT, 11) | // PA11 -
    GPIOCNF( CNF_INPUT, 12) | // PA12 -
    GPIOCNF( CNF_INPUT, 13) | // PA13 jTMS
    GPIOCNF( CNF_INPUT, 14) | // PA14 jTCK
    GPIOCNF( CNF_INPUT, 15) | // PA15 jTDI
    0;

  GPIO_CRL(GPIOB) =
    GPIOCNF( CNF_INANA,  0) | // PB0  X2 X_Axis_Output
    GPIOCNF( CNF_INPUT,  1) | // PB1  -
    GPIOCNF( CNF_INPUT,  2) | // PB2  -
    GPIOCNF( CNF_OPP10,  3) | // PB3  jTDO
    GPIOCNF( CNF_INPUT,  4) | // PB4  njTRST
    GPIOCNF( CNF_INPUT,  5) | // PB5  X3 Left button
    GPIOCNF( CNF_INPUT,  6) | // PB6  X4 Rigth button
    GPIOCNF( CNF_INPUT,  7) | // PB7  -
    0;
  GPIO_CRH(GPIOB) =
    GPIOCNF( CNF_INPUT,  8) | // PB8  -
    GPIOCNF( CNF_INPUT,  9) | // PB9  -
    GPIOCNF( CNF_APP10, 10) | // PB10 usart3tx
    GPIOCNF( CNF_INFLT, 11) | // PB11 usart3rx
    GPIOCNF( CNF_INPUT, 12) | // PB12 X2 Center_Detect
    GPIOCNF( CNF_INPUT, 13) | // PB13 X5 23 Select X8 USART3
    GPIOCNF( CNF_INPUT, 14) | // PB14 X5 13 Select X6 USART1
    GPIOCNF( CNF_INPUT, 15) | // PB15 -
    0;

  GPIO_CRL(GPIOC) =
    GPIOCNF( CNF_INPUT,  0) | // PC0  NA
    GPIOCNF( CNF_INPUT,  1) | // PC1  NA
    GPIOCNF( CNF_INPUT,  2) | // PC2  NA
    GPIOCNF( CNF_INPUT,  3) | // PC3  NA
    GPIOCNF( CNF_INPUT,  4) | // PC4  NA
    GPIOCNF( CNF_INPUT,  5) | // PC5  NA
    GPIOCNF( CNF_INPUT,  6) | // PC6  NA
    GPIOCNF( CNF_INPUT,  7) | // PC7  NA
    0;
  GPIO_CRH(GPIOC) =
    GPIOCNF( CNF_INPUT,  8) | // PC8  NA
    GPIOCNF( CNF_INPUT,  9) | // PC9  NA
    GPIOCNF( CNF_INPUT, 10) | // PC10 NA
    GPIOCNF( CNF_INPUT, 11) | // PC11 NA
    GPIOCNF( CNF_INPUT, 12) | // PC12 NA
    GPIOCNF( CNF_INPUT, 13) | // PC13 -
    GPIOCNF( CNF_INPUT, 14) | // PC14 -
    GPIOCNF( CNF_INPUT, 15) | // PC15 -
    0;

  memset(gpioport, 0, sizeof(gpioport));
  gpioport[0].base   = GPIO_PORT_A_BASE;
  gpioport[1].base   = GPIO_PORT_B_BASE;
  gpioport[0].pullup = 0xffff;
  gpioport[1].pullup = PULLUP;
  for (ix = 0; ix < GPIOPORT_SZ; ix++) {
    gpioport[ix].write  = gpioport[ix].pullup;
    GPIO_ODR(gpioport[ix].base) = gpioport[ix].pullup;
  }

  USART_BRR(USART1)  = (APB2_FREQ + BAUDRATE/2) / BAUDRATE;
  USART_GTPR(USART1) = 0;
  USART_CR3(USART1)  = 0; // no flow control
  USART_CR2(USART1)  = 0; // 1 stop bit, async
  USART_CR1(USART1)  = USART_CR1_RE | USART_CR1_TE | USART_CR1_UE; // no parity, 8bit bytes

  USART_BRR(USART2)  = (APB1_FREQ + BAUDRATE/2) / BAUDRATE;
  USART_GTPR(USART2) = 0;
  USART_CR3(USART2)  = 0; // no flow control
  USART_CR2(USART2)  = 0; // 1 stop bit, async
  USART_CR1(USART2)  = USART_CR1_RE | USART_CR1_TE | USART_CR1_UE;

  USART_BRR(USART3)  = (APB1_FREQ + BAUDRATE/2) /  BAUDRATE;
  USART_GTPR(USART3) = 0;
  USART_CR3(USART3)  = 0; // no flow control
  USART_CR2(USART3)  = 0; // 1 stop bit, async
  USART_CR1(USART3)  = USART_CR1_RE | USART_CR1_TE | USART_CR1_UE;

  RCC_CFGR = (RCC_CFGR & ~RCC_CFGR_ADCPRE) | ( 0 << RCC_CFGR_ADCPRE_SHIFT );
  adc_set_sample_time_on_all_channels(ADC1, ADC_SMPR_SMP_239DOT5CYC);
  ADC1_CR2 |= ADC_CR2_ADON;

  /*
    wait 2 > cycles for adc calibration
    wait for gpio pullups to charge up their connections, 100ms seems to suffice
  */
  time_set(0,0);
  while (timespec_current.tv_nsec <= 100*1000*1000) { time_get(); }

  adc_reset_calibration(ADC1);
  adc_calibrate(ADC1);

  /* init the rest */
  for (ix = 0; ix < NUARTS; ix++) {
    buf_init(&bufr[ix], buffer_area[ix][0], BUFSZ);
    buf_init(&bufw[ix], buffer_area[ix][1], BUFSZ);
  }

  memset(adc, 0, sizeof(adc));

  gpioport[0].read = GPIO_IDR(gpioport[0].base);
  gpioport[1].read = GPIO_IDR(gpioport[1].base);
}

void adc_start_single(uint8_t ch) {
  ADC_SQR3(ADC1) = ch & 0x1f;
  ADC1_CR2 |= ADC_CR2_ADON;
}

int adc_run(void) {
  if (adc_state < 0) {
    // nop
  } else if (adc_state >= (int) sizeof(adc_ch)) {
    adc_state = -1;
  } else {
    if (ADC_SR(ADC1) & ADC_SR_EOC) {
      ADC_SR(ADC1) &= ~ADC_SR_EOC;
      adc[adc_state] = ADC_DR(ADC1);

      adc_state++;
      if (adc_state < (int) sizeof(adc_ch)) {
	adc_start_single(adc_ch[adc_state]);
      }
    }
  }
  return adc_state;
}

void uart_run(void) {
  int ix = 0;

  for (ix = 0; ix < NUARTS; ix++) {
    uint8_t cc;
    cc = tty_fd_run_fixed((void *) usart_addr[ix], BYTESZ, &bufr[ix], &bufw[ix]);
    if (cc) { buf_push(&bufw[ix], 'M'); }
  }
}

void button_sta(struct button_state *bt_old, struct button_state *bt_nxt) {
  (void) bt_old;
  bt_nxt->lb = LEFT_BUTTON;
  bt_nxt->rb = RIGHT_BUTTON;
  bt_nxt->cd = CENTER_DETECT;

  if (SELECT_USART1) { bt_nxt->uart = 0; }
  else if (SELECT_USART3) { bt_nxt->uart = 2; }
  else { bt_nxt->uart = 1; }
}
void button_end(struct button_state *bt_old, struct button_state *bt_nxt) {
  *bt_old = *bt_nxt;
}

/*
  https://www.kryslix.com/nsfaq/Q.12.html

 The Microsoft serial mouse is the most popular 2 button mouse. It is
 supported by all major operating systems. The maximum tracking rate
 for a Microsoft mouse is 40 reports/second * 127 counts per report,
 in other words, 5080 counts per second. The most common range for
 mice is is 100 to 400 CPI (counts per inch) but can be up to 1000
 CPI. A 100CPI mouse can discriminate motion up to 50.8 inches/second
 while a 400 CPI mouse can only discriminate motion up to 12.7
 inches/second.

Pinout

9 pin  25 pin    Line    Comments
shell  1         GND
3      2         TD      Serial data from host to mouse (only for power)
2      3         RD      Serial data from mouse to host
7      4         RTS     Positive voltage to mouse
8      5         CTS
6      6         DSR
5      7         SGND
4      20        DTR     Positive voltage to mouse and reset/detection

RTS = Request to Send   CTS  = Clear to Send
DSR = Data Set Ready    DTR  = Data Terminal Ready
GND = Protective Ground SGND = Signal Ground

To function correctly, both the RTS and DTR lines must be
positive. DTR/DSR and RTS/CTS must NOT be shorted. RTS may be toggled
negative for at least 100ms to reset the mouse. (After a cold boot,
the RTS line is usually negative. This provides an automatic toggle
when RTS is brought positive). When DTR is toggled the mouse should
send a single byte 0x45 (ASCII 'M').

Serial data parameters:
1200bps, 7 databits, 1 stop-bit

Data packet format:
Data is sent in 3 byte packets for each event (a button is pressed or released or the mouse moves):

        D7      D6      D5      D4      D3      D2      D1      D0
 
Byte 1  X       1       LB      RB      Y7      Y6      X7      X6
Byte 2  X       0       X5      X4      X3      X2      X1      X0      
Byte 3  X       0       Y5      Y4      Y3      Y2      Y1      Y0

LB is the state of the left button (1 means down)
RB is the state of the right button (1 means down)
X7-X0 movement in X direction since last packet (signed byte)
Y7-Y0 movement in Y direction since last packet (signed byte)

The high order bit of each byte (D7) is ignored. Bit D6 indicates the
start of an event, which allows the software to synchronize with the
mouse.
*/

void mouse_MS(uint8_t msg[MSMOUSE_MSG_SZ], uint8_t lb, uint8_t rb, int8_t X, int8_t Y) {
  msg[0] = BV(6) | (lb ? 0x20 : 0)  |  (rb ? 0x10 : 0)  | (0x3 & (Y>>6))<<2 | (0x3 & (X>>6));
  msg[1] = 0x3f & X;
  msg[2] = 0x3f & Y;
}
int mouse_send(uint8_t uart, uint8_t lb, uint8_t rb, int8_t X, int8_t Y) {
  uint8_t msg[MSMOUSE_MSG_SZ];
  bufsz len = 0;
  mouse_MS( msg, lb, rb, X, Y);

  len = buf_put(&bufw[uart], msg, MSMOUSE_MSG_SZ);
  return len != MSMOUSE_MSG_SZ;
}

int main(void) {
  struct button_state bt_old;
  struct button_state bt_nxt;
  uint8_t null_msg[MSMOUSE_MSG_SZ];
  struct timespec nxt;

  init();
  button_sta(&bt_old, &bt_nxt);
  button_end(&bt_old, &bt_nxt);

  mouse_MS(null_msg,0,0,0,0);
  nxt = timespec_add(timespec_current, timespec_upd);

  while (1) {
    time_get();
    gpio_run();

    /* handle the result when the adc conversion (started below) is ready */
    if (adc_run() >= (int) sizeof(adc_ch)) {
      // mouse uses 8bits, adc is 12 bits
      int32_t x = adc[0];
      int32_t y = adc[1];
      int flg = 0;

      x -= ADC_MID;
      y  = ADC_MID - y; /* it seems y was upside down */
      x >>= 4;
      y >>= 4;

      x >>= 4;
      y >>= 4;
      button_sta(&bt_old, &bt_nxt);

      if (bt_nxt.cd) x = y = 0;
      else flg = 1;
      if (bt_old.uart != bt_nxt.uart) {
	if ( bt_nxt.lb || bt_nxt.rb ) {
	  mouse_send(bt_old.uart, 0, 0, 0, 0);
	  flg = 1;
	}
      }
      if (bt_old.lb ^ bt_nxt.lb || bt_old.rb ^ bt_nxt.rb) flg = 1;
      if (flg) mouse_send(bt_nxt.uart, bt_nxt.lb, bt_nxt.rb, x, y);

      button_end(&bt_old, &bt_nxt);
    }

    /* start adc conversions at regular interval, this defines max. output rate */
    if (timespec_cmp(nxt, timespec_current) <= 0) {
      nxt = timespec_add(nxt, timespec_upd);
      adc_state = 0;
      adc_start_single(adc_ch[adc_state]);
    }

    uart_run();

  }

  return 0;
}
