static ssize_t usbtmc_generic_read()

in class/usbtmc.c [817:1046]


static ssize_t usbtmc_generic_read(struct usbtmc_file_data *file_data,
				   void __user *user_buffer,
				   u32 transfer_size,
				   u32 *transferred,
				   u32 flags)
{
	struct usbtmc_device_data *data = file_data->data;
	struct device *dev = &data->intf->dev;
	u32 done = 0;
	u32 remaining;
	const u32 bufsize = USBTMC_BUFSIZE;
	int retval = 0;
	u32 max_transfer_size;
	unsigned long expire;
	int bufcount = 1;
	int again = 0;

	/* mutex already locked */

	*transferred = done;

	max_transfer_size = transfer_size;

	if (flags & USBTMC_FLAG_IGNORE_TRAILER) {
		/* The device may send extra alignment bytes (up to
		 * wMaxPacketSize – 1) to avoid sending a zero-length
		 * packet
		 */
		remaining = transfer_size;
		if ((max_transfer_size % data->wMaxPacketSize) == 0)
			max_transfer_size += (data->wMaxPacketSize - 1);
	} else {
		/* round down to bufsize to avoid truncated data left */
		if (max_transfer_size > bufsize) {
			max_transfer_size =
				roundup(max_transfer_size + 1 - bufsize,
					bufsize);
		}
		remaining = max_transfer_size;
	}

	spin_lock_irq(&file_data->err_lock);

	if (file_data->in_status) {
		/* return the very first error */
		retval = file_data->in_status;
		spin_unlock_irq(&file_data->err_lock);
		goto error;
	}

	if (flags & USBTMC_FLAG_ASYNC) {
		if (usb_anchor_empty(&file_data->in_anchor))
			again = 1;

		if (file_data->in_urbs_used == 0) {
			file_data->in_transfer_size = 0;
			file_data->in_status = 0;
		}
	} else {
		file_data->in_transfer_size = 0;
		file_data->in_status = 0;
	}

	if (max_transfer_size == 0) {
		bufcount = 0;
	} else {
		bufcount = roundup(max_transfer_size, bufsize) / bufsize;
		if (bufcount > file_data->in_urbs_used)
			bufcount -= file_data->in_urbs_used;
		else
			bufcount = 0;

		if (bufcount + file_data->in_urbs_used > MAX_URBS_IN_FLIGHT) {
			bufcount = MAX_URBS_IN_FLIGHT -
					file_data->in_urbs_used;
		}
	}
	spin_unlock_irq(&file_data->err_lock);

	dev_dbg(dev, "%s: requested=%u flags=0x%X size=%u bufs=%d used=%d\n",
		__func__, transfer_size, flags,
		max_transfer_size, bufcount, file_data->in_urbs_used);

	while (bufcount > 0) {
		u8 *dmabuf = NULL;
		struct urb *urb = usbtmc_create_urb();

		if (!urb) {
			retval = -ENOMEM;
			goto error;
		}

		dmabuf = urb->transfer_buffer;

		usb_fill_bulk_urb(urb, data->usb_dev,
			usb_rcvbulkpipe(data->usb_dev, data->bulk_in),
			dmabuf, bufsize,
			usbtmc_read_bulk_cb, file_data);

		usb_anchor_urb(urb, &file_data->submitted);
		retval = usb_submit_urb(urb, GFP_KERNEL);
		/* urb is anchored. We can release our reference. */
		usb_free_urb(urb);
		if (unlikely(retval)) {
			usb_unanchor_urb(urb);
			goto error;
		}
		file_data->in_urbs_used++;
		bufcount--;
	}

	if (again) {
		dev_dbg(dev, "%s: ret=again\n", __func__);
		return -EAGAIN;
	}

	if (user_buffer == NULL)
		return -EINVAL;

	expire = msecs_to_jiffies(file_data->timeout);

	while (max_transfer_size > 0) {
		u32 this_part;
		struct urb *urb = NULL;

		if (!(flags & USBTMC_FLAG_ASYNC)) {
			dev_dbg(dev, "%s: before wait time %lu\n",
				__func__, expire);
			retval = wait_event_interruptible_timeout(
				file_data->wait_bulk_in,
				usbtmc_do_transfer(file_data),
				expire);

			dev_dbg(dev, "%s: wait returned %d\n",
				__func__, retval);

			if (retval <= 0) {
				if (retval == 0)
					retval = -ETIMEDOUT;
				goto error;
			}
		}

		urb = usb_get_from_anchor(&file_data->in_anchor);
		if (!urb) {
			if (!(flags & USBTMC_FLAG_ASYNC)) {
				/* synchronous case: must not happen */
				retval = -EFAULT;
				goto error;
			}

			/* asynchronous case: ready, do not block or wait */
			*transferred = done;
			dev_dbg(dev, "%s: (async) done=%u ret=0\n",
				__func__, done);
			return 0;
		}

		file_data->in_urbs_used--;

		if (max_transfer_size > urb->actual_length)
			max_transfer_size -= urb->actual_length;
		else
			max_transfer_size = 0;

		if (remaining > urb->actual_length)
			this_part = urb->actual_length;
		else
			this_part = remaining;

		print_hex_dump_debug("usbtmc ", DUMP_PREFIX_NONE, 16, 1,
			urb->transfer_buffer, urb->actual_length, true);

		if (copy_to_user(user_buffer + done,
				 urb->transfer_buffer, this_part)) {
			usb_free_urb(urb);
			retval = -EFAULT;
			goto error;
		}

		remaining -= this_part;
		done += this_part;

		spin_lock_irq(&file_data->err_lock);
		if (urb->status) {
			/* return the very first error */
			retval = file_data->in_status;
			spin_unlock_irq(&file_data->err_lock);
			usb_free_urb(urb);
			goto error;
		}
		spin_unlock_irq(&file_data->err_lock);

		if (urb->actual_length < bufsize) {
			/* short packet or ZLP received => ready */
			usb_free_urb(urb);
			retval = 1;
			break;
		}

		if (!(flags & USBTMC_FLAG_ASYNC) &&
		    max_transfer_size > (bufsize * file_data->in_urbs_used)) {
			/* resubmit, since other buffers still not enough */
			usb_anchor_urb(urb, &file_data->submitted);
			retval = usb_submit_urb(urb, GFP_KERNEL);
			if (unlikely(retval)) {
				usb_unanchor_urb(urb);
				usb_free_urb(urb);
				goto error;
			}
			file_data->in_urbs_used++;
		}
		usb_free_urb(urb);
		retval = 0;
	}

error:
	*transferred = done;

	dev_dbg(dev, "%s: before kill\n", __func__);
	/* Attention: killing urbs can take long time (2 ms) */
	usb_kill_anchored_urbs(&file_data->submitted);
	dev_dbg(dev, "%s: after kill\n", __func__);
	usb_scuttle_anchored_urbs(&file_data->in_anchor);
	file_data->in_urbs_used = 0;
	file_data->in_status = 0; /* no spinlock needed here */
	dev_dbg(dev, "%s: done=%u ret=%d\n", __func__, done, retval);

	return retval;
}