18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* usb-urb.c is part of the DVB USB library.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@posteo.de)
58c2ecf20Sopenharmony_ci * see dvb-usb-init.c for copyright information.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * This file keeps functions for initializing and handling the
88c2ecf20Sopenharmony_ci * BULK and ISOC USB data transfers in a generic way.
98c2ecf20Sopenharmony_ci * Can be used for DVB-only and also, that's the plan, for
108c2ecf20Sopenharmony_ci * Hybrid USB devices (analog and DVB).
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci#include "dvb-usb-common.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci/* URB stuff for streaming */
158c2ecf20Sopenharmony_cistatic void usb_urb_complete(struct urb *urb)
168c2ecf20Sopenharmony_ci{
178c2ecf20Sopenharmony_ci	struct usb_data_stream *stream = urb->context;
188c2ecf20Sopenharmony_ci	int ptype = usb_pipetype(urb->pipe);
198c2ecf20Sopenharmony_ci	int i;
208c2ecf20Sopenharmony_ci	u8 *b;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	deb_uxfer("'%s' urb completed. status: %d, length: %d/%d, pack_num: %d, errors: %d\n",
238c2ecf20Sopenharmony_ci		ptype == PIPE_ISOCHRONOUS ? "isoc" : "bulk",
248c2ecf20Sopenharmony_ci		urb->status,urb->actual_length,urb->transfer_buffer_length,
258c2ecf20Sopenharmony_ci		urb->number_of_packets,urb->error_count);
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	switch (urb->status) {
288c2ecf20Sopenharmony_ci		case 0:         /* success */
298c2ecf20Sopenharmony_ci		case -ETIMEDOUT:    /* NAK */
308c2ecf20Sopenharmony_ci			break;
318c2ecf20Sopenharmony_ci		case -ECONNRESET:   /* kill */
328c2ecf20Sopenharmony_ci		case -ENOENT:
338c2ecf20Sopenharmony_ci		case -ESHUTDOWN:
348c2ecf20Sopenharmony_ci			return;
358c2ecf20Sopenharmony_ci		default:        /* error */
368c2ecf20Sopenharmony_ci			deb_ts("urb completion error %d.\n", urb->status);
378c2ecf20Sopenharmony_ci			break;
388c2ecf20Sopenharmony_ci	}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	b = (u8 *) urb->transfer_buffer;
418c2ecf20Sopenharmony_ci	switch (ptype) {
428c2ecf20Sopenharmony_ci		case PIPE_ISOCHRONOUS:
438c2ecf20Sopenharmony_ci			for (i = 0; i < urb->number_of_packets; i++) {
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci				if (urb->iso_frame_desc[i].status != 0)
468c2ecf20Sopenharmony_ci					deb_ts("iso frame descriptor has an error: %d\n",urb->iso_frame_desc[i].status);
478c2ecf20Sopenharmony_ci				else if (urb->iso_frame_desc[i].actual_length > 0)
488c2ecf20Sopenharmony_ci					stream->complete(stream, b + urb->iso_frame_desc[i].offset, urb->iso_frame_desc[i].actual_length);
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci				urb->iso_frame_desc[i].status = 0;
518c2ecf20Sopenharmony_ci				urb->iso_frame_desc[i].actual_length = 0;
528c2ecf20Sopenharmony_ci			}
538c2ecf20Sopenharmony_ci			debug_dump(b,20,deb_uxfer);
548c2ecf20Sopenharmony_ci			break;
558c2ecf20Sopenharmony_ci		case PIPE_BULK:
568c2ecf20Sopenharmony_ci			if (urb->actual_length > 0)
578c2ecf20Sopenharmony_ci				stream->complete(stream, b, urb->actual_length);
588c2ecf20Sopenharmony_ci			break;
598c2ecf20Sopenharmony_ci		default:
608c2ecf20Sopenharmony_ci			err("unknown endpoint type in completion handler.");
618c2ecf20Sopenharmony_ci			return;
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci	usb_submit_urb(urb,GFP_ATOMIC);
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ciint usb_urb_kill(struct usb_data_stream *stream)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	int i;
698c2ecf20Sopenharmony_ci	for (i = 0; i < stream->urbs_submitted; i++) {
708c2ecf20Sopenharmony_ci		deb_ts("killing URB no. %d.\n",i);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci		/* stop the URB */
738c2ecf20Sopenharmony_ci		usb_kill_urb(stream->urb_list[i]);
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci	stream->urbs_submitted = 0;
768c2ecf20Sopenharmony_ci	return 0;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ciint usb_urb_submit(struct usb_data_stream *stream)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	int i,ret;
828c2ecf20Sopenharmony_ci	for (i = 0; i < stream->urbs_initialized; i++) {
838c2ecf20Sopenharmony_ci		deb_ts("submitting URB no. %d\n",i);
848c2ecf20Sopenharmony_ci		if ((ret = usb_submit_urb(stream->urb_list[i],GFP_ATOMIC))) {
858c2ecf20Sopenharmony_ci			err("could not submit URB no. %d - get them all back",i);
868c2ecf20Sopenharmony_ci			usb_urb_kill(stream);
878c2ecf20Sopenharmony_ci			return ret;
888c2ecf20Sopenharmony_ci		}
898c2ecf20Sopenharmony_ci		stream->urbs_submitted++;
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci	return 0;
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic int usb_free_stream_buffers(struct usb_data_stream *stream)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	if (stream->state & USB_STATE_URB_BUF) {
978c2ecf20Sopenharmony_ci		while (stream->buf_num) {
988c2ecf20Sopenharmony_ci			stream->buf_num--;
998c2ecf20Sopenharmony_ci			deb_mem("freeing buffer %d\n",stream->buf_num);
1008c2ecf20Sopenharmony_ci			usb_free_coherent(stream->udev, stream->buf_size,
1018c2ecf20Sopenharmony_ci					  stream->buf_list[stream->buf_num],
1028c2ecf20Sopenharmony_ci					  stream->dma_addr[stream->buf_num]);
1038c2ecf20Sopenharmony_ci		}
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	stream->state &= ~USB_STATE_URB_BUF;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	return 0;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic int usb_allocate_stream_buffers(struct usb_data_stream *stream, int num, unsigned long size)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	stream->buf_num = 0;
1148c2ecf20Sopenharmony_ci	stream->buf_size = size;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	deb_mem("all in all I will use %lu bytes for streaming\n",num*size);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	for (stream->buf_num = 0; stream->buf_num < num; stream->buf_num++) {
1198c2ecf20Sopenharmony_ci		deb_mem("allocating buffer %d\n",stream->buf_num);
1208c2ecf20Sopenharmony_ci		if (( stream->buf_list[stream->buf_num] =
1218c2ecf20Sopenharmony_ci					usb_alloc_coherent(stream->udev, size, GFP_KERNEL,
1228c2ecf20Sopenharmony_ci					&stream->dma_addr[stream->buf_num]) ) == NULL) {
1238c2ecf20Sopenharmony_ci			deb_mem("not enough memory for urb-buffer allocation.\n");
1248c2ecf20Sopenharmony_ci			usb_free_stream_buffers(stream);
1258c2ecf20Sopenharmony_ci			return -ENOMEM;
1268c2ecf20Sopenharmony_ci		}
1278c2ecf20Sopenharmony_ci		deb_mem("buffer %d: %p (dma: %Lu)\n",
1288c2ecf20Sopenharmony_ci			stream->buf_num,
1298c2ecf20Sopenharmony_cistream->buf_list[stream->buf_num], (long long)stream->dma_addr[stream->buf_num]);
1308c2ecf20Sopenharmony_ci		memset(stream->buf_list[stream->buf_num],0,size);
1318c2ecf20Sopenharmony_ci		stream->state |= USB_STATE_URB_BUF;
1328c2ecf20Sopenharmony_ci	}
1338c2ecf20Sopenharmony_ci	deb_mem("allocation successful\n");
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	return 0;
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic int usb_bulk_urb_init(struct usb_data_stream *stream)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	int i, j;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	if ((i = usb_allocate_stream_buffers(stream,stream->props.count,
1438c2ecf20Sopenharmony_ci					stream->props.u.bulk.buffersize)) < 0)
1448c2ecf20Sopenharmony_ci		return i;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	/* allocate the URBs */
1478c2ecf20Sopenharmony_ci	for (i = 0; i < stream->props.count; i++) {
1488c2ecf20Sopenharmony_ci		stream->urb_list[i] = usb_alloc_urb(0, GFP_KERNEL);
1498c2ecf20Sopenharmony_ci		if (!stream->urb_list[i]) {
1508c2ecf20Sopenharmony_ci			deb_mem("not enough memory for urb_alloc_urb!.\n");
1518c2ecf20Sopenharmony_ci			for (j = 0; j < i; j++)
1528c2ecf20Sopenharmony_ci				usb_free_urb(stream->urb_list[j]);
1538c2ecf20Sopenharmony_ci			return -ENOMEM;
1548c2ecf20Sopenharmony_ci		}
1558c2ecf20Sopenharmony_ci		usb_fill_bulk_urb( stream->urb_list[i], stream->udev,
1568c2ecf20Sopenharmony_ci				usb_rcvbulkpipe(stream->udev,stream->props.endpoint),
1578c2ecf20Sopenharmony_ci				stream->buf_list[i],
1588c2ecf20Sopenharmony_ci				stream->props.u.bulk.buffersize,
1598c2ecf20Sopenharmony_ci				usb_urb_complete, stream);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci		stream->urb_list[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
1628c2ecf20Sopenharmony_ci		stream->urb_list[i]->transfer_dma = stream->dma_addr[i];
1638c2ecf20Sopenharmony_ci		stream->urbs_initialized++;
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci	return 0;
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_cistatic int usb_isoc_urb_init(struct usb_data_stream *stream)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	int i,j;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	if ((i = usb_allocate_stream_buffers(stream,stream->props.count,
1738c2ecf20Sopenharmony_ci					stream->props.u.isoc.framesize*stream->props.u.isoc.framesperurb)) < 0)
1748c2ecf20Sopenharmony_ci		return i;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	/* allocate the URBs */
1778c2ecf20Sopenharmony_ci	for (i = 0; i < stream->props.count; i++) {
1788c2ecf20Sopenharmony_ci		struct urb *urb;
1798c2ecf20Sopenharmony_ci		int frame_offset = 0;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci		stream->urb_list[i] = usb_alloc_urb(stream->props.u.isoc.framesperurb, GFP_KERNEL);
1828c2ecf20Sopenharmony_ci		if (!stream->urb_list[i]) {
1838c2ecf20Sopenharmony_ci			deb_mem("not enough memory for urb_alloc_urb!\n");
1848c2ecf20Sopenharmony_ci			for (j = 0; j < i; j++)
1858c2ecf20Sopenharmony_ci				usb_free_urb(stream->urb_list[j]);
1868c2ecf20Sopenharmony_ci			return -ENOMEM;
1878c2ecf20Sopenharmony_ci		}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci		urb = stream->urb_list[i];
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci		urb->dev = stream->udev;
1928c2ecf20Sopenharmony_ci		urb->context = stream;
1938c2ecf20Sopenharmony_ci		urb->complete = usb_urb_complete;
1948c2ecf20Sopenharmony_ci		urb->pipe = usb_rcvisocpipe(stream->udev,stream->props.endpoint);
1958c2ecf20Sopenharmony_ci		urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
1968c2ecf20Sopenharmony_ci		urb->interval = stream->props.u.isoc.interval;
1978c2ecf20Sopenharmony_ci		urb->number_of_packets = stream->props.u.isoc.framesperurb;
1988c2ecf20Sopenharmony_ci		urb->transfer_buffer_length = stream->buf_size;
1998c2ecf20Sopenharmony_ci		urb->transfer_buffer = stream->buf_list[i];
2008c2ecf20Sopenharmony_ci		urb->transfer_dma = stream->dma_addr[i];
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci		for (j = 0; j < stream->props.u.isoc.framesperurb; j++) {
2038c2ecf20Sopenharmony_ci			urb->iso_frame_desc[j].offset = frame_offset;
2048c2ecf20Sopenharmony_ci			urb->iso_frame_desc[j].length = stream->props.u.isoc.framesize;
2058c2ecf20Sopenharmony_ci			frame_offset += stream->props.u.isoc.framesize;
2068c2ecf20Sopenharmony_ci		}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci		stream->urbs_initialized++;
2098c2ecf20Sopenharmony_ci	}
2108c2ecf20Sopenharmony_ci	return 0;
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ciint usb_urb_init(struct usb_data_stream *stream, struct usb_data_stream_properties *props)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	if (stream == NULL || props == NULL)
2168c2ecf20Sopenharmony_ci		return -EINVAL;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	memcpy(&stream->props, props, sizeof(*props));
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	usb_clear_halt(stream->udev,usb_rcvbulkpipe(stream->udev,stream->props.endpoint));
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	if (stream->complete == NULL) {
2238c2ecf20Sopenharmony_ci		err("there is no data callback - this doesn't make sense.");
2248c2ecf20Sopenharmony_ci		return -EINVAL;
2258c2ecf20Sopenharmony_ci	}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	switch (stream->props.type) {
2288c2ecf20Sopenharmony_ci		case USB_BULK:
2298c2ecf20Sopenharmony_ci			return usb_bulk_urb_init(stream);
2308c2ecf20Sopenharmony_ci		case USB_ISOC:
2318c2ecf20Sopenharmony_ci			return usb_isoc_urb_init(stream);
2328c2ecf20Sopenharmony_ci		default:
2338c2ecf20Sopenharmony_ci			err("unknown URB-type for data transfer.");
2348c2ecf20Sopenharmony_ci			return -EINVAL;
2358c2ecf20Sopenharmony_ci	}
2368c2ecf20Sopenharmony_ci}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ciint usb_urb_exit(struct usb_data_stream *stream)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	int i;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	usb_urb_kill(stream);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	for (i = 0; i < stream->urbs_initialized; i++) {
2458c2ecf20Sopenharmony_ci		if (stream->urb_list[i] != NULL) {
2468c2ecf20Sopenharmony_ci			deb_mem("freeing URB no. %d.\n",i);
2478c2ecf20Sopenharmony_ci			/* free the URBs */
2488c2ecf20Sopenharmony_ci			usb_free_urb(stream->urb_list[i]);
2498c2ecf20Sopenharmony_ci		}
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci	stream->urbs_initialized = 0;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	usb_free_stream_buffers(stream);
2548c2ecf20Sopenharmony_ci	return 0;
2558c2ecf20Sopenharmony_ci}
256