Commit | Line | Data |
---|---|---|
702422bd T |
1 | /****************************************************************************** |
2 | * * | |
3 | * easycap_main.c * | |
4 | * * | |
5 | * Video driver for EasyCAP USB2.0 Video Capture Device DC60 * | |
6 | * * | |
7 | * * | |
8 | ******************************************************************************/ | |
9 | /* | |
10 | * | |
11 | * Copyright (C) 2010 R.M. Thomas <rmthomas@sciolus.org> | |
12 | * | |
13 | * | |
14 | * This is free software; you can redistribute it and/or modify | |
15 | * it under the terms of the GNU General Public License as published by | |
16 | * the Free Software Foundation; either version 2 of the License, or | |
17 | * (at your option) any later version. | |
18 | * | |
19 | * The software is distributed in the hope that it will be useful, | |
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
22 | * GNU General Public License for more details. | |
23 | * | |
24 | * You should have received a copy of the GNU General Public License | |
25 | * along with this software; if not, write to the Free Software | |
26 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
27 | * | |
28 | */ | |
29 | /*****************************************************************************/ | |
30 | ||
31 | #include "easycap.h" | |
fc3cc2ca | 32 | #include <linux/usb/audio.h> |
702422bd | 33 | |
02149cf7 TW |
34 | |
35 | MODULE_LICENSE("GPL"); | |
36 | MODULE_AUTHOR("R.M. Thomas <rmthomas@sciolus.org>"); | |
37 | MODULE_DESCRIPTION(EASYCAP_DRIVER_DESCRIPTION); | |
38 | MODULE_VERSION(EASYCAP_DRIVER_VERSION); | |
39 | ||
40 | #ifdef CONFIG_EASYCAP_DEBUG | |
18545cfd | 41 | int easycap_debug; |
62af33ec | 42 | module_param_named(debug, easycap_debug, int, S_IRUGO | S_IWUSR); |
02149cf7 TW |
43 | MODULE_PARM_DESC(debug, "Debug level: 0(default),1,2,...,9"); |
44 | #endif /* CONFIG_EASYCAP_DEBUG */ | |
45 | ||
2ef0c05e TW |
46 | bool easycap_readback; |
47 | module_param_named(readback, easycap_readback, bool, S_IRUGO | S_IWUSR); | |
48 | MODULE_PARM_DESC(readback, "read back written registers: (default false)"); | |
49 | ||
02149cf7 | 50 | static int easycap_bars = 1; |
62af33ec | 51 | module_param_named(bars, easycap_bars, int, S_IRUGO | S_IWUSR); |
02149cf7 TW |
52 | MODULE_PARM_DESC(bars, |
53 | "Testcard bars on input signal failure: 0=>no, 1=>yes(default)"); | |
702422bd | 54 | |
02149cf7 TW |
55 | static int easycap_gain = 16; |
56 | module_param_named(gain, easycap_gain, int, S_IRUGO | S_IWUSR); | |
57 | MODULE_PARM_DESC(gain, "Audio gain: 0,...,16(default),...31"); | |
d090bf57 | 58 | |
8d613954 TW |
59 | static bool easycap_ntsc; |
60 | module_param_named(ntsc, easycap_ntsc, bool, S_IRUGO | S_IWUSR); | |
aa3e842d | 61 | MODULE_PARM_DESC(ntsc, "NTSC default encoding (default PAL)"); |
8d613954 | 62 | |
d090bf57 TW |
63 | |
64 | ||
a9855917 MT |
65 | struct easycap_dongle easycapdc60_dongle[DONGLE_MANY]; |
66 | static struct mutex mutex_dongle; | |
d090bf57 TW |
67 | static void easycap_complete(struct urb *purb); |
68 | static int reset(struct easycap *peasycap); | |
e68703cf | 69 | |
5c0c6c39 TW |
70 | const char *strerror(int err) |
71 | { | |
72 | #define ERRNOSTR(_e) case _e: return # _e | |
73 | switch (err) { | |
74 | case 0: return "OK"; | |
75 | ERRNOSTR(ENOMEM); | |
76 | ERRNOSTR(ENODEV); | |
77 | ERRNOSTR(ENXIO); | |
78 | ERRNOSTR(EINVAL); | |
79 | ERRNOSTR(EAGAIN); | |
80 | ERRNOSTR(EFBIG); | |
81 | ERRNOSTR(EPIPE); | |
82 | ERRNOSTR(EMSGSIZE); | |
83 | ERRNOSTR(ENOSPC); | |
84 | ERRNOSTR(EINPROGRESS); | |
85 | ERRNOSTR(ENOSR); | |
86 | ERRNOSTR(EOVERFLOW); | |
87 | ERRNOSTR(EPROTO); | |
88 | ERRNOSTR(EILSEQ); | |
89 | ERRNOSTR(ETIMEDOUT); | |
90 | ERRNOSTR(EOPNOTSUPP); | |
91 | ERRNOSTR(EPFNOSUPPORT); | |
92 | ERRNOSTR(EAFNOSUPPORT); | |
93 | ERRNOSTR(EADDRINUSE); | |
94 | ERRNOSTR(EADDRNOTAVAIL); | |
95 | ERRNOSTR(ENOBUFS); | |
96 | ERRNOSTR(EISCONN); | |
97 | ERRNOSTR(ENOTCONN); | |
98 | ERRNOSTR(ESHUTDOWN); | |
99 | ERRNOSTR(ENOENT); | |
100 | ERRNOSTR(ECONNRESET); | |
101 | ERRNOSTR(ETIME); | |
102 | ERRNOSTR(ECOMM); | |
103 | ERRNOSTR(EREMOTEIO); | |
104 | ERRNOSTR(EXDEV); | |
105 | ERRNOSTR(EPERM); | |
106 | default: return "unknown"; | |
107 | } | |
108 | ||
109 | #undef ERRNOSTR | |
110 | } | |
111 | ||
702422bd T |
112 | /*---------------------------------------------------------------------------*/ |
113 | /* | |
114 | * PARAMETERS USED WHEN REGISTERING THE VIDEO INTERFACE | |
115 | * | |
116 | * NOTE: SOME KERNELS IGNORE usb_class_driver.minor_base, AS MENTIONED BY | |
117 | * CORBET ET AL. "LINUX DEVICE DRIVERS", 3rd EDITION, PAGE 253. | |
118 | * THIS IS THE CASE FOR OpenSUSE. | |
119 | */ | |
120 | /*---------------------------------------------------------------------------*/ | |
702422bd | 121 | /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ |
702422bd | 122 | /****************************************************************************/ |
e68703cf | 123 | /*---------------------------------------------------------------------------*/ |
702422bd | 124 | /* |
268dfede | 125 | * THIS ROUTINE DOES NOT DETECT DUPLICATE OCCURRENCES OF POINTER peasycap |
e68703cf MT |
126 | */ |
127 | /*---------------------------------------------------------------------------*/ | |
c7506658 | 128 | int isdongle(struct easycap *peasycap) |
e68703cf | 129 | { |
c7506658 | 130 | int k; |
6888393c | 131 | if (!peasycap) |
c7506658 TW |
132 | return -2; |
133 | for (k = 0; k < DONGLE_MANY; k++) { | |
134 | if (easycapdc60_dongle[k].peasycap == peasycap) { | |
135 | peasycap->isdongle = k; | |
136 | return k; | |
137 | } | |
e68703cf | 138 | } |
c7506658 | 139 | return -1; |
e68703cf | 140 | } |
702422bd | 141 | /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ |
d090bf57 | 142 | static int easycap_open(struct inode *inode, struct file *file) |
702422bd | 143 | { |
c7506658 | 144 | struct video_device *pvideo_device; |
c7506658 TW |
145 | struct easycap *peasycap; |
146 | int rc; | |
702422bd | 147 | |
c7506658 TW |
148 | JOT(4, "\n"); |
149 | SAY("==========OPEN=========\n"); | |
702422bd | 150 | |
c7506658 | 151 | pvideo_device = video_devdata(file); |
6888393c | 152 | if (!pvideo_device) { |
c7506658 TW |
153 | SAY("ERROR: pvideo_device is NULL.\n"); |
154 | return -EFAULT; | |
155 | } | |
156 | peasycap = (struct easycap *)video_get_drvdata(pvideo_device); | |
6888393c | 157 | if (!peasycap) { |
c7506658 TW |
158 | SAY("ERROR: peasycap is NULL\n"); |
159 | return -EFAULT; | |
160 | } | |
6888393c | 161 | if (!peasycap->pusb_device) { |
c7506658 TW |
162 | SAM("ERROR: peasycap->pusb_device is NULL\n"); |
163 | return -EFAULT; | |
164 | } else { | |
165 | JOM(16, "peasycap->pusb_device=%p\n", peasycap->pusb_device); | |
166 | } | |
167 | file->private_data = peasycap; | |
168 | rc = wakeup_device(peasycap->pusb_device); | |
169 | if (0 == rc) | |
170 | JOM(8, "wakeup_device() OK\n"); | |
171 | else { | |
172 | SAM("ERROR: wakeup_device() rc = %i\n", rc); | |
173 | if (-ENODEV == rc) | |
174 | SAM("ERROR: wakeup_device() returned -ENODEV\n"); | |
175 | else | |
176 | SAM("ERROR: wakeup_device() rc = %i\n", rc); | |
177 | return rc; | |
178 | } | |
179 | peasycap->input = 0; | |
180 | rc = reset(peasycap); | |
181 | if (rc) { | |
182 | SAM("ERROR: reset() rc = %i\n", rc); | |
183 | return -EFAULT; | |
184 | } | |
185 | return 0; | |
f36bc37a | 186 | } |
d090bf57 | 187 | |
f36bc37a MT |
188 | /*****************************************************************************/ |
189 | /*---------------------------------------------------------------------------*/ | |
190 | /* | |
191 | * RESET THE HARDWARE TO ITS REFERENCE STATE. | |
192 | * | |
193 | * THIS ROUTINE MAY BE CALLED REPEATEDLY IF easycap_complete() DETECTS | |
194 | * A BAD VIDEO FRAME SIZE. | |
195 | */ | |
196 | /*---------------------------------------------------------------------------*/ | |
d090bf57 | 197 | static int reset(struct easycap *peasycap) |
f36bc37a | 198 | { |
c7506658 | 199 | struct easycap_standard const *peasycap_standard; |
fd1b821c | 200 | int fmtidx, input, rate; |
c7506658 | 201 | bool ntsc, other; |
fd1b821c | 202 | int rc; |
f36bc37a | 203 | |
6888393c | 204 | if (!peasycap) { |
c7506658 TW |
205 | SAY("ERROR: peasycap is NULL\n"); |
206 | return -EFAULT; | |
207 | } | |
208 | input = peasycap->input; | |
f36bc37a MT |
209 | |
210 | /*---------------------------------------------------------------------------*/ | |
211 | /* | |
ae59dad4 | 212 | * IF THE SAA7113H HAS ALREADY ACQUIRED SYNC, USE ITS HARDWARE-DETECTED |
f36bc37a MT |
213 | * FIELD FREQUENCY TO DISTINGUISH NTSC FROM PAL. THIS IS ESSENTIAL FOR |
214 | * gstreamer AND OTHER USERSPACE PROGRAMS WHICH MAY NOT ATTEMPT TO INITIATE | |
215 | * A SWITCH BETWEEN PAL AND NTSC. | |
216 | * | |
217 | * FUNCTION ready_saa() MAY REQUIRE A SUBSTANTIAL FRACTION OF A SECOND TO | |
218 | * COMPLETE, SO SHOULD NOT BE INVOKED WITHOUT GOOD REASON. | |
219 | */ | |
220 | /*---------------------------------------------------------------------------*/ | |
c7506658 TW |
221 | other = false; |
222 | JOM(8, "peasycap->ntsc=%d\n", peasycap->ntsc); | |
223 | ||
224 | rate = ready_saa(peasycap->pusb_device); | |
fd1b821c | 225 | if (rate < 0) { |
c7506658 | 226 | JOM(8, "not ready to capture after %i ms ...\n", PATIENCE); |
fd1b821c TW |
227 | ntsc = !peasycap->ntsc; |
228 | JOM(8, "... trying %s ..\n", ntsc ? "NTSC" : "PAL"); | |
229 | rc = setup_stk(peasycap->pusb_device, ntsc); | |
230 | if (rc) { | |
231 | SAM("ERROR: setup_stk() rc = %i\n", rc); | |
232 | return -EFAULT; | |
233 | } | |
234 | rc = setup_saa(peasycap->pusb_device, ntsc); | |
235 | if (rc) { | |
236 | SAM("ERROR: setup_saa() rc = %i\n", rc); | |
237 | return -EFAULT; | |
238 | } | |
239 | ||
240 | rate = ready_saa(peasycap->pusb_device); | |
241 | if (rate < 0) { | |
242 | JOM(8, "not ready to capture after %i ms\n", PATIENCE); | |
243 | JOM(8, "... saa register 0x1F has 0x%02X\n", | |
c7506658 | 244 | read_saa(peasycap->pusb_device, 0x1F)); |
fd1b821c | 245 | ntsc = peasycap->ntsc; |
c7506658 TW |
246 | } else { |
247 | JOM(8, "... success at second try: %i=rate\n", rate); | |
248 | ntsc = (0 < (rate/2)) ? true : false ; | |
249 | other = true; | |
250 | } | |
f36bc37a | 251 | } else { |
c7506658 TW |
252 | JOM(8, "... success at first try: %i=rate\n", rate); |
253 | ntsc = (0 < rate/2) ? true : false ; | |
254 | } | |
255 | JOM(8, "ntsc=%d\n", ntsc); | |
256 | /*---------------------------------------------------------------------------*/ | |
257 | ||
258 | rc = setup_stk(peasycap->pusb_device, ntsc); | |
fd1b821c | 259 | if (rc) { |
c7506658 TW |
260 | SAM("ERROR: setup_stk() rc = %i\n", rc); |
261 | return -EFAULT; | |
262 | } | |
263 | rc = setup_saa(peasycap->pusb_device, ntsc); | |
fd1b821c | 264 | if (rc) { |
c7506658 TW |
265 | SAM("ERROR: setup_saa() rc = %i\n", rc); |
266 | return -EFAULT; | |
f36bc37a | 267 | } |
702422bd | 268 | |
fd1b821c | 269 | memset(peasycap->merit, 0, sizeof(peasycap->merit)); |
c7506658 TW |
270 | |
271 | peasycap->video_eof = 0; | |
272 | peasycap->audio_eof = 0; | |
273 | do_gettimeofday(&peasycap->timeval7); | |
f36bc37a MT |
274 | /*---------------------------------------------------------------------------*/ |
275 | /* | |
276 | * RESTORE INPUT AND FORCE REFRESH OF STANDARD, FORMAT, ETC. | |
277 | * | |
278 | * WHILE THIS PROCEDURE IS IN PROGRESS, SOME IOCTL COMMANDS WILL RETURN -EBUSY. | |
279 | */ | |
280 | /*---------------------------------------------------------------------------*/ | |
c7506658 TW |
281 | peasycap->input = -8192; |
282 | peasycap->standard_offset = -8192; | |
283 | fmtidx = ntsc ? NTSC_M : PAL_BGHIN; | |
284 | if (other) { | |
285 | peasycap_standard = &easycap_standard[0]; | |
286 | while (0xFFFF != peasycap_standard->mask) { | |
287 | if (fmtidx == peasycap_standard->v4l2_standard.index) { | |
1dc6e418 | 288 | peasycap->inputset[input].standard_offset = |
c7506658 | 289 | peasycap_standard - easycap_standard; |
f36bc37a MT |
290 | break; |
291 | } | |
c7506658 | 292 | peasycap_standard++; |
f36bc37a | 293 | } |
c7506658 TW |
294 | if (0xFFFF == peasycap_standard->mask) { |
295 | SAM("ERROR: standard not found\n"); | |
296 | return -EINVAL; | |
297 | } | |
298 | JOM(8, "%i=peasycap->inputset[%i].standard_offset\n", | |
299 | peasycap->inputset[input].standard_offset, input); | |
f36bc37a | 300 | } |
c7506658 TW |
301 | peasycap->format_offset = -8192; |
302 | peasycap->brightness = -8192; | |
303 | peasycap->contrast = -8192; | |
304 | peasycap->saturation = -8192; | |
305 | peasycap->hue = -8192; | |
f36bc37a | 306 | |
c7506658 | 307 | rc = newinput(peasycap, input); |
702422bd | 308 | |
c7506658 TW |
309 | if (rc) { |
310 | SAM("ERROR: newinput(.,%i) rc = %i\n", rc, input); | |
311 | return -EFAULT; | |
312 | } | |
f36bc37a | 313 | JOM(4, "restored input, standard and format\n"); |
c7506658 TW |
314 | |
315 | JOM(8, "true=peasycap->ntsc %d\n", peasycap->ntsc); | |
316 | ||
317 | if (0 > peasycap->input) { | |
318 | SAM("MISTAKE: %i=peasycap->input\n", peasycap->input); | |
319 | return -ENOENT; | |
320 | } | |
321 | if (0 > peasycap->standard_offset) { | |
322 | SAM("MISTAKE: %i=peasycap->standard_offset\n", | |
323 | peasycap->standard_offset); | |
324 | return -ENOENT; | |
325 | } | |
326 | if (0 > peasycap->format_offset) { | |
327 | SAM("MISTAKE: %i=peasycap->format_offset\n", | |
328 | peasycap->format_offset); | |
329 | return -ENOENT; | |
330 | } | |
331 | if (0 > peasycap->brightness) { | |
332 | SAM("MISTAKE: %i=peasycap->brightness\n", | |
333 | peasycap->brightness); | |
334 | return -ENOENT; | |
335 | } | |
336 | if (0 > peasycap->contrast) { | |
337 | SAM("MISTAKE: %i=peasycap->contrast\n", peasycap->contrast); | |
338 | return -ENOENT; | |
339 | } | |
340 | if (0 > peasycap->saturation) { | |
341 | SAM("MISTAKE: %i=peasycap->saturation\n", | |
342 | peasycap->saturation); | |
343 | return -ENOENT; | |
344 | } | |
345 | if (0 > peasycap->hue) { | |
346 | SAM("MISTAKE: %i=peasycap->hue\n", peasycap->hue); | |
347 | return -ENOENT; | |
348 | } | |
349 | return 0; | |
f36bc37a MT |
350 | } |
351 | /*****************************************************************************/ | |
352 | /*---------------------------------------------------------------------------*/ | |
353 | /* | |
354 | * IF THE REQUESTED INPUT IS THE SAME AS THE EXISTING INPUT, DO NOTHING. | |
355 | * OTHERWISE: | |
356 | * KILL URBS, CLEAR FIELD AND FRAME BUFFERS AND RESET THEIR | |
357 | * _read AND _fill POINTERS. | |
358 | * SELECT THE NEW INPUT. | |
359 | * ADJUST THE STANDARD, FORMAT, BRIGHTNESS, CONTRAST, SATURATION AND HUE | |
360 | * ON THE BASIS OF INFORMATION IN STRUCTURE easycap.inputset[input]. | |
361 | * RESUBMIT THE URBS IF STREAMING WAS ALREADY IN PROGRESS. | |
362 | * | |
363 | * NOTE: | |
364 | * THIS ROUTINE MAY BE CALLED FREQUENTLY BY ZONEMINDER VIA IOCTL, | |
365 | * SO IT SHOULD WRITE ONLY SPARINGLY TO THE LOGFILE. | |
366 | */ | |
367 | /*---------------------------------------------------------------------------*/ | |
368 | int | |
369 | newinput(struct easycap *peasycap, int input) | |
370 | { | |
c7506658 TW |
371 | int rc, k, m, mood, off; |
372 | int inputnow, video_idlenow, audio_idlenow; | |
373 | bool resubmit; | |
f36bc37a | 374 | |
6888393c | 375 | if (!peasycap) { |
c7506658 TW |
376 | SAY("ERROR: peasycap is NULL\n"); |
377 | return -EFAULT; | |
378 | } | |
379 | JOM(8, "%i=input sought\n", input); | |
702422bd | 380 | |
c7506658 TW |
381 | if (0 > input && INPUT_MANY <= input) |
382 | return -ENOENT; | |
383 | inputnow = peasycap->input; | |
384 | if (input == inputnow) | |
385 | return 0; | |
702422bd | 386 | /*---------------------------------------------------------------------------*/ |
f36bc37a MT |
387 | /* |
388 | * IF STREAMING IS IN PROGRESS THE URBS ARE KILLED AT THIS | |
389 | * STAGE AND WILL BE RESUBMITTED PRIOR TO EXIT FROM THE ROUTINE. | |
390 | * IF NO STREAMING IS IN PROGRESS NO URBS WILL BE SUBMITTED BY THE | |
391 | * ROUTINE. | |
392 | */ | |
393 | /*---------------------------------------------------------------------------*/ | |
c7506658 TW |
394 | video_idlenow = peasycap->video_idle; |
395 | audio_idlenow = peasycap->audio_idle; | |
f36bc37a | 396 | |
c7506658 TW |
397 | peasycap->video_idle = 1; |
398 | peasycap->audio_idle = 1; | |
399 | if (peasycap->video_isoc_streaming) { | |
400 | resubmit = true; | |
401 | kill_video_urbs(peasycap); | |
402 | } else { | |
403 | resubmit = false; | |
404 | } | |
f36bc37a | 405 | /*---------------------------------------------------------------------------*/ |
6888393c | 406 | if (!peasycap->pusb_device) { |
c7506658 TW |
407 | SAM("ERROR: peasycap->pusb_device is NULL\n"); |
408 | return -ENODEV; | |
409 | } | |
410 | rc = usb_set_interface(peasycap->pusb_device, | |
411 | peasycap->video_interface, | |
412 | peasycap->video_altsetting_off); | |
413 | if (rc) { | |
414 | SAM("ERROR: usb_set_interface() rc = %i\n", rc); | |
415 | return -EFAULT; | |
416 | } | |
417 | rc = stop_100(peasycap->pusb_device); | |
418 | if (rc) { | |
419 | SAM("ERROR: stop_100() rc = %i\n", rc); | |
420 | return -EFAULT; | |
421 | } | |
422 | for (k = 0; k < FIELD_BUFFER_MANY; k++) { | |
423 | for (m = 0; m < FIELD_BUFFER_SIZE/PAGE_SIZE; m++) | |
424 | memset(peasycap->field_buffer[k][m].pgo, 0, PAGE_SIZE); | |
425 | } | |
426 | for (k = 0; k < FRAME_BUFFER_MANY; k++) { | |
427 | for (m = 0; m < FRAME_BUFFER_SIZE/PAGE_SIZE; m++) | |
428 | memset(peasycap->frame_buffer[k][m].pgo, 0, PAGE_SIZE); | |
429 | } | |
430 | peasycap->field_page = 0; | |
431 | peasycap->field_read = 0; | |
432 | peasycap->field_fill = 0; | |
433 | ||
434 | peasycap->frame_read = 0; | |
435 | peasycap->frame_fill = 0; | |
436 | for (k = 0; k < peasycap->input; k++) { | |
437 | (peasycap->frame_fill)++; | |
438 | if (peasycap->frame_buffer_many <= peasycap->frame_fill) | |
439 | peasycap->frame_fill = 0; | |
440 | } | |
441 | peasycap->input = input; | |
442 | select_input(peasycap->pusb_device, peasycap->input, 9); | |
f36bc37a | 443 | /*---------------------------------------------------------------------------*/ |
c7506658 TW |
444 | if (input == peasycap->inputset[input].input) { |
445 | off = peasycap->inputset[input].standard_offset; | |
446 | if (off != peasycap->standard_offset) { | |
447 | rc = adjust_standard(peasycap, | |
f36bc37a | 448 | easycap_standard[off].v4l2_standard.id); |
c7506658 TW |
449 | if (rc) { |
450 | SAM("ERROR: adjust_standard() rc = %i\n", rc); | |
451 | return -EFAULT; | |
452 | } | |
453 | JOM(8, "%i=peasycap->standard_offset\n", | |
454 | peasycap->standard_offset); | |
455 | } else { | |
456 | JOM(8, "%i=peasycap->standard_offset unchanged\n", | |
f36bc37a | 457 | peasycap->standard_offset); |
f36bc37a | 458 | } |
c7506658 TW |
459 | off = peasycap->inputset[input].format_offset; |
460 | if (off != peasycap->format_offset) { | |
461 | struct v4l2_pix_format *pix = | |
462 | &easycap_format[off].v4l2_format.fmt.pix; | |
463 | rc = adjust_format(peasycap, | |
464 | pix->width, pix->height, | |
465 | pix->pixelformat, pix->field, false); | |
466 | if (0 > rc) { | |
467 | SAM("ERROR: adjust_format() rc = %i\n", rc); | |
468 | return -EFAULT; | |
469 | } | |
470 | JOM(8, "%i=peasycap->format_offset\n", | |
471 | peasycap->format_offset); | |
472 | } else { | |
473 | JOM(8, "%i=peasycap->format_offset unchanged\n", | |
474 | peasycap->format_offset); | |
f36bc37a | 475 | } |
c7506658 TW |
476 | mood = peasycap->inputset[input].brightness; |
477 | if (mood != peasycap->brightness) { | |
478 | rc = adjust_brightness(peasycap, mood); | |
479 | if (rc) { | |
480 | SAM("ERROR: adjust_brightness rc = %i\n", rc); | |
481 | return -EFAULT; | |
482 | } | |
483 | JOM(8, "%i=peasycap->brightness\n", | |
484 | peasycap->brightness); | |
f36bc37a | 485 | } |
c7506658 TW |
486 | mood = peasycap->inputset[input].contrast; |
487 | if (mood != peasycap->contrast) { | |
488 | rc = adjust_contrast(peasycap, mood); | |
489 | if (rc) { | |
490 | SAM("ERROR: adjust_contrast rc = %i\n", rc); | |
491 | return -EFAULT; | |
492 | } | |
493 | JOM(8, "%i=peasycap->contrast\n", peasycap->contrast); | |
f36bc37a | 494 | } |
c7506658 TW |
495 | mood = peasycap->inputset[input].saturation; |
496 | if (mood != peasycap->saturation) { | |
497 | rc = adjust_saturation(peasycap, mood); | |
498 | if (rc) { | |
499 | SAM("ERROR: adjust_saturation rc = %i\n", rc); | |
500 | return -EFAULT; | |
501 | } | |
502 | JOM(8, "%i=peasycap->saturation\n", | |
503 | peasycap->saturation); | |
504 | } | |
505 | mood = peasycap->inputset[input].hue; | |
506 | if (mood != peasycap->hue) { | |
507 | rc = adjust_hue(peasycap, mood); | |
508 | if (rc) { | |
509 | SAM("ERROR: adjust_hue rc = %i\n", rc); | |
510 | return -EFAULT; | |
511 | } | |
512 | JOM(8, "%i=peasycap->hue\n", peasycap->hue); | |
f36bc37a | 513 | } |
c7506658 TW |
514 | } else { |
515 | SAM("MISTAKE: easycap.inputset[%i] unpopulated\n", input); | |
516 | return -ENOENT; | |
f36bc37a | 517 | } |
702422bd | 518 | /*---------------------------------------------------------------------------*/ |
6888393c | 519 | if (!peasycap->pusb_device) { |
c7506658 TW |
520 | SAM("ERROR: peasycap->pusb_device is NULL\n"); |
521 | return -ENODEV; | |
522 | } | |
523 | rc = usb_set_interface(peasycap->pusb_device, | |
524 | peasycap->video_interface, | |
525 | peasycap->video_altsetting_on); | |
526 | if (rc) { | |
527 | SAM("ERROR: usb_set_interface() rc = %i\n", rc); | |
528 | return -EFAULT; | |
529 | } | |
530 | rc = start_100(peasycap->pusb_device); | |
531 | if (rc) { | |
532 | SAM("ERROR: start_100() rc = %i\n", rc); | |
533 | return -EFAULT; | |
534 | } | |
27d683ab | 535 | if (resubmit) |
c7506658 | 536 | submit_video_urbs(peasycap); |
f36bc37a | 537 | |
c7506658 TW |
538 | peasycap->video_isoc_sequence = VIDEO_ISOC_BUFFER_MANY - 1; |
539 | peasycap->video_idle = video_idlenow; | |
540 | peasycap->audio_idle = audio_idlenow; | |
541 | peasycap->video_junk = 0; | |
702422bd | 542 | |
c7506658 | 543 | return 0; |
702422bd T |
544 | } |
545 | /*****************************************************************************/ | |
d090bf57 | 546 | int submit_video_urbs(struct easycap *peasycap) |
702422bd | 547 | { |
c7506658 TW |
548 | struct data_urb *pdata_urb; |
549 | struct urb *purb; | |
550 | struct list_head *plist_head; | |
551 | int j, isbad, nospc, m, rc; | |
552 | int isbuf; | |
e68703cf | 553 | |
6888393c | 554 | if (!peasycap) { |
c7506658 TW |
555 | SAY("ERROR: peasycap is NULL\n"); |
556 | return -EFAULT; | |
557 | } | |
558 | ||
6888393c | 559 | if (!peasycap->purb_video_head) { |
c7506658 TW |
560 | SAY("ERROR: peasycap->urb_video_head uninitialized\n"); |
561 | return -EFAULT; | |
562 | } | |
6888393c | 563 | if (!peasycap->pusb_device) { |
c7506658 TW |
564 | SAY("ERROR: peasycap->pusb_device is NULL\n"); |
565 | return -ENODEV; | |
566 | } | |
567 | if (!peasycap->video_isoc_streaming) { | |
568 | JOM(4, "submission of all video urbs\n"); | |
569 | isbad = 0; nospc = 0; m = 0; | |
570 | list_for_each(plist_head, (peasycap->purb_video_head)) { | |
571 | pdata_urb = list_entry(plist_head, | |
572 | struct data_urb, list_head); | |
573 | if (pdata_urb && pdata_urb->purb) { | |
574 | purb = pdata_urb->purb; | |
702422bd T |
575 | isbuf = pdata_urb->isbuf; |
576 | purb->interval = 1; | |
577 | purb->dev = peasycap->pusb_device; | |
1dc6e418 TW |
578 | purb->pipe = |
579 | usb_rcvisocpipe(peasycap->pusb_device, | |
702422bd T |
580 | peasycap->video_endpointnumber); |
581 | purb->transfer_flags = URB_ISO_ASAP; | |
1dc6e418 | 582 | purb->transfer_buffer = |
702422bd | 583 | peasycap->video_isoc_buffer[isbuf].pgo; |
1dc6e418 | 584 | purb->transfer_buffer_length = |
702422bd T |
585 | peasycap->video_isoc_buffer_size; |
586 | purb->complete = easycap_complete; | |
587 | purb->context = peasycap; | |
588 | purb->start_frame = 0; | |
1dc6e418 | 589 | purb->number_of_packets = |
702422bd T |
590 | peasycap->video_isoc_framesperdesc; |
591 | ||
c7506658 TW |
592 | for (j = 0; j < peasycap->video_isoc_framesperdesc; j++) { |
593 | purb->iso_frame_desc[j]. offset = | |
594 | j * peasycap->video_isoc_maxframesize; | |
595 | purb->iso_frame_desc[j]. length = | |
596 | peasycap->video_isoc_maxframesize; | |
597 | } | |
702422bd T |
598 | |
599 | rc = usb_submit_urb(purb, GFP_KERNEL); | |
5c0c6c39 | 600 | if (rc) { |
702422bd | 601 | isbad++; |
1dc6e418 | 602 | SAM("ERROR: usb_submit_urb() failed " |
c7506658 | 603 | "for urb with rc:-%s\n", |
5c0c6c39 TW |
604 | strerror(rc)); |
605 | if (rc == -ENOSPC) | |
e68703cf | 606 | nospc++; |
702422bd T |
607 | } else { |
608 | m++; | |
609 | } | |
702422bd | 610 | } else { |
5c0c6c39 | 611 | isbad++; |
702422bd T |
612 | } |
613 | } | |
c7506658 TW |
614 | if (nospc) { |
615 | SAM("-ENOSPC=usb_submit_urb() for %i urbs\n", nospc); | |
616 | SAM("..... possibly inadequate USB bandwidth\n"); | |
617 | peasycap->video_eof = 1; | |
618 | } | |
e68703cf | 619 | |
c7506658 TW |
620 | if (isbad) { |
621 | JOM(4, "attempting cleanup instead of submitting\n"); | |
622 | list_for_each(plist_head, (peasycap->purb_video_head)) { | |
623 | pdata_urb = list_entry(plist_head, | |
624 | struct data_urb, list_head); | |
6888393c | 625 | if (pdata_urb) { |
c7506658 | 626 | purb = pdata_urb->purb; |
6888393c | 627 | if (purb) |
c7506658 TW |
628 | usb_kill_urb(purb); |
629 | } | |
702422bd | 630 | } |
c7506658 TW |
631 | peasycap->video_isoc_streaming = 0; |
632 | } else { | |
633 | peasycap->video_isoc_streaming = 1; | |
634 | JOM(4, "submitted %i video urbs\n", m); | |
702422bd | 635 | } |
702422bd | 636 | } else { |
c7506658 | 637 | JOM(4, "already streaming video urbs\n"); |
702422bd | 638 | } |
c7506658 | 639 | return 0; |
702422bd T |
640 | } |
641 | /*****************************************************************************/ | |
c7506658 | 642 | int kill_video_urbs(struct easycap *peasycap) |
702422bd | 643 | { |
c7506658 TW |
644 | int m; |
645 | struct list_head *plist_head; | |
646 | struct data_urb *pdata_urb; | |
702422bd | 647 | |
6888393c | 648 | if (!peasycap) { |
c7506658 TW |
649 | SAY("ERROR: peasycap is NULL\n"); |
650 | return -EFAULT; | |
651 | } | |
652 | if (!peasycap->video_isoc_streaming) { | |
653 | JOM(8, "%i=video_isoc_streaming, no video urbs killed\n", | |
654 | peasycap->video_isoc_streaming); | |
655 | return 0; | |
656 | } | |
657 | if (!peasycap->purb_video_head) { | |
e68703cf | 658 | SAM("ERROR: peasycap->purb_video_head is NULL\n"); |
702422bd T |
659 | return -EFAULT; |
660 | } | |
c7506658 TW |
661 | |
662 | peasycap->video_isoc_streaming = 0; | |
663 | JOM(4, "killing video urbs\n"); | |
664 | m = 0; | |
665 | list_for_each(plist_head, (peasycap->purb_video_head)) { | |
666 | pdata_urb = list_entry(plist_head, struct data_urb, list_head); | |
667 | if (pdata_urb && pdata_urb->purb) { | |
668 | usb_kill_urb(pdata_urb->purb); | |
669 | m++; | |
670 | } | |
671 | } | |
672 | JOM(4, "%i video urbs killed\n", m); | |
673 | ||
674 | return 0; | |
702422bd T |
675 | } |
676 | /****************************************************************************/ | |
702422bd T |
677 | /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ |
678 | /*--------------------------------------------------------------------------*/ | |
d090bf57 TW |
679 | static int easycap_open_noinode(struct file *file) |
680 | { | |
681 | return easycap_open(NULL, file); | |
682 | } | |
683 | ||
d090bf57 | 684 | static int videodev_release(struct video_device *pvideo_device) |
702422bd | 685 | { |
c7506658 | 686 | struct easycap *peasycap; |
702422bd | 687 | |
c7506658 | 688 | peasycap = video_get_drvdata(pvideo_device); |
6888393c | 689 | if (!peasycap) { |
c7506658 TW |
690 | SAY("ERROR: peasycap is NULL\n"); |
691 | SAY("ending unsuccessfully\n"); | |
692 | return -EFAULT; | |
693 | } | |
c7506658 TW |
694 | if (0 != kill_video_urbs(peasycap)) { |
695 | SAM("ERROR: kill_video_urbs() failed\n"); | |
696 | return -EFAULT; | |
697 | } | |
698 | JOM(4, "ending successfully\n"); | |
699 | return 0; | |
702422bd | 700 | } |
e68703cf MT |
701 | /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ |
702 | /*****************************************************************************/ | |
702422bd T |
703 | /*--------------------------------------------------------------------------*/ |
704 | /* | |
ae59dad4 MT |
705 | * THIS FUNCTION IS CALLED FROM WITHIN easycap_usb_disconnect() AND IS |
706 | * PROTECTED BY SEMAPHORES SET AND CLEARED BY easycap_usb_disconnect(). | |
707 | * | |
708 | * BY THIS STAGE THE DEVICE HAS ALREADY BEEN PHYSICALLY UNPLUGGED, SO | |
709 | * peasycap->pusb_device IS NO LONGER VALID. | |
702422bd T |
710 | */ |
711 | /*---------------------------------------------------------------------------*/ | |
d090bf57 | 712 | static void easycap_delete(struct kref *pkref) |
702422bd | 713 | { |
c7506658 TW |
714 | struct easycap *peasycap; |
715 | struct data_urb *pdata_urb; | |
716 | struct list_head *plist_head, *plist_next; | |
717 | int k, m, gone, kd; | |
718 | int allocation_video_urb; | |
719 | int allocation_video_page; | |
720 | int allocation_video_struct; | |
721 | int allocation_audio_urb; | |
722 | int allocation_audio_page; | |
723 | int allocation_audio_struct; | |
724 | int registered_video, registered_audio; | |
725 | ||
726 | peasycap = container_of(pkref, struct easycap, kref); | |
6888393c | 727 | if (!peasycap) { |
c7506658 TW |
728 | SAM("ERROR: peasycap is NULL: cannot perform deletions\n"); |
729 | return; | |
730 | } | |
c7506658 | 731 | kd = isdongle(peasycap); |
702422bd T |
732 | /*---------------------------------------------------------------------------*/ |
733 | /* | |
734 | * FREE VIDEO. | |
735 | */ | |
736 | /*---------------------------------------------------------------------------*/ | |
6888393c | 737 | if (peasycap->purb_video_head) { |
c7506658 TW |
738 | JOM(4, "freeing video urbs\n"); |
739 | m = 0; | |
740 | list_for_each(plist_head, (peasycap->purb_video_head)) { | |
741 | pdata_urb = list_entry(plist_head, | |
742 | struct data_urb, list_head); | |
6888393c | 743 | if (!pdata_urb) { |
c7506658 TW |
744 | JOM(4, "ERROR: pdata_urb is NULL\n"); |
745 | } else { | |
6888393c | 746 | if (pdata_urb->purb) { |
c7506658 TW |
747 | usb_free_urb(pdata_urb->purb); |
748 | pdata_urb->purb = NULL; | |
749 | peasycap->allocation_video_urb -= 1; | |
750 | m++; | |
751 | } | |
702422bd T |
752 | } |
753 | } | |
702422bd | 754 | |
c7506658 | 755 | JOM(4, "%i video urbs freed\n", m); |
702422bd | 756 | /*---------------------------------------------------------------------------*/ |
c7506658 TW |
757 | JOM(4, "freeing video data_urb structures.\n"); |
758 | m = 0; | |
759 | list_for_each_safe(plist_head, plist_next, | |
760 | peasycap->purb_video_head) { | |
761 | pdata_urb = list_entry(plist_head, | |
762 | struct data_urb, list_head); | |
763 | if (pdata_urb) { | |
764 | peasycap->allocation_video_struct -= | |
702422bd | 765 | sizeof(struct data_urb); |
c7506658 TW |
766 | kfree(pdata_urb); |
767 | pdata_urb = NULL; | |
768 | m++; | |
769 | } | |
702422bd | 770 | } |
c7506658 TW |
771 | JOM(4, "%i video data_urb structures freed\n", m); |
772 | JOM(4, "setting peasycap->purb_video_head=NULL\n"); | |
773 | peasycap->purb_video_head = NULL; | |
702422bd | 774 | } |
702422bd | 775 | /*---------------------------------------------------------------------------*/ |
c7506658 TW |
776 | JOM(4, "freeing video isoc buffers.\n"); |
777 | m = 0; | |
778 | for (k = 0; k < VIDEO_ISOC_BUFFER_MANY; k++) { | |
779 | if (peasycap->video_isoc_buffer[k].pgo) { | |
780 | free_pages((unsigned long) | |
781 | peasycap->video_isoc_buffer[k].pgo, | |
782 | VIDEO_ISOC_ORDER); | |
783 | peasycap->video_isoc_buffer[k].pgo = NULL; | |
784 | peasycap->allocation_video_page -= | |
785 | BIT(VIDEO_ISOC_ORDER); | |
786 | m++; | |
787 | } | |
702422bd | 788 | } |
c7506658 TW |
789 | JOM(4, "isoc video buffers freed: %i pages\n", |
790 | m * (0x01 << VIDEO_ISOC_ORDER)); | |
791 | /*---------------------------------------------------------------------------*/ | |
792 | JOM(4, "freeing video field buffers.\n"); | |
793 | gone = 0; | |
794 | for (k = 0; k < FIELD_BUFFER_MANY; k++) { | |
795 | for (m = 0; m < FIELD_BUFFER_SIZE/PAGE_SIZE; m++) { | |
6888393c | 796 | if (peasycap->field_buffer[k][m].pgo) { |
c7506658 TW |
797 | free_page((unsigned long) |
798 | peasycap->field_buffer[k][m].pgo); | |
799 | peasycap->field_buffer[k][m].pgo = NULL; | |
800 | peasycap->allocation_video_page -= 1; | |
801 | gone++; | |
802 | } | |
702422bd T |
803 | } |
804 | } | |
c7506658 TW |
805 | JOM(4, "video field buffers freed: %i pages\n", gone); |
806 | /*---------------------------------------------------------------------------*/ | |
807 | JOM(4, "freeing video frame buffers.\n"); | |
808 | gone = 0; | |
809 | for (k = 0; k < FRAME_BUFFER_MANY; k++) { | |
810 | for (m = 0; m < FRAME_BUFFER_SIZE/PAGE_SIZE; m++) { | |
6888393c | 811 | if (peasycap->frame_buffer[k][m].pgo) { |
c7506658 TW |
812 | free_page((unsigned long) |
813 | peasycap->frame_buffer[k][m].pgo); | |
814 | peasycap->frame_buffer[k][m].pgo = NULL; | |
815 | peasycap->allocation_video_page -= 1; | |
816 | gone++; | |
817 | } | |
702422bd T |
818 | } |
819 | } | |
c7506658 | 820 | JOM(4, "video frame buffers freed: %i pages\n", gone); |
702422bd T |
821 | /*---------------------------------------------------------------------------*/ |
822 | /* | |
823 | * FREE AUDIO. | |
824 | */ | |
825 | /*---------------------------------------------------------------------------*/ | |
6888393c | 826 | if (peasycap->purb_audio_head) { |
c7506658 TW |
827 | JOM(4, "freeing audio urbs\n"); |
828 | m = 0; | |
829 | list_for_each(plist_head, (peasycap->purb_audio_head)) { | |
830 | pdata_urb = list_entry(plist_head, | |
831 | struct data_urb, list_head); | |
6888393c | 832 | if (!pdata_urb) |
c7506658 TW |
833 | JOM(4, "ERROR: pdata_urb is NULL\n"); |
834 | else { | |
6888393c | 835 | if (pdata_urb->purb) { |
c7506658 TW |
836 | usb_free_urb(pdata_urb->purb); |
837 | pdata_urb->purb = NULL; | |
838 | peasycap->allocation_audio_urb -= 1; | |
839 | m++; | |
840 | } | |
841 | } | |
842 | } | |
843 | JOM(4, "%i audio urbs freed\n", m); | |
844 | /*---------------------------------------------------------------------------*/ | |
845 | JOM(4, "freeing audio data_urb structures.\n"); | |
846 | m = 0; | |
847 | list_for_each_safe(plist_head, plist_next, | |
848 | peasycap->purb_audio_head) { | |
849 | pdata_urb = list_entry(plist_head, | |
850 | struct data_urb, list_head); | |
851 | if (pdata_urb) { | |
852 | peasycap->allocation_audio_struct -= | |
853 | sizeof(struct data_urb); | |
854 | kfree(pdata_urb); | |
855 | pdata_urb = NULL; | |
702422bd T |
856 | m++; |
857 | } | |
858 | } | |
c7506658 TW |
859 | JOM(4, "%i audio data_urb structures freed\n", m); |
860 | JOM(4, "setting peasycap->purb_audio_head=NULL\n"); | |
861 | peasycap->purb_audio_head = NULL; | |
702422bd | 862 | } |
702422bd | 863 | /*---------------------------------------------------------------------------*/ |
c7506658 | 864 | JOM(4, "freeing audio isoc buffers.\n"); |
702422bd | 865 | m = 0; |
c7506658 | 866 | for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) { |
6888393c | 867 | if (peasycap->audio_isoc_buffer[k].pgo) { |
c7506658 TW |
868 | free_pages((unsigned long) |
869 | (peasycap->audio_isoc_buffer[k].pgo), | |
870 | AUDIO_ISOC_ORDER); | |
871 | peasycap->audio_isoc_buffer[k].pgo = NULL; | |
872 | peasycap->allocation_audio_page -= | |
873 | BIT(AUDIO_ISOC_ORDER); | |
702422bd T |
874 | m++; |
875 | } | |
876 | } | |
c7506658 | 877 | JOM(4, "easyoss_delete(): isoc audio buffers freed: %i pages\n", |
702422bd | 878 | m * (0x01 << AUDIO_ISOC_ORDER)); |
702422bd | 879 | /*---------------------------------------------------------------------------*/ |
c7506658 TW |
880 | JOM(4, "freeing easycap structure.\n"); |
881 | allocation_video_urb = peasycap->allocation_video_urb; | |
882 | allocation_video_page = peasycap->allocation_video_page; | |
883 | allocation_video_struct = peasycap->allocation_video_struct; | |
884 | registered_video = peasycap->registered_video; | |
885 | allocation_audio_urb = peasycap->allocation_audio_urb; | |
886 | allocation_audio_page = peasycap->allocation_audio_page; | |
887 | allocation_audio_struct = peasycap->allocation_audio_struct; | |
888 | registered_audio = peasycap->registered_audio; | |
889 | ||
c7506658 TW |
890 | if (0 <= kd && DONGLE_MANY > kd) { |
891 | if (mutex_lock_interruptible(&mutex_dongle)) { | |
892 | SAY("ERROR: cannot down mutex_dongle\n"); | |
893 | } else { | |
894 | JOM(4, "locked mutex_dongle\n"); | |
895 | easycapdc60_dongle[kd].peasycap = NULL; | |
896 | mutex_unlock(&mutex_dongle); | |
897 | JOM(4, "unlocked mutex_dongle\n"); | |
898 | JOT(4, " null-->dongle[%i].peasycap\n", kd); | |
899 | allocation_video_struct -= sizeof(struct easycap); | |
900 | } | |
a9855917 | 901 | } else { |
c7506658 TW |
902 | SAY("ERROR: cannot purge dongle[].peasycap"); |
903 | } | |
38d0cffe DC |
904 | |
905 | kfree(peasycap); | |
906 | ||
702422bd | 907 | /*---------------------------------------------------------------------------*/ |
c7506658 TW |
908 | SAY("%8i=video urbs after all deletions\n", allocation_video_urb); |
909 | SAY("%8i=video pages after all deletions\n", allocation_video_page); | |
910 | SAY("%8i=video structs after all deletions\n", allocation_video_struct); | |
911 | SAY("%8i=video devices after all deletions\n", registered_video); | |
912 | SAY("%8i=audio urbs after all deletions\n", allocation_audio_urb); | |
913 | SAY("%8i=audio pages after all deletions\n", allocation_audio_page); | |
914 | SAY("%8i=audio structs after all deletions\n", allocation_audio_struct); | |
915 | SAY("%8i=audio devices after all deletions\n", registered_audio); | |
916 | ||
917 | JOT(4, "ending.\n"); | |
918 | return; | |
702422bd T |
919 | } |
920 | /*****************************************************************************/ | |
d090bf57 | 921 | static unsigned int easycap_poll(struct file *file, poll_table *wait) |
702422bd | 922 | { |
c7506658 TW |
923 | struct easycap *peasycap; |
924 | int rc, kd; | |
702422bd | 925 | |
c7506658 | 926 | JOT(8, "\n"); |
702422bd | 927 | |
c7506658 TW |
928 | if (NULL == ((poll_table *)wait)) |
929 | JOT(8, "WARNING: poll table pointer is NULL ... continuing\n"); | |
6888393c | 930 | if (!file) { |
c7506658 | 931 | SAY("ERROR: file pointer is NULL\n"); |
ae59dad4 MT |
932 | return -ERESTARTSYS; |
933 | } | |
934 | peasycap = file->private_data; | |
6888393c | 935 | if (!peasycap) { |
ae59dad4 | 936 | SAY("ERROR: peasycap is NULL\n"); |
c7506658 | 937 | return -EFAULT; |
ae59dad4 | 938 | } |
6888393c | 939 | if (!peasycap->pusb_device) { |
c7506658 TW |
940 | SAY("ERROR: peasycap->pusb_device is NULL\n"); |
941 | return -EFAULT; | |
ae59dad4 | 942 | } |
c7506658 TW |
943 | /*---------------------------------------------------------------------------*/ |
944 | kd = isdongle(peasycap); | |
945 | if (0 <= kd && DONGLE_MANY > kd) { | |
946 | if (mutex_lock_interruptible(&easycapdc60_dongle[kd].mutex_video)) { | |
947 | SAY("ERROR: cannot down dongle[%i].mutex_video\n", kd); | |
948 | return -ERESTARTSYS; | |
949 | } | |
950 | JOM(4, "locked dongle[%i].mutex_video\n", kd); | |
951 | /* | |
952 | * MEANWHILE, easycap_usb_disconnect() MAY HAVE FREED POINTER | |
953 | * peasycap, IN WHICH CASE A REPEAT CALL TO isdongle() WILL FAIL. | |
954 | * IF NECESSARY, BAIL OUT. | |
955 | */ | |
22f88fcf AK |
956 | if (kd != isdongle(peasycap)) { |
957 | mutex_unlock(&easycapdc60_dongle[kd].mutex_video); | |
c7506658 | 958 | return -ERESTARTSYS; |
22f88fcf | 959 | } |
6888393c | 960 | if (!file) { |
c7506658 TW |
961 | SAY("ERROR: file is NULL\n"); |
962 | mutex_unlock(&easycapdc60_dongle[kd].mutex_video); | |
963 | return -ERESTARTSYS; | |
964 | } | |
965 | peasycap = file->private_data; | |
6888393c | 966 | if (!peasycap) { |
c7506658 TW |
967 | SAY("ERROR: peasycap is NULL\n"); |
968 | mutex_unlock(&easycapdc60_dongle[kd].mutex_video); | |
969 | return -ERESTARTSYS; | |
970 | } | |
6888393c | 971 | if (!peasycap->pusb_device) { |
c7506658 TW |
972 | SAM("ERROR: peasycap->pusb_device is NULL\n"); |
973 | mutex_unlock(&easycapdc60_dongle[kd].mutex_video); | |
974 | return -ERESTARTSYS; | |
975 | } | |
976 | } else | |
ae59dad4 MT |
977 | /* |
978 | * IF easycap_usb_disconnect() HAS ALREADY FREED POINTER peasycap | |
979 | * BEFORE THE ATTEMPT TO ACQUIRE THE SEMAPHORE, isdongle() WILL | |
980 | * HAVE FAILED. BAIL OUT. | |
981 | */ | |
c7506658 TW |
982 | return -ERESTARTSYS; |
983 | /*---------------------------------------------------------------------------*/ | |
984 | rc = easycap_dqbuf(peasycap, 0); | |
985 | peasycap->polled = 1; | |
986 | mutex_unlock(&easycapdc60_dongle[kd].mutex_video); | |
987 | if (0 == rc) | |
988 | return POLLIN | POLLRDNORM; | |
989 | else | |
990 | return POLLERR; | |
991 | } | |
702422bd T |
992 | /*****************************************************************************/ |
993 | /*---------------------------------------------------------------------------*/ | |
994 | /* | |
995 | * IF mode IS NONZERO THIS ROUTINE RETURNS -EAGAIN RATHER THAN BLOCKING. | |
996 | */ | |
997 | /*---------------------------------------------------------------------------*/ | |
c7506658 | 998 | int easycap_dqbuf(struct easycap *peasycap, int mode) |
702422bd | 999 | { |
c7506658 | 1000 | int input, ifield, miss, rc; |
702422bd | 1001 | |
702422bd | 1002 | |
6888393c | 1003 | if (!peasycap) { |
c7506658 TW |
1004 | SAY("ERROR: peasycap is NULL\n"); |
1005 | return -EFAULT; | |
1006 | } | |
6888393c | 1007 | if (!peasycap->pusb_device) { |
c7506658 TW |
1008 | SAY("ERROR: peasycap->pusb_device is NULL\n"); |
1009 | return -EFAULT; | |
1010 | } | |
1011 | ifield = 0; | |
1012 | JOM(8, "%i=ifield\n", ifield); | |
702422bd | 1013 | /*---------------------------------------------------------------------------*/ |
849322a0 MT |
1014 | /* |
1015 | * CHECK FOR LOST INPUT SIGNAL. | |
1016 | * | |
1017 | * FOR THE FOUR-CVBS EasyCAP, THIS DOES NOT WORK AS EXPECTED. | |
ae59dad4 MT |
1018 | * IF INPUT 0 IS PRESENT AND SYNC ACQUIRED, UNPLUGGING INPUT 4 DOES NOT |
1019 | * RESULT IN SETTING BIT 0x40 ON REGISTER 0x1F, PRESUMABLY BECAUSE THERE | |
1020 | * IS FLYWHEELING ON INPUT 0. THE UPSHOT IS: | |
849322a0 MT |
1021 | * |
1022 | * INPUT 0 PLUGGED, INPUT 4 PLUGGED => SCREEN 0 OK, SCREEN 4 OK | |
1023 | * INPUT 0 PLUGGED, INPUT 4 UNPLUGGED => SCREEN 0 OK, SCREEN 4 BLACK | |
1024 | * INPUT 0 UNPLUGGED, INPUT 4 PLUGGED => SCREEN 0 BARS, SCREEN 4 OK | |
1025 | * INPUT 0 UNPLUGGED, INPUT 4 UNPLUGGED => SCREEN 0 BARS, SCREEN 4 BARS | |
1026 | */ | |
1027 | /*---------------------------------------------------------------------------*/ | |
c7506658 TW |
1028 | input = peasycap->input; |
1029 | if (0 <= input && INPUT_MANY > input) { | |
1030 | rc = read_saa(peasycap->pusb_device, 0x1F); | |
1031 | if (0 <= rc) { | |
1032 | if (rc & 0x40) | |
1033 | peasycap->lost[input] += 1; | |
1034 | else | |
1035 | peasycap->lost[input] -= 2; | |
849322a0 | 1036 | |
c7506658 TW |
1037 | if (0 > peasycap->lost[input]) |
1038 | peasycap->lost[input] = 0; | |
1039 | else if ((2 * VIDEO_LOST_TOLERATE) < peasycap->lost[input]) | |
1040 | peasycap->lost[input] = (2 * VIDEO_LOST_TOLERATE); | |
1041 | } | |
849322a0 | 1042 | } |
849322a0 | 1043 | /*---------------------------------------------------------------------------*/ |
702422bd | 1044 | /* |
40b8d50a | 1045 | * WAIT FOR FIELD ifield (0 => TOP, 1 => BOTTOM) |
702422bd T |
1046 | */ |
1047 | /*---------------------------------------------------------------------------*/ | |
c7506658 TW |
1048 | miss = 0; |
1049 | while ((peasycap->field_read == peasycap->field_fill) || | |
1050 | (0 != (0xFF00 & peasycap->field_buffer | |
1dc6e418 | 1051 | [peasycap->field_read][0].kount)) || |
c7506658 | 1052 | (ifield != (0x00FF & peasycap->field_buffer |
702422bd | 1053 | [peasycap->field_read][0].kount))) { |
c7506658 TW |
1054 | if (mode) |
1055 | return -EAGAIN; | |
702422bd | 1056 | |
a90f3620 TW |
1057 | JOM(8, "first wait on wq_video, %i=field_read %i=field_fill\n", |
1058 | peasycap->field_read, peasycap->field_fill); | |
c7506658 TW |
1059 | |
1060 | if (0 != (wait_event_interruptible(peasycap->wq_video, | |
1061 | (peasycap->video_idle || peasycap->video_eof || | |
1062 | ((peasycap->field_read != peasycap->field_fill) && | |
a90f3620 TW |
1063 | (0 == (0xFF00 & peasycap->field_buffer[peasycap->field_read][0].kount)) && |
1064 | (ifield == (0x00FF & peasycap->field_buffer[peasycap->field_read][0].kount))))))) { | |
c7506658 TW |
1065 | SAM("aborted by signal\n"); |
1066 | return -EIO; | |
a90f3620 | 1067 | } |
c7506658 TW |
1068 | if (peasycap->video_idle) { |
1069 | JOM(8, "%i=peasycap->video_idle returning -EAGAIN\n", | |
1070 | peasycap->video_idle); | |
f36bc37a MT |
1071 | return -EAGAIN; |
1072 | } | |
c7506658 TW |
1073 | if (peasycap->video_eof) { |
1074 | JOM(8, "%i=peasycap->video_eof\n", peasycap->video_eof); | |
1075 | #if defined(PERSEVERE) | |
1076 | if (1 == peasycap->status) { | |
1077 | JOM(8, "persevering ...\n"); | |
1078 | peasycap->video_eof = 0; | |
1079 | peasycap->audio_eof = 0; | |
1080 | if (0 != reset(peasycap)) { | |
1081 | JOM(8, " ... failed returning -EIO\n"); | |
1082 | peasycap->video_eof = 1; | |
1083 | peasycap->audio_eof = 1; | |
1084 | kill_video_urbs(peasycap); | |
1085 | return -EIO; | |
1086 | } | |
1087 | peasycap->status = 0; | |
1088 | JOM(8, " ... OK returning -EAGAIN\n"); | |
1089 | return -EAGAIN; | |
1090 | } | |
1091 | #endif /*PERSEVERE*/ | |
1092 | peasycap->video_eof = 1; | |
1093 | peasycap->audio_eof = 1; | |
1094 | kill_video_urbs(peasycap); | |
1095 | JOM(8, "returning -EIO\n"); | |
1096 | return -EIO; | |
1097 | } | |
a90f3620 | 1098 | miss++; |
702422bd | 1099 | } |
c7506658 | 1100 | JOM(8, "first awakening on wq_video after %i waits\n", miss); |
702422bd | 1101 | |
c7506658 TW |
1102 | rc = field2frame(peasycap); |
1103 | if (rc) | |
1104 | SAM("ERROR: field2frame() rc = %i\n", rc); | |
702422bd T |
1105 | /*---------------------------------------------------------------------------*/ |
1106 | /* | |
40b8d50a | 1107 | * WAIT FOR THE OTHER FIELD |
702422bd T |
1108 | */ |
1109 | /*---------------------------------------------------------------------------*/ | |
c7506658 TW |
1110 | if (ifield) |
1111 | ifield = 0; | |
1112 | else | |
1113 | ifield = 1; | |
1114 | miss = 0; | |
1115 | while ((peasycap->field_read == peasycap->field_fill) || | |
a90f3620 TW |
1116 | (0 != (0xFF00 & peasycap->field_buffer[peasycap->field_read][0].kount)) || |
1117 | (ifield != (0x00FF & peasycap->field_buffer[peasycap->field_read][0].kount))) { | |
c7506658 TW |
1118 | if (mode) |
1119 | return -EAGAIN; | |
702422bd | 1120 | |
c7506658 | 1121 | JOM(8, "second wait on wq_video %i=field_read %i=field_fill\n", |
702422bd | 1122 | peasycap->field_read, peasycap->field_fill); |
c7506658 | 1123 | if (0 != (wait_event_interruptible(peasycap->wq_video, |
1dc6e418 TW |
1124 | (peasycap->video_idle || peasycap->video_eof || |
1125 | ((peasycap->field_read != peasycap->field_fill) && | |
a90f3620 TW |
1126 | (0 == (0xFF00 & peasycap->field_buffer[peasycap->field_read][0].kount)) && |
1127 | (ifield == (0x00FF & peasycap->field_buffer[peasycap->field_read][0].kount))))))) { | |
c7506658 TW |
1128 | SAM("aborted by signal\n"); |
1129 | return -EIO; | |
1130 | } | |
1131 | if (peasycap->video_idle) { | |
1132 | JOM(8, "%i=peasycap->video_idle returning -EAGAIN\n", | |
f36bc37a | 1133 | peasycap->video_idle); |
f36bc37a MT |
1134 | return -EAGAIN; |
1135 | } | |
c7506658 TW |
1136 | if (peasycap->video_eof) { |
1137 | JOM(8, "%i=peasycap->video_eof\n", peasycap->video_eof); | |
a90f3620 | 1138 | #if defined(PERSEVERE) |
c7506658 TW |
1139 | if (1 == peasycap->status) { |
1140 | JOM(8, "persevering ...\n"); | |
1141 | peasycap->video_eof = 0; | |
1142 | peasycap->audio_eof = 0; | |
1143 | if (0 != reset(peasycap)) { | |
1144 | JOM(8, " ... failed returning -EIO\n"); | |
1145 | peasycap->video_eof = 1; | |
1146 | peasycap->audio_eof = 1; | |
1147 | kill_video_urbs(peasycap); | |
1148 | return -EIO; | |
1149 | } | |
1150 | peasycap->status = 0; | |
1151 | JOM(8, " ... OK ... returning -EAGAIN\n"); | |
1152 | return -EAGAIN; | |
1153 | } | |
a90f3620 | 1154 | #endif /*PERSEVERE*/ |
c7506658 TW |
1155 | peasycap->video_eof = 1; |
1156 | peasycap->audio_eof = 1; | |
1157 | kill_video_urbs(peasycap); | |
1158 | JOM(8, "returning -EIO\n"); | |
1159 | return -EIO; | |
1160 | } | |
a90f3620 | 1161 | miss++; |
702422bd | 1162 | } |
c7506658 | 1163 | JOM(8, "second awakening on wq_video after %i waits\n", miss); |
702422bd | 1164 | |
c7506658 TW |
1165 | rc = field2frame(peasycap); |
1166 | if (rc) | |
1167 | SAM("ERROR: field2frame() rc = %i\n", rc); | |
40b8d50a MT |
1168 | /*---------------------------------------------------------------------------*/ |
1169 | /* | |
1170 | * WASTE THIS FRAME | |
1171 | */ | |
1172 | /*---------------------------------------------------------------------------*/ | |
a90f3620 | 1173 | if (peasycap->skip) { |
c7506658 TW |
1174 | peasycap->skipped++; |
1175 | if (peasycap->skip != peasycap->skipped) | |
1176 | return peasycap->skip - peasycap->skipped; | |
a90f3620 TW |
1177 | else |
1178 | peasycap->skipped = 0; | |
c7506658 | 1179 | } |
40b8d50a | 1180 | /*---------------------------------------------------------------------------*/ |
c7506658 TW |
1181 | peasycap->frame_read = peasycap->frame_fill; |
1182 | peasycap->queued[peasycap->frame_read] = 0; | |
1183 | peasycap->done[peasycap->frame_read] = V4L2_BUF_FLAG_DONE; | |
702422bd | 1184 | |
c7506658 TW |
1185 | peasycap->frame_fill++; |
1186 | if (peasycap->frame_buffer_many <= peasycap->frame_fill) | |
1187 | peasycap->frame_fill = 0; | |
702422bd | 1188 | |
c7506658 TW |
1189 | if (0x01 & easycap_standard[peasycap->standard_offset].mask) |
1190 | peasycap->frame_buffer[peasycap->frame_read][0].kount = | |
702422bd | 1191 | V4L2_FIELD_TOP; |
c7506658 TW |
1192 | else |
1193 | peasycap->frame_buffer[peasycap->frame_read][0].kount = | |
702422bd | 1194 | V4L2_FIELD_BOTTOM; |
702422bd | 1195 | |
702422bd | 1196 | |
c7506658 TW |
1197 | JOM(8, "setting: %i=peasycap->frame_read\n", peasycap->frame_read); |
1198 | JOM(8, "bumped to: %i=peasycap->frame_fill\n", peasycap->frame_fill); | |
1199 | ||
1200 | return 0; | |
702422bd T |
1201 | } |
1202 | /*****************************************************************************/ | |
1203 | /*---------------------------------------------------------------------------*/ | |
1204 | /* | |
1205 | * BY DEFINITION, odd IS true FOR THE FIELD OCCUPYING LINES 1,3,5,...,479 | |
1206 | * odd IS false FOR THE FIELD OCCUPYING LINES 0,2,4,...,478 | |
1207 | * | |
1208 | * WHEN BOOLEAN PARAMETER decimatepixel IS true, ONLY THE FIELD FOR WHICH | |
1209 | * odd==false IS TRANSFERRED TO THE FRAME BUFFER. | |
1210 | * | |
1211 | * THE BOOLEAN PARAMETER offerfields IS true ONLY WHEN THE USER PROGRAM | |
40b8d50a | 1212 | * CHOOSES THE OPTION V4L2_FIELD_INTERLACED. |
702422bd T |
1213 | */ |
1214 | /*---------------------------------------------------------------------------*/ | |
1215 | int | |
1216 | field2frame(struct easycap *peasycap) | |
1217 | { | |
c7506658 TW |
1218 | struct timeval timeval; |
1219 | long long int above, below; | |
1220 | u32 remainder; | |
1221 | struct signed_div_result sdr; | |
1222 | ||
1223 | void *pex, *pad; | |
1224 | int kex, kad, mex, mad, rex, rad, rad2; | |
1225 | int c2, c3, w2, w3, cz, wz; | |
1226 | int rc, bytesperpixel, multiplier; | |
1227 | int much, more, over, rump, caches, input; | |
1228 | u8 mask, margin; | |
1229 | bool odd, isuy, decimatepixel, offerfields, badinput; | |
1230 | ||
6888393c | 1231 | if (!peasycap) { |
c7506658 TW |
1232 | SAY("ERROR: peasycap is NULL\n"); |
1233 | return -EFAULT; | |
1234 | } | |
e68703cf | 1235 | |
c7506658 TW |
1236 | badinput = false; |
1237 | input = 0x07 & peasycap->field_buffer[peasycap->field_read][0].input; | |
f36bc37a | 1238 | |
c7506658 TW |
1239 | JOM(8, "===== parity %i, input 0x%02X, field buffer %i --> " |
1240 | "frame buffer %i\n", | |
1dc6e418 TW |
1241 | peasycap->field_buffer[peasycap->field_read][0].kount, |
1242 | peasycap->field_buffer[peasycap->field_read][0].input, | |
702422bd | 1243 | peasycap->field_read, peasycap->frame_fill); |
c7506658 | 1244 | JOM(8, "===== %i=bytesperpixel\n", peasycap->bytesperpixel); |
27d683ab | 1245 | if (peasycap->offerfields) |
c7506658 | 1246 | JOM(8, "===== offerfields\n"); |
702422bd T |
1247 | |
1248 | /*---------------------------------------------------------------------------*/ | |
1249 | /* | |
1250 | * REJECT OR CLEAN BAD FIELDS | |
1251 | */ | |
1252 | /*---------------------------------------------------------------------------*/ | |
c7506658 TW |
1253 | if (peasycap->field_read == peasycap->field_fill) { |
1254 | SAM("ERROR: on entry, still filling field buffer %i\n", | |
1255 | peasycap->field_read); | |
1256 | return 0; | |
1257 | } | |
3fc0dae8 | 1258 | #ifdef EASYCAP_TESTCARD |
c7506658 | 1259 | easycap_testcard(peasycap, peasycap->field_read); |
702422bd | 1260 | #else |
c7506658 TW |
1261 | if (0 <= input && INPUT_MANY > input) { |
1262 | if (easycap_bars && VIDEO_LOST_TOLERATE <= peasycap->lost[input]) | |
1263 | easycap_testcard(peasycap, peasycap->field_read); | |
1264 | } | |
702422bd T |
1265 | #endif /*EASYCAP_TESTCARD*/ |
1266 | /*---------------------------------------------------------------------------*/ | |
1267 | ||
c7506658 TW |
1268 | offerfields = peasycap->offerfields; |
1269 | bytesperpixel = peasycap->bytesperpixel; | |
1270 | decimatepixel = peasycap->decimatepixel; | |
702422bd | 1271 | |
c7506658 TW |
1272 | if ((2 != bytesperpixel) && |
1273 | (3 != bytesperpixel) && | |
1274 | (4 != bytesperpixel)) { | |
1275 | SAM("MISTAKE: %i=bytesperpixel\n", bytesperpixel); | |
1276 | return -EFAULT; | |
1277 | } | |
27d683ab | 1278 | if (decimatepixel) |
c7506658 TW |
1279 | multiplier = 2; |
1280 | else | |
1281 | multiplier = 1; | |
702422bd | 1282 | |
c7506658 TW |
1283 | w2 = 2 * multiplier * (peasycap->width); |
1284 | w3 = bytesperpixel * multiplier * (peasycap->width); | |
1285 | wz = multiplier * (peasycap->height) * | |
1286 | multiplier * (peasycap->width); | |
1287 | ||
1288 | kex = peasycap->field_read; mex = 0; | |
1289 | kad = peasycap->frame_fill; mad = 0; | |
1290 | ||
1291 | pex = peasycap->field_buffer[kex][0].pgo; rex = PAGE_SIZE; | |
1292 | pad = peasycap->frame_buffer[kad][0].pgo; rad = PAGE_SIZE; | |
1293 | odd = !!(peasycap->field_buffer[kex][0].kount); | |
1294 | ||
febd32bc | 1295 | if (odd && (!decimatepixel)) { |
c7506658 TW |
1296 | JOM(8, "initial skipping %4i bytes p.%4i\n", |
1297 | w3/multiplier, mad); | |
1298 | pad += (w3 / multiplier); rad -= (w3 / multiplier); | |
1299 | } | |
1300 | isuy = true; | |
1301 | mask = 0; rump = 0; caches = 0; | |
1302 | ||
1303 | cz = 0; | |
1304 | while (cz < wz) { | |
1305 | /* | |
1306 | * PROCESS ONE LINE OF FRAME AT FULL RESOLUTION: | |
1307 | * READ w2 BYTES FROM FIELD BUFFER, | |
1308 | * WRITE w3 BYTES TO FRAME BUFFER | |
1309 | */ | |
febd32bc | 1310 | if (!decimatepixel) { |
c7506658 TW |
1311 | over = w2; |
1312 | do { | |
1313 | much = over; more = 0; | |
1314 | margin = 0; mask = 0x00; | |
1315 | if (rex < much) | |
1316 | much = rex; | |
1317 | rump = 0; | |
1318 | ||
1319 | if (much % 2) { | |
1320 | SAM("MISTAKE: much is odd\n"); | |
1321 | return -EFAULT; | |
1322 | } | |
702422bd | 1323 | |
c7506658 TW |
1324 | more = (bytesperpixel * |
1325 | much) / 2; | |
702422bd | 1326 | /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ |
c7506658 TW |
1327 | if (1 < bytesperpixel) { |
1328 | if (rad * 2 < much * bytesperpixel) { | |
1329 | /* | |
1330 | * INJUDICIOUS ALTERATION OF | |
1331 | * THIS STATEMENT BLOCK WILL | |
1332 | * CAUSE BREAKAGE. BEWARE. | |
1333 | */ | |
1334 | rad2 = rad + bytesperpixel - 1; | |
a90f3620 TW |
1335 | much = ((((2 * rad2)/bytesperpixel)/2) * 2); |
1336 | rump = ((bytesperpixel * much) / 2) - rad; | |
c7506658 | 1337 | more = rad; |
a90f3620 | 1338 | } |
c7506658 TW |
1339 | mask = (u8)rump; |
1340 | margin = 0; | |
1341 | if (much == rex) { | |
1342 | mask |= 0x04; | |
a90f3620 TW |
1343 | if ((mex + 1) < FIELD_BUFFER_SIZE / PAGE_SIZE) |
1344 | margin = *((u8 *)(peasycap->field_buffer[kex][mex + 1].pgo)); | |
1345 | else | |
c7506658 | 1346 | mask |= 0x08; |
702422bd | 1347 | } |
c7506658 TW |
1348 | } else { |
1349 | SAM("MISTAKE: %i=bytesperpixel\n", | |
1350 | bytesperpixel); | |
1351 | return -EFAULT; | |
702422bd | 1352 | } |
c7506658 TW |
1353 | if (rump) |
1354 | caches++; | |
27d683ab | 1355 | if (badinput) { |
c7506658 TW |
1356 | JOM(8, "ERROR: 0x%02X=->field_buffer" |
1357 | "[%i][%i].input, " | |
1358 | "0x%02X=(0x08|->input)\n", | |
1359 | peasycap->field_buffer | |
1360 | [kex][mex].input, kex, mex, | |
1361 | (0x08|peasycap->input)); | |
1362 | } | |
1363 | rc = redaub(peasycap, pad, pex, much, more, | |
1364 | mask, margin, isuy); | |
1365 | if (0 > rc) { | |
1366 | SAM("ERROR: redaub() failed\n"); | |
1367 | return -EFAULT; | |
f36bc37a | 1368 | } |
a90f3620 TW |
1369 | if (much % 4) |
1370 | isuy = !isuy; | |
1371 | ||
c7506658 TW |
1372 | over -= much; cz += much; |
1373 | pex += much; rex -= much; | |
1374 | if (!rex) { | |
1375 | mex++; | |
1376 | pex = peasycap->field_buffer[kex][mex].pgo; | |
1377 | rex = PAGE_SIZE; | |
a90f3620 | 1378 | if (peasycap->field_buffer[kex][mex].input != (0x08|peasycap->input)) |
c7506658 TW |
1379 | badinput = true; |
1380 | } | |
1381 | pad += more; | |
1382 | rad -= more; | |
1383 | if (!rad) { | |
1384 | mad++; | |
1385 | pad = peasycap->frame_buffer[kad][mad].pgo; | |
1386 | rad = PAGE_SIZE; | |
1387 | if (rump) { | |
1388 | pad += rump; | |
1389 | rad -= rump; | |
1390 | } | |
1391 | } | |
1392 | } while (over); | |
702422bd T |
1393 | /*---------------------------------------------------------------------------*/ |
1394 | /* | |
1395 | * SKIP w3 BYTES IN TARGET FRAME BUFFER, | |
1396 | * UNLESS IT IS THE LAST LINE OF AN ODD FRAME | |
1397 | */ | |
1398 | /*---------------------------------------------------------------------------*/ | |
febd32bc | 1399 | if (!odd || (cz != wz)) { |
c7506658 TW |
1400 | over = w3; |
1401 | do { | |
1402 | if (!rad) { | |
1403 | mad++; | |
1404 | pad = peasycap->frame_buffer | |
1405 | [kad][mad].pgo; | |
1406 | rad = PAGE_SIZE; | |
1407 | } | |
1408 | more = over; | |
1409 | if (rad < more) | |
1410 | more = rad; | |
1411 | over -= more; | |
1412 | pad += more; | |
1413 | rad -= more; | |
1414 | } while (over); | |
1415 | } | |
702422bd T |
1416 | /*---------------------------------------------------------------------------*/ |
1417 | /* | |
1418 | * PROCESS ONE LINE OF FRAME AT REDUCED RESOLUTION: | |
1419 | * ONLY IF false==odd, | |
1420 | * READ w2 BYTES FROM FIELD BUFFER, | |
1421 | * WRITE w3 / 2 BYTES TO FRAME BUFFER | |
1422 | */ | |
1423 | /*---------------------------------------------------------------------------*/ | |
febd32bc | 1424 | } else if (!odd) { |
c7506658 TW |
1425 | over = w2; |
1426 | do { | |
1427 | much = over; more = 0; margin = 0; mask = 0x00; | |
1428 | if (rex < much) | |
1429 | much = rex; | |
1430 | rump = 0; | |
702422bd | 1431 | |
c7506658 TW |
1432 | if (much % 2) { |
1433 | SAM("MISTAKE: much is odd\n"); | |
1434 | return -EFAULT; | |
1435 | } | |
702422bd | 1436 | |
c7506658 | 1437 | more = (bytesperpixel * much) / 4; |
702422bd | 1438 | /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ |
c7506658 TW |
1439 | if (1 < bytesperpixel) { |
1440 | if (rad * 4 < much * bytesperpixel) { | |
1441 | /* | |
1442 | * INJUDICIOUS ALTERATION OF | |
1443 | * THIS STATEMENT BLOCK | |
1444 | * WILL CAUSE BREAKAGE. | |
1445 | * BEWARE. | |
1446 | */ | |
1447 | rad2 = rad + bytesperpixel - 1; | |
a90f3620 TW |
1448 | much = ((((2 * rad2) / bytesperpixel) / 2) * 4); |
1449 | rump = ((bytesperpixel * much) / 4) - rad; | |
c7506658 | 1450 | more = rad; |
702422bd | 1451 | } |
c7506658 TW |
1452 | mask = (u8)rump; |
1453 | margin = 0; | |
1454 | if (much == rex) { | |
1455 | mask |= 0x04; | |
a90f3620 TW |
1456 | if ((mex + 1) < FIELD_BUFFER_SIZE / PAGE_SIZE) |
1457 | margin = *((u8 *)(peasycap->field_buffer[kex][mex + 1].pgo)); | |
1458 | else | |
c7506658 | 1459 | mask |= 0x08; |
702422bd T |
1460 | } |
1461 | /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | |
1462 | } else { | |
1dc6e418 | 1463 | SAM("MISTAKE: %i=bytesperpixel\n", |
702422bd T |
1464 | bytesperpixel); |
1465 | return -EFAULT; | |
1466 | } | |
1467 | /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | |
c7506658 TW |
1468 | if (rump) |
1469 | caches++; | |
1470 | ||
27d683ab | 1471 | if (badinput) { |
c7506658 TW |
1472 | JOM(8, "ERROR: 0x%02X=->field_buffer" |
1473 | "[%i][%i].input, " | |
1474 | "0x%02X=(0x08|->input)\n", | |
1475 | peasycap->field_buffer | |
1476 | [kex][mex].input, kex, mex, | |
1477 | (0x08|peasycap->input)); | |
1478 | } | |
1479 | rc = redaub(peasycap, pad, pex, much, more, | |
702422bd | 1480 | mask, margin, isuy); |
c7506658 TW |
1481 | if (0 > rc) { |
1482 | SAM("ERROR: redaub() failed\n"); | |
1483 | return -EFAULT; | |
702422bd | 1484 | } |
c7506658 TW |
1485 | over -= much; cz += much; |
1486 | pex += much; rex -= much; | |
1487 | if (!rex) { | |
1488 | mex++; | |
1489 | pex = peasycap->field_buffer[kex][mex].pgo; | |
1490 | rex = PAGE_SIZE; | |
1491 | if (peasycap->field_buffer[kex][mex].input != | |
1492 | (0x08|peasycap->input)) | |
1493 | badinput = true; | |
1494 | } | |
1495 | pad += more; | |
1496 | rad -= more; | |
1497 | if (!rad) { | |
1498 | mad++; | |
1499 | pad = peasycap->frame_buffer[kad][mad].pgo; | |
1500 | rad = PAGE_SIZE; | |
1501 | if (rump) { | |
1502 | pad += rump; | |
1503 | rad -= rump; | |
1504 | } | |
1505 | } | |
1506 | } while (over); | |
702422bd T |
1507 | /*---------------------------------------------------------------------------*/ |
1508 | /* | |
1509 | * OTHERWISE JUST | |
1510 | * READ w2 BYTES FROM FIELD BUFFER AND DISCARD THEM | |
1511 | */ | |
1512 | /*---------------------------------------------------------------------------*/ | |
c7506658 TW |
1513 | } else { |
1514 | over = w2; | |
1515 | do { | |
1516 | if (!rex) { | |
1517 | mex++; | |
1518 | pex = peasycap->field_buffer[kex][mex].pgo; | |
1519 | rex = PAGE_SIZE; | |
1520 | if (peasycap->field_buffer[kex][mex].input != | |
1521 | (0x08|peasycap->input)) { | |
1522 | JOM(8, "ERROR: 0x%02X=->field_buffer" | |
1523 | "[%i][%i].input, " | |
1524 | "0x%02X=(0x08|->input)\n", | |
1525 | peasycap->field_buffer | |
1526 | [kex][mex].input, kex, mex, | |
1527 | (0x08|peasycap->input)); | |
1528 | badinput = true; | |
1529 | } | |
f36bc37a | 1530 | } |
c7506658 TW |
1531 | much = over; |
1532 | if (rex < much) | |
1533 | much = rex; | |
1534 | over -= much; | |
1535 | cz += much; | |
1536 | pex += much; | |
1537 | rex -= much; | |
1538 | } while (over); | |
1539 | } | |
702422bd | 1540 | } |
702422bd T |
1541 | /*---------------------------------------------------------------------------*/ |
1542 | /* | |
1543 | * SANITY CHECKS | |
1544 | */ | |
1545 | /*---------------------------------------------------------------------------*/ | |
c7506658 TW |
1546 | c2 = (mex + 1)*PAGE_SIZE - rex; |
1547 | if (cz != c2) | |
1548 | SAM("ERROR: discrepancy %i in bytes read\n", c2 - cz); | |
1549 | c3 = (mad + 1)*PAGE_SIZE - rad; | |
1550 | ||
febd32bc | 1551 | if (!decimatepixel) { |
c7506658 | 1552 | if (bytesperpixel * cz != c3) |
1dc6e418 | 1553 | SAM("ERROR: discrepancy %i in bytes written\n", |
c7506658 TW |
1554 | c3 - (bytesperpixel * cz)); |
1555 | } else { | |
febd32bc | 1556 | if (!odd) { |
c7506658 TW |
1557 | if (bytesperpixel * |
1558 | cz != (4 * c3)) | |
1559 | SAM("ERROR: discrepancy %i in bytes written\n", | |
1560 | (2*c3)-(bytesperpixel * cz)); | |
1561 | } else { | |
1562 | if (0 != c3) | |
1563 | SAM("ERROR: discrepancy %i " | |
1564 | "in bytes written\n", c3); | |
1565 | } | |
1566 | } | |
1567 | if (rump) | |
1568 | SAM("WORRY: undischarged cache at end of line in frame buffer\n"); | |
702422bd | 1569 | |
c7506658 TW |
1570 | JOM(8, "===== field2frame(): %i bytes --> %i bytes (incl skip)\n", c2, c3); |
1571 | JOM(8, "===== field2frame(): %i=mad %i=rad\n", mad, rad); | |
702422bd | 1572 | |
27d683ab | 1573 | if (odd) |
c7506658 | 1574 | JOM(8, "+++++ field2frame(): frame buffer %i is full\n", kad); |
702422bd | 1575 | |
c7506658 TW |
1576 | if (peasycap->field_read == peasycap->field_fill) |
1577 | SAM("WARNING: on exit, filling field buffer %i\n", | |
1578 | peasycap->field_read); | |
702422bd T |
1579 | /*---------------------------------------------------------------------------*/ |
1580 | /* | |
1581 | * CALCULATE VIDEO STREAMING RATE | |
1582 | */ | |
1583 | /*---------------------------------------------------------------------------*/ | |
c7506658 TW |
1584 | do_gettimeofday(&timeval); |
1585 | if (peasycap->timeval6.tv_sec) { | |
1586 | below = ((long long int)(1000000)) * | |
1587 | ((long long int)(timeval.tv_sec - | |
1588 | peasycap->timeval6.tv_sec)) + | |
1589 | (long long int)(timeval.tv_usec - peasycap->timeval6.tv_usec); | |
1590 | above = (long long int)1000000; | |
702422bd | 1591 | |
c7506658 TW |
1592 | sdr = signed_div(above, below); |
1593 | above = sdr.quotient; | |
1594 | remainder = (u32)sdr.remainder; | |
702422bd | 1595 | |
c7506658 TW |
1596 | JOM(8, "video streaming at %3lli.%03i fields per second\n", |
1597 | above, (remainder/1000)); | |
1598 | } | |
1599 | peasycap->timeval6 = timeval; | |
702422bd | 1600 | |
c7506658 TW |
1601 | if (caches) |
1602 | JOM(8, "%i=caches\n", caches); | |
1603 | return 0; | |
702422bd T |
1604 | } |
1605 | /*****************************************************************************/ | |
1606 | struct signed_div_result | |
1607 | signed_div(long long int above, long long int below) | |
1608 | { | |
c7506658 TW |
1609 | struct signed_div_result sdr; |
1610 | ||
1611 | if (((0 <= above) && (0 <= below)) || ((0 > above) && (0 > below))) { | |
1612 | sdr.remainder = (unsigned long long int) do_div(above, below); | |
1613 | sdr.quotient = (long long int) above; | |
1614 | } else { | |
1615 | if (0 > above) | |
1616 | above = -above; | |
1617 | if (0 > below) | |
1618 | below = -below; | |
1619 | sdr.remainder = (unsigned long long int) do_div(above, below); | |
1620 | sdr.quotient = -((long long int) above); | |
1621 | } | |
1622 | return sdr; | |
702422bd T |
1623 | } |
1624 | /*****************************************************************************/ | |
1625 | /*---------------------------------------------------------------------------*/ | |
1626 | /* | |
1627 | * DECIMATION AND COLOURSPACE CONVERSION. | |
1628 | * | |
1629 | * THIS ROUTINE REQUIRES THAT ALL THE DATA TO BE READ RESIDES ON ONE PAGE | |
1630 | * AND THAT ALL THE DATA TO BE WRITTEN RESIDES ON ONE (DIFFERENT) PAGE. | |
1631 | * THE CALLING ROUTINE MUST ENSURE THAT THIS REQUIREMENT IS MET, AND MUST | |
1632 | * ALSO ENSURE THAT much IS EVEN. | |
1633 | * | |
1634 | * much BYTES ARE READ, AT LEAST (bytesperpixel * much)/2 BYTES ARE WRITTEN | |
1635 | * IF THERE IS NO DECIMATION, HALF THIS AMOUNT IF THERE IS DECIMATION. | |
1636 | * | |
1637 | * mask IS ZERO WHEN NO SPECIAL BEHAVIOUR REQUIRED. OTHERWISE IT IS SET THUS: | |
1638 | * 0x03 & mask = number of bytes to be written to cache instead of to | |
1639 | * frame buffer | |
1640 | * 0x04 & mask => use argument margin to set the chrominance for last pixel | |
1641 | * 0x08 & mask => do not set the chrominance for last pixel | |
1642 | * | |
1643 | * YUV to RGB CONVERSION IS (OR SHOULD BE) ITU-R BT 601. | |
1644 | * | |
1645 | * THERE IS A LOT OF CODE REPETITION IN THIS ROUTINE IN ORDER TO AVOID | |
1646 | * INEFFICIENT SWITCHING INSIDE INNER LOOPS. REARRANGING THE LOGIC TO | |
1647 | * REDUCE CODE LENGTH WILL GENERALLY IMPAIR RUNTIME PERFORMANCE. BEWARE. | |
1648 | */ | |
1649 | /*---------------------------------------------------------------------------*/ | |
1650 | int | |
1dc6e418 | 1651 | redaub(struct easycap *peasycap, void *pad, void *pex, int much, int more, |
055e3a3a | 1652 | u8 mask, u8 margin, bool isuy) |
702422bd | 1653 | { |
c7506658 TW |
1654 | static s32 ay[256], bu[256], rv[256], gu[256], gv[256]; |
1655 | u8 *pcache; | |
1656 | u8 r, g, b, y, u, v, c, *p2, *p3, *pz, *pr; | |
1657 | int bytesperpixel; | |
1658 | bool byteswaporder, decimatepixel, last; | |
1659 | int j, rump; | |
1660 | s32 tmp; | |
1661 | ||
1662 | if (much % 2) { | |
1663 | SAM("MISTAKE: much is odd\n"); | |
1664 | return -EFAULT; | |
1665 | } | |
1666 | bytesperpixel = peasycap->bytesperpixel; | |
1667 | byteswaporder = peasycap->byteswaporder; | |
1668 | decimatepixel = peasycap->decimatepixel; | |
1669 | ||
1670 | /*---------------------------------------------------------------------------*/ | |
1671 | if (!bu[255]) { | |
1672 | for (j = 0; j < 112; j++) { | |
1673 | tmp = (0xFF00 & (453 * j)) >> 8; | |
1674 | bu[j + 128] = tmp; bu[127 - j] = -tmp; | |
1675 | tmp = (0xFF00 & (359 * j)) >> 8; | |
1676 | rv[j + 128] = tmp; rv[127 - j] = -tmp; | |
1677 | tmp = (0xFF00 & (88 * j)) >> 8; | |
1678 | gu[j + 128] = tmp; gu[127 - j] = -tmp; | |
1679 | tmp = (0xFF00 & (183 * j)) >> 8; | |
1680 | gv[j + 128] = tmp; gv[127 - j] = -tmp; | |
1681 | } | |
1682 | for (j = 0; j < 16; j++) { | |
1683 | bu[j] = bu[16]; rv[j] = rv[16]; | |
1684 | gu[j] = gu[16]; gv[j] = gv[16]; | |
1685 | } | |
1686 | for (j = 240; j < 256; j++) { | |
1687 | bu[j] = bu[239]; rv[j] = rv[239]; | |
1688 | gu[j] = gu[239]; gv[j] = gv[239]; | |
1689 | } | |
1690 | for (j = 16; j < 236; j++) | |
1691 | ay[j] = j; | |
1692 | for (j = 0; j < 16; j++) | |
1693 | ay[j] = ay[16]; | |
1694 | for (j = 236; j < 256; j++) | |
1695 | ay[j] = ay[235]; | |
1696 | JOM(8, "lookup tables are prepared\n"); | |
1697 | } | |
1698 | pcache = peasycap->pcache; | |
6888393c | 1699 | if (!pcache) |
c7506658 | 1700 | pcache = &peasycap->cache[0]; |
702422bd T |
1701 | /*---------------------------------------------------------------------------*/ |
1702 | /* | |
1703 | * TRANSFER CONTENTS OF CACHE TO THE FRAME BUFFER | |
1704 | */ | |
1705 | /*---------------------------------------------------------------------------*/ | |
c7506658 TW |
1706 | if (!pcache) { |
1707 | SAM("MISTAKE: pcache is NULL\n"); | |
1708 | return -EFAULT; | |
1709 | } | |
702422bd | 1710 | |
c7506658 TW |
1711 | if (pcache != &peasycap->cache[0]) |
1712 | JOM(16, "cache has %i bytes\n", (int)(pcache - &peasycap->cache[0])); | |
1713 | p2 = &peasycap->cache[0]; | |
1714 | p3 = (u8 *)pad - (int)(pcache - &peasycap->cache[0]); | |
1715 | while (p2 < pcache) { | |
1716 | *p3++ = *p2; p2++; | |
1717 | } | |
1718 | pcache = &peasycap->cache[0]; | |
1719 | if (p3 != pad) { | |
1720 | SAM("MISTAKE: pointer misalignment\n"); | |
1721 | return -EFAULT; | |
1722 | } | |
702422bd | 1723 | /*---------------------------------------------------------------------------*/ |
c7506658 TW |
1724 | rump = (int)(0x03 & mask); |
1725 | u = 0; v = 0; | |
1726 | p2 = (u8 *)pex; pz = p2 + much; pr = p3 + more; last = false; | |
1727 | p2++; | |
702422bd | 1728 | |
27d683ab | 1729 | if (isuy) |
c7506658 TW |
1730 | u = *(p2 - 1); |
1731 | else | |
1732 | v = *(p2 - 1); | |
702422bd | 1733 | |
c7506658 TW |
1734 | if (rump) |
1735 | JOM(16, "%4i=much %4i=more %i=rump\n", much, more, rump); | |
702422bd T |
1736 | |
1737 | /*---------------------------------------------------------------------------*/ | |
c7506658 TW |
1738 | switch (bytesperpixel) { |
1739 | case 2: { | |
febd32bc | 1740 | if (!decimatepixel) { |
c7506658 | 1741 | memcpy(pad, pex, (size_t)much); |
febd32bc | 1742 | if (!byteswaporder) { |
c7506658 TW |
1743 | /* UYVY */ |
1744 | return 0; | |
1745 | } else { | |
1746 | /* YUYV */ | |
1747 | p3 = (u8 *)pad; pz = p3 + much; | |
1748 | while (pz > p3) { | |
1749 | c = *p3; | |
1750 | *p3 = *(p3 + 1); | |
1751 | *(p3 + 1) = c; | |
1752 | p3 += 2; | |
702422bd | 1753 | } |
c7506658 | 1754 | return 0; |
702422bd | 1755 | } |
702422bd | 1756 | } else { |
febd32bc | 1757 | if (!byteswaporder) { |
c7506658 TW |
1758 | /* UYVY DECIMATED */ |
1759 | p2 = (u8 *)pex; p3 = (u8 *)pad; pz = p2 + much; | |
1760 | while (pz > p2) { | |
1761 | *p3 = *p2; | |
1762 | *(p3 + 1) = *(p2 + 1); | |
1763 | *(p3 + 2) = *(p2 + 2); | |
1764 | *(p3 + 3) = *(p2 + 3); | |
1765 | p3 += 4; p2 += 8; | |
702422bd | 1766 | } |
c7506658 TW |
1767 | return 0; |
1768 | } else { | |
1769 | /* YUYV DECIMATED */ | |
1770 | p2 = (u8 *)pex; p3 = (u8 *)pad; pz = p2 + much; | |
1771 | while (pz > p2) { | |
1772 | *p3 = *(p2 + 1); | |
1773 | *(p3 + 1) = *p2; | |
1774 | *(p3 + 2) = *(p2 + 3); | |
1775 | *(p3 + 3) = *(p2 + 2); | |
1776 | p3 += 4; p2 += 8; | |
702422bd | 1777 | } |
c7506658 | 1778 | return 0; |
702422bd | 1779 | } |
c7506658 TW |
1780 | } |
1781 | break; | |
1782 | } | |
1783 | case 3: | |
1784 | { | |
febd32bc TW |
1785 | if (!decimatepixel) { |
1786 | if (!byteswaporder) { | |
c7506658 TW |
1787 | /* RGB */ |
1788 | while (pz > p2) { | |
1789 | if (pr <= (p3 + bytesperpixel)) | |
1790 | last = true; | |
1791 | else | |
1792 | last = false; | |
1793 | y = *p2; | |
27d683ab | 1794 | if (last && (0x0C & mask)) { |
c7506658 | 1795 | if (0x04 & mask) { |
27d683ab | 1796 | if (isuy) |
c7506658 TW |
1797 | v = margin; |
1798 | else | |
1799 | u = margin; | |
1800 | } else | |
1801 | if (0x08 & mask) | |
1802 | ; | |
1803 | } else { | |
27d683ab | 1804 | if (isuy) |
c7506658 | 1805 | v = *(p2 + 1); |
702422bd | 1806 | else |
c7506658 TW |
1807 | u = *(p2 + 1); |
1808 | } | |
702422bd | 1809 | |
27d17669 TW |
1810 | tmp = ay[(int)y] + rv[(int)v]; |
1811 | r = (255 < tmp) ? 255 : ((0 > tmp) ? | |
055e3a3a | 1812 | 0 : (u8)tmp); |
c7506658 | 1813 | tmp = ay[(int)y] - gu[(int)u] - gv[(int)v]; |
27d17669 | 1814 | g = (255 < tmp) ? 255 : ((0 > tmp) ? |
055e3a3a | 1815 | 0 : (u8)tmp); |
27d17669 TW |
1816 | tmp = ay[(int)y] + bu[(int)u]; |
1817 | b = (255 < tmp) ? 255 : ((0 > tmp) ? | |
055e3a3a | 1818 | 0 : (u8)tmp); |
702422bd | 1819 | |
27d683ab | 1820 | if (last && rump) { |
e68703cf | 1821 | pcache = &peasycap->cache[0]; |
702422bd T |
1822 | switch (bytesperpixel - rump) { |
1823 | case 1: { | |
1824 | *p3 = r; | |
1825 | *pcache++ = g; | |
1826 | *pcache++ = b; | |
1827 | break; | |
1828 | } | |
1829 | case 2: { | |
1830 | *p3 = r; | |
1831 | *(p3 + 1) = g; | |
1832 | *pcache++ = b; | |
1833 | break; | |
1834 | } | |
1835 | default: { | |
c7506658 TW |
1836 | SAM("MISTAKE: %i=rump\n", |
1837 | bytesperpixel - rump); | |
702422bd T |
1838 | return -EFAULT; |
1839 | } | |
1840 | } | |
1841 | } else { | |
1842 | *p3 = r; | |
1843 | *(p3 + 1) = g; | |
1844 | *(p3 + 2) = b; | |
1845 | } | |
c7506658 | 1846 | p2 += 2; |
27d683ab | 1847 | if (isuy) |
c7506658 TW |
1848 | isuy = false; |
1849 | else | |
1850 | isuy = true; | |
702422bd | 1851 | p3 += bytesperpixel; |
702422bd | 1852 | } |
c7506658 TW |
1853 | return 0; |
1854 | } else { | |
1855 | /* BGR */ | |
1856 | while (pz > p2) { | |
1857 | if (pr <= (p3 + bytesperpixel)) | |
1858 | last = true; | |
1859 | else | |
1860 | last = false; | |
1861 | y = *p2; | |
27d683ab | 1862 | if (last && (0x0C & mask)) { |
c7506658 | 1863 | if (0x04 & mask) { |
27d683ab | 1864 | if (isuy) |
c7506658 TW |
1865 | v = margin; |
1866 | else | |
1867 | u = margin; | |
1868 | } | |
1869 | else | |
702422bd T |
1870 | if (0x08 & mask) |
1871 | ; | |
c7506658 | 1872 | } else { |
27d683ab | 1873 | if (isuy) |
c7506658 TW |
1874 | v = *(p2 + 1); |
1875 | else | |
1876 | u = *(p2 + 1); | |
1877 | } | |
702422bd | 1878 | |
27d17669 TW |
1879 | tmp = ay[(int)y] + rv[(int)v]; |
1880 | r = (255 < tmp) ? 255 : ((0 > tmp) ? | |
c7506658 TW |
1881 | 0 : (u8)tmp); |
1882 | tmp = ay[(int)y] - gu[(int)u] - gv[(int)v]; | |
27d17669 | 1883 | g = (255 < tmp) ? 255 : ((0 > tmp) ? |
c7506658 | 1884 | 0 : (u8)tmp); |
27d17669 TW |
1885 | tmp = ay[(int)y] + bu[(int)u]; |
1886 | b = (255 < tmp) ? 255 : ((0 > tmp) ? | |
c7506658 | 1887 | 0 : (u8)tmp); |
702422bd | 1888 | |
27d683ab | 1889 | if (last && rump) { |
e68703cf | 1890 | pcache = &peasycap->cache[0]; |
702422bd T |
1891 | switch (bytesperpixel - rump) { |
1892 | case 1: { | |
1893 | *p3 = b; | |
1894 | *pcache++ = g; | |
1895 | *pcache++ = r; | |
1896 | break; | |
1897 | } | |
1898 | case 2: { | |
1899 | *p3 = b; | |
1900 | *(p3 + 1) = g; | |
1901 | *pcache++ = r; | |
1902 | break; | |
1903 | } | |
1904 | default: { | |
c7506658 TW |
1905 | SAM("MISTAKE: %i=rump\n", |
1906 | bytesperpixel - rump); | |
702422bd T |
1907 | return -EFAULT; |
1908 | } | |
1909 | } | |
1910 | } else { | |
1911 | *p3 = b; | |
1912 | *(p3 + 1) = g; | |
1913 | *(p3 + 2) = r; | |
1914 | } | |
c7506658 | 1915 | p2 += 2; |
27d683ab | 1916 | if (isuy) |
c7506658 TW |
1917 | isuy = false; |
1918 | else | |
1919 | isuy = true; | |
702422bd T |
1920 | p3 += bytesperpixel; |
1921 | } | |
702422bd T |
1922 | } |
1923 | return 0; | |
c7506658 | 1924 | } else { |
febd32bc | 1925 | if (!byteswaporder) { |
c7506658 TW |
1926 | /* RGB DECIMATED */ |
1927 | while (pz > p2) { | |
1928 | if (pr <= (p3 + bytesperpixel)) | |
1929 | last = true; | |
1930 | else | |
1931 | last = false; | |
1932 | y = *p2; | |
27d683ab | 1933 | if (last && (0x0C & mask)) { |
c7506658 | 1934 | if (0x04 & mask) { |
27d683ab | 1935 | if (isuy) |
c7506658 TW |
1936 | v = margin; |
1937 | else | |
1938 | u = margin; | |
1939 | } else | |
1940 | if (0x08 & mask) | |
1941 | ; | |
1942 | } else { | |
27d683ab | 1943 | if (isuy) |
c7506658 | 1944 | v = *(p2 + 1); |
702422bd | 1945 | else |
c7506658 | 1946 | u = *(p2 + 1); |
702422bd | 1947 | } |
c7506658 | 1948 | |
27d683ab | 1949 | if (isuy) { |
c7506658 TW |
1950 | tmp = ay[(int)y] + rv[(int)v]; |
1951 | r = (255 < tmp) ? 255 : ((0 > tmp) ? | |
1952 | 0 : (u8)tmp); | |
1953 | tmp = ay[(int)y] - gu[(int)u] - | |
1954 | gv[(int)v]; | |
1955 | g = (255 < tmp) ? 255 : ((0 > tmp) ? | |
1956 | 0 : (u8)tmp); | |
1957 | tmp = ay[(int)y] + bu[(int)u]; | |
1958 | b = (255 < tmp) ? 255 : ((0 > tmp) ? | |
1959 | 0 : (u8)tmp); | |
1960 | ||
27d683ab | 1961 | if (last && rump) { |
c7506658 TW |
1962 | pcache = &peasycap->cache[0]; |
1963 | switch (bytesperpixel - rump) { | |
1964 | case 1: { | |
1965 | *p3 = r; | |
1966 | *pcache++ = g; | |
1967 | *pcache++ = b; | |
1968 | break; | |
1969 | } | |
1970 | case 2: { | |
1971 | *p3 = r; | |
1972 | *(p3 + 1) = g; | |
1973 | *pcache++ = b; | |
1974 | break; | |
1975 | } | |
1976 | default: { | |
1977 | SAM("MISTAKE: " | |
1978 | "%i=rump\n", | |
1979 | bytesperpixel - rump); | |
1980 | return -EFAULT; | |
1981 | } | |
1982 | } | |
1983 | } else { | |
1984 | *p3 = r; | |
1985 | *(p3 + 1) = g; | |
1986 | *(p3 + 2) = b; | |
1987 | } | |
1988 | isuy = false; | |
1989 | p3 += bytesperpixel; | |
1990 | } else { | |
1991 | isuy = true; | |
702422bd | 1992 | } |
c7506658 | 1993 | p2 += 2; |
702422bd | 1994 | } |
c7506658 TW |
1995 | return 0; |
1996 | } else { | |
1997 | /* BGR DECIMATED */ | |
1998 | while (pz > p2) { | |
1999 | if (pr <= (p3 + bytesperpixel)) | |
2000 | last = true; | |
2001 | else | |
2002 | last = false; | |
2003 | y = *p2; | |
27d683ab | 2004 | if (last && (0x0C & mask)) { |
c7506658 | 2005 | if (0x04 & mask) { |
27d683ab | 2006 | if (isuy) |
c7506658 TW |
2007 | v = margin; |
2008 | else | |
2009 | u = margin; | |
2010 | } else | |
2011 | if (0x08 & mask) | |
2012 | ; | |
2013 | } else { | |
27d683ab | 2014 | if (isuy) |
c7506658 | 2015 | v = *(p2 + 1); |
702422bd | 2016 | else |
c7506658 | 2017 | u = *(p2 + 1); |
702422bd | 2018 | } |
c7506658 | 2019 | |
27d683ab | 2020 | if (isuy) { |
c7506658 TW |
2021 | |
2022 | tmp = ay[(int)y] + rv[(int)v]; | |
2023 | r = (255 < tmp) ? 255 : ((0 > tmp) ? | |
2024 | 0 : (u8)tmp); | |
2025 | tmp = ay[(int)y] - gu[(int)u] - | |
2026 | gv[(int)v]; | |
2027 | g = (255 < tmp) ? 255 : ((0 > tmp) ? | |
2028 | 0 : (u8)tmp); | |
2029 | tmp = ay[(int)y] + bu[(int)u]; | |
2030 | b = (255 < tmp) ? 255 : ((0 > tmp) ? | |
2031 | 0 : (u8)tmp); | |
2032 | ||
27d683ab | 2033 | if (last && rump) { |
c7506658 TW |
2034 | pcache = &peasycap->cache[0]; |
2035 | switch (bytesperpixel - rump) { | |
2036 | case 1: { | |
2037 | *p3 = b; | |
2038 | *pcache++ = g; | |
2039 | *pcache++ = r; | |
2040 | break; | |
2041 | } | |
2042 | case 2: { | |
2043 | *p3 = b; | |
2044 | *(p3 + 1) = g; | |
2045 | *pcache++ = r; | |
2046 | break; | |
2047 | } | |
2048 | default: { | |
2049 | SAM("MISTAKE: " | |
2050 | "%i=rump\n", | |
2051 | bytesperpixel - rump); | |
2052 | return -EFAULT; | |
2053 | } | |
2054 | } | |
2055 | } else { | |
2056 | *p3 = b; | |
2057 | *(p3 + 1) = g; | |
2058 | *(p3 + 2) = r; | |
2059 | } | |
2060 | isuy = false; | |
2061 | p3 += bytesperpixel; | |
2062 | } | |
2063 | else | |
2064 | isuy = true; | |
2065 | p2 += 2; | |
702422bd | 2066 | } |
c7506658 | 2067 | return 0; |
702422bd | 2068 | } |
702422bd | 2069 | } |
c7506658 | 2070 | break; |
702422bd | 2071 | } |
c7506658 TW |
2072 | case 4: |
2073 | { | |
febd32bc TW |
2074 | if (!decimatepixel) { |
2075 | if (!byteswaporder) { | |
c7506658 TW |
2076 | /* RGBA */ |
2077 | while (pz > p2) { | |
2078 | if (pr <= (p3 + bytesperpixel)) | |
2079 | last = true; | |
2080 | else | |
2081 | last = false; | |
2082 | y = *p2; | |
27d683ab | 2083 | if (last && (0x0C & mask)) { |
c7506658 | 2084 | if (0x04 & mask) { |
27d683ab | 2085 | if (isuy) |
c7506658 TW |
2086 | v = margin; |
2087 | else | |
2088 | u = margin; | |
2089 | } else | |
2090 | if (0x08 & mask) | |
2091 | ; | |
2092 | } else { | |
27d683ab | 2093 | if (isuy) |
c7506658 | 2094 | v = *(p2 + 1); |
702422bd | 2095 | else |
c7506658 TW |
2096 | u = *(p2 + 1); |
2097 | } | |
702422bd | 2098 | |
27d17669 TW |
2099 | tmp = ay[(int)y] + rv[(int)v]; |
2100 | r = (255 < tmp) ? 255 : ((0 > tmp) ? | |
c7506658 TW |
2101 | 0 : (u8)tmp); |
2102 | tmp = ay[(int)y] - gu[(int)u] - gv[(int)v]; | |
27d17669 | 2103 | g = (255 < tmp) ? 255 : ((0 > tmp) ? |
c7506658 | 2104 | 0 : (u8)tmp); |
27d17669 TW |
2105 | tmp = ay[(int)y] + bu[(int)u]; |
2106 | b = (255 < tmp) ? 255 : ((0 > tmp) ? | |
c7506658 | 2107 | 0 : (u8)tmp); |
702422bd | 2108 | |
27d683ab | 2109 | if (last && rump) { |
e68703cf | 2110 | pcache = &peasycap->cache[0]; |
702422bd T |
2111 | switch (bytesperpixel - rump) { |
2112 | case 1: { | |
2113 | *p3 = r; | |
2114 | *pcache++ = g; | |
2115 | *pcache++ = b; | |
2116 | *pcache++ = 0; | |
2117 | break; | |
2118 | } | |
2119 | case 2: { | |
2120 | *p3 = r; | |
2121 | *(p3 + 1) = g; | |
2122 | *pcache++ = b; | |
2123 | *pcache++ = 0; | |
2124 | break; | |
2125 | } | |
2126 | case 3: { | |
2127 | *p3 = r; | |
2128 | *(p3 + 1) = g; | |
2129 | *(p3 + 2) = b; | |
2130 | *pcache++ = 0; | |
2131 | break; | |
2132 | } | |
2133 | default: { | |
c7506658 TW |
2134 | SAM("MISTAKE: %i=rump\n", |
2135 | bytesperpixel - rump); | |
702422bd | 2136 | return -EFAULT; |
c7506658 | 2137 | } |
702422bd T |
2138 | } |
2139 | } else { | |
2140 | *p3 = r; | |
2141 | *(p3 + 1) = g; | |
2142 | *(p3 + 2) = b; | |
2143 | *(p3 + 3) = 0; | |
c7506658 TW |
2144 | } |
2145 | p2 += 2; | |
27d683ab | 2146 | if (isuy) |
c7506658 | 2147 | isuy = false; |
702422bd | 2148 | else |
c7506658 TW |
2149 | isuy = true; |
2150 | p3 += bytesperpixel; | |
702422bd | 2151 | } |
c7506658 TW |
2152 | return 0; |
2153 | } else { | |
2154 | /* | |
2155 | * BGRA | |
2156 | */ | |
2157 | while (pz > p2) { | |
2158 | if (pr <= (p3 + bytesperpixel)) | |
2159 | last = true; | |
2160 | else | |
2161 | last = false; | |
2162 | y = *p2; | |
27d683ab | 2163 | if (last && (0x0C & mask)) { |
c7506658 | 2164 | if (0x04 & mask) { |
27d683ab | 2165 | if (isuy) |
c7506658 TW |
2166 | v = margin; |
2167 | else | |
2168 | u = margin; | |
2169 | } else | |
2170 | if (0x08 & mask) | |
2171 | ; | |
2172 | } else { | |
27d683ab | 2173 | if (isuy) |
c7506658 TW |
2174 | v = *(p2 + 1); |
2175 | else | |
2176 | u = *(p2 + 1); | |
2177 | } | |
702422bd | 2178 | |
27d17669 TW |
2179 | tmp = ay[(int)y] + rv[(int)v]; |
2180 | r = (255 < tmp) ? 255 : ((0 > tmp) ? | |
c7506658 TW |
2181 | 0 : (u8)tmp); |
2182 | tmp = ay[(int)y] - gu[(int)u] - gv[(int)v]; | |
27d17669 | 2183 | g = (255 < tmp) ? 255 : ((0 > tmp) ? |
c7506658 | 2184 | 0 : (u8)tmp); |
27d17669 TW |
2185 | tmp = ay[(int)y] + bu[(int)u]; |
2186 | b = (255 < tmp) ? 255 : ((0 > tmp) ? | |
c7506658 | 2187 | 0 : (u8)tmp); |
702422bd | 2188 | |
27d683ab | 2189 | if (last && rump) { |
e68703cf | 2190 | pcache = &peasycap->cache[0]; |
702422bd T |
2191 | switch (bytesperpixel - rump) { |
2192 | case 1: { | |
2193 | *p3 = b; | |
2194 | *pcache++ = g; | |
2195 | *pcache++ = r; | |
2196 | *pcache++ = 0; | |
2197 | break; | |
2198 | } | |
2199 | case 2: { | |
2200 | *p3 = b; | |
2201 | *(p3 + 1) = g; | |
2202 | *pcache++ = r; | |
2203 | *pcache++ = 0; | |
2204 | break; | |
2205 | } | |
2206 | case 3: { | |
2207 | *p3 = b; | |
2208 | *(p3 + 1) = g; | |
2209 | *(p3 + 2) = r; | |
2210 | *pcache++ = 0; | |
2211 | break; | |
2212 | } | |
c7506658 TW |
2213 | default: |
2214 | SAM("MISTAKE: %i=rump\n", | |
2215 | bytesperpixel - rump); | |
702422bd T |
2216 | return -EFAULT; |
2217 | } | |
702422bd T |
2218 | } else { |
2219 | *p3 = b; | |
2220 | *(p3 + 1) = g; | |
2221 | *(p3 + 2) = r; | |
2222 | *(p3 + 3) = 0; | |
2223 | } | |
c7506658 | 2224 | p2 += 2; |
27d683ab | 2225 | if (isuy) |
c7506658 TW |
2226 | isuy = false; |
2227 | else | |
2228 | isuy = true; | |
702422bd | 2229 | p3 += bytesperpixel; |
c7506658 TW |
2230 | } |
2231 | } | |
2232 | return 0; | |
2233 | } else { | |
febd32bc | 2234 | if (!byteswaporder) { |
c7506658 TW |
2235 | /* |
2236 | * RGBA DECIMATED | |
2237 | */ | |
2238 | while (pz > p2) { | |
2239 | if (pr <= (p3 + bytesperpixel)) | |
2240 | last = true; | |
2241 | else | |
2242 | last = false; | |
2243 | y = *p2; | |
27d683ab | 2244 | if (last && (0x0C & mask)) { |
c7506658 | 2245 | if (0x04 & mask) { |
27d683ab | 2246 | if (isuy) |
c7506658 TW |
2247 | v = margin; |
2248 | else | |
2249 | u = margin; | |
2250 | } else | |
2251 | if (0x08 & mask) | |
2252 | ; | |
2253 | } else { | |
27d683ab | 2254 | if (isuy) |
c7506658 TW |
2255 | v = *(p2 + 1); |
2256 | else | |
2257 | u = *(p2 + 1); | |
2258 | } | |
2259 | ||
27d683ab | 2260 | if (isuy) { |
c7506658 TW |
2261 | |
2262 | tmp = ay[(int)y] + rv[(int)v]; | |
2263 | r = (255 < tmp) ? 255 : ((0 > tmp) ? | |
2264 | 0 : (u8)tmp); | |
2265 | tmp = ay[(int)y] - gu[(int)u] - | |
2266 | gv[(int)v]; | |
2267 | g = (255 < tmp) ? 255 : ((0 > tmp) ? | |
2268 | 0 : (u8)tmp); | |
2269 | tmp = ay[(int)y] + bu[(int)u]; | |
2270 | b = (255 < tmp) ? 255 : ((0 > tmp) ? | |
2271 | 0 : (u8)tmp); | |
2272 | ||
27d683ab | 2273 | if (last && rump) { |
c7506658 TW |
2274 | pcache = &peasycap->cache[0]; |
2275 | switch (bytesperpixel - rump) { | |
2276 | case 1: { | |
2277 | *p3 = r; | |
2278 | *pcache++ = g; | |
2279 | *pcache++ = b; | |
2280 | *pcache++ = 0; | |
2281 | break; | |
2282 | } | |
2283 | case 2: { | |
2284 | *p3 = r; | |
2285 | *(p3 + 1) = g; | |
2286 | *pcache++ = b; | |
2287 | *pcache++ = 0; | |
2288 | break; | |
2289 | } | |
2290 | case 3: { | |
2291 | *p3 = r; | |
2292 | *(p3 + 1) = g; | |
2293 | *(p3 + 2) = b; | |
2294 | *pcache++ = 0; | |
2295 | break; | |
2296 | } | |
2297 | default: { | |
2298 | SAM("MISTAKE: " | |
2299 | "%i=rump\n", | |
2300 | bytesperpixel - | |
2301 | rump); | |
2302 | return -EFAULT; | |
2303 | } | |
2304 | } | |
2305 | } else { | |
2306 | *p3 = r; | |
2307 | *(p3 + 1) = g; | |
2308 | *(p3 + 2) = b; | |
2309 | *(p3 + 3) = 0; | |
2310 | } | |
2311 | isuy = false; | |
2312 | p3 += bytesperpixel; | |
2313 | } else | |
2314 | isuy = true; | |
702422bd T |
2315 | p2 += 2; |
2316 | } | |
c7506658 TW |
2317 | return 0; |
2318 | } else { | |
2319 | /* | |
2320 | * BGRA DECIMATED | |
2321 | */ | |
2322 | while (pz > p2) { | |
2323 | if (pr <= (p3 + bytesperpixel)) | |
2324 | last = true; | |
2325 | else | |
2326 | last = false; | |
2327 | y = *p2; | |
27d683ab | 2328 | if (last && (0x0C & mask)) { |
c7506658 | 2329 | if (0x04 & mask) { |
27d683ab | 2330 | if (isuy) |
c7506658 TW |
2331 | v = margin; |
2332 | else | |
2333 | u = margin; | |
2334 | } else | |
2335 | if (0x08 & mask) | |
2336 | ; | |
2337 | } else { | |
27d683ab | 2338 | if (isuy) |
c7506658 TW |
2339 | v = *(p2 + 1); |
2340 | else | |
2341 | u = *(p2 + 1); | |
2342 | } | |
2343 | ||
27d683ab | 2344 | if (isuy) { |
c7506658 TW |
2345 | tmp = ay[(int)y] + rv[(int)v]; |
2346 | r = (255 < tmp) ? 255 : ((0 > tmp) ? | |
2347 | 0 : (u8)tmp); | |
2348 | tmp = ay[(int)y] - gu[(int)u] - | |
2349 | gv[(int)v]; | |
2350 | g = (255 < tmp) ? 255 : ((0 > tmp) ? | |
2351 | 0 : (u8)tmp); | |
2352 | tmp = ay[(int)y] + bu[(int)u]; | |
2353 | b = (255 < tmp) ? 255 : ((0 > tmp) ? | |
2354 | 0 : (u8)tmp); | |
2355 | ||
27d683ab | 2356 | if (last && rump) { |
c7506658 TW |
2357 | pcache = &peasycap->cache[0]; |
2358 | switch (bytesperpixel - rump) { | |
2359 | case 1: { | |
2360 | *p3 = b; | |
2361 | *pcache++ = g; | |
2362 | *pcache++ = r; | |
2363 | *pcache++ = 0; | |
2364 | break; | |
2365 | } | |
2366 | case 2: { | |
2367 | *p3 = b; | |
2368 | *(p3 + 1) = g; | |
2369 | *pcache++ = r; | |
2370 | *pcache++ = 0; | |
2371 | break; | |
2372 | } | |
2373 | case 3: { | |
2374 | *p3 = b; | |
2375 | *(p3 + 1) = g; | |
2376 | *(p3 + 2) = r; | |
2377 | *pcache++ = 0; | |
2378 | break; | |
2379 | } | |
2380 | default: { | |
2381 | SAM("MISTAKE: " | |
2382 | "%i=rump\n", | |
2383 | bytesperpixel - rump); | |
2384 | return -EFAULT; | |
2385 | } | |
2386 | } | |
2387 | } else { | |
2388 | *p3 = b; | |
2389 | *(p3 + 1) = g; | |
2390 | *(p3 + 2) = r; | |
2391 | *(p3 + 3) = 0; | |
2392 | } | |
2393 | isuy = false; | |
2394 | p3 += bytesperpixel; | |
2395 | } else | |
2396 | isuy = true; | |
2397 | p2 += 2; | |
2398 | } | |
2399 | return 0; | |
2400 | } | |
702422bd | 2401 | } |
c7506658 TW |
2402 | break; |
2403 | } | |
2404 | default: { | |
2405 | SAM("MISTAKE: %i=bytesperpixel\n", bytesperpixel); | |
2406 | return -EFAULT; | |
702422bd | 2407 | } |
702422bd | 2408 | } |
c7506658 | 2409 | return 0; |
702422bd T |
2410 | } |
2411 | /*****************************************************************************/ | |
702422bd T |
2412 | /* |
2413 | * SEE CORBET ET AL. "LINUX DEVICE DRIVERS", 3rd EDITION, PAGES 430-434 | |
2414 | */ | |
702422bd | 2415 | /*****************************************************************************/ |
d090bf57 | 2416 | static void easycap_vma_open(struct vm_area_struct *pvma) |
702422bd | 2417 | { |
c7506658 | 2418 | struct easycap *peasycap; |
702422bd | 2419 | |
c7506658 | 2420 | peasycap = pvma->vm_private_data; |
6888393c | 2421 | if (!peasycap) { |
c7506658 TW |
2422 | SAY("ERROR: peasycap is NULL\n"); |
2423 | return; | |
2424 | } | |
c7506658 TW |
2425 | peasycap->vma_many++; |
2426 | JOT(8, "%i=peasycap->vma_many\n", peasycap->vma_many); | |
268dfede MT |
2427 | return; |
2428 | } | |
702422bd | 2429 | /*****************************************************************************/ |
d090bf57 | 2430 | static void easycap_vma_close(struct vm_area_struct *pvma) |
702422bd | 2431 | { |
c7506658 | 2432 | struct easycap *peasycap; |
702422bd | 2433 | |
c7506658 | 2434 | peasycap = pvma->vm_private_data; |
6888393c | 2435 | if (!peasycap) { |
c7506658 TW |
2436 | SAY("ERROR: peasycap is NULL\n"); |
2437 | return; | |
2438 | } | |
c7506658 TW |
2439 | peasycap->vma_many--; |
2440 | JOT(8, "%i=peasycap->vma_many\n", peasycap->vma_many); | |
268dfede | 2441 | return; |
702422bd | 2442 | } |
702422bd | 2443 | /*****************************************************************************/ |
d090bf57 | 2444 | static int easycap_vma_fault(struct vm_area_struct *pvma, struct vm_fault *pvmf) |
702422bd | 2445 | { |
c7506658 TW |
2446 | int k, m, retcode; |
2447 | void *pbuf; | |
2448 | struct page *page; | |
2449 | struct easycap *peasycap; | |
702422bd | 2450 | |
c7506658 | 2451 | retcode = VM_FAULT_NOPAGE; |
702422bd | 2452 | |
6888393c | 2453 | if (!pvma) { |
c7506658 TW |
2454 | SAY("pvma is NULL\n"); |
2455 | return retcode; | |
2456 | } | |
6888393c | 2457 | if (!pvmf) { |
c7506658 TW |
2458 | SAY("pvmf is NULL\n"); |
2459 | return retcode; | |
2460 | } | |
702422bd | 2461 | |
c7506658 TW |
2462 | k = (pvmf->pgoff) / (FRAME_BUFFER_SIZE/PAGE_SIZE); |
2463 | m = (pvmf->pgoff) % (FRAME_BUFFER_SIZE/PAGE_SIZE); | |
702422bd | 2464 | |
c7506658 TW |
2465 | if (!m) |
2466 | JOT(4, "%4i=k, %4i=m\n", k, m); | |
2467 | else | |
2468 | JOT(16, "%4i=k, %4i=m\n", k, m); | |
702422bd | 2469 | |
c7506658 TW |
2470 | if ((0 > k) || (FRAME_BUFFER_MANY <= k)) { |
2471 | SAY("ERROR: buffer index %i out of range\n", k); | |
2472 | return retcode; | |
2473 | } | |
2474 | if ((0 > m) || (FRAME_BUFFER_SIZE/PAGE_SIZE <= m)) { | |
2475 | SAY("ERROR: page number %i out of range\n", m); | |
2476 | return retcode; | |
2477 | } | |
2478 | peasycap = pvma->vm_private_data; | |
6888393c | 2479 | if (!peasycap) { |
c7506658 TW |
2480 | SAY("ERROR: peasycap is NULL\n"); |
2481 | return retcode; | |
2482 | } | |
702422bd | 2483 | /*---------------------------------------------------------------------------*/ |
c7506658 | 2484 | pbuf = peasycap->frame_buffer[k][m].pgo; |
6888393c | 2485 | if (!pbuf) { |
c7506658 TW |
2486 | SAM("ERROR: pbuf is NULL\n"); |
2487 | return retcode; | |
2488 | } | |
2489 | page = virt_to_page(pbuf); | |
6888393c | 2490 | if (!page) { |
c7506658 TW |
2491 | SAM("ERROR: page is NULL\n"); |
2492 | return retcode; | |
2493 | } | |
2494 | get_page(page); | |
702422bd | 2495 | /*---------------------------------------------------------------------------*/ |
6888393c | 2496 | if (!page) { |
c7506658 TW |
2497 | SAM("ERROR: page is NULL after get_page(page)\n"); |
2498 | } else { | |
2499 | pvmf->page = page; | |
2500 | retcode = VM_FAULT_MINOR; | |
2501 | } | |
2502 | return retcode; | |
702422bd | 2503 | } |
d090bf57 TW |
2504 | |
2505 | static const struct vm_operations_struct easycap_vm_ops = { | |
2506 | .open = easycap_vma_open, | |
2507 | .close = easycap_vma_close, | |
2508 | .fault = easycap_vma_fault, | |
2509 | }; | |
2510 | ||
2511 | static int easycap_mmap(struct file *file, struct vm_area_struct *pvma) | |
2512 | { | |
2513 | JOT(8, "\n"); | |
2514 | ||
2515 | pvma->vm_ops = &easycap_vm_ops; | |
2516 | pvma->vm_flags |= VM_RESERVED; | |
6888393c | 2517 | if (file) |
d090bf57 TW |
2518 | pvma->vm_private_data = file->private_data; |
2519 | easycap_vma_open(pvma); | |
2520 | return 0; | |
2521 | } | |
702422bd T |
2522 | /*****************************************************************************/ |
2523 | /*---------------------------------------------------------------------------*/ | |
2524 | /* | |
2525 | * ON COMPLETION OF A VIDEO URB ITS DATA IS COPIED TO THE FIELD BUFFERS | |
ce36ceda | 2526 | * PROVIDED peasycap->video_idle IS ZERO. REGARDLESS OF THIS BEING TRUE, |
702422bd T |
2527 | * IT IS RESUBMITTED PROVIDED peasycap->video_isoc_streaming IS NOT ZERO. |
2528 | * | |
2529 | * THIS FUNCTION IS AN INTERRUPT SERVICE ROUTINE AND MUST NOT SLEEP. | |
2530 | * | |
2531 | * INFORMATION ABOUT THE VALIDITY OF THE CONTENTS OF THE FIELD BUFFER ARE | |
2532 | * STORED IN THE TWO-BYTE STATUS PARAMETER | |
2533 | * peasycap->field_buffer[peasycap->field_fill][0].kount | |
2534 | * NOTICE THAT THE INFORMATION IS STORED ONLY WITH PAGE 0 OF THE FIELD BUFFER. | |
2535 | * | |
2536 | * THE LOWER BYTE CONTAINS THE FIELD PARITY BYTE FURNISHED BY THE SAA7113H | |
2537 | * CHIP. | |
2538 | * | |
2539 | * THE UPPER BYTE IS ZERO IF NO PROBLEMS, OTHERWISE: | |
2540 | * 0 != (kount & 0x8000) => AT LEAST ONE URB COMPLETED WITH ERRORS | |
2541 | * 0 != (kount & 0x4000) => BUFFER HAS TOO MUCH DATA | |
2542 | * 0 != (kount & 0x2000) => BUFFER HAS NOT ENOUGH DATA | |
f36bc37a | 2543 | * 0 != (kount & 0x1000) => BUFFER HAS DATA FROM DISPARATE INPUTS |
ce36ceda | 2544 | * 0 != (kount & 0x0400) => RESERVED |
702422bd T |
2545 | * 0 != (kount & 0x0200) => FIELD BUFFER NOT YET CHECKED |
2546 | * 0 != (kount & 0x0100) => BUFFER HAS TWO EXTRA BYTES - WHY? | |
2547 | */ | |
2548 | /*---------------------------------------------------------------------------*/ | |
d090bf57 | 2549 | static void easycap_complete(struct urb *purb) |
702422bd | 2550 | { |
c7506658 TW |
2551 | struct easycap *peasycap; |
2552 | struct data_buffer *pfield_buffer; | |
2553 | char errbuf[16]; | |
2554 | int i, more, much, leap, rc, last; | |
2555 | int videofieldamount; | |
2556 | unsigned int override, bad; | |
2557 | int framestatus, framelength, frameactual, frameoffset; | |
2558 | u8 *pu; | |
2559 | ||
6888393c | 2560 | if (!purb) { |
c7506658 TW |
2561 | SAY("ERROR: easycap_complete(): purb is NULL\n"); |
2562 | return; | |
2563 | } | |
2564 | peasycap = purb->context; | |
6888393c | 2565 | if (!peasycap) { |
c7506658 TW |
2566 | SAY("ERROR: easycap_complete(): peasycap is NULL\n"); |
2567 | return; | |
2568 | } | |
c7506658 TW |
2569 | if (peasycap->video_eof) |
2570 | return; | |
2571 | for (i = 0; i < VIDEO_ISOC_BUFFER_MANY; i++) | |
2572 | if (purb->transfer_buffer == peasycap->video_isoc_buffer[i].pgo) | |
2573 | break; | |
2574 | JOM(16, "%2i=urb\n", i); | |
2575 | last = peasycap->video_isoc_sequence; | |
2576 | if ((((VIDEO_ISOC_BUFFER_MANY - 1) == last) && (0 != i)) || | |
2577 | (((VIDEO_ISOC_BUFFER_MANY - 1) != last) && ((last + 1) != i))) { | |
2578 | JOM(16, "ERROR: out-of-order urbs %i,%i ... continuing\n", | |
2579 | last, i); | |
2580 | } | |
2581 | peasycap->video_isoc_sequence = i; | |
702422bd | 2582 | |
c7506658 TW |
2583 | if (peasycap->video_idle) { |
2584 | JOM(16, "%i=video_idle %i=video_isoc_streaming\n", | |
2585 | peasycap->video_idle, peasycap->video_isoc_streaming); | |
2586 | if (peasycap->video_isoc_streaming) { | |
2587 | rc = usb_submit_urb(purb, GFP_ATOMIC); | |
2588 | if (rc) { | |
2589 | SAM("%s:%d ENOMEM\n", strerror(rc), rc); | |
2590 | if (-ENODEV != rc) | |
2591 | SAM("ERROR: while %i=video_idle, " | |
2592 | "usb_submit_urb() " | |
2593 | "failed with rc:\n", | |
2594 | peasycap->video_idle); | |
2595 | } | |
702422bd | 2596 | } |
c7506658 | 2597 | return; |
702422bd | 2598 | } |
c7506658 | 2599 | override = 0; |
702422bd | 2600 | /*---------------------------------------------------------------------------*/ |
c7506658 TW |
2601 | if (FIELD_BUFFER_MANY <= peasycap->field_fill) { |
2602 | SAM("ERROR: bad peasycap->field_fill\n"); | |
702422bd T |
2603 | return; |
2604 | } | |
c7506658 TW |
2605 | if (purb->status) { |
2606 | if ((-ESHUTDOWN == purb->status) || (-ENOENT == purb->status)) { | |
2607 | JOM(8, "urb status -ESHUTDOWN or -ENOENT\n"); | |
2608 | return; | |
2609 | } | |
702422bd | 2610 | |
c7506658 TW |
2611 | (peasycap->field_buffer[peasycap->field_fill][0].kount) |= 0x8000 ; |
2612 | SAM("ERROR: bad urb status -%s: %d\n", | |
2613 | strerror(purb->status), purb->status); | |
702422bd | 2614 | /*---------------------------------------------------------------------------*/ |
c7506658 TW |
2615 | } else { |
2616 | for (i = 0; i < purb->number_of_packets; i++) { | |
2617 | if (0 != purb->iso_frame_desc[i].status) { | |
2618 | (peasycap->field_buffer | |
2619 | [peasycap->field_fill][0].kount) |= 0x8000 ; | |
2620 | /* FIXME: 1. missing '-' check boundaries */ | |
2621 | strcpy(&errbuf[0], | |
2622 | strerror(purb->iso_frame_desc[i].status)); | |
702422bd | 2623 | } |
c7506658 TW |
2624 | framestatus = purb->iso_frame_desc[i].status; |
2625 | framelength = purb->iso_frame_desc[i].length; | |
2626 | frameactual = purb->iso_frame_desc[i].actual_length; | |
2627 | frameoffset = purb->iso_frame_desc[i].offset; | |
2628 | ||
2629 | JOM(16, "frame[%2i]:" | |
2630 | "%4i=status " | |
2631 | "%4i=actual " | |
2632 | "%4i=length " | |
2633 | "%5i=offset\n", | |
2634 | i, framestatus, frameactual, framelength, frameoffset); | |
2635 | if (!purb->iso_frame_desc[i].status) { | |
2636 | more = purb->iso_frame_desc[i].actual_length; | |
2637 | pfield_buffer = &peasycap->field_buffer | |
2638 | [peasycap->field_fill][peasycap->field_page]; | |
2639 | videofieldamount = (peasycap->field_page * | |
2640 | PAGE_SIZE) + | |
2641 | (int)(pfield_buffer->pto - pfield_buffer->pgo); | |
2642 | if (4 == more) | |
2643 | peasycap->video_mt++; | |
2644 | if (4 < more) { | |
2645 | if (peasycap->video_mt) { | |
2646 | JOM(8, "%4i empty video urb frames\n", | |
2647 | peasycap->video_mt); | |
2648 | peasycap->video_mt = 0; | |
2649 | } | |
2650 | if (FIELD_BUFFER_MANY <= peasycap->field_fill) { | |
2651 | SAM("ERROR: bad peasycap->field_fill\n"); | |
2652 | return; | |
2653 | } | |
2654 | if (FIELD_BUFFER_SIZE/PAGE_SIZE <= | |
2655 | peasycap->field_page) { | |
2656 | SAM("ERROR: bad peasycap->field_page\n"); | |
2657 | return; | |
2658 | } | |
2659 | pfield_buffer = &peasycap->field_buffer | |
2660 | [peasycap->field_fill][peasycap->field_page]; | |
2661 | pu = (u8 *)(purb->transfer_buffer + | |
2662 | purb->iso_frame_desc[i].offset); | |
2663 | if (0x80 & *pu) | |
2664 | leap = 8; | |
2665 | else | |
2666 | leap = 4; | |
702422bd T |
2667 | /*--------------------------------------------------------------------------*/ |
2668 | /* | |
2669 | * EIGHT-BYTE END-OF-VIDEOFIELD MARKER. | |
2670 | * NOTE: A SUCCESSION OF URB FRAMES FOLLOWING THIS ARE EMPTY, | |
2671 | * CORRESPONDING TO THE FIELD FLYBACK (VERTICAL BLANKING) PERIOD. | |
2672 | * | |
2673 | * PROVIDED THE FIELD BUFFER CONTAINS GOOD DATA AS INDICATED BY A ZERO UPPER | |
2674 | * BYTE OF | |
2675 | * peasycap->field_buffer[peasycap->field_fill][0].kount | |
2676 | * THE CONTENTS OF THE FIELD BUFFER ARE OFFERED TO dqbuf(), field_read IS | |
2677 | * UPDATED AND field_fill IS BUMPED. IF THE FIELD BUFFER CONTAINS BAD DATA | |
2678 | * NOTHING IS OFFERED TO dqbuf(). | |
2679 | * | |
2680 | * THE DECISION ON WHETHER THE PARITY OF THE OFFERED FIELD BUFFER IS RIGHT | |
2681 | * RESTS WITH dqbuf(). | |
2682 | */ | |
2683 | /*---------------------------------------------------------------------------*/ | |
c7506658 TW |
2684 | if ((8 == more) || override) { |
2685 | if (videofieldamount > | |
2686 | peasycap->videofieldamount) { | |
2687 | if (2 == videofieldamount - | |
2688 | peasycap-> | |
2689 | videofieldamount) { | |
2690 | (peasycap->field_buffer | |
2691 | [peasycap->field_fill] | |
2692 | [0].kount) |= 0x0100; | |
2693 | peasycap->video_junk += (1 + | |
2694 | VIDEO_JUNK_TOLERATE); | |
2695 | } else | |
2696 | (peasycap->field_buffer | |
2697 | [peasycap->field_fill] | |
2698 | [0].kount) |= 0x4000; | |
2699 | } else if (videofieldamount < | |
2700 | peasycap-> | |
2701 | videofieldamount) { | |
2702 | (peasycap->field_buffer | |
2703 | [peasycap->field_fill] | |
2704 | [0].kount) |= 0x2000; | |
2705 | } | |
2706 | bad = 0xFF00 & peasycap->field_buffer | |
2707 | [peasycap->field_fill] | |
2708 | [0].kount; | |
2709 | if (!bad) { | |
2710 | (peasycap->video_junk)--; | |
2711 | if (-VIDEO_JUNK_TOLERATE > | |
2712 | peasycap->video_junk) | |
2713 | peasycap->video_junk = | |
2714 | -VIDEO_JUNK_TOLERATE; | |
2715 | peasycap->field_read = | |
2716 | (peasycap-> | |
2717 | field_fill)++; | |
2718 | if (FIELD_BUFFER_MANY <= | |
2719 | peasycap-> | |
2720 | field_fill) | |
1dc6e418 | 2721 | peasycap-> |
c7506658 TW |
2722 | field_fill = 0; |
2723 | peasycap->field_page = 0; | |
2724 | pfield_buffer = &peasycap-> | |
2725 | field_buffer | |
2726 | [peasycap-> | |
2727 | field_fill] | |
2728 | [peasycap-> | |
2729 | field_page]; | |
2730 | pfield_buffer->pto = | |
2731 | pfield_buffer->pgo; | |
2732 | JOM(8, "bumped to: %i=" | |
2733 | "peasycap->" | |
2734 | "field_fill %i=" | |
2735 | "parity\n", | |
2736 | peasycap->field_fill, | |
2737 | 0x00FF & | |
2738 | pfield_buffer->kount); | |
2739 | JOM(8, "field buffer %i has " | |
2740 | "%i bytes fit to be " | |
2741 | "read\n", | |
2742 | peasycap->field_read, | |
2743 | videofieldamount); | |
2744 | JOM(8, "wakeup call to " | |
2745 | "wq_video, " | |
2746 | "%i=field_read " | |
2747 | "%i=field_fill " | |
2748 | "%i=parity\n", | |
2749 | peasycap->field_read, | |
2750 | peasycap->field_fill, | |
2751 | 0x00FF & peasycap-> | |
2752 | field_buffer | |
2753 | [peasycap-> | |
2754 | field_read][0].kount); | |
2755 | wake_up_interruptible | |
2756 | (&(peasycap-> | |
2757 | wq_video)); | |
2758 | do_gettimeofday | |
2759 | (&peasycap->timeval7); | |
2760 | } else { | |
2761 | peasycap->video_junk++; | |
2762 | if (bad & 0x0010) | |
2763 | peasycap->video_junk += | |
2764 | (1 + VIDEO_JUNK_TOLERATE/2); | |
2765 | JOM(8, "field buffer %i had %i " | |
2766 | "bytes, now discarded: " | |
2767 | "0x%04X\n", | |
2768 | peasycap->field_fill, | |
2769 | videofieldamount, | |
2770 | (0xFF00 & | |
2771 | peasycap->field_buffer | |
2772 | [peasycap->field_fill][0]. | |
2773 | kount)); | |
2774 | (peasycap->field_fill)++; | |
2775 | ||
2776 | if (FIELD_BUFFER_MANY <= | |
2777 | peasycap->field_fill) | |
2778 | peasycap->field_fill = 0; | |
f36bc37a | 2779 | peasycap->field_page = 0; |
c7506658 TW |
2780 | pfield_buffer = |
2781 | &peasycap->field_buffer | |
2782 | [peasycap->field_fill] | |
2783 | [peasycap->field_page]; | |
1dc6e418 | 2784 | pfield_buffer->pto = |
c7506658 TW |
2785 | pfield_buffer->pgo; |
2786 | ||
2787 | JOM(8, "bumped to: %i=peasycap->" | |
2788 | "field_fill %i=parity\n", | |
1dc6e418 | 2789 | peasycap->field_fill, |
c7506658 TW |
2790 | 0x00FF & pfield_buffer->kount); |
2791 | } | |
2792 | if (8 == more) { | |
2793 | JOM(8, "end-of-field: received " | |
2794 | "parity byte 0x%02X\n", | |
2795 | (0xFF & *pu)); | |
2796 | if (0x40 & *pu) | |
2797 | pfield_buffer->kount = 0x0000; | |
2798 | else | |
2799 | pfield_buffer->kount = 0x0001; | |
2800 | pfield_buffer->input = 0x08 | | |
2801 | (0x07 & peasycap->input); | |
2802 | JOM(8, "end-of-field: 0x%02X=kount\n", | |
2803 | 0xFF & pfield_buffer->kount); | |
2804 | } | |
702422bd | 2805 | } |
702422bd T |
2806 | /*---------------------------------------------------------------------------*/ |
2807 | /* | |
2808 | * COPY more BYTES FROM ISOC BUFFER TO FIELD BUFFER | |
2809 | */ | |
2810 | /*---------------------------------------------------------------------------*/ | |
c7506658 TW |
2811 | pu += leap; |
2812 | more -= leap; | |
702422bd | 2813 | |
c7506658 TW |
2814 | if (FIELD_BUFFER_MANY <= peasycap->field_fill) { |
2815 | SAM("ERROR: bad peasycap->field_fill\n"); | |
702422bd T |
2816 | return; |
2817 | } | |
c7506658 TW |
2818 | if (FIELD_BUFFER_SIZE/PAGE_SIZE <= peasycap->field_page) { |
2819 | SAM("ERROR: bad peasycap->field_page\n"); | |
2820 | return; | |
2821 | } | |
2822 | pfield_buffer = &peasycap->field_buffer | |
2823 | [peasycap->field_fill][peasycap->field_page]; | |
2824 | while (more) { | |
2825 | pfield_buffer = &peasycap->field_buffer | |
1dc6e418 | 2826 | [peasycap->field_fill] |
702422bd | 2827 | [peasycap->field_page]; |
c7506658 TW |
2828 | if (PAGE_SIZE < (pfield_buffer->pto - |
2829 | pfield_buffer->pgo)) { | |
2830 | SAM("ERROR: bad pfield_buffer->pto\n"); | |
2831 | return; | |
2832 | } | |
2833 | if (PAGE_SIZE == (pfield_buffer->pto - | |
2834 | pfield_buffer->pgo)) { | |
2835 | (peasycap->field_page)++; | |
2836 | if (FIELD_BUFFER_SIZE/PAGE_SIZE <= | |
2837 | peasycap->field_page) { | |
2838 | JOM(16, "wrapping peasycap->" | |
2839 | "field_page\n"); | |
2840 | peasycap->field_page = 0; | |
2841 | } | |
2842 | pfield_buffer = &peasycap-> | |
2843 | field_buffer | |
2844 | [peasycap->field_fill] | |
2845 | [peasycap->field_page]; | |
2846 | pfield_buffer->pto = pfield_buffer->pgo; | |
2847 | pfield_buffer->input = 0x08 | | |
2848 | (0x07 & peasycap->input); | |
2849 | if ((peasycap->field_buffer[peasycap-> | |
2850 | field_fill][0]). | |
2851 | input != | |
2852 | pfield_buffer->input) | |
2853 | (peasycap->field_buffer | |
2854 | [peasycap->field_fill] | |
2855 | [0]).kount |= 0x1000; | |
2856 | } | |
702422bd | 2857 | |
c7506658 TW |
2858 | much = PAGE_SIZE - |
2859 | (int)(pfield_buffer->pto - | |
702422bd T |
2860 | pfield_buffer->pgo); |
2861 | ||
c7506658 TW |
2862 | if (much > more) |
2863 | much = more; | |
2864 | memcpy(pfield_buffer->pto, pu, much); | |
2865 | pu += much; | |
2866 | (pfield_buffer->pto) += much; | |
2867 | more -= much; | |
2868 | } | |
702422bd T |
2869 | } |
2870 | } | |
2871 | } | |
2872 | } | |
702422bd | 2873 | /*---------------------------------------------------------------------------*/ |
702422bd T |
2874 | /* |
2875 | * RESUBMIT THIS URB, UNLESS A SEVERE PERSISTENT ERROR CONDITION EXISTS. | |
2876 | * | |
2877 | * IF THE WAIT QUEUES ARE NOT CLEARED IN RESPONSE TO AN ERROR CONDITION | |
2878 | * THE USERSPACE PROGRAM, E.G. mplayer, MAY HANG ON EXIT. BEWARE. | |
2879 | */ | |
2880 | /*---------------------------------------------------------------------------*/ | |
c7506658 TW |
2881 | if (VIDEO_ISOC_BUFFER_MANY <= peasycap->video_junk) { |
2882 | SAM("easycap driver shutting down on condition green\n"); | |
2883 | peasycap->status = 1; | |
2884 | peasycap->video_eof = 1; | |
2885 | peasycap->video_junk = 0; | |
2886 | wake_up_interruptible(&peasycap->wq_video); | |
f36bc37a | 2887 | #if !defined(PERSEVERE) |
c7506658 TW |
2888 | peasycap->audio_eof = 1; |
2889 | wake_up_interruptible(&peasycap->wq_audio); | |
f36bc37a | 2890 | #endif /*PERSEVERE*/ |
c7506658 | 2891 | return; |
702422bd | 2892 | } |
c7506658 TW |
2893 | if (peasycap->video_isoc_streaming) { |
2894 | rc = usb_submit_urb(purb, GFP_ATOMIC); | |
2895 | if (rc) { | |
2896 | SAM("%s: %d\n", strerror(rc), rc); | |
2897 | if (-ENODEV != rc) | |
2898 | SAM("ERROR: while %i=video_idle, " | |
2899 | "usb_submit_urb() " | |
2900 | "failed with rc:\n", | |
2901 | peasycap->video_idle); | |
2902 | } | |
2903 | } | |
2904 | return; | |
702422bd | 2905 | } |
d090bf57 TW |
2906 | static const struct file_operations easycap_fops = { |
2907 | .owner = THIS_MODULE, | |
2908 | .open = easycap_open, | |
f2b3c685 | 2909 | .unlocked_ioctl = easycap_unlocked_ioctl, |
d090bf57 TW |
2910 | .poll = easycap_poll, |
2911 | .mmap = easycap_mmap, | |
2912 | .llseek = no_llseek, | |
2913 | }; | |
2914 | static const struct usb_class_driver easycap_class = { | |
2915 | .name = "usb/easycap%d", | |
2916 | .fops = &easycap_fops, | |
2917 | .minor_base = USB_SKEL_MINOR_BASE, | |
2918 | }; | |
2919 | /*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ | |
d090bf57 TW |
2920 | static const struct v4l2_file_operations v4l2_fops = { |
2921 | .owner = THIS_MODULE, | |
2922 | .open = easycap_open_noinode, | |
f2b3c685 | 2923 | .unlocked_ioctl = easycap_unlocked_ioctl, |
d090bf57 TW |
2924 | .poll = easycap_poll, |
2925 | .mmap = easycap_mmap, | |
2926 | }; | |
702422bd T |
2927 | /*****************************************************************************/ |
2928 | /*---------------------------------------------------------------------------*/ | |
2929 | /* | |
a9855917 MT |
2930 | * WHEN THE EasyCAP IS PHYSICALLY PLUGGED IN, THIS FUNCTION IS CALLED THREE |
2931 | * TIMES, ONCE FOR EACH OF THE THREE INTERFACES. BEWARE. | |
702422bd T |
2932 | */ |
2933 | /*---------------------------------------------------------------------------*/ | |
11ff12fe TW |
2934 | static int easycap_usb_probe(struct usb_interface *intf, |
2935 | const struct usb_device_id *id) | |
702422bd | 2936 | { |
11ff12fe TW |
2937 | struct usb_device *usbdev; |
2938 | struct usb_host_interface *alt; | |
2939 | struct usb_endpoint_descriptor *ep; | |
2940 | struct usb_interface_descriptor *interface; | |
7dcef374 TW |
2941 | struct urb *purb; |
2942 | struct easycap *peasycap; | |
2943 | int ndong; | |
2944 | struct data_urb *pdata_urb; | |
1d243c2e | 2945 | int i, j, k, m, rc; |
7dcef374 TW |
2946 | u8 bInterfaceNumber; |
2947 | u8 bInterfaceClass; | |
2948 | u8 bInterfaceSubClass; | |
2949 | void *pbuf; | |
2950 | int okalt[8], isokalt; | |
2951 | int okepn[8]; | |
2952 | int okmps[8]; | |
2953 | int maxpacketsize; | |
2954 | u16 mask; | |
2955 | s32 value; | |
2956 | struct easycap_format *peasycap_format; | |
b4a5916e TW |
2957 | int fmtidx; |
2958 | struct inputset *inputset; | |
702422bd | 2959 | |
11ff12fe | 2960 | usbdev = interface_to_usbdev(intf); |
ee99aa49 | 2961 | |
702422bd | 2962 | /*---------------------------------------------------------------------------*/ |
11ff12fe TW |
2963 | alt = usb_altnum_to_altsetting(intf, 0); |
2964 | if (!alt) { | |
2965 | SAY("ERROR: usb_host_interface not found\n"); | |
7dcef374 TW |
2966 | return -EFAULT; |
2967 | } | |
11ff12fe TW |
2968 | interface = &alt->desc; |
2969 | if (!interface) { | |
2970 | SAY("ERROR: intf_descriptor is NULL\n"); | |
7dcef374 TW |
2971 | return -EFAULT; |
2972 | } | |
702422bd T |
2973 | /*---------------------------------------------------------------------------*/ |
2974 | /* | |
2975 | * GET PROPERTIES OF PROBED INTERFACE | |
2976 | */ | |
2977 | /*---------------------------------------------------------------------------*/ | |
11ff12fe TW |
2978 | bInterfaceNumber = interface->bInterfaceNumber; |
2979 | bInterfaceClass = interface->bInterfaceClass; | |
2980 | bInterfaceSubClass = interface->bInterfaceSubClass; | |
7dcef374 | 2981 | |
e03da5e2 | 2982 | JOT(4, "intf[%i]: num_altsetting=%i\n", |
11ff12fe | 2983 | bInterfaceNumber, intf->num_altsetting); |
e03da5e2 TW |
2984 | JOT(4, "intf[%i]: cur_altsetting - altsetting=%li\n", |
2985 | bInterfaceNumber, | |
11ff12fe | 2986 | (long int)(intf->cur_altsetting - intf->altsetting)); |
e03da5e2 TW |
2987 | JOT(4, "intf[%i]: bInterfaceClass=0x%02X bInterfaceSubClass=0x%02X\n", |
2988 | bInterfaceNumber, bInterfaceClass, bInterfaceSubClass); | |
702422bd T |
2989 | /*---------------------------------------------------------------------------*/ |
2990 | /* | |
2991 | * A NEW struct easycap IS ALWAYS ALLOCATED WHEN INTERFACE 0 IS PROBED. | |
2992 | * IT IS NOT POSSIBLE HERE TO FREE ANY EXISTING struct easycap. THIS | |
e68703cf MT |
2993 | * SHOULD HAVE BEEN DONE BY easycap_delete() WHEN THE EasyCAP WAS |
2994 | * PHYSICALLY UNPLUGGED. | |
2995 | * | |
2996 | * THE POINTER peasycap TO THE struct easycap IS REMEMBERED WHEN | |
2997 | * INTERFACES 1 AND 2 ARE PROBED. | |
e68703cf | 2998 | */ |
702422bd | 2999 | /*---------------------------------------------------------------------------*/ |
7dcef374 TW |
3000 | if (0 == bInterfaceNumber) { |
3001 | peasycap = kzalloc(sizeof(struct easycap), GFP_KERNEL); | |
6888393c | 3002 | if (!peasycap) { |
7dcef374 TW |
3003 | SAY("ERROR: Could not allocate peasycap\n"); |
3004 | return -ENOMEM; | |
3005 | } | |
702422bd T |
3006 | /*---------------------------------------------------------------------------*/ |
3007 | /* | |
e68703cf MT |
3008 | * PERFORM URGENT INTIALIZATIONS ... |
3009 | */ | |
702422bd | 3010 | /*---------------------------------------------------------------------------*/ |
7dcef374 | 3011 | peasycap->minor = -1; |
7dcef374 TW |
3012 | kref_init(&peasycap->kref); |
3013 | JOM(8, "intf[%i]: after kref_init(..._video) " | |
3014 | "%i=peasycap->kref.refcount.counter\n", | |
3015 | bInterfaceNumber, peasycap->kref.refcount.counter); | |
702422bd | 3016 | |
7dcef374 TW |
3017 | /* module params */ |
3018 | peasycap->gain = (s8)clamp(easycap_gain, 0, 31); | |
2a9a05c4 | 3019 | |
7dcef374 TW |
3020 | init_waitqueue_head(&peasycap->wq_video); |
3021 | init_waitqueue_head(&peasycap->wq_audio); | |
3022 | init_waitqueue_head(&peasycap->wq_trigger); | |
e68703cf | 3023 | |
7dcef374 | 3024 | if (mutex_lock_interruptible(&mutex_dongle)) { |
dfcce7bf | 3025 | SAY("ERROR: cannot down mutex_dongle\n"); |
7dcef374 TW |
3026 | return -ERESTARTSYS; |
3027 | } else { | |
a9855917 MT |
3028 | /*---------------------------------------------------------------------------*/ |
3029 | /* | |
3030 | * FOR INTERFACES 1 AND 2 THE POINTER peasycap WILL NEED TO | |
3031 | * TO BE THE SAME AS THAT ALLOCATED NOW FOR INTERFACE 0. | |
3032 | * | |
3033 | * NORMALLY ndong WILL NOT HAVE CHANGED SINCE INTERFACE 0 WAS | |
3034 | * PROBED, BUT THIS MAY NOT BE THE CASE IF, FOR EXAMPLE, TWO | |
3035 | * EASYCAPs ARE PLUGGED IN SIMULTANEOUSLY. | |
3036 | */ | |
3037 | /*---------------------------------------------------------------------------*/ | |
7dcef374 | 3038 | for (ndong = 0; ndong < DONGLE_MANY; ndong++) { |
6888393c | 3039 | if ((!easycapdc60_dongle[ndong].peasycap) && |
7dcef374 TW |
3040 | (!mutex_is_locked(&easycapdc60_dongle |
3041 | [ndong].mutex_video)) && | |
3042 | (!mutex_is_locked(&easycapdc60_dongle | |
3043 | [ndong].mutex_audio))) { | |
3044 | easycapdc60_dongle[ndong].peasycap = peasycap; | |
3045 | peasycap->isdongle = ndong; | |
3046 | JOM(8, "intf[%i]: peasycap-->easycap" | |
3047 | "_dongle[%i].peasycap\n", | |
3048 | bInterfaceNumber, ndong); | |
3049 | break; | |
3050 | } | |
3051 | } | |
3052 | if (DONGLE_MANY <= ndong) { | |
3053 | SAM("ERROR: too many dongles\n"); | |
3054 | mutex_unlock(&mutex_dongle); | |
3055 | return -ENOMEM; | |
ae59dad4 | 3056 | } |
a9855917 | 3057 | mutex_unlock(&mutex_dongle); |
a9855917 | 3058 | } |
7dcef374 TW |
3059 | peasycap->allocation_video_struct = sizeof(struct easycap); |
3060 | peasycap->allocation_video_page = 0; | |
3061 | peasycap->allocation_video_urb = 0; | |
3062 | peasycap->allocation_audio_struct = 0; | |
3063 | peasycap->allocation_audio_page = 0; | |
3064 | peasycap->allocation_audio_urb = 0; | |
e68703cf MT |
3065 | |
3066 | /*---------------------------------------------------------------------------*/ | |
3067 | /* | |
3068 | * ... AND FURTHER INITIALIZE THE STRUCTURE | |
3069 | */ | |
3070 | /*---------------------------------------------------------------------------*/ | |
11ff12fe TW |
3071 | peasycap->pusb_device = usbdev; |
3072 | peasycap->pusb_interface = intf; | |
702422bd | 3073 | |
7dcef374 TW |
3074 | peasycap->ilk = 0; |
3075 | peasycap->microphone = false; | |
702422bd | 3076 | |
7dcef374 TW |
3077 | peasycap->video_interface = -1; |
3078 | peasycap->video_altsetting_on = -1; | |
3079 | peasycap->video_altsetting_off = -1; | |
3080 | peasycap->video_endpointnumber = -1; | |
3081 | peasycap->video_isoc_maxframesize = -1; | |
3082 | peasycap->video_isoc_buffer_size = -1; | |
702422bd | 3083 | |
7dcef374 TW |
3084 | peasycap->audio_interface = -1; |
3085 | peasycap->audio_altsetting_on = -1; | |
3086 | peasycap->audio_altsetting_off = -1; | |
3087 | peasycap->audio_endpointnumber = -1; | |
3088 | peasycap->audio_isoc_maxframesize = -1; | |
3089 | peasycap->audio_isoc_buffer_size = -1; | |
702422bd | 3090 | |
7dcef374 | 3091 | peasycap->frame_buffer_many = FRAME_BUFFER_MANY; |
e68703cf | 3092 | |
7dcef374 TW |
3093 | for (k = 0; k < INPUT_MANY; k++) |
3094 | peasycap->lost[k] = 0; | |
3095 | peasycap->skip = 0; | |
3096 | peasycap->skipped = 0; | |
3097 | peasycap->offerfields = 0; | |
702422bd T |
3098 | /*---------------------------------------------------------------------------*/ |
3099 | /* | |
f36bc37a | 3100 | * DYNAMICALLY FILL IN THE AVAILABLE FORMATS ... |
702422bd T |
3101 | */ |
3102 | /*---------------------------------------------------------------------------*/ | |
7dcef374 TW |
3103 | rc = fillin_formats(); |
3104 | if (0 > rc) { | |
c7506658 | 3105 | SAM("ERROR: fillin_formats() rc = %i\n", rc); |
7dcef374 TW |
3106 | return -EFAULT; |
3107 | } | |
3108 | JOM(4, "%i formats available\n", rc); | |
f36bc37a MT |
3109 | /*---------------------------------------------------------------------------*/ |
3110 | /* | |
3111 | * ... AND POPULATE easycap.inputset[] | |
3112 | */ | |
3113 | /*---------------------------------------------------------------------------*/ | |
b4a5916e TW |
3114 | /* FIXME: maybe we just use memset 0 */ |
3115 | inputset = peasycap->inputset; | |
7dcef374 | 3116 | for (k = 0; k < INPUT_MANY; k++) { |
b4a5916e TW |
3117 | inputset[k].input_ok = 0; |
3118 | inputset[k].standard_offset_ok = 0; | |
3119 | inputset[k].format_offset_ok = 0; | |
3120 | inputset[k].brightness_ok = 0; | |
3121 | inputset[k].contrast_ok = 0; | |
3122 | inputset[k].saturation_ok = 0; | |
3123 | inputset[k].hue_ok = 0; | |
7dcef374 | 3124 | } |
b4a5916e TW |
3125 | |
3126 | fmtidx = peasycap->ntsc ? NTSC_M : PAL_BGHIN; | |
3127 | m = 0; | |
3128 | mask = 0; | |
3129 | for (i = 0; 0xFFFF != easycap_standard[i].mask; i++) { | |
3130 | if (fmtidx == easycap_standard[i].v4l2_standard.index) { | |
3131 | m++; | |
3132 | for (k = 0; k < INPUT_MANY; k++) | |
3133 | inputset[k].standard_offset = i; | |
3134 | ||
7dcef374 | 3135 | mask = easycap_standard[i].mask; |
f36bc37a | 3136 | } |
f36bc37a | 3137 | } |
7dcef374 TW |
3138 | |
3139 | if (1 != m) { | |
b4a5916e TW |
3140 | SAM("ERROR: " |
3141 | "inputset->standard_offset unpopulated, %i=m\n", m); | |
7dcef374 TW |
3142 | return -ENOENT; |
3143 | } | |
3144 | ||
3145 | peasycap_format = &easycap_format[0]; | |
f36bc37a | 3146 | m = 0; |
b4a5916e TW |
3147 | for (i = 0; peasycap_format->v4l2_format.fmt.pix.width; i++) { |
3148 | struct v4l2_pix_format *pix = | |
3149 | &peasycap_format->v4l2_format.fmt.pix; | |
7dcef374 | 3150 | if (((peasycap_format->mask & 0x0F) == (mask & 0x0F)) && |
b4a5916e TW |
3151 | pix->field == V4L2_FIELD_NONE && |
3152 | pix->pixelformat == V4L2_PIX_FMT_UYVY && | |
3153 | pix->width == 640 && pix->height == 480) { | |
f36bc37a | 3154 | m++; |
7dcef374 | 3155 | for (k = 0; k < INPUT_MANY; k++) |
b4a5916e | 3156 | inputset[k].format_offset = i; |
7dcef374 | 3157 | break; |
f36bc37a | 3158 | } |
e03da5e2 | 3159 | peasycap_format++; |
f36bc37a | 3160 | } |
7dcef374 | 3161 | if (1 != m) { |
b4a5916e | 3162 | SAM("ERROR: inputset[]->format_offset unpopulated\n"); |
e03da5e2 | 3163 | return -ENOENT; |
f36bc37a | 3164 | } |
f36bc37a | 3165 | |
7dcef374 | 3166 | m = 0; |
b4a5916e | 3167 | for (i = 0; 0xFFFFFFFF != easycap_control[i].id; i++) { |
7dcef374 TW |
3168 | value = easycap_control[i].default_value; |
3169 | if (V4L2_CID_BRIGHTNESS == easycap_control[i].id) { | |
3170 | m++; | |
3171 | for (k = 0; k < INPUT_MANY; k++) | |
b4a5916e | 3172 | inputset[k].brightness = value; |
7dcef374 TW |
3173 | } else if (V4L2_CID_CONTRAST == easycap_control[i].id) { |
3174 | m++; | |
3175 | for (k = 0; k < INPUT_MANY; k++) | |
b4a5916e | 3176 | inputset[k].contrast = value; |
7dcef374 TW |
3177 | } else if (V4L2_CID_SATURATION == easycap_control[i].id) { |
3178 | m++; | |
3179 | for (k = 0; k < INPUT_MANY; k++) | |
b4a5916e | 3180 | inputset[k].saturation = value; |
7dcef374 TW |
3181 | } else if (V4L2_CID_HUE == easycap_control[i].id) { |
3182 | m++; | |
3183 | for (k = 0; k < INPUT_MANY; k++) | |
b4a5916e | 3184 | inputset[k].hue = value; |
7dcef374 | 3185 | } |
f36bc37a | 3186 | } |
e03da5e2 | 3187 | |
7dcef374 | 3188 | if (4 != m) { |
b4a5916e | 3189 | SAM("ERROR: inputset[]->brightness underpopulated\n"); |
7dcef374 TW |
3190 | return -ENOENT; |
3191 | } | |
3192 | for (k = 0; k < INPUT_MANY; k++) | |
b4a5916e TW |
3193 | inputset[k].input = k; |
3194 | JOM(4, "populated inputset[]\n"); | |
7dcef374 TW |
3195 | JOM(4, "finished initialization\n"); |
3196 | } else { | |
702422bd | 3197 | /*---------------------------------------------------------------------------*/ |
a9855917 MT |
3198 | /* |
3199 | * FIXME | |
3200 | * | |
3201 | * IDENTIFY THE APPROPRIATE POINTER peasycap FOR INTERFACES 1 AND 2. | |
3202 | * THE ADDRESS OF peasycap->pusb_device IS RELUCTANTLY USED FOR THIS PURPOSE. | |
3203 | */ | |
e68703cf | 3204 | /*---------------------------------------------------------------------------*/ |
7dcef374 | 3205 | for (ndong = 0; ndong < DONGLE_MANY; ndong++) { |
11ff12fe | 3206 | if (usbdev == easycapdc60_dongle[ndong].peasycap-> |
7dcef374 TW |
3207 | pusb_device) { |
3208 | peasycap = easycapdc60_dongle[ndong].peasycap; | |
c7506658 TW |
3209 | JOT(8, "intf[%i]: dongle[%i].peasycap\n", |
3210 | bInterfaceNumber, ndong); | |
7dcef374 TW |
3211 | break; |
3212 | } | |
3213 | } | |
3214 | if (DONGLE_MANY <= ndong) { | |
3215 | SAY("ERROR: peasycap is unknown when probing interface %i\n", | |
3216 | bInterfaceNumber); | |
3217 | return -ENODEV; | |
3218 | } | |
6888393c | 3219 | if (!peasycap) { |
7dcef374 TW |
3220 | SAY("ERROR: peasycap is NULL when probing interface %i\n", |
3221 | bInterfaceNumber); | |
3222 | return -ENODEV; | |
a9855917 | 3223 | } |
dfcce7bf | 3224 | } |
702422bd | 3225 | /*---------------------------------------------------------------------------*/ |
7dcef374 | 3226 | if ((USB_CLASS_VIDEO == bInterfaceClass) || |
dfcce7bf | 3227 | (USB_CLASS_VENDOR_SPEC == bInterfaceClass)) { |
7dcef374 TW |
3228 | if (-1 == peasycap->video_interface) { |
3229 | peasycap->video_interface = bInterfaceNumber; | |
3230 | JOM(4, "setting peasycap->video_interface=%i\n", | |
3231 | peasycap->video_interface); | |
3232 | } else { | |
3233 | if (peasycap->video_interface != bInterfaceNumber) { | |
3234 | SAM("ERROR: attempting to reset " | |
3235 | "peasycap->video_interface\n"); | |
3236 | SAM("...... continuing with " | |
3237 | "%i=peasycap->video_interface\n", | |
702422bd | 3238 | peasycap->video_interface); |
7dcef374 | 3239 | } |
702422bd | 3240 | } |
7dcef374 | 3241 | } else if ((USB_CLASS_AUDIO == bInterfaceClass) && |
fc3cc2ca | 3242 | (USB_SUBCLASS_AUDIOSTREAMING == bInterfaceSubClass)) { |
7dcef374 TW |
3243 | if (-1 == peasycap->audio_interface) { |
3244 | peasycap->audio_interface = bInterfaceNumber; | |
3245 | JOM(4, "setting peasycap->audio_interface=%i\n", | |
3246 | peasycap->audio_interface); | |
3247 | } else { | |
3248 | if (peasycap->audio_interface != bInterfaceNumber) { | |
3249 | SAM("ERROR: attempting to reset " | |
3250 | "peasycap->audio_interface\n"); | |
3251 | SAM("...... continuing with " | |
3252 | "%i=peasycap->audio_interface\n", | |
3253 | peasycap->audio_interface); | |
3254 | } | |
702422bd T |
3255 | } |
3256 | } | |
702422bd T |
3257 | /*---------------------------------------------------------------------------*/ |
3258 | /* | |
3259 | * INVESTIGATE ALL ALTSETTINGS. | |
3260 | * DONE IN DETAIL BECAUSE USB DEVICE 05e1:0408 HAS DISPARATE INCARNATIONS. | |
3261 | */ | |
3262 | /*---------------------------------------------------------------------------*/ | |
7dcef374 | 3263 | isokalt = 0; |
702422bd | 3264 | |
11ff12fe TW |
3265 | for (i = 0; i < intf->num_altsetting; i++) { |
3266 | alt = usb_altnum_to_altsetting(intf, i); | |
3267 | if (!alt) { | |
3268 | SAM("ERROR: alt is NULL\n"); | |
7dcef374 | 3269 | return -EFAULT; |
702422bd | 3270 | } |
11ff12fe TW |
3271 | interface = &alt->desc; |
3272 | if (!interface) { | |
3273 | SAM("ERROR: intf_descriptor is NULL\n"); | |
7dcef374 | 3274 | return -EFAULT; |
702422bd | 3275 | } |
7dcef374 | 3276 | |
11ff12fe | 3277 | if (0 == interface->bNumEndpoints) |
e03da5e2 TW |
3278 | JOM(4, "intf[%i]alt[%i] has no endpoints\n", |
3279 | bInterfaceNumber, i); | |
7dcef374 | 3280 | /*---------------------------------------------------------------------------*/ |
11ff12fe TW |
3281 | for (j = 0; j < interface->bNumEndpoints; j++) { |
3282 | ep = &alt->endpoint[j].desc; | |
3283 | if (!ep) { | |
3284 | SAM("ERROR: ep is NULL.\n"); | |
7dcef374 TW |
3285 | SAM("...... skipping\n"); |
3286 | continue; | |
3287 | } | |
1d243c2e TW |
3288 | |
3289 | if (!usb_endpoint_is_isoc_in(ep)) { | |
3290 | JOM(4, "intf[%i]alt[%i]end[%i] is a %d endpoint\n", | |
3291 | bInterfaceNumber, | |
3292 | i, j, ep->bmAttributes); | |
3293 | if (usb_endpoint_dir_out(ep)) { | |
3294 | SAM("ERROR: OUT endpoint unexpected\n"); | |
3295 | SAM("...... continuing\n"); | |
3296 | } | |
3297 | continue; | |
7dcef374 | 3298 | } |
1d243c2e TW |
3299 | switch (bInterfaceClass) { |
3300 | case USB_CLASS_VIDEO: | |
3301 | case USB_CLASS_VENDOR_SPEC: { | |
3302 | if (ep->wMaxPacketSize) { | |
3303 | if (8 > isokalt) { | |
3304 | okalt[isokalt] = i; | |
3305 | JOM(4, | |
3306 | "%i=okalt[%i]\n", | |
3307 | okalt[isokalt], | |
3308 | isokalt); | |
3309 | okepn[isokalt] = | |
3310 | ep-> | |
3311 | bEndpointAddress & | |
3312 | 0x0F; | |
3313 | JOM(4, | |
3314 | "%i=okepn[%i]\n", | |
3315 | okepn[isokalt], | |
3316 | isokalt); | |
3317 | okmps[isokalt] = | |
3318 | le16_to_cpu(ep-> | |
3319 | wMaxPacketSize); | |
3320 | JOM(4, | |
3321 | "%i=okmps[%i]\n", | |
3322 | okmps[isokalt], | |
3323 | isokalt); | |
3324 | isokalt++; | |
702422bd | 3325 | } |
1d243c2e TW |
3326 | } else { |
3327 | if (-1 == peasycap-> | |
3328 | video_altsetting_off) { | |
3329 | peasycap-> | |
3330 | video_altsetting_off = | |
3331 | i; | |
3332 | JOM(4, "%i=video_" | |
3333 | "altsetting_off " | |
3334 | "<====\n", | |
3335 | peasycap-> | |
3336 | video_altsetting_off); | |
3337 | } else { | |
3338 | SAM("ERROR: peasycap" | |
3339 | "->video_altsetting_" | |
3340 | "off already set\n"); | |
3341 | SAM("...... " | |
3342 | "continuing with " | |
3343 | "%i=peasycap->video_" | |
3344 | "altsetting_off\n", | |
3345 | peasycap-> | |
3346 | video_altsetting_off); | |
3347 | } | |
3348 | } | |
3349 | break; | |
3350 | } | |
3351 | case USB_CLASS_AUDIO: { | |
3352 | if (bInterfaceSubClass != | |
3353 | USB_SUBCLASS_AUDIOSTREAMING) | |
702422bd | 3354 | break; |
1d243c2e TW |
3355 | if (!peasycap) { |
3356 | SAM("MISTAKE: " | |
3357 | "peasycap is NULL\n"); | |
3358 | return -EFAULT; | |
3359 | } | |
3360 | if (ep->wMaxPacketSize) { | |
3361 | if (8 > isokalt) { | |
3362 | okalt[isokalt] = i ; | |
3363 | JOM(4, | |
3364 | "%i=okalt[%i]\n", | |
3365 | okalt[isokalt], | |
3366 | isokalt); | |
3367 | okepn[isokalt] = | |
3368 | ep-> | |
3369 | bEndpointAddress & | |
3370 | 0x0F; | |
3371 | JOM(4, | |
3372 | "%i=okepn[%i]\n", | |
3373 | okepn[isokalt], | |
3374 | isokalt); | |
3375 | okmps[isokalt] = | |
3376 | le16_to_cpu(ep-> | |
3377 | wMaxPacketSize); | |
3378 | JOM(4, | |
3379 | "%i=okmps[%i]\n", | |
3380 | okmps[isokalt], | |
3381 | isokalt); | |
3382 | isokalt++; | |
7dcef374 | 3383 | } |
1d243c2e TW |
3384 | } else { |
3385 | if (-1 == peasycap-> | |
3386 | audio_altsetting_off) { | |
3387 | peasycap-> | |
3388 | audio_altsetting_off = | |
3389 | i; | |
3390 | JOM(4, "%i=audio_" | |
3391 | "altsetting_off " | |
3392 | "<====\n", | |
3393 | peasycap-> | |
3394 | audio_altsetting_off); | |
3395 | } else { | |
3396 | SAM("ERROR: peasycap" | |
3397 | "->audio_altsetting_" | |
3398 | "off already set\n"); | |
3399 | SAM("...... " | |
3400 | "continuing with " | |
3401 | "%i=peasycap->" | |
3402 | "audio_altsetting_" | |
3403 | "off\n", | |
3404 | peasycap-> | |
3405 | audio_altsetting_off); | |
7dcef374 | 3406 | } |
702422bd | 3407 | } |
1d243c2e TW |
3408 | break; |
3409 | } | |
3410 | default: | |
3411 | break; | |
7dcef374 | 3412 | } |
11ff12fe | 3413 | if (0 == ep->wMaxPacketSize) { |
7dcef374 TW |
3414 | JOM(4, "intf[%i]alt[%i]end[%i] " |
3415 | "has zero packet size\n", | |
3416 | bInterfaceNumber, i, j); | |
702422bd | 3417 | } |
702422bd T |
3418 | } |
3419 | } | |
702422bd T |
3420 | /*---------------------------------------------------------------------------*/ |
3421 | /* | |
3422 | * PERFORM INITIALIZATION OF THE PROBED INTERFACE | |
3423 | */ | |
3424 | /*---------------------------------------------------------------------------*/ | |
7dcef374 | 3425 | JOM(4, "initialization begins for interface %i\n", |
11ff12fe | 3426 | interface->bInterfaceNumber); |
7dcef374 | 3427 | switch (bInterfaceNumber) { |
702422bd T |
3428 | /*---------------------------------------------------------------------------*/ |
3429 | /* | |
3430 | * INTERFACE 0 IS THE VIDEO INTERFACE | |
3431 | */ | |
3432 | /*---------------------------------------------------------------------------*/ | |
7dcef374 TW |
3433 | case 0: { |
3434 | if (!peasycap) { | |
3435 | SAM("MISTAKE: peasycap is NULL\n"); | |
3436 | return -EFAULT; | |
3437 | } | |
3438 | if (!isokalt) { | |
3439 | SAM("ERROR: no viable video_altsetting_on\n"); | |
3440 | return -ENOENT; | |
3441 | } else { | |
3442 | peasycap->video_altsetting_on = okalt[isokalt - 1]; | |
3443 | JOM(4, "%i=video_altsetting_on <====\n", | |
3444 | peasycap->video_altsetting_on); | |
3445 | } | |
702422bd T |
3446 | /*---------------------------------------------------------------------------*/ |
3447 | /* | |
3448 | * DECIDE THE VIDEO STREAMING PARAMETERS | |
3449 | */ | |
3450 | /*---------------------------------------------------------------------------*/ | |
7dcef374 TW |
3451 | peasycap->video_endpointnumber = okepn[isokalt - 1]; |
3452 | JOM(4, "%i=video_endpointnumber\n", peasycap->video_endpointnumber); | |
3453 | maxpacketsize = okmps[isokalt - 1]; | |
e03da5e2 TW |
3454 | |
3455 | peasycap->video_isoc_maxframesize = | |
3456 | min(maxpacketsize, USB_2_0_MAXPACKETSIZE); | |
7dcef374 TW |
3457 | if (0 >= peasycap->video_isoc_maxframesize) { |
3458 | SAM("ERROR: bad video_isoc_maxframesize\n"); | |
3459 | SAM(" possibly because port is USB 1.1\n"); | |
3460 | return -ENOENT; | |
3461 | } | |
e03da5e2 TW |
3462 | JOM(4, "%i=video_isoc_maxframesize\n", |
3463 | peasycap->video_isoc_maxframesize); | |
3464 | ||
7dcef374 TW |
3465 | peasycap->video_isoc_framesperdesc = VIDEO_ISOC_FRAMESPERDESC; |
3466 | JOM(4, "%i=video_isoc_framesperdesc\n", | |
3467 | peasycap->video_isoc_framesperdesc); | |
3468 | if (0 >= peasycap->video_isoc_framesperdesc) { | |
3469 | SAM("ERROR: bad video_isoc_framesperdesc\n"); | |
3470 | return -ENOENT; | |
3471 | } | |
3472 | peasycap->video_isoc_buffer_size = | |
3473 | peasycap->video_isoc_maxframesize * | |
3474 | peasycap->video_isoc_framesperdesc; | |
3475 | JOM(4, "%i=video_isoc_buffer_size\n", | |
3476 | peasycap->video_isoc_buffer_size); | |
3477 | if ((PAGE_SIZE << VIDEO_ISOC_ORDER) < | |
3478 | peasycap->video_isoc_buffer_size) { | |
3479 | SAM("MISTAKE: peasycap->video_isoc_buffer_size too big\n"); | |
3480 | return -EFAULT; | |
3481 | } | |
702422bd | 3482 | /*---------------------------------------------------------------------------*/ |
7dcef374 TW |
3483 | if (-1 == peasycap->video_interface) { |
3484 | SAM("MISTAKE: video_interface is unset\n"); | |
3485 | return -EFAULT; | |
3486 | } | |
3487 | if (-1 == peasycap->video_altsetting_on) { | |
3488 | SAM("MISTAKE: video_altsetting_on is unset\n"); | |
3489 | return -EFAULT; | |
3490 | } | |
3491 | if (-1 == peasycap->video_altsetting_off) { | |
3492 | SAM("MISTAKE: video_interface_off is unset\n"); | |
3493 | return -EFAULT; | |
3494 | } | |
3495 | if (-1 == peasycap->video_endpointnumber) { | |
3496 | SAM("MISTAKE: video_endpointnumber is unset\n"); | |
3497 | return -EFAULT; | |
3498 | } | |
3499 | if (-1 == peasycap->video_isoc_maxframesize) { | |
3500 | SAM("MISTAKE: video_isoc_maxframesize is unset\n"); | |
3501 | return -EFAULT; | |
3502 | } | |
3503 | if (-1 == peasycap->video_isoc_buffer_size) { | |
3504 | SAM("MISTAKE: video_isoc_buffer_size is unset\n"); | |
3505 | return -EFAULT; | |
3506 | } | |
702422bd T |
3507 | /*---------------------------------------------------------------------------*/ |
3508 | /* | |
3509 | * ALLOCATE MEMORY FOR VIDEO BUFFERS. LISTS MUST BE INITIALIZED FIRST. | |
3510 | */ | |
3511 | /*---------------------------------------------------------------------------*/ | |
7dcef374 TW |
3512 | INIT_LIST_HEAD(&(peasycap->urb_video_head)); |
3513 | peasycap->purb_video_head = &(peasycap->urb_video_head); | |
3514 | /*---------------------------------------------------------------------------*/ | |
3515 | JOM(4, "allocating %i frame buffers of size %li\n", | |
3516 | FRAME_BUFFER_MANY, (long int)FRAME_BUFFER_SIZE); | |
3517 | JOM(4, ".... each scattered over %li pages\n", | |
3518 | FRAME_BUFFER_SIZE/PAGE_SIZE); | |
3519 | ||
3520 | for (k = 0; k < FRAME_BUFFER_MANY; k++) { | |
3521 | for (m = 0; m < FRAME_BUFFER_SIZE/PAGE_SIZE; m++) { | |
6888393c | 3522 | if (peasycap->frame_buffer[k][m].pgo) |
7dcef374 TW |
3523 | SAM("attempting to reallocate frame " |
3524 | " buffers\n"); | |
3525 | else { | |
3526 | pbuf = (void *)__get_free_page(GFP_KERNEL); | |
6888393c | 3527 | if (!pbuf) { |
7dcef374 TW |
3528 | SAM("ERROR: Could not allocate frame " |
3529 | "buffer %i page %i\n", k, m); | |
3530 | return -ENOMEM; | |
3531 | } else | |
3532 | peasycap->allocation_video_page += 1; | |
3533 | peasycap->frame_buffer[k][m].pgo = pbuf; | |
3534 | } | |
3535 | peasycap->frame_buffer[k][m].pto = | |
3536 | peasycap->frame_buffer[k][m].pgo; | |
702422bd | 3537 | } |
702422bd | 3538 | } |
702422bd | 3539 | |
7dcef374 TW |
3540 | peasycap->frame_fill = 0; |
3541 | peasycap->frame_read = 0; | |
3542 | JOM(4, "allocation of frame buffers done: %i pages\n", k * | |
3543 | m); | |
3544 | /*---------------------------------------------------------------------------*/ | |
3545 | JOM(4, "allocating %i field buffers of size %li\n", | |
3546 | FIELD_BUFFER_MANY, (long int)FIELD_BUFFER_SIZE); | |
3547 | JOM(4, ".... each scattered over %li pages\n", | |
3548 | FIELD_BUFFER_SIZE/PAGE_SIZE); | |
3549 | ||
3550 | for (k = 0; k < FIELD_BUFFER_MANY; k++) { | |
3551 | for (m = 0; m < FIELD_BUFFER_SIZE/PAGE_SIZE; m++) { | |
6888393c | 3552 | if (peasycap->field_buffer[k][m].pgo) { |
7dcef374 TW |
3553 | SAM("ERROR: attempting to reallocate " |
3554 | "field buffers\n"); | |
3555 | } else { | |
3556 | pbuf = (void *) __get_free_page(GFP_KERNEL); | |
6888393c | 3557 | if (!pbuf) { |
7dcef374 TW |
3558 | SAM("ERROR: Could not allocate field" |
3559 | " buffer %i page %i\n", k, m); | |
3560 | return -ENOMEM; | |
3561 | } | |
3562 | else | |
3563 | peasycap->allocation_video_page += 1; | |
3564 | peasycap->field_buffer[k][m].pgo = pbuf; | |
702422bd | 3565 | } |
7dcef374 TW |
3566 | peasycap->field_buffer[k][m].pto = |
3567 | peasycap->field_buffer[k][m].pgo; | |
3568 | } | |
3569 | peasycap->field_buffer[k][0].kount = 0x0200; | |
702422bd | 3570 | } |
7dcef374 TW |
3571 | peasycap->field_fill = 0; |
3572 | peasycap->field_page = 0; | |
3573 | peasycap->field_read = 0; | |
3574 | JOM(4, "allocation of field buffers done: %i pages\n", k * | |
3575 | m); | |
3576 | /*---------------------------------------------------------------------------*/ | |
3577 | JOM(4, "allocating %i isoc video buffers of size %i\n", | |
3578 | VIDEO_ISOC_BUFFER_MANY, | |
3579 | peasycap->video_isoc_buffer_size); | |
3580 | JOM(4, ".... each occupying contiguous memory pages\n"); | |
3581 | ||
3582 | for (k = 0; k < VIDEO_ISOC_BUFFER_MANY; k++) { | |
a90f3620 TW |
3583 | pbuf = (void *)__get_free_pages(GFP_KERNEL, |
3584 | VIDEO_ISOC_ORDER); | |
6888393c | 3585 | if (!pbuf) { |
7dcef374 TW |
3586 | SAM("ERROR: Could not allocate isoc video buffer " |
3587 | "%i\n", k); | |
3588 | return -ENOMEM; | |
3589 | } else | |
3590 | peasycap->allocation_video_page += | |
a90f3620 | 3591 | BIT(VIDEO_ISOC_ORDER); |
702422bd | 3592 | |
7dcef374 | 3593 | peasycap->video_isoc_buffer[k].pgo = pbuf; |
a90f3620 TW |
3594 | peasycap->video_isoc_buffer[k].pto = |
3595 | pbuf + peasycap->video_isoc_buffer_size; | |
7dcef374 TW |
3596 | peasycap->video_isoc_buffer[k].kount = k; |
3597 | } | |
3598 | JOM(4, "allocation of isoc video buffers done: %i pages\n", | |
3599 | k * (0x01 << VIDEO_ISOC_ORDER)); | |
702422bd T |
3600 | /*---------------------------------------------------------------------------*/ |
3601 | /* | |
3602 | * ALLOCATE AND INITIALIZE MULTIPLE struct urb ... | |
3603 | */ | |
3604 | /*---------------------------------------------------------------------------*/ | |
7dcef374 TW |
3605 | JOM(4, "allocating %i struct urb.\n", VIDEO_ISOC_BUFFER_MANY); |
3606 | JOM(4, "using %i=peasycap->video_isoc_framesperdesc\n", | |
3607 | peasycap->video_isoc_framesperdesc); | |
3608 | JOM(4, "using %i=peasycap->video_isoc_maxframesize\n", | |
3609 | peasycap->video_isoc_maxframesize); | |
3610 | JOM(4, "using %i=peasycap->video_isoc_buffer_sizen", | |
3611 | peasycap->video_isoc_buffer_size); | |
3612 | ||
3613 | for (k = 0; k < VIDEO_ISOC_BUFFER_MANY; k++) { | |
3614 | purb = usb_alloc_urb(peasycap->video_isoc_framesperdesc, | |
3615 | GFP_KERNEL); | |
6888393c | 3616 | if (!purb) { |
7dcef374 TW |
3617 | SAM("ERROR: usb_alloc_urb returned NULL for buffer " |
3618 | "%i\n", k); | |
3619 | return -ENOMEM; | |
3620 | } else | |
3621 | peasycap->allocation_video_urb += 1; | |
702422bd | 3622 | /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ |
7dcef374 | 3623 | pdata_urb = kzalloc(sizeof(struct data_urb), GFP_KERNEL); |
6888393c | 3624 | if (!pdata_urb) { |
7dcef374 TW |
3625 | SAM("ERROR: Could not allocate struct data_urb.\n"); |
3626 | return -ENOMEM; | |
3627 | } else | |
3628 | peasycap->allocation_video_struct += | |
3629 | sizeof(struct data_urb); | |
702422bd | 3630 | |
7dcef374 TW |
3631 | pdata_urb->purb = purb; |
3632 | pdata_urb->isbuf = k; | |
3633 | pdata_urb->length = 0; | |
3634 | list_add_tail(&(pdata_urb->list_head), | |
3635 | peasycap->purb_video_head); | |
702422bd T |
3636 | /*---------------------------------------------------------------------------*/ |
3637 | /* | |
3638 | * ... AND INITIALIZE THEM | |
3639 | */ | |
3640 | /*---------------------------------------------------------------------------*/ | |
7dcef374 TW |
3641 | if (!k) { |
3642 | JOM(4, "initializing video urbs thus:\n"); | |
3643 | JOM(4, " purb->interval = 1;\n"); | |
3644 | JOM(4, " purb->dev = peasycap->pusb_device;\n"); | |
3645 | JOM(4, " purb->pipe = usb_rcvisocpipe" | |
3646 | "(peasycap->pusb_device,%i);\n", | |
3647 | peasycap->video_endpointnumber); | |
3648 | JOM(4, " purb->transfer_flags = URB_ISO_ASAP;\n"); | |
3649 | JOM(4, " purb->transfer_buffer = peasycap->" | |
3650 | "video_isoc_buffer[.].pgo;\n"); | |
3651 | JOM(4, " purb->transfer_buffer_length = %i;\n", | |
3652 | peasycap->video_isoc_buffer_size); | |
3653 | JOM(4, " purb->complete = easycap_complete;\n"); | |
3654 | JOM(4, " purb->context = peasycap;\n"); | |
3655 | JOM(4, " purb->start_frame = 0;\n"); | |
3656 | JOM(4, " purb->number_of_packets = %i;\n", | |
3657 | peasycap->video_isoc_framesperdesc); | |
3658 | JOM(4, " for (j = 0; j < %i; j++)\n", | |
3659 | peasycap->video_isoc_framesperdesc); | |
3660 | JOM(4, " {\n"); | |
3661 | JOM(4, " purb->iso_frame_desc[j].offset = j*%i;\n", | |
3662 | peasycap->video_isoc_maxframesize); | |
3663 | JOM(4, " purb->iso_frame_desc[j].length = %i;\n", | |
3664 | peasycap->video_isoc_maxframesize); | |
3665 | JOM(4, " }\n"); | |
3666 | } | |
702422bd | 3667 | |
7dcef374 TW |
3668 | purb->interval = 1; |
3669 | purb->dev = peasycap->pusb_device; | |
3670 | purb->pipe = usb_rcvisocpipe(peasycap->pusb_device, | |
3671 | peasycap->video_endpointnumber); | |
3672 | purb->transfer_flags = URB_ISO_ASAP; | |
3673 | purb->transfer_buffer = peasycap->video_isoc_buffer[k].pgo; | |
3674 | purb->transfer_buffer_length = | |
3675 | peasycap->video_isoc_buffer_size; | |
3676 | purb->complete = easycap_complete; | |
3677 | purb->context = peasycap; | |
3678 | purb->start_frame = 0; | |
3679 | purb->number_of_packets = peasycap->video_isoc_framesperdesc; | |
3680 | for (j = 0; j < peasycap->video_isoc_framesperdesc; j++) { | |
3681 | purb->iso_frame_desc[j].offset = j * | |
3682 | peasycap->video_isoc_maxframesize; | |
3683 | purb->iso_frame_desc[j].length = | |
3684 | peasycap->video_isoc_maxframesize; | |
3685 | } | |
702422bd | 3686 | } |
7dcef374 | 3687 | JOM(4, "allocation of %i struct urb done.\n", k); |
702422bd T |
3688 | /*--------------------------------------------------------------------------*/ |
3689 | /* | |
3690 | * SAVE POINTER peasycap IN THIS INTERFACE. | |
3691 | */ | |
3692 | /*--------------------------------------------------------------------------*/ | |
11ff12fe | 3693 | usb_set_intfdata(intf, peasycap); |
268dfede MT |
3694 | /*---------------------------------------------------------------------------*/ |
3695 | /* | |
3696 | * IT IS ESSENTIAL TO INITIALIZE THE HARDWARE BEFORE, RATHER THAN AFTER, | |
3697 | * THE DEVICE IS REGISTERED, BECAUSE SOME VERSIONS OF THE videodev MODULE | |
3698 | * CALL easycap_open() IMMEDIATELY AFTER REGISTRATION, CAUSING A CLASH. | |
3699 | * BEWARE. | |
3700 | */ | |
3701 | /*---------------------------------------------------------------------------*/ | |
8d613954 TW |
3702 | peasycap->ntsc = easycap_ntsc; |
3703 | JOM(8, "defaulting initially to %s\n", | |
3704 | easycap_ntsc ? "NTSC" : "PAL"); | |
7dcef374 TW |
3705 | rc = reset(peasycap); |
3706 | if (rc) { | |
c7506658 | 3707 | SAM("ERROR: reset() rc = %i\n", rc); |
7dcef374 TW |
3708 | return -EFAULT; |
3709 | } | |
702422bd T |
3710 | /*--------------------------------------------------------------------------*/ |
3711 | /* | |
3712 | * THE VIDEO DEVICE CAN BE REGISTERED NOW, AS IT IS READY. | |
3713 | */ | |
3714 | /*--------------------------------------------------------------------------*/ | |
cdaa898b | 3715 | if (v4l2_device_register(&intf->dev, &peasycap->v4l2_device)) { |
7dcef374 TW |
3716 | SAM("v4l2_device_register() failed\n"); |
3717 | return -ENODEV; | |
7dcef374 | 3718 | } |
cdaa898b TW |
3719 | JOM(4, "registered device instance: %s\n", |
3720 | peasycap->v4l2_device.name); | |
e68703cf MT |
3721 | /*---------------------------------------------------------------------------*/ |
3722 | /* | |
a9855917 | 3723 | * FIXME |
ae59dad4 MT |
3724 | * |
3725 | * | |
e68703cf MT |
3726 | * THIS IS BELIEVED TO BE HARMLESS, BUT MAY WELL BE UNNECESSARY OR WRONG: |
3727 | */ | |
3728 | /*---------------------------------------------------------------------------*/ | |
7dcef374 | 3729 | peasycap->video_device.v4l2_dev = NULL; |
e68703cf | 3730 | /*---------------------------------------------------------------------------*/ |
702422bd | 3731 | |
e68703cf | 3732 | |
7dcef374 | 3733 | strcpy(&peasycap->video_device.name[0], "easycapdc60"); |
7dcef374 | 3734 | peasycap->video_device.fops = &v4l2_fops; |
7dcef374 TW |
3735 | peasycap->video_device.minor = -1; |
3736 | peasycap->video_device.release = (void *)(&videodev_release); | |
702422bd | 3737 | |
7dcef374 | 3738 | video_set_drvdata(&(peasycap->video_device), (void *)peasycap); |
702422bd | 3739 | |
7dcef374 TW |
3740 | if (0 != (video_register_device(&(peasycap->video_device), |
3741 | VFL_TYPE_GRABBER, -1))) { | |
3742 | err("Not able to register with videodev"); | |
3743 | videodev_release(&(peasycap->video_device)); | |
3744 | return -ENODEV; | |
3745 | } else { | |
3746 | (peasycap->registered_video)++; | |
3747 | SAM("registered with videodev: %i=minor\n", | |
3748 | peasycap->video_device.minor); | |
3749 | peasycap->minor = peasycap->video_device.minor; | |
3750 | } | |
e68703cf | 3751 | /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ |
a9855917 | 3752 | |
7dcef374 | 3753 | break; |
dfcce7bf | 3754 | } |
702422bd T |
3755 | /*--------------------------------------------------------------------------*/ |
3756 | /* | |
3757 | * INTERFACE 1 IS THE AUDIO CONTROL INTERFACE | |
3758 | * INTERFACE 2 IS THE AUDIO STREAMING INTERFACE | |
3759 | */ | |
3760 | /*--------------------------------------------------------------------------*/ | |
7dcef374 | 3761 | case 1: { |
7dcef374 TW |
3762 | if (!peasycap) { |
3763 | SAM("MISTAKE: peasycap is NULL\n"); | |
3764 | return -EFAULT; | |
3765 | } | |
702422bd T |
3766 | /*--------------------------------------------------------------------------*/ |
3767 | /* | |
3768 | * SAVE POINTER peasycap IN INTERFACE 1 | |
3769 | */ | |
3770 | /*--------------------------------------------------------------------------*/ | |
11ff12fe | 3771 | usb_set_intfdata(intf, peasycap); |
7dcef374 | 3772 | JOM(4, "no initialization required for interface %i\n", |
11ff12fe | 3773 | interface->bInterfaceNumber); |
7dcef374 | 3774 | break; |
702422bd | 3775 | } |
c7506658 | 3776 | /*--------------------------------------------------------------------------*/ |
7dcef374 | 3777 | case 2: { |
7dcef374 TW |
3778 | if (!peasycap) { |
3779 | SAM("MISTAKE: peasycap is NULL\n"); | |
3780 | return -EFAULT; | |
3781 | } | |
3782 | if (!isokalt) { | |
3783 | SAM("ERROR: no viable audio_altsetting_on\n"); | |
3784 | return -ENOENT; | |
3785 | } else { | |
3786 | peasycap->audio_altsetting_on = okalt[isokalt - 1]; | |
3787 | JOM(4, "%i=audio_altsetting_on <====\n", | |
3788 | peasycap->audio_altsetting_on); | |
3789 | } | |
e68703cf | 3790 | |
7dcef374 TW |
3791 | peasycap->audio_endpointnumber = okepn[isokalt - 1]; |
3792 | JOM(4, "%i=audio_endpointnumber\n", peasycap->audio_endpointnumber); | |
e68703cf | 3793 | |
7dcef374 TW |
3794 | peasycap->audio_isoc_maxframesize = okmps[isokalt - 1]; |
3795 | JOM(4, "%i=audio_isoc_maxframesize\n", | |
3796 | peasycap->audio_isoc_maxframesize); | |
3797 | if (0 >= peasycap->audio_isoc_maxframesize) { | |
3798 | SAM("ERROR: bad audio_isoc_maxframesize\n"); | |
3799 | return -ENOENT; | |
3800 | } | |
3801 | if (9 == peasycap->audio_isoc_maxframesize) { | |
3802 | peasycap->ilk |= 0x02; | |
3803 | SAM("audio hardware is microphone\n"); | |
3804 | peasycap->microphone = true; | |
a90f3620 TW |
3805 | peasycap->audio_pages_per_fragment = |
3806 | PAGES_PER_AUDIO_FRAGMENT; | |
7dcef374 TW |
3807 | } else if (256 == peasycap->audio_isoc_maxframesize) { |
3808 | peasycap->ilk &= ~0x02; | |
3809 | SAM("audio hardware is AC'97\n"); | |
3810 | peasycap->microphone = false; | |
a90f3620 TW |
3811 | peasycap->audio_pages_per_fragment = |
3812 | PAGES_PER_AUDIO_FRAGMENT; | |
7dcef374 TW |
3813 | } else { |
3814 | SAM("hardware is unidentified:\n"); | |
3815 | SAM("%i=audio_isoc_maxframesize\n", | |
a90f3620 | 3816 | peasycap->audio_isoc_maxframesize); |
7dcef374 TW |
3817 | return -ENOENT; |
3818 | } | |
702422bd | 3819 | |
7dcef374 | 3820 | peasycap->audio_bytes_per_fragment = |
a90f3620 | 3821 | peasycap->audio_pages_per_fragment * PAGE_SIZE; |
7dcef374 | 3822 | peasycap->audio_buffer_page_many = (AUDIO_FRAGMENT_MANY * |
a90f3620 | 3823 | peasycap->audio_pages_per_fragment); |
7dcef374 TW |
3824 | |
3825 | JOM(4, "%6i=AUDIO_FRAGMENT_MANY\n", AUDIO_FRAGMENT_MANY); | |
3826 | JOM(4, "%6i=audio_pages_per_fragment\n", | |
3827 | peasycap->audio_pages_per_fragment); | |
3828 | JOM(4, "%6i=audio_bytes_per_fragment\n", | |
3829 | peasycap->audio_bytes_per_fragment); | |
3830 | JOM(4, "%6i=audio_buffer_page_many\n", | |
3831 | peasycap->audio_buffer_page_many); | |
3832 | ||
3833 | peasycap->audio_isoc_framesperdesc = AUDIO_ISOC_FRAMESPERDESC; | |
3834 | ||
3835 | JOM(4, "%i=audio_isoc_framesperdesc\n", | |
3836 | peasycap->audio_isoc_framesperdesc); | |
3837 | if (0 >= peasycap->audio_isoc_framesperdesc) { | |
3838 | SAM("ERROR: bad audio_isoc_framesperdesc\n"); | |
3839 | return -ENOENT; | |
3840 | } | |
702422bd | 3841 | |
7dcef374 TW |
3842 | peasycap->audio_isoc_buffer_size = |
3843 | peasycap->audio_isoc_maxframesize * | |
3844 | peasycap->audio_isoc_framesperdesc; | |
3845 | JOM(4, "%i=audio_isoc_buffer_size\n", | |
3846 | peasycap->audio_isoc_buffer_size); | |
3847 | if (AUDIO_ISOC_BUFFER_SIZE < peasycap->audio_isoc_buffer_size) { | |
3848 | SAM("MISTAKE: audio_isoc_buffer_size bigger " | |
3849 | "than %li=AUDIO_ISOC_BUFFER_SIZE\n", | |
3850 | AUDIO_ISOC_BUFFER_SIZE); | |
3851 | return -EFAULT; | |
3852 | } | |
3853 | if (-1 == peasycap->audio_interface) { | |
3854 | SAM("MISTAKE: audio_interface is unset\n"); | |
3855 | return -EFAULT; | |
3856 | } | |
3857 | if (-1 == peasycap->audio_altsetting_on) { | |
3858 | SAM("MISTAKE: audio_altsetting_on is unset\n"); | |
3859 | return -EFAULT; | |
3860 | } | |
3861 | if (-1 == peasycap->audio_altsetting_off) { | |
3862 | SAM("MISTAKE: audio_interface_off is unset\n"); | |
3863 | return -EFAULT; | |
3864 | } | |
3865 | if (-1 == peasycap->audio_endpointnumber) { | |
3866 | SAM("MISTAKE: audio_endpointnumber is unset\n"); | |
3867 | return -EFAULT; | |
3868 | } | |
3869 | if (-1 == peasycap->audio_isoc_maxframesize) { | |
3870 | SAM("MISTAKE: audio_isoc_maxframesize is unset\n"); | |
3871 | return -EFAULT; | |
3872 | } | |
3873 | if (-1 == peasycap->audio_isoc_buffer_size) { | |
3874 | SAM("MISTAKE: audio_isoc_buffer_size is unset\n"); | |
3875 | return -EFAULT; | |
3876 | } | |
702422bd T |
3877 | /*---------------------------------------------------------------------------*/ |
3878 | /* | |
3879 | * ALLOCATE MEMORY FOR AUDIO BUFFERS. LISTS MUST BE INITIALIZED FIRST. | |
3880 | */ | |
3881 | /*---------------------------------------------------------------------------*/ | |
7dcef374 TW |
3882 | INIT_LIST_HEAD(&(peasycap->urb_audio_head)); |
3883 | peasycap->purb_audio_head = &(peasycap->urb_audio_head); | |
702422bd | 3884 | |
702422bd | 3885 | /*---------------------------------------------------------------------------*/ |
7dcef374 | 3886 | JOM(4, "allocating %i isoc audio buffers of size %i\n", |
a90f3620 TW |
3887 | AUDIO_ISOC_BUFFER_MANY, |
3888 | peasycap->audio_isoc_buffer_size); | |
7dcef374 | 3889 | JOM(4, ".... each occupying contiguous memory pages\n"); |
702422bd | 3890 | |
7dcef374 | 3891 | for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) { |
a90f3620 TW |
3892 | pbuf = (void *)__get_free_pages(GFP_KERNEL, |
3893 | AUDIO_ISOC_ORDER); | |
6888393c | 3894 | if (!pbuf) { |
7dcef374 TW |
3895 | SAM("ERROR: Could not allocate isoc audio buffer " |
3896 | "%i\n", k); | |
3897 | return -ENOMEM; | |
3898 | } else | |
3899 | peasycap->allocation_audio_page += | |
a90f3620 | 3900 | BIT(AUDIO_ISOC_ORDER); |
702422bd | 3901 | |
7dcef374 TW |
3902 | peasycap->audio_isoc_buffer[k].pgo = pbuf; |
3903 | peasycap->audio_isoc_buffer[k].pto = pbuf + | |
3904 | peasycap->audio_isoc_buffer_size; | |
3905 | peasycap->audio_isoc_buffer[k].kount = k; | |
3906 | } | |
3907 | JOM(4, "allocation of isoc audio buffers done.\n"); | |
702422bd T |
3908 | /*---------------------------------------------------------------------------*/ |
3909 | /* | |
3910 | * ALLOCATE AND INITIALIZE MULTIPLE struct urb ... | |
3911 | */ | |
3912 | /*---------------------------------------------------------------------------*/ | |
7dcef374 TW |
3913 | JOM(4, "allocating %i struct urb.\n", AUDIO_ISOC_BUFFER_MANY); |
3914 | JOM(4, "using %i=peasycap->audio_isoc_framesperdesc\n", | |
a90f3620 | 3915 | peasycap->audio_isoc_framesperdesc); |
7dcef374 | 3916 | JOM(4, "using %i=peasycap->audio_isoc_maxframesize\n", |
a90f3620 | 3917 | peasycap->audio_isoc_maxframesize); |
7dcef374 | 3918 | JOM(4, "using %i=peasycap->audio_isoc_buffer_size\n", |
a90f3620 | 3919 | peasycap->audio_isoc_buffer_size); |
7dcef374 TW |
3920 | |
3921 | for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) { | |
3922 | purb = usb_alloc_urb(peasycap->audio_isoc_framesperdesc, | |
a90f3620 | 3923 | GFP_KERNEL); |
6888393c | 3924 | if (!purb) { |
7dcef374 TW |
3925 | SAM("ERROR: usb_alloc_urb returned NULL for buffer " |
3926 | "%i\n", k); | |
3927 | return -ENOMEM; | |
e03da5e2 TW |
3928 | } |
3929 | peasycap->allocation_audio_urb += 1 ; | |
702422bd | 3930 | /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ |
7dcef374 | 3931 | pdata_urb = kzalloc(sizeof(struct data_urb), GFP_KERNEL); |
6888393c | 3932 | if (!pdata_urb) { |
7dcef374 TW |
3933 | SAM("ERROR: Could not allocate struct data_urb.\n"); |
3934 | return -ENOMEM; | |
e03da5e2 TW |
3935 | } |
3936 | peasycap->allocation_audio_struct += | |
3937 | sizeof(struct data_urb); | |
702422bd | 3938 | |
7dcef374 TW |
3939 | pdata_urb->purb = purb; |
3940 | pdata_urb->isbuf = k; | |
3941 | pdata_urb->length = 0; | |
3942 | list_add_tail(&(pdata_urb->list_head), | |
3943 | peasycap->purb_audio_head); | |
702422bd T |
3944 | /*---------------------------------------------------------------------------*/ |
3945 | /* | |
3946 | * ... AND INITIALIZE THEM | |
3947 | */ | |
3948 | /*---------------------------------------------------------------------------*/ | |
7dcef374 TW |
3949 | if (!k) { |
3950 | JOM(4, "initializing audio urbs thus:\n"); | |
3951 | JOM(4, " purb->interval = 1;\n"); | |
3952 | JOM(4, " purb->dev = peasycap->pusb_device;\n"); | |
3953 | JOM(4, " purb->pipe = usb_rcvisocpipe(peasycap->" | |
3954 | "pusb_device,%i);\n", | |
3955 | peasycap->audio_endpointnumber); | |
3956 | JOM(4, " purb->transfer_flags = URB_ISO_ASAP;\n"); | |
3957 | JOM(4, " purb->transfer_buffer = " | |
3958 | "peasycap->audio_isoc_buffer[.].pgo;\n"); | |
3959 | JOM(4, " purb->transfer_buffer_length = %i;\n", | |
a90f3620 | 3960 | peasycap->audio_isoc_buffer_size); |
7dcef374 | 3961 | JOM(4, " purb->complete = easycap_alsa_complete;\n"); |
7dcef374 TW |
3962 | JOM(4, " purb->context = peasycap;\n"); |
3963 | JOM(4, " purb->start_frame = 0;\n"); | |
3964 | JOM(4, " purb->number_of_packets = %i;\n", | |
3965 | peasycap->audio_isoc_framesperdesc); | |
3966 | JOM(4, " for (j = 0; j < %i; j++)\n", | |
3967 | peasycap->audio_isoc_framesperdesc); | |
3968 | JOM(4, " {\n"); | |
3969 | JOM(4, " purb->iso_frame_desc[j].offset = j*%i;\n", | |
a90f3620 | 3970 | peasycap->audio_isoc_maxframesize); |
7dcef374 | 3971 | JOM(4, " purb->iso_frame_desc[j].length = %i;\n", |
a90f3620 | 3972 | peasycap->audio_isoc_maxframesize); |
7dcef374 | 3973 | JOM(4, " }\n"); |
dfcce7bf | 3974 | } |
702422bd | 3975 | |
7dcef374 TW |
3976 | purb->interval = 1; |
3977 | purb->dev = peasycap->pusb_device; | |
3978 | purb->pipe = usb_rcvisocpipe(peasycap->pusb_device, | |
3979 | peasycap->audio_endpointnumber); | |
3980 | purb->transfer_flags = URB_ISO_ASAP; | |
3981 | purb->transfer_buffer = peasycap->audio_isoc_buffer[k].pgo; | |
3982 | purb->transfer_buffer_length = | |
3983 | peasycap->audio_isoc_buffer_size; | |
7dcef374 | 3984 | purb->complete = easycap_alsa_complete; |
7dcef374 TW |
3985 | purb->context = peasycap; |
3986 | purb->start_frame = 0; | |
3987 | purb->number_of_packets = peasycap->audio_isoc_framesperdesc; | |
3988 | for (j = 0; j < peasycap->audio_isoc_framesperdesc; j++) { | |
3989 | purb->iso_frame_desc[j].offset = j * | |
3990 | peasycap->audio_isoc_maxframesize; | |
3991 | purb->iso_frame_desc[j].length = | |
3992 | peasycap->audio_isoc_maxframesize; | |
3993 | } | |
702422bd | 3994 | } |
7dcef374 | 3995 | JOM(4, "allocation of %i struct urb done.\n", k); |
702422bd T |
3996 | /*---------------------------------------------------------------------------*/ |
3997 | /* | |
3998 | * SAVE POINTER peasycap IN THIS INTERFACE. | |
3999 | */ | |
4000 | /*---------------------------------------------------------------------------*/ | |
11ff12fe | 4001 | usb_set_intfdata(intf, peasycap); |
702422bd T |
4002 | /*---------------------------------------------------------------------------*/ |
4003 | /* | |
4004 | * THE AUDIO DEVICE CAN BE REGISTERED NOW, AS IT IS READY. | |
4005 | */ | |
4006 | /*---------------------------------------------------------------------------*/ | |
7dcef374 | 4007 | JOM(4, "initializing ALSA card\n"); |
a9855917 | 4008 | |
7dcef374 TW |
4009 | rc = easycap_alsa_probe(peasycap); |
4010 | if (rc) { | |
c7506658 | 4011 | err("easycap_alsa_probe() rc = %i\n", rc); |
7dcef374 | 4012 | return -ENODEV; |
7dcef374 | 4013 | } |
a9855917 | 4014 | |
a9855917 | 4015 | |
ad30d7af TW |
4016 | JOM(8, "kref_get() with %i=kref.refcount.counter\n", |
4017 | peasycap->kref.refcount.counter); | |
4018 | kref_get(&peasycap->kref); | |
4019 | peasycap->registered_audio++; | |
7dcef374 | 4020 | break; |
dfcce7bf | 4021 | } |
702422bd T |
4022 | /*---------------------------------------------------------------------------*/ |
4023 | /* | |
4024 | * INTERFACES OTHER THAN 0, 1 AND 2 ARE UNEXPECTED | |
4025 | */ | |
4026 | /*---------------------------------------------------------------------------*/ | |
dfcce7bf TW |
4027 | default: |
4028 | JOM(4, "ERROR: unexpected interface %i\n", bInterfaceNumber); | |
4029 | return -EINVAL; | |
7dcef374 | 4030 | } |
e03da5e2 | 4031 | SAM("ends successfully for interface %i\n", bInterfaceNumber); |
7dcef374 | 4032 | return 0; |
702422bd T |
4033 | } |
4034 | /*****************************************************************************/ | |
4035 | /*---------------------------------------------------------------------------*/ | |
4036 | /* | |
ae59dad4 MT |
4037 | * WHEN THIS FUNCTION IS CALLED THE EasyCAP HAS ALREADY BEEN PHYSICALLY |
4038 | * UNPLUGGED. HENCE peasycap->pusb_device IS NO LONGER VALID. | |
a9855917 | 4039 | * |
73019286 | 4040 | * THIS FUNCTION AFFECTS ALSA. BEWARE. |
702422bd T |
4041 | */ |
4042 | /*---------------------------------------------------------------------------*/ | |
d090bf57 | 4043 | static void easycap_usb_disconnect(struct usb_interface *pusb_interface) |
702422bd | 4044 | { |
7dcef374 TW |
4045 | struct usb_host_interface *pusb_host_interface; |
4046 | struct usb_interface_descriptor *pusb_interface_descriptor; | |
4047 | u8 bInterfaceNumber; | |
4048 | struct easycap *peasycap; | |
4049 | ||
4050 | struct list_head *plist_head; | |
4051 | struct data_urb *pdata_urb; | |
4052 | int minor, m, kd; | |
702422bd | 4053 | |
7dcef374 | 4054 | JOT(4, "\n"); |
702422bd | 4055 | |
7dcef374 | 4056 | pusb_host_interface = pusb_interface->cur_altsetting; |
6888393c | 4057 | if (!pusb_host_interface) { |
7dcef374 TW |
4058 | JOT(4, "ERROR: pusb_host_interface is NULL\n"); |
4059 | return; | |
4060 | } | |
4061 | pusb_interface_descriptor = &(pusb_host_interface->desc); | |
6888393c | 4062 | if (!pusb_interface_descriptor) { |
7dcef374 TW |
4063 | JOT(4, "ERROR: pusb_interface_descriptor is NULL\n"); |
4064 | return; | |
4065 | } | |
4066 | bInterfaceNumber = pusb_interface_descriptor->bInterfaceNumber; | |
4067 | minor = pusb_interface->minor; | |
4068 | JOT(4, "intf[%i]: minor=%i\n", bInterfaceNumber, minor); | |
702422bd | 4069 | |
7dcef374 TW |
4070 | if (1 == bInterfaceNumber) |
4071 | return; | |
e68703cf | 4072 | |
7dcef374 | 4073 | peasycap = usb_get_intfdata(pusb_interface); |
6888393c | 4074 | if (!peasycap) { |
7dcef374 TW |
4075 | SAY("ERROR: peasycap is NULL\n"); |
4076 | return; | |
4077 | } | |
e68703cf MT |
4078 | /*---------------------------------------------------------------------------*/ |
4079 | /* | |
4080 | * IF THE WAIT QUEUES ARE NOT CLEARED A DEADLOCK IS POSSIBLE. BEWARE. | |
4081 | */ | |
4082 | /*---------------------------------------------------------------------------*/ | |
7dcef374 TW |
4083 | peasycap->video_eof = 1; |
4084 | peasycap->audio_eof = 1; | |
4085 | wake_up_interruptible(&(peasycap->wq_video)); | |
4086 | wake_up_interruptible(&(peasycap->wq_audio)); | |
4087 | /*---------------------------------------------------------------------------*/ | |
4088 | switch (bInterfaceNumber) { | |
4089 | case 0: { | |
6888393c | 4090 | if (peasycap->purb_video_head) { |
7dcef374 TW |
4091 | JOM(4, "killing video urbs\n"); |
4092 | m = 0; | |
a90f3620 | 4093 | list_for_each(plist_head, peasycap->purb_video_head) { |
7dcef374 TW |
4094 | pdata_urb = list_entry(plist_head, |
4095 | struct data_urb, list_head); | |
6888393c TW |
4096 | if (pdata_urb) { |
4097 | if (pdata_urb->purb) { | |
7dcef374 TW |
4098 | usb_kill_urb(pdata_urb->purb); |
4099 | m++; | |
4100 | } | |
702422bd T |
4101 | } |
4102 | } | |
7dcef374 | 4103 | JOM(4, "%i video urbs killed\n", m); |
e68703cf | 4104 | } |
7dcef374 | 4105 | break; |
702422bd T |
4106 | } |
4107 | /*---------------------------------------------------------------------------*/ | |
7dcef374 | 4108 | case 2: { |
6888393c | 4109 | if (peasycap->purb_audio_head) { |
7dcef374 TW |
4110 | JOM(4, "killing audio urbs\n"); |
4111 | m = 0; | |
a90f3620 | 4112 | list_for_each(plist_head, peasycap->purb_audio_head) { |
7dcef374 TW |
4113 | pdata_urb = list_entry(plist_head, |
4114 | struct data_urb, list_head); | |
6888393c TW |
4115 | if (pdata_urb) { |
4116 | if (pdata_urb->purb) { | |
7dcef374 TW |
4117 | usb_kill_urb(pdata_urb->purb); |
4118 | m++; | |
4119 | } | |
702422bd T |
4120 | } |
4121 | } | |
7dcef374 | 4122 | JOM(4, "%i audio urbs killed\n", m); |
e68703cf | 4123 | } |
7dcef374 | 4124 | break; |
702422bd | 4125 | } |
7dcef374 TW |
4126 | default: |
4127 | break; | |
c7506658 | 4128 | } |
702422bd T |
4129 | /*--------------------------------------------------------------------------*/ |
4130 | /* | |
4131 | * DEREGISTER | |
ae59dad4 MT |
4132 | * |
4133 | * THIS PROCEDURE WILL BLOCK UNTIL easycap_poll(), VIDEO IOCTL AND AUDIO | |
4134 | * IOCTL ARE ALL UNLOCKED. IF THIS IS NOT DONE AN Oops CAN OCCUR WHEN | |
4135 | * AN EasyCAP IS UNPLUGGED WHILE THE URBS ARE RUNNING. BEWARE. | |
702422bd T |
4136 | */ |
4137 | /*--------------------------------------------------------------------------*/ | |
c7506658 TW |
4138 | kd = isdongle(peasycap); |
4139 | switch (bInterfaceNumber) { | |
4140 | case 0: { | |
4141 | if (0 <= kd && DONGLE_MANY > kd) { | |
4142 | wake_up_interruptible(&peasycap->wq_video); | |
4143 | JOM(4, "about to lock dongle[%i].mutex_video\n", kd); | |
4144 | if (mutex_lock_interruptible(&easycapdc60_dongle[kd]. | |
ae59dad4 | 4145 | mutex_video)) { |
c7506658 TW |
4146 | SAY("ERROR: " |
4147 | "cannot lock dongle[%i].mutex_video\n", kd); | |
4148 | return; | |
4149 | } | |
4150 | JOM(4, "locked dongle[%i].mutex_video\n", kd); | |
4151 | } else { | |
4152 | SAY("ERROR: %i=kd is bad: cannot lock dongle\n", kd); | |
ae59dad4 | 4153 | } |
ae59dad4 | 4154 | /*---------------------------------------------------------------------------*/ |
c7506658 TW |
4155 | if (!peasycap->v4l2_device.name[0]) { |
4156 | SAM("ERROR: peasycap->v4l2_device.name is empty\n"); | |
4157 | if (0 <= kd && DONGLE_MANY > kd) | |
4158 | mutex_unlock(&easycapdc60_dongle[kd].mutex_video); | |
4159 | return; | |
4160 | } | |
4161 | v4l2_device_disconnect(&peasycap->v4l2_device); | |
4162 | JOM(4, "v4l2_device_disconnect() OK\n"); | |
4163 | v4l2_device_unregister(&peasycap->v4l2_device); | |
4164 | JOM(4, "v4l2_device_unregister() OK\n"); | |
4165 | ||
4166 | video_unregister_device(&peasycap->video_device); | |
4167 | JOM(4, "intf[%i]: video_unregister_device() minor=%i\n", | |
4168 | bInterfaceNumber, minor); | |
4169 | peasycap->registered_video--; | |
268dfede MT |
4170 | /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ |
4171 | ||
c7506658 TW |
4172 | if (0 <= kd && DONGLE_MANY > kd) { |
4173 | mutex_unlock(&easycapdc60_dongle[kd].mutex_video); | |
4174 | JOM(4, "unlocked dongle[%i].mutex_video\n", kd); | |
4175 | } | |
4176 | break; | |
ae59dad4 | 4177 | } |
c7506658 TW |
4178 | case 2: { |
4179 | if (0 <= kd && DONGLE_MANY > kd) { | |
4180 | wake_up_interruptible(&peasycap->wq_audio); | |
4181 | JOM(4, "about to lock dongle[%i].mutex_audio\n", kd); | |
4182 | if (mutex_lock_interruptible(&easycapdc60_dongle[kd]. | |
ae59dad4 | 4183 | mutex_audio)) { |
c7506658 TW |
4184 | SAY("ERROR: " |
4185 | "cannot lock dongle[%i].mutex_audio\n", kd); | |
4186 | return; | |
4187 | } | |
4188 | JOM(4, "locked dongle[%i].mutex_audio\n", kd); | |
4189 | } else | |
4190 | SAY("ERROR: %i=kd is bad: cannot lock dongle\n", kd); | |
c7506658 TW |
4191 | if (0 != snd_card_free(peasycap->psnd_card)) { |
4192 | SAY("ERROR: snd_card_free() failed\n"); | |
4193 | } else { | |
4194 | peasycap->psnd_card = NULL; | |
4195 | (peasycap->registered_audio)--; | |
4196 | } | |
c7506658 TW |
4197 | if (0 <= kd && DONGLE_MANY > kd) { |
4198 | mutex_unlock(&easycapdc60_dongle[kd].mutex_audio); | |
4199 | JOM(4, "unlocked dongle[%i].mutex_audio\n", kd); | |
4200 | } | |
4201 | break; | |
4202 | } | |
4203 | default: | |
4204 | break; | |
ae59dad4 | 4205 | } |
702422bd T |
4206 | /*---------------------------------------------------------------------------*/ |
4207 | /* | |
4208 | * CALL easycap_delete() IF NO REMAINING REFERENCES TO peasycap | |
a9855917 | 4209 | * (ALSO WHEN ALSA HAS BEEN IN USE) |
702422bd T |
4210 | */ |
4211 | /*---------------------------------------------------------------------------*/ | |
c7506658 TW |
4212 | if (!peasycap->kref.refcount.counter) { |
4213 | SAM("ERROR: peasycap->kref.refcount.counter is zero " | |
4214 | "so cannot call kref_put()\n"); | |
ae59dad4 MT |
4215 | SAM("ending unsuccessfully: may cause memory leak\n"); |
4216 | return; | |
4217 | } | |
c7506658 TW |
4218 | if (0 <= kd && DONGLE_MANY > kd) { |
4219 | JOM(4, "about to lock dongle[%i].mutex_video\n", kd); | |
4220 | if (mutex_lock_interruptible(&easycapdc60_dongle[kd].mutex_video)) { | |
4221 | SAY("ERROR: cannot lock dongle[%i].mutex_video\n", kd); | |
4222 | SAM("ending unsuccessfully: may cause memory leak\n"); | |
4223 | return; | |
4224 | } | |
4225 | JOM(4, "locked dongle[%i].mutex_video\n", kd); | |
4226 | JOM(4, "about to lock dongle[%i].mutex_audio\n", kd); | |
4227 | if (mutex_lock_interruptible(&easycapdc60_dongle[kd].mutex_audio)) { | |
4228 | SAY("ERROR: cannot lock dongle[%i].mutex_audio\n", kd); | |
4229 | mutex_unlock(&(easycapdc60_dongle[kd].mutex_video)); | |
4230 | JOM(4, "unlocked dongle[%i].mutex_video\n", kd); | |
4231 | SAM("ending unsuccessfully: may cause memory leak\n"); | |
4232 | return; | |
4233 | } | |
4234 | JOM(4, "locked dongle[%i].mutex_audio\n", kd); | |
4235 | } | |
4236 | JOM(4, "intf[%i]: %i=peasycap->kref.refcount.counter\n", | |
4237 | bInterfaceNumber, (int)peasycap->kref.refcount.counter); | |
4238 | kref_put(&peasycap->kref, easycap_delete); | |
4239 | JOT(4, "intf[%i]: kref_put() done.\n", bInterfaceNumber); | |
4240 | if (0 <= kd && DONGLE_MANY > kd) { | |
4241 | mutex_unlock(&(easycapdc60_dongle[kd].mutex_audio)); | |
4242 | JOT(4, "unlocked dongle[%i].mutex_audio\n", kd); | |
4243 | mutex_unlock(&easycapdc60_dongle[kd].mutex_video); | |
4244 | JOT(4, "unlocked dongle[%i].mutex_video\n", kd); | |
4245 | } | |
702422bd | 4246 | /*---------------------------------------------------------------------------*/ |
c7506658 TW |
4247 | JOM(4, "ends\n"); |
4248 | return; | |
702422bd T |
4249 | } |
4250 | /*****************************************************************************/ | |
a9855917 | 4251 | |
702422bd T |
4252 | /*---------------------------------------------------------------------------*/ |
4253 | /* | |
d090bf57 | 4254 | * PARAMETERS APPLICABLE TO ENTIRE DRIVER, I.E. BOTH VIDEO AND AUDIO |
702422bd T |
4255 | */ |
4256 | /*---------------------------------------------------------------------------*/ | |
d090bf57 TW |
4257 | static struct usb_device_id easycap_usb_device_id_table[] = { |
4258 | {USB_DEVICE(USB_EASYCAP_VENDOR_ID, USB_EASYCAP_PRODUCT_ID)}, | |
4259 | { } | |
4260 | }; | |
4261 | ||
4262 | MODULE_DEVICE_TABLE(usb, easycap_usb_device_id_table); | |
4263 | struct usb_driver easycap_usb_driver = { | |
4264 | .name = "easycap", | |
4265 | .id_table = easycap_usb_device_id_table, | |
4266 | .probe = easycap_usb_probe, | |
4267 | .disconnect = easycap_usb_disconnect, | |
4268 | }; | |
702422bd | 4269 | |
d090bf57 TW |
4270 | static int __init easycap_module_init(void) |
4271 | { | |
4272 | int k, rc; | |
4273 | ||
aff512c8 TW |
4274 | printk(KERN_INFO "Easycap version: "EASYCAP_DRIVER_VERSION "\n"); |
4275 | ||
d090bf57 TW |
4276 | JOT(4, "begins. %i=debug %i=bars %i=gain\n", |
4277 | easycap_debug, easycap_bars, easycap_gain); | |
d090bf57 TW |
4278 | |
4279 | mutex_init(&mutex_dongle); | |
4280 | for (k = 0; k < DONGLE_MANY; k++) { | |
3c1fb66e | 4281 | easycapdc60_dongle[k].peasycap = NULL; |
d090bf57 TW |
4282 | mutex_init(&easycapdc60_dongle[k].mutex_video); |
4283 | mutex_init(&easycapdc60_dongle[k].mutex_audio); | |
4284 | } | |
d090bf57 | 4285 | rc = usb_register(&easycap_usb_driver); |
6911e7e4 | 4286 | if (rc) |
aff512c8 | 4287 | printk(KERN_ERR "Easycap: usb_register failed rc=%d\n", rc); |
d090bf57 | 4288 | |
d090bf57 | 4289 | return rc; |
702422bd T |
4290 | } |
4291 | /*****************************************************************************/ | |
dbf4805e | 4292 | static void __exit easycap_module_exit(void) |
702422bd | 4293 | { |
d090bf57 | 4294 | usb_deregister(&easycap_usb_driver); |
702422bd T |
4295 | } |
4296 | /*****************************************************************************/ | |
4297 | ||
4298 | module_init(easycap_module_init); | |
4299 | module_exit(easycap_module_exit); | |
4300 | ||
702422bd | 4301 | /*****************************************************************************/ |