• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  *
4  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
5  */
6 
7 #include "pvrusb2-context.h"
8 #include "pvrusb2-io.h"
9 #include "pvrusb2-ioread.h"
10 #include "pvrusb2-hdw.h"
11 #include "pvrusb2-debug.h"
12 #include <linux/wait.h>
13 #include <linux/kthread.h>
14 #include <linux/errno.h>
15 #include <linux/string.h>
16 #include <linux/slab.h>
17 
18 static struct pvr2_context *pvr2_context_exist_first;
19 static struct pvr2_context *pvr2_context_exist_last;
20 static struct pvr2_context *pvr2_context_notify_first;
21 static struct pvr2_context *pvr2_context_notify_last;
22 static DEFINE_MUTEX(pvr2_context_mutex);
23 static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_sync_data);
24 static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_cleanup_data);
25 static int pvr2_context_cleanup_flag;
26 static int pvr2_context_cleaned_flag;
27 static struct task_struct *pvr2_context_thread_ptr;
28 
29 
pvr2_context_set_notify(struct pvr2_context * mp,int fl)30 static void pvr2_context_set_notify(struct pvr2_context *mp, int fl)
31 {
32 	int signal_flag = 0;
33 	mutex_lock(&pvr2_context_mutex);
34 	if (fl) {
35 		if (!mp->notify_flag) {
36 			signal_flag = (pvr2_context_notify_first == NULL);
37 			mp->notify_prev = pvr2_context_notify_last;
38 			mp->notify_next = NULL;
39 			pvr2_context_notify_last = mp;
40 			if (mp->notify_prev) {
41 				mp->notify_prev->notify_next = mp;
42 			} else {
43 				pvr2_context_notify_first = mp;
44 			}
45 			mp->notify_flag = !0;
46 		}
47 	} else {
48 		if (mp->notify_flag) {
49 			mp->notify_flag = 0;
50 			if (mp->notify_next) {
51 				mp->notify_next->notify_prev = mp->notify_prev;
52 			} else {
53 				pvr2_context_notify_last = mp->notify_prev;
54 			}
55 			if (mp->notify_prev) {
56 				mp->notify_prev->notify_next = mp->notify_next;
57 			} else {
58 				pvr2_context_notify_first = mp->notify_next;
59 			}
60 		}
61 	}
62 	mutex_unlock(&pvr2_context_mutex);
63 	if (signal_flag) wake_up(&pvr2_context_sync_data);
64 }
65 
66 
pvr2_context_destroy(struct pvr2_context * mp)67 static void pvr2_context_destroy(struct pvr2_context *mp)
68 {
69 	pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (destroy)",mp);
70 	pvr2_hdw_destroy(mp->hdw);
71 	pvr2_context_set_notify(mp, 0);
72 	mutex_lock(&pvr2_context_mutex);
73 	if (mp->exist_next) {
74 		mp->exist_next->exist_prev = mp->exist_prev;
75 	} else {
76 		pvr2_context_exist_last = mp->exist_prev;
77 	}
78 	if (mp->exist_prev) {
79 		mp->exist_prev->exist_next = mp->exist_next;
80 	} else {
81 		pvr2_context_exist_first = mp->exist_next;
82 	}
83 	if (!pvr2_context_exist_first) {
84 		/* Trigger wakeup on control thread in case it is waiting
85 		   for an exit condition. */
86 		wake_up(&pvr2_context_sync_data);
87 	}
88 	mutex_unlock(&pvr2_context_mutex);
89 	kfree(mp);
90 }
91 
92 
pvr2_context_notify(struct pvr2_context * mp)93 static void pvr2_context_notify(struct pvr2_context *mp)
94 {
95 	pvr2_context_set_notify(mp,!0);
96 }
97 
98 
pvr2_context_check(struct pvr2_context * mp)99 static void pvr2_context_check(struct pvr2_context *mp)
100 {
101 	struct pvr2_channel *ch1, *ch2;
102 	pvr2_trace(PVR2_TRACE_CTXT,
103 		   "pvr2_context %p (notify)", mp);
104 	if (!mp->initialized_flag && !mp->disconnect_flag) {
105 		mp->initialized_flag = !0;
106 		pvr2_trace(PVR2_TRACE_CTXT,
107 			   "pvr2_context %p (initialize)", mp);
108 		/* Finish hardware initialization */
109 		if (pvr2_hdw_initialize(mp->hdw,
110 					(void (*)(void *))pvr2_context_notify,
111 					mp)) {
112 			mp->video_stream.stream =
113 				pvr2_hdw_get_video_stream(mp->hdw);
114 			/* Trigger interface initialization.  By doing this
115 			   here initialization runs in our own safe and
116 			   cozy thread context. */
117 			if (mp->setup_func) mp->setup_func(mp);
118 		} else {
119 			pvr2_trace(PVR2_TRACE_CTXT,
120 				   "pvr2_context %p (thread skipping setup)",
121 				   mp);
122 			/* Even though initialization did not succeed,
123 			   we're still going to continue anyway.  We need
124 			   to do this in order to await the expected
125 			   disconnect (which we will detect in the normal
126 			   course of operation). */
127 		}
128 	}
129 
130 	for (ch1 = mp->mc_first; ch1; ch1 = ch2) {
131 		ch2 = ch1->mc_next;
132 		if (ch1->check_func) ch1->check_func(ch1);
133 	}
134 
135 	if (mp->disconnect_flag && !mp->mc_first) {
136 		/* Go away... */
137 		pvr2_context_destroy(mp);
138 		return;
139 	}
140 }
141 
142 
pvr2_context_shutok(void)143 static int pvr2_context_shutok(void)
144 {
145 	return pvr2_context_cleanup_flag && (pvr2_context_exist_first == NULL);
146 }
147 
148 
pvr2_context_thread_func(void * foo)149 static int pvr2_context_thread_func(void *foo)
150 {
151 	struct pvr2_context *mp;
152 
153 	pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread start");
154 
155 	do {
156 		while ((mp = pvr2_context_notify_first) != NULL) {
157 			pvr2_context_set_notify(mp, 0);
158 			pvr2_context_check(mp);
159 		}
160 		wait_event_interruptible(
161 			pvr2_context_sync_data,
162 			((pvr2_context_notify_first != NULL) ||
163 			 pvr2_context_shutok()));
164 	} while (!pvr2_context_shutok());
165 
166 	pvr2_context_cleaned_flag = !0;
167 	wake_up(&pvr2_context_cleanup_data);
168 
169 	pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread cleaned up");
170 
171 	wait_event_interruptible(
172 		pvr2_context_sync_data,
173 		kthread_should_stop());
174 
175 	pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread end");
176 
177 	return 0;
178 }
179 
180 
pvr2_context_global_init(void)181 int pvr2_context_global_init(void)
182 {
183 	pvr2_context_thread_ptr = kthread_run(pvr2_context_thread_func,
184 					      NULL,
185 					      "pvrusb2-context");
186 	return IS_ERR(pvr2_context_thread_ptr) ? -ENOMEM : 0;
187 }
188 
189 
pvr2_context_global_done(void)190 void pvr2_context_global_done(void)
191 {
192 	pvr2_context_cleanup_flag = !0;
193 	wake_up(&pvr2_context_sync_data);
194 	wait_event_interruptible(
195 		pvr2_context_cleanup_data,
196 		pvr2_context_cleaned_flag);
197 	kthread_stop(pvr2_context_thread_ptr);
198 }
199 
200 
pvr2_context_create(struct usb_interface * intf,const struct usb_device_id * devid,void (* setup_func)(struct pvr2_context *))201 struct pvr2_context *pvr2_context_create(
202 	struct usb_interface *intf,
203 	const struct usb_device_id *devid,
204 	void (*setup_func)(struct pvr2_context *))
205 {
206 	struct pvr2_context *mp = NULL;
207 	mp = kzalloc(sizeof(*mp),GFP_KERNEL);
208 	if (!mp) goto done;
209 	pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (create)",mp);
210 	mp->setup_func = setup_func;
211 	mutex_init(&mp->mutex);
212 	mutex_lock(&pvr2_context_mutex);
213 	mp->exist_prev = pvr2_context_exist_last;
214 	mp->exist_next = NULL;
215 	pvr2_context_exist_last = mp;
216 	if (mp->exist_prev) {
217 		mp->exist_prev->exist_next = mp;
218 	} else {
219 		pvr2_context_exist_first = mp;
220 	}
221 	mutex_unlock(&pvr2_context_mutex);
222 	mp->hdw = pvr2_hdw_create(intf,devid);
223 	if (!mp->hdw) {
224 		pvr2_context_destroy(mp);
225 		mp = NULL;
226 		goto done;
227 	}
228 	pvr2_context_set_notify(mp, !0);
229  done:
230 	return mp;
231 }
232 
233 
pvr2_context_reset_input_limits(struct pvr2_context * mp)234 static void pvr2_context_reset_input_limits(struct pvr2_context *mp)
235 {
236 	unsigned int tmsk,mmsk;
237 	struct pvr2_channel *cp;
238 	struct pvr2_hdw *hdw = mp->hdw;
239 	mmsk = pvr2_hdw_get_input_available(hdw);
240 	tmsk = mmsk;
241 	for (cp = mp->mc_first; cp; cp = cp->mc_next) {
242 		if (!cp->input_mask) continue;
243 		tmsk &= cp->input_mask;
244 	}
245 	pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk);
246 	pvr2_hdw_commit_ctl(hdw);
247 }
248 
249 
pvr2_context_enter(struct pvr2_context * mp)250 static void pvr2_context_enter(struct pvr2_context *mp)
251 {
252 	mutex_lock(&mp->mutex);
253 }
254 
255 
pvr2_context_exit(struct pvr2_context * mp)256 static void pvr2_context_exit(struct pvr2_context *mp)
257 {
258 	int destroy_flag = 0;
259 	if (!(mp->mc_first || !mp->disconnect_flag)) {
260 		destroy_flag = !0;
261 	}
262 	mutex_unlock(&mp->mutex);
263 	if (destroy_flag) pvr2_context_notify(mp);
264 }
265 
266 
pvr2_context_disconnect(struct pvr2_context * mp)267 void pvr2_context_disconnect(struct pvr2_context *mp)
268 {
269 	pvr2_hdw_disconnect(mp->hdw);
270 	mp->disconnect_flag = !0;
271 	if (!pvr2_context_shutok())
272 		pvr2_context_notify(mp);
273 }
274 
275 
pvr2_channel_init(struct pvr2_channel * cp,struct pvr2_context * mp)276 void pvr2_channel_init(struct pvr2_channel *cp,struct pvr2_context *mp)
277 {
278 	pvr2_context_enter(mp);
279 	cp->hdw = mp->hdw;
280 	cp->mc_head = mp;
281 	cp->mc_next = NULL;
282 	cp->mc_prev = mp->mc_last;
283 	if (mp->mc_last) {
284 		mp->mc_last->mc_next = cp;
285 	} else {
286 		mp->mc_first = cp;
287 	}
288 	mp->mc_last = cp;
289 	pvr2_context_exit(mp);
290 }
291 
292 
pvr2_channel_disclaim_stream(struct pvr2_channel * cp)293 static void pvr2_channel_disclaim_stream(struct pvr2_channel *cp)
294 {
295 	if (!cp->stream) return;
296 	pvr2_stream_kill(cp->stream->stream);
297 	cp->stream->user = NULL;
298 	cp->stream = NULL;
299 }
300 
301 
pvr2_channel_done(struct pvr2_channel * cp)302 void pvr2_channel_done(struct pvr2_channel *cp)
303 {
304 	struct pvr2_context *mp = cp->mc_head;
305 	pvr2_context_enter(mp);
306 	cp->input_mask = 0;
307 	pvr2_channel_disclaim_stream(cp);
308 	pvr2_context_reset_input_limits(mp);
309 	if (cp->mc_next) {
310 		cp->mc_next->mc_prev = cp->mc_prev;
311 	} else {
312 		mp->mc_last = cp->mc_prev;
313 	}
314 	if (cp->mc_prev) {
315 		cp->mc_prev->mc_next = cp->mc_next;
316 	} else {
317 		mp->mc_first = cp->mc_next;
318 	}
319 	cp->hdw = NULL;
320 	pvr2_context_exit(mp);
321 }
322 
323 
pvr2_channel_limit_inputs(struct pvr2_channel * cp,unsigned int cmsk)324 int pvr2_channel_limit_inputs(struct pvr2_channel *cp,unsigned int cmsk)
325 {
326 	unsigned int tmsk,mmsk;
327 	int ret = 0;
328 	struct pvr2_channel *p2;
329 	struct pvr2_hdw *hdw = cp->hdw;
330 
331 	mmsk = pvr2_hdw_get_input_available(hdw);
332 	cmsk &= mmsk;
333 	if (cmsk == cp->input_mask) {
334 		/* No change; nothing to do */
335 		return 0;
336 	}
337 
338 	pvr2_context_enter(cp->mc_head);
339 	do {
340 		if (!cmsk) {
341 			cp->input_mask = 0;
342 			pvr2_context_reset_input_limits(cp->mc_head);
343 			break;
344 		}
345 		tmsk = mmsk;
346 		for (p2 = cp->mc_head->mc_first; p2; p2 = p2->mc_next) {
347 			if (p2 == cp) continue;
348 			if (!p2->input_mask) continue;
349 			tmsk &= p2->input_mask;
350 		}
351 		if (!(tmsk & cmsk)) {
352 			ret = -EPERM;
353 			break;
354 		}
355 		tmsk &= cmsk;
356 		if ((ret = pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk)) != 0) {
357 			/* Internal failure changing allowed list; probably
358 			   should not happen, but react if it does. */
359 			break;
360 		}
361 		cp->input_mask = cmsk;
362 		pvr2_hdw_commit_ctl(hdw);
363 	} while (0);
364 	pvr2_context_exit(cp->mc_head);
365 	return ret;
366 }
367 
368 
pvr2_channel_get_limited_inputs(struct pvr2_channel * cp)369 unsigned int pvr2_channel_get_limited_inputs(struct pvr2_channel *cp)
370 {
371 	return cp->input_mask;
372 }
373 
374 
pvr2_channel_claim_stream(struct pvr2_channel * cp,struct pvr2_context_stream * sp)375 int pvr2_channel_claim_stream(struct pvr2_channel *cp,
376 			      struct pvr2_context_stream *sp)
377 {
378 	int code = 0;
379 	pvr2_context_enter(cp->mc_head); do {
380 		if (sp == cp->stream) break;
381 		if (sp && sp->user) {
382 			code = -EBUSY;
383 			break;
384 		}
385 		pvr2_channel_disclaim_stream(cp);
386 		if (!sp) break;
387 		sp->user = cp;
388 		cp->stream = sp;
389 	} while (0);
390 	pvr2_context_exit(cp->mc_head);
391 	return code;
392 }
393 
394 
395 // This is the marker for the real beginning of a legitimate mpeg2 stream.
396 static char stream_sync_key[] = {
397 	0x00, 0x00, 0x01, 0xba,
398 };
399 
pvr2_channel_create_mpeg_stream(struct pvr2_context_stream * sp)400 struct pvr2_ioread *pvr2_channel_create_mpeg_stream(
401 	struct pvr2_context_stream *sp)
402 {
403 	struct pvr2_ioread *cp;
404 	cp = pvr2_ioread_create();
405 	if (!cp) return NULL;
406 	pvr2_ioread_setup(cp,sp->stream);
407 	pvr2_ioread_set_sync_key(cp,stream_sync_key,sizeof(stream_sync_key));
408 	return cp;
409 }
410