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(lock: &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, 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 | |
93 | static 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 | |
101 | static 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 | |
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, 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 | |
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(ptr: 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(k: 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(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 | |
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,change_mask: mmsk,change_val: 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(lock: &mp->mutex); |
263 | if (destroy_flag) pvr2_context_notify(ptr: mp); |
264 | } |
265 | |
266 | |
267 | void 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 | |
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 | |
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 | |
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 | |
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(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 | |
369 | unsigned int pvr2_channel_get_limited_inputs(struct pvr2_channel *cp) |
370 | { |
371 | return cp->input_mask; |
372 | } |
373 | |
374 | |
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(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. |
396 | static char stream_sync_key[] = { |
397 | 0x00, 0x00, 0x01, 0xba, |
398 | }; |
399 | |
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,sync_key_ptr: stream_sync_key,sync_key_len: sizeof(stream_sync_key)); |
408 | return cp; |
409 | } |
410 | |