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