pvrusb2-context.c (9623B)
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(struct pvr2_context *mp) 94{ 95 pvr2_context_set_notify(mp,!0); 96} 97 98 99static 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 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 mp->disconnect_flag = !0; 271 pvr2_context_notify(mp); 272} 273 274 275void 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 292static 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 301void 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 323int 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 368unsigned int pvr2_channel_get_limited_inputs(struct pvr2_channel *cp) 369{ 370 return cp->input_mask; 371} 372 373 374int 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. 395static char stream_sync_key[] = { 396 0x00, 0x00, 0x01, 0xba, 397}; 398 399struct 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}