1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* 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 the |
8 | * BULK and ISOC USB data transfers in a generic way. |
9 | * Can be used for DVB-only and also, that's the plan, for |
10 | * Hybrid USB devices (analog and DVB). |
11 | */ |
12 | #include "dvb_usb_common.h" |
13 | |
14 | /* URB stuff for streaming */ |
15 | |
16 | int usb_urb_reconfig(struct usb_data_stream *stream, |
17 | struct usb_data_stream_properties *props); |
18 | |
19 | static 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 | |
79 | int 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(urb: stream->urb_list[i]); |
86 | } |
87 | stream->urbs_submitted = 0; |
88 | return 0; |
89 | } |
90 | |
91 | int 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(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 | |
117 | static 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(urb: stream->urb_list[i]); |
129 | } |
130 | } |
131 | stream->urbs_initialized = 0; |
132 | |
133 | return 0; |
134 | } |
135 | |
136 | static 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(iso_packets: 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(urb: stream->urb_list[j]); |
148 | return -ENOMEM; |
149 | } |
150 | usb_fill_bulk_urb(urb: stream->urb_list[i], |
151 | dev: stream->udev, |
152 | usb_rcvbulkpipe(stream->udev, |
153 | stream->props.endpoint), |
154 | transfer_buffer: stream->buf_list[i], |
155 | buffer_length: stream->props.u.bulk.buffersize, |
156 | complete_fn: usb_urb_complete, context: stream); |
157 | |
158 | stream->urbs_initialized++; |
159 | } |
160 | return 0; |
161 | } |
162 | |
163 | static 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 | iso_packets: 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(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 | |
207 | static 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(objp: stream->buf_list[stream->buf_num]); |
213 | } |
214 | } |
215 | |
216 | stream->state &= ~USB_STATE_URB_BUF; |
217 | |
218 | return 0; |
219 | } |
220 | |
221 | static 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 | |
250 | int 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 | |
306 | int 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, num: stream->props.count, |
326 | size: 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, num: stream->props.count, |
333 | size: 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 | |
347 | int 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 | |