blob: b5db6a5bab31e94b5338feb1b788ee14b129eb6e [file] [log] [blame]
Mike Iselyd8554972006-06-26 20:58:46 -03001/*
2 * $Id$
3 *
4 * Copyright (C) 2005 Mike Isely <isely@pobox.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
20
21#include "pvrusb2-context.h"
22#include "pvrusb2-io.h"
23#include "pvrusb2-ioread.h"
24#include "pvrusb2-hdw.h"
25#include "pvrusb2-debug.h"
Mike Iselye5be15c2008-04-07 02:22:04 -030026#include <linux/wait.h>
Mike Isely794b1602008-04-22 14:45:45 -030027#include <linux/kthread.h>
Mike Iselyd8554972006-06-26 20:58:46 -030028#include <linux/errno.h>
29#include <linux/string.h>
30#include <linux/slab.h>
Mike Iselyd8554972006-06-26 20:58:46 -030031
Mike Iselye5be15c2008-04-07 02:22:04 -030032static struct pvr2_context *pvr2_context_exist_first;
33static struct pvr2_context *pvr2_context_exist_last;
34static struct pvr2_context *pvr2_context_notify_first;
35static struct pvr2_context *pvr2_context_notify_last;
36static DEFINE_MUTEX(pvr2_context_mutex);
37static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_sync_data);
Mike Isely18ecbb42008-04-09 05:14:11 -030038static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_cleanup_data);
39static int pvr2_context_cleanup_flag;
40static int pvr2_context_cleaned_flag;
Mike Iselye5be15c2008-04-07 02:22:04 -030041static struct task_struct *pvr2_context_thread_ptr;
42
43
44static void pvr2_context_set_notify(struct pvr2_context *mp, int fl)
45{
46 int signal_flag = 0;
47 mutex_lock(&pvr2_context_mutex);
48 if (fl) {
49 if (!mp->notify_flag) {
50 signal_flag = (pvr2_context_notify_first == NULL);
51 mp->notify_prev = pvr2_context_notify_last;
52 mp->notify_next = NULL;
53 pvr2_context_notify_last = mp;
54 if (mp->notify_prev) {
55 mp->notify_prev->notify_next = mp;
56 } else {
57 pvr2_context_notify_first = mp;
58 }
59 mp->notify_flag = !0;
60 }
61 } else {
62 if (mp->notify_flag) {
63 mp->notify_flag = 0;
64 if (mp->notify_next) {
65 mp->notify_next->notify_prev = mp->notify_prev;
66 } else {
67 pvr2_context_notify_last = mp->notify_prev;
68 }
69 if (mp->notify_prev) {
70 mp->notify_prev->notify_next = mp->notify_next;
71 } else {
72 pvr2_context_notify_first = mp->notify_next;
73 }
74 }
75 }
76 mutex_unlock(&pvr2_context_mutex);
77 if (signal_flag) wake_up(&pvr2_context_sync_data);
78}
79
Mike Iselyd8554972006-06-26 20:58:46 -030080
81static void pvr2_context_destroy(struct pvr2_context *mp)
82{
Mike Isely794b1602008-04-22 14:45:45 -030083 pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (destroy)",mp);
Mike Isely681c7392007-11-26 01:48:52 -030084 if (mp->hdw) pvr2_hdw_destroy(mp->hdw);
Mike Iselye5be15c2008-04-07 02:22:04 -030085 pvr2_context_set_notify(mp, 0);
86 mutex_lock(&pvr2_context_mutex);
87 if (mp->exist_next) {
88 mp->exist_next->exist_prev = mp->exist_prev;
89 } else {
90 pvr2_context_exist_last = mp->exist_prev;
91 }
92 if (mp->exist_prev) {
93 mp->exist_prev->exist_next = mp->exist_next;
94 } else {
95 pvr2_context_exist_first = mp->exist_next;
96 }
97 if (!pvr2_context_exist_first) {
98 /* Trigger wakeup on control thread in case it is waiting
99 for an exit condition. */
100 wake_up(&pvr2_context_sync_data);
101 }
102 mutex_unlock(&pvr2_context_mutex);
Mike Iselyd8554972006-06-26 20:58:46 -0300103 kfree(mp);
104}
105
106
Mike Isely794b1602008-04-22 14:45:45 -0300107static void pvr2_context_notify(struct pvr2_context *mp)
Mike Iselyd8554972006-06-26 20:58:46 -0300108{
Mike Iselye5be15c2008-04-07 02:22:04 -0300109 pvr2_context_set_notify(mp,!0);
Mike Isely794b1602008-04-22 14:45:45 -0300110}
Mike Iselyd8554972006-06-26 20:58:46 -0300111
Mike Isely794b1602008-04-22 14:45:45 -0300112
Mike Iselye5be15c2008-04-07 02:22:04 -0300113static void pvr2_context_check(struct pvr2_context *mp)
Mike Isely794b1602008-04-22 14:45:45 -0300114{
Mike Iselye5be15c2008-04-07 02:22:04 -0300115 struct pvr2_channel *ch1, *ch2;
116 pvr2_trace(PVR2_TRACE_CTXT,
117 "pvr2_context %p (notify)", mp);
118 if (!mp->initialized_flag && !mp->disconnect_flag) {
119 mp->initialized_flag = !0;
Mike Isely794b1602008-04-22 14:45:45 -0300120 pvr2_trace(PVR2_TRACE_CTXT,
Mike Iselye5be15c2008-04-07 02:22:04 -0300121 "pvr2_context %p (initialize)", mp);
122 /* Finish hardware initialization */
123 if (pvr2_hdw_initialize(mp->hdw,
124 (void (*)(void *))pvr2_context_notify,
125 mp)) {
126 mp->video_stream.stream =
127 pvr2_hdw_get_video_stream(mp->hdw);
128 /* Trigger interface initialization. By doing this
129 here initialization runs in our own safe and
130 cozy thread context. */
131 if (mp->setup_func) mp->setup_func(mp);
132 } else {
133 pvr2_trace(PVR2_TRACE_CTXT,
134 "pvr2_context %p (thread skipping setup)",
135 mp);
136 /* Even though initialization did not succeed,
137 we're still going to continue anyway. We need
138 to do this in order to await the expected
139 disconnect (which we will detect in the normal
140 course of operation). */
141 }
Mike Isely681c7392007-11-26 01:48:52 -0300142 }
David Howellsc4028952006-11-22 14:57:56 +0000143
Mike Iselye5be15c2008-04-07 02:22:04 -0300144 for (ch1 = mp->mc_first; ch1; ch1 = ch2) {
145 ch2 = ch1->mc_next;
146 if (ch1->check_func) ch1->check_func(ch1);
Mike Isely794b1602008-04-22 14:45:45 -0300147 }
Mike Iselye5be15c2008-04-07 02:22:04 -0300148
149 if (mp->disconnect_flag && !mp->mc_first) {
150 /* Go away... */
151 pvr2_context_destroy(mp);
152 return;
153 }
154}
155
156
157static int pvr2_context_shutok(void)
158{
Mike Isely18ecbb42008-04-09 05:14:11 -0300159 return pvr2_context_cleanup_flag && (pvr2_context_exist_first == NULL);
Mike Iselye5be15c2008-04-07 02:22:04 -0300160}
161
162
163static int pvr2_context_thread_func(void *foo)
164{
165 struct pvr2_context *mp;
166
167 pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread start");
168
169 do {
170 while ((mp = pvr2_context_notify_first) != NULL) {
171 pvr2_context_set_notify(mp, 0);
172 pvr2_context_check(mp);
173 }
174 wait_event_interruptible(
175 pvr2_context_sync_data,
176 ((pvr2_context_notify_first != NULL) ||
177 pvr2_context_shutok()));
178 } while (!pvr2_context_shutok());
179
Mike Isely18ecbb42008-04-09 05:14:11 -0300180 pvr2_context_cleaned_flag = !0;
181 wake_up(&pvr2_context_cleanup_data);
182
183 pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread cleaned up");
184
185 wait_event_interruptible(
186 pvr2_context_sync_data,
187 kthread_should_stop());
188
Mike Iselye5be15c2008-04-07 02:22:04 -0300189 pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread end");
190
Mike Isely794b1602008-04-22 14:45:45 -0300191 return 0;
192}
Mike Iselyd8554972006-06-26 20:58:46 -0300193
194
Mike Iselye5be15c2008-04-07 02:22:04 -0300195int pvr2_context_global_init(void)
196{
197 pvr2_context_thread_ptr = kthread_run(pvr2_context_thread_func,
198 0,
199 "pvrusb2-context");
200 return (pvr2_context_thread_ptr ? 0 : -ENOMEM);
201}
202
203
204void pvr2_context_global_done(void)
205{
Mike Isely18ecbb42008-04-09 05:14:11 -0300206 pvr2_context_cleanup_flag = !0;
207 wake_up(&pvr2_context_sync_data);
208 wait_event_interruptible(
209 pvr2_context_cleanup_data,
210 pvr2_context_cleaned_flag);
Mike Iselye5be15c2008-04-07 02:22:04 -0300211 kthread_stop(pvr2_context_thread_ptr);
212}
213
214
Mike Iselyd8554972006-06-26 20:58:46 -0300215struct pvr2_context *pvr2_context_create(
216 struct usb_interface *intf,
217 const struct usb_device_id *devid,
218 void (*setup_func)(struct pvr2_context *))
219{
Mike Iselya0fd1cb2006-06-30 11:35:28 -0300220 struct pvr2_context *mp = NULL;
Mike Iselyca545f72007-01-20 00:37:11 -0300221 mp = kzalloc(sizeof(*mp),GFP_KERNEL);
Mike Iselyd8554972006-06-26 20:58:46 -0300222 if (!mp) goto done;
Mike Isely794b1602008-04-22 14:45:45 -0300223 pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (create)",mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300224 mp->setup_func = setup_func;
225 mutex_init(&mp->mutex);
Mike Iselye5be15c2008-04-07 02:22:04 -0300226 mutex_lock(&pvr2_context_mutex);
227 mp->exist_prev = pvr2_context_exist_last;
228 mp->exist_next = NULL;
229 pvr2_context_exist_last = mp;
230 if (mp->exist_prev) {
231 mp->exist_prev->exist_next = mp;
232 } else {
233 pvr2_context_exist_first = mp;
234 }
235 mutex_unlock(&pvr2_context_mutex);
Mike Iselyd8554972006-06-26 20:58:46 -0300236 mp->hdw = pvr2_hdw_create(intf,devid);
237 if (!mp->hdw) {
238 pvr2_context_destroy(mp);
Mike Iselya0fd1cb2006-06-30 11:35:28 -0300239 mp = NULL;
Mike Iselyd8554972006-06-26 20:58:46 -0300240 goto done;
241 }
Mike Iselye5be15c2008-04-07 02:22:04 -0300242 pvr2_context_set_notify(mp, !0);
Mike Iselyd8554972006-06-26 20:58:46 -0300243 done:
244 return mp;
245}
246
247
Mike Isely1cb03b72008-04-21 03:47:43 -0300248static void pvr2_context_reset_input_limits(struct pvr2_context *mp)
249{
250 unsigned int tmsk,mmsk;
251 struct pvr2_channel *cp;
252 struct pvr2_hdw *hdw = mp->hdw;
253 mmsk = pvr2_hdw_get_input_available(hdw);
254 tmsk = mmsk;
255 for (cp = mp->mc_first; cp; cp = cp->mc_next) {
256 if (!cp->input_mask) continue;
257 tmsk &= cp->input_mask;
258 }
259 pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk);
260 pvr2_hdw_commit_ctl(hdw);
261}
262
263
Mike Isely794b1602008-04-22 14:45:45 -0300264static void pvr2_context_enter(struct pvr2_context *mp)
Mike Iselyd8554972006-06-26 20:58:46 -0300265{
266 mutex_lock(&mp->mutex);
Mike Iselyd8554972006-06-26 20:58:46 -0300267}
268
269
Mike Isely794b1602008-04-22 14:45:45 -0300270static void pvr2_context_exit(struct pvr2_context *mp)
Mike Iselyd8554972006-06-26 20:58:46 -0300271{
272 int destroy_flag = 0;
273 if (!(mp->mc_first || !mp->disconnect_flag)) {
274 destroy_flag = !0;
275 }
Mike Iselyd8554972006-06-26 20:58:46 -0300276 mutex_unlock(&mp->mutex);
Mike Isely794b1602008-04-22 14:45:45 -0300277 if (destroy_flag) pvr2_context_notify(mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300278}
279
280
281void pvr2_context_disconnect(struct pvr2_context *mp)
282{
Mike Isely794b1602008-04-22 14:45:45 -0300283 pvr2_hdw_disconnect(mp->hdw);
284 mp->disconnect_flag = !0;
285 pvr2_context_notify(mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300286}
287
288
289void pvr2_channel_init(struct pvr2_channel *cp,struct pvr2_context *mp)
290{
Mike Isely794b1602008-04-22 14:45:45 -0300291 pvr2_context_enter(mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300292 cp->hdw = mp->hdw;
293 cp->mc_head = mp;
Mike Iselya0fd1cb2006-06-30 11:35:28 -0300294 cp->mc_next = NULL;
Mike Iselyd8554972006-06-26 20:58:46 -0300295 cp->mc_prev = mp->mc_last;
296 if (mp->mc_last) {
297 mp->mc_last->mc_next = cp;
298 } else {
299 mp->mc_first = cp;
300 }
301 mp->mc_last = cp;
Mike Isely794b1602008-04-22 14:45:45 -0300302 pvr2_context_exit(mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300303}
304
305
306static void pvr2_channel_disclaim_stream(struct pvr2_channel *cp)
307{
308 if (!cp->stream) return;
309 pvr2_stream_kill(cp->stream->stream);
Mike Iselya0fd1cb2006-06-30 11:35:28 -0300310 cp->stream->user = NULL;
311 cp->stream = NULL;
Mike Iselyd8554972006-06-26 20:58:46 -0300312}
313
314
315void pvr2_channel_done(struct pvr2_channel *cp)
316{
317 struct pvr2_context *mp = cp->mc_head;
Mike Isely794b1602008-04-22 14:45:45 -0300318 pvr2_context_enter(mp);
Mike Isely1cb03b72008-04-21 03:47:43 -0300319 cp->input_mask = 0;
Mike Iselyd8554972006-06-26 20:58:46 -0300320 pvr2_channel_disclaim_stream(cp);
Mike Isely1cb03b72008-04-21 03:47:43 -0300321 pvr2_context_reset_input_limits(mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300322 if (cp->mc_next) {
323 cp->mc_next->mc_prev = cp->mc_prev;
324 } else {
325 mp->mc_last = cp->mc_prev;
326 }
327 if (cp->mc_prev) {
328 cp->mc_prev->mc_next = cp->mc_next;
329 } else {
330 mp->mc_first = cp->mc_next;
331 }
Mike Iselya0fd1cb2006-06-30 11:35:28 -0300332 cp->hdw = NULL;
Mike Isely794b1602008-04-22 14:45:45 -0300333 pvr2_context_exit(mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300334}
335
336
Mike Isely1cb03b72008-04-21 03:47:43 -0300337int pvr2_channel_limit_inputs(struct pvr2_channel *cp,unsigned int cmsk)
338{
339 unsigned int tmsk,mmsk;
340 int ret = 0;
341 struct pvr2_channel *p2;
342 struct pvr2_hdw *hdw = cp->hdw;
343
344 mmsk = pvr2_hdw_get_input_available(hdw);
345 cmsk &= mmsk;
346 if (cmsk == cp->input_mask) {
347 /* No change; nothing to do */
348 return 0;
349 }
350
351 pvr2_context_enter(cp->mc_head);
352 do {
353 if (!cmsk) {
354 cp->input_mask = 0;
355 pvr2_context_reset_input_limits(cp->mc_head);
356 break;
357 }
358 tmsk = mmsk;
359 for (p2 = cp->mc_head->mc_first; p2; p2 = p2->mc_next) {
360 if (p2 == cp) continue;
361 if (!p2->input_mask) continue;
362 tmsk &= p2->input_mask;
363 }
364 if (!(tmsk & cmsk)) {
365 ret = -EPERM;
366 break;
367 }
368 tmsk &= cmsk;
369 if ((ret = pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk)) != 0) {
370 /* Internal failure changing allowed list; probably
371 should not happen, but react if it does. */
372 break;
373 }
374 cp->input_mask = cmsk;
375 pvr2_hdw_commit_ctl(hdw);
376 } while (0);
377 pvr2_context_exit(cp->mc_head);
378 return ret;
379}
380
381
382unsigned int pvr2_channel_get_limited_inputs(struct pvr2_channel *cp)
383{
384 return cp->input_mask;
385}
386
387
Mike Iselyd8554972006-06-26 20:58:46 -0300388int pvr2_channel_claim_stream(struct pvr2_channel *cp,
389 struct pvr2_context_stream *sp)
390{
391 int code = 0;
392 pvr2_context_enter(cp->mc_head); do {
393 if (sp == cp->stream) break;
Mike Isely99a6acf2008-04-22 14:45:39 -0300394 if (sp && sp->user) {
Mike Iselyd8554972006-06-26 20:58:46 -0300395 code = -EBUSY;
396 break;
397 }
398 pvr2_channel_disclaim_stream(cp);
399 if (!sp) break;
400 sp->user = cp;
401 cp->stream = sp;
402 } while (0); pvr2_context_exit(cp->mc_head);
403 return code;
404}
405
406
407// This is the marker for the real beginning of a legitimate mpeg2 stream.
408static char stream_sync_key[] = {
409 0x00, 0x00, 0x01, 0xba,
410};
411
412struct pvr2_ioread *pvr2_channel_create_mpeg_stream(
413 struct pvr2_context_stream *sp)
414{
415 struct pvr2_ioread *cp;
416 cp = pvr2_ioread_create();
Mike Iselya0fd1cb2006-06-30 11:35:28 -0300417 if (!cp) return NULL;
Mike Iselyd8554972006-06-26 20:58:46 -0300418 pvr2_ioread_setup(cp,sp->stream);
419 pvr2_ioread_set_sync_key(cp,stream_sync_key,sizeof(stream_sync_key));
420 return cp;
421}
422
423
424/*
425 Stuff for Emacs to see, in order to encourage consistent editing style:
426 *** Local Variables: ***
427 *** mode: c ***
428 *** fill-column: 75 ***
429 *** tab-width: 8 ***
430 *** c-basic-offset: 8 ***
431 *** End: ***
432 */