/* Cesar project {{{ * * Copyright (C) 2007 Spidcom * * <<>> * * }}} */ /** * \file hal/phy/src/bridgedma.c * \brief HAL Phy Bridge DMA functions for the prototype. * \ingroup hal_phy * * Add in the config file the CONFIG_BRIDGEDMA_PROTO = y to use this * bridgedma. */ #include #include "cyg/hal/hal_io.h" #include "common/std.h" #include "hal/phy/inc/bridgedma.h" #include "hal/phy/inc/bridgedma_soft.h" #include #include #include "lib/bitstream.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)) #ifdef __sparc__ #define bridgedma_crc_compute_continue_block crc_compute_continue_block_le #define bridgedma_crc_compute_block crc_compute_block_le #else #define bridgedma_crc_compute_continue_block crc_compute_continue_block #define bridgedma_crc_compute_block crc_compute_block #endif static cyg_uint32 _bridgedma_ecos_isr(cyg_vector_t vector, cyg_addrword_t data) { volatile uint* ecos_it_register = (uint*) BRIDGEDMA_PROTO_ECOS_IT_DISABLE_REGISTER; /* nothing to do except calling the bridgedma callback */ phy_bridgedma_t *bridgedma_ctx; /* Reset the IT interruption. */ *ecos_it_register &= ~BRIDGEDMA_ECOS_IT_MASK; cyg_interrupt_mask(BRIDGEDMA_ECOS_IT); bridgedma_ctx = (phy_bridgedma_t *)data; 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_interrupt_unmask(BRIDGEDMA_ECOS_IT); 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_interrupt_unmask(BRIDGEDMA_ECOS_IT); return; } /** * 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; /* init the crc context */ bridgedma_ctx.proto_ctx.crc_ctx.width = 32; bridgedma_ctx.proto_ctx.crc_ctx.generator = HPAV_CRC32_GENERATOR; bridgedma_ctx.proto_ctx.crc_ctx.init = HPAV_CRC32_INIT; bridgedma_ctx.proto_ctx.crc_ctx.refin = true; bridgedma_ctx.proto_ctx.crc_ctx.refout = true; bridgedma_ctx.proto_ctx.crc_ctx.xorout = 0xffffffff; bridgedma_ctx.proto_ctx.crc_ctx.reg_init = 0; bridgedma_ctx.proto_ctx.crc_ctx.table.t32 = enc_tab; crc_init(&bridgedma_ctx.proto_ctx.crc_ctx); /* register ISR et DSR to eCos */ cyg_interrupt_create(BRIDGEDMA_ECOS_IT, PHY_HAL_INTERRUPT_PRIORITY, (cyg_addrword_t)&bridgedma_ctx, _bridgedma_ecos_isr, _bridgedma_ecos_dsr, &bridgedma_ctx.proto_ctx.interrupt_handle, &bridgedma_ctx.proto_ctx.interrupt); cyg_interrupt_attach(bridgedma_ctx.proto_ctx.interrupt_handle); // Create the thread. cyg_thread_create(BRIDGEDMA_THREAD_PRIORITY, &phy_bridgedma_proto_process, (cyg_addrword_t) &bridgedma_ctx, "BRIDGEDMA_PROTO", bridgedma_ctx.proto_ctx.thread_stack, BRIDGEDMA_THREAD_STACK_SIZE, &bridgedma_ctx.proto_ctx.thread_handle, &bridgedma_ctx.proto_ctx.thread); return &bridgedma_ctx; } /** * Reset and uninitialise the Bridge DMA. * \param ctx Bridge DMA context */ void phy_bridgedma_uninit (phy_bridgedma_t *ctx) { cyg_thread_suspend (ctx->proto_ctx.thread_handle); } /** * 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 */ static uint _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) bitstream_memcpy(*pb_data_ptr, mac_ptr + mac_cnt, pb_size - (*pb_data_ptr - (*pb_current)->data)); else bitstream_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) bitstream_memcpy(*pb_data_ptr, mac_ptr + mac_cnt, mac_len - mac_cnt); else bitstream_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 char * mac_ptr, *data_ptr; blk_t *pb_current; unsigned long icv_final, icv_compare; /* variable to write in the ecos raising it. */ volatile uint* ecos_enable_it = (uint*) BRIDGEDMA_PROTO_ECOS_IT_ENABLE_REGISTER; 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->proto_ctx.icv_current = crc_compute_begin(&ctx->proto_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... */ } } /* 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->proto_ctx.icv_current = bridgedma_crc_compute_continue_block( &ctx->proto_ctx.crc_ctx, ctx->proto_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->proto_ctx.icv_current = bridgedma_crc_compute_continue_block( &ctx->proto_ctx.crc_ctx, ctx->proto_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->proto_ctx.icv_current = bridgedma_crc_compute_continue_block( &ctx->proto_ctx.crc_ctx, ctx->proto_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->proto_ctx.crc_ctx, ctx->proto_ctx.icv_current); else icv_final = ctx->proto_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... */ } } /* 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->proto_ctx.icv_current = bridgedma_crc_compute_continue_block( &ctx->proto_ctx.crc_ctx, ctx->proto_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->proto_ctx.icv_current = bridgedma_crc_compute_continue_block( &ctx->proto_ctx.crc_ctx, ctx->proto_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->proto_ctx.icv_current = bridgedma_crc_compute_continue_block( &ctx->proto_ctx.crc_ctx, ctx->proto_ctx.icv_current, job->data_addr, job->data_len); } if(job->crc_store) { /* check crc */ icv_final = crc_compute_end( &ctx->proto_ctx.crc_ctx, ctx->proto_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); } } if(ctx->job_current->job_it) { *ecos_enable_it |= BRIDGEDMA_ECOS_IT_MASK; cyg_interrupt_unmask(BRIDGEDMA_ECOS_IT); #ifndef __sparc__ cyg_hal_sys_kill (cyg_hal_sys_getpid(), CYG_HAL_SYS_SIGIO); #endif } return true; } /** * 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; } else { /* there are still job inside */ dbg_assert(ctx->job_last); ctx->job_last->next = job_first; ctx->job_last = job_last; } /* Resume the bridge dma thread to process the jobs. */ cyg_thread_resume (ctx->proto_ctx.thread_handle); } void phy_bridgedma_proto_process (cyg_addrword_t data) { phy_bridgedma_t *bridgedma_ctx; dbg_assert (data); bridgedma_ctx = (phy_bridgedma_t *) data; while (true) { cyg_thread_yield(); if (bridgedma_ctx->job_current) { _job_process(bridgedma_ctx, bridgedma_ctx->job_current); bridgedma_ctx->job_current = bridgedma_ctx->job_current->next; } else { /* refresh bridge dma status */ bridgedma_ctx->status.running = 0; bridgedma_ctx->status.stop = 1; /* Stop the thread. */ cyg_thread_suspend (bridgedma_ctx->proto_ctx.thread_handle); } } }