162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* usb-urb.c is part of the DVB USB library.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@posteo.de)
562306a36Sopenharmony_ci * see dvb-usb-init.c for copyright information.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * This file keeps functions for initializing and handling the
862306a36Sopenharmony_ci * BULK and ISOC USB data transfers in a generic way.
962306a36Sopenharmony_ci * Can be used for DVB-only and also, that's the plan, for
1062306a36Sopenharmony_ci * Hybrid USB devices (analog and DVB).
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci#include "dvb-usb-common.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci/* URB stuff for streaming */
1562306a36Sopenharmony_cistatic void usb_urb_complete(struct urb *urb)
1662306a36Sopenharmony_ci{
1762306a36Sopenharmony_ci	struct usb_data_stream *stream = urb->context;
1862306a36Sopenharmony_ci	int ptype = usb_pipetype(urb->pipe);
1962306a36Sopenharmony_ci	int i;
2062306a36Sopenharmony_ci	u8 *b;
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci	deb_uxfer("'%s' urb completed. status: %d, length: %d/%d, pack_num: %d, errors: %d\n",
2362306a36Sopenharmony_ci		ptype == PIPE_ISOCHRONOUS ? "isoc" : "bulk",
2462306a36Sopenharmony_ci		urb->status,urb->actual_length,urb->transfer_buffer_length,
2562306a36Sopenharmony_ci		urb->number_of_packets,urb->error_count);
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	switch (urb->status) {
2862306a36Sopenharmony_ci		case 0:         /* success */
2962306a36Sopenharmony_ci		case -ETIMEDOUT:    /* NAK */
3062306a36Sopenharmony_ci			break;
3162306a36Sopenharmony_ci		case -ECONNRESET:   /* kill */
3262306a36Sopenharmony_ci		case -ENOENT:
3362306a36Sopenharmony_ci		case -ESHUTDOWN:
3462306a36Sopenharmony_ci			return;
3562306a36Sopenharmony_ci		default:        /* error */
3662306a36Sopenharmony_ci			deb_ts("urb completion error %d.\n", urb->status);
3762306a36Sopenharmony_ci			break;
3862306a36Sopenharmony_ci	}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	b = (u8 *) urb->transfer_buffer;
4162306a36Sopenharmony_ci	switch (ptype) {
4262306a36Sopenharmony_ci		case PIPE_ISOCHRONOUS:
4362306a36Sopenharmony_ci			for (i = 0; i < urb->number_of_packets; i++) {
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci				if (urb->iso_frame_desc[i].status != 0)
4662306a36Sopenharmony_ci					deb_ts("iso frame descriptor has an error: %d\n",urb->iso_frame_desc[i].status);
4762306a36Sopenharmony_ci				else if (urb->iso_frame_desc[i].actual_length > 0)
4862306a36Sopenharmony_ci					stream->complete(stream, b + urb->iso_frame_desc[i].offset, urb->iso_frame_desc[i].actual_length);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci				urb->iso_frame_desc[i].status = 0;
5162306a36Sopenharmony_ci				urb->iso_frame_desc[i].actual_length = 0;
5262306a36Sopenharmony_ci			}
5362306a36Sopenharmony_ci			debug_dump(b,20,deb_uxfer);
5462306a36Sopenharmony_ci			break;
5562306a36Sopenharmony_ci		case PIPE_BULK:
5662306a36Sopenharmony_ci			if (urb->actual_length > 0)
5762306a36Sopenharmony_ci				stream->complete(stream, b, urb->actual_length);
5862306a36Sopenharmony_ci			break;
5962306a36Sopenharmony_ci		default:
6062306a36Sopenharmony_ci			err("unknown endpoint type in completion handler.");
6162306a36Sopenharmony_ci			return;
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci	usb_submit_urb(urb,GFP_ATOMIC);
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ciint usb_urb_kill(struct usb_data_stream *stream)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	int i;
6962306a36Sopenharmony_ci	for (i = 0; i < stream->urbs_submitted; i++) {
7062306a36Sopenharmony_ci		deb_ts("killing URB no. %d.\n",i);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci		/* stop the URB */
7362306a36Sopenharmony_ci		usb_kill_urb(stream->urb_list[i]);
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci	stream->urbs_submitted = 0;
7662306a36Sopenharmony_ci	return 0;
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ciint usb_urb_submit(struct usb_data_stream *stream)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	int i,ret;
8262306a36Sopenharmony_ci	for (i = 0; i < stream->urbs_initialized; i++) {
8362306a36Sopenharmony_ci		deb_ts("submitting URB no. %d\n",i);
8462306a36Sopenharmony_ci		if ((ret = usb_submit_urb(stream->urb_list[i],GFP_ATOMIC))) {
8562306a36Sopenharmony_ci			err("could not submit URB no. %d - get them all back",i);
8662306a36Sopenharmony_ci			usb_urb_kill(stream);
8762306a36Sopenharmony_ci			return ret;
8862306a36Sopenharmony_ci		}
8962306a36Sopenharmony_ci		stream->urbs_submitted++;
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci	return 0;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic int usb_free_stream_buffers(struct usb_data_stream *stream)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	if (stream->state & USB_STATE_URB_BUF) {
9762306a36Sopenharmony_ci		while (stream->buf_num) {
9862306a36Sopenharmony_ci			stream->buf_num--;
9962306a36Sopenharmony_ci			deb_mem("freeing buffer %d\n",stream->buf_num);
10062306a36Sopenharmony_ci			usb_free_coherent(stream->udev, stream->buf_size,
10162306a36Sopenharmony_ci					  stream->buf_list[stream->buf_num],
10262306a36Sopenharmony_ci					  stream->dma_addr[stream->buf_num]);
10362306a36Sopenharmony_ci		}
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	stream->state &= ~USB_STATE_URB_BUF;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	return 0;
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic int usb_allocate_stream_buffers(struct usb_data_stream *stream, int num, unsigned long size)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	stream->buf_num = 0;
11462306a36Sopenharmony_ci	stream->buf_size = size;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	deb_mem("all in all I will use %lu bytes for streaming\n",num*size);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	for (stream->buf_num = 0; stream->buf_num < num; stream->buf_num++) {
11962306a36Sopenharmony_ci		deb_mem("allocating buffer %d\n",stream->buf_num);
12062306a36Sopenharmony_ci		if (( stream->buf_list[stream->buf_num] =
12162306a36Sopenharmony_ci					usb_alloc_coherent(stream->udev, size, GFP_KERNEL,
12262306a36Sopenharmony_ci					&stream->dma_addr[stream->buf_num]) ) == NULL) {
12362306a36Sopenharmony_ci			deb_mem("not enough memory for urb-buffer allocation.\n");
12462306a36Sopenharmony_ci			usb_free_stream_buffers(stream);
12562306a36Sopenharmony_ci			return -ENOMEM;
12662306a36Sopenharmony_ci		}
12762306a36Sopenharmony_ci		deb_mem("buffer %d: %p (dma: %Lu)\n",
12862306a36Sopenharmony_ci			stream->buf_num,
12962306a36Sopenharmony_cistream->buf_list[stream->buf_num], (long long)stream->dma_addr[stream->buf_num]);
13062306a36Sopenharmony_ci		memset(stream->buf_list[stream->buf_num],0,size);
13162306a36Sopenharmony_ci		stream->state |= USB_STATE_URB_BUF;
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci	deb_mem("allocation successful\n");
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	return 0;
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic int usb_bulk_urb_init(struct usb_data_stream *stream)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	int i, j;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	if ((i = usb_allocate_stream_buffers(stream,stream->props.count,
14362306a36Sopenharmony_ci					stream->props.u.bulk.buffersize)) < 0)
14462306a36Sopenharmony_ci		return i;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	/* allocate the URBs */
14762306a36Sopenharmony_ci	for (i = 0; i < stream->props.count; i++) {
14862306a36Sopenharmony_ci		stream->urb_list[i] = usb_alloc_urb(0, GFP_KERNEL);
14962306a36Sopenharmony_ci		if (!stream->urb_list[i]) {
15062306a36Sopenharmony_ci			deb_mem("not enough memory for urb_alloc_urb!.\n");
15162306a36Sopenharmony_ci			for (j = 0; j < i; j++)
15262306a36Sopenharmony_ci				usb_free_urb(stream->urb_list[j]);
15362306a36Sopenharmony_ci			return -ENOMEM;
15462306a36Sopenharmony_ci		}
15562306a36Sopenharmony_ci		usb_fill_bulk_urb( stream->urb_list[i], stream->udev,
15662306a36Sopenharmony_ci				usb_rcvbulkpipe(stream->udev,stream->props.endpoint),
15762306a36Sopenharmony_ci				stream->buf_list[i],
15862306a36Sopenharmony_ci				stream->props.u.bulk.buffersize,
15962306a36Sopenharmony_ci				usb_urb_complete, stream);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci		stream->urb_list[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
16262306a36Sopenharmony_ci		stream->urb_list[i]->transfer_dma = stream->dma_addr[i];
16362306a36Sopenharmony_ci		stream->urbs_initialized++;
16462306a36Sopenharmony_ci	}
16562306a36Sopenharmony_ci	return 0;
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic int usb_isoc_urb_init(struct usb_data_stream *stream)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	int i,j;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	if ((i = usb_allocate_stream_buffers(stream,stream->props.count,
17362306a36Sopenharmony_ci					stream->props.u.isoc.framesize*stream->props.u.isoc.framesperurb)) < 0)
17462306a36Sopenharmony_ci		return i;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	/* allocate the URBs */
17762306a36Sopenharmony_ci	for (i = 0; i < stream->props.count; i++) {
17862306a36Sopenharmony_ci		struct urb *urb;
17962306a36Sopenharmony_ci		int frame_offset = 0;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci		stream->urb_list[i] = usb_alloc_urb(stream->props.u.isoc.framesperurb, GFP_KERNEL);
18262306a36Sopenharmony_ci		if (!stream->urb_list[i]) {
18362306a36Sopenharmony_ci			deb_mem("not enough memory for urb_alloc_urb!\n");
18462306a36Sopenharmony_ci			for (j = 0; j < i; j++)
18562306a36Sopenharmony_ci				usb_free_urb(stream->urb_list[j]);
18662306a36Sopenharmony_ci			return -ENOMEM;
18762306a36Sopenharmony_ci		}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci		urb = stream->urb_list[i];
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci		urb->dev = stream->udev;
19262306a36Sopenharmony_ci		urb->context = stream;
19362306a36Sopenharmony_ci		urb->complete = usb_urb_complete;
19462306a36Sopenharmony_ci		urb->pipe = usb_rcvisocpipe(stream->udev,stream->props.endpoint);
19562306a36Sopenharmony_ci		urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
19662306a36Sopenharmony_ci		urb->interval = stream->props.u.isoc.interval;
19762306a36Sopenharmony_ci		urb->number_of_packets = stream->props.u.isoc.framesperurb;
19862306a36Sopenharmony_ci		urb->transfer_buffer_length = stream->buf_size;
19962306a36Sopenharmony_ci		urb->transfer_buffer = stream->buf_list[i];
20062306a36Sopenharmony_ci		urb->transfer_dma = stream->dma_addr[i];
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci		for (j = 0; j < stream->props.u.isoc.framesperurb; j++) {
20362306a36Sopenharmony_ci			urb->iso_frame_desc[j].offset = frame_offset;
20462306a36Sopenharmony_ci			urb->iso_frame_desc[j].length = stream->props.u.isoc.framesize;
20562306a36Sopenharmony_ci			frame_offset += stream->props.u.isoc.framesize;
20662306a36Sopenharmony_ci		}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci		stream->urbs_initialized++;
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci	return 0;
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ciint usb_urb_init(struct usb_data_stream *stream, struct usb_data_stream_properties *props)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	if (stream == NULL || props == NULL)
21662306a36Sopenharmony_ci		return -EINVAL;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	memcpy(&stream->props, props, sizeof(*props));
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	usb_clear_halt(stream->udev,usb_rcvbulkpipe(stream->udev,stream->props.endpoint));
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	if (stream->complete == NULL) {
22362306a36Sopenharmony_ci		err("there is no data callback - this doesn't make sense.");
22462306a36Sopenharmony_ci		return -EINVAL;
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	switch (stream->props.type) {
22862306a36Sopenharmony_ci		case USB_BULK:
22962306a36Sopenharmony_ci			return usb_bulk_urb_init(stream);
23062306a36Sopenharmony_ci		case USB_ISOC:
23162306a36Sopenharmony_ci			return usb_isoc_urb_init(stream);
23262306a36Sopenharmony_ci		default:
23362306a36Sopenharmony_ci			err("unknown URB-type for data transfer.");
23462306a36Sopenharmony_ci			return -EINVAL;
23562306a36Sopenharmony_ci	}
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ciint usb_urb_exit(struct usb_data_stream *stream)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	int i;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	usb_urb_kill(stream);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	for (i = 0; i < stream->urbs_initialized; i++) {
24562306a36Sopenharmony_ci		if (stream->urb_list[i] != NULL) {
24662306a36Sopenharmony_ci			deb_mem("freeing URB no. %d.\n",i);
24762306a36Sopenharmony_ci			/* free the URBs */
24862306a36Sopenharmony_ci			usb_free_urb(stream->urb_list[i]);
24962306a36Sopenharmony_ci		}
25062306a36Sopenharmony_ci	}
25162306a36Sopenharmony_ci	stream->urbs_initialized = 0;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	usb_free_stream_buffers(stream);
25462306a36Sopenharmony_ci	return 0;
25562306a36Sopenharmony_ci}
256