static rt_size_t _read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
    rt_size_t outsz = 0;
    struct rt_vbus_dev *vdev = dev->user_data;

    RT_ASSERT(vdev->chnr != 0);

    if (vdev->act == RT_NULL)
    {
        vdev->act = rt_vbus_data_pop(vdev->chnr);
        vdev->pos = 0;
    }

    while (1)
    {
        rt_err_t err;

        while (vdev->act)
        {
            rt_size_t cpysz;

            if (size - outsz > vdev->act->size - vdev->pos)
                cpysz = vdev->act->size - vdev->pos;
            else
                cpysz = size - outsz;

            rt_memcpy((char*)buffer + outsz, ((char*)(vdev->act+1)) + vdev->pos, cpysz);
            vdev->pos += cpysz;

            outsz += cpysz;
            if (outsz == size)
            {
                return outsz;
            }
            else if (outsz > size)
                RT_ASSERT(0);

            /* free old and get new */
            rt_free(vdev->act);
            vdev->act = rt_vbus_data_pop(vdev->chnr);
            vdev->pos = 0;
        }

        /* TODO: We don't want to touch the rx_indicate here. But this lead to
         * some duplication. Maybe we should find a better way to handle this.
         */
        if (rt_interrupt_get_nest() == 0)
        {
            err = rt_vbus_listen_on(vdev->chnr, RT_WAITING_FOREVER);
        }
        else
        {
            err = rt_vbus_listen_on(vdev->chnr, 0);
        }
        if (err != RT_EOK)
        {
            rt_set_errno(err);
            return outsz;
        }
        vdev->act = rt_vbus_data_pop(vdev->chnr);
        vdev->pos = 0;
    }
}
Exemplo n.º 2
0
static ssize_t vbus_chnx_read(struct file *filp,
			      char __user *buf, size_t size,
			      loff_t *offp)
{
	unsigned long chnr = (unsigned long)filp->private_data;
	struct vbus_chnx_ctx *ctx = &_ctxs[chnr];
	size_t outsz = 0;

	if (ctx->datap == NULL) {
		ctx->datap = rt_vbus_data_pop(chnr);
		ctx->pos   = 0;
	}
	else if (ctx->pos == ctx->datap->size) {
		kfree(ctx->datap);
		ctx->datap = rt_vbus_data_pop(chnr);
		ctx->pos   = 0;
	}

	if (ctx->datap == NULL) {
		int err;

		if (!rt_vbus_connection_ok(chnr))
			return 0;

		if (filp->f_flags & O_NONBLOCK)
			return -EAGAIN;

		err = wait_event_interruptible(_ctxs[chnr].wait,
					       !rt_vbus_data_empty(chnr) ||
					       !rt_vbus_connection_ok(chnr));
		if (err)
			return err;

		ctx->datap = rt_vbus_data_pop(chnr);
		if (ctx->datap == NULL) {
			return 0;
		}
		ctx->pos = 0;
	}

	if (IS_ERR(ctx->datap)) {
		ssize_t err = PTR_ERR(ctx->datap);
		/* Cleanup datap so we don't crash if we access it again. */
		ctx->datap = NULL;
		return err;
	}

	while (ctx->datap) {
		size_t cpysz;

		if (size - outsz > ctx->datap->size - ctx->pos)
			cpysz = ctx->datap->size - ctx->pos;
		else
			cpysz = size - outsz;

		if (copy_to_user(buf + outsz,
				 ((char*)(ctx->datap+1)) + ctx->pos,
				 cpysz))
			return -EFAULT;
		ctx->pos += cpysz;

		outsz += cpysz;
		if (outsz == size) {
			return outsz;
		}
		BUG_ON(outsz > size);

		/* Free the old, get the new. */
		kfree(ctx->datap);
		ctx->datap = rt_vbus_data_pop(chnr);
		if (IS_ERR(ctx->datap)) {
			ctx->datap = NULL;
		}
		ctx->pos   = 0;
		/*pr_info("get other data %p, %d copied\n", ctx->datap, outsz);*/
	}
	return outsz;
}