/** * rpmsg_virtio_ns_callback * * This callback handles name service announcement from the remote device * and creates/deletes rpmsg channels. * * @param ept - pointer to server channel control block. * @param data - pointer to received messages * @param len - length of received data * @param priv - any private data * @param src - source address * * @return - rpmag endpoint callback handled */ static int rpmsg_virtio_ns_callback(struct rpmsg_endpoint *ept, void *data, size_t len, uint32_t src, void *priv) { struct rpmsg_device *rdev = ept->rdev; struct rpmsg_virtio_device *rvdev = (struct rpmsg_virtio_device *)rdev; struct metal_io_region *io = rvdev->shbuf_io; struct rpmsg_endpoint *_ept; struct rpmsg_ns_msg *ns_msg; uint32_t dest; char name[RPMSG_NAME_SIZE]; (void)priv; (void)src; ns_msg = data; if (len != sizeof(*ns_msg)) /* Returns as the message is corrupted */ return RPMSG_SUCCESS; metal_io_block_read(io, metal_io_virt_to_offset(io, ns_msg->name), &name, sizeof(name)); dest = ns_msg->addr; /* check if a Ept has been locally registered */ metal_mutex_acquire(&rdev->lock); _ept = rpmsg_get_endpoint(rdev, name, RPMSG_ADDR_ANY, dest); if (ns_msg->flags & RPMSG_NS_DESTROY) { if (_ept) _ept->dest_addr = RPMSG_ADDR_ANY; metal_mutex_release(&rdev->lock); if (_ept && _ept->ns_unbind_cb) _ept->ns_unbind_cb(ept); } else { if (!_ept) { /* * send callback to application, that can * - create the associated endpoints. * - store information for future use. * - just ignore the request as service not supported. */ metal_mutex_release(&rdev->lock); if (rdev->ns_bind_cb) rdev->ns_bind_cb(rdev, name, dest); } else { _ept->dest_addr = dest; metal_mutex_release(&rdev->lock); } } return RPMSG_SUCCESS; }
/** * rpmsg_virtio_rx_callback * * Rx callback function. * * @param vq - pointer to virtqueue on which messages is received * */ static void rpmsg_virtio_rx_callback(struct virtqueue *vq) { struct virtio_device *vdev = vq->vq_dev; struct rpmsg_virtio_device *rvdev = vdev->priv; struct rpmsg_device *rdev = &rvdev->rdev; struct rpmsg_endpoint *ept; struct rpmsg_hdr *rp_hdr; uint32_t len; uint16_t idx; int status; metal_mutex_acquire(&rdev->lock); /* Process the received data from remote node */ rp_hdr = rpmsg_virtio_get_rx_buffer(rvdev, &len, &idx); metal_mutex_release(&rdev->lock); while (rp_hdr) { /* Get the channel node from the remote device channels list. */ metal_mutex_acquire(&rdev->lock); ept = rpmsg_get_ept_from_addr(rdev, rp_hdr->dst); metal_mutex_release(&rdev->lock); if (ept) { if (ept->dest_addr == RPMSG_ADDR_ANY) { /* * First message received from the remote side, * update channel destination address */ ept->dest_addr = rp_hdr->src; } status = ept->cb(ept, RPMSG_LOCATE_DATA(rp_hdr), rp_hdr->len, rp_hdr->src, ept->priv); RPMSG_ASSERT(status >= 0, "unexpected callback status\r\n"); } metal_mutex_acquire(&rdev->lock); /* Return used buffers. */ rpmsg_virtio_return_buffer(rvdev, rp_hdr, len, idx); rp_hdr = rpmsg_virtio_get_rx_buffer(rvdev, &len, &idx); if (rp_hdr == NULL) { /* tell peer we return some rx buffer */ virtqueue_kick(rvdev->rvq); } metal_mutex_release(&rdev->lock); } }
int rpmsg_virtio_get_buffer_size(struct rpmsg_device *rdev) { int size; struct rpmsg_virtio_device *rvdev; if (!rdev) return RPMSG_ERR_PARAM; metal_mutex_acquire(&rdev->lock); rvdev = (struct rpmsg_virtio_device *)rdev; size = _rpmsg_virtio_get_buffer_size(rvdev); metal_mutex_release(&rdev->lock); return size; }
int metal_condition_wait(struct metal_condition *cv, struct metal_mutex *m) { struct metal_mutex *tmpm = 0; int v; /* Check if the mutex has been acquired */ if (!cv || !m || !metal_mutex_is_acquired(m)) return -EINVAL; if (!atomic_compare_exchange_strong(&cv->m, &tmpm, m)) { if (m != tmpm) return -EINVAL; } v = atomic_load(&cv->v); /* Release the mutex first. */ metal_mutex_release(m); while (atomic_load(&cv->v) == v); /* Acquire the mutex again. */ metal_mutex_acquire(m); return 0; }
/** * This function sends rpmsg "message" to remote device. * * @param rdev - pointer to rpmsg device * @param src - source address of channel * @param dst - destination address of channel * @param data - data to transmit * @param size - size of data * @param wait - boolean, wait or not for buffer to become * available * * @return - size of data sent or negative value for failure. * */ static int rpmsg_virtio_send_offchannel_raw(struct rpmsg_device *rdev, uint32_t src, uint32_t dst, const void *data, int size, int wait) { struct rpmsg_virtio_device *rvdev; struct rpmsg_hdr rp_hdr; void *buffer = NULL; uint16_t idx; int tick_count; uint32_t buff_len; int status; struct metal_io_region *io; /* Get the associated remote device for channel. */ rvdev = metal_container_of(rdev, struct rpmsg_virtio_device, rdev); status = rpmsg_virtio_get_status(rvdev); /* Validate device state */ if (!(status & VIRTIO_CONFIG_STATUS_DRIVER_OK)) { return RPMSG_ERR_DEV_STATE; } if (wait) tick_count = RPMSG_TICK_COUNT / RPMSG_TICKS_PER_INTERVAL; else tick_count = 0; while (1) { int avail_size; /* Lock the device to enable exclusive access to virtqueues */ metal_mutex_acquire(&rdev->lock); avail_size = _rpmsg_virtio_get_buffer_size(rvdev); if (size <= avail_size) buffer = rpmsg_virtio_get_tx_buffer(rvdev, &buff_len, &idx); metal_mutex_release(&rdev->lock); if (buffer || !tick_count) break; if (avail_size != 0) return RPMSG_ERR_BUFF_SIZE; metal_sleep_usec(RPMSG_TICKS_PER_INTERVAL); tick_count--; } if (!buffer) return RPMSG_ERR_NO_BUFF; /* Initialize RPMSG header. */ rp_hdr.dst = dst; rp_hdr.src = src; rp_hdr.len = size; rp_hdr.reserved = 0; /* Copy data to rpmsg buffer. */ io = rvdev->shbuf_io; status = metal_io_block_write(io, metal_io_virt_to_offset(io, buffer), &rp_hdr, sizeof(rp_hdr)); RPMSG_ASSERT(status == sizeof(rp_hdr), "failed to write header\r\n"); status = metal_io_block_write(io, metal_io_virt_to_offset(io, RPMSG_LOCATE_DATA(buffer)), data, size); RPMSG_ASSERT(status == size, "failed to write buffer\r\n"); metal_mutex_acquire(&rdev->lock); /* Enqueue buffer on virtqueue. */ status = rpmsg_virtio_enqueue_buffer(rvdev, buffer, buff_len, idx); RPMSG_ASSERT(status == VQUEUE_SUCCESS, "failed to enqueue buffer\r\n"); /* Let the other side know that there is a job to process. */ virtqueue_kick(rvdev->svq); metal_mutex_release(&rdev->lock); return size; }