int ps3_vuart_read()

in ps3-vuart.c [598:654]


int ps3_vuart_read(struct ps3_system_bus_device *dev, void *buf,
	unsigned int bytes)
{
	int result;
	struct ps3_vuart_port_priv *priv = to_port_priv(dev);
	unsigned long flags;
	struct list_buffer *lb, *n;
	unsigned long bytes_read;

	dev_dbg(&dev->core, "%s:%d: %u(%xh) bytes\n", __func__, __LINE__,
		bytes, bytes);

	spin_lock_irqsave(&priv->rx_list.lock, flags);

	/* Queue rx bytes here for polled reads. */

	while (priv->rx_list.bytes_held < bytes) {
		u64 tmp;

		result = ps3_vuart_queue_rx_bytes(dev, &tmp);
		if (result || !tmp) {
			dev_dbg(&dev->core, "%s:%d: starved for %lxh bytes\n",
				__func__, __LINE__,
				bytes - priv->rx_list.bytes_held);
			spin_unlock_irqrestore(&priv->rx_list.lock, flags);
			return -EAGAIN;
		}
	}

	list_for_each_entry_safe(lb, n, &priv->rx_list.head, link) {
		bytes_read = min((unsigned int)(lb->tail - lb->head), bytes);

		memcpy(buf, lb->head, bytes_read);
		buf += bytes_read;
		bytes -= bytes_read;
		priv->rx_list.bytes_held -= bytes_read;

		if (bytes_read < lb->tail - lb->head) {
			lb->head += bytes_read;
			dev_dbg(&dev->core, "%s:%d: buf_%lu: dequeued %lxh "
				"bytes\n", __func__, __LINE__, lb->dbg_number,
				bytes_read);
			spin_unlock_irqrestore(&priv->rx_list.lock, flags);
			return 0;
		}

		dev_dbg(&dev->core, "%s:%d: buf_%lu: free, dequeued %lxh "
			"bytes\n", __func__, __LINE__, lb->dbg_number,
			bytes_read);

		list_del(&lb->link);
		kfree(lb);
	}

	spin_unlock_irqrestore(&priv->rx_list.lock, flags);
	return 0;
}