summaryrefslogtreecommitdiff
path: root/cesar/hal/phy/maximus/src
diff options
context:
space:
mode:
Diffstat (limited to 'cesar/hal/phy/maximus/src')
-rw-r--r--cesar/hal/phy/maximus/src/maximus_bridgedma.c405
-rw-r--r--cesar/hal/phy/maximus/src/maximus_pbdma.c187
-rw-r--r--cesar/hal/phy/maximus/src/maximus_phy_access.c961
-rw-r--r--cesar/hal/phy/maximus/src/maximus_phy_ctrl.c2873
-rw-r--r--cesar/hal/phy/maximus/src/maximus_phy_ctrl_cb.c966
-rw-r--r--cesar/hal/phy/maximus/src/maximus_tmdma.c111
-rw-r--r--cesar/hal/phy/maximus/src/maximus_trace.c103
7 files changed, 5606 insertions, 0 deletions
diff --git a/cesar/hal/phy/maximus/src/maximus_bridgedma.c b/cesar/hal/phy/maximus/src/maximus_bridgedma.c
new file mode 100644
index 0000000000..60c75e8b31
--- /dev/null
+++ b/cesar/hal/phy/maximus/src/maximus_bridgedma.c
@@ -0,0 +1,405 @@
+/* Cesar project {{{
+ *
+ * Copyright (C) 2007 Spidcom
+ *
+ * <<<Licence>>>
+ *
+ * }}} */
+/**
+ * \file hal/phy/maximus/src/maximus_bridgedma.c
+ * \brief HAL Phy Bridge DMA functions for Maximus.
+ * \ingroup hal_phy_maximus
+ */
+
+#include "common/std.h"
+#include "ecos/packages/hal/maximus/arch/current/include/hal_host_intr.h"
+#include "hal/phy/maximus/inc/maximus_interrupts.h"
+#include "hal/phy/maximus/inc/maximus_phy_ctx.h"
+#include "hal/phy/maximus/inc/maximus_bridgedma_ctx.h"
+#include <stdlib.h>
+#include <errno.h>
+
+#define ETH_BUFFER_MASK(job) (~((job)->eth_buffer_mask << 7))
+#define ETH_BUFFER_SIZE(job) (ETH_BUFFER_MASK(job) + 1)
+#define ETH_BUFFER_ADDR(job) (unsigned char *)((unsigned long)job->data_addr & ~ETH_BUFFER_MASK(job))
+
+/*struct job_cb_data
+{
+ phy_bridgedma_t *ctx;
+ int is_it;
+};*/
+
+#ifdef ECOS
+static cyg_uint32 _bridgedma_ecos_isr(cyg_vector_t vector, cyg_addrword_t data)
+{
+ /* nothing to do except calling the bridgedma callback */
+ phy_bridgedma_t *bridgedma_ctx;
+ bridgedma_ctx = (phy_bridgedma_t *)data;
+ cyg_drv_interrupt_mask(PHY_HAL_INTERRUPT_BRIDGEDMA);
+ cyg_drv_interrupt_acknowledge(PHY_HAL_INTERRUPT_BRIDGEDMA);
+ if((*bridgedma_ctx->bridgedma_cb)(bridgedma_ctx->user_data, *((u32 *)((void *)&bridgedma_ctx->status))))
+ return CYG_ISR_CALL_DSR; // Cause DSR to be run
+ else
+ {
+ cyg_drv_interrupt_unmask(PHY_HAL_INTERRUPT_BRIDGEDMA);
+ return CYG_ISR_HANDLED;
+ }
+}
+
+static void _bridgedma_ecos_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
+{
+ /* nothing to do except calling the phy dsr */
+ phy_bridgedma_t *bridgedma_ctx;
+ bridgedma_ctx = (phy_bridgedma_t *)data;
+ (*bridgedma_ctx->deferred_cb)(bridgedma_ctx->user_data);
+ cyg_drv_interrupt_unmask(PHY_HAL_INTERRUPT_BRIDGEDMA);
+ return;
+}
+#endif /* ECOS */
+
+static void _job_process_cb(void *data);
+
+/**
+ * Initialise the Bridge DMA.
+ * \param user_data User data passed to any callback
+ * \param bridgedma_cb Bridge DMA interrupt callback
+ * \param deferred_cb DSR callback
+ * \return the newly created context
+ */
+phy_bridgedma_t *
+phy_bridgedma_init (void *user_data, phy_bridgedma_cb_t bridgedma_cb,
+ phy_deferred_cb_t deferred_cb)
+{
+ static phy_bridgedma_t bridgedma_ctx;
+ static u32 enc_tab[256];
+
+ memset(&bridgedma_ctx, '\0', sizeof(bridgedma_ctx));
+ bridgedma_ctx.user_data = user_data;
+ bridgedma_ctx.bridgedma_cb = bridgedma_cb;
+ bridgedma_ctx.deferred_cb = deferred_cb;
+ bridgedma_ctx.status.stop = 1;
+ bridgedma_ctx.station = &my_station;
+
+ /* init the crc context */
+ bridgedma_ctx.crc_ctx.width = 32;
+ bridgedma_ctx.crc_ctx.generator = HPAV_CRC32_GENERATOR;
+ bridgedma_ctx.crc_ctx.init = HPAV_CRC32_INIT;
+ bridgedma_ctx.crc_ctx.refin = true;
+ bridgedma_ctx.crc_ctx.refout = true;
+ bridgedma_ctx.crc_ctx.xorout = 0xffffffff;
+ bridgedma_ctx.crc_ctx.reg_init = 0;
+ bridgedma_ctx.crc_ctx.table.t32 = enc_tab;
+ crc_init(&bridgedma_ctx.crc_ctx);
+
+#ifdef ECOS
+ /* register ISR et DSR to eCos */
+ cyg_drv_interrupt_create(PHY_HAL_INTERRUPT_BRIDGEDMA,
+ PHY_HAL_INTERRUPT_PRIORITY,
+ (cyg_addrword_t)&bridgedma_ctx,
+ _bridgedma_ecos_isr,
+ _bridgedma_ecos_dsr,
+ &bridgedma_ctx.interrupt_handle,
+ &bridgedma_ctx.interrupt);
+ cyg_drv_interrupt_attach(bridgedma_ctx.interrupt_handle);
+ cyg_drv_interrupt_unmask(PHY_HAL_INTERRUPT_BRIDGEDMA);
+#endif /* ECOS */
+ return &bridgedma_ctx;
+}
+
+
+/**
+ * Reset and uninitialise the Bridge DMA.
+ * \param ctx Bridge DMA context
+ */
+void
+phy_bridgedma_uninit (phy_bridgedma_t *ctx)
+{
+
+}
+
+static int _compute_duration(unsigned int length)
+{
+ return (length / BRIDGEDMA_BYTE_PER_TICK + 1
+ + (int) ((double)BRIDGEDMA_ADD_MAX_TICK * (rand() / (RAND_MAX + 1.0))));
+}
+
+/**
+ * Fragment a mac_frame data into one or several PBs.
+ * Mac frame data MUST NOT cross the mac frame buffer boundary (going at beginning of circular buffer)
+ * ie: we must have mac_ptr + mac_len <= end_of_mac_buffer
+ * \param pb_current pointer of pointer to first pb to store data
+ * \param pb_data_ptr pointer of pointer to data beginning into first pb
+ * \param pb_size size of pb data (128 or 512)
+ * \param mac_ptr pointer to mac_frame data
+ * \param mac_len length of mac frame data
+ * \param direction data copy direction: MACFRAME_TO_PB or PB_TO_MACFRAME
+ * \return 0
+ */
+
+#ifndef UNIT_TEST
+static int
+#else /* UNIT_TEST */
+int
+#endif /* UNIT_TEST */
+_pb_data_add(
+ blk_t **pb_current,
+ unsigned char **pb_data_ptr,
+ unsigned int pb_size,
+ unsigned char *mac_ptr,
+ unsigned int mac_len,
+ bridgedma_copy_dir_t direction)
+{
+ unsigned int mac_cnt;
+
+ mac_cnt = 0;
+
+ while((mac_len - mac_cnt) > pb_size - (*pb_data_ptr - (*pb_current)->data))
+ {
+ if(direction == MACFRAME_TO_PB)
+ memcpy(*pb_data_ptr, mac_ptr + mac_cnt, pb_size - (*pb_data_ptr - (*pb_current)->data));
+ else
+ memcpy(mac_ptr + mac_cnt, *pb_data_ptr, pb_size - (*pb_data_ptr - (*pb_current)->data));
+ mac_cnt += pb_size - (*pb_data_ptr - (*pb_current)->data);
+ *pb_current = (*pb_current)->next;
+ dbg_assert(*pb_current);
+ *pb_data_ptr = (*pb_current)->data;
+ }
+
+ if(direction == MACFRAME_TO_PB)
+ memcpy(*pb_data_ptr, mac_ptr + mac_cnt, mac_len - mac_cnt);
+ else
+ memcpy(mac_ptr + mac_cnt, *pb_data_ptr, mac_len - mac_cnt);
+ *pb_data_ptr += mac_len - mac_cnt;
+ return 0;
+}
+
+/**
+ * Process 1 job for fragmentation or reassembly
+ * \param ctx Bridge DMA context
+ * \param job job to process
+ * \return 0
+ * */
+static int
+_job_process(phy_bridgedma_t *ctx, phy_bridgedma_job_t *job)
+{
+ unsigned long duration;
+ netclock_id_t netclock_id;
+ unsigned char * mac_ptr, *data_ptr;
+ blk_t *pb_current;
+ unsigned long icv_final, icv_compare;
+
+ ctx->job_current = job;
+
+ dbg_assert(job->data_addr);
+ dbg_assert((job->header_len == 0) || (job->header_len == 2) || (job->header_len == 6));
+ dbg_assert((job->data_len >= 60) && (job->data_len <= 1518));
+ dbg_assert(job->first_pb_desc);
+ dbg_assert(job->first_pb_offset < job->segment_len);
+ dbg_assert((job->segment_len == 128) || (job->segment_len == 512));
+
+ /* check icv reset */
+ if(job->crc_reset)
+ ctx->icv_current = crc_compute_begin(&ctx->crc_ctx);
+
+ pb_current = job->first_pb_desc;
+ data_ptr = pb_current->data + job->first_pb_offset;
+ mac_ptr = job->data_addr;
+
+ if(job->direction == 0)
+ {
+ /* segmentation processing */
+
+ if(job->header_len > 0)
+ {
+ /* add header */
+ _pb_data_add(&pb_current,
+ &data_ptr,
+ job->segment_len,
+ (unsigned char *)&job->mf_header1, /* little endian !!! */
+ 2,
+ MACFRAME_TO_PB); /* we are in little endian for simulator... */
+ if(job->header_len == 6)
+ {
+ /* add ATS */
+ _pb_data_add(&pb_current,
+ &data_ptr,
+ job->segment_len,
+ ((unsigned char *)&job->mf_header1) + 2, /* little endian !!! */
+ 4,
+ MACFRAME_TO_PB); /* we are in little endian for simulator... */
+ if(!job->crc_error)
+ ctx->icv_current = crc_compute_continue_block(&ctx->crc_ctx, ctx->icv_current, ((unsigned char *)&job->mf_header1) + 2, 4);
+ }
+ }
+
+ /* check if mac data must be separated into 2 parts (circular buffer rollover) */
+ if((job->eth_buffer_mask != 0)
+ && (job->data_addr + job->data_len >= ETH_BUFFER_ADDR(job) + ETH_BUFFER_SIZE(job)))
+ {
+ _pb_data_add(&pb_current, &data_ptr, job->segment_len, job->data_addr, ETH_BUFFER_ADDR(job) + ETH_BUFFER_SIZE(job) - job->data_addr, MACFRAME_TO_PB);
+ if(!job->crc_error)
+ ctx->icv_current = crc_compute_continue_block(&ctx->crc_ctx, ctx->icv_current, job->data_addr, ETH_BUFFER_ADDR(job) + ETH_BUFFER_SIZE(job) - job->data_addr);
+ _pb_data_add(&pb_current, &data_ptr, job->segment_len, job->data_addr, ((unsigned long)(job->data_addr) + job->data_len) & ETH_BUFFER_MASK(job), MACFRAME_TO_PB);
+ if(!job->crc_error)
+ ctx->icv_current = crc_compute_continue_block(&ctx->crc_ctx, ctx->icv_current, job->data_addr, ((unsigned long)(job->data_addr) + job->data_len) & ETH_BUFFER_MASK(job));
+ }
+ else
+ {
+ _pb_data_add(&pb_current, &data_ptr, job->segment_len, job->data_addr, job->data_len, MACFRAME_TO_PB);
+ if(!job->crc_error)
+ ctx->icv_current = crc_compute_continue_block(&ctx->crc_ctx, ctx->icv_current, job->data_addr, job->data_len);
+ }
+ if(job->crc_store)
+ {
+ /* add crc */
+ if(!job->crc_error)
+ icv_final = crc_compute_end(&ctx->crc_ctx, ctx->icv_current);
+ else
+ icv_final = ctx->icv_substitution;
+ _pb_data_add(&pb_current, &data_ptr, job->segment_len, (unsigned char *)&icv_final, 4, MACFRAME_TO_PB); /* we are in little endian for simulator... */
+ }
+ if(job->append_zero)
+ {
+ /* add zeroed padding */
+ memset(data_ptr, '\0', job->segment_len - (data_ptr - pb_current->data));
+ }
+ }
+ else /* job->direction != 0 */
+ {
+ /* reassembly processing */
+ if(job->header_len > 0)
+ {
+ /* get the mac frame header */
+ _pb_data_add(&pb_current,
+ &data_ptr,
+ job->segment_len,
+ (unsigned char *)&job->mf_header1, /* little endian !!! */
+ 2,
+ PB_TO_MACFRAME); /* we are in little endian for simulator... */
+ if(job->header_len == 6)
+ {
+ /* add ATS */
+ _pb_data_add(&pb_current,
+ &data_ptr,
+ job->segment_len,
+ ((unsigned char *)&job->mf_header1) + 2, /* little endian */
+ 4,
+ PB_TO_MACFRAME); /* we are in little endian for simulator... */
+ ctx->icv_current = crc_compute_continue_block(&ctx->crc_ctx, ctx->icv_current, ((unsigned char *)&job->mf_header1) + 2, 4);
+ }
+ }
+
+ /* check if mac data must be separated into 2 parts (circular buffer rollover) */
+ if((job->eth_buffer_mask != 0)
+ && (job->data_addr + job->data_len >= ETH_BUFFER_ADDR(job) + ETH_BUFFER_SIZE(job)))
+ {
+ _pb_data_add(&pb_current, &data_ptr, job->segment_len, job->data_addr, ETH_BUFFER_ADDR(job) + ETH_BUFFER_SIZE(job) - job->data_addr, PB_TO_MACFRAME);
+ ctx->icv_current = crc_compute_continue_block(&ctx->crc_ctx, ctx->icv_current, job->data_addr, ETH_BUFFER_ADDR(job) + ETH_BUFFER_SIZE(job) - job->data_addr);
+ _pb_data_add(&pb_current, &data_ptr, job->segment_len, (unsigned char *)((unsigned long)job->data_addr & ~ETH_BUFFER_MASK(job)), ((unsigned long)(job->data_addr) + job->data_len) & ETH_BUFFER_MASK(job), PB_TO_MACFRAME);
+ ctx->icv_current = crc_compute_continue_block(&ctx->crc_ctx, ctx->icv_current, (unsigned char *)((unsigned long)job->data_addr & ~ETH_BUFFER_MASK(job)), ((unsigned long)(job->data_addr) + job->data_len) & ETH_BUFFER_MASK(job));
+ }
+ else
+ {
+ _pb_data_add(&pb_current, &data_ptr, job->segment_len, job->data_addr, job->data_len, PB_TO_MACFRAME);
+ ctx->icv_current = crc_compute_continue_block(&ctx->crc_ctx, ctx->icv_current, job->data_addr, job->data_len);
+ }
+ if(job->crc_store)
+ {
+ /* check crc */
+ icv_final = crc_compute_end(&ctx->crc_ctx, ctx->icv_current);
+ _pb_data_add(&pb_current, &data_ptr, job->segment_len, (unsigned char *)&icv_compare, 4, PB_TO_MACFRAME); /* we are in little endian for simulator... */
+ job->crc_error = (icv_compare != icv_final);
+ }
+ }
+
+ /* schedule the process duration */
+ duration = _compute_duration(job->data_len + job->header_len + sizeof(unsigned long));
+ if (-1 == netclock_schedule(ctx->station->netclock,
+ &ctx->netclock_cb,
+ NETWORK_CLOCK_TYPE_STATION,
+ ctx->station->current_tick_tck + duration,
+ _job_process_cb,
+ ctx,
+ &netclock_id))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when scheduling a netclock message", errno);
+ }
+
+ return 0;
+}
+
+static void _job_process_cb(void *data)
+{
+ struct phy_bridgedma_t *bridgedma_ctx;
+
+ dbg_assert(data);
+ bridgedma_ctx = (struct phy_bridgedma_t *)data;
+
+ /* set interrupt raising */
+ if(bridgedma_ctx->job_current->job_it)
+ maximus_pending_isrs |= (1 << PHY_HAL_INTERRUPT_BRIDGEDMA);
+
+ /* check if we are the last job */
+ if(bridgedma_ctx->job_current == bridgedma_ctx->job_last)
+ {
+ /* last job: stop the process */
+ bridgedma_ctx->job_first = NULL;
+ bridgedma_ctx->job_current = NULL;
+ bridgedma_ctx->job_last = NULL;
+
+ /* refresh bridge dma status */
+ bridgedma_ctx->status.running = 0;
+ bridgedma_ctx->status.stop = 1;
+ }
+ else
+ {
+ /* not the last job: process next */
+ dbg_assert(bridgedma_ctx->job_current->next);
+ bridgedma_ctx->job_current = bridgedma_ctx->job_current->next;
+ _job_process(bridgedma_ctx, bridgedma_ctx->job_current);
+ }
+
+ return;
+}
+
+/**
+ * Enqueue and start a list of jobs.
+ * \param ctx Bridge DMA context
+ * \param job_first first job to enqueue
+ * \param job_last last job to enqueue
+ *
+ * The new jobs are added to the Bridge DMA queue and the Bridge DMA is
+ * restarted if it was stopped. The \c last flag must be set in the last
+ * enqueued job.
+ */
+void
+phy_bridgedma_start (phy_bridgedma_t *ctx, phy_bridgedma_job_t *job_first,
+ phy_bridgedma_job_t *job_last)
+{
+ dbg_assert(ctx);
+ dbg_assert(job_first);
+ dbg_assert(job_last);
+
+ /* check current job processing */
+ if(ctx->job_first == NULL)
+ {
+ /* no more job in queue */
+ ctx->job_first = job_first;
+ ctx->job_current = job_first;
+ ctx->job_last = job_last;
+ memset(&ctx->status, '\0', sizeof(phy_bridgedma_status_t));
+ ctx->status.running = 1;
+ _job_process(ctx, ctx->job_current);
+ }
+ else
+ {
+ /* there are still job inside */
+ dbg_assert(ctx->job_last);
+ ctx->job_last->next = job_first;
+ ctx->job_last = job_last;
+ }
+
+ return;
+}
diff --git a/cesar/hal/phy/maximus/src/maximus_pbdma.c b/cesar/hal/phy/maximus/src/maximus_pbdma.c
new file mode 100644
index 0000000000..7da493ca4d
--- /dev/null
+++ b/cesar/hal/phy/maximus/src/maximus_pbdma.c
@@ -0,0 +1,187 @@
+/* Cesar project {{{
+ *
+ * Copyright (C) 2007 Spidcom
+ *
+ * <<<Licence>>>
+ *
+ * }}} */
+/**
+ * \file hal/phy/maximus/src/maximus_pbdma.c
+ * \brief HAL Phy PB DMA functions for Maximus.
+ * \ingroup hal_phy_maximus
+ */
+
+#include "common/std.h"
+#include "hal/phy/maximus/inc/maximus_phy_ctx.h"
+#include "mac/common/defs.h" // for 'MAC_MAX_PB_PER_MPDU'
+#include <errno.h>
+
+
+/**
+ * Start a PB transfer.
+ * \param ctx phy context
+ * \param bypass_aes do not encrypt or decrypt using AES
+ * \param iv three first AES initialisation vector words
+ * \param nek AES network encryption key
+ * \param nb_total total number of PB
+ * \param nb_ready number of ready descriptors
+ * \param nb_pb_it number of the PB after which an interrupt is triggered
+ * \param first_pb first PB descriptor
+ * set errno to:
+ * - EINVAL if ctx or first_pb are null, or if arguments are out-of-range or incoherent
+ *
+ * The transfer will start when the reception or the transmission starts.
+ */
+void
+phy_pbdma_start (phy_t *ctx, bool bypass_aes, const u32 iv[3],
+ const u32 nek[4], uint nb_total, uint nb_ready,
+ uint nb_pb_it, phy_pb_t *first_pb)
+{
+ dbg_assert_ptr(ctx);
+ dbg_assert(MAC_MAX_PB_PER_MPDU >= nb_total);
+ dbg_assert(nb_total >= nb_ready);
+ dbg_assert(nb_total >= nb_pb_it);
+ dbg_assert_ptr(first_pb);
+ MAXIMUS_PHY_TRACE (PBDMA_START, bypass_aes, nb_total, nb_ready, nb_pb_it);
+ if ((NULL == ctx)
+ || (MAC_MAX_PB_PER_MPDU < nb_total)
+ || (nb_total < nb_ready)
+ || (nb_total < nb_pb_it)
+ || (NULL == first_pb))
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ ctx->pbdma.bypass_aes = bypass_aes;
+ if (!bypass_aes)
+ {
+ memcpy(ctx->pbdma.iv, iv, 3*sizeof(u32));
+ memcpy(ctx->pbdma.nek, nek, 4*sizeof(u32));
+ }
+ else
+ {
+ memset(ctx->pbdma.iv, '\0', 3*sizeof(u32));
+ memset(ctx->pbdma.nek, '\0', 4*sizeof(u32));
+ }
+ ctx->pbdma.nb_total = nb_total;
+ ctx->pbdma.nb_ready = nb_ready;
+ ctx->pbdma.nb_pb_it = nb_pb_it;
+ ctx->pbdma.first_pb = first_pb;
+
+ // reset current PB descriptor in case of Rx
+ ctx->pbdma.current_pb = &ctx->pbdma.first_pb->pb_rx;
+ }
+}
+
+
+/**
+ * Update PB transfer counters.
+ * \param ctx phy context
+ * \param nb_ready number of ready descriptors
+ * \param nb_pb_it number of the PB after which an interrupt is triggered
+ * set errno to:
+ * - EINVAL if ctx is null, or if arguments are incoherent
+ */
+void
+phy_pbdma_update (phy_t *ctx, uint nb_ready, uint nb_pb_it)
+{
+ dbg_assert_ptr(ctx);
+ dbg_assert(nb_ready >= ctx->pbdma.nb_ready);
+ dbg_assert(nb_pb_it <= ctx->pbdma.nb_total);
+ dbg_assert(nb_ready <= ctx->pbdma.nb_total);
+ MAXIMUS_PHY_TRACE (PBDMA_UPDATE, nb_ready, nb_pb_it);
+ if ((NULL == ctx)
+ || (nb_ready < ctx->pbdma.nb_ready)
+ || (nb_pb_it > ctx->pbdma.nb_total)
+ || (nb_ready > ctx->pbdma.nb_total))
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ ctx->pbdma.nb_ready = nb_ready;
+ ctx->pbdma.nb_pb_it = nb_pb_it;
+ }
+}
+
+
+/**
+ * Retrieve last used PB descriptor.
+ * \param ctx phy context
+ * \return last used PB descriptor
+ * set errno to:
+ * - EINVAL if ctx is null
+ */
+phy_pb_t *
+phy_pbdma_get_tail (phy_t *ctx)
+{
+ phy_pb_t * tail = NULL;
+
+ dbg_assert_ptr(ctx);
+ if (NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ // PHY context current PB points to the last valid received PB
+ tail = PARENT_OF(phy_pb_t, pb_rx, ctx->pbdma.current_pb);
+ }
+
+ return tail;
+}
+
+
+/**
+ * Start a channel data transfer.
+ * \param ctx phy context
+ * \param first_chandata first transfer descriptor
+ * set errno to:
+ * - EINVAL if ctx or first_chandata are null, or if first_chandata->type equals 0
+ *
+ * If a PB transfer is set up, this transfer will only start after PB transfer
+ * completion.
+ */
+void
+phy_pbdma_start_chandata (phy_t *ctx, phy_chandata_t *first_chandata)
+{
+ dbg_assert_ptr(ctx);
+ dbg_assert_ptr(first_chandata);
+ dbg_assert((PHY_CHANDATA_TYPE_NO_MEMORY_READ < first_chandata->type) && (PHY_CHANDATA_TYPE_NRJ_SYMBOL >= first_chandata->type));
+ MAXIMUS_PHY_TRACE (PBDMA_START_CHANDATA);
+ if ((NULL == ctx)
+ || (NULL == first_chandata)
+ || (PHY_CHANDATA_TYPE_NO_MEMORY_READ >= first_chandata->type) || (PHY_CHANDATA_TYPE_NRJ_SYMBOL < first_chandata->type))
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ ctx->pbdma.first_chandata = first_chandata;
+
+ // set chandata transfer
+ ctx->pbdma.chandata_transfer = true;
+ }
+}
+
+
+/**
+ * Return the address of the CRC bitmap registers.
+ * \param ctx phy context
+ * \return address of the first of the eight CRC bitmap registers
+ */
+volatile const u32 *
+phy_pbdma_get_crc_bitmap (phy_t *ctx)
+{
+ return ctx->pbdma.crc_bitmap;
+}
+
diff --git a/cesar/hal/phy/maximus/src/maximus_phy_access.c b/cesar/hal/phy/maximus/src/maximus_phy_access.c
new file mode 100644
index 0000000000..59096adfff
--- /dev/null
+++ b/cesar/hal/phy/maximus/src/maximus_phy_access.c
@@ -0,0 +1,961 @@
+/* Cesar project {{{
+ *
+ * Copyright (C) 2007 Spidcom
+ *
+ * <<<Licence>>>
+ *
+ * }}} */
+/**
+ * \file hal/phy/maximus/src/maximus_phy_access.c
+ * \brief HAL Phy access functions for Maximus.
+ * \ingroup hal_phy_maximus
+ */
+
+#include "common/std.h"
+#include "ecos/packages/hal/maximus/arch/current/include/hal_host_intr.h"
+#include "hal/phy/access.h"
+#include "host/station.h"
+#include "hal/phy/maximus/inc/maximus_phy_ctx.h"
+#include "hal/phy/maximus/inc/maximus_phy_access.h"
+#include "hal/phy/maximus/inc/maximus_phy_ctrl.h"
+#include "hal/phy/maximus/inc/maximus_interrupts.h"
+#include "mac/common/timings.h" // for 'MAC_SLOT_TCK'
+#include <string.h> // for 'memset'
+#include <errno.h>
+
+/**
+ * Maximus access backoff start callback called when the corresponding netclock message is received.
+ * \param data pointer to phy_t structure.
+ * set errno to:
+ * - EINVAL if data is null
+ * - EPROTO if medium state is not idle
+ * if 'netclock_schedule()' fails, it sets errno
+ */
+void maximus_phy_access_backoff_cb (void *data)
+{
+ dbg_assert_ptr(data);
+ if (NULL == data)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ phy_t *ctx = (phy_t*)data;
+
+ dbg_assert_ptr(ctx);
+ if (NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ dbg_assert(MAXIMUS_PHY_MEDIUM_IDLE == ctx->access.medium_state);
+ if (MAXIMUS_PHY_MEDIUM_IDLE != ctx->access.medium_state)
+ {
+ errno = EPROTO;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d because cannot start the PRP if medium state is not IDLE", __FUNCTION__, errno);
+ }
+ else
+ {
+ MAXIMUS_PHY_TRACE (M_PRP0, my_station.current_tick_tck);
+
+ // update medium state
+ ctx->access.medium_state = MAXIMUS_PHY_MEDIUM_PRS0;
+
+ // reset cap medium
+ ctx->access.cap_medium = 0;
+
+ // reset PRS0 = 0
+ ctx->access.prs0 = false;
+
+ // reset slot counter
+ ctx->access.slot_count = 0;
+
+ // reset TX blocked
+ ctx->control.tx_blocked_on_false_alarm = false;
+
+ /* Send a netclock message to Maximus simulator scheduled in a slot time,
+ * to increment slot counter. */
+
+ if (-1 == netclock_schedule(my_station.netclock,
+ ctx->access.backoff_slot_count_netclock_cb,
+ NETWORK_CLOCK_TYPE_STATION,
+ my_station.current_tick_tck + MAC_SLOT_TCK,
+ &maximus_phy_access_slot_count_cb,
+ (void*)ctx,
+ &ctx->access.backoff_slot_count_netclock_id))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when scheduling a netclock message", errno);
+ }
+
+ /* Send a netclock message to Maximus simulator scheduled in PRS detection time,
+ * to emit or read PRS0. */
+
+ if (-1 == netclock_schedule(my_station.netclock,
+ ctx->access.backoff_start_netclock_cb,
+ NETWORK_CLOCK_TYPE_STATION,
+ my_station.current_tick_tck + MAXIMUS_PHY_PRS_DETECTION_DELAY_TCK,
+ &maximus_phy_access_prs0_cb,
+ (void*)ctx,
+ &ctx->access.backoff_start_netclock_id))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when scheduling a netclock message", errno);
+ }
+ }
+ }
+ }
+}
+
+
+/**
+ * Maximus access slot count callback called when the corresponding netclock message is received.
+ * \param data pointer to phy_t structure.
+ * set errno to:
+ * - EINVAL if data is null
+ * - EPROTO if medium state is not PRS0, PRS1 or CW
+ * if 'netclock_schedule()' fails, it sets errno
+ */
+void
+maximus_phy_access_slot_count_cb (void *data)
+{
+ dbg_assert_ptr(data);
+ if (NULL == data)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ phy_t *ctx = (phy_t*)data;
+
+ dbg_assert((MAXIMUS_PHY_MEDIUM_PRS0 == ctx->access.medium_state)
+ || (MAXIMUS_PHY_MEDIUM_PRS1 == ctx->access.medium_state)
+ || (MAXIMUS_PHY_MEDIUM_CW == ctx->access.medium_state)
+ || (MAXIMUS_PHY_MEDIUM_WAIT_CONF == ctx->access.medium_state));
+ if ((MAXIMUS_PHY_MEDIUM_PRS0 != ctx->access.medium_state)
+ && (MAXIMUS_PHY_MEDIUM_PRS1 != ctx->access.medium_state)
+ && (MAXIMUS_PHY_MEDIUM_CW != ctx->access.medium_state)
+ && (MAXIMUS_PHY_MEDIUM_WAIT_CONF != ctx->access.medium_state))
+ {
+ errno = EPROTO;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d because medium state is not PRS0, PRS1, CW or WAIT_CONF", __FUNCTION__, errno);
+ }
+ else
+ {
+ // increment slot count
+ ctx->access.slot_count++;
+
+ MAXIMUS_PHY_TRACE (M_SLOT, my_station.current_tick_tck,
+ ctx->access.slot_count);
+
+ /* During PRP, send a netclock message to Maximus simulator scheduled in a slot time,
+ * to increment slot counter. */
+
+ if (-1 == netclock_schedule(my_station.netclock,
+ ctx->access.backoff_slot_count_netclock_cb,
+ NETWORK_CLOCK_TYPE_STATION,
+ my_station.current_tick_tck + MAC_SLOT_TCK,
+ &maximus_phy_access_slot_count_cb,
+ (void*)ctx,
+ &ctx->access.backoff_slot_count_netclock_id))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when scheduling a netclock message", errno);
+ }
+ }
+ }
+}
+
+
+/**
+ * Maximus access PRS0 callback called when the corresponding netclock message is received,
+ * i.e. when the PRS0 must be sent.
+ * \param data pointer to phy_t structure.
+ * set errno to:
+ * - EINVAL if data is null
+ * if 'sci_send()' fails, it sets errno
+ */
+void
+maximus_phy_access_prs0_cb (void *data)
+{
+ dbg_assert_ptr(data);
+ if (NULL == data)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ phy_t *ctx = (phy_t*)data;
+
+ /* According to the CAP value, PHY emits or reads PRS0 and PRS1:
+ * - if CAP = 3, PHY emits PRS0 (= 1) and PRS1 (= 1);
+ * - if CAP = 2, PHY emits PRS0 (= 1) and reads PRS1;
+ * - if CAP = 1, PHY reads PRS0:
+ * - if PRS0 = 0, PHY emits PRS1 (= 1);
+ * - if PRS0 = 1, PHY reads PRS1;
+ * - if CAP = 0, PHY reads PRS0 and PRS1. */
+
+ if (2 <= ctx->access.cap_sent)
+ {
+ /* PHY emits PRS0. */
+
+ // for phy sci message
+ sci_msg_t msg;
+
+ // init for phy sci message
+ memset(ctx->buffer, '\0', SCI_MSG_MAX_SIZE);
+
+ if (0 != sci_msg_init(&msg, ctx->buffer, SCI_MSG_MAX_SIZE))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when initializing SCI message", errno);
+ }
+ else
+ {
+ // for phy message
+ uint32_t iv[3];
+ uint32_t nek[4];
+ uint32_t pb_measurement[PHY_PB_MAX_NB];
+ uint32_t pb_header[PHY_PB_MAX_NB];
+ uint8_t prs0 = 1;
+
+ // init for phy message
+ memset(iv, '\0', 3*sizeof(uint32_t));
+ memset(nek, '\0', 4*sizeof(uint32_t));
+ memset(pb_measurement, '\0', PHY_PB_MAX_NB*sizeof(uint32_t));
+ memset(pb_header, '\0', PHY_PB_MAX_NB*sizeof(uint32_t));
+
+ /* Send phy message containing PRS0. */
+
+ // fill sci data
+ if ((int)sizeof(uint8_t) != sci_msg_push(&msg, sizeof(uint8_t)))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when pushing SCI message", errno);
+ }
+ else
+ {
+ memcpy(msg.data_begin, &prs0, sizeof(u8));
+
+ // fill phy and sci header
+ if ( (0 != maximus_phy_fill_hdr(ctx,
+ &msg,
+ PHY_TYPE_PRS,
+ PHY_MPDU_FORMAT_NONE,
+ 0, // pb_nb
+ 1, // msg_nb
+ (uint8_t)ctx->control.next_tx_param.fc_mode,
+ (uint8_t)ctx->control.next_tx_param.short_ppdu,
+ (uint8_t)ctx->control.next_tx_param.mod,
+ (uint8_t)ctx->control.next_tx_param.fecrate,
+ (uint8_t)ctx->control.next_tx_param.gil,
+ (uint8_t)ctx->control.next_tx_param.tonemap_index,
+ 0, // tx_id
+ PHY_FLAG_CRC_OK,
+ 0, // symbol_nb
+ iv,
+ nek,
+ pb_measurement,
+ pb_header))
+ || (0 != sci_fill_hdr(my_station.sci, &msg, SCI_MSG_TYPE_PHY, 0 /* flags */)) )
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when filling header", errno);
+ }
+ else
+ {
+ // send message
+ if (msg.length != sci_send(my_station.sci, &msg))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when sending the PRS0", errno);
+ }
+ }
+ }
+ }
+
+ // update cap medium
+ ctx->access.cap_medium = 2;
+ }
+
+ /* Send a netclock message to Maximus simulator scheduled in (a slot time - PRS detection time),
+ * to compute PRS0 result. */
+
+ if (-1 == netclock_schedule(my_station.netclock,
+ ctx->access.backoff_start_netclock_cb,
+ NETWORK_CLOCK_TYPE_STATION,
+ my_station.current_tick_tck + MAC_SLOT_TCK - MAXIMUS_PHY_PRS_DETECTION_DELAY_TCK,
+ &maximus_phy_access_prs0_result_cb,
+ (void*)ctx,
+ &ctx->access.backoff_start_netclock_id))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when scheduling a netclock message", errno);
+ }
+ }
+}
+
+
+/**
+ * Maximus access PRS0 result callback called when the corresponding netclock message is received,
+ * i.e. at the end of the PRS0.
+ * \param data pointer to phy_t structure.
+ * set errno to:
+ * - EINVAL if data is null
+ * if 'netclock_schedule()' fails, it sets errno
+ */
+void
+maximus_phy_access_prs0_result_cb (void *data)
+{
+ dbg_assert_ptr(data);
+ if (NULL == data)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ phy_t *ctx = (phy_t*)data;
+
+ MAXIMUS_PHY_TRACE (M_PRP1, my_station.current_tick_tck);
+
+ // update medium state
+ ctx->access.medium_state = MAXIMUS_PHY_MEDIUM_PRS1;
+
+ // if PHY read PRS0 = 1, we already know that PRP is lost
+ if (ctx->access.prs0)
+ {
+ // set prp result
+ ctx->access.prp_result = false;
+ }
+
+ /* Send a netclock message to Maximus simulator scheduled in PRS detection time,
+ * to emit or read PRS1. */
+
+ if (-1 == netclock_schedule(my_station.netclock,
+ ctx->access.backoff_start_netclock_cb,
+ NETWORK_CLOCK_TYPE_STATION,
+ my_station.current_tick_tck + MAXIMUS_PHY_PRS_DETECTION_DELAY_TCK,
+ &maximus_phy_access_prs1_cb,
+ (void*)ctx,
+ &ctx->access.backoff_start_netclock_id))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when scheduling a netclock message", errno);
+ }
+ }
+}
+
+
+/**
+ * Maximus access PRS1 callback called when the corresponding netclock message is received,
+ * i.e. when the PRS1 must be sent.
+ * \param data pointer to phy_t structure.
+ * set errno to:
+ * - EINVAL if data is null
+ * if 'sci_send()' or 'netclock_schedule()' fails, it sets errno
+ */
+void
+maximus_phy_access_prs1_cb (void *data)
+{
+ dbg_assert_ptr(data);
+ if (NULL == data)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ phy_t *ctx = (phy_t*)data;
+
+ /* According to the CAP value, PHY emits or reads PRS0 and PRS1:
+ * - if CAP = 3, PHY emits PRS0 (= 1) and PRS1 (= 1);
+ * - if CAP = 2, PHY emits PRS0 (= 1) and reads PRS1;
+ * - if CAP = 1, PHY reads PRS0:
+ * - if PRS0 = 0, PHY emits PRS1 (= 1);
+ * - if PRS0 = 1, PHY reads PRS1;
+ * - if CAP = 0, PHY reads PRS0 and PRS1. */
+
+ if ((3 == ctx->access.cap_sent)
+ || ((1 == ctx->access.cap_sent)
+ && !ctx->access.prs0))
+ {
+ /* PHY emits PRS1. */
+
+ // for phy sci message
+ sci_msg_t msg;
+
+ // init for phy sci message
+ memset(ctx->buffer, '\0', SCI_MSG_MAX_SIZE);
+
+ if (0 != sci_msg_init(&msg, ctx->buffer, SCI_MSG_MAX_SIZE))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when initializing SCI message", errno);
+ }
+ else
+ {
+ // for phy message
+ uint32_t iv[3];
+ uint32_t nek[4];
+ uint32_t pb_measurement[PHY_PB_MAX_NB];
+ uint32_t pb_header[PHY_PB_MAX_NB];
+ u8 prs1 = 1;
+
+ // init for phy message
+ memset(iv, '\0', 3*sizeof(uint32_t));
+ memset(nek, '\0', 4*sizeof(uint32_t));
+ memset(pb_measurement, '\0', PHY_PB_MAX_NB*sizeof(uint32_t));
+ memset(pb_header, '\0', PHY_PB_MAX_NB*sizeof(uint32_t));
+
+ /* Send phy message containing PRS1. */
+
+ // fill sci data
+ if ((int)sizeof(uint8_t) != sci_msg_push(&msg, sizeof(uint8_t)))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when pushing SCI message", errno);
+ }
+ else
+ {
+ memcpy(msg.data_begin, &prs1, sizeof(u8));
+
+ // fill phy and sci header
+ if ( (0 != maximus_phy_fill_hdr(ctx,
+ &msg,
+ PHY_TYPE_PRS,
+ PHY_MPDU_FORMAT_NONE,
+ 0, // pb_nb
+ 1, // msg_nb
+ (uint8_t)ctx->control.next_tx_param.fc_mode,
+ (uint8_t)ctx->control.next_tx_param.short_ppdu,
+ (uint8_t)ctx->control.next_tx_param.mod,
+ (uint8_t)ctx->control.next_tx_param.fecrate,
+ (uint8_t)ctx->control.next_tx_param.gil,
+ (uint8_t)ctx->control.next_tx_param.tonemap_index,
+ 0, // tx_id
+ PHY_FLAG_CRC_OK,
+ 0, // symbol_nb
+ iv,
+ nek,
+ pb_measurement,
+ pb_header))
+ || (0 != sci_fill_hdr(my_station.sci, &msg, SCI_MSG_TYPE_PHY, 0 /* flags */)) )
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when filling header", errno);
+ }
+ else
+ {
+ // send message
+ if (msg.length != sci_send(my_station.sci, &msg))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when sending the PRS1", errno);
+ }
+ }
+ }
+ }
+
+ // update cap medium
+ ctx->access.cap_medium |= 0x01;
+ }
+
+ /* Send a netclock message to Maximus simulator scheduled in (a slot time - PRS detection time - 1),
+ * to compute PRP result one tick before the beginning of the third slot (= first slot of CW). */
+
+ if (-1 == netclock_schedule(my_station.netclock,
+ ctx->access.backoff_start_netclock_cb,
+ NETWORK_CLOCK_TYPE_STATION,
+ my_station.current_tick_tck + MAC_SLOT_TCK - MAXIMUS_PHY_PRS_DETECTION_DELAY_TCK - 1,
+ &maximus_phy_access_prp_result_cb,
+ (void*)ctx,
+ &ctx->access.backoff_start_netclock_id))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when scheduling a netclock message", errno);
+ }
+ }
+}
+
+
+/**
+ * Maximus access PRP result callback called when the corresponding netclock message is received,
+ * i.e. at the end of the PRS1.
+ * \param data pointer to phy_t structure.
+ * set errno to:
+ * - EINVAL if data is null
+ */
+void
+maximus_phy_access_prp_result_cb (void *data)
+{
+ dbg_assert_ptr(data);
+ if (NULL == data)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ phy_t *ctx = (phy_t*)data;
+
+ MAXIMUS_PHY_TRACE (M_PRP_END, my_station.current_tick_tck,
+ ctx->access.cap_sent, ctx->access.cap_medium,
+ ctx->access.cap_mpdu);
+
+ // update medium state
+ ctx->access.medium_state = MAXIMUS_PHY_MEDIUM_CW;
+
+ // activate preamble detection
+ ctx->control.pre_detection = true;
+
+ /* If cap_sent = cap_medium (cap_sent can be different of cap_mpdu if the CAP was updated during the PRS0),
+ * OR cap_mpdu ≥ cap_medium, PRP is won. */
+
+ // compute prp result
+ if ((ctx->access.cap_sent == ctx->access.cap_medium)
+ || ((uint)ctx->access.cap_mpdu >= (uint)ctx->access.cap_medium))
+ {
+ ctx->access.prp_result = true;
+ }
+ else
+ {
+ ctx->access.prp_result = false;
+
+ /* If PRP is lost (i.e. the detected CAP is greater than the programmed one), PHY stops Tx. */
+
+ if (ctx->control.stop_tx_on_prp_lost)
+ {
+ maximus_phy_next_tx_frame_cancel(ctx);
+ maximus_phy_current_tx_frame_cancel(ctx);
+ }
+ }
+
+ ctx->access.backoff_start_netclock_id = 0;
+ }
+}
+
+
+/**
+ * Maximus access timer program callback called when the corresponding netclock message is received.
+ * set errno to:
+ * - EINVAL if data or ctx->control.access_cb are null
+ */
+void maximus_phy_access_timer_cb (void *data)
+{
+ /* If no PRE has been received, PHY raises an IT_ACCESS when access timer expires,
+ * i.e. this callback has to call the 'phy_access_cb'. */
+
+ dbg_assert_ptr(data);
+ if (NULL == data)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ phy_t *ctx = (phy_t*)data;
+
+ dbg_assert_ptr(ctx);
+ dbg_assert_ptr(ctx->control.access_cb);
+ if ((NULL == ctx)
+ || (NULL == ctx->control.access_cb))
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ if (MAXIMUS_PHY_MEDIUM_BUSY_RX != ctx->access.medium_state)
+ {
+ ctx->control.current_cb = ctx->control.access_cb;
+ maximus_pending_isrs |= (1 << PHY_HAL_INTERRUPT_PHY);
+ }
+
+ ctx->access.timer_program_netclock_id = 0;
+ }
+ }
+}
+
+
+/**
+ * Cancel the backoff procedure.
+ * \param ctx phy context.
+ * set errno to:
+ * - EINVAL if ctx is null
+ */
+void maximus_phy_access_backoff_cancel (phy_t *ctx)
+{
+ dbg_assert_ptr(ctx);
+ if(NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ if (0 != ctx->access.backoff_start_netclock_id)
+ {
+ /* Send a netclock message to Maximus simulator
+ * to unschedule the event sent in 'phy_access_backoff_start'
+ * according to ctx->access.backoff_start_netclok_id. */
+
+ if (-1 == netclock_unschedule(my_station.netclock, ctx->access.backoff_start_netclock_id))
+ {
+ if (ctx->warning_assert)
+ {
+ station_log(&my_station, STATION_LOG_WARNING, STATION_LOGTYPE_PHY,
+ "%s: errno = %d when unscheduling a netclock message", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when unscheduling a netclock message", errno);
+ }
+ }
+ ctx->access.backoff_start_netclock_id = 0;
+ }
+ }
+}
+
+
+/**
+ * Reset the slot count.
+ * \param ctx phy context.
+ * set errno to:
+ * - EINVAL if ctx is null
+ */
+void maximus_phy_access_slot_count_reset (phy_t *ctx)
+{
+ dbg_assert_ptr(ctx);
+ if(NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ /* Send a netclock message to Maximus simulator
+ * to unschedule the event sent in 'maximus_phy_access_backoff_cb' or 'maximus_phy_access_slot_count_cb',
+ * according to ctx->access.backoff_slot_count_netclok_id. */
+
+ if (0 != ctx->access.backoff_slot_count_netclock_id)
+ {
+ if (-1 == netclock_unschedule(my_station.netclock, ctx->access.backoff_slot_count_netclock_id))
+ {
+ if (ctx->warning_assert)
+ {
+ station_log(&my_station, STATION_LOG_WARNING, STATION_LOGTYPE_PHY,
+ "%s: errno = %d when unscheduling a netclock message", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when unscheduling a netclock message", errno);
+ }
+ }
+ ctx->access.backoff_slot_count_netclock_id = 0;
+ }
+ }
+}
+
+
+/**
+ * Start the backoff procedure.
+ * \param ctx phy context
+ * \param date PRP start date
+ * \param cap channel access priority
+ * set errno to:
+ * - EINVAL if ctx is null
+ * if 'netclock_schedule()' fails, it sets errno
+ *
+ * Reset the backoff procedure hardware to start a new backoff procedure at
+ * the given date with the given priority.
+ */
+void
+phy_access_backoff_start (phy_t *ctx, u32 date, uint cap)
+{
+ /* To prepare next Tx, CA starts the backoff procedure,
+ * defining a date to start PRP and giving the CAP. */
+
+ dbg_assert_ptr(ctx);
+ if (NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ MAXIMUS_PHY_TRACE (ACCESS_BACKOFF_START, date, cap);
+
+ // cancel the backoff procedure and reset the slot count
+ maximus_phy_access_backoff_cancel(ctx);
+ maximus_phy_access_slot_count_reset(ctx);
+
+ // update cap mpdu
+ ctx->access.cap_mpdu = cap;
+
+ // update cap sent
+ ctx->access.cap_sent = cap;
+
+ if (MAXIMUS_PHY_MEDIUM_CW == ctx->access.medium_state)
+ {
+ // reset medium state: consider that CW is finished
+ ctx->access.medium_state = MAXIMUS_PHY_MEDIUM_IDLE;
+ }
+
+ // reset prp result to 'true'
+ ctx->access.prp_result = true;
+
+ /* Send a netclock message to Maximus simulator scheduled at the given date. */
+
+ if (-1 == netclock_schedule(my_station.netclock,
+ ctx->access.backoff_start_netclock_cb,
+ NETWORK_CLOCK_TYPE_STATION,
+ maximus_phy_schedule_tick(date),
+ &maximus_phy_access_backoff_cb,
+ (void*)ctx,
+ &ctx->access.backoff_start_netclock_id))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when scheduling a netclock message", errno);
+ }
+ }
+}
+
+
+/**
+ * Change the cap to a new one.
+ * \param ctx phy context
+ * \param cap the new channel access priority
+ * set errno to:
+ * - EINVAL if ctx is null
+ *
+ * Update the hardware priority.
+ */
+void
+phy_access_backoff_update (phy_t *ctx, uint cap)
+{
+ /* CA can change the cap_sent to a new one:
+ * - if this occurs before the PRP, the new CAP can be used without any other precaution;
+ * - if this occurs during the PRS0, the new CAP can change the PRS1 only (if the new CAP is higher than the old one, the PRS1 will be 1, if it is lower, the PRS1 will be 0);
+ * - if this occurs after the PRS0, the CAP can not be changed. */
+
+ dbg_assert_ptr(ctx);
+ if (NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ MAXIMUS_PHY_TRACE (ACCESS_BACKOFF_UPDATE, cap);
+
+ // update cap mpdu without condition
+ ctx->access.cap_mpdu = (u8)cap;
+
+ if ((MAXIMUS_PHY_MEDIUM_PRS0 != ctx->access.medium_state)
+ && (MAXIMUS_PHY_MEDIUM_PRS1 != ctx->access.medium_state)
+ && (MAXIMUS_PHY_MEDIUM_CW != ctx->access.medium_state))
+ {
+ /* Set the given cap value into PHY context (cap_sent). */
+
+ ctx->access.cap_sent = (u8)cap;
+ }
+ else if (MAXIMUS_PHY_MEDIUM_PRS0 == ctx->access.medium_state)
+ {
+ if ((u8)cap > ctx->access.cap_sent)
+ {
+ ctx->access.cap_sent |= 0x01; // set PRS1 to 1
+ }
+ else if ((u8)cap < ctx->access.cap_sent)
+ {
+ ctx->access.cap_sent &= 0x02; // set PRS1 to 0
+ }
+ }
+ }
+}
+
+
+/**
+ * Return the number of slot counted.
+ * \param ctx phy context
+ * \return slot_count
+ * return 0 if it fails with errno =
+ * - EINVAL if ctx is null
+ */
+uint
+phy_access_backoff_slot_count (phy_t *ctx)
+{
+ /* CA can get the number of slots counted.
+ * PHY counts slots after the PRP until the next Tx or until a PRE is received.
+ * This counter is incremented at the beginning of each new slot.
+ * Return the slot_count value of PHY context. */
+
+ uint slot_count = 0;
+
+ dbg_assert_ptr(ctx);
+ if (NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ slot_count = ctx->access.slot_count;
+ }
+
+ return slot_count;
+}
+
+
+/**
+ * Return true if the PRP is won.
+ * \param ctx phy context
+ * \return true if CAP_sent == CAP_medium && CAP_mpdu >= CAP_medium
+ * return 'false' if it fails with errno =
+ * - EINVAL if ctx is null
+ */
+bool
+phy_access_backoff_prp_won (phy_t *ctx)
+{
+ /* CA can get the PRP result (won/lost).
+ * If cap_sent = cap_medium (cap_sent can be different of cap_mpdu if the CAP was updated during the PRS0)
+ * OR cap_mpdu ≥ cap_medium, PRP is won.
+ * Return the prp_result value of PHY context. */
+
+ bool prp_result = false;
+
+ dbg_assert_ptr(ctx);
+ if (NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ prp_result = ctx->access.prp_result;
+ }
+
+ return prp_result;
+}
+
+
+/**
+ * Program the access timer to the given date.
+ * \param ctx phy context
+ * \param date timer expiration date
+ * set errno to:
+ * - EINVAL if ctx is null
+ * if 'netclock_schedule()' fails, it sets errno
+ *
+ * ACCESS interrupt is disabled on preamble reception but not on PRP loss.
+ */
+void
+phy_access_timer_program (phy_t *ctx, u32 date)
+{
+ /* CA programs the access timer in order to precise the date at which CA decides to prepare its Tx. */
+
+ dbg_assert_ptr(ctx);
+ if (NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ MAXIMUS_PHY_TRACE (ACCESS_TIMER_PROGRAM, date);
+
+ phy_access_timer_cancel(ctx);
+
+ /* Send a netclock message to Maximus simulator scheduled at the given date. */
+
+ if (-1 == netclock_schedule(my_station.netclock,
+ ctx->access.timer_program_netclock_cb,
+ NETWORK_CLOCK_TYPE_STATION,
+ maximus_phy_schedule_tick(date),
+ &maximus_phy_access_timer_cb,
+ (void*)ctx,
+ &ctx->access.timer_program_netclock_id))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when scheduling a netclock message", errno);
+ }
+ }
+}
+
+
+/**
+ * Cancel the access timer.
+ * \param ctx phy context
+ * set errno to:
+ * - EINVAL if ctx is null
+ * if 'netclock_unschedule()' fails, it sets errno
+ */
+void
+phy_access_timer_cancel (phy_t *ctx)
+{
+ /* When modem is stopped, CA cancels the access timer. */
+
+ dbg_assert_ptr(ctx);
+ if(NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ MAXIMUS_PHY_TRACE (ACCESS_TIMER_CANCEL);
+
+ /* Send a netclock message to Maximus simulator
+ * to unschedule the event sent in the previous function ('phy_access_timer_program')
+ * according to ctx->access.timer_program_netclok_id. */
+
+ if (0 != ctx->access.timer_program_netclock_id)
+ {
+ if (-1 == netclock_unschedule(my_station.netclock, ctx->access.timer_program_netclock_id))
+ {
+ if (ctx->warning_assert)
+ {
+ station_log(&my_station, STATION_LOG_WARNING, STATION_LOGTYPE_PHY,
+ "%s: errno = %d when unscheduling a netclock message", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when unscheduling a netclock message", errno);
+ }
+ }
+ ctx->access.timer_program_netclock_id = 0;
+ }
+ }
+}
+
diff --git a/cesar/hal/phy/maximus/src/maximus_phy_ctrl.c b/cesar/hal/phy/maximus/src/maximus_phy_ctrl.c
new file mode 100644
index 0000000000..c3eeef5527
--- /dev/null
+++ b/cesar/hal/phy/maximus/src/maximus_phy_ctrl.c
@@ -0,0 +1,2873 @@
+/* Cesar project {{{
+ *
+ * Copyright (C) 2007 Spidcom
+ *
+ * <<<Licence>>>
+ *
+ * }}} */
+/**
+ * \file hal/phy/maximus/src/maximus_phy_ctrl.c
+ * \brief HAL Phy control functions for Maximus.
+ * \ingroup hal_phy_maximus
+ */
+
+#include "common/std.h"
+#include "ecos/packages/hal/maximus/arch/current/include/hal_host_intr.h"
+#include "host/fwd.h" // for 'phy_msg_hdr_t'
+#include "hal/phy/maximus/inc/maximus_phy_ctx.h"
+#include "hal/phy/maximus/inc/maximus_phy_ctrl.h"
+#include "hal/phy/maximus/inc/maximus_phy_access.h"
+#include "hal/phy/maximus/inc/maximus_interrupts.h"
+#include "hal/phy/maximus/dur/inc/maximus_dur.h"
+#include "hal/phy/maximus/inc/maximus_defs.h"
+#include "hal/phy/defs.h" // for 'PHY_PREAMBLE_DETECTION_DELAY_TCK'
+#include "lib/swap.h" // for 'ntohl'
+#include "mac/common/timings.h" // for 'MAC_PREAMBLE_TCK', 'MAC_PREAMBLE_HYBRID_TCK', 'MAC_FC_10_TCK' and 'MAC_FC_AV_TCK'
+#include "mac/common/defs.h" // for 'MAC_PB520_BYTES' and 'MAC_MAX_SYMB_PER_MPDU'
+#include <string.h> // for 'memset'
+#include <time.h> // for 'time()'
+#include <errno.h>
+
+#ifdef ECOS
+static cyg_uint32 _phy_ecos_isr(cyg_vector_t vector, cyg_addrword_t data)
+{
+ int is_call_dsr;
+ phy_t *ctx;
+ ctx = (phy_t *)data;
+ cyg_drv_interrupt_mask(PHY_HAL_INTERRUPT_PHY);
+ cyg_drv_interrupt_acknowledge(PHY_HAL_INTERRUPT_PHY);
+ /* check the callback to use */
+ dbg_assert(ctx->control.current_cb);
+ dbg_assert((ctx->control.current_cb == ctx->control.access_cb)
+ || (ctx->control.current_cb == ctx->control.access_conf_cb)
+ || (ctx->control.current_cb == ctx->control.rx_fc_cb)
+ || (ctx->control.current_cb == ctx->control.tx_false_alarm_cb)
+ || (ctx->control.current_cb == ctx->control.zero_cross_cb)
+ || (ctx->control.current_cb == ctx->control.extra_timer_cb));
+ if(ctx->control.current_cb == ctx->control.access_cb)
+ {
+ MAXIMUS_PHY_TRACE (ACCESS_CB);
+ is_call_dsr = (*ctx->control.access_cb)(ctx->control.user_data);
+ }
+ else if(ctx->control.current_cb == ctx->control.access_conf_cb)
+ {
+ MAXIMUS_PHY_TRACE (ACCESS_CONF_CB);
+ is_call_dsr = (*ctx->control.access_conf_cb)(ctx->control.user_data);
+ }
+ else if(ctx->control.current_cb == ctx->control.rx_fc_cb)
+ {
+ MAXIMUS_PHY_TRACE (RX_FC_CB, ctx->control.rx_fc_param.rx_date);
+ is_call_dsr = (*ctx->control.rx_fc_cb)(ctx->control.user_data, ctx->control.rx_fc_param.rx_date, ctx->control.rx_fc_param.fc_av);
+ }
+ else if(ctx->control.current_cb == ctx->control.tx_false_alarm_cb)
+ {
+ MAXIMUS_PHY_TRACE (TX_FALSE_ALARM_CB);
+ is_call_dsr = (*ctx->control.tx_false_alarm_cb)(ctx->control.user_data);
+ }
+ else if(ctx->control.current_cb == ctx->control.zero_cross_cb)
+ {
+ is_call_dsr = (*ctx->control.zero_cross_cb)(ctx->control.user_data, ctx->control.zero_cross_date);
+ }
+ else
+ {
+ MAXIMUS_PHY_TRACE (EXTRA_TIMER_CB);
+ is_call_dsr = (*ctx->control.extra_timer_cb)(ctx->control.extra_timer_user_data);
+ }
+ /* reset current_cb pointer */
+ ctx->control.current_cb = NULL;
+
+ if(is_call_dsr)
+ return CYG_ISR_CALL_DSR; // Cause DSR to be run
+ else
+ {
+ cyg_drv_interrupt_unmask(PHY_HAL_INTERRUPT_PHY);
+ return CYG_ISR_HANDLED;
+ }
+}
+
+static cyg_uint32 _pbdma_ecos_isr(cyg_vector_t vector, cyg_addrword_t data)
+{
+ /* nothing to do except calling the bridgedma callback */
+ phy_t *ctx;
+ ctx = (phy_t *)data;
+ cyg_drv_interrupt_mask(PHY_HAL_INTERRUPT_PBDMA);
+ cyg_drv_interrupt_acknowledge(PHY_HAL_INTERRUPT_PBDMA);
+ MAXIMUS_PHY_TRACE (PBDMA_CB, *(u32*) (void*) &ctx->pbdma.status_word);
+ if((*ctx->control.pbdma_cb)(ctx->control.user_data, (*(u32*)(void*)&(ctx->pbdma.status_word))))
+ {
+ return CYG_ISR_CALL_DSR; // Cause DSR to be run
+ }
+ else
+ {
+ cyg_drv_interrupt_unmask(PHY_HAL_INTERRUPT_PBDMA);
+ return CYG_ISR_HANDLED;
+ }
+}
+
+static void _phy_ecos_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
+{
+ /* nothing to do except calling the phy dsr */
+ phy_t *ctx;
+ ctx = (phy_t *)data;
+ MAXIMUS_PHY_TRACE (DEFERRED_CB);
+ (*ctx->control.deferred_cb)(ctx->control.user_data);
+ cyg_drv_interrupt_unmask(PHY_HAL_INTERRUPT_PHY);
+ cyg_drv_interrupt_unmask(PHY_HAL_INTERRUPT_PBDMA);
+ return;
+}
+#endif /* ECOS */
+
+
+typedef int (*maximus_phy_recv_function)(phy_t *ctx, sci_msg_t *msg);
+maximus_phy_recv_function maximus_phy_function_array[PHY_TYPE_NB] = { &maximus_phy_recv_none,
+ &maximus_phy_recv_preamble,
+ &maximus_phy_recv_fc_hybrid_mode,
+ &maximus_phy_recv_fc_av_only_mode,
+ &maximus_phy_recv_prs,
+ &maximus_phy_recv_mpdu_payload,
+ &maximus_phy_recv_tonemask,
+ &maximus_phy_recv_tonemap,
+ &maximus_phy_recv_noise,
+ &maximus_phy_recv_rx,
+ &maximus_phy_recv_zero_cross };
+
+
+/**
+ * Fill a blank phy header
+ * \param ctx current phy context
+ * \param msg pointer to sci message to fill header
+ * \param type type of message (PREAMBLE, FC_HYBRID_MODE, FC_AV_ONLY_MODE, PRS, MPDU_PAYLOAD, TONEMASK, TONEMAP, NOISE, RX, ZERO_CROSS)
+ * \param mpdu_format MPDU format of message (BEACON, SOF, SACK, RTS_CTS, SOUND, RSOF)
+ * \param pb_nb number of PBs into message
+ * \param fc_mode Frame Control mode
+ * \param short_ppdu short PPDU
+ * \param mod modulation
+ * \param fecrate FEC rate
+ * \param gil Guard Interval
+ * \param tonemap_index tone map index used for message
+ * \param tx_id transmission ID used for message
+ * \param flags flags of message
+ * \param symbol_nb number of symbols into message
+ * \param iv iv used for message
+ * \param nek nek used for message
+ * \param pb_measurement ber, halfit and crc measured for each PB of message
+ * \param pb_header PB header for each PB of message
+ * \return 0 if ok, -1 if it fails with errno =
+ * - EINVAL if ctx or msg are NULL, or if arguments are out-of-range
+ */
+int
+maximus_phy_fill_hdr(phy_t *ctx,
+ sci_msg_t *msg,
+ uint8_t type,
+ uint8_t mpdu_format,
+ uint8_t pb_nb,
+ uint8_t msg_nb,
+ uint8_t fc_mode,
+ uint8_t short_ppdu,
+ uint8_t mod,
+ uint8_t fecrate,
+ uint8_t gil,
+ uint8_t tonemap_index,
+ uint16_t tx_id,
+ uint16_t flags,
+ uint32_t symbol_nb,
+ uint32_t iv[3],
+ uint32_t nek[4],
+ uint32_t pb_measurement[PHY_PB_MAX_NB],
+ uint32_t pb_header[PHY_PB_MAX_NB])
+{
+ int ret = -1;
+
+ dbg_assert_ptr(ctx);
+ dbg_assert_ptr(msg);
+ dbg_assert((PHY_TYPE_NB > type) && (PHY_TYPE_NONE < type));
+ dbg_assert(PHY_MPDU_FORMAT_NB > mpdu_format);
+ dbg_assert(PHY_PB_MAX_NB >= pb_nb);
+ dbg_assert((0 != msg_nb) && (PHY_MSG_MAX_NB_PER_MPDU >= msg_nb));
+ dbg_assert(PHY_FC_MODE_NB > fc_mode);
+ dbg_assert((0 == short_ppdu) || (1 == short_ppdu));
+ dbg_assert(PHY_MOD_NONE >= mod);
+ dbg_assert(PHY_FEC_RATE_NONE >= fecrate);
+ dbg_assert(PHY_GIL_NONE >= gil);
+ dbg_assert(TONEMAP_INDEX_NB > tonemap_index);
+ dbg_assert(PHY_FLAG_MAX >= flags);
+ dbg_assert(MAC_MAX_SYMB_PER_MPDU >= symbol_nb);
+ dbg_assert_ptr(iv);
+ dbg_assert_ptr(nek);
+ dbg_assert_ptr(pb_measurement);
+ dbg_assert_ptr(pb_header);
+ if ((NULL == ctx)
+ || (NULL == msg)
+ || (PHY_TYPE_NB <= type)
+ || (PHY_MPDU_FORMAT_NB <= mpdu_format)
+ || (PHY_PB_MAX_NB < pb_nb)
+ || (0 == msg_nb) || (PHY_MSG_MAX_NB_PER_MPDU < msg_nb)
+ || (PHY_FC_MODE_NB <= fc_mode)
+ || ((0 == short_ppdu) && (1 == short_ppdu))
+ || (PHY_MOD_NONE < mod)
+ || (PHY_FEC_RATE_NONE < fecrate)
+ || (PHY_GIL_NONE < gil)
+ || (TONEMAP_INDEX_NB <= tonemap_index)
+ || (PHY_FLAG_MAX < flags)
+ || (MAC_MAX_SYMB_PER_MPDU < symbol_nb)
+ || (NULL == iv)
+ || (NULL == nek)
+ || (NULL == pb_measurement)
+ || (NULL == pb_header))
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ int i = 0;
+
+ // reserve space
+ if ((int)sizeof(phy_msg_hdr_t) != sci_msg_push(msg, sizeof(phy_msg_hdr_t)))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when pushing SCI message", errno);
+ }
+ else
+ {
+ /* Call 'htonl()' function for IV, NEK, PB measurement and PB header. */
+ for (i=0; i<3; i++)
+ {
+ iv[i] = htonl(iv[i]);
+ }
+ for (i=0; i<4; i++)
+ {
+ nek[i] = htonl(nek[i]);
+ }
+ for (i=0; i<PHY_PB_MAX_NB; i++)
+ {
+ pb_measurement[i] = htonl(pb_measurement[i]);
+ pb_header[i] = htonl(pb_header[i]);
+ }
+
+ // fill the reserved header
+ msg->hdr.phy = (phy_msg_hdr_t*)msg->data_begin;
+ memset(msg->hdr.phy->iv, '\0', 3*sizeof(uint32_t));
+ memset(msg->hdr.phy->nek, '\0', 4*sizeof(uint32_t));
+ memset(msg->hdr.phy->pb_measurement, '\0', PHY_PB_MAX_NB*sizeof(uint32_t));
+ memset(msg->hdr.phy->pb_header, '\0', PHY_PB_MAX_NB*sizeof(uint32_t));
+ msg->hdr.phy->version = PHY_VERSION;
+ msg->hdr.phy->type = type;
+ msg->hdr.phy->mpdu_format = mpdu_format;
+ msg->hdr.phy->pb_nb = pb_nb;
+ msg->hdr.phy->msg_nb = msg_nb;
+ msg->hdr.phy->fc_mode = fc_mode;
+ msg->hdr.phy->short_ppdu = short_ppdu;
+ msg->hdr.phy->mod = mod;
+ msg->hdr.phy->fecrate = fecrate;
+ msg->hdr.phy->gil = gil;
+ msg->hdr.phy->tonemap_index = tonemap_index;
+ msg->hdr.phy->reserved = 0;
+ msg->hdr.phy->tx_id = htons(tx_id);
+ msg->hdr.phy->flags = htons(flags);
+ msg->hdr.phy->symbol_nb = htonl(symbol_nb);
+ memcpy(msg->hdr.phy->iv, iv, 3*sizeof(uint32_t));
+ memcpy(msg->hdr.phy->nek, nek, 4*sizeof(uint32_t));
+ memcpy(msg->hdr.phy->pb_measurement, pb_measurement, pb_nb*sizeof(uint32_t));
+ memcpy(msg->hdr.phy->pb_header, pb_header, pb_nb*sizeof(uint32_t));
+
+ ret = 0;
+ }
+ }
+
+ return ret;
+}
+
+
+/**
+ * Process phy message received by the sci layer.
+ * This function must be registred to SCI layer with SCI_MSG_TYPE_PHY type.
+ * \param msg message to process
+ * \param phy phy current context
+ * \return 0 if ok, -1 if it fails with errno =
+ * - EINVAL if msg or phy are NULL
+ * - EPROTO if msg->hdr.phy is null, or if msg->hdr.phy values are out-of-range or incoherent
+ */
+int
+maximus_phy_recv (sci_msg_t *msg, void *phy)
+{
+ int ret = -1;
+ phy_t *ctx;
+ phy_msg_hdr_t phy_hdr;
+
+ dbg_assert_ptr(msg);
+ dbg_assert_ptr(msg->data_begin);
+ dbg_assert_ptr(phy);
+ if((NULL == msg) || (NULL == msg->data_begin) || (NULL == phy))
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ // set phy context
+ ctx = (phy_t *)phy;
+
+ // set header pointer in case of not already done
+ memcpy(&phy_hdr , msg->data_begin, sizeof(phy_msg_hdr_t));
+ if (sizeof(phy_msg_hdr_t) != sci_msg_pop(msg, sizeof(phy_msg_hdr_t)))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when poping SCI message", errno);
+ }
+ else
+ {
+ msg->hdr.phy = &phy_hdr;
+
+ /* Check phy header. */
+
+ dbg_assert_ptr(msg->hdr.phy);
+ dbg_assert(PHY_TYPE_NB > msg->hdr.phy->type);
+ dbg_assert(PHY_MPDU_FORMAT_NB > msg->hdr.phy->mpdu_format);
+ dbg_assert(PHY_PB_MAX_NB >= msg->hdr.phy->pb_nb);
+ dbg_assert((0 != msg->hdr.phy->msg_nb) && (PHY_MSG_MAX_NB_PER_MPDU >= msg->hdr.phy->msg_nb));
+ dbg_assert(PHY_FC_MODE_NONE >= msg->hdr.phy->fc_mode);
+ dbg_assert((0 == msg->hdr.phy->short_ppdu) || (1 == msg->hdr.phy->short_ppdu));
+ dbg_assert(PHY_MOD_NONE >= msg->hdr.phy->mod);
+ dbg_assert(PHY_FEC_RATE_NONE >= msg->hdr.phy->fecrate);
+ dbg_assert(PHY_GIL_NONE >= msg->hdr.phy->gil);
+ dbg_assert(TONEMAP_INDEX_NB > msg->hdr.phy->tonemap_index);
+ dbg_assert(PHY_FLAG_MAX >= ntohs(msg->hdr.phy->flags));
+ dbg_assert(MAC_MAX_SYMB_PER_MPDU >= ntohl(msg->hdr.phy->symbol_nb));
+ dbg_assert_ptr(msg->hdr.phy->iv);
+ dbg_assert_ptr(msg->hdr.phy->nek);
+ dbg_assert_ptr(msg->hdr.phy->pb_measurement);
+ dbg_assert_ptr(msg->hdr.phy->pb_header);
+ if ((NULL == msg->hdr.phy)
+ || (PHY_TYPE_NB <= msg->hdr.phy->type)
+ || (PHY_MPDU_FORMAT_NB <= msg->hdr.phy->mpdu_format)
+ || (PHY_PB_MAX_NB < msg->hdr.phy->pb_nb)
+ || (0 == msg->hdr.phy->msg_nb) || (PHY_MSG_MAX_NB_PER_MPDU < msg->hdr.phy->msg_nb)
+ || (PHY_FC_MODE_NONE < msg->hdr.phy->fc_mode)
+ || ((0 != msg->hdr.phy->short_ppdu) && (1 != msg->hdr.phy->short_ppdu))
+ || (PHY_MOD_NONE < msg->hdr.phy->mod)
+ || (PHY_FEC_RATE_NONE < msg->hdr.phy->fecrate)
+ || (PHY_GIL_NONE < msg->hdr.phy->gil)
+ || (TONEMAP_INDEX_NB <= msg->hdr.phy->tonemap_index)
+ || (PHY_FLAG_MAX < ntohs(msg->hdr.phy->flags))
+ || (MAC_MAX_SYMB_PER_MPDU < ntohl(msg->hdr.phy->symbol_nb))
+ || (NULL == msg->hdr.phy->iv)
+ || (NULL == msg->hdr.phy->nek)
+ || (NULL == msg->hdr.phy->pb_measurement)
+ || (NULL == msg->hdr.phy->pb_header))
+ {
+ errno = EPROTO;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ int i = 0;
+
+ /* Read flags. */
+ // message encryption
+ bool encrypted = (PHY_FLAG_ENCRYPTED == (ntohs(msg->hdr.phy->flags) & PHY_FLAG_ENCRYPTED));
+ // CRC
+ bool crc_ok = (PHY_FLAG_CRC_OK == (ntohs(msg->hdr.phy->flags) & PHY_FLAG_CRC_OK));
+ // pb size
+ ctx->control.rx_param.pb_size = (PHY_FLAG_PB512 == (ntohs(msg->hdr.phy->flags) & PHY_FLAG_PB512)) ? PHY_PB_SIZE_520 : PHY_PB_SIZE_136;
+
+ /* Call 'ntohl()' function for IV, NEK, PB measurement and PB header. */
+ for (i=0; i<3; i++)
+ {
+ msg->hdr.phy->iv[i] = ntohl(msg->hdr.phy->iv[i]);
+ }
+ for (i=0; i<4; i++)
+ {
+ msg->hdr.phy->nek[i] = ntohl(msg->hdr.phy->nek[i]);
+ }
+ for (i=0; i<PHY_PB_MAX_NB; i++)
+ {
+ msg->hdr.phy->pb_measurement[i] = ntohl(msg->hdr.phy->pb_measurement[i]);
+ msg->hdr.phy->pb_header[i] = ntohl(msg->hdr.phy->pb_header[i]);
+ }
+
+ // handle message only if:
+ if ((!encrypted // - message is not encrypted
+ || (encrypted // or message is encrypted with a correct iv
+ && (0 == memcmp(msg->hdr.phy->iv, ctx->pbdma.iv, 3*sizeof(uint32_t)))))
+ && (msg->hdr.phy->tonemap_index == ctx->control.rx_param.tonemap_index) // - and received tonemap index is correct
+ && crc_ok) // - and crc is ok
+ {
+ // check nek
+ if (encrypted // message is encrypted with an incorrect nek
+ && (0 != memcmp(msg->hdr.phy->nek, ctx->pbdma.nek, 4*sizeof(uint32_t))))
+ {
+ if (ctx->warning_assert)
+ {
+ errno = EPROTO;
+ station_log(&my_station, STATION_LOG_WARNING, STATION_LOGTYPE_PHY,
+ "%s: errno = %d because recv a PHY msg with an incorrect NEK", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d because recv a PHY msg with an incorrect NEK", errno);
+ }
+ else
+ {
+ // modify message contents
+ lib_rnd_buffer(ctx->control.rnd, msg->data_begin, (uint)msg->length);
+ }
+ }
+
+ // depending on the phy header type, different actions have to be done
+ ret = (*(maximus_phy_function_array[msg->hdr.phy->type]))(ctx, msg);
+ }
+ else
+ {
+ errno = EPROTO;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d because recv a PHY msg with a bad PHY hdr config", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d because recv a PHY msg with a bad PHY hdr config", errno);
+ }
+ }
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * Process phy message received by the sci layer, depending on the phy header type.
+ * \param ctx phy current context
+ * \param msg message to process
+ * \return 0 if ok, -1 if if it fails with errno =
+ * - EINVAL if ctx, msg, msg->sci_hdr or ctx->control.rx_fc_cb are null
+ * - EPROTO if msg->hdr.phy->type is incorrect, if msg->hdr.phy values are incorrect, incoherent or out-of-range,
+ * if recv MPDU payload but medium state is not RX or RX has not been prepared,
+ * or if chandata is null
+ */
+
+int
+maximus_phy_recv_none (phy_t *ctx, sci_msg_t *msg)
+{
+ errno = EPROTO;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d because PHY hdr type is incorrect", errno);
+ return -1;
+}
+
+int
+maximus_phy_recv_preamble (phy_t *ctx, sci_msg_t *msg)
+{
+ /* When a preamble is received (PHY_PREAMBLE_DETECTION_DELAY_TCK after the beginning of the preamble),
+ * the medium state becomes MAXIMUS_PHY_MEDIUM_WAIT_CONF.
+ * A netclock event is scheduled at the end of the preamble reception, depending on the RX param FC mode:
+ * - in Hybrid Mode, preamble duration equals to 1152 ticks 25 Mhz;
+ * - in AV-Only Mode, preamble duration equals to 1280 ticks 25 Mhz.
+ * The netclock event scheduled date is calculated as following:
+ * current date + MAC_PREAMBLE_(HYBRID_)TCK - PHY_PREAMBLE_DETECTION_DELAY_TCK + MAXIMUS_PHY_PREAMBLE_RECEPTION_DELAY_TCK (= 5 μs)
+ * A boolean wrong_preamble equals to the PHY_FLAG_WRONG_PREAMBLE is given as data of the netclock message. */
+
+ static maximus_phy_recv_preamble_t recv_preamble_cb_data;
+
+ int ret = -1;
+
+ dbg_assert_ptr(ctx);
+ dbg_assert_ptr(msg);
+ dbg_assert_ptr(msg->hdr.phy);
+ dbg_assert_ptr(msg->sci_hdr);
+ dbg_assert(0 != ntohs(msg->hdr.phy->tx_id));
+ if((NULL == ctx)
+ || (NULL == msg)
+ || (NULL == msg->hdr.phy)
+ || (NULL == msg->sci_hdr)
+ || (0 == ntohs(msg->hdr.phy->tx_id)))
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ if (ctx->control.pre_detection // PRE detection is set in 'phy_rx_activate' or 'maximus_phy_rx_activate_cb',
+ // and in 'maximus_phy_access_prp_result_cb' when the medium state becomes CW.
+ // PRE detection is deactivated in 'maximus_phy_recv_preamble_cb' and in 'maximus_phy_tx_frame_cb'.
+ && (MAXIMUS_PHY_MEDIUM_BUSY_RX != ctx->access.medium_state)
+ && (MAXIMUS_PHY_MEDIUM_BUSY_TX != ctx->access.medium_state)
+ && (MAXIMUS_PHY_MEDIUM_WAIT_CONF != ctx->access.medium_state)
+ && (0 == ctx->control.rx_param.recv_preamble_netclock_id))
+ {
+ // for netclock message
+ tick_t pre_duration = 0;
+ if ( (PHY_FC_MODE_HYBRID_1 == ctx->control.rx_param.fc_mode)
+ || (PHY_FC_MODE_HYBRID_2 == ctx->control.rx_param.fc_mode) )
+ {
+ pre_duration = MAC_PREAMBLE_HYBRID_TCK;
+ }
+ else if ( (PHY_FC_MODE_AV_1 == ctx->control.rx_param.fc_mode)
+ || (PHY_FC_MODE_AV_2 == ctx->control.rx_param.fc_mode) )
+ {
+ pre_duration = MAC_PREAMBLE_TCK;
+ }
+
+ // update medium state
+ ctx->access.medium_state = MAXIMUS_PHY_MEDIUM_WAIT_CONF;
+
+ // fill callback data
+ recv_preamble_cb_data.ctx = ctx;
+ recv_preamble_cb_data.wrong_preamble = (ntohs(msg->hdr.phy->flags) & PHY_FLAG_WRONG_PREAMBLE);
+ recv_preamble_cb_data.src_tx_id = ntohs(msg->hdr.phy->tx_id);
+ recv_preamble_cb_data.src_station_id = ntohs(msg->sci_hdr->station_id);
+
+ // send netclock message to Maximus
+ if (-1 == netclock_schedule(my_station.netclock,
+ ctx->control.rx_param.recv_preamble_netclock_cb,
+ NETWORK_CLOCK_TYPE_STATION,
+ maximus_phy_schedule_tick((u32)my_station.current_tick_tck + pre_duration - PHY_PREAMBLE_DETECTION_DELAY_TCK + MAXIMUS_PHY_PREAMBLE_RECEPTION_DELAY_TCK),
+ &maximus_phy_recv_preamble_cb,
+ (void*)&recv_preamble_cb_data,
+ &ctx->control.rx_param.recv_preamble_netclock_id))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when scheduling a netclock message", errno);
+ }
+
+ ret = 0;
+ }
+ else if (!ctx->control.pre_detection)
+ {
+ station_log(&my_station, STATION_LOG_WARNING, STATION_LOGTYPE_PHY,
+ "%s: recv a PREAMBLE but does not process it because PRE detection is deactivated", __FUNCTION__);
+
+ if (!ctx->warning_assert)
+ {
+ ret = 0;
+ }
+ }
+ else if ( (MAXIMUS_PHY_MEDIUM_BUSY_RX == ctx->access.medium_state)
+ || (MAXIMUS_PHY_MEDIUM_BUSY_TX == ctx->access.medium_state) )
+ {
+ station_log(&my_station, STATION_LOG_WARNING, STATION_LOGTYPE_PHY,
+ "%s: recv a PREAMBLE but does not process it because medium state is already BUSY", __FUNCTION__);
+
+ if (!ctx->warning_assert)
+ {
+ ret = 0;
+ }
+ }
+ else if ( (MAXIMUS_PHY_MEDIUM_WAIT_CONF == ctx->access.medium_state)
+ || (0 != ctx->control.rx_param.recv_preamble_netclock_id) )
+ {
+ station_log(&my_station, STATION_LOG_WARNING, STATION_LOGTYPE_PHY,
+ "%s: recv a PREAMBLE but does not process it because one is already being received", __FUNCTION__);
+
+ ret = 0;
+ }
+ }
+
+ return ret;
+}
+
+int
+maximus_phy_recv_fc_hybrid_mode (phy_t *ctx, sci_msg_t *msg)
+{
+ int ret = -1;
+
+ dbg_assert_ptr(ctx);
+ dbg_assert_ptr(msg);
+ if((NULL == ctx)
+ || (NULL == msg))
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ if (MAXIMUS_PHY_MEDIUM_BUSY_RX == ctx->access.medium_state)
+ {
+ memcpy(&ctx->control.rx_param.fc_10, msg->data_begin, 4);
+ if (4 != sci_msg_pop(msg, 4))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when poping SCI message", errno);
+ }
+ else
+ {
+ ret = maximus_phy_recv_fc_av_only_mode(ctx, msg);
+ }
+ }
+ else if (ctx->warning_assert)
+ {
+ station_log(&my_station, STATION_LOG_WARNING, STATION_LOGTYPE_PHY,
+ "%s: recv a FC 1.0 but does not process it because medium state is not RX", __FUNCTION__);
+ }
+ else
+ {
+ ret = 0;
+ }
+ }
+
+ return ret;
+}
+
+int
+maximus_phy_recv_fc_av_only_mode (phy_t *ctx, sci_msg_t *msg)
+{
+ /* If a PRE is detected before Tx starts, CW is lost.
+ * PHY stops Tx without raising an IT. */
+
+ int ret = -1;
+
+ dbg_assert_ptr(ctx);
+ dbg_assert_ptr(msg);
+ dbg_assert_ptr(msg->hdr.phy);
+ dbg_assert_ptr(msg->sci_hdr);
+ dbg_assert_ptr(ctx->control.rx_fc_cb);
+ if((NULL == ctx)
+ || (NULL == msg)
+ || (NULL == msg->hdr.phy)
+ || (NULL == msg->sci_hdr)
+ || (NULL == ctx->control.rx_fc_cb))
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ if (MAXIMUS_PHY_MEDIUM_BUSY_RX == ctx->access.medium_state)
+ {
+ // check transmission ID and station ID of the received frame control
+ dbg_assert(ctx->control.rx_param.src_tx_id == ntohs(msg->hdr.phy->tx_id));
+ dbg_assert(ctx->control.rx_param.src_station_id == ntohs(msg->sci_hdr->station_id));
+ if ((ctx->control.rx_param.src_tx_id == ntohs(msg->hdr.phy->tx_id))
+ && (ctx->control.rx_param.src_station_id == ntohs(msg->sci_hdr->station_id)))
+ {
+ MAXIMUS_PHY_TRACE (M_FC_DETECT, my_station.current_tick_tck);
+
+ u32 pre_duration = MAC_PREAMBLE_TCK; // in AV mode
+ u32 fc_duration = MAC_FC_AV_TCK; // in AV mode on 1 symbol
+
+ memcpy(ctx->control.rx_param.fc_av, msg->data_begin, 16);
+ if (16 != sci_msg_pop(msg, 16))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when poping SCI message", errno);
+ }
+ else
+ {
+ /* rx_date should be the date of preamble start, not current date. */
+ /* => Calculate rx_date. */
+
+ // calculate frame control duration and preamble duration,
+ // depending on AV/Hybrid mode and number of symbols
+ if ((PHY_FC_MODE_HYBRID_2 == msg->hdr.phy->fc_mode)
+ || (PHY_FC_MODE_AV_2 == msg->hdr.phy->fc_mode))
+ {
+ fc_duration *= 2; // on 2 symbols
+ }
+ if ((PHY_FC_MODE_HYBRID_1 == msg->hdr.phy->fc_mode)
+ || (PHY_FC_MODE_HYBRID_2 == msg->hdr.phy->fc_mode))
+ {
+ pre_duration = MAC_PREAMBLE_HYBRID_TCK; // in Hybrid mode
+ fc_duration += MAC_FC_10_TCK; // in Hybrid mode
+ }
+
+ // compute rx_date
+ ctx->control.rx_fc_param.rx_date = ntohl(msg->sci_hdr->netclock_low) - MAXIMUS_PHY_FC_RECEPTION_DELAY_TCK - fc_duration - pre_duration;
+
+ // PHY raises an IT_RX_FC by calling 'phy_rx_fc_cb'
+
+ // if the RX FC mode is hybrid and the TX FC mode is AV only, the FC 1.0 FCCS should be wrong => set fc_10 = (u32)-1
+ if (((PHY_FC_MODE_HYBRID_1 == ctx->control.rx_param.fc_mode)
+ || (PHY_FC_MODE_HYBRID_2 == ctx->control.rx_param.fc_mode))
+ && ((PHY_FC_MODE_AV_1 == msg->hdr.phy->fc_mode)
+ || (PHY_FC_MODE_AV_2 == msg->hdr.phy->fc_mode)))
+ {
+ ctx->control.rx_param.fc_10 = (u32)-1;
+ }
+
+ // if the RX FC mode is different from the TX FC mode, the FC AV FCCS_AV should be wrong => send fc_av = NULL
+ if (ctx->control.rx_param.fc_mode != msg->hdr.phy->fc_mode)
+ {
+ ctx->control.rx_fc_param.fc_av = NULL;
+ }
+ else
+ {
+ ctx->control.rx_fc_param.fc_av = ctx->control.rx_param.fc_av;
+ }
+
+ // set callback function to call
+ ctx->control.current_cb = ctx->control.rx_fc_cb;
+ maximus_pending_isrs |= (1 << PHY_HAL_INTERRUPT_PHY);
+
+ ret = 0;
+ }
+ }
+ else
+ {
+ errno = EPROTO;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: recv a FC with incorrect TX ID and/or station ID", __FUNCTION__);
+ }
+ }
+ else if (ctx->warning_assert)
+ {
+ station_log(&my_station, STATION_LOG_WARNING, STATION_LOGTYPE_PHY,
+ "%s: recv a FC AV but does not process it because medium state is not RX", __FUNCTION__);
+ }
+ else
+ {
+ ret = 0;
+ }
+ }
+
+ return ret;
+}
+
+int
+maximus_phy_recv_prs (phy_t *ctx, sci_msg_t *msg)
+{
+ int ret = -1;
+
+ dbg_assert_ptr(ctx);
+ dbg_assert_ptr(msg);
+ dbg_assert_ptr(msg->hdr.phy);
+ dbg_assert(0 == msg->hdr.phy->tx_id);
+ if((NULL == ctx)
+ || (NULL == msg)
+ || (NULL == msg->hdr.phy)
+ || (0 != msg->hdr.phy->tx_id))
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ if ((MAXIMUS_PHY_MEDIUM_PRS0 == ctx->access.medium_state)
+ || (MAXIMUS_PHY_MEDIUM_PRS1 == ctx->access.medium_state))
+ {
+ uint8_t prs = 0;
+ memcpy(&prs, msg->data_begin, sizeof(uint8_t));
+ if ((int)sizeof(uint8_t) != sci_msg_pop(msg, sizeof(uint8_t)))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when poping SCI message", errno);
+ }
+ else
+ {
+ dbg_assert(1 >= prs);
+ if (1 < prs)
+ {
+ errno = EPROTO;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ if (MAXIMUS_PHY_MEDIUM_PRS0 == ctx->access.medium_state)
+ {
+ if (1 == prs)
+ {
+ // update cap medium
+ ctx->access.cap_medium = 2;
+
+ // update PRS0 = 1
+ ctx->access.prs0 = true;
+ }
+ }
+ else // ctx->access.medium_state = MAXIMUS_PHY_MEDIUM_PRS1
+ {
+ // update cap medium
+ if (1 == prs)
+ {
+ if (!ctx->access.prs0) // PRS0 = 0
+ {
+ ctx->access.cap_medium = 1;
+ }
+ else // PRS0 = 1
+ {
+ ctx->access.cap_medium = 3;
+ }
+ }
+ }
+ ret = 0;
+ }
+ }
+ }
+ else if (ctx->warning_assert)
+ {
+ station_log(&my_station, STATION_LOG_WARNING, STATION_LOGTYPE_PHY,
+ "%s: recv a PRS symbol but does not process it because medium state is not PRS0 or PRS1", __FUNCTION__);
+ }
+ else
+ {
+ ret = 0;
+ }
+ }
+
+ return ret;
+}
+
+int
+maximus_phy_recv_mpdu_payload (phy_t *ctx, sci_msg_t *msg)
+{
+ int ret = -1;
+
+ dbg_assert_ptr(ctx);
+ dbg_assert_ptr(msg);
+ dbg_assert_ptr(msg->hdr.phy);
+ dbg_assert_ptr(msg->sci_hdr);
+ if((NULL == ctx)
+ || (NULL == msg)
+ || (NULL == msg->hdr.phy)
+ || (NULL == msg->sci_hdr))
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else if ((PHY_MPDU_FORMAT_NONE == msg->hdr.phy->mpdu_format)
+ || (((PHY_MPDU_FORMAT_BEACON
+ || PHY_MPDU_FORMAT_SOF
+ || PHY_MPDU_FORMAT_SOUND
+ || PHY_MPDU_FORMAT_RSOF) == msg->hdr.phy->mpdu_format)
+ && (0 == msg->hdr.phy->pb_nb)))
+ {
+ errno = EPROTO;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d because PHY hdr values are incorrect or incoherent", __FUNCTION__, errno);
+ }
+ else
+ {
+ dbg_assert_print(ctx->control.rx_prepared,
+ "receive MPDU payload but RX has not been prepared before");
+ if ( (ctx->control.rx_prepared) && (ctx->control.rx_param.short_ppdu) )
+ {
+ /* Cesar indicated that the HAL PHY will not receive an MPDU payload
+ * ('short_ppdu' has been set to 'true' in 'phy_rx_prepare()').
+ * However, if an MPDU payload is received,
+ * the HAL PHY has to ignore it without throwing an error.
+ * Moreover, the HAL PHY has to reset the the 'rx_prepared' boolean and the medium state. */
+
+ // reset RX preprared
+ ctx->control.rx_prepared = false;
+
+ // set medium state to idle
+ ctx->access.medium_state = MAXIMUS_PHY_MEDIUM_IDLE;
+ }
+ else if ( (ctx->control.rx_prepared) && (!ctx->control.rx_param.short_ppdu) )
+ {
+ dbg_assert_print((MAXIMUS_PHY_MEDIUM_BUSY_RX == ctx->access.medium_state),
+ "receive MPDU payload but PHY is not in BUSY_RX state");
+ if (MAXIMUS_PHY_MEDIUM_BUSY_RX == ctx->access.medium_state)
+ {
+ // check transmission ID and station ID of the received mpdu
+ dbg_assert(ctx->control.rx_param.src_tx_id == ntohs(msg->hdr.phy->tx_id));
+ dbg_assert(ctx->control.rx_param.src_station_id == ntohs(msg->sci_hdr->station_id));
+ if ((ctx->control.rx_param.src_tx_id == ntohs(msg->hdr.phy->tx_id))
+ && (ctx->control.rx_param.src_station_id == ntohs(msg->sci_hdr->station_id)))
+ {
+ MAXIMUS_PHY_TRACE (M_RECV_MPDU_PAYLOAD, my_station.current_tick_tck);
+ int pb_counter;
+ unsigned short int pb_size = (PHY_PB_SIZE_520 == ctx->control.rx_param.pb_size) ? 512 : 128;
+
+ // for pb dma callback
+ memset(&ctx->pbdma.status_word, '\0', sizeof(phy_pbdma_status_t));
+
+ // update current PB in case of an MPDU cut into several PHY messages
+ if ((0 != ctx->pbdma.index_current_pb) && (NULL != ctx->pbdma.current_pb))
+ {
+ ctx->pbdma.current_pb = PARENT_OF(phy_pb_rx_t, blk, ctx->pbdma.current_pb->blk.next);
+ }
+
+ // for each PB of PHY message
+ for (pb_counter = 0; pb_counter < msg->hdr.phy->pb_nb; pb_counter++)
+ {
+ dbg_assert_ptr(ctx->pbdma.current_pb);
+ if (NULL == ctx->pbdma.current_pb)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d because current PB is NULL", __FUNCTION__, errno);
+ }
+
+ if (ctx->pbdma.index_current_pb == ctx->pbdma.nb_ready)
+ {
+ /* When index_current_pb = nb_ready,
+ * PB null is set until the end of the MPDU
+ * (even if, after, index_current_pb <= pb_nb_ready). */
+
+ ctx->pbdma.current_pb = NULL;
+
+ /* If not enough PBs are available in PB DMA,
+ * PB DMA sets its status word (pb null and null pb index),
+ * but does not raise the PHY_HAL_INTERRUPT_PBDMA IT. */
+
+ ctx->pbdma.status_word.pb_null = 1;
+ ctx->pbdma.status_word.null_pb_index = ctx->pbdma.index_current_pb;
+
+ errno = EPROTO;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: not enough ready PBs", __FUNCTION__);
+ dbg_assert_print(false, "not enough ready PBs");
+ }
+
+ if (NULL != ctx->pbdma.current_pb)
+ {
+ // copy PB header
+ ctx->pbdma.current_pb->header = msg->hdr.phy->pb_header[pb_counter];
+
+ // copy PB measurement
+ memcpy(&ctx->pbdma.current_pb->pb_measurement, &msg->hdr.phy->pb_measurement[pb_counter], sizeof(pb_measurement_t));
+
+ // fill CRC bitmap registers
+ ctx->pbdma.crc_bitmap[ctx->pbdma.index_current_pb / 32] |= ctx->pbdma.current_pb->pb_measurement.crc_error << (ctx->pbdma.index_current_pb % 32);
+
+ // when message is received, first PB is read at the end of data
+ memcpy(ctx->pbdma.current_pb->blk.data, msg->data_end-(pb_counter+1)*pb_size, pb_size);
+
+ /* If the current pb is not the last pb of the received PHY message, find the next pb.
+ * Else, do not update the current pb, in order that the function 'phy_pbdma_get_tail()' can return the last valid pb. */
+
+ if (pb_counter < msg->hdr.phy->pb_nb-1)
+ {
+ ctx->pbdma.current_pb = PARENT_OF(phy_pb_rx_t, blk, ctx->pbdma.current_pb->blk.next);
+ }
+ }
+ if ((int)pb_size != sci_msg_pop(msg, pb_size))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when poping SCI message", errno);
+ }
+ else
+ {
+ // increment internal PB counter
+ ctx->pbdma.index_current_pb++;
+
+ if (ctx->pbdma.index_current_pb == ctx->pbdma.nb_pb_it)
+ {
+ /* At the end of the nth PB transfer
+ * (i.e. index_current_pb = nb_pb_it),
+ * a PB end interrupt is simulated by calling phy_pbdma_cb callback. */
+
+ ctx->pbdma.status_word.pb_it = 1;
+ maximus_pending_isrs |= (1 << PHY_HAL_INTERRUPT_PBDMA);
+ }
+ }
+ }
+
+ if (ctx->pbdma.index_current_pb == ctx->pbdma.nb_total) // end of transfer
+ {
+ u32 expected_crc_bitmap[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ // reset RX preprared
+ ctx->control.rx_prepared = false;
+
+ // once phy message has been received, set medium state to idle
+ ctx->access.medium_state = MAXIMUS_PHY_MEDIUM_IDLE;
+
+ if (0 != memcmp(ctx->pbdma.crc_bitmap, expected_crc_bitmap, 8 * sizeof(u32))) // CRC is not correct
+ {
+ // at least one received PB was received with error
+ ctx->pbdma.status_word.pb_crc_error = 1;
+ }
+ ctx->pbdma.status_word.end_rx_pb = 1;
+ }
+
+ ret = 0;
+ }
+ else
+ {
+ errno = EPROTO;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: recv an MPDU with incorrect TX ID and/or station ID", __FUNCTION__);
+ }
+ }
+ else
+ {
+ errno = EPROTO;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d because recv MPDU payload but medium state is not RX", __FUNCTION__, errno);
+ }
+ }
+ else
+ {
+ errno = EPROTO;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d because recv MPDU payload but RX has not been prepared", __FUNCTION__, errno);
+ }
+ }
+
+ return ret;
+}
+
+int
+maximus_phy_recv_tonemask (phy_t *ctx, sci_msg_t *msg)
+{
+ errno = EPROTO;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d because station should not receive a PHY SCI message of type TONEMASK", errno);
+ return -1;
+}
+
+int
+maximus_phy_recv_tonemap (phy_t *ctx, sci_msg_t *msg)
+{
+ errno = EPROTO;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d because station should not receive a PHY SCI message of type TONEMAP", errno);
+ return -1;
+}
+
+int
+maximus_phy_recv_noise (phy_t *ctx, sci_msg_t *msg)
+{
+ int ret = -1;
+
+ dbg_assert_ptr(ctx);
+ dbg_assert_ptr(msg);
+ dbg_assert_ptr(msg->hdr.phy);
+ dbg_assert_ptr(msg->sci_hdr);
+ if((NULL == ctx)
+ || (NULL == msg)
+ || (NULL == msg->hdr.phy)
+ || (NULL == msg->sci_hdr))
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ // check transmission ID and station ID of the received noise message
+ dbg_assert(ctx->control.rx_param.src_tx_id == ntohs(msg->hdr.phy->tx_id));
+ dbg_assert(ctx->control.rx_param.src_station_id == ntohs(msg->sci_hdr->station_id));
+ if ((ctx->control.rx_param.src_tx_id == ntohs(msg->hdr.phy->tx_id))
+ && (ctx->control.rx_param.src_station_id == ntohs(msg->sci_hdr->station_id)))
+ {
+ if (ctx->pbdma.chandata_transfer) // 'phy_pbdma_start_chandata()' has been called
+ {
+ // if channel perturbation is enabled, get the noise
+ if (0 != msg->length)
+ {
+ MAXIMUS_PHY_TRACE (M_RECV_CHANDATA, my_station.current_tick_tck);
+
+ unsigned short int current_size; // in octets
+ phy_chandata_type_t current_type;
+ phy_chandata_t *current_chandata;
+ unsigned short int total_size[2]; // in octets
+ total_size[0] = 0;
+ total_size[1] = 0;
+ unsigned short int max_size[2]; // in octets
+ max_size[PHY_CHANDATA_TYPE_NRJ - 1] = PHY_CARRIER_NB*sizeof(u16); // in octets
+ max_size[PHY_CHANDATA_TYPE_NRJ_SYMBOL - 1] = MAC_MAX_SYMB_PER_MPDU*sizeof(u16); // in octets
+
+ /* For a chandata transfer, all data are received into one SCI message. */
+ u8 frequency_noise[PHY_CARRIER_NB*sizeof(u16)];
+ u8 time_noise[MAC_MAX_SYMB_PER_MPDU*sizeof(u16)];
+ u8 * noise[2];
+ noise[PHY_CHANDATA_TYPE_NRJ - 1] = frequency_noise;
+ noise[PHY_CHANDATA_TYPE_NRJ_SYMBOL - 1] = time_noise;
+
+ // when message is received, frequency_noise is read
+ memcpy(frequency_noise, msg->data_begin, PHY_CARRIER_NB*sizeof(u16));
+ if (PHY_CARRIER_NB*(int)sizeof(u16) != sci_msg_pop(msg, PHY_CARRIER_NB*sizeof(u16)))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when poping SCI message", errno);
+ }
+ else
+ {
+ // then, time_noise is read
+ memcpy(time_noise, msg->data_begin, MAC_MAX_SYMB_PER_MPDU*sizeof(u16));
+ if (MAC_MAX_SYMB_PER_MPDU*(int)sizeof(u16) != sci_msg_pop(msg, MAC_MAX_SYMB_PER_MPDU*sizeof(u16)))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when poping SCI message", errno);
+ }
+ else
+ {
+ // for pb dma callback
+ memset(&ctx->pbdma.status_word, '\0', sizeof(phy_pbdma_status_t));
+
+ // set current chandata
+ current_chandata = ctx->pbdma.first_chandata;
+ dbg_assert_ptr(current_chandata);
+ while (NULL != current_chandata)
+ {
+ current_size = 4*current_chandata->size; // current_chandata->size in words
+ current_type = current_chandata->type;
+ if ((PHY_CHANDATA_TYPE_NO_MEMORY_READ >= current_type)
+ || (PHY_CHANDATA_TYPE_NRJ_SYMBOL < current_type))
+ {
+ errno = EPROTO;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d because of a bad chandata type", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d because of a bad chandata type", errno);
+ }
+ else
+ {
+ if (max_size[current_type-1] < total_size[current_type-1] + current_size)
+ {
+ current_size = max_size[current_type-1] - total_size[current_type-1];
+ }
+ memcpy(current_chandata->blk.data, &(*(noise[current_type-1]+total_size[current_type-1])), current_size);
+ total_size[current_type-1] += current_size;
+
+ if (0 == current_chandata->last)
+ {
+ // find next chandata
+ current_chandata = PARENT_OF(phy_chandata_t, blk, current_chandata->blk.next);
+ dbg_assert_ptr(current_chandata);
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ if (NULL == current_chandata)
+ {
+ errno = EPROTO;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d because current chandata is null", __FUNCTION__, errno);
+ }
+ }
+ }
+ }
+ else // msg->length = 0
+ {
+ station_log(&my_station, STATION_LOG_DEBUG, STATION_LOGTYPE_PHY,
+ "%s: phy_pbdma_start_chandata() has been called but channel perturbation is disabled", __FUNCTION__);
+ }
+
+ /* At the end of the chandata transfer (i.e. current_chandata->last = 1),
+ * or if 'phy_pbdma_start_chandata()' has been called,
+ * a chandata end interrupt is simulated by calling phy_pbdma_cb callback. */
+ ctx->pbdma.status_word.end_chandata = 1;
+ maximus_pending_isrs |= (1 << PHY_HAL_INTERRUPT_PBDMA);
+ }
+ else // ctx->pbdma.chandata_transfer = false
+ {
+ station_log(&my_station, STATION_LOG_DEBUG, STATION_LOGTYPE_PHY,
+ "%s: phy_pbdma_start_chandata() has not been called and channel perturbation = %d", __FUNCTION__, 0 != msg->length);
+ }
+
+ // reset chandata transfer
+ ctx->pbdma.chandata_transfer = false;
+
+ ret = 0;
+ }
+ else
+ {
+ errno = EPROTO;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d because recv a NOISE message with incorrect TX ID and/or station ID", __FUNCTION__, errno);
+ }
+ }
+
+ return ret;
+}
+
+int
+maximus_phy_recv_rx (phy_t *ctx, sci_msg_t *msg)
+{
+ errno = EPROTO;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d because station should not receive a PHY SCI message of type RX", errno);
+ return -1;
+}
+
+int
+maximus_phy_recv_zero_cross (phy_t *ctx, sci_msg_t *msg)
+{
+ int ret = -1;
+
+ dbg_assert_ptr(ctx);
+ dbg_assert_ptr(msg);
+ if ((NULL == ctx)
+ || (NULL == msg))
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ ctx->control.zero_cross_date = (u32)my_station.current_tick_tck;
+
+ // 'ctx->control.zero_cross_cb' can be NULL if not set
+ if (NULL != ctx->control.zero_cross_cb)
+ {
+ // set callback function to call
+ ctx->control.current_cb = ctx->control.zero_cross_cb;
+ maximus_pending_isrs |= (1 << PHY_HAL_INTERRUPT_PHY);
+ }
+
+ ret = 0;
+ }
+
+ return ret;
+}
+
+/**
+ * Send a PHY SCI message of type TONEMASK, TONEMAP or RX to Maximus.
+ * \param ctx phy current context
+ * \return 0 if ok, -1 if if it fails with errno =
+ * - EINVAL if ctx is null
+ * if 'sci_send()' fails, it sets errno
+ */
+int
+maximus_phy_send_tonemask (phy_t *ctx)
+{
+ int ret = -1;
+
+ dbg_assert_ptr(ctx);
+ if (NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ // for phy message
+ sci_msg_t msg;
+
+ // for phy header
+ u32 pb_measurement[PHY_PB_MAX_NB];
+ u32 pb_header[PHY_PB_MAX_NB];
+
+ // initialize pb measurement
+ memset(pb_measurement, '\0', PHY_PB_MAX_NB*sizeof(uint32_t));
+
+ // initialize pb header
+ memset(pb_header, '\0', PHY_PB_MAX_NB*sizeof(uint32_t));
+
+ // init for phy message
+ memset(ctx->buffer, '\0', SCI_MSG_MAX_SIZE);
+ if (0 != sci_msg_init(&msg, ctx->buffer, SCI_MSG_MAX_SIZE))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when initializing SCI message", errno);
+ }
+ else
+ {
+ // calculate tonemask length
+ uint tonemask_length = (PHY_CARRIER_NB+7)/8;
+
+ // fill sci data
+ if ((int)tonemask_length != sci_msg_push(&msg, tonemask_length))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when pushing SCI message", errno);
+ }
+ else
+ {
+ memcpy(msg.data_begin, ctx->tmdma.tonemask, tonemask_length);
+
+ if ((int)sizeof(uint) != sci_msg_push(&msg, sizeof(uint)))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when pushing SCI messages", errno);
+ }
+ else
+ {
+ memcpy(msg.data_begin, &ctx->tmdma.carrier_nb, sizeof(uint));
+
+ // fill phy and sci header
+ if ( (0 != maximus_phy_fill_hdr(ctx,
+ &msg,
+ PHY_TYPE_TONEMASK,
+ PHY_MPDU_FORMAT_NONE,
+ 0, // pb_nb
+ 1, // msg_nb
+ (uint8_t)ctx->control.current_tx_param.fc_mode,
+ (uint8_t)ctx->control.current_tx_param.short_ppdu,
+ (uint8_t)ctx->control.current_tx_param.mod,
+ (uint8_t)ctx->control.current_tx_param.fecrate,
+ (uint8_t)ctx->control.current_tx_param.gil,
+ (uint8_t)ctx->control.current_tx_param.tonemap_index,
+ 0, // tx_id
+ PHY_FLAG_NONE,
+ 0, // symbol_nb
+ ctx->pbdma.iv,
+ ctx->pbdma.nek,
+ pb_measurement,
+ pb_header))
+ || (0 != sci_fill_hdr(my_station.sci, &msg, SCI_MSG_TYPE_PHY, 0 /* flags */)) )
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when filling header", errno);
+ }
+ else
+ {
+ // send the message
+ if (msg.length != sci_send(my_station.sci, &msg))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when sending the TONEMASK", errno);
+ }
+ else
+ {
+ ret = 0;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return ret;
+}
+
+int
+maximus_phy_send_tonemap (phy_t *ctx)
+{
+ int ret = -1;
+
+ dbg_assert_ptr(ctx);
+ if (NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ // for phy message
+ sci_msg_t msg;
+
+ // for phy header
+ uint8_t type = PHY_TYPE_TONEMAP;
+ uint8_t mpdu_format = PHY_MPDU_FORMAT_NONE;
+ uint8_t flags = PHY_FLAG_NONE;
+ uint32_t iv[3];
+ uint32_t nek[4];
+ uint32_t pb_measurement[PHY_PB_MAX_NB];
+ uint32_t pb_header[PHY_PB_MAX_NB];
+
+ // initialize iv
+ memset(iv, '\0', 3*sizeof(uint32_t));
+
+ // initialize nek
+ memset(nek, '\0', 4*sizeof(uint32_t));
+
+ // initialize pb measurement
+ memset(pb_measurement, '\0', PHY_PB_MAX_NB*sizeof(uint32_t));
+
+ // initialize pb header
+ memset(pb_header, '\0', PHY_PB_MAX_NB*sizeof(uint32_t));
+
+ // init for phy message
+ memset(ctx->buffer, '\0', SCI_MSG_MAX_SIZE);
+ if (0 != sci_msg_init(&msg, ctx->buffer, SCI_MSG_MAX_SIZE))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when initializing SCI message", errno);
+ }
+ else
+ {
+ /* For a TONEMAP transfer, all data are sent into one SCI message. */
+
+ if ((NULL != ctx->tmdma.tonemap[ctx->control.current_tx_param.tonemap_index])
+ && (NULL != ctx->tmdma.tonemap[ctx->control.current_tx_param.tonemap_index]->next))
+ {
+ // fill sci data
+ if (MAC_PB520_BYTES != sci_msg_push(&msg, MAC_PB520_BYTES))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when pushing SCI message", errno);
+ }
+ else
+ {
+ memcpy(msg.data_begin, ctx->tmdma.tonemap[ctx->control.current_tx_param.tonemap_index]->data, MAC_PB520_BYTES);
+
+ if ((PHY_CARRIER_NB+1)/2-MAC_PB520_BYTES != sci_msg_push(&msg, (PHY_CARRIER_NB+1)/2-MAC_PB520_BYTES))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when pushing SCI message", errno);
+ }
+ else
+ {
+ memcpy(msg.data_begin, ctx->tmdma.tonemap[ctx->control.current_tx_param.tonemap_index]->next->data, (PHY_CARRIER_NB+1)/2-MAC_PB520_BYTES);
+ }
+ }
+ }
+ else
+ {
+ if ((PHY_CARRIER_NB+1)/2 != sci_msg_push(&msg, (PHY_CARRIER_NB+1)/2))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when pushing SCI message", errno);
+ }
+ else
+ {
+ memset(msg.data_begin, '\0', (PHY_CARRIER_NB+1)/2);
+ }
+ }
+
+ // fill phy header
+ if ( (0 != maximus_phy_fill_hdr(ctx,
+ &msg,
+ type,
+ mpdu_format,
+ 0, // pb_nb
+ 1, // msg_nb
+ (uint8_t)ctx->control.current_tx_param.fc_mode,
+ (uint8_t)ctx->control.current_tx_param.short_ppdu,
+ (uint8_t)ctx->control.current_tx_param.mod,
+ (uint8_t)ctx->control.current_tx_param.fecrate,
+ (uint8_t)ctx->control.current_tx_param.gil,
+ (uint8_t)ctx->control.current_tx_param.tonemap_index,
+ ctx->control.current_tx_param.tx_id,
+ flags,
+ 0, // symbol_nb
+ iv,
+ nek,
+ pb_measurement,
+ pb_header))
+ || (0 != sci_fill_hdr(my_station.sci, &msg, SCI_MSG_TYPE_PHY, 0 /* flags */)) )
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when filling header", errno);
+ }
+ else
+ {
+ // send the message
+ if (msg.length != sci_send(my_station.sci, &msg))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when sending the TONEMAP", errno);
+ }
+ else
+ {
+ ret = 0;
+ }
+ }
+
+ // once phy message has been sent, set medium state to idle
+ ctx->access.medium_state = MAXIMUS_PHY_MEDIUM_IDLE;
+ }
+ }
+
+ return ret;
+}
+
+int
+maximus_phy_send_rx (phy_t *ctx)
+{
+ /* When RX is prepared, this function is called if the station is waiting for a long MPDU. */
+
+ int ret = -1;
+
+ dbg_assert_ptr(ctx);
+ if (NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ // for phy message
+ sci_msg_t msg;
+
+ // for phy header
+ uint8_t type = PHY_TYPE_RX;
+ uint8_t mpdu_format = PHY_MPDU_FORMAT_NONE;
+ uint8_t flags = PHY_FLAG_NONE;
+ uint32_t iv[3];
+ uint32_t nek[4];
+ uint32_t pb_measurement[PHY_PB_MAX_NB];
+ uint32_t pb_header[PHY_PB_MAX_NB];
+
+ // initialize iv
+ memset(iv, '\0', 3*sizeof(uint32_t));
+
+ // initialize nek
+ memset(nek, '\0', 4*sizeof(uint32_t));
+
+ // initialize pb measurement
+ memset(pb_measurement, '\0', PHY_PB_MAX_NB*sizeof(uint32_t));
+
+ // initialize pb header
+ memset(pb_header, '\0', PHY_PB_MAX_NB*sizeof(uint32_t));
+
+ // init for phy message
+ memset(ctx->buffer, '\0', SCI_MSG_MAX_SIZE);
+ if (0 != sci_msg_init(&msg, ctx->buffer, SCI_MSG_MAX_SIZE))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when initializing SCI message", errno);
+ }
+ else
+ {
+ // fill sci data
+ if ((int)sizeof(uint16_t) != sci_msg_push(&msg, sizeof(uint16_t)))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when pushing SCI message", errno);
+ }
+ else
+ {
+ memcpy(msg.data_begin, &ctx->control.rx_param.src_station_id, sizeof(uint16_t));
+
+ // fill phy header
+ if ( (0 != maximus_phy_fill_hdr(ctx,
+ &msg,
+ type,
+ mpdu_format,
+ 0, // pb_nb
+ 1, // msg_nb
+ (uint8_t)ctx->control.rx_param.fc_mode,
+ (uint8_t)ctx->control.rx_param.short_ppdu,
+ (uint8_t)ctx->control.rx_param.mod,
+ (uint8_t)ctx->control.rx_param.fecrate,
+ (uint8_t)ctx->control.rx_param.gil,
+ (uint8_t)ctx->control.rx_param.tonemap_index,
+ ctx->control.rx_param.src_tx_id,
+ flags,
+ 0, // symbol_nb
+ iv,
+ nek,
+ pb_measurement,
+ pb_header))
+ || (0 != sci_fill_hdr(my_station.sci, &msg, SCI_MSG_TYPE_PHY, 0 /* flags */)) )
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when filling header", errno);
+ }
+ else
+ {
+ // send the message
+ if (msg.length != sci_send(my_station.sci, &msg))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when sending the RX message", errno);
+ }
+ else
+ {
+ ret = 0;
+ }
+ }
+ }
+ }
+ }
+
+ return ret;
+}
+
+
+/**
+ * Cancel the next TX frame request.
+ * \param ctx phy context.
+ * set errno to:
+ * - EINVAL if ctx is null
+ * if 'netclock_unschedule()' fails, it sets errno
+ */
+void maximus_phy_next_tx_frame_cancel (phy_t *ctx)
+{
+ dbg_assert_ptr(ctx);
+ if (NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ if (0 != ctx->control.next_tx_param.tx_frame_netclock_id)
+ {
+ if (-1 == netclock_unschedule(my_station.netclock, ctx->control.next_tx_param.tx_frame_netclock_id))
+ {
+ if (ctx->warning_assert)
+ {
+ station_log(&my_station, STATION_LOG_WARNING, STATION_LOGTYPE_PHY,
+ "%s: errno = %d when unscheduling a netclock message", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when unscheduling a netclock message", errno);
+ }
+ }
+ ctx->control.next_tx_param.tx_frame_netclock_id = 0;
+ }
+ }
+}
+
+
+/**
+ * Cancel the current TX frame request.
+ * \param ctx phy context.
+ * set errno to:
+ * - EINVAL if ctx is null
+ * if 'netclock_unschedule()' fails, it sets errno
+ */
+void maximus_phy_current_tx_frame_cancel (phy_t *ctx)
+{
+ /* This function is only called by 'phy_uninit'
+ * (a Tx that has been started cannot be cancelled). */
+
+ dbg_assert_ptr(ctx);
+ if (NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ if (0 != ctx->control.current_tx_param.tx_frame_netclock_id)
+ {
+ if (-1 == netclock_unschedule(my_station.netclock, ctx->control.current_tx_param.tx_frame_netclock_id))
+ {
+ if (ctx->warning_assert)
+ {
+ station_log(&my_station, STATION_LOG_WARNING, STATION_LOGTYPE_PHY,
+ "%s: errno = %d when unscheduling a netclock message", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when unscheduling a netclock message", errno);
+ }
+ }
+ ctx->control.current_tx_param.tx_frame_netclock_id = 0;
+ }
+ }
+}
+
+
+/**
+ * Cancel the previous RX activate request.
+ * \param ctx phy context.
+ * set errno to:
+ * - EINVAL if ctx is null
+ * if 'netclock_unschedule()' fails, it sets errno
+ */
+void maximus_phy_rx_activate_cancel (phy_t *ctx)
+{
+ dbg_assert_ptr(ctx);
+ if (NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ if (0 != ctx->control.rx_param.rx_activate_netclock_id)
+ {
+ if (-1 == netclock_unschedule(my_station.netclock, ctx->control.rx_param.rx_activate_netclock_id))
+ {
+ if (ctx->warning_assert)
+ {
+ station_log(&my_station, STATION_LOG_WARNING, STATION_LOGTYPE_PHY,
+ "%s: errno = %d when unscheduling a netclock message", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when unscheduling a netclock message", errno);
+ }
+ }
+ ctx->control.rx_param.rx_activate_netclock_id = 0;
+ }
+ }
+}
+
+
+/**
+ * Compute schedule tick.
+ * \param date programmed date
+ * \return schedule tick if ok, -1 if programmed date is considered to be in the past with errno = EINVAL
+ */
+tick_t maximus_phy_schedule_tick (u32 date)
+{
+ tick_t schedule_tick = (tick_t)-1;
+
+ const u32 tick_high = my_station.current_tick_tck >> 32;
+ const u32 tick_low = (u32)my_station.current_tick_tck;
+
+ /* Determinate whether programmed date is considered to be in the past,
+ * or in the future after tick rollover. */
+
+ u32 tolerance = tick_low - (u32)MAXIMUS_PHY_DATE_TOLERANCE;
+
+ if (tick_low > tolerance)
+ {
+ if (date >= tick_low)
+ {
+ // programmed date is in the future
+ schedule_tick = ((tick_t)tick_high << 32) | (tick_t)date;
+ }
+ else if (date < tolerance)
+ {
+ // programmed date is considered to be in the future, after tick rollover
+ schedule_tick = ((tick_t)(tick_high+1) << 32) | (tick_t)date;
+ }
+ else
+ {
+ // programmed date is considered to be in the past
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d because netclock msg programmed in the past", errno);
+ }
+ }
+ else
+ {
+ if ((date < tolerance) && (date >= tick_low))
+ {
+ // programmed date is in the future
+ schedule_tick = ((tick_t)tick_high << 32) | (tick_t)date;
+ }
+ else
+ {
+ // programmed date is considered to be in the past
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d because netclock msg programmed in the past", errno);
+ }
+ }
+
+ return schedule_tick;
+}
+
+
+/**
+ * Initialise the HAL Phy.
+ * \param user_data User data passed to any callback
+ * \param rx_fc_cb RX FC event callback
+ * \param access_cb ACCESS event callback
+ * \param access_conf_cb ACCESS CONFIRM event callback
+ * \param pbdma_cb PB DMA callback
+ * \param tx_false_alarm_cb TX FALSE ALARM callback
+ * \param deferred_cb DSR callback
+ * \return the newly created context
+ * set errno to:
+ * - EINVAL if arguments are null
+ */
+phy_t *
+phy_init (void *user_data, phy_rx_fc_cb_t rx_fc_cb, phy_access_cb_t access_cb,
+ phy_access_conf_cb_t access_conf_cb, phy_pbdma_cb_t pbdma_cb,
+ phy_tx_false_alarm_cb_t tx_false_alarm_cb, phy_deferred_cb_t deferred_cb)
+{
+ /* Set the user_data value of PHY context.
+ * Set the rx_fc_cb value of PHY context.
+ * Set the access_cb value of PHY context.
+ * Set the access_conf_cb value of PHY context.
+ * Set the pbdma_cb value of PHY context.
+ * Set the tx_false_alarm_cb value of PHY context.
+ * Set the deferred_cb value of PHY context.
+ * Reset the PRP result to true. */
+
+ static phy_t ctx;
+
+ static netclock_callback_t access_backoff_start_netclock_cb;
+ static netclock_callback_t access_backoff_slot_count_netclock_cb;
+ static netclock_callback_t access_timer_program_netclock_cb;
+ static netclock_callback_t next_tx_frame_netclock_cb;
+ static netclock_callback_t current_tx_frame_netclock_cb;
+ static netclock_callback_t rx_activate_netclock_cb;
+ static netclock_callback_t extra_timer_program_netclock_cb;
+ static netclock_callback_t recv_preamble_netclock_cb;
+
+ static lib_rnd_t rnd;
+
+ static u8 phy_tonemask[(PHY_CARRIER_NB+7)/8]; // uses 192 bytes (1 bit per carrier)
+ static blk_t phy_tonemap[2*TONEMAP_INDEX_NB]; // each tonemap uses two blocks
+ static u8 phy_tonemap1_data[MAC_PB520_BYTES][TONEMAP_INDEX_NB];
+ static u8 phy_tonemap2_data[(PHY_CARRIER_NB+1)/2-MAC_PB520_BYTES][TONEMAP_INDEX_NB]; // 4 bits per carrier
+
+ dbg_assert_ptr(user_data);
+ dbg_assert_ptr(rx_fc_cb);
+ dbg_assert_ptr(access_cb);
+ dbg_assert_ptr(access_conf_cb);
+ dbg_assert_ptr(pbdma_cb);
+ dbg_assert_ptr(tx_false_alarm_cb);
+ dbg_assert_ptr(deferred_cb);
+ if ((NULL == user_data)
+ || (NULL == rx_fc_cb)
+ || (NULL == access_cb)
+ || (NULL == access_conf_cb)
+ || (NULL == pbdma_cb)
+ || (NULL == tx_false_alarm_cb)
+ || (NULL == deferred_cb))
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ unsigned short int tonemap_index;
+
+ memset(&ctx, '\0', sizeof(phy_t));
+ ctx.control.user_data = user_data;
+ ctx.control.rx_fc_cb = rx_fc_cb;
+ ctx.control.access_cb = access_cb;
+ ctx.control.access_conf_cb = access_conf_cb;
+ ctx.control.pbdma_cb = pbdma_cb;
+ ctx.control.tx_false_alarm_cb = tx_false_alarm_cb;
+ ctx.control.deferred_cb = deferred_cb;
+ ctx.access.backoff_start_netclock_cb = &access_backoff_start_netclock_cb;
+ ctx.access.backoff_slot_count_netclock_cb = &access_backoff_slot_count_netclock_cb;
+ ctx.access.timer_program_netclock_cb = &access_timer_program_netclock_cb;
+ ctx.control.next_tx_param.tx_frame_netclock_cb = &next_tx_frame_netclock_cb;
+ ctx.control.current_tx_param.tx_frame_netclock_cb = &current_tx_frame_netclock_cb;
+ ctx.control.rx_param.rx_activate_netclock_cb = &rx_activate_netclock_cb;
+ ctx.control.extra_timer_program_netclock_cb = &extra_timer_program_netclock_cb;
+ ctx.control.rx_param.recv_preamble_netclock_cb = &recv_preamble_netclock_cb;
+ ctx.access.prp_result = true;
+
+ // initialize random library
+ ctx.control.rnd = &rnd;
+ lib_rnd_init(ctx.control.rnd, MAXIMUS_PHY_LIB_RND_SEED);
+
+ // initialize tonemask and tonemap
+ memset(phy_tonemask, '\0', (PHY_CARRIER_NB+7)/8);
+ memset(phy_tonemap1_data, '\0', MAC_PB520_BYTES);
+ memset(phy_tonemap2_data, '\0', (PHY_CARRIER_NB+1)/2-MAC_PB520_BYTES);
+ ctx.tmdma.tonemask = phy_tonemask;
+ for (tonemap_index=0; tonemap_index<TONEMAP_INDEX_NB; tonemap_index++)
+ {
+ phy_tonemap[2*tonemap_index].next = &phy_tonemap[2*tonemap_index+1];
+ phy_tonemap[2*tonemap_index].data = &phy_tonemap1_data[0][tonemap_index];
+ phy_tonemap[2*tonemap_index+1].next = NULL;
+ phy_tonemap[2*tonemap_index+1].data = &phy_tonemap2_data[0][tonemap_index];
+ ctx.tmdma.tonemap[tonemap_index] = &phy_tonemap[2*tonemap_index];
+ }
+
+ // register phy_recv to the sci layer
+ sci_register_callback(my_station.sci, SCI_MSG_TYPE_PHY, maximus_phy_recv, &ctx);
+
+#ifdef ECOS
+ // register the phy ISR and DSR into eCos
+ cyg_drv_interrupt_create(PHY_HAL_INTERRUPT_PHY,
+ PHY_HAL_INTERRUPT_PRIORITY,
+ (cyg_addrword_t)&ctx,
+ _phy_ecos_isr,
+ _phy_ecos_dsr,
+ &ctx.control.phy_interrupt_handle,
+ &ctx.control.phy_interrupt);
+ cyg_drv_interrupt_attach(ctx.control.phy_interrupt_handle);
+ cyg_drv_interrupt_unmask(PHY_HAL_INTERRUPT_PHY);
+
+ // register the pbdma ISR and DSR into eCos
+ cyg_drv_interrupt_create(PHY_HAL_INTERRUPT_PBDMA,
+ PHY_HAL_INTERRUPT_PRIORITY,
+ (cyg_addrword_t)&ctx,
+ _pbdma_ecos_isr,
+ _phy_ecos_dsr, /* same DSRfor phy/pbdma interrupts */
+ &ctx.control.pbdma_interrupt_handle,
+ &ctx.control.pbdma_interrupt);
+ cyg_drv_interrupt_attach(ctx.control.pbdma_interrupt_handle);
+ cyg_drv_interrupt_unmask(PHY_HAL_INTERRUPT_PBDMA);
+#endif /* ECOS */
+ maximus_phy_trace_init (&ctx);
+ TRACE_FAST_SHORT (MAXIMUS_PHY_TRACE_, &ctx.trace, INIT);
+ }
+
+ return &ctx;
+}
+
+
+/**
+ * Reset the hardware to a known sane state.
+ * \param ctx phy context
+ * set errno to:
+ * - EINVAL if ctx is null
+ */
+void
+phy_reset (phy_t *ctx)
+{
+ /* After an error detection, PBP can reset the HW to a known sane state.
+ * Uninitialize PHY context ('phy_uninit'), and set PHY context to its initial values. */
+
+ dbg_assert_ptr(ctx);
+ if (NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+#ifdef ECOS
+ // mask IRQs
+ cyg_drv_interrupt_unmask(PHY_HAL_INTERRUPT_PHY);
+ cyg_drv_interrupt_unmask(PHY_HAL_INTERRUPT_PBDMA);
+ // detach IRQs
+ cyg_drv_interrupt_detach(ctx->control.phy_interrupt_handle);
+ cyg_drv_interrupt_detach(ctx->control.pbdma_interrupt_handle);
+ // unregister IRQs
+ cyg_drv_interrupt_delete(ctx->control.phy_interrupt_handle);
+ cyg_drv_interrupt_delete(ctx->control.pbdma_interrupt_handle);
+#endif /* ECOS */
+
+ // save values to keep
+ void *extra_timer_user_data = ctx->control.extra_timer_user_data;
+ void *user_data = ctx->control.user_data;
+ phy_rx_fc_cb_t rx_fc_cb = ctx->control.rx_fc_cb;
+ phy_access_cb_t access_cb = ctx->control.access_cb;
+ phy_access_conf_cb_t access_conf_cb = ctx->control.access_conf_cb;
+ phy_pbdma_cb_t pbdma_cb = ctx->control.pbdma_cb;
+ phy_tx_false_alarm_cb_t tx_false_alarm_cb = ctx->control.tx_false_alarm_cb;
+ phy_zero_cross_cb_t zero_cross_cb = ctx->control.zero_cross_cb;
+ phy_deferred_cb_t deferred_cb = ctx->control.deferred_cb;
+ phy_extra_timer_cb_t extra_timer_cb = ctx->control.extra_timer_cb;
+ netclock_callback_t *access_backoff_start_netclock_cb = ctx->access.backoff_start_netclock_cb;
+ netclock_callback_t *access_backoff_slot_count_netclock_cb = ctx->access.backoff_slot_count_netclock_cb;
+ netclock_callback_t *access_timer_program_netclock_cb = ctx->access.timer_program_netclock_cb;
+ netclock_callback_t *next_tx_frame_netclock_cb = ctx->control.next_tx_param.tx_frame_netclock_cb;
+ netclock_callback_t *current_tx_frame_netclock_cb = ctx->control.current_tx_param.tx_frame_netclock_cb;
+ netclock_callback_t *rx_activate_netclock_cb = ctx->control.rx_param.rx_activate_netclock_cb;
+ netclock_callback_t *extra_timer_program_netclock_cb = ctx->control.extra_timer_program_netclock_cb;
+ netclock_callback_t *recv_preamble_netclock_cb = ctx->control.rx_param.recv_preamble_netclock_cb;
+
+ // save random library context
+ lib_rnd_t *rnd = ctx->control.rnd;
+
+ // save tonemask and tonemap
+ maximus_tmdma_t tmdma = ctx->tmdma;
+
+ // reset and uninitialize phy context
+ phy_uninit(ctx);
+
+ // reinitialize it
+ ctx->control.extra_timer_user_data = extra_timer_user_data;
+ ctx->control.user_data = user_data;
+ ctx->control.rx_fc_cb = rx_fc_cb;
+ ctx->control.access_cb = access_cb;
+ ctx->control.access_conf_cb = access_conf_cb;
+ ctx->control.pbdma_cb = pbdma_cb;
+ ctx->control.tx_false_alarm_cb = tx_false_alarm_cb;
+ ctx->control.zero_cross_cb = zero_cross_cb;
+ ctx->control.deferred_cb = deferred_cb;
+ ctx->control.extra_timer_cb = extra_timer_cb;
+ ctx->access.backoff_start_netclock_cb = access_backoff_start_netclock_cb;
+ ctx->access.backoff_slot_count_netclock_cb = access_backoff_slot_count_netclock_cb;
+ ctx->access.timer_program_netclock_cb = access_timer_program_netclock_cb;
+ ctx->control.next_tx_param.tx_frame_netclock_cb = next_tx_frame_netclock_cb;
+ ctx->control.current_tx_param.tx_frame_netclock_cb = current_tx_frame_netclock_cb;
+ ctx->control.rx_param.rx_activate_netclock_cb = rx_activate_netclock_cb;
+ ctx->control.extra_timer_program_netclock_cb = extra_timer_program_netclock_cb;
+ ctx->control.rx_param.recv_preamble_netclock_cb = recv_preamble_netclock_cb;
+ ctx->access.prp_result = true;
+
+ // reinitialize random library
+ ctx->control.rnd = rnd;
+
+ // reinitialize tonemask and tonemap
+ ctx->tmdma = tmdma;
+
+ /* re-init IRQs */
+#ifdef ECOS
+ // register the phy ISR and DSR into eCos
+ cyg_drv_interrupt_create(PHY_HAL_INTERRUPT_PHY,
+ PHY_HAL_INTERRUPT_PRIORITY,
+ (cyg_addrword_t)ctx,
+ _phy_ecos_isr,
+ _phy_ecos_dsr,
+ &ctx->control.phy_interrupt_handle,
+ &ctx->control.phy_interrupt);
+ cyg_drv_interrupt_attach(ctx->control.phy_interrupt_handle);
+ cyg_drv_interrupt_unmask(PHY_HAL_INTERRUPT_PHY);
+
+ // register the pbdma ISR and DSR into eCos
+ cyg_drv_interrupt_create(PHY_HAL_INTERRUPT_PBDMA,
+ PHY_HAL_INTERRUPT_PRIORITY,
+ (cyg_addrword_t)ctx,
+ _pbdma_ecos_isr,
+ _phy_ecos_dsr, /* same DSRfor phy/pbdma interrupts */
+ &ctx->control.pbdma_interrupt_handle,
+ &ctx->control.pbdma_interrupt);
+ cyg_drv_interrupt_attach(ctx->control.pbdma_interrupt_handle);
+ cyg_drv_interrupt_unmask(PHY_HAL_INTERRUPT_PBDMA);
+#endif /* ECOS */
+
+ maximus_phy_trace_init (ctx);
+ MAXIMUS_PHY_TRACE (RESET);
+ }
+}
+
+
+/**
+ * Reset and uninitialise the HAL Phy.
+ * \param ctx phy context
+ * set errno to:
+ * - EINVAL if ctx is null
+ */
+void
+phy_uninit (phy_t *ctx)
+{
+ /* For test purpose, PBP can reset and uninitialize the HAL PHY
+ * when modem is deactivated/reactivated.
+ * If netclock messages had been sent, unschedule all of them.
+ * Then, stop Tx/Rx, and set PHY context to NULL values. */
+
+ dbg_assert_ptr(ctx);
+ if (NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ MAXIMUS_PHY_TRACE (UNINIT);
+ // unschedule all sent netclock messages
+ maximus_phy_access_backoff_cancel(ctx);
+ maximus_phy_access_slot_count_reset(ctx);
+ phy_access_timer_cancel(ctx);
+ maximus_phy_next_tx_frame_cancel(ctx);
+ maximus_phy_current_tx_frame_cancel(ctx);
+ maximus_phy_rx_activate_cancel(ctx);
+ phy_extra_timer_cancel(ctx);
+ if (0 != ctx->control.rx_param.recv_preamble_netclock_id)
+ {
+ /* Send a netclock message to Maximus simulator
+ * to unschedule the event sent in 'maximus_phy_recv_preamble'
+ * according to ctx->control.rx_param.recv_preamble_netclock_id. */
+
+ if (-1 == netclock_unschedule(my_station.netclock, ctx->control.rx_param.recv_preamble_netclock_id))
+ {
+ if (ctx->warning_assert)
+ {
+ station_log(&my_station, STATION_LOG_WARNING, STATION_LOGTYPE_PHY,
+ "%s: errno = %d when unscheduling a netclock message", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when unscheduling a netclock message", errno);
+ }
+ }
+ }
+ maximus_phy_trace_uninit(ctx);
+
+ // reset phy context
+ memset(ctx, '\0', sizeof(phy_t));
+ }
+}
+
+
+/**
+ * Get current date.
+ * \param ctx phy context
+ * \return current phy date
+ * set errno to:
+ * - EINVAL if ctx is null
+ */
+u32
+phy_date (phy_t *ctx)
+{
+ u32 date = 0;
+
+ dbg_assert_ptr(ctx);
+ if (NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ date = (u32)my_station.current_tick_tck;
+ }
+
+ return date;
+}
+
+
+/**
+ * Retrieve uncorrected current date.
+ * \param ctx phy context
+ * \return current uncorrected date
+ * set errno to:
+ * - EINVAL if ctx is null
+ */
+u32
+phy_sysdate (phy_t *ctx)
+{
+ u32 sysdate = 0;
+
+ dbg_assert_ptr(ctx);
+ if (NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ // TODO (for the moment, return the current date)
+ sysdate = (u32)my_station.current_tick_tck;
+ }
+
+ return sysdate;
+}
+
+
+/**
+ * Set the clock correction.
+ * \param ctx phy context
+ * \param numerator clock correction
+ * set errno to:
+ * - EINVAL if ctx is null
+ */
+void
+phy_clock_set_numerator (phy_t *ctx, uint numerator)
+{
+ dbg_assert_ptr(ctx);
+ if (NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ MAXIMUS_PHY_TRACE (CLOCK_SET_NUMERATOR, numerator);
+ // TODO
+ }
+}
+
+
+/**
+ * Get date of last zero-cross.
+ * \param ctx phy context
+ * \return last zero-cross date
+ * set errno to:
+ * - EINVAL if ctx is null
+ */
+u32
+phy_clock_get_zero_cross_captured_date (phy_t *ctx)
+{
+ u32 date = 0;
+
+ dbg_assert_ptr(ctx);
+ if (NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ date = ctx->control.zero_cross_date;
+ }
+
+ return date;
+}
+
+
+/**
+ * Retrieve uncorrected date of last zero-cross.
+ * \param ctx phy context
+ * \return last zero-cross uncorrected date
+ * set errno to:
+ * - EINVAL if ctx is null
+ */
+u32
+phy_clock_get_zero_cross_captured_sysdate (phy_t *ctx)
+{
+ u32 sysdate = 0;
+
+ dbg_assert_ptr(ctx);
+ if (NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ // TODO (for the moment, return the last zero-cross date)
+ sysdate = ctx->control.zero_cross_date;
+ }
+
+ return sysdate;
+}
+
+
+/**
+ * Start Homeplug 1.0 frame control encoding.
+ * \param ctx phy context
+ * \param fc_10 Homeplug 1.0 frame control
+ * set errno to:
+ * - EINVAL if ctx is null
+ */
+void
+phy_tx_fc10 (phy_t *ctx, u32 fc_10)
+{
+ /* If PRP is won (i.e. no more prior access is detected),
+ * PBP calls this function for HP 1.0 FC encoding
+ * 25µs before the start date given into 'phy_tx_frame'.
+ * Set the fc_10 value of PHY context. */
+
+ dbg_assert_ptr(ctx);
+ if (NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ MAXIMUS_PHY_TRACE (TX_FC10, fc_10);
+ ctx->control.next_tx_param.fc_10 = fc_10;
+ ctx->control.next_tx_param.fc_10_tick = my_station.current_tick_tck + PHY_FC_10_PREPARATION_DELAY_TCK;
+ }
+}
+
+
+/**
+ * Set TX parameters.
+ * \param ctx phy context
+ * \param fc_mode frame control mode
+ * \param short_ppdu true if no data symbols will be sent
+ * \param mod modulation type
+ * \param fecrate TCC rate
+ * \param pb_size PB size
+ * \param gil guard interval for third symbol and following symbols
+ * \param tonemap_index tone map index
+ * set errno to:
+ * - EINVAL if ctx is null, or if arguments are out-of-range or incoherent
+ *
+ * For short PPDU, following arguments are ignored.
+ *
+ * When modulation type is a ROBO mode, following arguments are ignored.
+ */
+void
+phy_tx_param (phy_t *ctx, phy_fc_mode_t fc_mode, bool short_ppdu,
+ phy_mod_t mod, phy_fecrate_t fecrate, phy_pb_size_t pb_size,
+ phy_gil_t gil, uint tonemap_index)
+{
+ /* Set the fc_mode value of PHY context.
+ * Set the short_ppdu value of PHY context.
+ * Set the mod value of PHY context.
+ * Set the fecrate value of PHY context.
+ * Set the pb_size value of PHY context.
+ * Set the gil value of PHY context.
+ * Set the tonemap_index value of PHY context. */
+
+ dbg_assert_ptr(ctx);
+ dbg_assert(PHY_FC_MODE_NB > fc_mode);
+ dbg_assert(short_ppdu
+ || (!short_ppdu
+ && (PHY_MOD_NONE > mod)));
+ dbg_assert(short_ppdu
+ || (!short_ppdu
+ && (PHY_MOD_TM != mod))
+ || (!short_ppdu
+ && (PHY_MOD_TM == mod)
+ && (PHY_FEC_RATE_NONE > fecrate)
+ && (PHY_PB_SIZE_NONE > pb_size)
+ && (PHY_GIL_NB > gil)
+ && (TONEMAP_INDEX_NB > tonemap_index)));
+ if ((NULL == ctx)
+ || (PHY_FC_MODE_NB <= fc_mode)
+ || (!short_ppdu
+ && (PHY_MOD_NONE <= mod))
+ || (!short_ppdu
+ && (PHY_MOD_TM == mod)
+ && ((PHY_FEC_RATE_NONE <= fecrate)
+ || (PHY_PB_SIZE_NONE <= pb_size)
+ || (PHY_GIL_NB <= gil)
+ || (TONEMAP_INDEX_NB <= tonemap_index))))
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ MAXIMUS_PHY_TRACE (TX_PARAM, fc_mode, short_ppdu, mod, fecrate,
+ pb_size, gil, tonemap_index);
+ ctx->control.next_tx_param.fc_mode = fc_mode;
+ ctx->control.next_tx_param.short_ppdu = short_ppdu;
+
+ if (!short_ppdu)
+ {
+ ctx->control.next_tx_param.mod = mod;
+
+ if (PHY_MOD_TM == mod)
+ {
+ ctx->control.next_tx_param.fecrate = fecrate;
+ ctx->control.next_tx_param.pb_size = pb_size;
+ ctx->control.next_tx_param.gil = gil;
+ ctx->control.next_tx_param.tonemap_index = tonemap_index;
+ }
+ else // tonemap is not used
+ {
+ ctx->control.next_tx_param.fecrate = PHY_FEC_RATE_1_2;
+ ctx->control.next_tx_param.tonemap_index = 0;
+ if (PHY_MOD_MINI_ROBO == mod)
+ {
+ ctx->control.next_tx_param.pb_size = PHY_PB_SIZE_136;
+ ctx->control.next_tx_param.gil = PHY_GIL_567;
+ }
+ else
+ {
+ ctx->control.next_tx_param.pb_size = PHY_PB_SIZE_520;
+ ctx->control.next_tx_param.gil = PHY_GIL_417;
+ }
+ }
+ }
+ else // no data symbols will be sent
+ {
+ ctx->control.next_tx_param.mod = PHY_MOD_NONE;
+ ctx->control.next_tx_param.fecrate = PHY_FEC_RATE_NONE;
+ ctx->control.next_tx_param.pb_size = PHY_PB_SIZE_NONE;
+ ctx->control.next_tx_param.gil = PHY_GIL_NONE;
+ ctx->control.next_tx_param.tonemap_index = 0;
+ }
+ }
+}
+
+
+/**
+ * Schedule a TX start.
+ * \param ctx phy context
+ * \param date hardware date when the transmission should begin
+ * \param want_conf request an ACCESS CONF interrupt
+ * \param stop_tx_on_prp_lost abort TX if PRP was lost
+ * \param fc_av frame control
+ * set errno to:
+ * - EINVAL if ctx is null
+ * if 'netclock_schedule()' fails, it sets errno
+ */
+void
+phy_tx_frame (phy_t *ctx, u32 date, bool want_conf, bool stop_tx_on_prp_lost,
+ const u32 fc_av[4])
+{
+ static maximus_phy_tx_frame_t tx_frame_cb_data;
+ static u32 next_tx_frame_date = 0;
+
+ /* PBP schedules to start a Tx.
+ * When scheduling a Tx, PBP selects if it wants an IT_ACCESS_CONF or not
+ * via the want_conf boolean argument.
+ * Set the stop_tx_on_prp_lost value of PHY context.
+ * Set the fc_av[4] value of PHY context.
+ * Send a netclock message to Maximus simulator scheduled at the given date. */
+
+ dbg_assert_ptr(ctx);
+ if (NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ MAXIMUS_PHY_TRACE (TX_FRAME, date, want_conf, stop_tx_on_prp_lost,
+ fc_av[0]);
+
+ if ((u32)my_station.current_tick_tck < next_tx_frame_date)
+ {
+ // cancel the next TX frame request
+ maximus_phy_next_tx_frame_cancel(ctx);
+ }
+
+ // set next TX frame request date
+ next_tx_frame_date = date;
+
+ // set phy context values
+ ctx->control.stop_tx_on_prp_lost = stop_tx_on_prp_lost;
+ memcpy(ctx->control.next_tx_param.fc_av, fc_av, 4*sizeof(u32));
+
+ // fill callback data
+ tx_frame_cb_data.ctx = ctx;
+ tx_frame_cb_data.want_conf = want_conf;
+
+ // send netclock message to Maximus
+ if (-1 == netclock_schedule(my_station.netclock,
+ ctx->control.next_tx_param.tx_frame_netclock_cb,
+ NETWORK_CLOCK_TYPE_STATION,
+ maximus_phy_schedule_tick(date),
+ &maximus_phy_tx_frame_cb,
+ (void*)&tx_frame_cb_data,
+ &ctx->control.next_tx_param.tx_frame_netclock_id))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when scheduling a netclock message", errno);
+ }
+ }
+}
+
+
+/**
+ * Store the STA SYS clock frequency error value relative to CCo clock in ppm,
+ * useful for interpolation block in the DSP.
+ * \param ctx phy context
+ * \param rho_ppm frequency error
+ * set errno to:
+ * - EINVAL if ctx is null
+ */
+void
+phy_sysclock_set_freqerror (phy_t *ctx, double rho_ppm)
+{
+ dbg_assert_ptr(ctx);
+ if (NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ // TODO
+ }
+}
+
+
+/**
+ * Set RX parameters.
+ * \param ctx phy context
+ * \param fc_mode frame control mode
+ * set errno to:
+ * - EINVAL if ctx is null, or if fc_mode is out-of-range
+ *
+ * The new mode is used immediately.
+ */
+void
+phy_rx_param (phy_t *ctx, phy_fc_mode_t fc_mode)
+{
+ /* Set the fc_mode value of phy context. */
+
+ dbg_assert_ptr(ctx);
+ dbg_assert(PHY_FC_MODE_NB > fc_mode);
+ if ((NULL == ctx)
+ || (PHY_FC_MODE_NB <= fc_mode))
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ MAXIMUS_PHY_TRACE (RX_PARAM, fc_mode);
+ ctx->control.rx_param.fc_mode = fc_mode;
+ }
+}
+
+
+/**
+ * Activate or deactivate preamble detection.
+ * \param ctx phy context
+ * \param date activation date
+ * \param flag true to activate
+ * set errno to:
+ * - EINVAL if ctx is null
+ * if 'netclock_schedule()' fails, it sets errno
+ *
+ * A pending interrupt is not cancelled.
+ */
+void
+phy_rx_activate (phy_t *ctx, bool now, u32 date, bool pre_detection)
+{
+ static maximus_phy_rx_activate_t rx_activate_cb_data;
+
+ /* PBP activates PRE detection.
+ * PHY will deactivate Rx in 2 cases:
+ * - when a PRE is detected;
+ * - when a Tx starts (i.e. at the start date defined into 'phy_tx_frame').
+ * If PHY deactivates Rx, PBP has to reactivate it. */
+
+ dbg_assert_ptr(ctx);
+ if (NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ // if a RX activate had already been requested, cancel the previous one
+ maximus_phy_rx_activate_cancel(ctx);
+ if (!pre_detection)
+ {
+ // cancel the backoff procedure and reset the slot count
+ maximus_phy_access_backoff_cancel(ctx);
+ maximus_phy_access_slot_count_reset(ctx);
+ }
+
+ if (!now) // activate rx at date
+ {
+ MAXIMUS_PHY_TRACE (RX_ACTIVATE, date, pre_detection);
+
+ // fill callback data
+ rx_activate_cb_data.ctx = ctx;
+ rx_activate_cb_data.pre_detection = pre_detection;
+
+ /* Send a netclock message to Maximus simulator scheduled at the given date. */
+
+ if (-1 == netclock_schedule(my_station.netclock,
+ ctx->control.rx_param.rx_activate_netclock_cb,
+ NETWORK_CLOCK_TYPE_STATION,
+ maximus_phy_schedule_tick(date),
+ &maximus_phy_rx_activate_cb,
+ (void*)&rx_activate_cb_data,
+ &ctx->control.rx_param.rx_activate_netclock_id))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when scheduling a netclock message", errno);
+ }
+ }
+ else // activate rx now
+ {
+ MAXIMUS_PHY_TRACE (RX_ACTIVATE_NOW, pre_detection);
+
+ ctx->control.pre_detection = pre_detection;
+
+ if (ctx->control.pre_detection)
+ {
+ // reset prp result to 'true'
+ ctx->access.prp_result = true;
+ }
+
+ // reset TX blocked
+ ctx->control.tx_blocked_on_false_alarm = false;
+ }
+ }
+}
+
+
+/**
+ * Set parameter for frame payload reception.
+ * \param ctx phy context
+ * \param short_ppdu true if no data symbols will be received
+ * \param mod modulation type
+ * \param fecrate TCC rate
+ * \param pb_size PB size
+ * \param gil guard interval for third symbol and following symbols
+ * \param tonemap_index tone map index
+ * \param symbol_nb number of expected symbols
+ * set errno to:
+ * - EINVAL if ctx is null, or if arguments are out-of-range or incoherent
+ *
+ * This call signals hardware that it can continue to process the incoming
+ * data.
+ */
+void
+phy_rx_prepare (phy_t *ctx, bool short_ppdu, phy_mod_t mod,
+ phy_fecrate_t fecrate, phy_pb_size_t pb_size, phy_gil_t gil,
+ uint tonemap_index, uint symbol_nb)
+{
+ /* PBP prepares HW for reception.
+ * This function is called when a FC is received, in the 'rx_fc_cb'. */
+
+ /* Set the short_ppdu value of PHY context.
+ * Set the mod value of PHY context.
+ * Set the fecrate value of PHY context.
+ * Set the pb_size value of PHY context.
+ * Set the gil value of PHY context.
+ * Set the tonemap_index value of PHY context.
+ * Set the symbol_nb value of PHY context. */
+
+ dbg_assert_ptr(ctx);
+ dbg_assert(short_ppdu
+ || (!short_ppdu
+ && (PHY_MOD_NONE > mod)));
+ dbg_assert(short_ppdu
+ || (!short_ppdu
+ && (PHY_MOD_TM != mod))
+ || (!short_ppdu
+ && (PHY_MOD_TM == mod)
+ && (PHY_FEC_RATE_NONE > fecrate)
+ && (PHY_PB_SIZE_NONE > pb_size)
+ && (PHY_GIL_NB > gil)
+ && (TONEMAP_INDEX_NB > tonemap_index)));
+ if ((NULL == ctx)
+ || (!short_ppdu
+ && (PHY_MOD_NONE <= mod))
+ || (!short_ppdu
+ && (PHY_MOD_TM == mod)
+ && ((PHY_FEC_RATE_NONE <= fecrate)
+ || (PHY_PB_SIZE_NONE <= pb_size)
+ || (PHY_GIL_NB <= gil)
+ || (TONEMAP_INDEX_NB <= tonemap_index))))
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ MAXIMUS_PHY_TRACE (RX_PREPARE, short_ppdu, mod, fecrate, pb_size, gil,
+ tonemap_index, symbol_nb);
+
+ // set phy context values
+
+ ctx->control.rx_param.short_ppdu = short_ppdu;
+ ctx->control.rx_param.symbol_nb = symbol_nb;
+
+ if (!short_ppdu)
+ {
+ ctx->control.rx_param.mod = mod; // set modulation mode
+
+ if (PHY_MOD_TM == mod)
+ {
+ ctx->control.rx_param.fecrate = fecrate;
+ ctx->control.rx_param.pb_size = pb_size;
+ ctx->control.rx_param.gil = gil;
+ ctx->control.rx_param.tonemap_index = tonemap_index;
+ }
+ else // tonemap is not used
+ {
+ ctx->control.rx_param.fecrate = PHY_FEC_RATE_1_2;
+ ctx->control.rx_param.tonemap_index = 0;
+ if (PHY_MOD_MINI_ROBO == mod)
+ {
+ ctx->control.rx_param.pb_size = PHY_PB_SIZE_136;
+ ctx->control.rx_param.gil = PHY_GIL_567;
+ }
+ else
+ {
+ ctx->control.rx_param.pb_size = PHY_PB_SIZE_520;
+ ctx->control.rx_param.gil = PHY_GIL_417;
+ }
+ }
+
+ /* Send a PHY SCI message RX. */
+ if (0 != maximus_phy_send_rx(ctx))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d because RX message has not been correctly sent", errno);
+ }
+ }
+ else // no data symbols will be received
+ {
+ ctx->control.rx_param.mod = PHY_MOD_NONE;
+ ctx->control.rx_param.fecrate = PHY_FEC_RATE_NONE;
+ ctx->control.rx_param.pb_size = PHY_PB_SIZE_NONE;
+ ctx->control.rx_param.gil = PHY_GIL_NB;
+ ctx->control.rx_param.tonemap_index = 0;
+
+ // reset medium state
+ ctx->access.medium_state = MAXIMUS_PHY_MEDIUM_IDLE;
+ }
+
+ // indicate that RX has been prepared
+ ctx->control.rx_prepared = true;
+
+ // reset internal PB counter
+ ctx->pbdma.index_current_pb = 0;
+
+ // reset CRC bitmap registers
+ memset(ctx->pbdma.crc_bitmap, '\0', 8 * sizeof(u32));
+ }
+}
+
+
+/**
+ * Retrieve Homeplug 1.0 frame control.
+ * \param ctx phy context
+ * \return received Homeplug 1.0 frame control or (u32)-1 on CRC error
+ * return 0 if it fails with errno =
+ * - EINVAL if ctx is null
+ */
+u32
+phy_rx_fc10 (phy_t *ctx)
+{
+ u32 fc_10 = 0;
+
+ dbg_assert_ptr(ctx);
+ if (NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ fc_10 = ctx->control.rx_param.fc_10;
+ }
+
+ return fc_10;
+}
+
+
+/**
+ * Retrieve uncorrected date of last start of preamble.
+ * \param ctx phy context
+ * \return last start of preamble uncorrected date
+ */
+u32
+phy_rx_sysdate (phy_t *ctx)
+{
+ u32 sysdate = 0;
+
+ dbg_assert_ptr(ctx);
+ if (NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ sysdate = ctx->control.rx_param.preamble_start_date;
+ }
+
+ return sysdate;
+}
+
+
+/**
+ * Initialise extra timer callback.
+ * \param ctx phy context
+ * \param extra_timer_user_data user data passed to the callback
+ * \param extra_timer_cb extra timer callback
+ * set errno to:
+ * - EINVAL if ctx or extra_timer_cb are null
+ */
+void
+phy_extra_timer_init (phy_t *ctx, void *extra_timer_user_data,
+ phy_extra_timer_cb_t extra_timer_cb)
+{
+ dbg_assert_ptr(ctx);
+ dbg_assert_ptr(extra_timer_cb);
+ if ((NULL == ctx)
+ || (NULL == extra_timer_cb))
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ ctx->control.extra_timer_user_data = extra_timer_user_data;
+ ctx->control.extra_timer_cb = extra_timer_cb;
+ }
+}
+
+
+/**
+ * Program the extra timer to the given date.
+ * \param ctx phy context
+ * \param date timer expiration date
+ * set errno to:
+ * - EINVAL if ctx is null
+ * if 'netclock_schedule()' fails, it sets errno
+ */
+void
+phy_extra_timer_program (phy_t *ctx, u32 date)
+{
+ dbg_assert_ptr(ctx);
+ if (NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ phy_extra_timer_cancel(ctx);
+
+ MAXIMUS_PHY_TRACE (EXTRA_TIMER_PROGRAM, date);
+
+ /* Send a netclock message to Maximus simulator scheduled at the given date. */
+
+ if (-1 == netclock_schedule(my_station.netclock,
+ ctx->control.extra_timer_program_netclock_cb,
+ NETWORK_CLOCK_TYPE_STATION,
+ maximus_phy_schedule_tick(date),
+ &maximus_phy_extra_timer_cb,
+ (void*)ctx,
+ &ctx->control.extra_timer_program_netclock_id))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when scheduling a netclock message", errno);
+ }
+ }
+}
+
+
+/**
+ * Cancel the extra timer.
+ * \param ctx phy context
+ * set errno to:
+ * - EINVAL if ctx is null
+ * if 'netclock_unschedule()' fails, it sets errno
+ */
+void
+phy_extra_timer_cancel (phy_t *ctx)
+{
+ dbg_assert_ptr(ctx);
+ if(NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ MAXIMUS_PHY_TRACE (EXTRA_TIMER_CANCEL);
+
+ /* Send a netclock message to Maximus simulator
+ * to unschedule the event sent in the previous function ('phy_extra_timer_program')
+ * according to ctx->control.netclok_id. */
+
+ if (0 != ctx->control.extra_timer_program_netclock_id)
+ {
+ if (-1 == netclock_unschedule(my_station.netclock, ctx->control.extra_timer_program_netclock_id))
+ {
+ if (ctx->warning_assert)
+ {
+ station_log(&my_station, STATION_LOG_WARNING, STATION_LOGTYPE_PHY,
+ "%s: errno = %d when unscheduling a netclock message", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when unscheduling a netclock message", errno);
+ }
+ }
+ ctx->control.extra_timer_program_netclock_id = 0;
+ }
+ }
+}
+
+
+/**
+ * Initialise zero-cross callback.
+ * \param ctx phy context
+ * \param zero_cross_cb zero-cross callback
+ * set errno to:
+ * - EINVAL if ctx or zero_cross_cb are null
+ */
+void
+phy_zero_cross_init (phy_t *ctx, phy_zero_cross_cb_t zero_cross_cb)
+{
+ dbg_assert_ptr(ctx);
+ dbg_assert_ptr(zero_cross_cb);
+ if ((NULL == ctx)
+ || (NULL == zero_cross_cb))
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ /* Set the zero_cross_cb value of PHY context. */
+ ctx->control.zero_cross_cb = zero_cross_cb;
+ }
+}
+
diff --git a/cesar/hal/phy/maximus/src/maximus_phy_ctrl_cb.c b/cesar/hal/phy/maximus/src/maximus_phy_ctrl_cb.c
new file mode 100644
index 0000000000..e739205bef
--- /dev/null
+++ b/cesar/hal/phy/maximus/src/maximus_phy_ctrl_cb.c
@@ -0,0 +1,966 @@
+/* Cesar project {{{
+ *
+ * Copyright (C) 2007 Spidcom
+ *
+ * <<<Licence>>>
+ *
+ * }}} */
+/**
+ * \file hal/phy/maximus/src/maximus_phy_ctrl_cb.c
+ * \brief HAL Phy control functions for Maximus.
+ * \ingroup hal_phy_maximus
+ */
+
+#include "common/std.h"
+#include "ecos/packages/hal/maximus/arch/current/include/hal_host_intr.h"
+#include "host/fwd.h" // for 'phy_msg_hdr_t'
+#include "hal/phy/maximus/inc/maximus_phy_ctx.h"
+#include "hal/phy/maximus/inc/maximus_phy_ctrl.h"
+#include "hal/phy/maximus/inc/maximus_phy_access.h"
+#include "hal/phy/maximus/inc/maximus_interrupts.h"
+#include "hal/phy/maximus/dur/inc/maximus_dur.h"
+#include "hal/phy/maximus/inc/maximus_defs.h"
+#include "lib/swap.h" // for 'ntohl'
+#include "mac/common/timings.h" // for 'MAC_PREAMBLE_TCK', 'MAC_PREAMBLE_HYBRID_TCK', 'MAC_FC_10_TCK' and 'MAC_FC_AV_TCK'
+#include "mac/common/defs.h" // for 'MAC_PB520_BYTES' and 'MAC_MAX_SYMB_PER_MPDU'
+#include <string.h> // for 'memset'
+#include <errno.h>
+
+
+/**
+ * Maximus PHY preamble reception callback called when the corresponding netclock message is received.
+ * \param data pointer to 'maximus_phy_recv_preamble_t' structure,
+ * containing flag PHY_FLAG_WRONG_PREAMBLE of the received preamble message.
+ * set errno to:
+ * - EINVAL if data is null
+ */
+void
+maximus_phy_recv_preamble_cb (void *data)
+{
+ dbg_assert_ptr(data);
+ if (NULL == data)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ maximus_phy_recv_preamble_t *recv_preamble_data = (maximus_phy_recv_preamble_t*)data;
+ phy_t *ctx = recv_preamble_data->ctx;
+
+ dbg_assert_ptr(ctx);
+ if (NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ /* If a PRE is detected before Tx starts, CW is lost.
+ * PHY stops Tx without raising an IT. */
+
+ if (ctx->control.pre_detection // PRE detection is set in 'phy_rx_activate' or 'maximus_phy_rx_activate_cb',
+ // and in 'maximus_phy_access_prp_result_cb' when the medium state becomes CW.
+ // PRE detection is deactivated here and in 'maximus_phy_tx_frame_cb'.
+ && (MAXIMUS_PHY_MEDIUM_BUSY_RX != ctx->access.medium_state)
+ && (MAXIMUS_PHY_MEDIUM_BUSY_TX != ctx->access.medium_state))
+ {
+ if (recv_preamble_data->wrong_preamble)
+ {
+ // set medium state to IDLE
+ ctx->access.medium_state = MAXIMUS_PHY_MEDIUM_IDLE;
+
+ // stop the slot count
+ maximus_phy_access_slot_count_reset(ctx);
+
+ // check TX blocked
+ if (ctx->control.tx_blocked_on_false_alarm)
+ {
+ /* Raise a TX_FALSE_ALARM IT. */
+ // set callback function to call
+ ctx->control.current_cb = ctx->control.tx_false_alarm_cb;
+ // raise an IT
+ maximus_pending_isrs |= (1 << PHY_HAL_INTERRUPT_PHY);
+ }
+ }
+ else
+ {
+ MAXIMUS_PHY_TRACE (M_PRE_DETECT, my_station.current_tick_tck);
+
+ // update medium state
+ ctx->access.medium_state = MAXIMUS_PHY_MEDIUM_BUSY_RX;
+
+ // deactivate preamble detection
+ ctx->control.pre_detection = false;
+
+ // set transmission ID and station ID of the received preamble
+ ctx->control.rx_param.src_tx_id = recv_preamble_data->src_tx_id;
+ ctx->control.rx_param.src_station_id = recv_preamble_data->src_station_id;
+
+ // cancel the backoff procedure and reset the slot count
+ maximus_phy_access_backoff_cancel(ctx);
+ maximus_phy_access_slot_count_reset(ctx);
+
+ // HW: timer value at last Preamble_found rising edge
+ ctx->control.rx_param.preamble_start_date = (u32)my_station.current_tick_tck;
+ }
+ }
+ else
+ {
+ station_log(&my_station, STATION_LOG_WARNING, STATION_LOGTYPE_PHY,
+ "%s: detect a PREAMBLE but does not process it because PRE detection is deactivated or because medium state is already BUSY",
+ __FUNCTION__);
+
+ if (ctx->warning_assert)
+ {
+ dbg_assert_print(false,
+ "detect a PREAMBLE but does not process it because PRE detection is deactivated or because medium state is already BUSY");
+ }
+ }
+
+ // reset RX preamble netclock message id
+ ctx->control.rx_param.recv_preamble_netclock_id = 0;
+ }
+ }
+}
+
+
+/**
+ * Maximus TX frame first callback called when the corresponding netclock message is received.
+ * \param data pointer to 'maximus_tx_frame_t' structure, containing argument 'want_conf' of 'phy_tx_frame'.
+ * set errno to:
+ * - EINVAL if data, ctx->control.access_conf_cb or ctx->control.deferred_cb are null
+ * - EPROTO if medium state is not idle
+ * if 'netclock_schedule()' fails, it sets errno
+ */
+void
+maximus_phy_tx_frame_cb (void *data)
+{
+ dbg_assert_ptr(data);
+ if (NULL == data)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ maximus_phy_tx_frame_t *tx_frame_data = (maximus_phy_tx_frame_t*)data;
+ phy_t *ctx = tx_frame_data->ctx;
+
+ dbg_assert_ptr(ctx);
+ dbg_assert_ptr(ctx->control.access_conf_cb);
+ dbg_assert_ptr(ctx->control.deferred_cb);
+ if ((NULL == ctx)
+ || (NULL == ctx->control.access_conf_cb)
+ || (NULL == ctx->control.deferred_cb))
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ dbg_assert((MAXIMUS_PHY_MEDIUM_IDLE == ctx->access.medium_state)
+ || (MAXIMUS_PHY_MEDIUM_CW == ctx->access.medium_state)
+ || (MAXIMUS_PHY_MEDIUM_BUSY_RX == ctx->access.medium_state)
+ || (MAXIMUS_PHY_MEDIUM_WAIT_CONF == ctx->access.medium_state));
+ if ((MAXIMUS_PHY_MEDIUM_IDLE == ctx->access.medium_state)
+ || (MAXIMUS_PHY_MEDIUM_CW == ctx->access.medium_state))
+ {
+ MAXIMUS_PHY_TRACE (M_TX_FRAME, my_station.current_tick_tck);
+
+ dbg_assert(my_station.current_tick_tck >= ctx->control.next_tx_param.fc_10_tick);
+ if (my_station.current_tick_tck < ctx->control.next_tx_param.fc_10_tick)
+ {
+ errno = EPROTO;
+ station_log(&my_station, STATION_LOG_INFO, STATION_LOGTYPE_PHY,
+ "%s: errno = %d because FC 1.0 preparation is not finished", __FUNCTION__, errno);
+ }
+ else
+ {
+ // save all TX parameters
+ ctx->control.current_tx_param.fc_10 = ctx->control.next_tx_param.fc_10;
+ ctx->control.current_tx_param.fc_10_tick = ctx->control.next_tx_param.fc_10_tick;
+ ctx->control.current_tx_param.fc_mode = ctx->control.next_tx_param.fc_mode;
+ ctx->control.current_tx_param.short_ppdu = ctx->control.next_tx_param.short_ppdu;
+ ctx->control.current_tx_param.mod = ctx->control.next_tx_param.mod;
+ ctx->control.current_tx_param.fecrate = ctx->control.next_tx_param.fecrate;
+ ctx->control.current_tx_param.pb_size = ctx->control.next_tx_param.pb_size;
+ ctx->control.current_tx_param.gil = ctx->control.next_tx_param.gil;
+ ctx->control.current_tx_param.tonemap_index = ctx->control.next_tx_param.tonemap_index;
+ ctx->control.current_tx_param.symbol_nb = ctx->control.next_tx_param.symbol_nb;
+ memcpy(ctx->control.current_tx_param.fc_av, ctx->control.next_tx_param.fc_av, 4*sizeof(u32));
+
+ // update medium state
+ ctx->access.medium_state = MAXIMUS_PHY_MEDIUM_BUSY_TX;
+
+ // deactivate preamble detection
+ ctx->control.pre_detection = false;
+
+ // increment TX ID
+ ctx->control.current_tx_param.tx_id++;
+
+ // reset next TX frame netclock message id
+ ctx->control.next_tx_param.tx_frame_netclock_id = 0;
+
+ /* Reset the slot count. */
+
+ maximus_phy_access_slot_count_reset(ctx);
+
+ /* If want_conf equals true and if no PRE has been received,
+ * PHY raises an IT_ACCESS_CONF at start date defined into 'phy_tx_frame',
+ * i.e. this callback has to call the 'phy_access_conf_cb'.
+ * In other cases, IT_ACCESS_CONF is not risen.
+ * Note that want_conf equals false only when there is no frame payload,
+ * i.e. for RTS/CTS and SACK MPDU. */
+
+ if (tx_frame_data->want_conf)
+ {
+ ctx->control.current_cb = ctx->control.access_conf_cb;
+ maximus_pending_isrs |= (1 << PHY_HAL_INTERRUPT_PHY);
+ }
+
+ // send netclock message to Maximus
+ if (-1 == netclock_schedule(my_station.netclock,
+ ctx->control.current_tx_param.tx_frame_netclock_cb,
+ NETWORK_CLOCK_TYPE_STATION,
+ my_station.current_tick_tck + PHY_PREAMBLE_DETECTION_DELAY_TCK,
+ &maximus_phy_tx_pre_cb,
+ (void*)ctx,
+ &ctx->control.current_tx_param.tx_frame_netclock_id))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when scheduling a netclock message", errno);
+ }
+ }
+ }
+ else if (MAXIMUS_PHY_MEDIUM_BUSY_RX == ctx->access.medium_state)
+ {
+ station_log(&my_station, STATION_LOG_INFO, STATION_LOGTYPE_PHY,
+ "%s: does not transmit the frame because medium state is BUSY_RX", __FUNCTION__);
+ }
+ else if (MAXIMUS_PHY_MEDIUM_WAIT_CONF == ctx->access.medium_state)
+ {
+ // set TX blocked
+ ctx->control.tx_blocked_on_false_alarm = true;
+
+ station_log(&my_station, STATION_LOG_INFO, STATION_LOGTYPE_PHY,
+ "%s: TX blocked because medium state is WANT_CONF", __FUNCTION__);
+ }
+ }
+ }
+}
+
+
+/**
+ * Maximus TX frame second callback called when the corresponding netclock message is received.
+ * \param data pointer to 'phy_t' structure.
+ * set errno to:
+ * - EINVAL if data is null
+ * - EPROTO if medium state is not tx
+ * if 'sci_send()' or 'netclock_schedule()' fails, it sets errno
+ */
+void
+maximus_phy_tx_pre_cb (void *data)
+{
+ dbg_assert_ptr(data);
+ if (NULL == data)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ phy_t *ctx = (phy_t*)data;
+
+ dbg_assert_ptr(ctx);
+ if (NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ dbg_assert(MAXIMUS_PHY_MEDIUM_BUSY_TX == ctx->access.medium_state);
+ if (MAXIMUS_PHY_MEDIUM_BUSY_TX != ctx->access.medium_state)
+ {
+ errno = EPROTO;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d because medium state is not TX", __FUNCTION__, errno);
+ }
+ else
+ {
+ MAXIMUS_PHY_TRACE (M_TX_PRE, my_station.current_tick_tck);
+
+ // for phy message
+ sci_msg_t msg;
+
+ // for netclock message
+ tick_t schedule_tick;
+
+ // for frame control transmission/reception time
+ uint pre_duration = MAC_PREAMBLE_TCK; // in AV mode
+ uint fc_duration = MAC_FC_AV_TCK; // in AV mode
+
+ /* PHY message. */
+
+ // init for phy message
+ memset(ctx->buffer, '\0', SCI_MSG_MAX_SIZE);
+
+ if (0 != sci_msg_init(&msg, ctx->buffer, SCI_MSG_MAX_SIZE))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when initializing SCI message", errno);
+ }
+ else
+ {
+ // for phy header
+ uint8_t type = PHY_TYPE_PREAMBLE;
+ uint8_t flags = PHY_FLAG_CRC_OK;
+ uint32_t iv[3];
+ uint32_t nek[4];
+ uint32_t pb_measurement[PHY_PB_MAX_NB];
+ uint32_t pb_header[PHY_PB_MAX_NB];
+
+ // set phy header values
+ memset(iv, '\0', 3*sizeof(uint32_t));
+ memset(nek, '\0', 4*sizeof(uint32_t));
+ memset(pb_measurement, '\0', PHY_PB_MAX_NB*sizeof(uint32_t));
+ memset(pb_header, '\0', PHY_PB_MAX_NB*sizeof(uint32_t));
+
+ // fill phy and sci header
+ if ( (0 != maximus_phy_fill_hdr(ctx,
+ &msg,
+ type,
+ PHY_MPDU_FORMAT_NONE,
+ 0, // pb_nb
+ 1, // msg_nb
+ (uint8_t)ctx->control.current_tx_param.fc_mode,
+ (uint8_t)ctx->control.current_tx_param.short_ppdu,
+ (uint8_t)ctx->control.current_tx_param.mod,
+ (uint8_t)ctx->control.current_tx_param.fecrate,
+ (uint8_t)ctx->control.current_tx_param.gil,
+ (uint8_t)ctx->control.current_tx_param.tonemap_index,
+ ctx->control.current_tx_param.tx_id,
+ flags,
+ 0, // symbol_nb
+ iv,
+ nek,
+ pb_measurement,
+ pb_header))
+ || (0 != sci_fill_hdr(my_station.sci, &msg, SCI_MSG_TYPE_PHY, 0 /* flags */)) )
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when filling header", errno);
+ }
+ else
+ {
+ // send the message
+ if (msg.length != sci_send(my_station.sci, &msg))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when sending the PREAMBLE", errno);
+ }
+ }
+ }
+
+ /* Netclock message. */
+
+ // set schedule tick
+ schedule_tick = my_station.current_tick_tck;
+
+ // calculate frame control transimission time
+ if ((PHY_FC_MODE_HYBRID_2 == ctx->control.current_tx_param.fc_mode)
+ || (PHY_FC_MODE_AV_2 == ctx->control.current_tx_param.fc_mode))
+ {
+ fc_duration *= 2; // 2 symbols
+ }
+ if ((PHY_FC_MODE_HYBRID_1 == ctx->control.current_tx_param.fc_mode)
+ || (PHY_FC_MODE_HYBRID_2 == ctx->control.current_tx_param.fc_mode))
+ {
+ pre_duration = MAC_PREAMBLE_HYBRID_TCK; // in Hybrid mode
+ fc_duration += MAC_FC_10_TCK; // in Hybrid mode
+ }
+ schedule_tick += (pre_duration - PHY_PREAMBLE_DETECTION_DELAY_TCK) \
+ + fc_duration \
+ + MAXIMUS_PHY_FC_RECEPTION_DELAY_TCK;
+
+ // send netclock message to Maximus
+ if (-1 == netclock_schedule(my_station.netclock,
+ ctx->control.current_tx_param.tx_frame_netclock_cb,
+ NETWORK_CLOCK_TYPE_STATION,
+ schedule_tick,
+ &maximus_phy_tx_fc_cb,
+ (void*)ctx,
+ &ctx->control.current_tx_param.tx_frame_netclock_id))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when scheduling a netclock message", errno);
+ }
+ }
+ }
+ }
+}
+
+
+/**
+ * Maximus TX frame third callback called when the corresponding netclock message is received.
+ * \param data pointer to 'phy_t' structure.
+ * set errno to:
+ * - EINVAL if data is null
+ * - EPROTO if medium state is not tx
+ * if 'sci_send()' or 'netclock_schedule()' fails, it sets errno
+ */
+void
+maximus_phy_tx_fc_cb (void *data)
+{
+ dbg_assert_ptr(data);
+ if (NULL == data)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ phy_t *ctx = (phy_t*)data;
+
+ dbg_assert_ptr(ctx);
+ if (NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ dbg_assert(MAXIMUS_PHY_MEDIUM_BUSY_TX == ctx->access.medium_state);
+ if (MAXIMUS_PHY_MEDIUM_BUSY_TX != ctx->access.medium_state)
+ {
+ errno = EPROTO;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d because medium state is not TX", __FUNCTION__, errno);
+ }
+ else
+ {
+ MAXIMUS_PHY_TRACE (M_TX_FC, my_station.current_tick_tck);
+
+ // for phy message
+ sci_msg_t msg;
+
+ // for netclock message
+ tick_t schedule_tick;
+
+ // for MPDU payload transmission/reception time
+ uint bits_per_symbol;
+ uint duration_in_ticks;
+
+ /* This callback has to send a phy message containing fc_av
+ * (and fc_10 in Hybrid Mode). */
+
+ // init for phy message
+ memset(ctx->buffer, '\0', SCI_MSG_MAX_SIZE);
+
+ if (0 != sci_msg_init(&msg, ctx->buffer, SCI_MSG_MAX_SIZE))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when initializing SCI message", errno);
+ }
+ else
+ {
+ // for phy header
+ uint8_t type = PHY_TYPE_FC_HYBRID_MODE;
+ uint8_t flags = PHY_FLAG_CRC_OK;
+ uint32_t iv[3];
+ uint32_t nek[4];
+ uint32_t pb_measurement[PHY_PB_MAX_NB];
+ uint32_t pb_header[PHY_PB_MAX_NB];
+
+ // set phy header values
+ memset(iv, '\0', 3*sizeof(uint32_t));
+ memset(nek, '\0', 4*sizeof(uint32_t));
+ memset(pb_measurement, '\0', PHY_PB_MAX_NB*sizeof(uint32_t));
+ memset(pb_header, '\0', PHY_PB_MAX_NB*sizeof(uint32_t));
+ if ((PHY_FC_MODE_AV_1 == ctx->control.current_tx_param.fc_mode)
+ || (PHY_FC_MODE_AV_2 == ctx->control.current_tx_param.fc_mode))
+ {
+ type = PHY_TYPE_FC_AV_ONLY_MODE;
+ }
+
+ // fill sci data
+ if (4*(int)sizeof(u32) != sci_msg_push(&msg, 4*sizeof(u32)))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when pushing SCI message", errno);
+ }
+ else
+ {
+ memcpy(msg.data_begin, ctx->control.current_tx_param.fc_av, 4*sizeof(u32));
+
+ if (PHY_TYPE_FC_HYBRID_MODE == type)
+ {
+ if ((int)sizeof(u32) != sci_msg_push(&msg, sizeof(u32)))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when pushing SCI message", errno);
+ }
+ else
+ {
+ memcpy(msg.data_begin, &ctx->control.current_tx_param.fc_10, sizeof(u32));
+ }
+ }
+
+ // fill phy and sci header
+ if ( (0 != maximus_phy_fill_hdr(ctx,
+ &msg,
+ type,
+ PHY_MPDU_FORMAT_NONE,
+ 0, // pb_nb
+ 1, // msg_nb
+ (uint8_t)ctx->control.current_tx_param.fc_mode,
+ (uint8_t)ctx->control.current_tx_param.short_ppdu,
+ (uint8_t)ctx->control.current_tx_param.mod,
+ (uint8_t)ctx->control.current_tx_param.fecrate,
+ (uint8_t)ctx->control.current_tx_param.gil,
+ (uint8_t)ctx->control.current_tx_param.tonemap_index,
+ ctx->control.current_tx_param.tx_id,
+ flags,
+ 0, // symbol_nb
+ iv,
+ nek,
+ pb_measurement,
+ pb_header))
+ || (0 != sci_fill_hdr(my_station.sci, &msg, SCI_MSG_TYPE_PHY, 0 /* flags */)) )
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when filling header", errno);
+ }
+ else
+ {
+ // send the message
+ if (msg.length != sci_send(my_station.sci, &msg))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when sending the FC", errno);
+ }
+ }
+ }
+ }
+
+ if (!ctx->control.current_tx_param.short_ppdu)
+ {
+ /* At last, to simulate the transmission time, this callback has to send a netclock message
+ * scheduled at a date calculated depending on tonemask, modulation, tonemap, fecrate,
+ * PB size, number of bits per symbol, number of PBs, guard interval, and number of symbols. */
+
+ // set schedule tick
+ schedule_tick = my_station.current_tick_tck;
+
+ // calculate MPDU payload transimission time
+ bits_per_symbol = maximus_dur_bits_per_symbol(ctx->control.current_tx_param.mod,
+ ctx->tmdma.tonemask,
+ ctx->tmdma.tonemap[ctx->control.current_tx_param.tonemap_index],
+ ctx->tmdma.carrier_nb); // 4 bits per carrier
+ ctx->control.current_tx_param.symbol_nb = maximus_dur_symbol_nb(ctx->control.current_tx_param.fecrate,
+ ctx->control.current_tx_param.pb_size,
+ bits_per_symbol,
+ ctx->pbdma.nb_total);
+ MAXIMUS_PHY_TRACE (M_TX_SYMBOLS, ctx->control.current_tx_param.symbol_nb);
+ duration_in_ticks = maximus_dur_data_tck(ctx->control.current_tx_param.gil,
+ ctx->control.current_tx_param.symbol_nb);
+ schedule_tick += duration_in_ticks - MAXIMUS_PHY_FC_RECEPTION_DELAY_TCK + MAXIMUS_PHY_MPDU_RECEPTION_DELAY_TCK;
+
+ if (-1 == netclock_schedule(my_station.netclock,
+ ctx->control.current_tx_param.tx_frame_netclock_cb,
+ NETWORK_CLOCK_TYPE_STATION,
+ schedule_tick,
+ &maximus_phy_tx_mpdu_cb,
+ (void*)ctx,
+ &ctx->control.current_tx_param.tx_frame_netclock_id))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when scheduling a netclock message", errno);
+ }
+ }
+ else
+ {
+ // once phy message has been sent, set medium state to idle
+ ctx->access.medium_state = MAXIMUS_PHY_MEDIUM_IDLE;
+
+ // reset TX frame netclock message id
+ ctx->control.current_tx_param.tx_frame_netclock_id = 0;
+ }
+ }
+ }
+ }
+}
+
+
+/**
+ * Maximus TX frame fourth callback called when the corresponding netclock message is received.
+ * \param data pointer to 'phy_t' structure.
+ * set errno to:
+ * - EINVAL if data is null
+ * - EPROTO if medium state is not tx
+ * if 'sci_send()' fails, it sets errno
+ */
+void
+maximus_phy_tx_mpdu_cb (void *data)
+{
+ /* At the corresponding event reception, the transfer really starts,
+ * i.e. this callback has to send a phy message containing MPDU payload. */
+
+ dbg_assert_ptr(data);
+ if (NULL == data)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ phy_t *ctx = (phy_t*)data;
+
+ dbg_assert_ptr(ctx);
+ dbg_assert_ptr(ctx->control.pbdma_cb);
+ if ((NULL == ctx)
+ || (NULL == ctx->control.pbdma_cb))
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ dbg_assert(MAXIMUS_PHY_MEDIUM_BUSY_TX == ctx->access.medium_state);
+ if (MAXIMUS_PHY_MEDIUM_BUSY_TX != ctx->access.medium_state)
+ {
+ errno = EPROTO;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d because medium state is not TX", __FUNCTION__, errno);
+ }
+ else
+ {
+ MAXIMUS_PHY_TRACE (M_TX_MPDU, my_station.current_tick_tck);
+
+ // for phy message
+ sci_msg_t msg;
+
+ // for phy header
+ uint8_t type = PHY_TYPE_MPDU_PAYLOAD;
+ uint8_t mpdu_format = PHY_MPDU_FORMAT_NONE;
+ uint8_t flags = PHY_FLAG_CRC_OK;
+ uint32_t pb_measurement[PHY_PB_MAX_NB];
+ uint32_t pb_header[PHY_PB_MAX_NB];
+
+ unsigned short int msg_nb = 1; // number of phy messages needed to transmit all PBs
+ uint8_t pb_nb = ctx->pbdma.nb_total;
+ uint8_t last_msg_pb_nb = PHY_PB_MAX_NB; // number of PBs to transmit into last phy message
+ unsigned short int msg_counter, pb_counter;
+
+ // find first descriptor
+ phy_pb_t *current_desc = ctx->pbdma.first_pb;
+
+ // set flags and pb size
+ unsigned short int pb_size = 128;
+ if (PHY_PB_SIZE_520 == ctx->control.current_tx_param.pb_size)
+ {
+ flags |= PHY_FLAG_PB512;
+ pb_size = 512;
+ }
+ if (!ctx->pbdma.bypass_aes)
+ {
+ flags |= PHY_FLAG_ENCRYPTED;
+ }
+
+ // set pb measurement
+ memset(pb_measurement, '\0', PHY_PB_MAX_NB*sizeof(uint32_t));
+
+ // initialize pb header
+ memset(pb_header, '\0', PHY_PB_MAX_NB*sizeof(uint32_t));
+
+ // initialize PB DMA status
+ memset(&ctx->pbdma.status_word, '\0', sizeof(phy_pbdma_status_t));
+
+ // reset internal PB counter
+ ctx->pbdma.index_current_pb = 0;
+
+ // calculate number of phy messages needed to transmit all PBs,
+ // and number of PBs to transmit into last phy message
+ if (PHY_PB_MAX_NB < ctx->pbdma.nb_total)
+ {
+ msg_nb = (unsigned short int)(ctx->pbdma.nb_total/PHY_PB_MAX_NB);
+ pb_nb = PHY_PB_MAX_NB;
+ if (0 != (ctx->pbdma.nb_total % PHY_PB_MAX_NB))
+ {
+ last_msg_pb_nb = (uint8_t)(ctx->pbdma.nb_total % PHY_PB_MAX_NB);
+ msg_nb++;
+ }
+ }
+
+ /* Send tonemap to Maximus. */
+ if ((PHY_MOD_TM == ctx->control.current_tx_param.mod) && (0 != maximus_phy_send_tonemap(ctx)))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d because TONEMAP message has not been correctly sent", errno);
+ }
+
+ // for each phy message to send
+ for (msg_counter=0; msg_counter<msg_nb; msg_counter++)
+ {
+ // init for phy message
+ memset(ctx->buffer, '\0', SCI_MSG_MAX_SIZE);
+ if (0 != sci_msg_init(&msg, ctx->buffer, SCI_MSG_MAX_SIZE))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when initializing SCI message", errno);
+ }
+ else
+ {
+ // in case of several phy messages to send, set number of PBs to send into last message
+ if ((1 != msg_nb) && (msg_nb-1 == msg_counter))
+ {
+ pb_nb = last_msg_pb_nb;
+ }
+
+ // for each pb to send into current message
+ for (pb_counter=0; pb_counter<pb_nb; pb_counter++)
+ {
+ if (ctx->pbdma.index_current_pb == ctx->pbdma.nb_ready)
+ {
+ /* When index_current_pb = nb_ready,
+ * PB null is sent until the end of the MPDU
+ * (even if, after, index_current_pb <= pb_nb_ready). */
+
+ current_desc = NULL;
+
+ /* If not enough PBs are available in PB DMA,
+ * PB DMA sets its status word (pb null and null pb index),
+ * but does not raise the PHY_HAL_INTERRUPT_PBDMA IT. */
+
+ ctx->pbdma.status_word.pb_null = 1;
+ ctx->pbdma.status_word.null_pb_index = ctx->pbdma.index_current_pb;
+ }
+
+ if (NULL != current_desc)
+ {
+ // set pb header of current pb
+ pb_header[pb_counter] = (uint32_t)current_desc->pb_tx.header;
+
+ // fill sci data
+ if ((int)pb_size != sci_msg_push(&msg, pb_size))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when pushing SCI message", errno);
+ }
+ else
+ {
+ memcpy(msg.data_begin, current_desc->blk.data, pb_size);
+
+ // find next pb
+ current_desc = PARENT_OF(phy_pb_t, blk, current_desc->blk.next);
+ }
+ }
+ else // PB null is sent until the end of the MPDU
+ {
+ // fill sci data
+ if ((int)pb_size != sci_msg_push(&msg, pb_size))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when pushing SCI message", errno);
+ }
+ else
+ {
+ memset(msg.data_begin, '\0', pb_size);
+ }
+ }
+ // increment internal PB counter
+ ctx->pbdma.index_current_pb++;
+
+ if (ctx->pbdma.index_current_pb == ctx->pbdma.nb_pb_it)
+ {
+ /* At the end of the nth PB transfer
+ * (i.e. index_current_pb = nb_pb_it),
+ * a PB end interrupt is simulated by calling phy_pbdma_cb callback. */
+
+ ctx->pbdma.status_word.pb_it = 1;
+ maximus_pending_isrs |= (1 << PHY_HAL_INTERRUPT_PBDMA);
+ }
+ }
+
+ // set mpdu format
+ // HomePlug AV specs => delimiter type (DT_AV) is described by bits 0-2 of first fc_av octet:
+ // 000 Beacon
+ // 001 SOF
+ // 010 SACK
+ // 011 RTS/CTS
+ // 100 SOUND
+ // 101 RSOF
+ mpdu_format = (uint8_t)((ctx->control.current_tx_param.fc_av[0] & 0x07) + 1); // +1 to have the correspondance with our own mpdu format definition (of 'phy_types.h')
+
+ // fill phy and sci header
+ if ( (0 != maximus_phy_fill_hdr(ctx,
+ &msg,
+ type,
+ mpdu_format,
+ pb_nb,
+ msg_nb,
+ (uint8_t)ctx->control.current_tx_param.fc_mode,
+ (uint8_t)ctx->control.current_tx_param.short_ppdu,
+ (uint8_t)ctx->control.current_tx_param.mod,
+ (uint8_t)ctx->control.current_tx_param.fecrate,
+ (uint8_t)ctx->control.current_tx_param.gil,
+ (uint8_t)ctx->control.current_tx_param.tonemap_index,
+ ctx->control.current_tx_param.tx_id,
+ flags,
+ ctx->control.current_tx_param.symbol_nb,
+ ctx->pbdma.iv,
+ ctx->pbdma.nek,
+ pb_measurement,
+ pb_header))
+ || (0 != sci_fill_hdr(my_station.sci, &msg, SCI_MSG_TYPE_PHY, 0 /* flags */)) )
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when filling header", errno);
+ }
+ else
+ {
+ // send the message
+ if (msg.length != sci_send(my_station.sci, &msg))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d when sending the MPDU", errno);
+ }
+ }
+ }
+ }
+
+ // once phy messages have been sent, set medium state to idle
+ ctx->access.medium_state = MAXIMUS_PHY_MEDIUM_IDLE;
+
+ // reset TX frame netclock message id
+ ctx->control.current_tx_param.tx_frame_netclock_id = 0;
+
+ if (ctx->pbdma.index_current_pb == ctx->pbdma.nb_total)
+ {
+ ctx->pbdma.status_word.end_tx_pb = 1; // it is the end of a PB transfer
+ }
+ }
+ }
+ }
+}
+
+
+/**
+ * Maximus RX activate callback called when the corresponding netclock message is received.
+ * \param data pointer to 'maximus_rx_activate_t' structure, containing argument 'flag' of 'phy_rx_activate'.
+ * set errno to:
+ * - EINVAL if data is null
+ */
+void
+maximus_phy_rx_activate_cb (void *data)
+{
+ /* This callback has to set ctx->control.pre_detection with the pre_detection value given as argument. */
+
+ dbg_assert_ptr(data);
+ if (NULL == data)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ maximus_phy_rx_activate_t *rx_activate_data = (maximus_phy_rx_activate_t*)data;
+ phy_t *ctx = rx_activate_data->ctx;
+
+ dbg_assert_ptr(ctx);
+ if (NULL == ctx)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ MAXIMUS_PHY_TRACE (M_RX_ACTIVATE, my_station.current_tick_tck);
+
+ ctx->control.pre_detection = rx_activate_data->pre_detection;
+
+ if (ctx->control.pre_detection)
+ {
+ // reset prp result to 'true'
+ ctx->access.prp_result = true;
+ }
+
+ // reset RX activate netclock message id
+ ctx->control.rx_param.rx_activate_netclock_id = 0;
+
+ // reset TX blocked
+ ctx->control.tx_blocked_on_false_alarm = false;
+ }
+ }
+}
+
+
+/**
+ * Maximus extra timer program callback called when the corresponding netclock message is received.
+ * \param data pointer to 'phy_t' structure.
+ * set errno to:
+ * - EINVAL if data or ctx->control.extra_timer_cb are null
+ */
+void maximus_phy_extra_timer_cb (void *data)
+{
+ /* PHY calls the 'extra_timer_cb' when extra timer expires.
+ * Not canceled on preamble reception. */
+
+ dbg_assert_ptr(data);
+ if (NULL == data)
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ phy_t *ctx = (phy_t*)data;
+
+ dbg_assert_ptr(ctx);
+ dbg_assert_ptr(ctx->control.extra_timer_cb);
+ if ((NULL == ctx)
+ || (NULL == ctx->control.extra_timer_cb))
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ ctx->control.current_cb = ctx->control.extra_timer_cb;
+ maximus_pending_isrs |= (1 << PHY_HAL_INTERRUPT_PHY);
+ }
+ }
+}
+
diff --git a/cesar/hal/phy/maximus/src/maximus_tmdma.c b/cesar/hal/phy/maximus/src/maximus_tmdma.c
new file mode 100644
index 0000000000..af9677b543
--- /dev/null
+++ b/cesar/hal/phy/maximus/src/maximus_tmdma.c
@@ -0,0 +1,111 @@
+/* Cesar project {{{
+ *
+ * Copyright (C) 2007 Spidcom
+ *
+ * <<<Licence>>>
+ *
+ * }}} */
+/**
+ * \file hal/phy/maximus/src/maximus_tmdma.c
+ * \brief HAL Phy Tone Map DMA functions for Maximus.
+ * \ingroup hal_phy_maximus
+ */
+
+#include "common/std.h"
+#include "hal/phy/maximus/inc/maximus_phy_ctx.h"
+#include "hal/phy/maximus/inc/maximus_phy_ctrl.h"
+#include "mac/common/defs.h" // for 'MAC_PB520_BYTES'
+#include <errno.h>
+
+/**
+ * Set the tone mask and its related parameters.
+ * \param ctx phy context
+ * \param tonemask tonemask block descriptor
+ * \param carrier_nb number of active carriers in the given tone mask
+ * set errno to:
+ * - EINVAL if ctx or tonemask are null
+ *
+ * This also set ROBO modes parameters, HP1.0 mask and other tone mask related
+ * registers.
+ */
+void
+phy_set_tonemask (phy_t *ctx, u8 *tonemask, uint carrier_nb)
+{
+ /* Set the carrier_nb value of PHY context.
+ * Copy tonemask contents into PHY context. */
+
+ dbg_assert_ptr(ctx);
+ dbg_assert_ptr(tonemask);
+ if ((NULL == ctx)
+ || (NULL == tonemask))
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ MAXIMUS_PHY_TRACE (SET_TONEMASK, carrier_nb);
+
+ // set number of carriers
+ ctx->tmdma.carrier_nb = carrier_nb;
+
+ // copy tonemask (1 bit per carrier)
+ memcpy(ctx->tmdma.tonemask, tonemask, (PHY_CARRIER_NB+7)/8);
+
+ /* Send carrier_nb and tonemask to Maximus. */
+ if (0 != maximus_phy_send_tonemask(ctx))
+ {
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ dbg_assert_print(false, "errno = %d because TONEMASK message has not been correctly sent", errno);
+ }
+ }
+}
+
+
+/**
+ * Transfer tone map to hardware using the TM DMA.
+ * \param ctx phy context
+ * \param tonemap_index tonemap index where to store tonemap
+ * \param tonemap tonemap blocks first descriptor
+ * set errno to:
+ * - EINVAL if ctx or tonemap are null, or if arguments are out-of-range or incorrect
+ *
+ * The tonemap uses two blocks.
+ */
+void
+phy_set_tonemap (phy_t *ctx, uint tonemap_index, blk_t *tonemap)
+{
+ /* Set the tonemap_index value of PHY context.
+ * Copy tonemap contents into PHY context. */
+
+ dbg_assert_ptr(ctx);
+ dbg_assert(TONEMAP_INDEX_NB > tonemap_index);
+ dbg_assert_ptr(tonemap);
+ dbg_assert_ptr(tonemap->next);
+ dbg_assert_ptr(tonemap->data);
+ dbg_assert(NULL == tonemap->next->next);
+ dbg_assert_ptr(tonemap->next->data);
+ if ((NULL == ctx)
+ || (TONEMAP_INDEX_NB <= tonemap_index)
+ || (NULL == tonemap)
+ || (NULL == tonemap->next) // tonemap uses two blocks
+ || (NULL == tonemap->data)
+ || (NULL != tonemap->next->next) // tonemap uses two blocks
+ || (NULL == tonemap->next->data))
+ {
+ errno = EINVAL;
+ station_log(&my_station, STATION_LOG_ERROR, STATION_LOGTYPE_PHY,
+ "%s: errno = %d", __FUNCTION__, errno);
+ }
+ else
+ {
+ MAXIMUS_PHY_TRACE (SET_TONEMAP, tonemap_index, tonemap);
+
+ // copy tonemap
+ memcpy(ctx->tmdma.tonemap[tonemap_index]->data, tonemap->data, MAC_PB520_BYTES);
+ memcpy(ctx->tmdma.tonemap[tonemap_index]->next->data, tonemap->next->data, (PHY_CARRIER_NB+1)/2-MAC_PB520_BYTES);
+ }
+}
+
diff --git a/cesar/hal/phy/maximus/src/maximus_trace.c b/cesar/hal/phy/maximus/src/maximus_trace.c
new file mode 100644
index 0000000000..cdd12e6ca9
--- /dev/null
+++ b/cesar/hal/phy/maximus/src/maximus_trace.c
@@ -0,0 +1,103 @@
+/* Cesar project {{{
+ *
+ * Copyright (C) 2008 Spidcom
+ *
+ * <<<Licence>>>
+ *
+ * }}} */
+/**
+ * \file hal/phy/maximus/src/maximus_trace.c
+ * \brief Define PHY trace events.
+ * \ingroup hal_phy_maximus
+ */
+#include "common/std.h"
+
+#include "inc/maximus_phy_ctx.h"
+
+#define MAXIMUS_PHY_TRACE_EVENT(id, format, args...) \
+ TRACE_EVENT (MAXIMUS_PHY_TRACE_ ## id, format, ## args)
+
+void
+maximus_phy_trace_init (phy_t *ctx)
+{
+ static trace_namespace_t namespace;
+ static const trace_event_id_t event_ids[] =
+ {
+ MAXIMUS_PHY_TRACE_EVENT (INIT, "init"),
+ MAXIMUS_PHY_TRACE_EVENT (SET_TONEMASK, "set tonemask carrier_nb=%u"),
+ MAXIMUS_PHY_TRACE_EVENT (UNINIT, "uninit"),
+ MAXIMUS_PHY_TRACE_EVENT (RESET, "reset"),
+ MAXIMUS_PHY_TRACE_EVENT (CLOCK_SET_NUMERATOR,
+ "clock set numerator %u"),
+ MAXIMUS_PHY_TRACE_EVENT (SET_TONEMAP, "set tonemap %u, %x"),
+ MAXIMUS_PHY_TRACE_EVENT (TX_FC10, "tx fc10 %x"),
+ MAXIMUS_PHY_TRACE_EVENT (TX_PARAM,
+ "tx param mode=%u short=%b mod=%u "
+ "fecrate=%u pb_size=%u gil=%u tm=%u"),
+ MAXIMUS_PHY_TRACE_EVENT (TX_FRAME,
+ "tx frame date=%x want_conf=%b "
+ "stop_on_prp_lost=%b fc=%x"),
+ MAXIMUS_PHY_TRACE_EVENT (RX_PARAM, "rx param mode=%u"),
+ MAXIMUS_PHY_TRACE_EVENT (RX_ACTIVATE,
+ "rx activate date=%x flag=%b"),
+ MAXIMUS_PHY_TRACE_EVENT (RX_ACTIVATE_NOW,
+ "rx activate now flag=%b"),
+ MAXIMUS_PHY_TRACE_EVENT (RX_PREPARE,
+ "rx prepare short=%b mod=%u fecrate=%u "
+ "pb_size=%u gil=%u tm=%u symb_nb=%u"),
+ MAXIMUS_PHY_TRACE_EVENT (EXTRA_TIMER_PROGRAM,
+ "extra timer program %x"),
+ MAXIMUS_PHY_TRACE_EVENT (EXTRA_TIMER_CANCEL, "extra timer cancel"),
+ MAXIMUS_PHY_TRACE_EVENT (ACCESS_CONF_CB, "access conf cb"),
+ MAXIMUS_PHY_TRACE_EVENT (RX_FC_CB, "rx fc cb rx_date=%x"),
+ MAXIMUS_PHY_TRACE_EVENT (TX_FALSE_ALARM_CB, "tx false alarm cb"),
+ MAXIMUS_PHY_TRACE_EVENT (DEFERRED_CB, "deferred cb"),
+ MAXIMUS_PHY_TRACE_EVENT (EXTRA_TIMER_CB, "extra timer cb"),
+
+ MAXIMUS_PHY_TRACE_EVENT (ACCESS_BACKOFF_START,
+ "access backoff start date=%x cap=%u"),
+ MAXIMUS_PHY_TRACE_EVENT (ACCESS_BACKOFF_UPDATE,
+ "access backoff update cap=%u"),
+ MAXIMUS_PHY_TRACE_EVENT (ACCESS_TIMER_PROGRAM,
+ "access timer program date=%x"),
+ MAXIMUS_PHY_TRACE_EVENT (ACCESS_TIMER_CANCEL, "access timer cancel"),
+ MAXIMUS_PHY_TRACE_EVENT (ACCESS_CB, "access cb"),
+
+ MAXIMUS_PHY_TRACE_EVENT (PBDMA_START, "pbdma start bypass_aes=%b "
+ "nb_total=%u nb_ready=%u nb_pb_it=%u"),
+ MAXIMUS_PHY_TRACE_EVENT (PBDMA_UPDATE, "pbdma update nb_ready=%u "
+ "nb_pb_it=%u"),
+ MAXIMUS_PHY_TRACE_EVENT (PBDMA_START_CHANDATA,
+ "pbdma start chandata"),
+ MAXIMUS_PHY_TRACE_EVENT (PBDMA_CB, "pbdma cb %x"),
+
+ MAXIMUS_PHY_TRACE_EVENT (M_PRP0, " prp0", TIMESTAMP),
+ MAXIMUS_PHY_TRACE_EVENT (M_PRP1, " prp1", TIMESTAMP),
+ MAXIMUS_PHY_TRACE_EVENT (M_PRP_END, " prp end cap_sent=%u "
+ "cap_medium=%u cap_mpdu=%u", TIMESTAMP),
+ MAXIMUS_PHY_TRACE_EVENT (M_SLOT, " slot count=%d", TIMESTAMP),
+ MAXIMUS_PHY_TRACE_EVENT (M_PRE_DETECT, " pre detect", TIMESTAMP),
+ MAXIMUS_PHY_TRACE_EVENT (M_FC_DETECT, " fc detect", TIMESTAMP),
+ MAXIMUS_PHY_TRACE_EVENT (M_RECV_MPDU_PAYLOAD, " recv payload",
+ TIMESTAMP),
+ MAXIMUS_PHY_TRACE_EVENT (M_RECV_CHANDATA, " recv chandata",
+ TIMESTAMP),
+ MAXIMUS_PHY_TRACE_EVENT (M_TX_FRAME, " tx frame", TIMESTAMP),
+ MAXIMUS_PHY_TRACE_EVENT (M_TX_PRE, " tx pre", TIMESTAMP),
+ MAXIMUS_PHY_TRACE_EVENT (M_TX_FC, " tx fc", TIMESTAMP),
+ MAXIMUS_PHY_TRACE_EVENT (M_TX_SYMBOLS, " tx symb_nb=%u"),
+ MAXIMUS_PHY_TRACE_EVENT (M_TX_MPDU, " tx mpdu", TIMESTAMP),
+ MAXIMUS_PHY_TRACE_EVENT (M_RX_ACTIVATE, " rx activate", TIMESTAMP),
+ };
+ dbg_assert (ctx);
+ trace_namespace_init (&namespace, event_ids, COUNT (event_ids));
+ trace_buffer_add (&ctx->trace, "phy", 8, 16, true, &namespace);
+}
+
+void
+maximus_phy_trace_uninit (phy_t *ctx)
+{
+ dbg_assert (ctx);
+ trace_buffer_remove (&ctx->trace);
+}
+