/* * Copyright (c) 2004-2005 Endace Technology Ltd, Hamilton, New Zealand. * All rights reserved. * * This source code is proprietary to Endace Technology Limited and no part * of it may be redistributed, published or disclosed except as outlined in * the written contract supplied with this product. * * $Id: vdagapi.c 13301 2010-10-07 03:51:27Z alexey.korolev $ */ /* C Standard Library headers. */ #include #include #include #include #include #include #include /* Endace header */ #include "vdagapi.h" #ifndef NDEBUG #include "dagutil.h" #endif #define DAG_REG_BASE 0x900 typedef struct vdag_module_list* vdag_module_list_p; typedef struct vdag_module_list { dag_reg_t module_node; struct vdag_module_list *next; } vdag_module_list_t; /**************************************************************************** * FUNCTION: cleanup_module_list * DESCRIPTION: This is an internal functon that clean up the module linked list. * PARAMETER: head - a pointer to the list of module ****************************************************************************/ static void cleanup_module_list(vdag_module_list_p head) { vdag_module_list_p temp = NULL; vdag_module_list_p next_module = NULL; temp = head; while(temp != NULL) { next_module = temp->next; free(temp); temp = next_module; } } /**************************************************************************** * FUNCTION: read_module_list * DESCRIPTION: This is an internal function that read module list from DRB * space and then contruct a linked list to store each of modules. * PARAMETER: head - a pointer to the list of module * table - a pointer to an array of module * RETURN: -1 if failed, 0 if success ****************************************************************************/ static int read_module_list(vdag_module_list_p *head, dag_reg_t *table) { dag_reg_t* rptr = table; vdag_module_list_p tail = NULL; vdag_module_list_p node = NULL; if ((rptr->module != DAG_REG_START) || (rptr->addr != DAG_REG_ADDR_START)) { #ifndef NDEBUG dagutil_error("[read_module_list] invalid register start\n"); #endif return -1; } /* Read drb enumeration table and construct the linked list */ do { node = (vdag_module_list_t*) malloc(sizeof(vdag_module_list_t)); memset(node,0, sizeof(vdag_module_list_t)); memcpy(&node->module_node, rptr, sizeof(dag_reg_t)); /* append modules into list */ if (*head == NULL) *head = node; else tail->next = node; tail = node; } while (!((rptr++)->module == DAG_REG_END)); return 0; } /**************************************************************************** * FUNCTION: write_module_list * DESCRIPTION: This is an internal function that write module based linked list * back to DRB space. * PARAMETER: head - a pointer to the list of module * table - a pointer to an array of module * RETURN: -1 if failed, 0 if success ****************************************************************************/ static int write_module_list(vdag_module_list_p head, dag_reg_t *table) { dag_reg_t *rptr = table; vdag_module_list_p temp = NULL; temp = head; if (temp == NULL) { #ifndef NDEBUG dagutil_error("[write_module_list] head is empty\n"); #endif return -1; } while ( temp != NULL ) { *rptr = temp->module_node; rptr ++; temp = temp->next; } return 0; } /**************************************************************************** * FUNCTION: insert_module * DESCRIPTION: This is an internal function that add a firmware module into * linked list by given name, address and version of * module. * PARAMETER: head - a pointer to the list of module * new_module - a new module to be inserted to linked list * RETURN: -1 if failed, 0 if suceess ****************************************************************************/ static int insert_module(vdag_module_list_p head, dag_reg_t new_module) { vdag_module_list_p temp = NULL; vdag_module_list_p tail = NULL; vdag_module_list_p new_node = NULL; new_node = (vdag_module_list_t*) malloc(sizeof(vdag_module_list_t)); memcpy(&new_node->module_node, &new_module, sizeof(dag_reg_t)); temp = head; if (temp == NULL) { #ifndef NDEBUG dagutil_error("[insert_module] List is empty\n"); #endif return -1; } tail = temp->next; while (tail != NULL ) { /* appended module in middle of linked list if two of name of module are same */ if(temp->module_node.module == new_node->module_node.module) { if(temp->module_node.addr == new_node->module_node.addr) { #ifndef NDEBUG dagutil_error("[insert_module] address conflict\n"); #endif return -1; } new_node->next = temp->next; temp->next = new_node; break; } /* appended module into the end of linked list */ if(tail->module_node.module == DAG_REG_END) { temp->next = new_node; new_node->next = tail; break; } tail = tail->next; temp = temp->next; } return 0; } /**************************************************************************** * FUNCTION: remove_node * DESCRIPTION: This is an internal function that is remove a module from * linked list by given name of module and address. * PARAMETERS: head - a pointer to the list of module * remove_module - a module need to be removed * RETURN: -1 if failed, 0 if success ****************************************************************************/ static int remove_node(vdag_module_list_p head, dag_reg_t remove_module) { vdag_module_list_p temp = NULL; vdag_module_list_p next_module = NULL; temp = head; if (temp == NULL) { #ifndef NDEBUG dagutil_error("[remove_node] List is empty\n"); #endif return -1; } next_module = temp->next; while (next_module != NULL) { /* free a node from linked list by given name and address of module*/ if ( (next_module->module_node.module == remove_module.module) && (next_module->module_node.addr == remove_module.addr) ) { temp->next = next_module->next; free(next_module); break; } temp = temp->next; next_module = next_module->next; } return 0; } /*****************************************************************************/ int vdag_set_device_info(int vdagfd, uint16_t dev_id, uint8_t brd_rev) { uint32_t info; /* check the device id is invalid */ if ((dev_id & 0xFFFF) == 0 ) { #ifndef NDEBUG dagutil_error("[vdag_set_device_info] invalid device id\n"); #endif errno = EINVAL; return -1; } info = (brd_rev & 0xF) << 16 | dev_id; /* ioctl call to set the device id to driver. */ if(ioctl(vdagfd, DAGIOCDEVINFO, &info) < 0) { #ifndef NDEBUG dagutil_error("[vdag_set_device_id] ioctl DAGIOCDEVINFO: fail%s\n", strerror(errno)); #endif return -1; } return 0; } /*****************************************************************************/ int vdag_insert_module(int vdagfd, uint32_t reg_module, uint32_t addr, uint32_t ver) { uint8_t *iom; dag_reg_t *rptr; dag_reg_t new_module; /* new module to be insert */ vdag_module_list_p list = NULL; new_module.addr = addr & 0xFFFF; new_module.module = reg_module & 0xFF; new_module.version = ver & 0xF; new_module.flags = 0; if ((new_module.module == DAG_REG_START) || (new_module.addr == DAG_REG_ADDR_START) || (new_module.module == DAG_REG_END) || (new_module.addr == DAG_REG_ADDR_END)) { #ifndef NDEBUG dagutil_error("[vdag_insert_module] Invalid module"); #endif errno = EINVAL; return -1; } iom = dag_iom(vdagfd); if (iom == NULL) { errno = EBADF; return -1; } rptr = (dag_reg_t*)(iom+DAG_REG_BASE); /* read DRB space and construct it in linked list */ if (read_module_list(&list, rptr) < 0) { #ifndef NDEBUG dagutil_error("[vdag_insert_module] read drb space failed\n"); #endif errno = EIO; return -1; } /* remove a module from linked list with specific module name and address */ if (insert_module(list, new_module) < 0) { #ifndef NDEBUG dagutil_error("[vdag_insert_module] insert node failed\n"); #endif return -1; } /* write linked list back to DRB space */ if (write_module_list(list, rptr) < 0) { #ifndef NDEBUG dagutil_error("[vdag_insert_module] write drb space failed\n"); #endif errno = EIO; return -1; } cleanup_module_list(list); return 0; } /*****************************************************************************/ int vdag_remove_module(int vdagfd, uint32_t reg_module, uint32_t addr) { uint8_t *iom; dag_reg_t *rptr; dag_reg_t remove_module; /* module to be remove */ vdag_module_list_p list = NULL; remove_module.addr = addr & 0xFFFF; remove_module.module = reg_module & 0xFF; remove_module.flags = 0; remove_module.version = 0; if ((remove_module.module == DAG_REG_START) || (remove_module.addr == DAG_REG_ADDR_START) || (remove_module.module == DAG_REG_END) || (remove_module.addr == DAG_REG_ADDR_END)) { #ifndef NDEBUG dagutil_error( "[vdag_remove_module] can't remove this module"); #endif errno = EINVAL; return -1; } iom = dag_iom(vdagfd); if (iom == NULL) { errno = EBADF; return -1; } rptr = (dag_reg_t*)(iom+DAG_REG_BASE); /* read DRB space and construct it in linked list */ if (read_module_list(&list, rptr) < 0) { #ifndef NDEBUG dagutil_error("[vdag_remove_module] read drb space failed\n"); #endif errno = EIO; return -1; } /* remove a module from linked list with specific module name and address */ if (remove_node(list, remove_module) < 0) { #ifndef NDEBUG dagutil_error("vdag_remove_module] remove node failed\n"); #endif return -1; } /* write linked list back to DRB space */ if (write_module_list(list, rptr) < 0) { #ifndef NDEBUG dagutil_error("vdag_remove_module] write drb space failed\n"); #endif errno = EIO; return -1; } cleanup_module_list(list); return 0; } /*****************************************************************************/ int vdag_set_stream_count(int vdagfd, uint8_t rx_stream_num, uint8_t tx_stream_num) { int total_stream_num; uint8_t *iom; dag_reg_t *rptr; vdag_module_list_p list = NULL; int prev_value; iom = dag_iom(vdagfd); if (iom == NULL) { errno = EBADF; return -1; } if (vdag_lock_all_stream(vdagfd)) { errno = EACCES; goto exit; } rptr = (dag_reg_t*)(iom+DAG_REG_BASE); /* read DRB space and construct it in linked list */ if (read_module_list(&list, rptr) < 0) { #ifndef NDEBUG dagutil_error("[vdag_set_stream_count] read drb space failed\n"); #endif errno = EIO; return -1; } /* * find the PBM module and set the total number of streams into the * address of stream count */ while (list != NULL) { if (list->module_node.module == DAG_REG_PBM) { switch (list->module_node.version) { case 0: break; case 1: case 2: // version 2 CSBM total_stream_num = ((rx_stream_num & 0xf) << 20) | ((tx_stream_num & 0xf) << 24); prev_value = *(uint32_t*)(iom + list->module_node.addr); *(uint32_t*)(iom + list->module_node.addr) = total_stream_num | prev_value; break; case 3: // version 3 HSBM total_stream_num = (rx_stream_num & 0xfff) | ((tx_stream_num & 0xfff) << 16); *(uint32_t*)(iom + list->module_node.addr + 0x0C) = total_stream_num; break; default: errno = EIO; return -1; } } else if (list->module_node.module == DAG_REG_STREAM_FTR) { switch (list->module_node.version) { case 0: *(uint32_t*)(iom + list->module_node.addr) = 0x04000000 + rx_stream_num; break; default: errno = EIO; return -1; } } list = list->next; } cleanup_module_list(list); /* read DRB space and construct it in linked list */ if (read_module_list(&list, rptr) < 0) { #ifndef NDEBUG dagutil_error("[vdag_set_stream_count] read drb space failed\n"); #endif errno = EIO; return -1; } /* * find the Stream Feature module and set the total number of streams */ while (list != NULL) { list = list->next; } cleanup_module_list(list); exit: vdag_unlock_all_stream(vdagfd); return 0; } /*****************************************************************************/ int vdag_lock_all_stream(int vdagfd) { int lock; lock = (DAG_LOCK_CARD | DAG_LOCK_OP_ACQUIRE); if(ioctl(vdagfd, DAGIOCLOCK, &lock) < 0) { return -1; } return 0; } /*****************************************************************************/ int vdag_unlock_all_stream(int vdagfd) { int unlock; unlock = (DAG_LOCK_CARD | DAG_LOCK_OP_RELEASE); if(ioctl(vdagfd, DAGIOCLOCK, &unlock) < 0) { return -1; } return 0; } /*****************************************************************************/ int vdag_alloc_memory(int vdagfd, uint32_t size, int32_t node) { user_mem_t mem_user; mem_user.node = node; mem_user.membuf_size=size; if (vdag_lock_all_stream(vdagfd)) { errno = EACCES; return -1; } /* free memory */ if (ioctl(vdagfd, DAGIOCFREEMEM) < 0) { errno = EFAULT; goto exit; } /* allocate memory */ if (ioctl(vdagfd, DAGIOCALLOCMEM, &mem_user) < 0) { errno = ENOMEM; goto exit; } vdag_unlock_all_stream(vdagfd); return 0; exit: vdag_unlock_all_stream(vdagfd); return -1; } .