blob: 53913e03a90cb746b46805de358a8b6fd397d79b [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 - 2021 DisplayLink (UK) Ltd.
*/
#include "hpal_data.h"
#include <linux/slab.h>
#include <linux/uio.h>
#include "hcd.h"
#include "hpal.h"
#include "hpal_events.h"
#include "utils.h"
int mausb_send_in_data_msg(struct mausb_device *dev, struct mausb_event *event)
{
struct mausb_kvec_data_wrapper data_to_send;
struct kvec kvec[2];
struct urb *urb = (struct urb *)(event->data.urb);
bool setup_packet = (usb_endpoint_xfer_control(&urb->ep->desc) &&
urb->setup_packet);
u32 kvec_num = setup_packet ? 2 : 1;
enum mausb_channel channel;
data_to_send.kvec_num = kvec_num;
data_to_send.length = MAUSB_TRANSFER_HDR_SIZE +
(setup_packet ? MAUSB_CONTROL_SETUP_SIZE : 0);
/* Prepare transfer header kvec */
kvec[0].iov_base = event->data.hdr;
kvec[0].iov_len = MAUSB_TRANSFER_HDR_SIZE;
/* Prepare setup packet kvec */
if (setup_packet) {
kvec[1].iov_base = urb->setup_packet;
kvec[1].iov_len = MAUSB_CONTROL_SETUP_SIZE;
}
data_to_send.kvec = kvec;
channel = mausb_transfer_type_to_channel(event->data.transfer_type);
return mausb_send_data(dev, channel, &data_to_send);
}
void mausb_receive_in_data(struct mausb_event *event,
struct mausb_urb_ctx *urb_ctx)
{
struct urb *urb = urb_ctx->urb;
struct mausb_data_iter *iterator = &urb_ctx->iterator;
struct ma_usb_hdr_common *common_hdr =
(struct ma_usb_hdr_common *)event->data.recv_buf;
void *buffer;
u32 payload_size = common_hdr->length - MAUSB_TRANSFER_HDR_SIZE;
u32 data_written = 0;
buffer = shift_ptr(common_hdr, MAUSB_TRANSFER_HDR_SIZE);
data_written = mausb_data_iterator_write(iterator, buffer,
payload_size);
dev_vdbg(mausb_host_dev.this_device, "data_written=%d, payload_size=%d",
data_written, payload_size);
event->data.rem_transfer_size -= data_written;
if (event->data.transfer_eot) {
dev_vdbg(mausb_host_dev.this_device, "transfer_size=%d, rem_transfer_size=%d, status=%d",
event->data.transfer_size,
event->data.rem_transfer_size, event->status);
mausb_complete_request(urb, event->data.transfer_size -
event->data.rem_transfer_size,
event->status);
}
}
static int
mausb_init_data_out_header_chunk(struct ma_usb_hdr_common *common_hdr,
struct list_head *chunks_list,
u32 *num_of_data_chunks)
{
int status = mausb_add_data_chunk(common_hdr, MAUSB_TRANSFER_HDR_SIZE,
chunks_list);
if (!status)
++(*num_of_data_chunks);
return status;
}
static int mausb_init_control_data_chunk(struct mausb_event *event,
struct list_head *chunks_list,
u32 *num_of_data_chunks)
{
int status;
void *buffer = ((struct urb *)event->data.urb)->setup_packet;
if (!event->data.first_control_packet)
return 0;
status = mausb_add_data_chunk(buffer, MAUSB_CONTROL_SETUP_SIZE,
chunks_list);
if (!status)
++(*num_of_data_chunks);
return status;
}
static int
mausb_prepare_transfer_packet(struct mausb_kvec_data_wrapper *wrapper,
struct mausb_event *event,
struct mausb_data_iter *iterator)
{
u32 num_of_data_chunks = 0;
u32 num_of_payload_data_chunks = 0;
u32 payload_data_size = 0;
int status = 0;
struct list_head chunks_list;
struct list_head payload_data_chunks;
struct ma_usb_hdr_common *data_hdr = (struct ma_usb_hdr_common *)
event->data.hdr;
INIT_LIST_HEAD(&chunks_list);
/* Initialize data chunk for MAUSB header and add it to chunks list */
if (mausb_init_data_out_header_chunk(data_hdr, &chunks_list,
&num_of_data_chunks) < 0) {
status = -ENOMEM;
goto cleanup_data_chunks;
}
/*
* Initialize data chunk for MAUSB control setup packet and
* add it to chunks list
*/
if (mausb_init_control_data_chunk(event, &chunks_list,
&num_of_data_chunks) < 0) {
status = -ENOMEM;
goto cleanup_data_chunks;
}
/* Get data chunks for data payload to send */
INIT_LIST_HEAD(&payload_data_chunks);
payload_data_size =
((struct ma_usb_hdr_common *)event->data.hdr)->length -
MAUSB_TRANSFER_HDR_SIZE -
(event->data.first_control_packet ?
MAUSB_CONTROL_SETUP_SIZE : 0);
if (mausb_data_iterator_read(iterator, payload_data_size,
&payload_data_chunks,
&num_of_payload_data_chunks) < 0) {
status = -ENOMEM;
goto cleanup_data_chunks;
}
list_splice_tail(&payload_data_chunks, &chunks_list);
num_of_data_chunks += num_of_payload_data_chunks;
/* Map all data chunks to data wrapper */
if (mausb_init_data_wrapper(wrapper, &chunks_list,
num_of_data_chunks) < 0) {
status = -ENOMEM;
goto cleanup_data_chunks;
}
cleanup_data_chunks: /* Cleanup all allocated data chunks */
mausb_cleanup_chunks_list(&chunks_list);
return status;
}
int mausb_send_out_data_msg(struct mausb_device *dev, struct mausb_event *event,
struct mausb_urb_ctx *urb_ctx)
{
int status;
struct mausb_kvec_data_wrapper data;
enum mausb_channel channel;
status = mausb_prepare_transfer_packet(&data, event,
&urb_ctx->iterator);
if (status < 0) {
dev_err(mausb_host_dev.this_device, "Failed to prepare transfer packet");
return status;
}
channel = mausb_transfer_type_to_channel(event->data.transfer_type);
status = mausb_send_data(dev, channel, &data);
kfree(data.kvec);
return status;
}
void mausb_receive_out_data(struct mausb_event *event,
struct mausb_urb_ctx *urb_ctx)
{
struct urb *urb = urb_ctx->urb;
dev_vdbg(mausb_host_dev.this_device, "transfer_size=%d, rem_transfer_size=%d, status=%d",
event->data.transfer_size, event->data.rem_transfer_size,
event->status);
if (event->data.transfer_eot) {
mausb_complete_request(urb, urb->transfer_buffer_length -
event->data.rem_transfer_size,
event->status);
}
}
static inline u32
__mausb_isoch_prepare_read_size_block(struct ma_usb_hdr_isochreadsizeblock_std *
isoch_readsize_block, struct urb *urb)
{
u32 i;
u32 number_of_packets = (u32)urb->number_of_packets;
if (number_of_packets == 0)
return 0;
isoch_readsize_block->service_intervals = number_of_packets;
isoch_readsize_block->max_segment_length =
(u32)urb->iso_frame_desc[0].length;
for (i = 0; i < number_of_packets; ++i) {
urb->iso_frame_desc[i].status = 0;
urb->iso_frame_desc[i].actual_length = 0;
}
return sizeof(struct ma_usb_hdr_isochreadsizeblock_std);
}
int mausb_send_isoch_in_msg(struct mausb_device *dev, struct mausb_event *event)
{
u32 read_size_block_length = 0;
struct mausb_kvec_data_wrapper data_to_send;
struct kvec kvec[MAUSB_ISOCH_IN_KVEC_NUM];
struct ma_usb_hdr_isochtransfer_optional opt_isoch_hdr;
struct ma_usb_hdr_isochreadsizeblock_std isoch_readsize_block;
struct ma_usb_hdr_common *hdr =
(struct ma_usb_hdr_common *)event->data.hdr;
struct urb *urb = (struct urb *)event->data.urb;
enum mausb_channel channel;
data_to_send.kvec_num = 0;
data_to_send.length = 0;
/* Prepare transfer header kvec */
kvec[0].iov_base = event->data.hdr;
kvec[0].iov_len = MAUSB_TRANSFER_HDR_SIZE;
data_to_send.length += (u32)kvec[0].iov_len;
data_to_send.kvec_num++;
/* Prepare optional header kvec */
opt_isoch_hdr.timestamp = MA_USB_TRANSFER_RESERVED;
opt_isoch_hdr.mtd = MA_USB_TRANSFER_RESERVED;
kvec[1].iov_base = &opt_isoch_hdr;
kvec[1].iov_len = sizeof(struct ma_usb_hdr_isochtransfer_optional);
data_to_send.length += (u32)kvec[1].iov_len;
data_to_send.kvec_num++;
/* Prepare read size blocks */
read_size_block_length =
__mausb_isoch_prepare_read_size_block(&isoch_readsize_block,
urb);
if (read_size_block_length > 0) {
kvec[2].iov_base = &isoch_readsize_block;
kvec[2].iov_len = read_size_block_length;
data_to_send.length += (u32)kvec[2].iov_len;
data_to_send.kvec_num++;
}
hdr->length = (u16)data_to_send.length;
data_to_send.kvec = kvec;
channel = mausb_transfer_type_to_channel(event->data.transfer_type);
return mausb_send_data(dev, channel, &data_to_send);
}
static void __mausb_process_in_isoch_short_resp(struct mausb_event *event,
struct ma_usb_hdr_common *hdr,
struct mausb_urb_ctx *urb_ctx)
{
u8 opt_hdr_shift = (hdr->flags & MA_USB_HDR_FLAGS_TIMESTAMP) ?
sizeof(struct ma_usb_hdr_isochtransfer_optional) : 0;
struct ma_usb_hdr_isochdatablock_short *data_block_hdr =
(struct ma_usb_hdr_isochdatablock_short *)
shift_ptr(mausb_hdr_isochtransfer_optional_hdr(hdr),
opt_hdr_shift);
u8 *isoch_data = shift_ptr(data_block_hdr, hdr->data.headers *
sizeof(*data_block_hdr));
u8 *end_of_packet = shift_ptr(hdr, hdr->length);
struct urb *urb = urb_ctx->urb;
int i;
if (isoch_data >= end_of_packet) {
dev_err(mausb_host_dev.this_device, "Bad header data. Data start pointer after end of packet: ep_handle=%#x",
event->data.ep_handle);
return;
}
for (i = 0; i < hdr->data.headers; ++i) {
u16 seg_num = data_block_hdr[i].segment_number;
u16 seg_size = data_block_hdr[i].block_length;
if (seg_num >= urb->number_of_packets) {
dev_err(mausb_host_dev.this_device, "Too many segments: ep_handle=%#x, seg_num=%d, urb.number_of_packets=%d",
event->data.ep_handle, seg_num,
urb->number_of_packets);
break;
}
if (seg_size > urb->iso_frame_desc[seg_num].length) {
dev_err(mausb_host_dev.this_device, "Block to long for segment: ep_handle=%#x",
event->data.ep_handle);
break;
}
if (shift_ptr(isoch_data, seg_size) > end_of_packet) {
dev_err(mausb_host_dev.this_device, "End of segment after enf of packet: ep_handle=%#x",
event->data.ep_handle);
break;
}
mausb_reset_data_iterator(&urb_ctx->iterator);
mausb_data_iterator_seek(&urb_ctx->iterator,
urb->iso_frame_desc[seg_num].offset);
mausb_data_iterator_write(&urb_ctx->iterator, isoch_data,
seg_size);
isoch_data = shift_ptr(isoch_data, seg_size);
urb->iso_frame_desc[seg_num].actual_length = seg_size;
urb->iso_frame_desc[seg_num].status = 0;
}
}
static void __mausb_process_in_isoch_std_resp(struct mausb_event *event,
struct ma_usb_hdr_common *hdr,
struct mausb_urb_ctx *urb_ctx)
{
u8 opt_hdr_shift = (hdr->flags & MA_USB_HDR_FLAGS_TIMESTAMP) ?
sizeof(struct ma_usb_hdr_isochtransfer_optional) : 0;
struct ma_usb_hdr_isochdatablock_std *data_block_hdr =
(struct ma_usb_hdr_isochdatablock_std *)
shift_ptr(mausb_hdr_isochtransfer_optional_hdr(hdr),
opt_hdr_shift);
u8 *isoch_data =
shift_ptr(data_block_hdr, hdr->data.headers *
sizeof(struct ma_usb_hdr_isochdatablock_std));
u8 *end_of_packet = shift_ptr(hdr, hdr->length);
struct urb *urb = (struct urb *)event->data.urb;
int i;
if (isoch_data >= end_of_packet) {
dev_err(mausb_host_dev.this_device, "Bad header data. Data start pointer after end of packet: ep_handle=%#x",
event->data.ep_handle);
return;
}
for (i = 0; i < hdr->data.headers; ++i) {
u16 seg_num = data_block_hdr[i].segment_number;
u16 seg_len = data_block_hdr[i].segment_length;
u16 block_len = data_block_hdr[i].block_length;
if (seg_num >= urb->number_of_packets) {
dev_err(mausb_host_dev.this_device, "Too many segments: ep_handle=%#x, seg_num=%d, number_of_packets=%d",
event->data.ep_handle, seg_num,
urb->number_of_packets);
break;
}
if (block_len > urb->iso_frame_desc[seg_num].length -
urb->iso_frame_desc[seg_num].actual_length) {
dev_err(mausb_host_dev.this_device, "Block too long for segment: ep_handle=%#x",
event->data.ep_handle);
break;
}
if (shift_ptr(isoch_data, block_len) >
end_of_packet) {
dev_err(mausb_host_dev.this_device, "End of fragment after end of packet: ep_handle=%#x",
event->data.ep_handle);
break;
}
mausb_reset_data_iterator(&urb_ctx->iterator);
mausb_data_iterator_seek(&urb_ctx->iterator,
urb->iso_frame_desc[seg_num].offset +
data_block_hdr[i].fragment_offset);
mausb_data_iterator_write(&urb_ctx->iterator,
isoch_data, block_len);
isoch_data = shift_ptr(isoch_data, block_len);
urb->iso_frame_desc[seg_num].actual_length += block_len;
if (urb->iso_frame_desc[seg_num].actual_length == seg_len)
urb->iso_frame_desc[seg_num].status = 0;
}
}
void mausb_receive_isoch_in_data(struct mausb_device *dev,
struct mausb_event *event,
struct mausb_urb_ctx *urb_ctx)
{
struct ma_usb_hdr_common *common_hdr =
(struct ma_usb_hdr_common *)event->data.recv_buf;
struct ma_usb_hdr_transfer *transfer_hdr =
mausb_get_data_transfer_hdr(common_hdr);
if (!(common_hdr->data.i_flags & MA_USB_DATA_IFLAGS_FMT_MASK)) {
/* Short ISO headers response */
__mausb_process_in_isoch_short_resp(event, common_hdr, urb_ctx);
} else if ((common_hdr->data.i_flags & MA_USB_DATA_IFLAGS_FMT_MASK) &
MA_USB_DATA_IFLAGS_HDR_FMT_STD) {
/* Standard ISO headers response */
__mausb_process_in_isoch_std_resp(event, common_hdr, urb_ctx);
} else if ((common_hdr->data.i_flags & MA_USB_DATA_IFLAGS_FMT_MASK) &
MA_USB_DATA_IFLAGS_HDR_FMT_LONG) {
/* Long ISO headers response */
dev_warn(mausb_host_dev.this_device, "Long isoc headers in response: ep_handle=%#x, req_id=%#x",
event->data.ep_handle, transfer_hdr->req_id);
} else {
/* Error */
dev_err(mausb_host_dev.this_device, "Isoc header error in response: ep_handle=%#x, req_id=%#x",
event->data.ep_handle, transfer_hdr->req_id);
}
}
static inline u32
__mausb_calculate_isoch_common_header_size(u32 num_of_segments)
{
return MAUSB_ISOCH_TRANSFER_HDR_SIZE +
MAUSB_ISOCH_STANDARD_FORMAT_SIZE * num_of_segments;
}
static struct ma_usb_hdr_common *
__mausb_create_isoch_out_transfer_packet(struct mausb_event *event,
struct mausb_urb_ctx *urb_ctx,
u16 payload_size, u32 seq_n,
u32 start_of_segments,
u32 number_of_segments)
{
struct ma_usb_hdr_common *hdr;
struct ma_usb_hdr_isochtransfer *hdr_isochtransfer;
struct ma_usb_hdr_isochdatablock_std *isoc_header_std;
struct ma_usb_hdr_isochtransfer_optional *hdr_opt_isochtransfer;
struct urb *urb = (struct urb *)event->data.urb;
void *isoc_headers = NULL;
u32 length;
u16 i;
unsigned long block_length;
u32 number_of_packets = (u32)event->data.isoch_seg_num;
u32 size_of_request =
__mausb_calculate_isoch_common_header_size(number_of_segments);
hdr = kzalloc(size_of_request, GFP_KERNEL);
if (!hdr)
return NULL;
hdr->version = MA_USB_HDR_VERSION_1_0;
hdr->ssid = event->data.mausb_ssid;
hdr->flags = MA_USB_HDR_FLAGS_HOST;
hdr->dev_addr = event->data.mausb_address;
hdr->handle.epv = event->data.ep_handle;
hdr->data.status = MA_USB_HDR_STATUS_NO_ERROR;
hdr->data.eps = MAUSB_TRANSFER_RESERVED;
hdr->data.t_flags = (u8)(usb_endpoint_type(&urb->ep->desc) << 3);
isoc_headers = shift_ptr(hdr, MAUSB_ISOCH_TRANSFER_HDR_SIZE);
for (i = (u16)start_of_segments;
i < number_of_segments + start_of_segments; ++i) {
block_length = i < number_of_packets - 1 ?
urb->iso_frame_desc[i + 1].offset -
urb->iso_frame_desc[i].offset :
mausb_data_iterator_length(&urb_ctx->iterator) -
urb->iso_frame_desc[i].offset;
urb->iso_frame_desc[i].status = MA_USB_HDR_STATUS_UNSUCCESSFUL;
isoc_header_std = (struct ma_usb_hdr_isochdatablock_std *)
shift_ptr(isoc_headers,
(u64)MAUSB_ISOCH_STANDARD_FORMAT_SIZE *
(i - start_of_segments));
isoc_header_std->block_length = (u16)block_length;
isoc_header_std->segment_number = i;
isoc_header_std->s_flags = 0;
isoc_header_std->segment_length = (u16)block_length;
isoc_header_std->fragment_offset = 0;
}
length = __mausb_calculate_isoch_common_header_size(number_of_segments);
hdr->flags |= MA_USB_HDR_FLAGS_TIMESTAMP;
hdr->type = (u8)MA_USB_HDR_TYPE_DATA_REQ(ISOCHTRANSFER);
hdr->data.headers = (u16)number_of_segments;
hdr->data.i_flags = MA_USB_DATA_IFLAGS_HDR_FMT_STD |
MA_USB_DATA_IFLAGS_ASAP;
hdr_opt_isochtransfer = mausb_hdr_isochtransfer_optional_hdr(hdr);
hdr_isochtransfer = mausb_get_isochtransfer_hdr(hdr);
hdr_isochtransfer->req_id = event->data.req_id;
hdr_isochtransfer->seq_n = seq_n;
hdr_isochtransfer->segments = number_of_packets;
hdr_isochtransfer->presentation_time = MA_USB_TRANSFER_RESERVED;
hdr_opt_isochtransfer->timestamp = MA_USB_TRANSFER_RESERVED;
hdr_opt_isochtransfer->mtd = MA_USB_TRANSFER_RESERVED;
hdr->length = (u16)length + payload_size;
return hdr;
}
static int
mausb_init_isoch_out_header_chunk(struct ma_usb_hdr_common *common_hdr,
struct list_head *chunks_list,
u32 *num_of_data_chunks,
u32 num_of_packets)
{
u32 header_size =
__mausb_calculate_isoch_common_header_size(num_of_packets);
int status = mausb_add_data_chunk(common_hdr, header_size, chunks_list);
if (!status)
++(*num_of_data_chunks);
return status;
}
static
int mausb_prepare_isoch_out_transfer_packet(struct ma_usb_hdr_common *hdr,
struct mausb_event *event,
struct mausb_urb_ctx *urb_ctx,
struct mausb_kvec_data_wrapper *
result_data_wrapper)
{
u32 num_of_data_chunks = 0;
u32 num_of_payload_data_chunks = 0;
u32 segment_number = event->data.isoch_seg_num;
u32 payload_data_size;
struct list_head chunks_list;
struct list_head payload_data_chunks;
int status = 0;
INIT_LIST_HEAD(&chunks_list);
/* Initialize data chunk for MAUSB header and add it to chunks list */
if (mausb_init_isoch_out_header_chunk(hdr, &chunks_list,
&num_of_data_chunks,
segment_number) < 0) {
status = -ENOMEM;
goto cleanup_data_chunks;
}
/* Get data chunks for data payload to send */
INIT_LIST_HEAD(&payload_data_chunks);
payload_data_size = hdr->length -
__mausb_calculate_isoch_common_header_size(segment_number);
if (mausb_data_iterator_read(&urb_ctx->iterator, payload_data_size,
&payload_data_chunks,
&num_of_payload_data_chunks) < 0) {
dev_err(mausb_host_dev.this_device, "Data iterator read failed");
status = -ENOMEM;
goto cleanup_data_chunks;
}
list_splice_tail(&payload_data_chunks, &chunks_list);
num_of_data_chunks += num_of_payload_data_chunks;
/* Map all data chunks to data wrapper */
if (mausb_init_data_wrapper(result_data_wrapper, &chunks_list,
num_of_data_chunks) < 0) {
dev_err(mausb_host_dev.this_device, "Data wrapper init failed");
status = -ENOMEM;
goto cleanup_data_chunks;
}
cleanup_data_chunks:
mausb_cleanup_chunks_list(&chunks_list);
return status;
}
static int mausb_create_and_send_isoch_transfer_req(struct mausb_device *dev,
struct mausb_event *event,
struct mausb_urb_ctx
*urb_ctx, u32 *seq_n,
u32 payload_size,
u32 start_of_segments,
u32 number_of_segments)
{
struct ma_usb_hdr_common *hdr;
struct mausb_kvec_data_wrapper data_to_send;
int status;
enum mausb_channel channel;
hdr = __mausb_create_isoch_out_transfer_packet(event, urb_ctx,
(u16)payload_size,
*seq_n,
start_of_segments,
number_of_segments);
if (!hdr) {
dev_alert(mausb_host_dev.this_device, "Isoch transfer packet alloc failed");
return -ENOMEM;
}
*seq_n = (*seq_n + 1) % (MA_USB_TRANSFER_SEQN_MAX + 1);
status = mausb_prepare_isoch_out_transfer_packet(hdr, event, urb_ctx,
&data_to_send);
if (status < 0) {
dev_alert(mausb_host_dev.this_device, "Failed to prepare transfer packet");
kfree(hdr);
return status;
}
channel = mausb_transfer_type_to_channel(event->data.transfer_type);
status = mausb_send_data(dev, channel, &data_to_send);
kfree(hdr);
kfree(data_to_send.kvec);
return status;
}
static inline int __mausb_send_isoch_out_packet(struct mausb_device *dev,
struct mausb_event *event,
struct mausb_urb_ctx *urb_ctx,
u32 *seq_n,
u32 *starting_segments,
u32 *rem_transfer_buf,
u32 *payload_size, u32 index)
{
int status = mausb_create_and_send_isoch_transfer_req(dev, event,
urb_ctx, seq_n, *payload_size,
*starting_segments,
index - *starting_segments);
if (status < 0) {
dev_err(mausb_host_dev.this_device, "ISOCH transfer request create and send failed");
return status;
}
*starting_segments = index;
*rem_transfer_buf = MAX_ISOCH_ASAP_PACKET_SIZE;
*payload_size = 0;
return 0;
}
int mausb_send_isoch_out_msg(struct mausb_device *ma_dev,
struct mausb_event *mausb_event,
struct mausb_urb_ctx *urb_ctx)
{
u32 starting_segments = 0;
u32 rem_transfer_buf = MAX_ISOCH_ASAP_PACKET_SIZE;
struct urb *urb = (struct urb *)mausb_event->data.urb;
u32 number_of_packets = (u32)urb->number_of_packets;
u32 payload_size = 0;
u32 chunk_size;
u32 seq_n = 0;
int status;
u32 i;
for (i = 0; i < number_of_packets; ++i) {
if (i < number_of_packets - 1)
chunk_size = urb->iso_frame_desc[i + 1].offset -
urb->iso_frame_desc[i].offset;
else
chunk_size =
mausb_data_iterator_length(&urb_ctx->iterator) -
urb->iso_frame_desc[i].offset;
if (chunk_size + MAUSB_ISOCH_STANDARD_FORMAT_SIZE >
rem_transfer_buf) {
if (payload_size == 0) {
dev_warn(mausb_host_dev.this_device, "Fragmented");
} else {
status = __mausb_send_isoch_out_packet
(ma_dev, mausb_event, urb_ctx,
&seq_n, &starting_segments,
&rem_transfer_buf,
&payload_size, i);
if (status < 0)
return status;
i--;
continue;
}
} else {
rem_transfer_buf -=
chunk_size + MAUSB_ISOCH_STANDARD_FORMAT_SIZE;
payload_size += chunk_size;
}
if (i == number_of_packets - 1 || rem_transfer_buf == 0) {
status = __mausb_send_isoch_out_packet
(ma_dev, mausb_event, urb_ctx, &seq_n,
&starting_segments, &rem_transfer_buf,
&payload_size, i + 1);
if (status < 0)
return status;
}
}
return 0;
}
void mausb_receive_isoch_out(struct mausb_event *event)
{
struct urb *urb = (struct urb *)event->data.urb;
u16 i;
dev_vdbg(mausb_host_dev.this_device, "transfer_size=%d, rem_transfer_size=%d, status=%d",
event->data.transfer_size, event->data.rem_transfer_size,
event->status);
for (i = 0; i < urb->number_of_packets; ++i)
urb->iso_frame_desc[i].status = event->status;
mausb_complete_request(urb, event->data.payload_size, event->status);
}