xref: /linux/drivers/media/usb/pvrusb2/pvrusb2-context.c (revision 17cfcb68af3bc7d5e8ae08779b1853310a2949f3)
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 
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 
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 
93 static void pvr2_context_notify(struct pvr2_context *mp)
94 {
95 	pvr2_context_set_notify(mp,!0);
96 }
97 
98 
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 
143 static int pvr2_context_shutok(void)
144 {
145 	return pvr2_context_cleanup_flag && (pvr2_context_exist_first == NULL);
146 }
147 
148 
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 
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 
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 
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 
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 
250 static void pvr2_context_enter(struct pvr2_context *mp)
251 {
252 	mutex_lock(&mp->mutex);
253 }
254 
255 
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 
267 void pvr2_context_disconnect(struct pvr2_context *mp)
268 {
269 	pvr2_hdw_disconnect(mp->hdw);
270 	mp->disconnect_flag = !0;
271 	pvr2_context_notify(mp);
272 }
273 
274 
275 void pvr2_channel_init(struct pvr2_channel *cp,struct pvr2_context *mp)
276 {
277 	pvr2_context_enter(mp);
278 	cp->hdw = mp->hdw;
279 	cp->mc_head = mp;
280 	cp->mc_next = NULL;
281 	cp->mc_prev = mp->mc_last;
282 	if (mp->mc_last) {
283 		mp->mc_last->mc_next = cp;
284 	} else {
285 		mp->mc_first = cp;
286 	}
287 	mp->mc_last = cp;
288 	pvr2_context_exit(mp);
289 }
290 
291 
292 static void pvr2_channel_disclaim_stream(struct pvr2_channel *cp)
293 {
294 	if (!cp->stream) return;
295 	pvr2_stream_kill(cp->stream->stream);
296 	cp->stream->user = NULL;
297 	cp->stream = NULL;
298 }
299 
300 
301 void pvr2_channel_done(struct pvr2_channel *cp)
302 {
303 	struct pvr2_context *mp = cp->mc_head;
304 	pvr2_context_enter(mp);
305 	cp->input_mask = 0;
306 	pvr2_channel_disclaim_stream(cp);
307 	pvr2_context_reset_input_limits(mp);
308 	if (cp->mc_next) {
309 		cp->mc_next->mc_prev = cp->mc_prev;
310 	} else {
311 		mp->mc_last = cp->mc_prev;
312 	}
313 	if (cp->mc_prev) {
314 		cp->mc_prev->mc_next = cp->mc_next;
315 	} else {
316 		mp->mc_first = cp->mc_next;
317 	}
318 	cp->hdw = NULL;
319 	pvr2_context_exit(mp);
320 }
321 
322 
323 int pvr2_channel_limit_inputs(struct pvr2_channel *cp,unsigned int cmsk)
324 {
325 	unsigned int tmsk,mmsk;
326 	int ret = 0;
327 	struct pvr2_channel *p2;
328 	struct pvr2_hdw *hdw = cp->hdw;
329 
330 	mmsk = pvr2_hdw_get_input_available(hdw);
331 	cmsk &= mmsk;
332 	if (cmsk == cp->input_mask) {
333 		/* No change; nothing to do */
334 		return 0;
335 	}
336 
337 	pvr2_context_enter(cp->mc_head);
338 	do {
339 		if (!cmsk) {
340 			cp->input_mask = 0;
341 			pvr2_context_reset_input_limits(cp->mc_head);
342 			break;
343 		}
344 		tmsk = mmsk;
345 		for (p2 = cp->mc_head->mc_first; p2; p2 = p2->mc_next) {
346 			if (p2 == cp) continue;
347 			if (!p2->input_mask) continue;
348 			tmsk &= p2->input_mask;
349 		}
350 		if (!(tmsk & cmsk)) {
351 			ret = -EPERM;
352 			break;
353 		}
354 		tmsk &= cmsk;
355 		if ((ret = pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk)) != 0) {
356 			/* Internal failure changing allowed list; probably
357 			   should not happen, but react if it does. */
358 			break;
359 		}
360 		cp->input_mask = cmsk;
361 		pvr2_hdw_commit_ctl(hdw);
362 	} while (0);
363 	pvr2_context_exit(cp->mc_head);
364 	return ret;
365 }
366 
367 
368 unsigned int pvr2_channel_get_limited_inputs(struct pvr2_channel *cp)
369 {
370 	return cp->input_mask;
371 }
372 
373 
374 int pvr2_channel_claim_stream(struct pvr2_channel *cp,
375 			      struct pvr2_context_stream *sp)
376 {
377 	int code = 0;
378 	pvr2_context_enter(cp->mc_head); do {
379 		if (sp == cp->stream) break;
380 		if (sp && sp->user) {
381 			code = -EBUSY;
382 			break;
383 		}
384 		pvr2_channel_disclaim_stream(cp);
385 		if (!sp) break;
386 		sp->user = cp;
387 		cp->stream = sp;
388 	} while (0);
389 	pvr2_context_exit(cp->mc_head);
390 	return code;
391 }
392 
393 
394 // This is the marker for the real beginning of a legitimate mpeg2 stream.
395 static char stream_sync_key[] = {
396 	0x00, 0x00, 0x01, 0xba,
397 };
398 
399 struct pvr2_ioread *pvr2_channel_create_mpeg_stream(
400 	struct pvr2_context_stream *sp)
401 {
402 	struct pvr2_ioread *cp;
403 	cp = pvr2_ioread_create();
404 	if (!cp) return NULL;
405 	pvr2_ioread_setup(cp,sp->stream);
406 	pvr2_ioread_set_sync_key(cp,stream_sync_key,sizeof(stream_sync_key));
407 	return cp;
408 }
409