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
18static struct pvr2_context *pvr2_context_exist_first;
19static struct pvr2_context *pvr2_context_exist_last;
20static struct pvr2_context *pvr2_context_notify_first;
21static struct pvr2_context *pvr2_context_notify_last;
22static DEFINE_MUTEX(pvr2_context_mutex);
23static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_sync_data);
24static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_cleanup_data);
25static int pvr2_context_cleanup_flag;
26static int pvr2_context_cleaned_flag;
27static struct task_struct *pvr2_context_thread_ptr;
28
29
30static 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
67static 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
93static void pvr2_context_notify(void *ptr)
94{
95	struct pvr2_context *mp = ptr;
96
97	pvr2_context_set_notify(mp,!0);
98}
99
100
101static void pvr2_context_check(struct pvr2_context *mp)
102{
103	struct pvr2_channel *ch1, *ch2;
104	pvr2_trace(PVR2_TRACE_CTXT,
105		   "pvr2_context %p (notify)", mp);
106	if (!mp->initialized_flag && !mp->disconnect_flag) {
107		mp->initialized_flag = !0;
108		pvr2_trace(PVR2_TRACE_CTXT,
109			   "pvr2_context %p (initialize)", mp);
110		/* Finish hardware initialization */
111		if (pvr2_hdw_initialize(mp->hdw, pvr2_context_notify, 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
143static int pvr2_context_shutok(void)
144{
145	return pvr2_context_cleanup_flag && (pvr2_context_exist_first == NULL);
146}
147
148
149static 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
181int 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
190void 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
201struct 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
234static 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
250static void pvr2_context_enter(struct pvr2_context *mp)
251{
252	mutex_lock(&mp->mutex);
253}
254
255
256static 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
267void pvr2_context_disconnect(struct pvr2_context *mp)
268{
269	pvr2_hdw_disconnect(mp->hdw);
270	if (!pvr2_context_shutok())
271		pvr2_context_notify(mp);
272	mp->disconnect_flag = !0;
273}
274
275
276void 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
293static 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
302void 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
324int 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
369unsigned int pvr2_channel_get_limited_inputs(struct pvr2_channel *cp)
370{
371	return cp->input_mask;
372}
373
374
375int 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.
396static char stream_sync_key[] = {
397	0x00, 0x00, 0x01, 0xba,
398};
399
400struct 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