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(lock: &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, fl: 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(lock: &pvr2_context_mutex);
89 kfree(objp: 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,fl: !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, callback_func: pvr2_context_notify, callback_data: 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, fl: 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(ptr: 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(k: 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(size: 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(lock: &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, fl: !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,change_mask: mmsk,change_val: 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(lock: &mp->mutex);
263 if (destroy_flag) pvr2_context_notify(ptr: 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(ptr: 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(mp: cp->mc_head);
339 do {
340 if (!cmsk) {
341 cp->input_mask = 0;
342 pvr2_context_reset_input_limits(mp: 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,change_mask: mmsk,change_val: 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(mp: 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(mp: 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(mp: 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,sync_key_ptr: stream_sync_key,sync_key_len: sizeof(stream_sync_key));
408 return cp;
409}
410

source code of linux/drivers/media/usb/pvrusb2/pvrusb2-context.c