blob: 22bd8302c4aa00b8b835fb31dd1b359387c089e7 [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 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
31
32static void pvr2_context_destroy(struct pvr2_context *mp)
33{
Mike Isely794b1602008-04-22 14:45:45 -030034 pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (destroy)",mp);
Mike Isely681c7392007-11-26 01:48:52 -030035 if (mp->hdw) pvr2_hdw_destroy(mp->hdw);
Mike Iselyd8554972006-06-26 20:58:46 -030036 kfree(mp);
37}
38
39
Mike Isely794b1602008-04-22 14:45:45 -030040static void pvr2_context_notify(struct pvr2_context *mp)
Mike Iselyd8554972006-06-26 20:58:46 -030041{
Mike Isely794b1602008-04-22 14:45:45 -030042 mp->notify_flag = !0;
43 wake_up(&mp->wait_data);
44}
Mike Iselyd8554972006-06-26 20:58:46 -030045
Mike Isely794b1602008-04-22 14:45:45 -030046
47static int pvr2_context_thread(void *_mp)
48{
49 struct pvr2_channel *ch1,*ch2;
50 struct pvr2_context *mp = _mp;
51 pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (thread start)",mp);
52
53 /* Finish hardware initialization */
54 if (pvr2_hdw_initialize(mp->hdw,
55 (void (*)(void *))pvr2_context_notify,mp)) {
56 mp->video_stream.stream =
57 pvr2_hdw_get_video_stream(mp->hdw);
58 /* Trigger interface initialization. By doing this here
59 initialization runs in our own safe and cozy thread
60 context. */
61 if (mp->setup_func) mp->setup_func(mp);
62 } else {
63 pvr2_trace(PVR2_TRACE_CTXT,
64 "pvr2_context %p (thread skipping setup)",mp);
65 /* Even though initialization did not succeed, we're still
66 going to enter the wait loop anyway. We need to do this
67 in order to await the expected disconnect (which we will
68 detect in the normal course of operation). */
Mike Isely681c7392007-11-26 01:48:52 -030069 }
David Howellsc4028952006-11-22 14:57:56 +000070
Mike Isely794b1602008-04-22 14:45:45 -030071 /* Now just issue callbacks whenever hardware state changes or if
72 there is a disconnect. If there is a disconnect and there are
73 no channels left, then there's no reason to stick around anymore
74 so we'll self-destruct - tearing down the rest of this driver
75 instance along the way. */
76 pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (thread enter loop)",mp);
77 while (!mp->disconnect_flag || mp->mc_first) {
78 if (mp->notify_flag) {
79 mp->notify_flag = 0;
80 pvr2_trace(PVR2_TRACE_CTXT,
81 "pvr2_context %p (thread notify)",mp);
82 for (ch1 = mp->mc_first; ch1; ch1 = ch2) {
83 ch2 = ch1->mc_next;
84 if (ch1->check_func) ch1->check_func(ch1);
85 }
Mike Iselyd8554972006-06-26 20:58:46 -030086 }
Mike Isely794b1602008-04-22 14:45:45 -030087 wait_event_interruptible(mp->wait_data, mp->notify_flag);
88 }
89 pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (thread end)",mp);
90 pvr2_context_destroy(mp);
91 return 0;
92}
Mike Iselyd8554972006-06-26 20:58:46 -030093
94
95struct pvr2_context *pvr2_context_create(
96 struct usb_interface *intf,
97 const struct usb_device_id *devid,
98 void (*setup_func)(struct pvr2_context *))
99{
Mike Isely794b1602008-04-22 14:45:45 -0300100 struct task_struct *thread;
Mike Iselya0fd1cb2006-06-30 11:35:28 -0300101 struct pvr2_context *mp = NULL;
Mike Iselyca545f72007-01-20 00:37:11 -0300102 mp = kzalloc(sizeof(*mp),GFP_KERNEL);
Mike Iselyd8554972006-06-26 20:58:46 -0300103 if (!mp) goto done;
Mike Isely794b1602008-04-22 14:45:45 -0300104 pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (create)",mp);
105 init_waitqueue_head(&mp->wait_data);
Mike Iselyd8554972006-06-26 20:58:46 -0300106 mp->setup_func = setup_func;
107 mutex_init(&mp->mutex);
108 mp->hdw = pvr2_hdw_create(intf,devid);
109 if (!mp->hdw) {
110 pvr2_context_destroy(mp);
Mike Iselya0fd1cb2006-06-30 11:35:28 -0300111 mp = NULL;
Mike Iselyd8554972006-06-26 20:58:46 -0300112 goto done;
113 }
Mike Isely794b1602008-04-22 14:45:45 -0300114 thread = kthread_run(pvr2_context_thread, mp, "pvrusb2-context");
115 if (!thread) {
116 pvr2_context_destroy(mp);
117 mp = NULL;
118 goto done;
119 }
Mike Iselyd8554972006-06-26 20:58:46 -0300120 done:
121 return mp;
122}
123
124
Mike Isely794b1602008-04-22 14:45:45 -0300125static void pvr2_context_enter(struct pvr2_context *mp)
Mike Iselyd8554972006-06-26 20:58:46 -0300126{
127 mutex_lock(&mp->mutex);
Mike Iselyd8554972006-06-26 20:58:46 -0300128}
129
130
Mike Isely794b1602008-04-22 14:45:45 -0300131static void pvr2_context_exit(struct pvr2_context *mp)
Mike Iselyd8554972006-06-26 20:58:46 -0300132{
133 int destroy_flag = 0;
134 if (!(mp->mc_first || !mp->disconnect_flag)) {
135 destroy_flag = !0;
136 }
Mike Iselyd8554972006-06-26 20:58:46 -0300137 mutex_unlock(&mp->mutex);
Mike Isely794b1602008-04-22 14:45:45 -0300138 if (destroy_flag) pvr2_context_notify(mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300139}
140
141
142void pvr2_context_disconnect(struct pvr2_context *mp)
143{
Mike Isely794b1602008-04-22 14:45:45 -0300144 pvr2_hdw_disconnect(mp->hdw);
145 mp->disconnect_flag = !0;
146 pvr2_context_notify(mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300147}
148
149
150void pvr2_channel_init(struct pvr2_channel *cp,struct pvr2_context *mp)
151{
Mike Isely794b1602008-04-22 14:45:45 -0300152 pvr2_context_enter(mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300153 cp->hdw = mp->hdw;
154 cp->mc_head = mp;
Mike Iselya0fd1cb2006-06-30 11:35:28 -0300155 cp->mc_next = NULL;
Mike Iselyd8554972006-06-26 20:58:46 -0300156 cp->mc_prev = mp->mc_last;
157 if (mp->mc_last) {
158 mp->mc_last->mc_next = cp;
159 } else {
160 mp->mc_first = cp;
161 }
162 mp->mc_last = cp;
Mike Isely794b1602008-04-22 14:45:45 -0300163 pvr2_context_exit(mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300164}
165
166
167static void pvr2_channel_disclaim_stream(struct pvr2_channel *cp)
168{
169 if (!cp->stream) return;
170 pvr2_stream_kill(cp->stream->stream);
Mike Iselya0fd1cb2006-06-30 11:35:28 -0300171 cp->stream->user = NULL;
172 cp->stream = NULL;
Mike Iselyd8554972006-06-26 20:58:46 -0300173}
174
175
176void pvr2_channel_done(struct pvr2_channel *cp)
177{
178 struct pvr2_context *mp = cp->mc_head;
Mike Isely794b1602008-04-22 14:45:45 -0300179 pvr2_context_enter(mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300180 pvr2_channel_disclaim_stream(cp);
181 if (cp->mc_next) {
182 cp->mc_next->mc_prev = cp->mc_prev;
183 } else {
184 mp->mc_last = cp->mc_prev;
185 }
186 if (cp->mc_prev) {
187 cp->mc_prev->mc_next = cp->mc_next;
188 } else {
189 mp->mc_first = cp->mc_next;
190 }
Mike Iselya0fd1cb2006-06-30 11:35:28 -0300191 cp->hdw = NULL;
Mike Isely794b1602008-04-22 14:45:45 -0300192 pvr2_context_exit(mp);
Mike Iselyd8554972006-06-26 20:58:46 -0300193}
194
195
196int pvr2_channel_claim_stream(struct pvr2_channel *cp,
197 struct pvr2_context_stream *sp)
198{
199 int code = 0;
200 pvr2_context_enter(cp->mc_head); do {
201 if (sp == cp->stream) break;
Mike Isely99a6acf2008-04-22 14:45:39 -0300202 if (sp && sp->user) {
Mike Iselyd8554972006-06-26 20:58:46 -0300203 code = -EBUSY;
204 break;
205 }
206 pvr2_channel_disclaim_stream(cp);
207 if (!sp) break;
208 sp->user = cp;
209 cp->stream = sp;
210 } while (0); pvr2_context_exit(cp->mc_head);
211 return code;
212}
213
214
215// This is the marker for the real beginning of a legitimate mpeg2 stream.
216static char stream_sync_key[] = {
217 0x00, 0x00, 0x01, 0xba,
218};
219
220struct pvr2_ioread *pvr2_channel_create_mpeg_stream(
221 struct pvr2_context_stream *sp)
222{
223 struct pvr2_ioread *cp;
224 cp = pvr2_ioread_create();
Mike Iselya0fd1cb2006-06-30 11:35:28 -0300225 if (!cp) return NULL;
Mike Iselyd8554972006-06-26 20:58:46 -0300226 pvr2_ioread_setup(cp,sp->stream);
227 pvr2_ioread_set_sync_key(cp,stream_sync_key,sizeof(stream_sync_key));
228 return cp;
229}
230
231
232/*
233 Stuff for Emacs to see, in order to encourage consistent editing style:
234 *** Local Variables: ***
235 *** mode: c ***
236 *** fill-column: 75 ***
237 *** tab-width: 8 ***
238 *** c-basic-offset: 8 ***
239 *** End: ***
240 */