| Mike Isely | d855497 | 2006-06-26 20:58:46 -0300 | [diff] [blame] | 1 | /* | 
| Mike Isely | d855497 | 2006-06-26 20:58:46 -0300 | [diff] [blame] | 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" | 
| Mike Isely | e5be15c | 2008-04-07 02:22:04 -0300 | [diff] [blame] | 25 | #include <linux/wait.h> | 
| Mike Isely | 794b160 | 2008-04-22 14:45:45 -0300 | [diff] [blame] | 26 | #include <linux/kthread.h> | 
| Mike Isely | d855497 | 2006-06-26 20:58:46 -0300 | [diff] [blame] | 27 | #include <linux/errno.h> | 
 | 28 | #include <linux/string.h> | 
 | 29 | #include <linux/slab.h> | 
| Mike Isely | d855497 | 2006-06-26 20:58:46 -0300 | [diff] [blame] | 30 |  | 
| Mike Isely | e5be15c | 2008-04-07 02:22:04 -0300 | [diff] [blame] | 31 | static struct pvr2_context *pvr2_context_exist_first; | 
 | 32 | static struct pvr2_context *pvr2_context_exist_last; | 
 | 33 | static struct pvr2_context *pvr2_context_notify_first; | 
 | 34 | static struct pvr2_context *pvr2_context_notify_last; | 
 | 35 | static DEFINE_MUTEX(pvr2_context_mutex); | 
 | 36 | static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_sync_data); | 
| Mike Isely | 18ecbb4 | 2008-04-09 05:14:11 -0300 | [diff] [blame] | 37 | static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_cleanup_data); | 
 | 38 | static int pvr2_context_cleanup_flag; | 
 | 39 | static int pvr2_context_cleaned_flag; | 
| Mike Isely | e5be15c | 2008-04-07 02:22:04 -0300 | [diff] [blame] | 40 | static struct task_struct *pvr2_context_thread_ptr; | 
 | 41 |  | 
 | 42 |  | 
 | 43 | static 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 |  | 
| Mike Isely | d855497 | 2006-06-26 20:58:46 -0300 | [diff] [blame] | 79 |  | 
 | 80 | static void pvr2_context_destroy(struct pvr2_context *mp) | 
 | 81 | { | 
| Mike Isely | 794b160 | 2008-04-22 14:45:45 -0300 | [diff] [blame] | 82 | 	pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (destroy)",mp); | 
| Mike Isely | 681c739 | 2007-11-26 01:48:52 -0300 | [diff] [blame] | 83 | 	if (mp->hdw) pvr2_hdw_destroy(mp->hdw); | 
| Mike Isely | e5be15c | 2008-04-07 02:22:04 -0300 | [diff] [blame] | 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); | 
| Mike Isely | d855497 | 2006-06-26 20:58:46 -0300 | [diff] [blame] | 102 | 	kfree(mp); | 
 | 103 | } | 
 | 104 |  | 
 | 105 |  | 
| Mike Isely | 794b160 | 2008-04-22 14:45:45 -0300 | [diff] [blame] | 106 | static void pvr2_context_notify(struct pvr2_context *mp) | 
| Mike Isely | d855497 | 2006-06-26 20:58:46 -0300 | [diff] [blame] | 107 | { | 
| Mike Isely | e5be15c | 2008-04-07 02:22:04 -0300 | [diff] [blame] | 108 | 	pvr2_context_set_notify(mp,!0); | 
| Mike Isely | 794b160 | 2008-04-22 14:45:45 -0300 | [diff] [blame] | 109 | } | 
| Mike Isely | d855497 | 2006-06-26 20:58:46 -0300 | [diff] [blame] | 110 |  | 
| Mike Isely | 794b160 | 2008-04-22 14:45:45 -0300 | [diff] [blame] | 111 |  | 
| Mike Isely | e5be15c | 2008-04-07 02:22:04 -0300 | [diff] [blame] | 112 | static void pvr2_context_check(struct pvr2_context *mp) | 
| Mike Isely | 794b160 | 2008-04-22 14:45:45 -0300 | [diff] [blame] | 113 | { | 
| Mike Isely | e5be15c | 2008-04-07 02:22:04 -0300 | [diff] [blame] | 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; | 
| Mike Isely | 794b160 | 2008-04-22 14:45:45 -0300 | [diff] [blame] | 119 | 		pvr2_trace(PVR2_TRACE_CTXT, | 
| Mike Isely | e5be15c | 2008-04-07 02:22:04 -0300 | [diff] [blame] | 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 | 		} | 
| Mike Isely | 681c739 | 2007-11-26 01:48:52 -0300 | [diff] [blame] | 141 | 	} | 
| David Howells | c402895 | 2006-11-22 14:57:56 +0000 | [diff] [blame] | 142 |  | 
| Mike Isely | e5be15c | 2008-04-07 02:22:04 -0300 | [diff] [blame] | 143 | 	for (ch1 = mp->mc_first; ch1; ch1 = ch2) { | 
 | 144 | 		ch2 = ch1->mc_next; | 
 | 145 | 		if (ch1->check_func) ch1->check_func(ch1); | 
| Mike Isely | 794b160 | 2008-04-22 14:45:45 -0300 | [diff] [blame] | 146 | 	} | 
| Mike Isely | e5be15c | 2008-04-07 02:22:04 -0300 | [diff] [blame] | 147 |  | 
 | 148 | 	if (mp->disconnect_flag && !mp->mc_first) { | 
 | 149 | 		/* Go away... */ | 
 | 150 | 		pvr2_context_destroy(mp); | 
 | 151 | 		return; | 
 | 152 | 	} | 
 | 153 | } | 
 | 154 |  | 
 | 155 |  | 
 | 156 | static int pvr2_context_shutok(void) | 
 | 157 | { | 
| Mike Isely | 18ecbb4 | 2008-04-09 05:14:11 -0300 | [diff] [blame] | 158 | 	return pvr2_context_cleanup_flag && (pvr2_context_exist_first == NULL); | 
| Mike Isely | e5be15c | 2008-04-07 02:22:04 -0300 | [diff] [blame] | 159 | } | 
 | 160 |  | 
 | 161 |  | 
 | 162 | static 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 |  | 
| Mike Isely | 18ecbb4 | 2008-04-09 05:14:11 -0300 | [diff] [blame] | 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 |  | 
| Mike Isely | e5be15c | 2008-04-07 02:22:04 -0300 | [diff] [blame] | 188 | 	pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread end"); | 
 | 189 |  | 
| Mike Isely | 794b160 | 2008-04-22 14:45:45 -0300 | [diff] [blame] | 190 | 	return 0; | 
 | 191 | } | 
| Mike Isely | d855497 | 2006-06-26 20:58:46 -0300 | [diff] [blame] | 192 |  | 
 | 193 |  | 
| Mike Isely | e5be15c | 2008-04-07 02:22:04 -0300 | [diff] [blame] | 194 | int pvr2_context_global_init(void) | 
 | 195 | { | 
 | 196 | 	pvr2_context_thread_ptr = kthread_run(pvr2_context_thread_func, | 
| Harvey Harrison | a6a3a17 | 2008-04-28 16:50:03 -0700 | [diff] [blame] | 197 | 					      NULL, | 
| Mike Isely | e5be15c | 2008-04-07 02:22:04 -0300 | [diff] [blame] | 198 | 					      "pvrusb2-context"); | 
 | 199 | 	return (pvr2_context_thread_ptr ? 0 : -ENOMEM); | 
 | 200 | } | 
 | 201 |  | 
 | 202 |  | 
 | 203 | void pvr2_context_global_done(void) | 
 | 204 | { | 
| Mike Isely | 18ecbb4 | 2008-04-09 05:14:11 -0300 | [diff] [blame] | 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); | 
| Mike Isely | e5be15c | 2008-04-07 02:22:04 -0300 | [diff] [blame] | 210 | 	kthread_stop(pvr2_context_thread_ptr); | 
 | 211 | } | 
 | 212 |  | 
 | 213 |  | 
| Mike Isely | d855497 | 2006-06-26 20:58:46 -0300 | [diff] [blame] | 214 | struct 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 | { | 
| Mike Isely | a0fd1cb | 2006-06-30 11:35:28 -0300 | [diff] [blame] | 219 | 	struct pvr2_context *mp = NULL; | 
| Mike Isely | ca545f7 | 2007-01-20 00:37:11 -0300 | [diff] [blame] | 220 | 	mp = kzalloc(sizeof(*mp),GFP_KERNEL); | 
| Mike Isely | d855497 | 2006-06-26 20:58:46 -0300 | [diff] [blame] | 221 | 	if (!mp) goto done; | 
| Mike Isely | 794b160 | 2008-04-22 14:45:45 -0300 | [diff] [blame] | 222 | 	pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (create)",mp); | 
| Mike Isely | d855497 | 2006-06-26 20:58:46 -0300 | [diff] [blame] | 223 | 	mp->setup_func = setup_func; | 
 | 224 | 	mutex_init(&mp->mutex); | 
| Mike Isely | e5be15c | 2008-04-07 02:22:04 -0300 | [diff] [blame] | 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); | 
| Mike Isely | d855497 | 2006-06-26 20:58:46 -0300 | [diff] [blame] | 235 | 	mp->hdw = pvr2_hdw_create(intf,devid); | 
 | 236 | 	if (!mp->hdw) { | 
 | 237 | 		pvr2_context_destroy(mp); | 
| Mike Isely | a0fd1cb | 2006-06-30 11:35:28 -0300 | [diff] [blame] | 238 | 		mp = NULL; | 
| Mike Isely | d855497 | 2006-06-26 20:58:46 -0300 | [diff] [blame] | 239 | 		goto done; | 
 | 240 | 	} | 
| Mike Isely | e5be15c | 2008-04-07 02:22:04 -0300 | [diff] [blame] | 241 | 	pvr2_context_set_notify(mp, !0); | 
| Mike Isely | d855497 | 2006-06-26 20:58:46 -0300 | [diff] [blame] | 242 |  done: | 
 | 243 | 	return mp; | 
 | 244 | } | 
 | 245 |  | 
 | 246 |  | 
| Mike Isely | 1cb03b7 | 2008-04-21 03:47:43 -0300 | [diff] [blame] | 247 | static 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 |  | 
| Mike Isely | 794b160 | 2008-04-22 14:45:45 -0300 | [diff] [blame] | 263 | static void pvr2_context_enter(struct pvr2_context *mp) | 
| Mike Isely | d855497 | 2006-06-26 20:58:46 -0300 | [diff] [blame] | 264 | { | 
 | 265 | 	mutex_lock(&mp->mutex); | 
| Mike Isely | d855497 | 2006-06-26 20:58:46 -0300 | [diff] [blame] | 266 | } | 
 | 267 |  | 
 | 268 |  | 
| Mike Isely | 794b160 | 2008-04-22 14:45:45 -0300 | [diff] [blame] | 269 | static void pvr2_context_exit(struct pvr2_context *mp) | 
| Mike Isely | d855497 | 2006-06-26 20:58:46 -0300 | [diff] [blame] | 270 | { | 
 | 271 | 	int destroy_flag = 0; | 
 | 272 | 	if (!(mp->mc_first || !mp->disconnect_flag)) { | 
 | 273 | 		destroy_flag = !0; | 
 | 274 | 	} | 
| Mike Isely | d855497 | 2006-06-26 20:58:46 -0300 | [diff] [blame] | 275 | 	mutex_unlock(&mp->mutex); | 
| Mike Isely | 794b160 | 2008-04-22 14:45:45 -0300 | [diff] [blame] | 276 | 	if (destroy_flag) pvr2_context_notify(mp); | 
| Mike Isely | d855497 | 2006-06-26 20:58:46 -0300 | [diff] [blame] | 277 | } | 
 | 278 |  | 
 | 279 |  | 
 | 280 | void pvr2_context_disconnect(struct pvr2_context *mp) | 
 | 281 | { | 
| Mike Isely | 794b160 | 2008-04-22 14:45:45 -0300 | [diff] [blame] | 282 | 	pvr2_hdw_disconnect(mp->hdw); | 
 | 283 | 	mp->disconnect_flag = !0; | 
 | 284 | 	pvr2_context_notify(mp); | 
| Mike Isely | d855497 | 2006-06-26 20:58:46 -0300 | [diff] [blame] | 285 | } | 
 | 286 |  | 
 | 287 |  | 
 | 288 | void pvr2_channel_init(struct pvr2_channel *cp,struct pvr2_context *mp) | 
 | 289 | { | 
| Mike Isely | 794b160 | 2008-04-22 14:45:45 -0300 | [diff] [blame] | 290 | 	pvr2_context_enter(mp); | 
| Mike Isely | d855497 | 2006-06-26 20:58:46 -0300 | [diff] [blame] | 291 | 	cp->hdw = mp->hdw; | 
 | 292 | 	cp->mc_head = mp; | 
| Mike Isely | a0fd1cb | 2006-06-30 11:35:28 -0300 | [diff] [blame] | 293 | 	cp->mc_next = NULL; | 
| Mike Isely | d855497 | 2006-06-26 20:58:46 -0300 | [diff] [blame] | 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; | 
| Mike Isely | 794b160 | 2008-04-22 14:45:45 -0300 | [diff] [blame] | 301 | 	pvr2_context_exit(mp); | 
| Mike Isely | d855497 | 2006-06-26 20:58:46 -0300 | [diff] [blame] | 302 | } | 
 | 303 |  | 
 | 304 |  | 
 | 305 | static void pvr2_channel_disclaim_stream(struct pvr2_channel *cp) | 
 | 306 | { | 
 | 307 | 	if (!cp->stream) return; | 
 | 308 | 	pvr2_stream_kill(cp->stream->stream); | 
| Mike Isely | a0fd1cb | 2006-06-30 11:35:28 -0300 | [diff] [blame] | 309 | 	cp->stream->user = NULL; | 
 | 310 | 	cp->stream = NULL; | 
| Mike Isely | d855497 | 2006-06-26 20:58:46 -0300 | [diff] [blame] | 311 | } | 
 | 312 |  | 
 | 313 |  | 
 | 314 | void pvr2_channel_done(struct pvr2_channel *cp) | 
 | 315 | { | 
 | 316 | 	struct pvr2_context *mp = cp->mc_head; | 
| Mike Isely | 794b160 | 2008-04-22 14:45:45 -0300 | [diff] [blame] | 317 | 	pvr2_context_enter(mp); | 
| Mike Isely | 1cb03b7 | 2008-04-21 03:47:43 -0300 | [diff] [blame] | 318 | 	cp->input_mask = 0; | 
| Mike Isely | d855497 | 2006-06-26 20:58:46 -0300 | [diff] [blame] | 319 | 	pvr2_channel_disclaim_stream(cp); | 
| Mike Isely | 1cb03b7 | 2008-04-21 03:47:43 -0300 | [diff] [blame] | 320 | 	pvr2_context_reset_input_limits(mp); | 
| Mike Isely | d855497 | 2006-06-26 20:58:46 -0300 | [diff] [blame] | 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 | 	} | 
| Mike Isely | a0fd1cb | 2006-06-30 11:35:28 -0300 | [diff] [blame] | 331 | 	cp->hdw = NULL; | 
| Mike Isely | 794b160 | 2008-04-22 14:45:45 -0300 | [diff] [blame] | 332 | 	pvr2_context_exit(mp); | 
| Mike Isely | d855497 | 2006-06-26 20:58:46 -0300 | [diff] [blame] | 333 | } | 
 | 334 |  | 
 | 335 |  | 
| Mike Isely | 1cb03b7 | 2008-04-21 03:47:43 -0300 | [diff] [blame] | 336 | int 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 |  | 
 | 381 | unsigned int pvr2_channel_get_limited_inputs(struct pvr2_channel *cp) | 
 | 382 | { | 
 | 383 | 	return cp->input_mask; | 
 | 384 | } | 
 | 385 |  | 
 | 386 |  | 
| Mike Isely | d855497 | 2006-06-26 20:58:46 -0300 | [diff] [blame] | 387 | int 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; | 
| Mike Isely | 99a6acf | 2008-04-22 14:45:39 -0300 | [diff] [blame] | 393 | 		if (sp && sp->user) { | 
| Mike Isely | d855497 | 2006-06-26 20:58:46 -0300 | [diff] [blame] | 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. | 
 | 407 | static char stream_sync_key[] = { | 
 | 408 | 	0x00, 0x00, 0x01, 0xba, | 
 | 409 | }; | 
 | 410 |  | 
 | 411 | struct pvr2_ioread *pvr2_channel_create_mpeg_stream( | 
 | 412 | 	struct pvr2_context_stream *sp) | 
 | 413 | { | 
 | 414 | 	struct pvr2_ioread *cp; | 
 | 415 | 	cp = pvr2_ioread_create(); | 
| Mike Isely | a0fd1cb | 2006-06-30 11:35:28 -0300 | [diff] [blame] | 416 | 	if (!cp) return NULL; | 
| Mike Isely | d855497 | 2006-06-26 20:58:46 -0300 | [diff] [blame] | 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 |   */ |