brintos

brintos / linux-shallow public Read only

0
0
Text · 9.0 KiB · 2ad2dde Raw
354 lines · c
1// SPDX-License-Identifier: GPL-2.02/* usb-urb.c is part of the DVB USB library.3 *4 * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@posteo.de)5 * see dvb-usb-init.c for copyright information.6 *7 * This file keeps functions for initializing and handling the8 * BULK and ISOC USB data transfers in a generic way.9 * Can be used for DVB-only and also, that's the plan, for10 * Hybrid USB devices (analog and DVB).11 */12#include "dvb_usb_common.h"13 14/* URB stuff for streaming */15 16int usb_urb_reconfig(struct usb_data_stream *stream,17		struct usb_data_stream_properties *props);18 19static void usb_urb_complete(struct urb *urb)20{21	struct usb_data_stream *stream = urb->context;22	int ptype = usb_pipetype(urb->pipe);23	int i;24	u8 *b;25 26	dev_dbg_ratelimited(&stream->udev->dev,27			"%s: %s urb completed status=%d length=%d/%d pack_num=%d errors=%d\n",28			__func__, ptype == PIPE_ISOCHRONOUS ? "isoc" : "bulk",29			urb->status, urb->actual_length,30			urb->transfer_buffer_length,31			urb->number_of_packets, urb->error_count);32 33	switch (urb->status) {34	case 0:         /* success */35	case -ETIMEDOUT:    /* NAK */36		break;37	case -ECONNRESET:   /* kill */38	case -ENOENT:39	case -ESHUTDOWN:40		return;41	default:        /* error */42		dev_dbg_ratelimited(&stream->udev->dev,43				"%s: urb completion failed=%d\n",44				__func__, urb->status);45		break;46	}47 48	b = (u8 *) urb->transfer_buffer;49	switch (ptype) {50	case PIPE_ISOCHRONOUS:51		for (i = 0; i < urb->number_of_packets; i++) {52			if (urb->iso_frame_desc[i].status != 0)53				dev_dbg(&stream->udev->dev,54						"%s: iso frame descriptor has an error=%d\n",55						__func__,56						urb->iso_frame_desc[i].status);57			else if (urb->iso_frame_desc[i].actual_length > 0)58				stream->complete(stream,59					b + urb->iso_frame_desc[i].offset,60					urb->iso_frame_desc[i].actual_length);61 62			urb->iso_frame_desc[i].status = 0;63			urb->iso_frame_desc[i].actual_length = 0;64		}65		break;66	case PIPE_BULK:67		if (urb->actual_length > 0)68			stream->complete(stream, b, urb->actual_length);69		break;70	default:71		dev_err(&stream->udev->dev,72				"%s: unknown endpoint type in completion handler\n",73				KBUILD_MODNAME);74		return;75	}76	usb_submit_urb(urb, GFP_ATOMIC);77}78 79int usb_urb_killv2(struct usb_data_stream *stream)80{81	int i;82	for (i = 0; i < stream->urbs_submitted; i++) {83		dev_dbg(&stream->udev->dev, "%s: kill urb=%d\n", __func__, i);84		/* stop the URB */85		usb_kill_urb(stream->urb_list[i]);86	}87	stream->urbs_submitted = 0;88	return 0;89}90 91int usb_urb_submitv2(struct usb_data_stream *stream,92		struct usb_data_stream_properties *props)93{94	int i, ret;95 96	if (props) {97		ret = usb_urb_reconfig(stream, props);98		if (ret < 0)99			return ret;100	}101 102	for (i = 0; i < stream->urbs_initialized; i++) {103		dev_dbg(&stream->udev->dev, "%s: submit urb=%d\n", __func__, i);104		ret = usb_submit_urb(stream->urb_list[i], GFP_ATOMIC);105		if (ret) {106			dev_err(&stream->udev->dev,107					"%s: could not submit urb no. %d - get them all back\n",108					KBUILD_MODNAME, i);109			usb_urb_killv2(stream);110			return ret;111		}112		stream->urbs_submitted++;113	}114	return 0;115}116 117static int usb_urb_free_urbs(struct usb_data_stream *stream)118{119	int i;120 121	usb_urb_killv2(stream);122 123	for (i = stream->urbs_initialized - 1; i >= 0; i--) {124		if (stream->urb_list[i]) {125			dev_dbg(&stream->udev->dev, "%s: free urb=%d\n",126					__func__, i);127			/* free the URBs */128			usb_free_urb(stream->urb_list[i]);129		}130	}131	stream->urbs_initialized = 0;132 133	return 0;134}135 136static int usb_urb_alloc_bulk_urbs(struct usb_data_stream *stream)137{138	int i, j;139 140	/* allocate the URBs */141	for (i = 0; i < stream->props.count; i++) {142		dev_dbg(&stream->udev->dev, "%s: alloc urb=%d\n", __func__, i);143		stream->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC);144		if (!stream->urb_list[i]) {145			dev_dbg(&stream->udev->dev, "%s: failed\n", __func__);146			for (j = 0; j < i; j++)147				usb_free_urb(stream->urb_list[j]);148			return -ENOMEM;149		}150		usb_fill_bulk_urb(stream->urb_list[i],151				stream->udev,152				usb_rcvbulkpipe(stream->udev,153						stream->props.endpoint),154				stream->buf_list[i],155				stream->props.u.bulk.buffersize,156				usb_urb_complete, stream);157 158		stream->urbs_initialized++;159	}160	return 0;161}162 163static int usb_urb_alloc_isoc_urbs(struct usb_data_stream *stream)164{165	int i, j;166 167	/* allocate the URBs */168	for (i = 0; i < stream->props.count; i++) {169		struct urb *urb;170		int frame_offset = 0;171		dev_dbg(&stream->udev->dev, "%s: alloc urb=%d\n", __func__, i);172		stream->urb_list[i] = usb_alloc_urb(173				stream->props.u.isoc.framesperurb, GFP_ATOMIC);174		if (!stream->urb_list[i]) {175			dev_dbg(&stream->udev->dev, "%s: failed\n", __func__);176			for (j = 0; j < i; j++)177				usb_free_urb(stream->urb_list[j]);178			return -ENOMEM;179		}180 181		urb = stream->urb_list[i];182 183		urb->dev = stream->udev;184		urb->context = stream;185		urb->complete = usb_urb_complete;186		urb->pipe = usb_rcvisocpipe(stream->udev,187				stream->props.endpoint);188		urb->transfer_flags = URB_ISO_ASAP;189		urb->interval = stream->props.u.isoc.interval;190		urb->number_of_packets = stream->props.u.isoc.framesperurb;191		urb->transfer_buffer_length = stream->props.u.isoc.framesize *192				stream->props.u.isoc.framesperurb;193		urb->transfer_buffer = stream->buf_list[i];194 195		for (j = 0; j < stream->props.u.isoc.framesperurb; j++) {196			urb->iso_frame_desc[j].offset = frame_offset;197			urb->iso_frame_desc[j].length =198					stream->props.u.isoc.framesize;199			frame_offset += stream->props.u.isoc.framesize;200		}201 202		stream->urbs_initialized++;203	}204	return 0;205}206 207static int usb_free_stream_buffers(struct usb_data_stream *stream)208{209	if (stream->state & USB_STATE_URB_BUF) {210		while (stream->buf_num) {211			stream->buf_num--;212			kfree(stream->buf_list[stream->buf_num]);213		}214	}215 216	stream->state &= ~USB_STATE_URB_BUF;217 218	return 0;219}220 221static int usb_alloc_stream_buffers(struct usb_data_stream *stream, int num,222				    unsigned long size)223{224	stream->buf_num = 0;225	stream->buf_size = size;226 227	dev_dbg(&stream->udev->dev,228			"%s: all in all I will use %lu bytes for streaming\n",229			__func__,  num * size);230 231	for (stream->buf_num = 0; stream->buf_num < num; stream->buf_num++) {232		stream->buf_list[stream->buf_num] = kzalloc(size, GFP_ATOMIC);233		if (!stream->buf_list[stream->buf_num]) {234			dev_dbg(&stream->udev->dev, "%s: alloc buf=%d failed\n",235					__func__, stream->buf_num);236			usb_free_stream_buffers(stream);237			return -ENOMEM;238		}239 240		dev_dbg(&stream->udev->dev, "%s: alloc buf=%d %p (dma %llu)\n",241				__func__, stream->buf_num,242				stream->buf_list[stream->buf_num],243				(long long)stream->dma_addr[stream->buf_num]);244		stream->state |= USB_STATE_URB_BUF;245	}246 247	return 0;248}249 250int usb_urb_reconfig(struct usb_data_stream *stream,251		struct usb_data_stream_properties *props)252{253	int buf_size;254 255	if (!props)256		return 0;257 258	/* check allocated buffers are large enough for the request */259	if (props->type == USB_BULK) {260		buf_size = stream->props.u.bulk.buffersize;261	} else if (props->type == USB_ISOC) {262		buf_size = props->u.isoc.framesize * props->u.isoc.framesperurb;263	} else {264		dev_err(&stream->udev->dev, "%s: invalid endpoint type=%d\n",265				KBUILD_MODNAME, props->type);266		return -EINVAL;267	}268 269	if (stream->buf_num < props->count || stream->buf_size < buf_size) {270		dev_err(&stream->udev->dev,271				"%s: cannot reconfigure as allocated buffers are too small\n",272				KBUILD_MODNAME);273		return -EINVAL;274	}275 276	/* check if all fields are same */277	if (stream->props.type == props->type &&278			stream->props.count == props->count &&279			stream->props.endpoint == props->endpoint) {280		if (props->type == USB_BULK &&281				props->u.bulk.buffersize ==282				stream->props.u.bulk.buffersize)283			return 0;284		else if (props->type == USB_ISOC &&285				props->u.isoc.framesperurb ==286				stream->props.u.isoc.framesperurb &&287				props->u.isoc.framesize ==288				stream->props.u.isoc.framesize &&289				props->u.isoc.interval ==290				stream->props.u.isoc.interval)291			return 0;292	}293 294	dev_dbg(&stream->udev->dev, "%s: re-alloc urbs\n", __func__);295 296	usb_urb_free_urbs(stream);297	memcpy(&stream->props, props, sizeof(*props));298	if (props->type == USB_BULK)299		return usb_urb_alloc_bulk_urbs(stream);300	else if (props->type == USB_ISOC)301		return usb_urb_alloc_isoc_urbs(stream);302 303	return 0;304}305 306int usb_urb_initv2(struct usb_data_stream *stream,307		const struct usb_data_stream_properties *props)308{309	int ret;310 311	if (!stream || !props)312		return -EINVAL;313 314	memcpy(&stream->props, props, sizeof(*props));315 316	if (!stream->complete) {317		dev_err(&stream->udev->dev,318				"%s: there is no data callback - this doesn't make sense\n",319				KBUILD_MODNAME);320		return -EINVAL;321	}322 323	switch (stream->props.type) {324	case USB_BULK:325		ret = usb_alloc_stream_buffers(stream, stream->props.count,326				stream->props.u.bulk.buffersize);327		if (ret < 0)328			return ret;329 330		return usb_urb_alloc_bulk_urbs(stream);331	case USB_ISOC:332		ret = usb_alloc_stream_buffers(stream, stream->props.count,333				stream->props.u.isoc.framesize *334				stream->props.u.isoc.framesperurb);335		if (ret < 0)336			return ret;337 338		return usb_urb_alloc_isoc_urbs(stream);339	default:340		dev_err(&stream->udev->dev,341				"%s: unknown urb-type for data transfer\n",342				KBUILD_MODNAME);343		return -EINVAL;344	}345}346 347int usb_urb_exitv2(struct usb_data_stream *stream)348{349	usb_urb_free_urbs(stream);350	usb_free_stream_buffers(stream);351 352	return 0;353}354