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