/* * OpenPSU firmware * External UART control interface * * Copyright (C) 2007 Michael Buesch * * 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. */ #include "ext_control.h" #include "calibration.h" #include "util.h" #include "main.h" #include #include #define BAUDRATE 2400 /* The receive buffer. */ static uint8_t rx_buffer[sizeof(struct extctl_command)]; /* The number of bytes received. */ static uint8_t nr_rx_bytes; /* Receive error flag */ static uint8_t rx_error; static void usart_tx(uint8_t data) { while (!(UCSR0A & (1 << UDRE0))) ; UDR0 = data; } static void usart_tx_buffer(void *_buf, uint8_t size) { uint8_t *buf = _buf; uint8_t i; for (i = 0; i < size; i++) usart_tx(buf[i]); } static int8_t usart_rx(uint8_t *data) { uint8_t status; status = UCSR0A; if (!(status & (1 << RXC0))) return 1; *data = UDR0; if (unlikely(status & ((1 << FE0) | (1 << UPE0) | (1 << DOR0)))) return -1; return 0; } static uint8_t crc8(uint8_t crc, uint8_t data) { uint8_t i, tmp; for (i = 8; i > 0; i--) { tmp = ((crc ^ data) & 0x01); if (tmp) { crc ^= 0x18; crc >>= 1; crc |= 0x80; } else crc >>= 1; data >>= 1; } return crc; } /* Create an XOR checksum out of the buffer. */ static uint8_t checksum_buffer(const void *_buf, uint8_t size) { uint8_t crc = 0; const uint8_t *buf = _buf; uint8_t i; for (i = 0; i < size; i++) crc = crc8(crc, buf[i]); crc ^= 0xFF; return crc; } static inline void command_result(uint8_t result_code) { usart_tx(result_code); } static void send_command_reply(uint32_t data) { struct extctl_reply reply; reply.data = cpu_to_le32(data); reply.checksum = checksum_buffer(&reply.data, sizeof(reply.data)); usart_tx_buffer(&reply, sizeof(reply)); } static void cmd_nop(uint32_t data) { command_result(EXTCTL_CMD_RESULT_OK); } static void cmd_setvoltage(uint32_t data) { uint16_t voltage; uint8_t profile; profile = (data & 0x00FF0000) >> 16; voltage = (data & 0x0000FFFF); if ((profile >= NR_PROFILES) || (voltage > MAX_VOLTAGE)) { command_result(EXTCTL_CMD_RESULT_EINVAL); return; } set_voltage_in_prof(profile, voltage); command_result(EXTCTL_CMD_RESULT_OK); } static void cmd_getvoltage(uint32_t data) { uint8_t profile; uint16_t voltage; profile = (data & 0x00FF0000) >> 16; if (profile >= NR_PROFILES) { command_result(EXTCTL_CMD_RESULT_EINVAL); return; } command_result(EXTCTL_CMD_RESULT_OK); voltage = get_voltage_from_prof(profile); send_command_reply(voltage); } static void cmd_setmaxcur(uint32_t data) { uint16_t maxcur; uint8_t profile; profile = (data & 0x00FF0000) >> 16; maxcur = (data & 0x0000FFFF); if ((profile >= NR_PROFILES) || (maxcur > MAX_CURRENT)) { command_result(EXTCTL_CMD_RESULT_EINVAL); return; } set_maxcur_in_prof(profile, maxcur); command_result(EXTCTL_CMD_RESULT_OK); } static void cmd_getmaxcur(uint32_t data) { uint8_t profile; uint16_t maxcur; profile = (data & 0x00FF0000) >> 16; if (profile >= NR_PROFILES) { command_result(EXTCTL_CMD_RESULT_EINVAL); return; } command_result(EXTCTL_CMD_RESULT_OK); maxcur = get_maxcur_from_prof(profile); send_command_reply(maxcur); } static void cmd_switchprof(uint32_t data) { uint8_t profile; profile = (data & 0x00FF0000) >> 16; if (profile >= NR_PROFILES) { command_result(EXTCTL_CMD_RESULT_EINVAL); return; } switch_to_profile(profile); command_result(EXTCTL_CMD_RESULT_OK); } static void cmd_getprof(uint32_t data) { uint8_t profile; command_result(EXTCTL_CMD_RESULT_OK); profile = get_active_profile(); send_command_reply((uint32_t)profile << 16); } /* We received a complete command. Handle it. */ static void handle_received_command(void) { struct extctl_command *cmd = (struct extctl_command *)rx_buffer; uint32_t data; uint8_t checksum; checksum = checksum_buffer(rx_buffer, sizeof(struct extctl_command) - sizeof(uint8_t)); if (unlikely(rx_error || cmd->checksum != checksum)) { /* Checksum error. */ command_result(EXTCTL_CMD_RESULT_ECSUM); rx_error = 0; return; } data = le32_to_cpu(cmd->data); switch (cmd->id) { case EXTCTL_CMD_NOP: cmd_nop(data); break; case EXTCTL_CMD_SETVOLTAGE: cmd_setvoltage(data); break; case EXTCTL_CMD_GETVOLTAGE: cmd_getvoltage(data); break; case EXTCTL_CMD_SETMAXCUR: cmd_setmaxcur(data); break; case EXTCTL_CMD_GETMAXCUR: cmd_getmaxcur(data); break; case EXTCTL_CMD_SWITCHPROF: cmd_switchprof(data); break; case EXTCTL_CMD_GETPROF: cmd_getprof(data); break; default: command_result(EXTCTL_CMD_RESULT_EINVAL); return; } } ISR(USART_RX_vect) { uint8_t data; int8_t err; while (1) { err = usart_rx(&data); if (err > 0) break; if (err) rx_error = 1; rx_buffer[nr_rx_bytes++] = data; if (nr_rx_bytes == sizeof(struct extctl_command)) { nr_rx_bytes = 0; handle_received_command(); } } } static void usart_init(void) { uint8_t dummy; /* Set baud rate */ UBRR0 = (F_CPU / 16 / BAUDRATE); /* 8 Data bits, 1 Stop bit, Even parity */ UCSR0C = (1 << UCSZ00) | (1 << UCSZ01) | (1 << UPM01); /* Enable transceiver and RX IRQs */ UCSR0B = (1 << RXEN0) | (1 << TXEN0) | (1 << RXCIE0); /* Drain the RX buffer */ while (usart_rx(&dummy) == 0) mb(); } void extctl_init(void) { nr_rx_bytes = 0; rx_error = 0; mb(); usart_init(); }