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