]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - drivers/media/usb/pvrusb2/pvrusb2-context.c
Merge branches 'for-4.11/upstream-fixes', 'for-4.12/accutouch', 'for-4.12/cp2112...
[mirror_ubuntu-artful-kernel.git] / drivers / media / usb / pvrusb2 / pvrusb2-context.c
CommitLineData
d855497e 1/*
d855497e
MI
2 *
3 * Copyright (C) 2005 Mike Isely <isely@pobox.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
d855497e
MI
14 */
15
16#include "pvrusb2-context.h"
17#include "pvrusb2-io.h"
18#include "pvrusb2-ioread.h"
19#include "pvrusb2-hdw.h"
20#include "pvrusb2-debug.h"
e5be15c6 21#include <linux/wait.h>
794b1607 22#include <linux/kthread.h>
d855497e
MI
23#include <linux/errno.h>
24#include <linux/string.h>
25#include <linux/slab.h>
d855497e 26
e5be15c6
MI
27static struct pvr2_context *pvr2_context_exist_first;
28static struct pvr2_context *pvr2_context_exist_last;
29static struct pvr2_context *pvr2_context_notify_first;
30static struct pvr2_context *pvr2_context_notify_last;
31static DEFINE_MUTEX(pvr2_context_mutex);
32static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_sync_data);
18ecbb47
MI
33static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_cleanup_data);
34static int pvr2_context_cleanup_flag;
35static int pvr2_context_cleaned_flag;
e5be15c6
MI
36static struct task_struct *pvr2_context_thread_ptr;
37
38
39static void pvr2_context_set_notify(struct pvr2_context *mp, int fl)
40{
41 int signal_flag = 0;
42 mutex_lock(&pvr2_context_mutex);
43 if (fl) {
44 if (!mp->notify_flag) {
45 signal_flag = (pvr2_context_notify_first == NULL);
46 mp->notify_prev = pvr2_context_notify_last;
47 mp->notify_next = NULL;
48 pvr2_context_notify_last = mp;
49 if (mp->notify_prev) {
50 mp->notify_prev->notify_next = mp;
51 } else {
52 pvr2_context_notify_first = mp;
53 }
54 mp->notify_flag = !0;
55 }
56 } else {
57 if (mp->notify_flag) {
58 mp->notify_flag = 0;
59 if (mp->notify_next) {
60 mp->notify_next->notify_prev = mp->notify_prev;
61 } else {
62 pvr2_context_notify_last = mp->notify_prev;
63 }
64 if (mp->notify_prev) {
65 mp->notify_prev->notify_next = mp->notify_next;
66 } else {
67 pvr2_context_notify_first = mp->notify_next;
68 }
69 }
70 }
71 mutex_unlock(&pvr2_context_mutex);
72 if (signal_flag) wake_up(&pvr2_context_sync_data);
73}
74
d855497e
MI
75
76static void pvr2_context_destroy(struct pvr2_context *mp)
77{
794b1607 78 pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (destroy)",mp);
83f56f7c 79 pvr2_hdw_destroy(mp->hdw);
e5be15c6
MI
80 pvr2_context_set_notify(mp, 0);
81 mutex_lock(&pvr2_context_mutex);
82 if (mp->exist_next) {
83 mp->exist_next->exist_prev = mp->exist_prev;
84 } else {
85 pvr2_context_exist_last = mp->exist_prev;
86 }
87 if (mp->exist_prev) {
88 mp->exist_prev->exist_next = mp->exist_next;
89 } else {
90 pvr2_context_exist_first = mp->exist_next;
91 }
92 if (!pvr2_context_exist_first) {
93 /* Trigger wakeup on control thread in case it is waiting
94 for an exit condition. */
95 wake_up(&pvr2_context_sync_data);
96 }
97 mutex_unlock(&pvr2_context_mutex);
d855497e
MI
98 kfree(mp);
99}
100
101
794b1607 102static void pvr2_context_notify(struct pvr2_context *mp)
d855497e 103{
e5be15c6 104 pvr2_context_set_notify(mp,!0);
794b1607
MI
105}
106
107
e5be15c6 108static void pvr2_context_check(struct pvr2_context *mp)
794b1607 109{
e5be15c6
MI
110 struct pvr2_channel *ch1, *ch2;
111 pvr2_trace(PVR2_TRACE_CTXT,
112 "pvr2_context %p (notify)", mp);
113 if (!mp->initialized_flag && !mp->disconnect_flag) {
114 mp->initialized_flag = !0;
794b1607 115 pvr2_trace(PVR2_TRACE_CTXT,
e5be15c6
MI
116 "pvr2_context %p (initialize)", mp);
117 /* Finish hardware initialization */
118 if (pvr2_hdw_initialize(mp->hdw,
119 (void (*)(void *))pvr2_context_notify,
120 mp)) {
121 mp->video_stream.stream =
122 pvr2_hdw_get_video_stream(mp->hdw);
123 /* Trigger interface initialization. By doing this
124 here initialization runs in our own safe and
125 cozy thread context. */
126 if (mp->setup_func) mp->setup_func(mp);
127 } else {
794b1607 128 pvr2_trace(PVR2_TRACE_CTXT,
e5be15c6
MI
129 "pvr2_context %p (thread skipping setup)",
130 mp);
131 /* Even though initialization did not succeed,
132 we're still going to continue anyway. We need
133 to do this in order to await the expected
134 disconnect (which we will detect in the normal
135 course of operation). */
d855497e 136 }
794b1607 137 }
e5be15c6
MI
138
139 for (ch1 = mp->mc_first; ch1; ch1 = ch2) {
140 ch2 = ch1->mc_next;
141 if (ch1->check_func) ch1->check_func(ch1);
142 }
143
144 if (mp->disconnect_flag && !mp->mc_first) {
145 /* Go away... */
146 pvr2_context_destroy(mp);
147 return;
148 }
149}
150
151
152static int pvr2_context_shutok(void)
153{
18ecbb47 154 return pvr2_context_cleanup_flag && (pvr2_context_exist_first == NULL);
e5be15c6
MI
155}
156
157
158static int pvr2_context_thread_func(void *foo)
159{
160 struct pvr2_context *mp;
161
162 pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread start");
163
164 do {
165 while ((mp = pvr2_context_notify_first) != NULL) {
166 pvr2_context_set_notify(mp, 0);
167 pvr2_context_check(mp);
168 }
169 wait_event_interruptible(
170 pvr2_context_sync_data,
171 ((pvr2_context_notify_first != NULL) ||
172 pvr2_context_shutok()));
173 } while (!pvr2_context_shutok());
174
18ecbb47
MI
175 pvr2_context_cleaned_flag = !0;
176 wake_up(&pvr2_context_cleanup_data);
177
178 pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread cleaned up");
179
180 wait_event_interruptible(
181 pvr2_context_sync_data,
182 kthread_should_stop());
183
e5be15c6
MI
184 pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread end");
185
794b1607
MI
186 return 0;
187}
d855497e
MI
188
189
e5be15c6
MI
190int pvr2_context_global_init(void)
191{
192 pvr2_context_thread_ptr = kthread_run(pvr2_context_thread_func,
a6a3a17b 193 NULL,
e5be15c6 194 "pvrusb2-context");
bb07df8a 195 return IS_ERR(pvr2_context_thread_ptr) ? -ENOMEM : 0;
e5be15c6
MI
196}
197
198
199void pvr2_context_global_done(void)
200{
18ecbb47
MI
201 pvr2_context_cleanup_flag = !0;
202 wake_up(&pvr2_context_sync_data);
203 wait_event_interruptible(
204 pvr2_context_cleanup_data,
205 pvr2_context_cleaned_flag);
e5be15c6
MI
206 kthread_stop(pvr2_context_thread_ptr);
207}
208
209
d855497e
MI
210struct pvr2_context *pvr2_context_create(
211 struct usb_interface *intf,
212 const struct usb_device_id *devid,
213 void (*setup_func)(struct pvr2_context *))
214{
a0fd1cb1 215 struct pvr2_context *mp = NULL;
ca545f7c 216 mp = kzalloc(sizeof(*mp),GFP_KERNEL);
d855497e 217 if (!mp) goto done;
794b1607 218 pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (create)",mp);
d855497e
MI
219 mp->setup_func = setup_func;
220 mutex_init(&mp->mutex);
e5be15c6
MI
221 mutex_lock(&pvr2_context_mutex);
222 mp->exist_prev = pvr2_context_exist_last;
223 mp->exist_next = NULL;
224 pvr2_context_exist_last = mp;
225 if (mp->exist_prev) {
226 mp->exist_prev->exist_next = mp;
227 } else {
228 pvr2_context_exist_first = mp;
229 }
230 mutex_unlock(&pvr2_context_mutex);
d855497e
MI
231 mp->hdw = pvr2_hdw_create(intf,devid);
232 if (!mp->hdw) {
233 pvr2_context_destroy(mp);
a0fd1cb1 234 mp = NULL;
d855497e
MI
235 goto done;
236 }
e5be15c6 237 pvr2_context_set_notify(mp, !0);
d855497e
MI
238 done:
239 return mp;
240}
241
242
1cb03b76
MI
243static void pvr2_context_reset_input_limits(struct pvr2_context *mp)
244{
245 unsigned int tmsk,mmsk;
246 struct pvr2_channel *cp;
247 struct pvr2_hdw *hdw = mp->hdw;
248 mmsk = pvr2_hdw_get_input_available(hdw);
249 tmsk = mmsk;
250 for (cp = mp->mc_first; cp; cp = cp->mc_next) {
251 if (!cp->input_mask) continue;
252 tmsk &= cp->input_mask;
253 }
254 pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk);
255 pvr2_hdw_commit_ctl(hdw);
256}
257
258
794b1607 259static void pvr2_context_enter(struct pvr2_context *mp)
d855497e
MI
260{
261 mutex_lock(&mp->mutex);
d855497e
MI
262}
263
264
794b1607 265static void pvr2_context_exit(struct pvr2_context *mp)
d855497e
MI
266{
267 int destroy_flag = 0;
268 if (!(mp->mc_first || !mp->disconnect_flag)) {
269 destroy_flag = !0;
270 }
d855497e 271 mutex_unlock(&mp->mutex);
794b1607 272 if (destroy_flag) pvr2_context_notify(mp);
d855497e
MI
273}
274
275
276void pvr2_context_disconnect(struct pvr2_context *mp)
277{
794b1607
MI
278 pvr2_hdw_disconnect(mp->hdw);
279 mp->disconnect_flag = !0;
280 pvr2_context_notify(mp);
d855497e
MI
281}
282
283
284void pvr2_channel_init(struct pvr2_channel *cp,struct pvr2_context *mp)
285{
794b1607 286 pvr2_context_enter(mp);
d855497e
MI
287 cp->hdw = mp->hdw;
288 cp->mc_head = mp;
a0fd1cb1 289 cp->mc_next = NULL;
d855497e
MI
290 cp->mc_prev = mp->mc_last;
291 if (mp->mc_last) {
292 mp->mc_last->mc_next = cp;
293 } else {
294 mp->mc_first = cp;
295 }
296 mp->mc_last = cp;
794b1607 297 pvr2_context_exit(mp);
d855497e
MI
298}
299
300
301static void pvr2_channel_disclaim_stream(struct pvr2_channel *cp)
302{
303 if (!cp->stream) return;
304 pvr2_stream_kill(cp->stream->stream);
a0fd1cb1
MI
305 cp->stream->user = NULL;
306 cp->stream = NULL;
d855497e
MI
307}
308
309
310void pvr2_channel_done(struct pvr2_channel *cp)
311{
312 struct pvr2_context *mp = cp->mc_head;
794b1607 313 pvr2_context_enter(mp);
1cb03b76 314 cp->input_mask = 0;
d855497e 315 pvr2_channel_disclaim_stream(cp);
1cb03b76 316 pvr2_context_reset_input_limits(mp);
d855497e
MI
317 if (cp->mc_next) {
318 cp->mc_next->mc_prev = cp->mc_prev;
319 } else {
320 mp->mc_last = cp->mc_prev;
321 }
322 if (cp->mc_prev) {
323 cp->mc_prev->mc_next = cp->mc_next;
324 } else {
325 mp->mc_first = cp->mc_next;
326 }
a0fd1cb1 327 cp->hdw = NULL;
794b1607 328 pvr2_context_exit(mp);
d855497e
MI
329}
330
331
1cb03b76
MI
332int pvr2_channel_limit_inputs(struct pvr2_channel *cp,unsigned int cmsk)
333{
334 unsigned int tmsk,mmsk;
335 int ret = 0;
336 struct pvr2_channel *p2;
337 struct pvr2_hdw *hdw = cp->hdw;
338
339 mmsk = pvr2_hdw_get_input_available(hdw);
340 cmsk &= mmsk;
341 if (cmsk == cp->input_mask) {
342 /* No change; nothing to do */
343 return 0;
344 }
345
346 pvr2_context_enter(cp->mc_head);
347 do {
348 if (!cmsk) {
349 cp->input_mask = 0;
350 pvr2_context_reset_input_limits(cp->mc_head);
351 break;
352 }
353 tmsk = mmsk;
354 for (p2 = cp->mc_head->mc_first; p2; p2 = p2->mc_next) {
355 if (p2 == cp) continue;
356 if (!p2->input_mask) continue;
357 tmsk &= p2->input_mask;
358 }
359 if (!(tmsk & cmsk)) {
360 ret = -EPERM;
361 break;
362 }
363 tmsk &= cmsk;
364 if ((ret = pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk)) != 0) {
365 /* Internal failure changing allowed list; probably
366 should not happen, but react if it does. */
367 break;
368 }
369 cp->input_mask = cmsk;
370 pvr2_hdw_commit_ctl(hdw);
371 } while (0);
372 pvr2_context_exit(cp->mc_head);
373 return ret;
374}
375
376
377unsigned int pvr2_channel_get_limited_inputs(struct pvr2_channel *cp)
378{
379 return cp->input_mask;
380}
381
382
d855497e
MI
383int pvr2_channel_claim_stream(struct pvr2_channel *cp,
384 struct pvr2_context_stream *sp)
385{
386 int code = 0;
387 pvr2_context_enter(cp->mc_head); do {
388 if (sp == cp->stream) break;
99a6acf9 389 if (sp && sp->user) {
d855497e
MI
390 code = -EBUSY;
391 break;
392 }
393 pvr2_channel_disclaim_stream(cp);
394 if (!sp) break;
395 sp->user = cp;
396 cp->stream = sp;
f419edd4
MCC
397 } while (0);
398 pvr2_context_exit(cp->mc_head);
d855497e
MI
399 return code;
400}
401
402
403// This is the marker for the real beginning of a legitimate mpeg2 stream.
404static char stream_sync_key[] = {
405 0x00, 0x00, 0x01, 0xba,
406};
407
408struct pvr2_ioread *pvr2_channel_create_mpeg_stream(
409 struct pvr2_context_stream *sp)
410{
411 struct pvr2_ioread *cp;
412 cp = pvr2_ioread_create();
a0fd1cb1 413 if (!cp) return NULL;
d855497e
MI
414 pvr2_ioread_setup(cp,sp->stream);
415 pvr2_ioread_set_sync_key(cp,stream_sync_key,sizeof(stream_sync_key));
416 return cp;
417}