linux/drivers/media/usb/pvrusb2/pvrusb2-context.c
<<
>>
Prefs
   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}
 409