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; } }
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; }