blob: a2ce022c515a97dcb0fd13c817ced8551672f4e0 [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 Isely794b1602008-04-22 14:45:45 -0300248static void pvr2_context_enter(struct pvr2_context *mp)
Mike Iselyd8554972006-06-26 20:58:46 -0300249{
250 mutex_lock(&mp->mutex);
Mike Iselyd8554972006-06-26 20:58:46 -0300251}
252
253
Mike Isely794b1602008-04-22 14:45:45 -0300254static void pvr2_context_exit(struct pvr2_context *mp)
Mike Iselyd8554972006-06-26 20:58:46 -0300255{
256 int destroy_flag = 0;
257 if (!(mp->mc_first || !mp->disconnect_flag)) {
258 destroy_flag = !0;
259 }
Mike Iselyd8554972006-06-26 20:58:46 -0300260 mutex_unlock(&mp->mutex);
Mike Isely794b1602008-04-22 14:45:45 -0300261 if (destroy_flag) pvr2_context_notify(mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300262}
263
264
265void pvr2_context_disconnect(struct pvr2_context *mp)
266{
Mike Isely794b1602008-04-22 14:45:45 -0300267 pvr2_hdw_disconnect(mp->hdw);
268 mp->disconnect_flag = !0;
269 pvr2_context_notify(mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300270}
271
272
273void pvr2_channel_init(struct pvr2_channel *cp,struct pvr2_context *mp)
274{
Mike Isely794b1602008-04-22 14:45:45 -0300275 pvr2_context_enter(mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300276 cp->hdw = mp->hdw;
277 cp->mc_head = mp;
Mike Iselya0fd1cb2006-06-30 11:35:28 -0300278 cp->mc_next = NULL;
Mike Iselyd8554972006-06-26 20:58:46 -0300279 cp->mc_prev = mp->mc_last;
280 if (mp->mc_last) {
281 mp->mc_last->mc_next = cp;
282 } else {
283 mp->mc_first = cp;
284 }
285 mp->mc_last = cp;
Mike Isely794b1602008-04-22 14:45:45 -0300286 pvr2_context_exit(mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300287}
288
289
290static void pvr2_channel_disclaim_stream(struct pvr2_channel *cp)
291{
292 if (!cp->stream) return;
293 pvr2_stream_kill(cp->stream->stream);
Mike Iselya0fd1cb2006-06-30 11:35:28 -0300294 cp->stream->user = NULL;
295 cp->stream = NULL;
Mike Iselyd8554972006-06-26 20:58:46 -0300296}
297
298
299void pvr2_channel_done(struct pvr2_channel *cp)
300{
301 struct pvr2_context *mp = cp->mc_head;
Mike Isely794b1602008-04-22 14:45:45 -0300302 pvr2_context_enter(mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300303 pvr2_channel_disclaim_stream(cp);
304 if (cp->mc_next) {
305 cp->mc_next->mc_prev = cp->mc_prev;
306 } else {
307 mp->mc_last = cp->mc_prev;
308 }
309 if (cp->mc_prev) {
310 cp->mc_prev->mc_next = cp->mc_next;
311 } else {
312 mp->mc_first = cp->mc_next;
313 }
Mike Iselya0fd1cb2006-06-30 11:35:28 -0300314 cp->hdw = NULL;
Mike Isely794b1602008-04-22 14:45:45 -0300315 pvr2_context_exit(mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300316}
317
318
319int pvr2_channel_claim_stream(struct pvr2_channel *cp,
320 struct pvr2_context_stream *sp)
321{
322 int code = 0;
323 pvr2_context_enter(cp->mc_head); do {
324 if (sp == cp->stream) break;
Mike Isely99a6acf2008-04-22 14:45:39 -0300325 if (sp && sp->user) {
Mike Iselyd8554972006-06-26 20:58:46 -0300326 code = -EBUSY;
327 break;
328 }
329 pvr2_channel_disclaim_stream(cp);
330 if (!sp) break;
331 sp->user = cp;
332 cp->stream = sp;
333 } while (0); pvr2_context_exit(cp->mc_head);
334 return code;
335}
336
337
338// This is the marker for the real beginning of a legitimate mpeg2 stream.
339static char stream_sync_key[] = {
340 0x00, 0x00, 0x01, 0xba,
341};
342
343struct pvr2_ioread *pvr2_channel_create_mpeg_stream(
344 struct pvr2_context_stream *sp)
345{
346 struct pvr2_ioread *cp;
347 cp = pvr2_ioread_create();
Mike Iselya0fd1cb2006-06-30 11:35:28 -0300348 if (!cp) return NULL;
Mike Iselyd8554972006-06-26 20:58:46 -0300349 pvr2_ioread_setup(cp,sp->stream);
350 pvr2_ioread_set_sync_key(cp,stream_sync_key,sizeof(stream_sync_key));
351 return cp;
352}
353
354
355/*
356 Stuff for Emacs to see, in order to encourage consistent editing style:
357 *** Local Variables: ***
358 *** mode: c ***
359 *** fill-column: 75 ***
360 *** tab-width: 8 ***
361 *** c-basic-offset: 8 ***
362 *** End: ***
363 */