12504ba9fSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
20c0d06caSMauro Carvalho Chehab /*
30c0d06caSMauro Carvalho Chehab  *
40c0d06caSMauro Carvalho Chehab  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
50c0d06caSMauro Carvalho Chehab  */
60c0d06caSMauro Carvalho Chehab 
70c0d06caSMauro Carvalho Chehab #include "pvrusb2-context.h"
80c0d06caSMauro Carvalho Chehab #include "pvrusb2-io.h"
90c0d06caSMauro Carvalho Chehab #include "pvrusb2-ioread.h"
100c0d06caSMauro Carvalho Chehab #include "pvrusb2-hdw.h"
110c0d06caSMauro Carvalho Chehab #include "pvrusb2-debug.h"
120c0d06caSMauro Carvalho Chehab #include <linux/wait.h>
130c0d06caSMauro Carvalho Chehab #include <linux/kthread.h>
140c0d06caSMauro Carvalho Chehab #include <linux/errno.h>
150c0d06caSMauro Carvalho Chehab #include <linux/string.h>
160c0d06caSMauro Carvalho Chehab #include <linux/slab.h>
170c0d06caSMauro Carvalho Chehab 
180c0d06caSMauro Carvalho Chehab static struct pvr2_context *pvr2_context_exist_first;
190c0d06caSMauro Carvalho Chehab static struct pvr2_context *pvr2_context_exist_last;
200c0d06caSMauro Carvalho Chehab static struct pvr2_context *pvr2_context_notify_first;
210c0d06caSMauro Carvalho Chehab static struct pvr2_context *pvr2_context_notify_last;
220c0d06caSMauro Carvalho Chehab static DEFINE_MUTEX(pvr2_context_mutex);
230c0d06caSMauro Carvalho Chehab static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_sync_data);
240c0d06caSMauro Carvalho Chehab static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_cleanup_data);
250c0d06caSMauro Carvalho Chehab static int pvr2_context_cleanup_flag;
260c0d06caSMauro Carvalho Chehab static int pvr2_context_cleaned_flag;
270c0d06caSMauro Carvalho Chehab static struct task_struct *pvr2_context_thread_ptr;
280c0d06caSMauro Carvalho Chehab 
290c0d06caSMauro Carvalho Chehab 
pvr2_context_set_notify(struct pvr2_context * mp,int fl)300c0d06caSMauro Carvalho Chehab static void pvr2_context_set_notify(struct pvr2_context *mp, int fl)
310c0d06caSMauro Carvalho Chehab {
320c0d06caSMauro Carvalho Chehab 	int signal_flag = 0;
330c0d06caSMauro Carvalho Chehab 	mutex_lock(&pvr2_context_mutex);
340c0d06caSMauro Carvalho Chehab 	if (fl) {
350c0d06caSMauro Carvalho Chehab 		if (!mp->notify_flag) {
360c0d06caSMauro Carvalho Chehab 			signal_flag = (pvr2_context_notify_first == NULL);
370c0d06caSMauro Carvalho Chehab 			mp->notify_prev = pvr2_context_notify_last;
380c0d06caSMauro Carvalho Chehab 			mp->notify_next = NULL;
390c0d06caSMauro Carvalho Chehab 			pvr2_context_notify_last = mp;
400c0d06caSMauro Carvalho Chehab 			if (mp->notify_prev) {
410c0d06caSMauro Carvalho Chehab 				mp->notify_prev->notify_next = mp;
420c0d06caSMauro Carvalho Chehab 			} else {
430c0d06caSMauro Carvalho Chehab 				pvr2_context_notify_first = mp;
440c0d06caSMauro Carvalho Chehab 			}
450c0d06caSMauro Carvalho Chehab 			mp->notify_flag = !0;
460c0d06caSMauro Carvalho Chehab 		}
470c0d06caSMauro Carvalho Chehab 	} else {
480c0d06caSMauro Carvalho Chehab 		if (mp->notify_flag) {
490c0d06caSMauro Carvalho Chehab 			mp->notify_flag = 0;
500c0d06caSMauro Carvalho Chehab 			if (mp->notify_next) {
510c0d06caSMauro Carvalho Chehab 				mp->notify_next->notify_prev = mp->notify_prev;
520c0d06caSMauro Carvalho Chehab 			} else {
530c0d06caSMauro Carvalho Chehab 				pvr2_context_notify_last = mp->notify_prev;
540c0d06caSMauro Carvalho Chehab 			}
550c0d06caSMauro Carvalho Chehab 			if (mp->notify_prev) {
560c0d06caSMauro Carvalho Chehab 				mp->notify_prev->notify_next = mp->notify_next;
570c0d06caSMauro Carvalho Chehab 			} else {
580c0d06caSMauro Carvalho Chehab 				pvr2_context_notify_first = mp->notify_next;
590c0d06caSMauro Carvalho Chehab 			}
600c0d06caSMauro Carvalho Chehab 		}
610c0d06caSMauro Carvalho Chehab 	}
620c0d06caSMauro Carvalho Chehab 	mutex_unlock(&pvr2_context_mutex);
630c0d06caSMauro Carvalho Chehab 	if (signal_flag) wake_up(&pvr2_context_sync_data);
640c0d06caSMauro Carvalho Chehab }
650c0d06caSMauro Carvalho Chehab 
660c0d06caSMauro Carvalho Chehab 
pvr2_context_destroy(struct pvr2_context * mp)670c0d06caSMauro Carvalho Chehab static void pvr2_context_destroy(struct pvr2_context *mp)
680c0d06caSMauro Carvalho Chehab {
690c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (destroy)",mp);
7083f56f7cSMarkus Elfring 	pvr2_hdw_destroy(mp->hdw);
710c0d06caSMauro Carvalho Chehab 	pvr2_context_set_notify(mp, 0);
720c0d06caSMauro Carvalho Chehab 	mutex_lock(&pvr2_context_mutex);
730c0d06caSMauro Carvalho Chehab 	if (mp->exist_next) {
740c0d06caSMauro Carvalho Chehab 		mp->exist_next->exist_prev = mp->exist_prev;
750c0d06caSMauro Carvalho Chehab 	} else {
760c0d06caSMauro Carvalho Chehab 		pvr2_context_exist_last = mp->exist_prev;
770c0d06caSMauro Carvalho Chehab 	}
780c0d06caSMauro Carvalho Chehab 	if (mp->exist_prev) {
790c0d06caSMauro Carvalho Chehab 		mp->exist_prev->exist_next = mp->exist_next;
800c0d06caSMauro Carvalho Chehab 	} else {
810c0d06caSMauro Carvalho Chehab 		pvr2_context_exist_first = mp->exist_next;
820c0d06caSMauro Carvalho Chehab 	}
830c0d06caSMauro Carvalho Chehab 	if (!pvr2_context_exist_first) {
840c0d06caSMauro Carvalho Chehab 		/* Trigger wakeup on control thread in case it is waiting
850c0d06caSMauro Carvalho Chehab 		   for an exit condition. */
860c0d06caSMauro Carvalho Chehab 		wake_up(&pvr2_context_sync_data);
870c0d06caSMauro Carvalho Chehab 	}
880c0d06caSMauro Carvalho Chehab 	mutex_unlock(&pvr2_context_mutex);
890c0d06caSMauro Carvalho Chehab 	kfree(mp);
900c0d06caSMauro Carvalho Chehab }
910c0d06caSMauro Carvalho Chehab 
920c0d06caSMauro Carvalho Chehab 
pvr2_context_notify(void * ptr)935b8d21f7SArnd Bergmann static void pvr2_context_notify(void *ptr)
940c0d06caSMauro Carvalho Chehab {
955b8d21f7SArnd Bergmann 	struct pvr2_context *mp = ptr;
965b8d21f7SArnd Bergmann 
970c0d06caSMauro Carvalho Chehab 	pvr2_context_set_notify(mp,!0);
980c0d06caSMauro Carvalho Chehab }
990c0d06caSMauro Carvalho Chehab 
1000c0d06caSMauro Carvalho Chehab 
pvr2_context_check(struct pvr2_context * mp)1010c0d06caSMauro Carvalho Chehab static void pvr2_context_check(struct pvr2_context *mp)
1020c0d06caSMauro Carvalho Chehab {
1030c0d06caSMauro Carvalho Chehab 	struct pvr2_channel *ch1, *ch2;
1040c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_CTXT,
1050c0d06caSMauro Carvalho Chehab 		   "pvr2_context %p (notify)", mp);
1060c0d06caSMauro Carvalho Chehab 	if (!mp->initialized_flag && !mp->disconnect_flag) {
1070c0d06caSMauro Carvalho Chehab 		mp->initialized_flag = !0;
1080c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_CTXT,
1090c0d06caSMauro Carvalho Chehab 			   "pvr2_context %p (initialize)", mp);
1100c0d06caSMauro Carvalho Chehab 		/* Finish hardware initialization */
1115b8d21f7SArnd Bergmann 		if (pvr2_hdw_initialize(mp->hdw, pvr2_context_notify, mp)) {
1120c0d06caSMauro Carvalho Chehab 			mp->video_stream.stream =
1130c0d06caSMauro Carvalho Chehab 				pvr2_hdw_get_video_stream(mp->hdw);
1140c0d06caSMauro Carvalho Chehab 			/* Trigger interface initialization.  By doing this
1150c0d06caSMauro Carvalho Chehab 			   here initialization runs in our own safe and
1160c0d06caSMauro Carvalho Chehab 			   cozy thread context. */
1170c0d06caSMauro Carvalho Chehab 			if (mp->setup_func) mp->setup_func(mp);
1180c0d06caSMauro Carvalho Chehab 		} else {
1190c0d06caSMauro Carvalho Chehab 			pvr2_trace(PVR2_TRACE_CTXT,
1200c0d06caSMauro Carvalho Chehab 				   "pvr2_context %p (thread skipping setup)",
1210c0d06caSMauro Carvalho Chehab 				   mp);
1220c0d06caSMauro Carvalho Chehab 			/* Even though initialization did not succeed,
1230c0d06caSMauro Carvalho Chehab 			   we're still going to continue anyway.  We need
1240c0d06caSMauro Carvalho Chehab 			   to do this in order to await the expected
1250c0d06caSMauro Carvalho Chehab 			   disconnect (which we will detect in the normal
1260c0d06caSMauro Carvalho Chehab 			   course of operation). */
1270c0d06caSMauro Carvalho Chehab 		}
1280c0d06caSMauro Carvalho Chehab 	}
1290c0d06caSMauro Carvalho Chehab 
1300c0d06caSMauro Carvalho Chehab 	for (ch1 = mp->mc_first; ch1; ch1 = ch2) {
1310c0d06caSMauro Carvalho Chehab 		ch2 = ch1->mc_next;
1320c0d06caSMauro Carvalho Chehab 		if (ch1->check_func) ch1->check_func(ch1);
1330c0d06caSMauro Carvalho Chehab 	}
1340c0d06caSMauro Carvalho Chehab 
1350c0d06caSMauro Carvalho Chehab 	if (mp->disconnect_flag && !mp->mc_first) {
1360c0d06caSMauro Carvalho Chehab 		/* Go away... */
1370c0d06caSMauro Carvalho Chehab 		pvr2_context_destroy(mp);
1380c0d06caSMauro Carvalho Chehab 		return;
1390c0d06caSMauro Carvalho Chehab 	}
1400c0d06caSMauro Carvalho Chehab }
1410c0d06caSMauro Carvalho Chehab 
1420c0d06caSMauro Carvalho Chehab 
pvr2_context_shutok(void)1430c0d06caSMauro Carvalho Chehab static int pvr2_context_shutok(void)
1440c0d06caSMauro Carvalho Chehab {
1450c0d06caSMauro Carvalho Chehab 	return pvr2_context_cleanup_flag && (pvr2_context_exist_first == NULL);
1460c0d06caSMauro Carvalho Chehab }
1470c0d06caSMauro Carvalho Chehab 
1480c0d06caSMauro Carvalho Chehab 
pvr2_context_thread_func(void * foo)1490c0d06caSMauro Carvalho Chehab static int pvr2_context_thread_func(void *foo)
1500c0d06caSMauro Carvalho Chehab {
1510c0d06caSMauro Carvalho Chehab 	struct pvr2_context *mp;
1520c0d06caSMauro Carvalho Chehab 
1530c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread start");
1540c0d06caSMauro Carvalho Chehab 
1550c0d06caSMauro Carvalho Chehab 	do {
1560c0d06caSMauro Carvalho Chehab 		while ((mp = pvr2_context_notify_first) != NULL) {
1570c0d06caSMauro Carvalho Chehab 			pvr2_context_set_notify(mp, 0);
1580c0d06caSMauro Carvalho Chehab 			pvr2_context_check(mp);
1590c0d06caSMauro Carvalho Chehab 		}
1600c0d06caSMauro Carvalho Chehab 		wait_event_interruptible(
1610c0d06caSMauro Carvalho Chehab 			pvr2_context_sync_data,
1620c0d06caSMauro Carvalho Chehab 			((pvr2_context_notify_first != NULL) ||
1630c0d06caSMauro Carvalho Chehab 			 pvr2_context_shutok()));
1640c0d06caSMauro Carvalho Chehab 	} while (!pvr2_context_shutok());
1650c0d06caSMauro Carvalho Chehab 
1660c0d06caSMauro Carvalho Chehab 	pvr2_context_cleaned_flag = !0;
1670c0d06caSMauro Carvalho Chehab 	wake_up(&pvr2_context_cleanup_data);
1680c0d06caSMauro Carvalho Chehab 
1690c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread cleaned up");
1700c0d06caSMauro Carvalho Chehab 
1710c0d06caSMauro Carvalho Chehab 	wait_event_interruptible(
1720c0d06caSMauro Carvalho Chehab 		pvr2_context_sync_data,
1730c0d06caSMauro Carvalho Chehab 		kthread_should_stop());
1740c0d06caSMauro Carvalho Chehab 
1750c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread end");
1760c0d06caSMauro Carvalho Chehab 
1770c0d06caSMauro Carvalho Chehab 	return 0;
1780c0d06caSMauro Carvalho Chehab }
1790c0d06caSMauro Carvalho Chehab 
1800c0d06caSMauro Carvalho Chehab 
pvr2_context_global_init(void)1810c0d06caSMauro Carvalho Chehab int pvr2_context_global_init(void)
1820c0d06caSMauro Carvalho Chehab {
1830c0d06caSMauro Carvalho Chehab 	pvr2_context_thread_ptr = kthread_run(pvr2_context_thread_func,
1840c0d06caSMauro Carvalho Chehab 					      NULL,
1850c0d06caSMauro Carvalho Chehab 					      "pvrusb2-context");
186bb07df8aSInsu Yun 	return IS_ERR(pvr2_context_thread_ptr) ? -ENOMEM : 0;
1870c0d06caSMauro Carvalho Chehab }
1880c0d06caSMauro Carvalho Chehab 
1890c0d06caSMauro Carvalho Chehab 
pvr2_context_global_done(void)1900c0d06caSMauro Carvalho Chehab void pvr2_context_global_done(void)
1910c0d06caSMauro Carvalho Chehab {
1920c0d06caSMauro Carvalho Chehab 	pvr2_context_cleanup_flag = !0;
1930c0d06caSMauro Carvalho Chehab 	wake_up(&pvr2_context_sync_data);
1940c0d06caSMauro Carvalho Chehab 	wait_event_interruptible(
1950c0d06caSMauro Carvalho Chehab 		pvr2_context_cleanup_data,
1960c0d06caSMauro Carvalho Chehab 		pvr2_context_cleaned_flag);
1970c0d06caSMauro Carvalho Chehab 	kthread_stop(pvr2_context_thread_ptr);
1980c0d06caSMauro Carvalho Chehab }
1990c0d06caSMauro Carvalho Chehab 
2000c0d06caSMauro Carvalho Chehab 
pvr2_context_create(struct usb_interface * intf,const struct usb_device_id * devid,void (* setup_func)(struct pvr2_context *))2010c0d06caSMauro Carvalho Chehab struct pvr2_context *pvr2_context_create(
2020c0d06caSMauro Carvalho Chehab 	struct usb_interface *intf,
2030c0d06caSMauro Carvalho Chehab 	const struct usb_device_id *devid,
2040c0d06caSMauro Carvalho Chehab 	void (*setup_func)(struct pvr2_context *))
2050c0d06caSMauro Carvalho Chehab {
2060c0d06caSMauro Carvalho Chehab 	struct pvr2_context *mp = NULL;
2070c0d06caSMauro Carvalho Chehab 	mp = kzalloc(sizeof(*mp),GFP_KERNEL);
2080c0d06caSMauro Carvalho Chehab 	if (!mp) goto done;
2090c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (create)",mp);
2100c0d06caSMauro Carvalho Chehab 	mp->setup_func = setup_func;
2110c0d06caSMauro Carvalho Chehab 	mutex_init(&mp->mutex);
2120c0d06caSMauro Carvalho Chehab 	mutex_lock(&pvr2_context_mutex);
2130c0d06caSMauro Carvalho Chehab 	mp->exist_prev = pvr2_context_exist_last;
2140c0d06caSMauro Carvalho Chehab 	mp->exist_next = NULL;
2150c0d06caSMauro Carvalho Chehab 	pvr2_context_exist_last = mp;
2160c0d06caSMauro Carvalho Chehab 	if (mp->exist_prev) {
2170c0d06caSMauro Carvalho Chehab 		mp->exist_prev->exist_next = mp;
2180c0d06caSMauro Carvalho Chehab 	} else {
2190c0d06caSMauro Carvalho Chehab 		pvr2_context_exist_first = mp;
2200c0d06caSMauro Carvalho Chehab 	}
2210c0d06caSMauro Carvalho Chehab 	mutex_unlock(&pvr2_context_mutex);
2220c0d06caSMauro Carvalho Chehab 	mp->hdw = pvr2_hdw_create(intf,devid);
2230c0d06caSMauro Carvalho Chehab 	if (!mp->hdw) {
2240c0d06caSMauro Carvalho Chehab 		pvr2_context_destroy(mp);
2250c0d06caSMauro Carvalho Chehab 		mp = NULL;
2260c0d06caSMauro Carvalho Chehab 		goto done;
2270c0d06caSMauro Carvalho Chehab 	}
2280c0d06caSMauro Carvalho Chehab 	pvr2_context_set_notify(mp, !0);
2290c0d06caSMauro Carvalho Chehab  done:
2300c0d06caSMauro Carvalho Chehab 	return mp;
2310c0d06caSMauro Carvalho Chehab }
2320c0d06caSMauro Carvalho Chehab 
2330c0d06caSMauro Carvalho Chehab 
pvr2_context_reset_input_limits(struct pvr2_context * mp)2340c0d06caSMauro Carvalho Chehab static void pvr2_context_reset_input_limits(struct pvr2_context *mp)
2350c0d06caSMauro Carvalho Chehab {
2360c0d06caSMauro Carvalho Chehab 	unsigned int tmsk,mmsk;
2370c0d06caSMauro Carvalho Chehab 	struct pvr2_channel *cp;
2380c0d06caSMauro Carvalho Chehab 	struct pvr2_hdw *hdw = mp->hdw;
2390c0d06caSMauro Carvalho Chehab 	mmsk = pvr2_hdw_get_input_available(hdw);
2400c0d06caSMauro Carvalho Chehab 	tmsk = mmsk;
2410c0d06caSMauro Carvalho Chehab 	for (cp = mp->mc_first; cp; cp = cp->mc_next) {
2420c0d06caSMauro Carvalho Chehab 		if (!cp->input_mask) continue;
2430c0d06caSMauro Carvalho Chehab 		tmsk &= cp->input_mask;
2440c0d06caSMauro Carvalho Chehab 	}
2450c0d06caSMauro Carvalho Chehab 	pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk);
2460c0d06caSMauro Carvalho Chehab 	pvr2_hdw_commit_ctl(hdw);
2470c0d06caSMauro Carvalho Chehab }
2480c0d06caSMauro Carvalho Chehab 
2490c0d06caSMauro Carvalho Chehab 
pvr2_context_enter(struct pvr2_context * mp)2500c0d06caSMauro Carvalho Chehab static void pvr2_context_enter(struct pvr2_context *mp)
2510c0d06caSMauro Carvalho Chehab {
2520c0d06caSMauro Carvalho Chehab 	mutex_lock(&mp->mutex);
2530c0d06caSMauro Carvalho Chehab }
2540c0d06caSMauro Carvalho Chehab 
2550c0d06caSMauro Carvalho Chehab 
pvr2_context_exit(struct pvr2_context * mp)2560c0d06caSMauro Carvalho Chehab static void pvr2_context_exit(struct pvr2_context *mp)
2570c0d06caSMauro Carvalho Chehab {
2580c0d06caSMauro Carvalho Chehab 	int destroy_flag = 0;
2590c0d06caSMauro Carvalho Chehab 	if (!(mp->mc_first || !mp->disconnect_flag)) {
2600c0d06caSMauro Carvalho Chehab 		destroy_flag = !0;
2610c0d06caSMauro Carvalho Chehab 	}
2620c0d06caSMauro Carvalho Chehab 	mutex_unlock(&mp->mutex);
2630c0d06caSMauro Carvalho Chehab 	if (destroy_flag) pvr2_context_notify(mp);
2640c0d06caSMauro Carvalho Chehab }
2650c0d06caSMauro Carvalho Chehab 
2660c0d06caSMauro Carvalho Chehab 
pvr2_context_disconnect(struct pvr2_context * mp)2670c0d06caSMauro Carvalho Chehab void pvr2_context_disconnect(struct pvr2_context *mp)
2680c0d06caSMauro Carvalho Chehab {
2690c0d06caSMauro Carvalho Chehab 	pvr2_hdw_disconnect(mp->hdw);
2702cf0005dSRicardo B. Marliere 	if (!pvr2_context_shutok())
2710c0d06caSMauro Carvalho Chehab 		pvr2_context_notify(mp);
272*8e60b99fSEdward Adam Davis 	mp->disconnect_flag = !0;
2730c0d06caSMauro Carvalho Chehab }
2740c0d06caSMauro Carvalho Chehab 
2750c0d06caSMauro Carvalho Chehab 
pvr2_channel_init(struct pvr2_channel * cp,struct pvr2_context * mp)2760c0d06caSMauro Carvalho Chehab void pvr2_channel_init(struct pvr2_channel *cp,struct pvr2_context *mp)
2770c0d06caSMauro Carvalho Chehab {
2780c0d06caSMauro Carvalho Chehab 	pvr2_context_enter(mp);
2790c0d06caSMauro Carvalho Chehab 	cp->hdw = mp->hdw;
2800c0d06caSMauro Carvalho Chehab 	cp->mc_head = mp;
2810c0d06caSMauro Carvalho Chehab 	cp->mc_next = NULL;
2820c0d06caSMauro Carvalho Chehab 	cp->mc_prev = mp->mc_last;
2830c0d06caSMauro Carvalho Chehab 	if (mp->mc_last) {
2840c0d06caSMauro Carvalho Chehab 		mp->mc_last->mc_next = cp;
2850c0d06caSMauro Carvalho Chehab 	} else {
2860c0d06caSMauro Carvalho Chehab 		mp->mc_first = cp;
2870c0d06caSMauro Carvalho Chehab 	}
2880c0d06caSMauro Carvalho Chehab 	mp->mc_last = cp;
2890c0d06caSMauro Carvalho Chehab 	pvr2_context_exit(mp);
2900c0d06caSMauro Carvalho Chehab }
2910c0d06caSMauro Carvalho Chehab 
2920c0d06caSMauro Carvalho Chehab 
pvr2_channel_disclaim_stream(struct pvr2_channel * cp)2930c0d06caSMauro Carvalho Chehab static void pvr2_channel_disclaim_stream(struct pvr2_channel *cp)
2940c0d06caSMauro Carvalho Chehab {
2950c0d06caSMauro Carvalho Chehab 	if (!cp->stream) return;
2960c0d06caSMauro Carvalho Chehab 	pvr2_stream_kill(cp->stream->stream);
2970c0d06caSMauro Carvalho Chehab 	cp->stream->user = NULL;
2980c0d06caSMauro Carvalho Chehab 	cp->stream = NULL;
2990c0d06caSMauro Carvalho Chehab }
3000c0d06caSMauro Carvalho Chehab 
3010c0d06caSMauro Carvalho Chehab 
pvr2_channel_done(struct pvr2_channel * cp)3020c0d06caSMauro Carvalho Chehab void pvr2_channel_done(struct pvr2_channel *cp)
3030c0d06caSMauro Carvalho Chehab {
3040c0d06caSMauro Carvalho Chehab 	struct pvr2_context *mp = cp->mc_head;
3050c0d06caSMauro Carvalho Chehab 	pvr2_context_enter(mp);
3060c0d06caSMauro Carvalho Chehab 	cp->input_mask = 0;
3070c0d06caSMauro Carvalho Chehab 	pvr2_channel_disclaim_stream(cp);
3080c0d06caSMauro Carvalho Chehab 	pvr2_context_reset_input_limits(mp);
3090c0d06caSMauro Carvalho Chehab 	if (cp->mc_next) {
3100c0d06caSMauro Carvalho Chehab 		cp->mc_next->mc_prev = cp->mc_prev;
3110c0d06caSMauro Carvalho Chehab 	} else {
3120c0d06caSMauro Carvalho Chehab 		mp->mc_last = cp->mc_prev;
3130c0d06caSMauro Carvalho Chehab 	}
3140c0d06caSMauro Carvalho Chehab 	if (cp->mc_prev) {
3150c0d06caSMauro Carvalho Chehab 		cp->mc_prev->mc_next = cp->mc_next;
3160c0d06caSMauro Carvalho Chehab 	} else {
3170c0d06caSMauro Carvalho Chehab 		mp->mc_first = cp->mc_next;
3180c0d06caSMauro Carvalho Chehab 	}
3190c0d06caSMauro Carvalho Chehab 	cp->hdw = NULL;
3200c0d06caSMauro Carvalho Chehab 	pvr2_context_exit(mp);
3210c0d06caSMauro Carvalho Chehab }
3220c0d06caSMauro Carvalho Chehab 
3230c0d06caSMauro Carvalho Chehab 
pvr2_channel_limit_inputs(struct pvr2_channel * cp,unsigned int cmsk)3240c0d06caSMauro Carvalho Chehab int pvr2_channel_limit_inputs(struct pvr2_channel *cp,unsigned int cmsk)
3250c0d06caSMauro Carvalho Chehab {
3260c0d06caSMauro Carvalho Chehab 	unsigned int tmsk,mmsk;
3270c0d06caSMauro Carvalho Chehab 	int ret = 0;
3280c0d06caSMauro Carvalho Chehab 	struct pvr2_channel *p2;
3290c0d06caSMauro Carvalho Chehab 	struct pvr2_hdw *hdw = cp->hdw;
3300c0d06caSMauro Carvalho Chehab 
3310c0d06caSMauro Carvalho Chehab 	mmsk = pvr2_hdw_get_input_available(hdw);
3320c0d06caSMauro Carvalho Chehab 	cmsk &= mmsk;
3330c0d06caSMauro Carvalho Chehab 	if (cmsk == cp->input_mask) {
3340c0d06caSMauro Carvalho Chehab 		/* No change; nothing to do */
3350c0d06caSMauro Carvalho Chehab 		return 0;
3360c0d06caSMauro Carvalho Chehab 	}
3370c0d06caSMauro Carvalho Chehab 
3380c0d06caSMauro Carvalho Chehab 	pvr2_context_enter(cp->mc_head);
3390c0d06caSMauro Carvalho Chehab 	do {
3400c0d06caSMauro Carvalho Chehab 		if (!cmsk) {
3410c0d06caSMauro Carvalho Chehab 			cp->input_mask = 0;
3420c0d06caSMauro Carvalho Chehab 			pvr2_context_reset_input_limits(cp->mc_head);
3430c0d06caSMauro Carvalho Chehab 			break;
3440c0d06caSMauro Carvalho Chehab 		}
3450c0d06caSMauro Carvalho Chehab 		tmsk = mmsk;
3460c0d06caSMauro Carvalho Chehab 		for (p2 = cp->mc_head->mc_first; p2; p2 = p2->mc_next) {
3470c0d06caSMauro Carvalho Chehab 			if (p2 == cp) continue;
3480c0d06caSMauro Carvalho Chehab 			if (!p2->input_mask) continue;
3490c0d06caSMauro Carvalho Chehab 			tmsk &= p2->input_mask;
3500c0d06caSMauro Carvalho Chehab 		}
3510c0d06caSMauro Carvalho Chehab 		if (!(tmsk & cmsk)) {
3520c0d06caSMauro Carvalho Chehab 			ret = -EPERM;
3530c0d06caSMauro Carvalho Chehab 			break;
3540c0d06caSMauro Carvalho Chehab 		}
3550c0d06caSMauro Carvalho Chehab 		tmsk &= cmsk;
3560c0d06caSMauro Carvalho Chehab 		if ((ret = pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk)) != 0) {
3570c0d06caSMauro Carvalho Chehab 			/* Internal failure changing allowed list; probably
3580c0d06caSMauro Carvalho Chehab 			   should not happen, but react if it does. */
3590c0d06caSMauro Carvalho Chehab 			break;
3600c0d06caSMauro Carvalho Chehab 		}
3610c0d06caSMauro Carvalho Chehab 		cp->input_mask = cmsk;
3620c0d06caSMauro Carvalho Chehab 		pvr2_hdw_commit_ctl(hdw);
3630c0d06caSMauro Carvalho Chehab 	} while (0);
3640c0d06caSMauro Carvalho Chehab 	pvr2_context_exit(cp->mc_head);
3650c0d06caSMauro Carvalho Chehab 	return ret;
3660c0d06caSMauro Carvalho Chehab }
3670c0d06caSMauro Carvalho Chehab 
3680c0d06caSMauro Carvalho Chehab 
pvr2_channel_get_limited_inputs(struct pvr2_channel * cp)3690c0d06caSMauro Carvalho Chehab unsigned int pvr2_channel_get_limited_inputs(struct pvr2_channel *cp)
3700c0d06caSMauro Carvalho Chehab {
3710c0d06caSMauro Carvalho Chehab 	return cp->input_mask;
3720c0d06caSMauro Carvalho Chehab }
3730c0d06caSMauro Carvalho Chehab 
3740c0d06caSMauro Carvalho Chehab 
pvr2_channel_claim_stream(struct pvr2_channel * cp,struct pvr2_context_stream * sp)3750c0d06caSMauro Carvalho Chehab int pvr2_channel_claim_stream(struct pvr2_channel *cp,
3760c0d06caSMauro Carvalho Chehab 			      struct pvr2_context_stream *sp)
3770c0d06caSMauro Carvalho Chehab {
3780c0d06caSMauro Carvalho Chehab 	int code = 0;
3790c0d06caSMauro Carvalho Chehab 	pvr2_context_enter(cp->mc_head); do {
3800c0d06caSMauro Carvalho Chehab 		if (sp == cp->stream) break;
3810c0d06caSMauro Carvalho Chehab 		if (sp && sp->user) {
3820c0d06caSMauro Carvalho Chehab 			code = -EBUSY;
3830c0d06caSMauro Carvalho Chehab 			break;
3840c0d06caSMauro Carvalho Chehab 		}
3850c0d06caSMauro Carvalho Chehab 		pvr2_channel_disclaim_stream(cp);
3860c0d06caSMauro Carvalho Chehab 		if (!sp) break;
3870c0d06caSMauro Carvalho Chehab 		sp->user = cp;
3880c0d06caSMauro Carvalho Chehab 		cp->stream = sp;
389f419edd4SMauro Carvalho Chehab 	} while (0);
390f419edd4SMauro Carvalho Chehab 	pvr2_context_exit(cp->mc_head);
3910c0d06caSMauro Carvalho Chehab 	return code;
3920c0d06caSMauro Carvalho Chehab }
3930c0d06caSMauro Carvalho Chehab 
3940c0d06caSMauro Carvalho Chehab 
3950c0d06caSMauro Carvalho Chehab // This is the marker for the real beginning of a legitimate mpeg2 stream.
3960c0d06caSMauro Carvalho Chehab static char stream_sync_key[] = {
3970c0d06caSMauro Carvalho Chehab 	0x00, 0x00, 0x01, 0xba,
3980c0d06caSMauro Carvalho Chehab };
3990c0d06caSMauro Carvalho Chehab 
pvr2_channel_create_mpeg_stream(struct pvr2_context_stream * sp)4000c0d06caSMauro Carvalho Chehab struct pvr2_ioread *pvr2_channel_create_mpeg_stream(
4010c0d06caSMauro Carvalho Chehab 	struct pvr2_context_stream *sp)
4020c0d06caSMauro Carvalho Chehab {
4030c0d06caSMauro Carvalho Chehab 	struct pvr2_ioread *cp;
4040c0d06caSMauro Carvalho Chehab 	cp = pvr2_ioread_create();
4050c0d06caSMauro Carvalho Chehab 	if (!cp) return NULL;
4060c0d06caSMauro Carvalho Chehab 	pvr2_ioread_setup(cp,sp->stream);
4070c0d06caSMauro Carvalho Chehab 	pvr2_ioread_set_sync_key(cp,stream_sync_key,sizeof(stream_sync_key));
4080c0d06caSMauro Carvalho Chehab 	return cp;
4090c0d06caSMauro Carvalho Chehab }
410