blob: e7a2ed58bde2f76bc34e8ef9e5158d1870851a8c [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);
38static struct task_struct *pvr2_context_thread_ptr;
39
40
41static void pvr2_context_set_notify(struct pvr2_context *mp, int fl)
42{
43 int signal_flag = 0;
44 mutex_lock(&pvr2_context_mutex);
45 if (fl) {
46 if (!mp->notify_flag) {
47 signal_flag = (pvr2_context_notify_first == NULL);
48 mp->notify_prev = pvr2_context_notify_last;
49 mp->notify_next = NULL;
50 pvr2_context_notify_last = mp;
51 if (mp->notify_prev) {
52 mp->notify_prev->notify_next = mp;
53 } else {
54 pvr2_context_notify_first = mp;
55 }
56 mp->notify_flag = !0;
57 }
58 } else {
59 if (mp->notify_flag) {
60 mp->notify_flag = 0;
61 if (mp->notify_next) {
62 mp->notify_next->notify_prev = mp->notify_prev;
63 } else {
64 pvr2_context_notify_last = mp->notify_prev;
65 }
66 if (mp->notify_prev) {
67 mp->notify_prev->notify_next = mp->notify_next;
68 } else {
69 pvr2_context_notify_first = mp->notify_next;
70 }
71 }
72 }
73 mutex_unlock(&pvr2_context_mutex);
74 if (signal_flag) wake_up(&pvr2_context_sync_data);
75}
76
Mike Iselyd8554972006-06-26 20:58:46 -030077
78static void pvr2_context_destroy(struct pvr2_context *mp)
79{
Mike Isely794b1602008-04-22 14:45:45 -030080 pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (destroy)",mp);
Mike Isely681c7392007-11-26 01:48:52 -030081 if (mp->hdw) pvr2_hdw_destroy(mp->hdw);
Mike Iselye5be15c2008-04-07 02:22:04 -030082 pvr2_context_set_notify(mp, 0);
83 mutex_lock(&pvr2_context_mutex);
84 if (mp->exist_next) {
85 mp->exist_next->exist_prev = mp->exist_prev;
86 } else {
87 pvr2_context_exist_last = mp->exist_prev;
88 }
89 if (mp->exist_prev) {
90 mp->exist_prev->exist_next = mp->exist_next;
91 } else {
92 pvr2_context_exist_first = mp->exist_next;
93 }
94 if (!pvr2_context_exist_first) {
95 /* Trigger wakeup on control thread in case it is waiting
96 for an exit condition. */
97 wake_up(&pvr2_context_sync_data);
98 }
99 mutex_unlock(&pvr2_context_mutex);
Mike Iselyd8554972006-06-26 20:58:46 -0300100 kfree(mp);
101}
102
103
Mike Isely794b1602008-04-22 14:45:45 -0300104static void pvr2_context_notify(struct pvr2_context *mp)
Mike Iselyd8554972006-06-26 20:58:46 -0300105{
Mike Iselye5be15c2008-04-07 02:22:04 -0300106 pvr2_context_set_notify(mp,!0);
Mike Isely794b1602008-04-22 14:45:45 -0300107}
Mike Iselyd8554972006-06-26 20:58:46 -0300108
Mike Isely794b1602008-04-22 14:45:45 -0300109
Mike Iselye5be15c2008-04-07 02:22:04 -0300110static void pvr2_context_check(struct pvr2_context *mp)
Mike Isely794b1602008-04-22 14:45:45 -0300111{
Mike Iselye5be15c2008-04-07 02:22:04 -0300112 struct pvr2_channel *ch1, *ch2;
113 pvr2_trace(PVR2_TRACE_CTXT,
114 "pvr2_context %p (notify)", mp);
115 if (!mp->initialized_flag && !mp->disconnect_flag) {
116 mp->initialized_flag = !0;
Mike Isely794b1602008-04-22 14:45:45 -0300117 pvr2_trace(PVR2_TRACE_CTXT,
Mike Iselye5be15c2008-04-07 02:22:04 -0300118 "pvr2_context %p (initialize)", mp);
119 /* Finish hardware initialization */
120 if (pvr2_hdw_initialize(mp->hdw,
121 (void (*)(void *))pvr2_context_notify,
122 mp)) {
123 mp->video_stream.stream =
124 pvr2_hdw_get_video_stream(mp->hdw);
125 /* Trigger interface initialization. By doing this
126 here initialization runs in our own safe and
127 cozy thread context. */
128 if (mp->setup_func) mp->setup_func(mp);
129 } else {
130 pvr2_trace(PVR2_TRACE_CTXT,
131 "pvr2_context %p (thread skipping setup)",
132 mp);
133 /* Even though initialization did not succeed,
134 we're still going to continue anyway. We need
135 to do this in order to await the expected
136 disconnect (which we will detect in the normal
137 course of operation). */
138 }
Mike Isely681c7392007-11-26 01:48:52 -0300139 }
David Howellsc4028952006-11-22 14:57:56 +0000140
Mike Iselye5be15c2008-04-07 02:22:04 -0300141 for (ch1 = mp->mc_first; ch1; ch1 = ch2) {
142 ch2 = ch1->mc_next;
143 if (ch1->check_func) ch1->check_func(ch1);
Mike Isely794b1602008-04-22 14:45:45 -0300144 }
Mike Iselye5be15c2008-04-07 02:22:04 -0300145
146 if (mp->disconnect_flag && !mp->mc_first) {
147 /* Go away... */
148 pvr2_context_destroy(mp);
149 return;
150 }
151}
152
153
154static int pvr2_context_shutok(void)
155{
156 return kthread_should_stop() && (pvr2_context_exist_first == NULL);
157}
158
159
160static int pvr2_context_thread_func(void *foo)
161{
162 struct pvr2_context *mp;
163
164 pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread start");
165
166 do {
167 while ((mp = pvr2_context_notify_first) != NULL) {
168 pvr2_context_set_notify(mp, 0);
169 pvr2_context_check(mp);
170 }
171 wait_event_interruptible(
172 pvr2_context_sync_data,
173 ((pvr2_context_notify_first != NULL) ||
174 pvr2_context_shutok()));
175 } while (!pvr2_context_shutok());
176
177 pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread end");
178
Mike Isely794b1602008-04-22 14:45:45 -0300179 return 0;
180}
Mike Iselyd8554972006-06-26 20:58:46 -0300181
182
Mike Iselye5be15c2008-04-07 02:22:04 -0300183int pvr2_context_global_init(void)
184{
185 pvr2_context_thread_ptr = kthread_run(pvr2_context_thread_func,
186 0,
187 "pvrusb2-context");
188 return (pvr2_context_thread_ptr ? 0 : -ENOMEM);
189}
190
191
192void pvr2_context_global_done(void)
193{
194 kthread_stop(pvr2_context_thread_ptr);
195}
196
197
Mike Iselyd8554972006-06-26 20:58:46 -0300198struct pvr2_context *pvr2_context_create(
199 struct usb_interface *intf,
200 const struct usb_device_id *devid,
201 void (*setup_func)(struct pvr2_context *))
202{
Mike Iselya0fd1cb2006-06-30 11:35:28 -0300203 struct pvr2_context *mp = NULL;
Mike Iselyca545f72007-01-20 00:37:11 -0300204 mp = kzalloc(sizeof(*mp),GFP_KERNEL);
Mike Iselyd8554972006-06-26 20:58:46 -0300205 if (!mp) goto done;
Mike Isely794b1602008-04-22 14:45:45 -0300206 pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (create)",mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300207 mp->setup_func = setup_func;
208 mutex_init(&mp->mutex);
Mike Iselye5be15c2008-04-07 02:22:04 -0300209 mutex_lock(&pvr2_context_mutex);
210 mp->exist_prev = pvr2_context_exist_last;
211 mp->exist_next = NULL;
212 pvr2_context_exist_last = mp;
213 if (mp->exist_prev) {
214 mp->exist_prev->exist_next = mp;
215 } else {
216 pvr2_context_exist_first = mp;
217 }
218 mutex_unlock(&pvr2_context_mutex);
Mike Iselyd8554972006-06-26 20:58:46 -0300219 mp->hdw = pvr2_hdw_create(intf,devid);
220 if (!mp->hdw) {
221 pvr2_context_destroy(mp);
Mike Iselya0fd1cb2006-06-30 11:35:28 -0300222 mp = NULL;
Mike Iselyd8554972006-06-26 20:58:46 -0300223 goto done;
224 }
Mike Iselye5be15c2008-04-07 02:22:04 -0300225 pvr2_context_set_notify(mp, !0);
Mike Iselyd8554972006-06-26 20:58:46 -0300226 done:
227 return mp;
228}
229
230
Mike Isely794b1602008-04-22 14:45:45 -0300231static void pvr2_context_enter(struct pvr2_context *mp)
Mike Iselyd8554972006-06-26 20:58:46 -0300232{
233 mutex_lock(&mp->mutex);
Mike Iselyd8554972006-06-26 20:58:46 -0300234}
235
236
Mike Isely794b1602008-04-22 14:45:45 -0300237static void pvr2_context_exit(struct pvr2_context *mp)
Mike Iselyd8554972006-06-26 20:58:46 -0300238{
239 int destroy_flag = 0;
240 if (!(mp->mc_first || !mp->disconnect_flag)) {
241 destroy_flag = !0;
242 }
Mike Iselyd8554972006-06-26 20:58:46 -0300243 mutex_unlock(&mp->mutex);
Mike Isely794b1602008-04-22 14:45:45 -0300244 if (destroy_flag) pvr2_context_notify(mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300245}
246
247
248void pvr2_context_disconnect(struct pvr2_context *mp)
249{
Mike Isely794b1602008-04-22 14:45:45 -0300250 pvr2_hdw_disconnect(mp->hdw);
251 mp->disconnect_flag = !0;
252 pvr2_context_notify(mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300253}
254
255
256void pvr2_channel_init(struct pvr2_channel *cp,struct pvr2_context *mp)
257{
Mike Isely794b1602008-04-22 14:45:45 -0300258 pvr2_context_enter(mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300259 cp->hdw = mp->hdw;
260 cp->mc_head = mp;
Mike Iselya0fd1cb2006-06-30 11:35:28 -0300261 cp->mc_next = NULL;
Mike Iselyd8554972006-06-26 20:58:46 -0300262 cp->mc_prev = mp->mc_last;
263 if (mp->mc_last) {
264 mp->mc_last->mc_next = cp;
265 } else {
266 mp->mc_first = cp;
267 }
268 mp->mc_last = cp;
Mike Isely794b1602008-04-22 14:45:45 -0300269 pvr2_context_exit(mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300270}
271
272
273static void pvr2_channel_disclaim_stream(struct pvr2_channel *cp)
274{
275 if (!cp->stream) return;
276 pvr2_stream_kill(cp->stream->stream);
Mike Iselya0fd1cb2006-06-30 11:35:28 -0300277 cp->stream->user = NULL;
278 cp->stream = NULL;
Mike Iselyd8554972006-06-26 20:58:46 -0300279}
280
281
282void pvr2_channel_done(struct pvr2_channel *cp)
283{
284 struct pvr2_context *mp = cp->mc_head;
Mike Isely794b1602008-04-22 14:45:45 -0300285 pvr2_context_enter(mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300286 pvr2_channel_disclaim_stream(cp);
287 if (cp->mc_next) {
288 cp->mc_next->mc_prev = cp->mc_prev;
289 } else {
290 mp->mc_last = cp->mc_prev;
291 }
292 if (cp->mc_prev) {
293 cp->mc_prev->mc_next = cp->mc_next;
294 } else {
295 mp->mc_first = cp->mc_next;
296 }
Mike Iselya0fd1cb2006-06-30 11:35:28 -0300297 cp->hdw = NULL;
Mike Isely794b1602008-04-22 14:45:45 -0300298 pvr2_context_exit(mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300299}
300
301
302int pvr2_channel_claim_stream(struct pvr2_channel *cp,
303 struct pvr2_context_stream *sp)
304{
305 int code = 0;
306 pvr2_context_enter(cp->mc_head); do {
307 if (sp == cp->stream) break;
Mike Isely99a6acf2008-04-22 14:45:39 -0300308 if (sp && sp->user) {
Mike Iselyd8554972006-06-26 20:58:46 -0300309 code = -EBUSY;
310 break;
311 }
312 pvr2_channel_disclaim_stream(cp);
313 if (!sp) break;
314 sp->user = cp;
315 cp->stream = sp;
316 } while (0); pvr2_context_exit(cp->mc_head);
317 return code;
318}
319
320
321// This is the marker for the real beginning of a legitimate mpeg2 stream.
322static char stream_sync_key[] = {
323 0x00, 0x00, 0x01, 0xba,
324};
325
326struct pvr2_ioread *pvr2_channel_create_mpeg_stream(
327 struct pvr2_context_stream *sp)
328{
329 struct pvr2_ioread *cp;
330 cp = pvr2_ioread_create();
Mike Iselya0fd1cb2006-06-30 11:35:28 -0300331 if (!cp) return NULL;
Mike Iselyd8554972006-06-26 20:58:46 -0300332 pvr2_ioread_setup(cp,sp->stream);
333 pvr2_ioread_set_sync_key(cp,stream_sync_key,sizeof(stream_sync_key));
334 return cp;
335}
336
337
338/*
339 Stuff for Emacs to see, in order to encourage consistent editing style:
340 *** Local Variables: ***
341 *** mode: c ***
342 *** fill-column: 75 ***
343 *** tab-width: 8 ***
344 *** c-basic-offset: 8 ***
345 *** End: ***
346 */