1/*
2 *
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"
25#include <linux/wait.h>
26#include <linux/kthread.h>
27#include <linux/errno.h>
28#include <linux/string.h>
29#include <linux/slab.h>
30
31static 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);
37static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_cleanup_data);
38static int pvr2_context_cleanup_flag;
39static int pvr2_context_cleaned_flag;
40static 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
79
80static void pvr2_context_destroy(struct pvr2_context *mp)
81{
82	pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (destroy)",mp);
83	if (mp->hdw) pvr2_hdw_destroy(mp->hdw);
84	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);
102	kfree(mp);
103}
104
105
106static void pvr2_context_notify(struct pvr2_context *mp)
107{
108	pvr2_context_set_notify(mp,!0);
109}
110
111
112static void pvr2_context_check(struct pvr2_context *mp)
113{
114	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;
119		pvr2_trace(PVR2_TRACE_CTXT,
120			   "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		}
141	}
142
143	for (ch1 = mp->mc_first; ch1; ch1 = ch2) {
144		ch2 = ch1->mc_next;
145		if (ch1->check_func) ch1->check_func(ch1);
146	}
147
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{
158	return pvr2_context_cleanup_flag && (pvr2_context_exist_first == NULL);
159}
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
179	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
188	pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread end");
189
190	return 0;
191}
192
193
194int pvr2_context_global_init(void)
195{
196	pvr2_context_thread_ptr = kthread_run(pvr2_context_thread_func,
197					      NULL,
198					      "pvrusb2-context");
199	return (pvr2_context_thread_ptr ? 0 : -ENOMEM);
200}
201
202
203void pvr2_context_global_done(void)
204{
205	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);
210	kthread_stop(pvr2_context_thread_ptr);
211}
212
213
214struct 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{
219	struct pvr2_context *mp = NULL;
220	mp = kzalloc(sizeof(*mp),GFP_KERNEL);
221	if (!mp) goto done;
222	pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (create)",mp);
223	mp->setup_func = setup_func;
224	mutex_init(&mp->mutex);
225	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);
235	mp->hdw = pvr2_hdw_create(intf,devid);
236	if (!mp->hdw) {
237		pvr2_context_destroy(mp);
238		mp = NULL;
239		goto done;
240	}
241	pvr2_context_set_notify(mp, !0);
242 done:
243	return mp;
244}
245
246
247static 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
263static void pvr2_context_enter(struct pvr2_context *mp)
264{
265	mutex_lock(&mp->mutex);
266}
267
268
269static void pvr2_context_exit(struct pvr2_context *mp)
270{
271	int destroy_flag = 0;
272	if (!(mp->mc_first || !mp->disconnect_flag)) {
273		destroy_flag = !0;
274	}
275	mutex_unlock(&mp->mutex);
276	if (destroy_flag) pvr2_context_notify(mp);
277}
278
279
280void pvr2_context_disconnect(struct pvr2_context *mp)
281{
282	pvr2_hdw_disconnect(mp->hdw);
283	mp->disconnect_flag = !0;
284	pvr2_context_notify(mp);
285}
286
287
288void pvr2_channel_init(struct pvr2_channel *cp,struct pvr2_context *mp)
289{
290	pvr2_context_enter(mp);
291	cp->hdw = mp->hdw;
292	cp->mc_head = mp;
293	cp->mc_next = NULL;
294	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;
301	pvr2_context_exit(mp);
302}
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);
309	cp->stream->user = NULL;
310	cp->stream = NULL;
311}
312
313
314void pvr2_channel_done(struct pvr2_channel *cp)
315{
316	struct pvr2_context *mp = cp->mc_head;
317	pvr2_context_enter(mp);
318	cp->input_mask = 0;
319	pvr2_channel_disclaim_stream(cp);
320	pvr2_context_reset_input_limits(mp);
321	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	}
331	cp->hdw = NULL;
332	pvr2_context_exit(mp);
333}
334
335
336int 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
387int 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;
393		if (sp && sp->user) {
394			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();
416	if (!cp) return NULL;
417	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  */
432