Allow network requests to span multiple TCP pkts
Incoming network requests can now span multiple TCP messages. Max upload buffer increased to 2048. Also return layerHeight and generatedBy values when asking for fileinfo on a gcode file. This is an interim commit that needs cleaning up before release.
This commit is contained in:
parent
8464b5bfac
commit
9366ef8e84
7 changed files with 1010 additions and 81 deletions
808
ArduinoCorePatches/sam/system/libsam/source/emac.c
Normal file
808
ArduinoCorePatches/sam/system/libsam/source/emac.c
Normal file
|
@ -0,0 +1,808 @@
|
|||
/**
|
||||
* \file
|
||||
*
|
||||
* \brief EMAC (Ethernet MAC) driver for SAM.
|
||||
*
|
||||
* Copyright (c) 2011-2012 Atmel Corporation. All rights reserved.
|
||||
*
|
||||
* \asf_license_start
|
||||
*
|
||||
* \page License
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of Atmel may not be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 4. This software may only be redistributed and used in connection with an
|
||||
* Atmel microcontroller product.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
|
||||
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* \asf_license_stop
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../chip.h"
|
||||
#include <string.h>
|
||||
|
||||
/// @cond 0
|
||||
/**INDENT-OFF**/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/**INDENT-ON**/
|
||||
/// @endcond
|
||||
|
||||
#if SAM3XA_SERIES
|
||||
|
||||
/**
|
||||
* \defgroup emac_group Ethernet Media Access Controller
|
||||
*
|
||||
* See \ref emac_quickstart.
|
||||
*
|
||||
* Driver for the EMAC (Ethernet Media Access Controller).
|
||||
* This file contains basic functions for the EMAC, with support for all modes, settings
|
||||
* and clock speeds.
|
||||
*
|
||||
* \section dependencies Dependencies
|
||||
* This driver does not depend on other modules.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
#if 1 // dc42 change for Duet
|
||||
#define EMAC_RX_BUFFERS (32)
|
||||
#define EMAC_TX_BUFFERS (8)
|
||||
#else
|
||||
#define EMAC_RX_BUFFERS 16
|
||||
#define EMAC_TX_BUFFERS 8
|
||||
#endif
|
||||
|
||||
#define MAC_PHY_RETRY_MAX 1000000
|
||||
|
||||
|
||||
/** TX descriptor lists */
|
||||
#ifdef __ICCARM__ /* IAR */
|
||||
#pragma data_alignment=8
|
||||
#endif
|
||||
static emac_tx_descriptor_t gs_tx_desc[EMAC_TX_BUFFERS];
|
||||
/** TX callback lists */
|
||||
static emac_dev_tx_cb_t gs_tx_callback[EMAC_TX_BUFFERS];
|
||||
/** RX descriptors lists */
|
||||
#ifdef __ICCARM__ /* IAR */
|
||||
#pragma data_alignment=8
|
||||
#endif
|
||||
static emac_rx_descriptor_t gs_rx_desc[EMAC_RX_BUFFERS];
|
||||
/** Send Buffer. Section 3.6 of AMBA 2.0 spec states that burst should not cross the
|
||||
* 1K Boundaries. Receive buffer manager write operations are burst of 2 words => 3 lsb bits
|
||||
* of the address shall be set to 0.
|
||||
*/
|
||||
#ifdef __ICCARM__ /* IAR */
|
||||
#pragma data_alignment=8
|
||||
#endif
|
||||
static uint8_t gs_uc_tx_buffer[EMAC_TX_BUFFERS * EMAC_TX_UNITSIZE]
|
||||
__attribute__ ((aligned(8)));
|
||||
|
||||
#ifdef __ICCARM__ /* IAR */
|
||||
#pragma data_alignment=8
|
||||
#endif
|
||||
/** Receive Buffer */
|
||||
static uint8_t gs_uc_rx_buffer[EMAC_RX_BUFFERS * EMAC_RX_UNITSIZE]
|
||||
__attribute__ ((aligned(8)));
|
||||
|
||||
/**
|
||||
* EMAC device memory management struct.
|
||||
*/
|
||||
typedef struct emac_dev_mem {
|
||||
/* Pointer to allocated buffer for RX. The address should be 8-byte aligned
|
||||
and the size should be EMAC_RX_UNITSIZE * wRxSize. */
|
||||
uint8_t *p_rx_buffer;
|
||||
/* Pointer to allocated RX descriptor list. */
|
||||
emac_rx_descriptor_t *p_rx_dscr;
|
||||
/* RX size, in number of registered units (RX descriptors). */
|
||||
uint16_t us_rx_size;
|
||||
/* Pointer to allocated buffer for TX. The address should be 8-byte aligned
|
||||
and the size should be EMAC_TX_UNITSIZE * wTxSize. */
|
||||
uint8_t *p_tx_buffer;
|
||||
/* Pointer to allocated TX descriptor list. */
|
||||
emac_tx_descriptor_t *p_tx_dscr;
|
||||
/* TX size, in number of registered units (TX descriptors). */
|
||||
uint16_t us_tx_size;
|
||||
} emac_dev_mem_t;
|
||||
|
||||
/** Return count in buffer */
|
||||
#define CIRC_CNT(head,tail,size) (((head) - (tail)) % (size))
|
||||
|
||||
/*
|
||||
* Return space available, from 0 to size-1.
|
||||
* Always leave one free char as a completely full buffer that has (head == tail),
|
||||
* which is the same as empty.
|
||||
*/
|
||||
#define CIRC_SPACE(head,tail,size) CIRC_CNT((tail),((head)+1),(size))
|
||||
|
||||
/** Circular buffer is empty ? */
|
||||
#define CIRC_EMPTY(head, tail) (head == tail)
|
||||
/** Clear circular buffer */
|
||||
#define CIRC_CLEAR(head, tail) (head = tail = 0)
|
||||
|
||||
/** Increment head or tail */
|
||||
static void circ_inc(uint16_t *headortail, uint32_t size)
|
||||
{
|
||||
(*headortail)++;
|
||||
if((*headortail) >= size) {
|
||||
(*headortail) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Wait PHY operation to be completed.
|
||||
*
|
||||
* \param p_emac HW controller address.
|
||||
* \param ul_retry The retry times, 0 to wait forever until completeness.
|
||||
*
|
||||
* Return EMAC_OK if the operation is completed successfully.
|
||||
*/
|
||||
static uint8_t emac_wait_phy(Emac* p_emac, const uint32_t ul_retry)
|
||||
{
|
||||
volatile uint32_t ul_retry_count = 0;
|
||||
|
||||
while (!emac_is_phy_idle(p_emac)) {
|
||||
if (ul_retry == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ul_retry_count++;
|
||||
|
||||
if (ul_retry_count >= ul_retry) {
|
||||
return EMAC_TIMEOUT;
|
||||
}
|
||||
}
|
||||
return EMAC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable transfer, reset registers and descriptor lists.
|
||||
*
|
||||
* \param p_dev Pointer to EMAC driver instance.
|
||||
*
|
||||
*/
|
||||
static void emac_reset_tx_mem(emac_device_t* p_dev)
|
||||
{
|
||||
Emac *p_hw = p_dev->p_hw;
|
||||
uint8_t *p_tx_buff = p_dev->p_tx_buffer;
|
||||
emac_tx_descriptor_t *p_td = p_dev->p_tx_dscr;
|
||||
|
||||
uint32_t ul_index;
|
||||
uint32_t ul_address;
|
||||
|
||||
/* Disable TX */
|
||||
emac_enable_transmit(p_hw, 0);
|
||||
|
||||
/* Set up the TX descriptors */
|
||||
CIRC_CLEAR(p_dev->us_tx_head, p_dev->us_tx_tail);
|
||||
for (ul_index = 0; ul_index < p_dev->us_tx_list_size; ul_index++) {
|
||||
ul_address = (uint32_t) (&(p_tx_buff[ul_index * EMAC_TX_UNITSIZE]));
|
||||
p_td[ul_index].addr = ul_address;
|
||||
p_td[ul_index].status.val = EMAC_TXD_USED;
|
||||
}
|
||||
p_td[p_dev->us_tx_list_size - 1].status.val =
|
||||
EMAC_TXD_USED | EMAC_TXD_WRAP;
|
||||
|
||||
/* Set transmit buffer queue */
|
||||
emac_set_tx_queue(p_hw, (uint32_t) p_td);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable receiver, reset registers and descriptor list.
|
||||
*
|
||||
* \param p_drv Pointer to EMAC Driver instance.
|
||||
*/
|
||||
static void emac_reset_rx_mem(emac_device_t* p_dev)
|
||||
{
|
||||
Emac *p_hw = p_dev->p_hw;
|
||||
uint8_t *p_rx_buff = p_dev->p_rx_buffer;
|
||||
emac_rx_descriptor_t *pRd = p_dev->p_rx_dscr;
|
||||
|
||||
uint32_t ul_index;
|
||||
uint32_t ul_address;
|
||||
|
||||
/* Disable RX */
|
||||
emac_enable_receive(p_hw, 0);
|
||||
|
||||
/* Set up the RX descriptors */
|
||||
p_dev->us_rx_idx = 0;
|
||||
for (ul_index = 0; ul_index < p_dev->us_rx_list_size; ul_index++) {
|
||||
ul_address = (uint32_t) (&(p_rx_buff[ul_index * EMAC_RX_UNITSIZE]));
|
||||
pRd[ul_index].addr.val = ul_address & EMAC_RXD_ADDR_MASK;
|
||||
pRd[ul_index].status.val = 0;
|
||||
}
|
||||
pRd[p_dev->us_rx_list_size - 1].addr.val |= EMAC_RXD_WRAP;
|
||||
|
||||
/* Set receive buffer queue */
|
||||
emac_set_rx_queue(p_hw, (uint32_t) pRd);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Initialize the allocated buffer lists for EMAC driver to transfer data.
|
||||
* Must be invoked after emac_dev_init() but before RX/TX starts.
|
||||
*
|
||||
* \note If input address is not 8-byte aligned, the address is automatically
|
||||
* adjusted and the list size is reduced by one.
|
||||
*
|
||||
* \param p_emac Pointer to EMAC instance.
|
||||
* \param p_emac_dev Pointer to EMAC device instance.
|
||||
* \param p_dev_mm Pointer to the EMAC memory management control block.
|
||||
* \param p_tx_cb Pointer to allocated TX callback list.
|
||||
*
|
||||
* \return EMAC_OK or EMAC_PARAM.
|
||||
*/
|
||||
static uint8_t emac_init_mem(Emac* p_emac, emac_device_t* p_emac_dev,
|
||||
emac_dev_mem_t* p_dev_mm,
|
||||
emac_dev_tx_cb_t* p_tx_cb)
|
||||
{
|
||||
if (p_dev_mm->us_rx_size <= 1 || p_dev_mm->us_tx_size <= 1 || p_tx_cb == NULL) {
|
||||
return EMAC_PARAM;
|
||||
}
|
||||
|
||||
/* Assign RX buffers */
|
||||
if (((uint32_t) p_dev_mm->p_rx_buffer & 0x7)
|
||||
|| ((uint32_t) p_dev_mm->p_rx_dscr & 0x7)) {
|
||||
p_dev_mm->us_rx_size--;
|
||||
}
|
||||
p_emac_dev->p_rx_buffer =
|
||||
(uint8_t *) ((uint32_t) p_dev_mm->p_rx_buffer & 0xFFFFFFF8);
|
||||
p_emac_dev->p_rx_dscr =
|
||||
(emac_rx_descriptor_t *) ((uint32_t) p_dev_mm->p_rx_dscr
|
||||
& 0xFFFFFFF8);
|
||||
p_emac_dev->us_rx_list_size = p_dev_mm->us_rx_size;
|
||||
|
||||
/* Assign TX buffers */
|
||||
if (((uint32_t) p_dev_mm->p_tx_buffer & 0x7)
|
||||
|| ((uint32_t) p_dev_mm->p_tx_dscr & 0x7)) {
|
||||
p_dev_mm->us_tx_size--;
|
||||
}
|
||||
p_emac_dev->p_tx_buffer =
|
||||
(uint8_t *) ((uint32_t) p_dev_mm->p_tx_buffer & 0xFFFFFFF8);
|
||||
p_emac_dev->p_tx_dscr =
|
||||
(emac_tx_descriptor_t *) ((uint32_t) p_dev_mm->p_tx_dscr
|
||||
& 0xFFFFFFF8);
|
||||
p_emac_dev->us_tx_list_size = p_dev_mm->us_tx_size;
|
||||
p_emac_dev->func_tx_cb_list = p_tx_cb;
|
||||
|
||||
/* Reset TX & RX */
|
||||
emac_reset_rx_mem(p_emac_dev);
|
||||
emac_reset_tx_mem(p_emac_dev);
|
||||
|
||||
/* Enable Rx and Tx, plus the statistics register */
|
||||
emac_enable_transmit(p_emac, 1);
|
||||
emac_enable_receive(p_emac, 1);
|
||||
emac_enable_statistics_write(p_emac, 1);
|
||||
|
||||
/* Set up the interrupts for transmission and errors */
|
||||
emac_enable_interrupt(p_emac,
|
||||
EMAC_IER_RXUBR | /* Enable receive used bit read interrupt. */
|
||||
EMAC_IER_TUND | /* Enable transmit underrun interrupt. */
|
||||
EMAC_IER_RLE | /* Enable retry limit exceeded interrupt. */
|
||||
EMAC_IER_TXERR | /* Enable transmit buffers exhausted in mid-frame interrupt. */
|
||||
EMAC_IER_TCOMP | /* Enable transmit complete interrupt. */
|
||||
EMAC_IER_ROVR | /* Enable receive overrun interrupt. */
|
||||
EMAC_IER_HRESP | /* Enable Hresp not OK interrupt. */
|
||||
EMAC_IER_PFR | /* Enable pause frame received interrupt. */
|
||||
EMAC_IER_PTZ); /* Enable pause time zero interrupt. */
|
||||
|
||||
return EMAC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Read the PHY register.
|
||||
*
|
||||
* \param p_emac Pointer to the EMAC instance.
|
||||
* \param uc_phy_address PHY address.
|
||||
* \param uc_address Register address.
|
||||
* \param p_value Pointer to a 32-bit location to store read data.
|
||||
*
|
||||
* \Return EMAC_OK if successfully, EMAC_TIMEOUT if timeout.
|
||||
*/
|
||||
uint8_t emac_phy_read(Emac* p_emac, uint8_t uc_phy_address, uint8_t uc_address,
|
||||
uint32_t* p_value)
|
||||
{
|
||||
emac_maintain_phy(p_emac, uc_phy_address, uc_address, 1, 0);
|
||||
|
||||
if (emac_wait_phy(p_emac, MAC_PHY_RETRY_MAX) == EMAC_TIMEOUT) {
|
||||
return EMAC_TIMEOUT;
|
||||
}
|
||||
*p_value = emac_get_phy_data(p_emac);
|
||||
return EMAC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Write the PHY register.
|
||||
*
|
||||
* \param p_emac Pointer to the EMAC instance.
|
||||
* \param uc_phy_address PHY Address.
|
||||
* \param uc_address Register Address.
|
||||
* \param ul_value Data to write, actually 16-bit data.
|
||||
*
|
||||
* \Return EMAC_OK if successfully, EMAC_TIMEOUT if timeout.
|
||||
*/
|
||||
uint8_t emac_phy_write(Emac* p_emac, uint8_t uc_phy_address,
|
||||
uint8_t uc_address, uint32_t ul_value)
|
||||
{
|
||||
emac_maintain_phy(p_emac, uc_phy_address, uc_address, 0, ul_value);
|
||||
|
||||
if (emac_wait_phy(p_emac, MAC_PHY_RETRY_MAX) == EMAC_TIMEOUT) {
|
||||
return EMAC_TIMEOUT;
|
||||
}
|
||||
return EMAC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Initialize the EMAC driver.
|
||||
*
|
||||
* \param p_emac Pointer to the EMAC instance.
|
||||
* \param p_emac_dev Pointer to the EMAC device instance.
|
||||
* \param p_opt EMAC configure options.
|
||||
*/
|
||||
void emac_dev_init(Emac* p_emac, emac_device_t* p_emac_dev,
|
||||
emac_options_t* p_opt)
|
||||
{
|
||||
emac_dev_mem_t emac_dev_mm;
|
||||
|
||||
/* Disable TX & RX and more */
|
||||
emac_network_control(p_emac, 0);
|
||||
emac_disable_interrupt(p_emac, ~0u);
|
||||
|
||||
emac_clear_statistics(p_emac);
|
||||
|
||||
/* Clear all status bits in the receive status register. */
|
||||
emac_clear_rx_status(p_emac, EMAC_RSR_OVR | EMAC_RSR_REC | EMAC_RSR_BNA);
|
||||
|
||||
/* Clear all status bits in the transmit status register */
|
||||
emac_clear_tx_status(p_emac, EMAC_TSR_UBR | EMAC_TSR_COL | EMAC_TSR_RLES
|
||||
| EMAC_TSR_BEX | EMAC_TSR_COMP | EMAC_TSR_UND);
|
||||
|
||||
/* Clear interrupts */
|
||||
emac_get_interrupt_status(p_emac);
|
||||
|
||||
/* Enable the copy of data into the buffers
|
||||
ignore broadcasts, and not copy FCS. */
|
||||
emac_set_configure(p_emac,
|
||||
emac_get_configure(p_emac) | EMAC_NCFGR_DRFCS | EMAC_NCFGR_PAE);
|
||||
|
||||
emac_enable_copy_all(p_emac, p_opt->uc_copy_all_frame);
|
||||
emac_disable_broadcast(p_emac, p_opt->uc_no_boardcast);
|
||||
|
||||
/* Fill in EMAC device memory management */
|
||||
emac_dev_mm.p_rx_buffer = gs_uc_rx_buffer;
|
||||
emac_dev_mm.p_rx_dscr = gs_rx_desc;
|
||||
emac_dev_mm.us_rx_size = EMAC_RX_BUFFERS;
|
||||
|
||||
emac_dev_mm.p_tx_buffer = gs_uc_tx_buffer;
|
||||
emac_dev_mm.p_tx_dscr = gs_tx_desc;
|
||||
emac_dev_mm.us_tx_size = EMAC_TX_BUFFERS;
|
||||
|
||||
emac_init_mem(p_emac, p_emac_dev, &emac_dev_mm, gs_tx_callback);
|
||||
|
||||
emac_set_address(p_emac, 0, p_opt->uc_mac_addr);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Frames can be read from the EMAC in multiple sections.
|
||||
* Read ul_frame_size bytes from the EMAC receive buffers to pcTo.
|
||||
* p_rcv_size is the size of the entire frame. Generally emac_read
|
||||
* will be repeatedly called until the sum of all the ul_frame_size equals
|
||||
* the value of p_rcv_size.
|
||||
*
|
||||
* \param p_emac_dev Pointer to the EMAC device instance.
|
||||
* \param p_frame Address of the frame buffer.
|
||||
* \param ul_frame_size Length of the frame.
|
||||
* \param p_rcv_size Received frame size.
|
||||
*
|
||||
* \return EMAC_OK if receiving frame successfully, otherwise failed.
|
||||
*/
|
||||
uint32_t emac_dev_read(emac_device_t* p_emac_dev, uint8_t* p_frame,
|
||||
uint32_t ul_frame_size, uint32_t* p_rcv_size)
|
||||
{
|
||||
uint16_t us_buffer_length;
|
||||
uint32_t tmp_ul_frame_size = 0;
|
||||
uint8_t *p_tmp_frame = 0;
|
||||
uint16_t us_tmp_idx = p_emac_dev->us_rx_idx;
|
||||
emac_rx_descriptor_t *p_rx_td =
|
||||
&p_emac_dev->p_rx_dscr[p_emac_dev->us_rx_idx];
|
||||
int8_t c_is_frame = 0;
|
||||
|
||||
if (p_frame == NULL)
|
||||
return EMAC_PARAM;
|
||||
|
||||
/* Set the default return value */
|
||||
*p_rcv_size = 0;
|
||||
|
||||
/* Process received RX descriptor */
|
||||
while ((p_rx_td->addr.val & EMAC_RXD_OWNERSHIP) == EMAC_RXD_OWNERSHIP) {
|
||||
/* A start of frame has been received, discard previous fragments */
|
||||
if ((p_rx_td->status.val & EMAC_RXD_SOF) == EMAC_RXD_SOF) {
|
||||
/* Skip previous fragment */
|
||||
while (us_tmp_idx != p_emac_dev->us_rx_idx) {
|
||||
p_rx_td = &p_emac_dev->p_rx_dscr[p_emac_dev->us_rx_idx];
|
||||
p_rx_td->addr.val &= ~(EMAC_RXD_OWNERSHIP);
|
||||
|
||||
circ_inc(&p_emac_dev->us_rx_idx, p_emac_dev->us_rx_list_size);
|
||||
}
|
||||
/* Reset the temporary frame pointer */
|
||||
p_tmp_frame = p_frame;
|
||||
tmp_ul_frame_size = 0;
|
||||
/* Start to gather buffers in a frame */
|
||||
c_is_frame = 1;
|
||||
}
|
||||
|
||||
/* Increment the pointer */
|
||||
circ_inc(&us_tmp_idx, p_emac_dev->us_rx_list_size);
|
||||
|
||||
/* Copy data in the frame buffer */
|
||||
if (c_is_frame) {
|
||||
if (us_tmp_idx == p_emac_dev->us_rx_idx) {
|
||||
do {
|
||||
p_rx_td = &p_emac_dev->p_rx_dscr[p_emac_dev->us_rx_idx];
|
||||
p_rx_td->addr.val &= ~(EMAC_RXD_OWNERSHIP);
|
||||
circ_inc(&p_emac_dev->us_rx_idx, p_emac_dev->us_rx_list_size);
|
||||
|
||||
} while (us_tmp_idx != p_emac_dev->us_rx_idx);
|
||||
|
||||
return EMAC_RX_NULL;
|
||||
}
|
||||
/* Copy the buffer into the application frame */
|
||||
us_buffer_length = EMAC_RX_UNITSIZE;
|
||||
if ((tmp_ul_frame_size + us_buffer_length) > ul_frame_size) {
|
||||
us_buffer_length = ul_frame_size - tmp_ul_frame_size;
|
||||
}
|
||||
|
||||
memcpy(p_tmp_frame,
|
||||
(void *)(p_rx_td->addr.val & EMAC_RXD_ADDR_MASK),
|
||||
us_buffer_length);
|
||||
p_tmp_frame += us_buffer_length;
|
||||
tmp_ul_frame_size += us_buffer_length;
|
||||
|
||||
/* An end of frame has been received, return the data */
|
||||
if ((p_rx_td->status.val & EMAC_RXD_EOF) == EMAC_RXD_EOF) {
|
||||
/* Frame size from the EMAC */
|
||||
*p_rcv_size = (p_rx_td->status.val & EMAC_RXD_LEN_MASK);
|
||||
|
||||
/* All data have been copied in the application frame buffer => release TD */
|
||||
while (p_emac_dev->us_rx_idx != us_tmp_idx) {
|
||||
p_rx_td = &p_emac_dev->p_rx_dscr[p_emac_dev->us_rx_idx];
|
||||
p_rx_td->addr.val &= ~(EMAC_RXD_OWNERSHIP);
|
||||
circ_inc(&p_emac_dev->us_rx_idx, p_emac_dev->us_rx_list_size);
|
||||
}
|
||||
|
||||
/* Application frame buffer is too small so that all data have not been copied */
|
||||
if (tmp_ul_frame_size < *p_rcv_size) {
|
||||
return EMAC_SIZE_TOO_SMALL;
|
||||
}
|
||||
|
||||
return EMAC_OK;
|
||||
}
|
||||
}
|
||||
/* SOF has not been detected, skip the fragment */
|
||||
else {
|
||||
p_rx_td->addr.val &= ~(EMAC_RXD_OWNERSHIP);
|
||||
p_emac_dev->us_rx_idx = us_tmp_idx;
|
||||
}
|
||||
|
||||
/* Process the next buffer */
|
||||
p_rx_td = &p_emac_dev->p_rx_dscr[us_tmp_idx];
|
||||
}
|
||||
|
||||
return EMAC_RX_NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Send ulLength bytes from pcFrom. This copies the buffer to one of the
|
||||
* EMAC Tx buffers, and then indicates to the EMAC that the buffer is ready.
|
||||
* If lEndOfFrame is true then the data being copied is the end of the frame
|
||||
* and the frame can be transmitted.
|
||||
*
|
||||
* \param p_emac_dev Pointer to the EMAC device instance.
|
||||
* \param p_buffer Pointer to the data buffer.
|
||||
* \param ul_size Length of the frame.
|
||||
* \param func_tx_cb Transmit callback function.
|
||||
*
|
||||
* \return Length sent.
|
||||
*/
|
||||
uint32_t emac_dev_write(emac_device_t* p_emac_dev, void *p_buffer,
|
||||
uint32_t ul_size, emac_dev_tx_cb_t func_tx_cb)
|
||||
{
|
||||
|
||||
volatile emac_tx_descriptor_t *p_tx_td;
|
||||
volatile emac_dev_tx_cb_t *p_func_tx_cb;
|
||||
|
||||
Emac *p_hw = p_emac_dev->p_hw;
|
||||
|
||||
|
||||
/* Check parameter */
|
||||
if (ul_size > EMAC_TX_UNITSIZE) {
|
||||
return EMAC_PARAM;
|
||||
}
|
||||
|
||||
/* Pointers to the current transmit descriptor */
|
||||
p_tx_td = &p_emac_dev->p_tx_dscr[p_emac_dev->us_tx_head];
|
||||
|
||||
/* If no free TxTd, buffer can't be sent, schedule the wakeup callback */
|
||||
if (CIRC_SPACE(p_emac_dev->us_tx_head, p_emac_dev->us_tx_tail,
|
||||
p_emac_dev->us_tx_list_size) == 0) {
|
||||
return EMAC_TX_BUSY;
|
||||
}
|
||||
|
||||
/* Pointers to the current Tx callback */
|
||||
p_func_tx_cb = &p_emac_dev->func_tx_cb_list[p_emac_dev->us_tx_head];
|
||||
|
||||
/* Set up/copy data to transmission buffer */
|
||||
if (p_buffer && ul_size) {
|
||||
/* Driver manages the ring buffer */
|
||||
memcpy((void *)p_tx_td->addr, p_buffer, ul_size);
|
||||
}
|
||||
|
||||
/* Tx callback */
|
||||
*p_func_tx_cb = func_tx_cb;
|
||||
|
||||
/* Update transmit descriptor status */
|
||||
|
||||
/* The buffer size defined is the length of ethernet frame,
|
||||
so it's always the last buffer of the frame. */
|
||||
if (p_emac_dev->us_tx_head == p_emac_dev->us_tx_list_size - 1) {
|
||||
p_tx_td->status.val =
|
||||
(ul_size & EMAC_TXD_LEN_MASK) | EMAC_TXD_LAST
|
||||
| EMAC_TXD_WRAP;
|
||||
} else {
|
||||
p_tx_td->status.val =
|
||||
(ul_size & EMAC_TXD_LEN_MASK) | EMAC_TXD_LAST;
|
||||
}
|
||||
|
||||
circ_inc(&p_emac_dev->us_tx_head, p_emac_dev->us_tx_list_size);
|
||||
|
||||
/* Now start to transmit if it is still not done */
|
||||
emac_start_transmission(p_hw);
|
||||
|
||||
return EMAC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get current load of transmit.
|
||||
*
|
||||
* \param p_emac_dev Pointer to the EMAC device instance.
|
||||
*
|
||||
* \return Current load of transmit.
|
||||
*/
|
||||
uint32_t emac_dev_get_tx_load(emac_device_t* p_emac_dev)
|
||||
{
|
||||
uint16_t us_head = p_emac_dev->us_tx_head;
|
||||
uint16_t us_tail = p_emac_dev->us_tx_tail;
|
||||
return CIRC_CNT(us_head, us_tail, p_emac_dev->us_tx_list_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Register/Clear RX callback. Callback will be invoked after the next received
|
||||
* frame.
|
||||
*
|
||||
* When emac_dev_read() returns EMAC_RX_NULL, the application task calls
|
||||
* emac_dev_set_rx_callback() to register func_rx_cb() callback and enters suspend state.
|
||||
* The callback is in charge to resume the task once a new frame has been
|
||||
* received. The next time emac_dev_read() is called, it will be successful.
|
||||
*
|
||||
* This function is usually invoked from the RX callback itself with NULL
|
||||
* callback, to unregister. Once the callback has resumed the application task,
|
||||
* there is no need to invoke the callback again.
|
||||
*
|
||||
* \param p_emac_dev Pointer to the EMAC device instance.
|
||||
* \param func_tx_cb Receive callback function.
|
||||
*/
|
||||
void emac_dev_set_rx_callback(emac_device_t* p_emac_dev,
|
||||
emac_dev_tx_cb_t func_rx_cb)
|
||||
{
|
||||
Emac *p_hw = p_emac_dev->p_hw;
|
||||
|
||||
if (func_rx_cb == NULL) {
|
||||
emac_disable_interrupt(p_hw, EMAC_IDR_RCOMP);
|
||||
p_emac_dev->func_rx_cb = NULL;
|
||||
} else {
|
||||
p_emac_dev->func_rx_cb = func_rx_cb;
|
||||
emac_enable_interrupt(p_hw, EMAC_IER_RCOMP);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Register/Clear TX wakeup callback.
|
||||
*
|
||||
* When emac_dev_write() returns EMAC_TX_BUSY (all transmit descriptor busy), the application
|
||||
* task calls emac_dev_set_tx_wakeup_callback() to register func_wakeup() callback and
|
||||
* enters suspend state. The callback is in charge to resume the task once
|
||||
* several transmit descriptors have been released. The next time emac_dev_write() will be called,
|
||||
* it shall be successful.
|
||||
*
|
||||
* This function is usually invoked with NULL callback from the TX wakeup
|
||||
* callback itself, to unregister. Once the callback has resumed the
|
||||
* application task, there is no need to invoke the callback again.
|
||||
*
|
||||
* \param p_emac_dev Pointer to EMAC device instance.
|
||||
* \param func_wakeup Pointer to wakeup callback function.
|
||||
* \param uc_threshold Number of free transmit descriptor before wakeup callback invoked.
|
||||
*
|
||||
* \return EMAC_OK, EMAC_PARAM on parameter error.
|
||||
*/
|
||||
uint8_t emac_dev_set_tx_wakeup_callback(emac_device_t* p_emac_dev,
|
||||
emac_dev_wakeup_cb_t func_wakeup_cb, uint8_t uc_threshold)
|
||||
{
|
||||
if (func_wakeup_cb == NULL) {
|
||||
p_emac_dev->func_wakeup_cb = NULL;
|
||||
} else {
|
||||
if (uc_threshold <= p_emac_dev->us_tx_list_size) {
|
||||
p_emac_dev->func_wakeup_cb = func_wakeup_cb;
|
||||
p_emac_dev->uc_wakeup_threshold = uc_threshold;
|
||||
} else {
|
||||
return EMAC_PARAM;
|
||||
}
|
||||
}
|
||||
|
||||
return EMAC_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Reset TX & RX queue & statistics.
|
||||
*
|
||||
* \param p_emac_dev Pointer to EMAC device instance.
|
||||
*/
|
||||
void emac_dev_reset(emac_device_t* p_emac_dev)
|
||||
{
|
||||
Emac *p_hw = p_emac_dev->p_hw;
|
||||
|
||||
emac_reset_rx_mem(p_emac_dev);
|
||||
emac_reset_tx_mem(p_emac_dev);
|
||||
emac_network_control(p_hw, EMAC_NCR_TE | EMAC_NCR_RE
|
||||
| EMAC_NCR_WESTAT | EMAC_NCR_CLRSTAT);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief EMAC Interrupt handler.
|
||||
*
|
||||
* \param p_emac_dev Pointer to EMAC device instance.
|
||||
*/
|
||||
void emac_handler(emac_device_t* p_emac_dev)
|
||||
{
|
||||
Emac *p_hw = p_emac_dev->p_hw;
|
||||
|
||||
emac_tx_descriptor_t *p_tx_td;
|
||||
emac_dev_tx_cb_t *p_tx_cb;
|
||||
volatile uint32_t ul_isr;
|
||||
volatile uint32_t ul_rsr;
|
||||
volatile uint32_t ul_tsr;
|
||||
uint32_t ul_rx_status_flag;
|
||||
uint32_t ul_tx_status_flag;
|
||||
|
||||
ul_isr = emac_get_interrupt_status(p_hw);
|
||||
ul_rsr = emac_get_rx_status(p_hw);
|
||||
ul_tsr = emac_get_tx_status(p_hw);
|
||||
|
||||
ul_isr &= ~(emac_get_interrupt_mask(p_hw) | 0xFFC300);
|
||||
|
||||
/* RX packet */
|
||||
if ((ul_isr & EMAC_ISR_RCOMP) || (ul_rsr & EMAC_RSR_REC)) {
|
||||
ul_rx_status_flag = EMAC_RSR_REC;
|
||||
|
||||
/* Check OVR */
|
||||
if (ul_rsr & EMAC_RSR_OVR) {
|
||||
ul_rx_status_flag |= EMAC_RSR_OVR;
|
||||
}
|
||||
/* Check BNA */
|
||||
if (ul_rsr & EMAC_RSR_BNA) {
|
||||
ul_rx_status_flag |= EMAC_RSR_BNA;
|
||||
}
|
||||
/* Clear status */
|
||||
emac_clear_rx_status(p_hw, ul_rx_status_flag);
|
||||
|
||||
/* Invoke callbacks */
|
||||
if (p_emac_dev->func_rx_cb) {
|
||||
p_emac_dev->func_rx_cb(ul_rx_status_flag);
|
||||
}
|
||||
}
|
||||
|
||||
/* TX packet */
|
||||
if ((ul_isr & EMAC_ISR_TCOMP) || (ul_tsr & EMAC_TSR_COMP)) {
|
||||
|
||||
ul_tx_status_flag = EMAC_TSR_COMP;
|
||||
|
||||
/* A frame transmitted */
|
||||
|
||||
/* Check RLE */
|
||||
if (ul_tsr & EMAC_TSR_RLES) {
|
||||
/* Status RLE & Number of discarded buffers */
|
||||
ul_tx_status_flag = EMAC_TSR_RLES | CIRC_CNT(p_emac_dev->us_tx_head,
|
||||
p_emac_dev->us_tx_tail, p_emac_dev->us_tx_list_size);
|
||||
p_tx_cb = &p_emac_dev->func_tx_cb_list[p_emac_dev->us_tx_tail];
|
||||
emac_reset_tx_mem(p_emac_dev);
|
||||
emac_enable_transmit(p_hw, 1);
|
||||
}
|
||||
/* Check COL */
|
||||
if (ul_tsr & EMAC_TSR_COL) {
|
||||
ul_tx_status_flag |= EMAC_TSR_COL;
|
||||
}
|
||||
/* Check BEX */
|
||||
if (ul_tsr & EMAC_TSR_BEX) {
|
||||
ul_tx_status_flag |= EMAC_TSR_BEX;
|
||||
}
|
||||
/* Check UND */
|
||||
if (ul_tsr & EMAC_TSR_UND) {
|
||||
ul_tx_status_flag |= EMAC_TSR_UND;
|
||||
}
|
||||
/* Clear status */
|
||||
emac_clear_tx_status(p_hw, ul_tx_status_flag);
|
||||
|
||||
if (!CIRC_EMPTY(p_emac_dev->us_tx_head, p_emac_dev->us_tx_tail)) {
|
||||
/* Check the buffers */
|
||||
do {
|
||||
p_tx_td = &p_emac_dev->p_tx_dscr[p_emac_dev->us_tx_tail];
|
||||
p_tx_cb = &p_emac_dev->func_tx_cb_list[p_emac_dev->us_tx_tail];
|
||||
/* Any error? Exit if buffer has not been sent yet */
|
||||
if ((p_tx_td->status.val & EMAC_TXD_USED) == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Notify upper layer that a packet has been sent */
|
||||
if (*p_tx_cb) {
|
||||
(*p_tx_cb) (ul_tx_status_flag);
|
||||
}
|
||||
|
||||
circ_inc(&p_emac_dev->us_tx_tail, p_emac_dev->us_tx_list_size);
|
||||
} while (CIRC_CNT(p_emac_dev->us_tx_head, p_emac_dev->us_tx_tail,
|
||||
p_emac_dev->us_tx_list_size));
|
||||
}
|
||||
|
||||
if (ul_tsr & EMAC_TSR_RLES) {
|
||||
/* Notify upper layer RLE */
|
||||
if (*p_tx_cb) {
|
||||
(*p_tx_cb) (ul_tx_status_flag);
|
||||
}
|
||||
}
|
||||
|
||||
/* If a wakeup has been scheduled, notify upper layer that it can
|
||||
send other packets, and the sending will be successful. */
|
||||
if ((CIRC_SPACE(p_emac_dev->us_tx_head, p_emac_dev->us_tx_tail,
|
||||
p_emac_dev->us_tx_list_size) >= p_emac_dev->uc_wakeup_threshold)
|
||||
&& p_emac_dev->func_wakeup_cb) {
|
||||
p_emac_dev->func_wakeup_cb();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
#endif // SAM3XA_SERIES
|
||||
|
||||
/// @cond 0
|
||||
/**INDENT-OFF**/
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
/**INDENT-ON**/
|
||||
/// @endcond
|
|
@ -24,8 +24,8 @@ Licence: GPL
|
|||
#define CONFIGURATION_H
|
||||
|
||||
#define NAME "RepRapFirmware"
|
||||
#define VERSION "0.59d-dc42"
|
||||
#define DATE "2014-05-28"
|
||||
#define VERSION "0.59e-dc42"
|
||||
#define DATE "2014-05-31"
|
||||
#define LAST_AUTHOR "dc42"
|
||||
|
||||
// Other firmware that we might switch to be compatible with.
|
||||
|
|
|
@ -53,11 +53,16 @@
|
|||
/*! EMAC RMII mode */
|
||||
#define BOARD_EMAC_MODE_RMII 1
|
||||
|
||||
// dc42, 2014-06-01.
|
||||
// The following #defines are not used, so I have commented them out.
|
||||
// The ones in hardware/arduino/sam/system/libsam/source/emac.c get used instead, and they need to be changed to these values (32 and 16).
|
||||
|
||||
/** Number of buffer for RX */
|
||||
#define EMAC_RX_BUFFERS 16
|
||||
//#define EMAC_RX_BUFFERS (32)
|
||||
|
||||
/** Number of buffer for TX */
|
||||
#define EMAC_TX_BUFFERS 8
|
||||
//#define EMAC_TX_BUFFERS (8)
|
||||
|
||||
|
||||
/** MAC PHY operation max retry count */
|
||||
#define MAC_PHY_RETRY_MAX 1000000
|
||||
|
|
43
Network.cpp
43
Network.cpp
|
@ -407,6 +407,8 @@ void Network::ConnectionClosing(HttpState* hs)
|
|||
hs->sendingRs->SetConnectionLost();
|
||||
hs->sendingRs = NULL;
|
||||
}
|
||||
|
||||
reprap.GetWebserver()->ResetState(hs); // if the webserver is waiting on this connection, it needs to give up
|
||||
}
|
||||
|
||||
void Network::ReceiveInput(pbuf *pb, HttpState* hs)
|
||||
|
@ -428,6 +430,41 @@ void Network::ReceiveInput(pbuf *pb, HttpState* hs)
|
|||
// debugPrintf("Network - input received\n");
|
||||
}
|
||||
|
||||
// This is called by the web server to get a new received packet.
|
||||
// If the connection parameter is NULL, we just return the request at the head of the ready list.
|
||||
// Otherwise, we are only interested in packets received from the specified connection. If we find one than
|
||||
// we move it to the head of the ready list, so that a subsequent call with a null connection parameter
|
||||
// will return the same one.
|
||||
RequestState *Network::GetRequest(const HttpState *connection)
|
||||
{
|
||||
RequestState *rs = readyTransactions;
|
||||
if (rs == NULL || connection == NULL || rs->hs == connection)
|
||||
{
|
||||
return rs;
|
||||
}
|
||||
|
||||
// There is at least one ready transaction, but it's not on the connection we are looking for
|
||||
for (;;)
|
||||
{
|
||||
RequestState *rsNext = rs->next;
|
||||
if (rsNext == NULL)
|
||||
{
|
||||
return rsNext;
|
||||
}
|
||||
if (rsNext->hs == connection)
|
||||
{
|
||||
irqflags_t flags = cpu_irq_save();
|
||||
rs->next = rsNext->next; // remove rsNext from the list
|
||||
rsNext->next = readyTransactions;
|
||||
readyTransactions = rsNext;
|
||||
cpu_irq_restore(flags);
|
||||
return rsNext;
|
||||
}
|
||||
rs = rsNext;
|
||||
}
|
||||
return NULL; // to keep Eclipse happy
|
||||
}
|
||||
|
||||
// Send the output data we already have, optionally with a file appended, then close the connection unless keepConnectionOpen is true.
|
||||
// The file may be too large for our buffer, so we may have to send it in multiple transactions.
|
||||
void Network::SendAndClose(FileStore *f, bool keepConnectionOpen)
|
||||
|
@ -518,7 +555,7 @@ bool RequestState::Read(char& b)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (inputPointer == pb->len)
|
||||
while (inputPointer == pb->len)
|
||||
{
|
||||
// See if there is another pbuf in the chain
|
||||
if (inputPointer < pb->tot_len)
|
||||
|
@ -533,6 +570,10 @@ bool RequestState::Read(char& b)
|
|||
}
|
||||
inputPointer = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
b = ((const char*)pb->payload)[inputPointer++];
|
||||
|
|
|
@ -70,6 +70,7 @@ public:
|
|||
void SetConnectionLost();
|
||||
bool LostConnection() const;
|
||||
bool IsReady() const { return hs == NULL || hs->sendingRs == NULL; }
|
||||
HttpState *GetConnection() const { return hs; }
|
||||
|
||||
private:
|
||||
void Reset();
|
||||
|
@ -103,7 +104,7 @@ public:
|
|||
void ConnectionClosing(HttpState* h);
|
||||
bool Active() const;
|
||||
bool LinkIsUp();
|
||||
RequestState *GetRequest() const { return readyTransactions; }
|
||||
RequestState *GetRequest(const HttpState *connection);
|
||||
void SendAndClose(FileStore *f, bool keepConnectionOpen = false);
|
||||
|
||||
Network();
|
||||
|
|
212
Webserver.cpp
212
Webserver.cpp
|
@ -290,7 +290,7 @@ void Webserver::SendFile(const char* nameOfFileToSend)
|
|||
}
|
||||
|
||||
Network *net = reprap.GetNetwork();
|
||||
RequestState *req = net->GetRequest();
|
||||
RequestState *req = net->GetRequest(NULL);
|
||||
req->Write("HTTP/1.1 200 OK\n");
|
||||
|
||||
const char* contentType;
|
||||
|
@ -339,7 +339,7 @@ void Webserver::SendFile(const char* nameOfFileToSend)
|
|||
void Webserver::SendJsonResponse(const char* command)
|
||||
{
|
||||
Network *net = reprap.GetNetwork();
|
||||
RequestState *req = net->GetRequest();
|
||||
RequestState *req = net->GetRequest(NULL);
|
||||
bool keepOpen = false;
|
||||
bool mayKeepOpen;
|
||||
if (numQualKeys == 0)
|
||||
|
@ -502,11 +502,14 @@ bool Webserver::GetJsonResponse(const char* request, const char* key, const char
|
|||
else if (StringEquals(request, "fileinfo") && StringEquals(key, "name"))
|
||||
{
|
||||
unsigned long length;
|
||||
float height, filament;
|
||||
bool found = GetFileInfo(value, length, height, filament);
|
||||
float height, filament, layerHeight;
|
||||
char generatedBy[50];
|
||||
bool found = GetFileInfo(value, length, height, filament, layerHeight, generatedBy, ARRAY_SIZE(generatedBy));
|
||||
if (found)
|
||||
{
|
||||
snprintf(jsonResponse, ARRAY_UPB(jsonResponse), "{\"err\":0,\"size\":%lu,\"height\":\"%.2f\",\"filament\":\"%.1f\"}", length, height, filament);
|
||||
snprintf(jsonResponse, ARRAY_UPB(jsonResponse),
|
||||
"{\"err\":0,\"size\":%lu,\"height\":\"%.2f\",\"filament\":\"%.1f\",\"layerHeight\":\"%.2f\",\"generatedBy\":\"%s\"}",
|
||||
length, height, filament, layerHeight, generatedBy);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -905,7 +908,7 @@ bool Webserver::CharFromClient(char c)
|
|||
switch(c)
|
||||
{
|
||||
case '\n':
|
||||
if (clientMessage + clientPointer == headers[numHeaderKeys].key)
|
||||
if (clientMessage + clientPointer == headers[numHeaderKeys].key) // if the key hasn't started yet, then this is the blank line at the end
|
||||
{
|
||||
return ProcessMessage();
|
||||
}
|
||||
|
@ -1037,7 +1040,7 @@ bool Webserver::RejectMessage(const char* response, unsigned int code)
|
|||
platform->Message(HOST_MESSAGE, "\n");
|
||||
|
||||
Network *net = reprap.GetNetwork();
|
||||
RequestState *req = net->GetRequest();
|
||||
RequestState *req = net->GetRequest(NULL);
|
||||
req->Printf("HTTP/1.1 %u %s\nConnection: close\n\n", code, response);
|
||||
net->SendAndClose(NULL);
|
||||
return true;
|
||||
|
@ -1049,6 +1052,7 @@ void Webserver::Spin()
|
|||
{
|
||||
if (state != inactive)
|
||||
{
|
||||
//#if 0
|
||||
if (uploadState == uploadOK && uploadReadIndex != uploadWriteIndex)
|
||||
{
|
||||
// Write some uploaded data to file
|
||||
|
@ -1063,39 +1067,51 @@ void Webserver::Spin()
|
|||
}
|
||||
}
|
||||
else
|
||||
//#endif
|
||||
{
|
||||
Network *net = reprap.GetNetwork();
|
||||
RequestState *req = net->GetRequest();
|
||||
RequestState *req = net->GetRequest(currentConnection);
|
||||
if (req != NULL && req->IsReady())
|
||||
{
|
||||
// To achieve a high upload speed, we try to process a complete request here
|
||||
for (;;)
|
||||
for (int i = 0; i < 100; ++i)
|
||||
{
|
||||
char c;
|
||||
if (req->Read(c))
|
||||
{
|
||||
if (CharFromClient(c))
|
||||
{
|
||||
ResetState();
|
||||
ResetState(NULL);
|
||||
break; // break if we have read all we want of this message
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We ran out of data before finding a complete request.
|
||||
// This can happen if the network connection was lost, so only report it if debug is enabled
|
||||
if (reprap.Debug())
|
||||
{
|
||||
platform->Message(HOST_MESSAGE, "Webserver: incomplete request\n");
|
||||
}
|
||||
net->SendAndClose(NULL);
|
||||
ResetState();
|
||||
// This happens when the incoming message length exceeds the TCP MSS. We need to process another packet on the same connection.
|
||||
currentConnection = req->GetConnection();
|
||||
net->SendAndClose(NULL, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
else if (uploadState == uploadOK && uploadReadIndex != uploadWriteIndex)
|
||||
{
|
||||
// Write some uploaded data to file
|
||||
for (unsigned int i = 0; i < 256 && uploadReadIndex != uploadWriteIndex && uploadState == uploadOK; ++i)
|
||||
{
|
||||
char c = uploadBuffer[uploadReadIndex];
|
||||
uploadReadIndex = (uploadReadIndex + 1) % uploadBufLength;
|
||||
if (!fileBeingUploaded.Write(c))
|
||||
{
|
||||
uploadState = uploadError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
platform->ClassReport("Webserver", longWait);
|
||||
}
|
||||
}
|
||||
|
@ -1122,21 +1138,27 @@ void Webserver::Init()
|
|||
gcodeReply[0] = 0;
|
||||
seq = 0;
|
||||
uploadState = notUploading;
|
||||
ResetState();
|
||||
ResetState(NULL);
|
||||
|
||||
// Reinitialise the message file
|
||||
|
||||
//platform->GetMassStorage()->Delete(platform->GetWebDir(), MESSAGE_FILE);
|
||||
}
|
||||
|
||||
void Webserver::ResetState()
|
||||
// This is called by the web server to reset the receive state.
|
||||
// It is also called by the network when the current connection is lost.
|
||||
void Webserver::ResetState(const HttpState *connection)
|
||||
{
|
||||
clientPointer = 0;
|
||||
state = doingCommandWord;
|
||||
numCommandWords = 0;
|
||||
numQualKeys = 0;
|
||||
numHeaderKeys = 0;
|
||||
commandWords[0] = clientMessage;
|
||||
if (connection == NULL || connection == currentConnection)
|
||||
{
|
||||
currentConnection = NULL;
|
||||
clientPointer = 0;
|
||||
state = doingCommandWord;
|
||||
numCommandWords = 0;
|
||||
numQualKeys = 0;
|
||||
numHeaderKeys = 0;
|
||||
commandWords[0] = clientMessage;
|
||||
}
|
||||
}
|
||||
|
||||
void Webserver::CancelUpload()
|
||||
|
@ -1232,7 +1254,7 @@ unsigned int Webserver::GetReportedUploadBufferSpace() const
|
|||
}
|
||||
|
||||
// Get information for a file on the SD card
|
||||
bool Webserver::GetFileInfo(const char *fileName, unsigned long& length, float& height, float& filamentUsed)
|
||||
bool Webserver::GetFileInfo(const char *fileName, unsigned long& length, float& height, float& filamentUsed, float& layerHeight, char* generatedBy, size_t generatedByLength)
|
||||
{
|
||||
FileStore *f = platform->GetFileStore("0:/", fileName, false);
|
||||
if (f != NULL)
|
||||
|
@ -1241,62 +1263,112 @@ bool Webserver::GetFileInfo(const char *fileName, unsigned long& length, float&
|
|||
length = f->Length();
|
||||
height = 0.0;
|
||||
filamentUsed = 0.0;
|
||||
layerHeight = 0.0;
|
||||
generatedBy[0] = 0;
|
||||
|
||||
if (length != 0 && (StringEndsWith(fileName, ".gcode") || StringEndsWith(fileName, ".g") || StringEndsWith(fileName, ".gco") || StringEndsWith(fileName, ".gc")))
|
||||
{
|
||||
const size_t readSize = 512; // read 512 bytes at a time (tried 1K but it sometimes gives us the wrong data)
|
||||
const size_t readSize = 1024; // read 1K bytes at a time
|
||||
const size_t overlap = 100;
|
||||
size_t sizeToRead;
|
||||
if (length <= sizeToRead + overlap)
|
||||
{
|
||||
sizeToRead = length; // read the whole file in one go
|
||||
}
|
||||
else
|
||||
{
|
||||
sizeToRead = length % readSize;
|
||||
if (sizeToRead <= overlap)
|
||||
{
|
||||
sizeToRead += readSize;
|
||||
}
|
||||
}
|
||||
unsigned long seekPos = length - sizeToRead; // read on a 1K boundary
|
||||
size_t sizeToScan = sizeToRead;
|
||||
char buf[readSize + overlap + 1]; // need the +1 so we can add a null terminator
|
||||
bool foundFilamentUsed = false;
|
||||
for (;;)
|
||||
|
||||
// Get slic3r settings by reading from the start of the file. We only read the first 1K or so, everything we are looking for should be there.
|
||||
{
|
||||
if (!f->Seek(seekPos))
|
||||
{
|
||||
//debugPrintf("Seek to %lu failed\n", seekPos);
|
||||
break;
|
||||
}
|
||||
//debugPrintf("Reading %u bytes at %lu\n", sizeToRead, seekPos);
|
||||
size_t sizeToRead = (size_t)min<unsigned long>(length, readSize + overlap);
|
||||
int nbytes = f->Read(buf, sizeToRead);
|
||||
if (nbytes != (int)sizeToRead)
|
||||
if (nbytes == (int)sizeToRead)
|
||||
{
|
||||
buf[sizeToRead] = 0;
|
||||
|
||||
// Look for layer height
|
||||
const char *pos = strstr(buf, "; layer_height ");
|
||||
if (pos != NULL)
|
||||
{
|
||||
while (strchr(" \t=", *pos))
|
||||
{
|
||||
++pos;
|
||||
}
|
||||
layerHeight = strtod(pos, NULL);
|
||||
}
|
||||
|
||||
pos = strstr(buf, "; generated by ");
|
||||
if (pos != NULL)
|
||||
{
|
||||
while (generatedByLength > 1 && *pos >= ' ')
|
||||
{
|
||||
char c = *pos++;
|
||||
if (c == '"' || c == '\\')
|
||||
{
|
||||
// Need to escape the quote-mark for JSON
|
||||
if (generatedByLength < 3)
|
||||
{
|
||||
break;
|
||||
}
|
||||
*generatedBy++ = '\\';
|
||||
}
|
||||
*generatedBy++ = c;
|
||||
}
|
||||
*generatedBy = 0;
|
||||
}
|
||||
|
||||
// Add code to look for other values here...
|
||||
}
|
||||
}
|
||||
|
||||
// Now get the object height and filament used by reading the end of the file
|
||||
{
|
||||
size_t sizeToRead;
|
||||
if (length <= readSize + overlap)
|
||||
{
|
||||
sizeToRead = length; // read the whole file in one go
|
||||
}
|
||||
else
|
||||
{
|
||||
sizeToRead = length % readSize;
|
||||
if (sizeToRead <= overlap)
|
||||
{
|
||||
sizeToRead += readSize;
|
||||
}
|
||||
}
|
||||
unsigned long seekPos = length - sizeToRead; // read on a 1K boundary
|
||||
size_t sizeToScan = sizeToRead;
|
||||
bool foundFilamentUsed = false;
|
||||
for (;;)
|
||||
{
|
||||
if (!f->Seek(seekPos))
|
||||
{
|
||||
//debugPrintf("Seek to %lu failed\n", seekPos);
|
||||
break;
|
||||
}
|
||||
//debugPrintf("Reading %u bytes at %lu\n", sizeToRead, seekPos);
|
||||
int nbytes = f->Read(buf, sizeToRead);
|
||||
if (nbytes != (int)sizeToRead)
|
||||
{
|
||||
//debugPrintf("Read failed, read %d bytes\n", nbytes);
|
||||
break; // read failed so give up
|
||||
}
|
||||
buf[sizeToScan] = 0; // add a null terminator
|
||||
break; // read failed so give up
|
||||
}
|
||||
buf[sizeToScan] = 0; // add a null terminator
|
||||
//debugPrintf("About to scan %u bytes starting: %.40s\n", sizeToScan, buf);
|
||||
if (!foundFilamentUsed)
|
||||
{
|
||||
foundFilamentUsed = FindFilamentUsed(buf, sizeToScan, filamentUsed);
|
||||
if (!foundFilamentUsed)
|
||||
{
|
||||
foundFilamentUsed = FindFilamentUsed(buf, sizeToScan, filamentUsed);
|
||||
//debugPrintf("Found fil: %c\n", foundFilamentUsed ? 'Y' : 'N');
|
||||
}
|
||||
if (FindHeight(buf, sizeToScan, height))
|
||||
{
|
||||
}
|
||||
if (FindHeight(buf, sizeToScan, height))
|
||||
{
|
||||
//debugPrintf("Found height = %f\n", height);
|
||||
break; // quit if found height
|
||||
}
|
||||
if (seekPos == 0 || length - seekPos >= 200000uL) // scan up to about the last 200K of the file (32K wasn't enough)
|
||||
{
|
||||
break; // quit if found height
|
||||
}
|
||||
if (seekPos == 0 || length - seekPos >= 200000uL) // scan up to about the last 200K of the file (32K wasn't enough)
|
||||
{
|
||||
//debugPrintf("Quitting loop at seekPos = %lu\n", seekPos);
|
||||
break; // quit if reached start of file or already scanned the last 32K of the file
|
||||
break; // quit if reached start of file or already scanned the last 32K of the file
|
||||
}
|
||||
seekPos -= readSize;
|
||||
sizeToRead = readSize;
|
||||
sizeToScan = readSize + overlap;
|
||||
memcpy(buf + sizeToRead, buf, overlap);
|
||||
}
|
||||
seekPos -= readSize;
|
||||
sizeToRead = readSize;
|
||||
sizeToScan = readSize + overlap;
|
||||
memcpy(buf + sizeToRead, buf, overlap);
|
||||
}
|
||||
}
|
||||
f->Close();
|
||||
|
|
12
Webserver.h
12
Webserver.h
|
@ -35,10 +35,11 @@ Licence: GPL
|
|||
#define KO_FIRST 3
|
||||
|
||||
const unsigned int gcodeBufLength = 512; // size of our gcode ring buffer, preferably a power of 2
|
||||
const unsigned int uploadBufLength = 2048; // size of our file upload buffer, preferably a power of 2
|
||||
const unsigned int maxReportedFreeBuf = 950; // the max we own up to having free, to avoid overlong messages. 1024 is too long for Chrome/Windows 8.1.
|
||||
const unsigned int uploadBufLength = 4096; // size of our file upload buffer, preferably a power of 2
|
||||
const unsigned int maxReportedFreeBuf = 2048; // the max we own up to having free, to avoid overlong messages
|
||||
|
||||
const unsigned int webMessageLength = 1500; // maximum length of the web message we accept after decoding, excluding POST data
|
||||
const unsigned int webMessageLength = 3000; // maximum length of the web message we accept after decoding, excluding POST data.
|
||||
// needs to be maxReportedFreeBuf + lots more to hold the HTTP headers.
|
||||
const unsigned int maxFilenameLength = 100; // maximum length of a filename (inc. path from root) that we can upload
|
||||
//const unsigned int postBoundaryLength = 100; // max length of the POST boundary string
|
||||
//const unsigned int postFilenameLength = 100; // max length of the POST filename
|
||||
|
@ -65,6 +66,7 @@ class Webserver
|
|||
void SetName(const char* nm);
|
||||
void HandleReply(const char *s, bool error);
|
||||
void AppendReply(const char* s);
|
||||
void ResetState(const HttpState *connection);
|
||||
|
||||
private:
|
||||
|
||||
|
@ -102,7 +104,6 @@ class Webserver
|
|||
const char* value;
|
||||
};
|
||||
|
||||
void ResetState();
|
||||
void CancelUpload();
|
||||
void SendFile(const char* nameOfFileToSend);
|
||||
void SendJsonResponse(const char* command);
|
||||
|
@ -122,7 +123,7 @@ class Webserver
|
|||
unsigned int GetUploadBufferSpace() const;
|
||||
unsigned int GetReportedUploadBufferSpace() const;
|
||||
void ProcessGcode(const char* gc);
|
||||
bool GetFileInfo(const char *fileName, unsigned long& length, float& height, float& filamentUsed);
|
||||
bool GetFileInfo(const char *fileName, unsigned long& length, float& height, float& filamentUsed, float& layerHeight, char* generatedBy, size_t generatedByLength);
|
||||
static bool FindHeight(const char* buf, size_t len, float& height);
|
||||
static bool FindFilamentUsed(const char* buf, size_t len, float& filamentUsed);
|
||||
|
||||
|
@ -132,6 +133,7 @@ class Webserver
|
|||
UploadState uploadState;
|
||||
FileData fileBeingUploaded;
|
||||
char filenameBeingUploaded[maxFilenameLength + 1];
|
||||
const HttpState *currentConnection;
|
||||
|
||||
float lastTime;
|
||||
float longWait;
|
||||
|
|
Reference in a new issue