blob: 7c19ff72e6b3255b2dbacbc32e40b028e4f76140 [file] [log] [blame]
Mike Iselyd8554972006-06-26 20:58:46 -03001/*
Mike Iselyd8554972006-06-26 20:58:46 -03002 *
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 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 */
19
20#include "pvrusb2-context.h"
21#include "pvrusb2-io.h"
22#include "pvrusb2-ioread.h"
23#include "pvrusb2-hdw.h"
24#include "pvrusb2-debug.h"
Mike Iselye5be15c2008-04-07 02:22:04 -030025#include <linux/wait.h>
Mike Isely794b1602008-04-22 14:45:45 -030026#include <linux/kthread.h>
Mike Iselyd8554972006-06-26 20:58:46 -030027#include <linux/errno.h>
28#include <linux/string.h>
29#include <linux/slab.h>
Mike Iselyd8554972006-06-26 20:58:46 -030030
Mike Iselye5be15c2008-04-07 02:22:04 -030031static struct pvr2_context *pvr2_context_exist_first;
32static struct pvr2_context *pvr2_context_exist_last;
33static struct pvr2_context *pvr2_context_notify_first;
34static struct pvr2_context *pvr2_context_notify_last;
35static DEFINE_MUTEX(pvr2_context_mutex);
36static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_sync_data);
Mike Isely18ecbb42008-04-09 05:14:11 -030037static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_cleanup_data);
38static int pvr2_context_cleanup_flag;
39static int pvr2_context_cleaned_flag;
Mike Iselye5be15c2008-04-07 02:22:04 -030040static struct task_struct *pvr2_context_thread_ptr;
41
42
43static void pvr2_context_set_notify(struct pvr2_context *mp, int fl)
44{
45 int signal_flag = 0;
46 mutex_lock(&pvr2_context_mutex);
47 if (fl) {
48 if (!mp->notify_flag) {
49 signal_flag = (pvr2_context_notify_first == NULL);
50 mp->notify_prev = pvr2_context_notify_last;
51 mp->notify_next = NULL;
52 pvr2_context_notify_last = mp;
53 if (mp->notify_prev) {
54 mp->notify_prev->notify_next = mp;
55 } else {
56 pvr2_context_notify_first = mp;
57 }
58 mp->notify_flag = !0;
59 }
60 } else {
61 if (mp->notify_flag) {
62 mp->notify_flag = 0;
63 if (mp->notify_next) {
64 mp->notify_next->notify_prev = mp->notify_prev;
65 } else {
66 pvr2_context_notify_last = mp->notify_prev;
67 }
68 if (mp->notify_prev) {
69 mp->notify_prev->notify_next = mp->notify_next;
70 } else {
71 pvr2_context_notify_first = mp->notify_next;
72 }
73 }
74 }
75 mutex_unlock(&pvr2_context_mutex);
76 if (signal_flag) wake_up(&pvr2_context_sync_data);
77}
78
Mike Iselyd8554972006-06-26 20:58:46 -030079
80static void pvr2_context_destroy(struct pvr2_context *mp)
81{
Mike Isely794b1602008-04-22 14:45:45 -030082 pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (destroy)",mp);
Mike Isely681c7392007-11-26 01:48:52 -030083 if (mp->hdw) pvr2_hdw_destroy(mp->hdw);
Mike Iselye5be15c2008-04-07 02:22:04 -030084 pvr2_context_set_notify(mp, 0);
85 mutex_lock(&pvr2_context_mutex);
86 if (mp->exist_next) {
87 mp->exist_next->exist_prev = mp->exist_prev;
88 } else {
89 pvr2_context_exist_last = mp->exist_prev;
90 }
91 if (mp->exist_prev) {
92 mp->exist_prev->exist_next = mp->exist_next;
93 } else {
94 pvr2_context_exist_first = mp->exist_next;
95 }
96 if (!pvr2_context_exist_first) {
97 /* Trigger wakeup on control thread in case it is waiting
98 for an exit condition. */
99 wake_up(&pvr2_context_sync_data);
100 }
101 mutex_unlock(&pvr2_context_mutex);
Mike Iselyd8554972006-06-26 20:58:46 -0300102 kfree(mp);
103}
104
105
Mike Isely794b1602008-04-22 14:45:45 -0300106static void pvr2_context_notify(struct pvr2_context *mp)
Mike Iselyd8554972006-06-26 20:58:46 -0300107{
Mike Iselye5be15c2008-04-07 02:22:04 -0300108 pvr2_context_set_notify(mp,!0);
Mike Isely794b1602008-04-22 14:45:45 -0300109}
Mike Iselyd8554972006-06-26 20:58:46 -0300110
Mike Isely794b1602008-04-22 14:45:45 -0300111
Mike Iselye5be15c2008-04-07 02:22:04 -0300112static void pvr2_context_check(struct pvr2_context *mp)
Mike Isely794b1602008-04-22 14:45:45 -0300113{
Mike Iselye5be15c2008-04-07 02:22:04 -0300114 struct pvr2_channel *ch1, *ch2;
115 pvr2_trace(PVR2_TRACE_CTXT,
116 "pvr2_context %p (notify)", mp);
117 if (!mp->initialized_flag && !mp->disconnect_flag) {
118 mp->initialized_flag = !0;
Mike Isely794b1602008-04-22 14:45:45 -0300119 pvr2_trace(PVR2_TRACE_CTXT,
Mike Iselye5be15c2008-04-07 02:22:04 -0300120 "pvr2_context %p (initialize)", mp);
121 /* Finish hardware initialization */
122 if (pvr2_hdw_initialize(mp->hdw,
123 (void (*)(void *))pvr2_context_notify,
124 mp)) {
125 mp->video_stream.stream =
126 pvr2_hdw_get_video_stream(mp->hdw);
127 /* Trigger interface initialization. By doing this
128 here initialization runs in our own safe and
129 cozy thread context. */
130 if (mp->setup_func) mp->setup_func(mp);
131 } else {
132 pvr2_trace(PVR2_TRACE_CTXT,
133 "pvr2_context %p (thread skipping setup)",
134 mp);
135 /* Even though initialization did not succeed,
136 we're still going to continue anyway. We need
137 to do this in order to await the expected
138 disconnect (which we will detect in the normal
139 course of operation). */
140 }
Mike Isely681c7392007-11-26 01:48:52 -0300141 }
David Howellsc4028952006-11-22 14:57:56 +0000142
Mike Iselye5be15c2008-04-07 02:22:04 -0300143 for (ch1 = mp->mc_first; ch1; ch1 = ch2) {
144 ch2 = ch1->mc_next;
145 if (ch1->check_func) ch1->check_func(ch1);
Mike Isely794b1602008-04-22 14:45:45 -0300146 }
Mike Iselye5be15c2008-04-07 02:22:04 -0300147
148 if (mp->disconnect_flag && !mp->mc_first) {
149 /* Go away... */
150 pvr2_context_destroy(mp);
151 return;
152 }
153}
154
155
156static int pvr2_context_shutok(void)
157{
Mike Isely18ecbb42008-04-09 05:14:11 -0300158 return pvr2_context_cleanup_flag && (pvr2_context_exist_first == NULL);
Mike Iselye5be15c2008-04-07 02:22:04 -0300159}
160
161
162static int pvr2_context_thread_func(void *foo)
163{
164 struct pvr2_context *mp;
165
166 pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread start");
167
168 do {
169 while ((mp = pvr2_context_notify_first) != NULL) {
170 pvr2_context_set_notify(mp, 0);
171 pvr2_context_check(mp);
172 }
173 wait_event_interruptible(
174 pvr2_context_sync_data,
175 ((pvr2_context_notify_first != NULL) ||
176 pvr2_context_shutok()));
177 } while (!pvr2_context_shutok());
178
Mike Isely18ecbb42008-04-09 05:14:11 -0300179 pvr2_context_cleaned_flag = !0;
180 wake_up(&pvr2_context_cleanup_data);
181
182 pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread cleaned up");
183
184 wait_event_interruptible(
185 pvr2_context_sync_data,
186 kthread_should_stop());
187
Mike Iselye5be15c2008-04-07 02:22:04 -0300188 pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread end");
189
Mike Isely794b1602008-04-22 14:45:45 -0300190 return 0;
191}
Mike Iselyd8554972006-06-26 20:58:46 -0300192
193
Mike Iselye5be15c2008-04-07 02:22:04 -0300194int pvr2_context_global_init(void)
195{
196 pvr2_context_thread_ptr = kthread_run(pvr2_context_thread_func,
Harvey Harrisona6a3a172008-04-28 16:50:03 -0700197 NULL,
Mike Iselye5be15c2008-04-07 02:22:04 -0300198 "pvrusb2-context");
199 return (pvr2_context_thread_ptr ? 0 : -ENOMEM);
200}
201
202
203void pvr2_context_global_done(void)
204{
Mike Isely18ecbb42008-04-09 05:14:11 -0300205 pvr2_context_cleanup_flag = !0;
206 wake_up(&pvr2_context_sync_data);
207 wait_event_interruptible(
208 pvr2_context_cleanup_data,
209 pvr2_context_cleaned_flag);
Mike Iselye5be15c2008-04-07 02:22:04 -0300210 kthread_stop(pvr2_context_thread_ptr);
211}
212
213
Mike Iselyd8554972006-06-26 20:58:46 -0300214struct pvr2_context *pvr2_context_create(
215 struct usb_interface *intf,
216 const struct usb_device_id *devid,
217 void (*setup_func)(struct pvr2_context *))
218{
Mike Iselya0fd1cb2006-06-30 11:35:28 -0300219 struct pvr2_context *mp = NULL;
Mike Iselyca545f72007-01-20 00:37:11 -0300220 mp = kzalloc(sizeof(*mp),GFP_KERNEL);
Mike Iselyd8554972006-06-26 20:58:46 -0300221 if (!mp) goto done;
Mike Isely794b1602008-04-22 14:45:45 -0300222 pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (create)",mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300223 mp->setup_func = setup_func;
224 mutex_init(&mp->mutex);
Mike Iselye5be15c2008-04-07 02:22:04 -0300225 mutex_lock(&pvr2_context_mutex);
226 mp->exist_prev = pvr2_context_exist_last;
227 mp->exist_next = NULL;
228 pvr2_context_exist_last = mp;
229 if (mp->exist_prev) {
230 mp->exist_prev->exist_next = mp;
231 } else {
232 pvr2_context_exist_first = mp;
233 }
234 mutex_unlock(&pvr2_context_mutex);
Mike Iselyd8554972006-06-26 20:58:46 -0300235 mp->hdw = pvr2_hdw_create(intf,devid);
236 if (!mp->hdw) {
237 pvr2_context_destroy(mp);
Mike Iselya0fd1cb2006-06-30 11:35:28 -0300238 mp = NULL;
Mike Iselyd8554972006-06-26 20:58:46 -0300239 goto done;
240 }
Mike Iselye5be15c2008-04-07 02:22:04 -0300241 pvr2_context_set_notify(mp, !0);
Mike Iselyd8554972006-06-26 20:58:46 -0300242 done:
243 return mp;
244}
245
246
Mike Isely1cb03b72008-04-21 03:47:43 -0300247static void pvr2_context_reset_input_limits(struct pvr2_context *mp)
248{
249 unsigned int tmsk,mmsk;
250 struct pvr2_channel *cp;
251 struct pvr2_hdw *hdw = mp->hdw;
252 mmsk = pvr2_hdw_get_input_available(hdw);
253 tmsk = mmsk;
254 for (cp = mp->mc_first; cp; cp = cp->mc_next) {
255 if (!cp->input_mask) continue;
256 tmsk &= cp->input_mask;
257 }
258 pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk);
259 pvr2_hdw_commit_ctl(hdw);
260}
261
262
Mike Isely794b1602008-04-22 14:45:45 -0300263static void pvr2_context_enter(struct pvr2_context *mp)
Mike Iselyd8554972006-06-26 20:58:46 -0300264{
265 mutex_lock(&mp->mutex);
Mike Iselyd8554972006-06-26 20:58:46 -0300266}
267
268
Mike Isely794b1602008-04-22 14:45:45 -0300269static void pvr2_context_exit(struct pvr2_context *mp)
Mike Iselyd8554972006-06-26 20:58:46 -0300270{
271 int destroy_flag = 0;
272 if (!(mp->mc_first || !mp->disconnect_flag)) {
273 destroy_flag = !0;
274 }
Mike Iselyd8554972006-06-26 20:58:46 -0300275 mutex_unlock(&mp->mutex);
Mike Isely794b1602008-04-22 14:45:45 -0300276 if (destroy_flag) pvr2_context_notify(mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300277}
278
279
280void pvr2_context_disconnect(struct pvr2_context *mp)
281{
Mike Isely794b1602008-04-22 14:45:45 -0300282 pvr2_hdw_disconnect(mp->hdw);
283 mp->disconnect_flag = !0;
284 pvr2_context_notify(mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300285}
286
287
288void pvr2_channel_init(struct pvr2_channel *cp,struct pvr2_context *mp)
289{
Mike Isely794b1602008-04-22 14:45:45 -0300290 pvr2_context_enter(mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300291 cp->hdw = mp->hdw;
292 cp->mc_head = mp;
Mike Iselya0fd1cb2006-06-30 11:35:28 -0300293 cp->mc_next = NULL;
Mike Iselyd8554972006-06-26 20:58:46 -0300294 cp->mc_prev = mp->mc_last;
295 if (mp->mc_last) {
296 mp->mc_last->mc_next = cp;
297 } else {
298 mp->mc_first = cp;
299 }
300 mp->mc_last = cp;
Mike Isely794b1602008-04-22 14:45:45 -0300301 pvr2_context_exit(mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300302}
303
304
305static void pvr2_channel_disclaim_stream(struct pvr2_channel *cp)
306{
307 if (!cp->stream) return;
308 pvr2_stream_kill(cp->stream->stream);
Mike Iselya0fd1cb2006-06-30 11:35:28 -0300309 cp->stream->user = NULL;
310 cp->stream = NULL;
Mike Iselyd8554972006-06-26 20:58:46 -0300311}
312
313
314void pvr2_channel_done(struct pvr2_channel *cp)
315{
316 struct pvr2_context *mp = cp->mc_head;
Mike Isely794b1602008-04-22 14:45:45 -0300317 pvr2_context_enter(mp);
Mike Isely1cb03b72008-04-21 03:47:43 -0300318 cp->input_mask = 0;
Mike Iselyd8554972006-06-26 20:58:46 -0300319 pvr2_channel_disclaim_stream(cp);
Mike Isely1cb03b72008-04-21 03:47:43 -0300320 pvr2_context_reset_input_limits(mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300321 if (cp->mc_next) {
322 cp->mc_next->mc_prev = cp->mc_prev;
323 } else {
324 mp->mc_last = cp->mc_prev;
325 }
326 if (cp->mc_prev) {
327 cp->mc_prev->mc_next = cp->mc_next;
328 } else {
329 mp->mc_first = cp->mc_next;
330 }
Mike Iselya0fd1cb2006-06-30 11:35:28 -0300331 cp->hdw = NULL;
Mike Isely794b1602008-04-22 14:45:45 -0300332 pvr2_context_exit(mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300333}
334
335
Mike Isely1cb03b72008-04-21 03:47:43 -0300336int pvr2_channel_limit_inputs(struct pvr2_channel *cp,unsigned int cmsk)
337{
338 unsigned int tmsk,mmsk;
339 int ret = 0;
340 struct pvr2_channel *p2;
341 struct pvr2_hdw *hdw = cp->hdw;
342
343 mmsk = pvr2_hdw_get_input_available(hdw);
344 cmsk &= mmsk;
345 if (cmsk == cp->input_mask) {
346 /* No change; nothing to do */
347 return 0;
348 }
349
350 pvr2_context_enter(cp->mc_head);
351 do {
352 if (!cmsk) {
353 cp->input_mask = 0;
354 pvr2_context_reset_input_limits(cp->mc_head);
355 break;
356 }
357 tmsk = mmsk;
358 for (p2 = cp->mc_head->mc_first; p2; p2 = p2->mc_next) {
359 if (p2 == cp) continue;
360 if (!p2->input_mask) continue;
361 tmsk &= p2->input_mask;
362 }
363 if (!(tmsk & cmsk)) {
364 ret = -EPERM;
365 break;
366 }
367 tmsk &= cmsk;
368 if ((ret = pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk)) != 0) {
369 /* Internal failure changing allowed list; probably
370 should not happen, but react if it does. */
371 break;
372 }
373 cp->input_mask = cmsk;
374 pvr2_hdw_commit_ctl(hdw);
375 } while (0);
376 pvr2_context_exit(cp->mc_head);
377 return ret;
378}
379
380
381unsigned int pvr2_channel_get_limited_inputs(struct pvr2_channel *cp)
382{
383 return cp->input_mask;
384}
385
386
Mike Iselyd8554972006-06-26 20:58:46 -0300387int pvr2_channel_claim_stream(struct pvr2_channel *cp,
388 struct pvr2_context_stream *sp)
389{
390 int code = 0;
391 pvr2_context_enter(cp->mc_head); do {
392 if (sp == cp->stream) break;
Mike Isely99a6acf2008-04-22 14:45:39 -0300393 if (sp && sp->user) {
Mike Iselyd8554972006-06-26 20:58:46 -0300394 code = -EBUSY;
395 break;
396 }
397 pvr2_channel_disclaim_stream(cp);
398 if (!sp) break;
399 sp->user = cp;
400 cp->stream = sp;
401 } while (0); pvr2_context_exit(cp->mc_head);
402 return code;
403}
404
405
406// This is the marker for the real beginning of a legitimate mpeg2 stream.
407static char stream_sync_key[] = {
408 0x00, 0x00, 0x01, 0xba,
409};
410
411struct pvr2_ioread *pvr2_channel_create_mpeg_stream(
412 struct pvr2_context_stream *sp)
413{
414 struct pvr2_ioread *cp;
415 cp = pvr2_ioread_create();
Mike Iselya0fd1cb2006-06-30 11:35:28 -0300416 if (!cp) return NULL;
Mike Iselyd8554972006-06-26 20:58:46 -0300417 pvr2_ioread_setup(cp,sp->stream);
418 pvr2_ioread_set_sync_key(cp,stream_sync_key,sizeof(stream_sync_key));
419 return cp;
420}
421
422
423/*
424 Stuff for Emacs to see, in order to encourage consistent editing style:
425 *** Local Variables: ***
426 *** mode: c ***
427 *** fill-column: 75 ***
428 *** tab-width: 8 ***
429 *** c-basic-offset: 8 ***
430 *** End: ***
431 */