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