/************************************************************************* * * FUNCTION * * _open * * DESCRIPTION * * Open a file. Minimal implementation * *************************************************************************/ int _open(const char * filename, int flags, int mode) { int filename_len = strlen(filename) + 1; int payload_size = sizeof(struct _sys_rpc) + filename_len; int retval = -1; if ((!filename) || (filename_len > FILE_NAME_LEN)) { return -1; } /* Construct rpc payload */ rpc_data->rpc->id = OPEN_SYSCALL_ID; rpc_data->rpc->sys_call_args.int_field1 = flags; rpc_data->rpc->sys_call_args.int_field2 = mode; rpc_data->rpc->sys_call_args.data_len = filename_len; memcpy(&rpc_data->rpc->sys_call_args.data, filename, filename_len); /* Transmit rpc request */ env_lock_mutex(rpc_data->rpc_lock); send_rpc((void*) rpc_data->rpc, payload_size); env_unlock_mutex(rpc_data->rpc_lock); /* Wait for response from proxy on master */ env_acquire_sync_lock(rpc_data->sync_lock); /* Obtain return args and return to caller */ if (rpc_data->rpc_response->id == OPEN_SYSCALL_ID) { retval = rpc_data->rpc_response->sys_call_args.int_field1; } return retval; }
/************************************************************************* * * FUNCTION * * _write * * DESCRIPTION * * Low level function to redirect IO to serial. * *************************************************************************/ int _write(int fd, const char * ptr, int len) { int retval = -1; int payload_size = sizeof(struct _sys_rpc) + len; int null_term = 0; if (fd == 1) { null_term = 1; } rpc_data->rpc->id = WRITE_SYSCALL_ID; rpc_data->rpc->sys_call_args.int_field1 = fd; rpc_data->rpc->sys_call_args.int_field2 = len; rpc_data->rpc->sys_call_args.data_len = len + null_term; memcpy(rpc_data->rpc->sys_call_args.data, ptr, len); if (null_term) { *(char*) (rpc_data->rpc->sys_call_args.data + len + null_term) = 0; } env_lock_mutex(rpc_data->rpc_lock); send_rpc((void*) rpc_data->rpc, payload_size); env_unlock_mutex(rpc_data->rpc_lock); env_acquire_sync_lock(rpc_data->sync_lock); if (rpc_data->rpc_response->id == WRITE_SYSCALL_ID) { retval = rpc_data->rpc_response->sys_call_args.int_field1; } return retval; }
/** * _rpmsg_create_channel * * Creates new rpmsg channel with the given parameters. * * @param rdev - pointer to remote device which contains the channel * @param name - name of the device * @param src - source address for the rpmsg channel * @param dst - destination address for the rpmsg channel * * @return - pointer to new rpmsg channel * */ struct rpmsg_channel *_rpmsg_create_channel(struct remote_device *rdev, char *name, unsigned long src, unsigned long dst) { struct rpmsg_channel *rp_chnl; struct llist *node; rp_chnl = env_allocate_memory(sizeof(struct rpmsg_channel)); if (rp_chnl) { env_memset(rp_chnl, 0x00, sizeof(struct rpmsg_channel)); env_strncpy(rp_chnl->name, name, sizeof(rp_chnl->name)); rp_chnl->src = src; rp_chnl->dst = dst; rp_chnl->rdev = rdev; /* Place channel on channels list */ node = env_allocate_memory(sizeof(struct llist)); if (!node) { env_free_memory(rp_chnl); return RPMSG_NULL ; } node->data = rp_chnl; env_lock_mutex(rdev->lock); add_to_list(&rdev->rp_channels , node); env_unlock_mutex(rdev->lock); } return rp_chnl; }
/************************************************************************* * * FUNCTION * * _read * * DESCRIPTION * * Low level function to redirect IO to serial. * *************************************************************************/ int _read(int fd, char * buffer, int buflen) { int payload_size = sizeof(struct _sys_rpc); int retval = -1; if (!buffer || !buflen) return retval; /* Construct rpc payload */ rpc_data->rpc->id = READ_SYSCALL_ID; rpc_data->rpc->sys_call_args.int_field1 = fd; rpc_data->rpc->sys_call_args.int_field2 = buflen; rpc_data->rpc->sys_call_args.data_len = 0; /*not used*/ /* Transmit rpc request */ env_lock_mutex(rpc_data->rpc_lock); get_response=0; send_rpc((void*) rpc_data->rpc, payload_size); env_unlock_mutex(rpc_data->rpc_lock); /* Wait for response from proxy on master */ env_acquire_sync_lock(rpc_data->sync_lock); /* Obtain return args and return to caller */ if (rpc_data->rpc_response->id == READ_SYSCALL_ID) { if (rpc_data->rpc_response->sys_call_args.int_field1 > 0) { memcpy(buffer, rpc_data->rpc_response->sys_call_args.data, rpc_data->rpc_response->sys_call_args.data_len); } retval = rpc_data->rpc_response->sys_call_args.int_field1; } return retval; }
/** * rpmsg_send_ns_message * * Sends name service announcement to remote device * * @param rdev - pointer to remote device * @param rp_chnl - pointer to rpmsg channel * @param flags - Channel creation/deletion flags * */ void rpmsg_send_ns_message(struct remote_device *rdev, struct rpmsg_channel *rp_chnl, unsigned long flags) { struct rpmsg_hdr *rp_hdr; struct rpmsg_ns_msg *ns_msg; unsigned short idx; int len; env_lock_mutex(rdev->lock); /* Get Tx buffer. */ rp_hdr = (struct rpmsg_hdr *) rpmsg_get_tx_buffer(rdev, &len, &idx); if (!rp_hdr) return; /* Fill out name service data. */ rp_hdr->dst = RPMSG_NS_EPT_ADDR; rp_hdr->len = sizeof(struct rpmsg_ns_msg); ns_msg = (struct rpmsg_ns_msg *) rp_hdr->data; env_strncpy(ns_msg->name, rp_chnl->name, sizeof(rp_chnl->name)); ns_msg->flags = flags; ns_msg->addr = rp_chnl->src; /* Place the buffer on virtqueue. */ rpmsg_enqueue_buffer(rdev, rp_hdr, len, idx); /* Notify the other side that it has data to process. */ virtqueue_kick(rdev->tvq); env_unlock_mutex(rdev->lock); }
/** * _create_endpoint * * This function creates rpmsg endpoint. * * @param rdev - pointer to remote device * @param cb - Rx completion call back * @param priv - private data * @param addr - endpoint src address * * @return - pointer to endpoint control block * */ struct rpmsg_endpoint *_create_endpoint(struct remote_device *rdev, rpmsg_rx_cb_t cb, void *priv, unsigned long addr) { struct rpmsg_endpoint *rp_ept; struct llist *node; int status = RPMSG_SUCCESS; rp_ept = env_allocate_memory(sizeof(struct rpmsg_endpoint)); if (!rp_ept) { return RPMSG_NULL ; } env_memset(rp_ept, 0x00, sizeof(struct rpmsg_endpoint)); node = env_allocate_memory(sizeof(struct llist)); if (!node) { env_free_memory(rp_ept); return RPMSG_NULL; } env_lock_mutex(rdev->lock); if (addr != RPMSG_ADDR_ANY) { /* * Application has requested a particular src address for endpoint, * first check if address is available. */ if (!rpmsg_is_address_set(rdev->bitmap, RPMSG_ADDR_BMP_SIZE, addr)) { /* Mark the address as used in the address bitmap. */ rpmsg_set_address(rdev->bitmap, RPMSG_ADDR_BMP_SIZE, addr); } else { status = RPMSG_ERR_DEV_ADDR; } } else { addr = rpmsg_get_address(rdev->bitmap, RPMSG_ADDR_BMP_SIZE); if (addr < 0) { status = RPMSG_ERR_DEV_ADDR; } } /* Do cleanup in case of error and return */ if (status) { env_free_memory(node); env_free_memory(rp_ept); env_unlock_mutex(rdev->lock); return RPMSG_NULL; } rp_ept->addr = addr; rp_ept->cb = cb; rp_ept->priv = priv; node->data = rp_ept; add_to_list(&rdev->rp_endpoints, node); env_unlock_mutex(rdev->lock); return rp_ept; }
/** * rpmsg_destroy_ept * * This function deletes rpmsg endpoint and performs cleanup. * * @param rdev - pointer to remote device * @param rp_ept - pointer to endpoint to destroy * */ void _destroy_endpoint(struct remote_device *rdev, struct rpmsg_endpoint *rp_ept) { struct llist *node; node = rpmsg_rdev_get_endpoint_from_addr(rdev, rp_ept->addr); if (node) { env_lock_mutex(rdev->lock); rpmsg_release_address(rdev->bitmap, RPMSG_ADDR_BMP_SIZE, rp_ept->addr); remove_from_list(&rdev->rp_endpoints, node); env_unlock_mutex(rdev->lock); env_free_memory(node); } env_free_memory(rp_ept); }
/** * _rpmsg_delete_channel * * Deletes given rpmsg channel. * * @param rp_chnl - pointer to rpmsg channel to delete * * return - none */ void _rpmsg_delete_channel(struct rpmsg_channel * rp_chnl) { struct llist *node; if (rp_chnl) { node = rpmsg_rdev_get_chnl_node_from_id(rp_chnl->rdev, rp_chnl->name); if (node) { env_lock_mutex(rp_chnl->rdev->lock); remove_from_list(&rp_chnl->rdev->rp_channels, node); env_unlock_mutex(rp_chnl->rdev->lock); env_free_memory(node); } env_free_memory(rp_chnl); } }
/** * rpmsg_rdev_get_endpoint_from_addr * * This function returns endpoint node based on src address. * * @param rdev - pointer remote device control block * @param addr - src address * * @return - endpoint node * */ struct llist *rpmsg_rdev_get_endpoint_from_addr(struct remote_device *rdev, unsigned long addr) { struct llist *rp_ept_lut_head; rp_ept_lut_head = rdev->rp_endpoints; env_lock_mutex(rdev->lock); while (rp_ept_lut_head) { struct rpmsg_endpoint *rp_ept = (struct rpmsg_endpoint *)rp_ept_lut_head->data; if (rp_ept->addr == addr) { env_unlock_mutex(rdev->lock); return rp_ept_lut_head; } rp_ept_lut_head = rp_ept_lut_head->next; } env_unlock_mutex(rdev->lock); return RPMSG_NULL; }
/** * rpmsg_rdev_get_chnl_from_addr * * This function returns channel node based on src/dst address. * * @param rdev - pointer remote device control block * @param addr - src/dst address * * @return - channel node * */ struct llist *rpmsg_rdev_get_chnl_from_addr(struct remote_device *rdev, unsigned long addr) { struct rpmsg_channel *rp_chnl; struct llist *rp_chnl_head; rp_chnl_head = rdev->rp_channels; env_lock_mutex(rdev->lock); while (rp_chnl_head) { rp_chnl = (struct rpmsg_channel *)rp_chnl_head->data; if ((rp_chnl->src == addr) || (rp_chnl->dst == addr)) { env_unlock_mutex(rdev->lock); return rp_chnl_head; } rp_chnl_head = rp_chnl_head->next; } env_unlock_mutex(rdev->lock); return RPMSG_NULL; }
/************************************************************************* * * FUNCTION * * _close * * DESCRIPTION * * Close a file. Minimal implementation * *************************************************************************/ int _close(int fd) { int payload_size = sizeof(struct _sys_rpc); int retval = -1; rpc_data->rpc->id = CLOSE_SYSCALL_ID; rpc_data->rpc->sys_call_args.int_field1 = fd; rpc_data->rpc->sys_call_args.int_field2 = 0; /*not used*/ rpc_data->rpc->sys_call_args.data_len = 0; /*not used*/ env_lock_mutex(rpc_data->rpc_lock); send_rpc((void*) rpc_data->rpc, payload_size); env_unlock_mutex(rpc_data->rpc_lock); /* Wait for response from proxy on master */ env_acquire_sync_lock(rpc_data->sync_lock); if (rpc_data->rpc_response->id == CLOSE_SYSCALL_ID) { retval = rpc_data->rpc_response->sys_call_args.int_field1; } return retval; }
/** * rpmsg_rdev_get_chnl_node_from_id * * This function returns channel node based on channel name. * * @param stack - pointer to remote device * @param rp_chnl_id - rpmsg channel name * * @return - channel node * */ struct llist *rpmsg_rdev_get_chnl_node_from_id(struct remote_device *rdev, char *rp_chnl_id) { struct rpmsg_channel *rp_chnl; struct llist *rp_chnl_head; rp_chnl_head = rdev->rp_channels; env_lock_mutex(rdev->lock); while (rp_chnl_head) { rp_chnl = (struct rpmsg_channel *)rp_chnl_head->data; if (env_strncmp (rp_chnl->name, rp_chnl_id, sizeof(rp_chnl->name)) == 0) { env_unlock_mutex(rdev->lock); return rp_chnl_head; } rp_chnl_head = rp_chnl_head->next; } env_unlock_mutex(rdev->lock); return RPMSG_NULL; }
/** * rpmsg_get_buffer_size * * Returns buffer size available for sending messages. * * @param channel - pointer to rpmsg channel * * @return - buffer size * */ int rpmsg_get_buffer_size(struct rpmsg_channel *rp_chnl) { struct remote_device *rdev; int length; if (!rp_chnl) { return RPMSG_ERR_PARAM; } /* Get associated remote device for channel. */ rdev = rp_chnl->rdev; /* Validate device state */ if (rp_chnl->state != RPMSG_CHNL_STATE_ACTIVE || rdev->state != RPMSG_DEV_STATE_ACTIVE) { return RPMSG_ERR_DEV_STATE; } env_lock_mutex(rdev->lock); if (rdev->role == RPMSG_REMOTE) { /* * If device role is Remote then buffers are provided by us * (RPMSG Master), so just provide the macro. */ length = RPMSG_BUFFER_SIZE - sizeof(struct rpmsg_hdr); } else { /* * If other core is Master then buffers are provided by it, * so get the buffer size from the virtqueue. */ length = (int) virtqueue_get_desc_size(rdev->tvq) - sizeof(struct rpmsg_hdr); } env_unlock_mutex(rdev->lock); return length; }
/** * rpmsg_rx_callback * * Rx callback function. * * @param vq - pointer to virtqueue on which messages is received * */ void rpmsg_rx_callback(struct virtqueue *vq) { struct remote_device *rdev; struct virtio_device *vdev; struct rpmsg_channel *rp_chnl; struct rpmsg_endpoint *rp_ept; struct rpmsg_hdr *rp_hdr; struct llist *node; unsigned long len; unsigned short idx; struct llist *chnl_hd; vdev = (struct virtio_device *) vq->vq_dev; rdev = (struct remote_device *) vdev; chnl_hd = rdev->rp_channels; if ((chnl_hd != RPMSG_NULL) && (rdev->role == RPMSG_MASTER)) { rp_chnl = (struct rpmsg_channel *) chnl_hd->data; if (rp_chnl->state == RPMSG_CHNL_STATE_IDLE) { if (rdev->support_ns) { rp_chnl->state = RPMSG_CHNL_STATE_NS; rpmsg_send_ns_message(rdev, rp_chnl, RPMSG_NS_CREATE); } else { rp_chnl->state = RPMSG_CHNL_STATE_ACTIVE; } return; } } env_lock_mutex(rdev->lock); /* Process the received data from remote node */ rp_hdr = (struct rpmsg_hdr *) rpmsg_get_rx_buffer(rdev, &len, &idx); env_unlock_mutex(rdev->lock); while(rp_hdr) { /* Get the channel node from the remote device channels list. */ node = rpmsg_rdev_get_endpoint_from_addr(rdev, rp_hdr->dst); if (!node) /* Fatal error no endpoint for the given dst addr. */ return; rp_ept = (struct rpmsg_endpoint *) node->data; rp_chnl = rp_ept->rp_chnl; if ((rp_chnl) && (rp_chnl->state == RPMSG_CHNL_STATE_NS)) { /* First message from RPMSG Master, update channel * destination address and state */ rp_chnl->dst = rp_hdr->src; rp_chnl->state = RPMSG_CHNL_STATE_ACTIVE; /* Notify channel creation to application */ if (rdev->channel_created) { rdev->channel_created(rp_chnl); } } else { rp_ept->cb(rp_chnl, rp_hdr->data, rp_hdr->len, rp_ept->priv, rp_hdr->src); } env_lock_mutex(rdev->lock); /* Return used buffers. */ rpmsg_return_buffer(rdev, rp_hdr, len, idx); rp_hdr = (struct rpmsg_hdr *) rpmsg_get_rx_buffer(rdev, &len, &idx); env_unlock_mutex(rdev->lock); } }
/** * rpmsg_rx_callback * * Rx callback function. * * @param vq - pointer to virtqueue on which messages is received * */ void rpmsg_rx_callback(struct virtqueue *vq) { struct remote_device *rdev; struct virtio_device *vdev; struct rpmsg_channel *rp_chnl; struct rpmsg_endpoint *rp_ept; struct rpmsg_hdr *rp_hdr; struct llist *node; unsigned long len; unsigned short idx; struct llist *chnl_hd; vdev = (struct virtio_device *) vq->vq_dev; rdev = (struct remote_device *) vdev; chnl_hd = rdev->rp_channels; if ((chnl_hd != RPMSG_NULL) && (rdev->role == RPMSG_MASTER)) { rp_chnl = (struct rpmsg_channel *) chnl_hd->data; if (rp_chnl->state == RPMSG_CHNL_STATE_IDLE) { if (rdev->support_ns) { rp_chnl->state = RPMSG_CHNL_STATE_NS; rpmsg_send_ns_message(rdev, rp_chnl, RPMSG_NS_CREATE); } else { rp_chnl->state = RPMSG_CHNL_STATE_ACTIVE; } return; } } env_lock_mutex(rdev->lock); /* Process the received data from remote node */ rp_hdr = (struct rpmsg_hdr *) rpmsg_get_rx_buffer(rdev, &len, &idx); env_unlock_mutex(rdev->lock); while(rp_hdr) { /* Clear 'rp_hdr->reserved' field that is used as 'callback' output */ rp_hdr->reserved = 0; /* Get the channel node from the remote device channels list. */ node = rpmsg_rdev_get_endpoint_from_addr(rdev, rp_hdr->dst); if (!node) /* Fatal error no endpoint for the given dst addr. */ return; rp_ept = (struct rpmsg_endpoint *) node->data; rp_chnl = rp_ept->rp_chnl; if ((rp_chnl) && (rp_chnl->state == RPMSG_CHNL_STATE_NS)) { /* First message from RPMSG Master, update channel * destination address and state */ /* * Only for Remote */ rp_chnl->dst = rp_hdr->src; rp_chnl->state = RPMSG_CHNL_STATE_ACTIVE; /* Notify channel creation to application */ if (rdev->channel_created) { rdev->channel_created(rp_chnl); } } else if(len <= 0xFFFF) { if (!(rp_hdr->flags & RPMSG_DROP_HDR_FLAG)) { rp_ept->cb(rp_chnl, rp_hdr->data, rp_hdr->len, rp_ept->priv, rp_hdr->src); } } else { /* Any message with totlen > 65535 are dropped, no way to notify the user about it */ } env_lock_mutex(rdev->lock); /* Check whether callback wants to hold buffer */ if (rp_hdr->reserved & RPMSG_BUF_HELD) { /* 'rp_hdr->reserved' field is now used as storage for * 'idx' and 'len' to release buffer later */ ((struct rpmsg_hdr_reserved*)&rp_hdr->reserved)->idx = idx; ((struct rpmsg_hdr_reserved*)&rp_hdr->reserved)->totlen = len; } else { /* Return used buffers. */ rpmsg_return_buffer(rdev, rp_hdr, len, idx); } rp_hdr = (struct rpmsg_hdr *) rpmsg_get_rx_buffer(rdev, &len, &idx); env_unlock_mutex(rdev->lock); } }
int rpmsg_send_offchannel_raw(struct rpmsg_channel *rp_chnl, unsigned long src, unsigned long dst, char *data, int size, int wait) { struct remote_device *rdev; struct rpmsg_hdr *rp_hdr; void *buffer; int status = RPMSG_SUCCESS; unsigned short idx; int tick_count = 0; int buff_len; if (!rp_chnl) { return RPMSG_ERR_PARAM; } /* Get the associated remote device for channel. */ rdev = rp_chnl->rdev; /* Validate device state */ if (rp_chnl->state != RPMSG_CHNL_STATE_ACTIVE || rdev->state != RPMSG_DEV_STATE_ACTIVE) { return RPMSG_ERR_DEV_STATE; } /* Lock the device to enable exclusive access to virtqueues */ env_lock_mutex(rdev->lock); /* Get rpmsg buffer for sending message. */ buffer = rpmsg_get_tx_buffer(rdev, &buff_len, &idx); if (!buffer && !wait) { status = RPMSG_ERR_NO_MEM; } env_unlock_mutex(rdev->lock); if (status == RPMSG_SUCCESS) { while (!buffer) { /* * Wait parameter is true - pool the buffer for * 15 secs as defined by the APIs. */ env_sleep_msec(RPMSG_TICKS_PER_INTERVAL); env_lock_mutex(rdev->lock); buffer = rpmsg_get_tx_buffer(rdev, &buff_len, &idx); env_unlock_mutex(rdev->lock); tick_count += RPMSG_TICKS_PER_INTERVAL; if (tick_count >= (RPMSG_TICK_COUNT / RPMSG_TICKS_PER_INTERVAL)) { status = RPMSG_ERR_NO_BUFF; break; } } if (status == RPMSG_SUCCESS) { //FIXME : may be just copy the data size equal to buffer length and Tx it. if (size > (buff_len - sizeof(struct rpmsg_hdr))) status = RPMSG_ERR_BUFF_SIZE; if (status == RPMSG_SUCCESS) { rp_hdr = (struct rpmsg_hdr *) buffer; /* Initialize RPMSG header. */ rp_hdr->dst = dst; rp_hdr->src = src; rp_hdr->len = size; /* Copy data to rpmsg buffer. */ env_memcpy(rp_hdr->data, data, size); env_lock_mutex(rdev->lock); /* Enqueue buffer on virtqueue. */ status = rpmsg_enqueue_buffer(rdev, buffer, buff_len, idx); if (status == RPMSG_SUCCESS) { /* Let the other side know that there is a job to process. */ virtqueue_kick(rdev->tvq); } env_unlock_mutex(rdev->lock); } } } /* Do cleanup in case of error.*/ if (status != RPMSG_SUCCESS) { rpmsg_free_buffer(rdev, buffer); } return status; }