linux/drivers/media/usb/pvrusb2/pvrusb2-context.c
<<
>>
Prefs
   1/*
   2 *
   3 *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
   4 *
   5 *  This program is free software; you can redistribute it and/or modify
   6 *  it under the terms of the GNU General Public License as published by
   7 *  the Free Software Foundation; either version 2 of the License
   8 *
   9 *  This program is distributed in the hope that it will be useful,
  10 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 *  GNU General Public License for more details.
  13 *
  14 */
  15
  16#include "pvrusb2-context.h"
  17#include "pvrusb2-io.h"
  18#include "pvrusb2-ioread.h"
  19#include "pvrusb2-hdw.h"
  20#include "pvrusb2-debug.h"
  21#include <linux/wait.h>
  22#include <linux/kthread.h>
  23#include <linux/errno.h>
  24#include <linux/string.h>
  25#include <linux/slab.h>
  26
  27static struct pvr2_context *pvr2_context_exist_first;
  28static struct pvr2_context *pvr2_context_exist_last;
  29static struct pvr2_context *pvr2_context_notify_first;
  30static struct pvr2_context *pvr2_context_notify_last;
  31static DEFINE_MUTEX(pvr2_context_mutex);
  32static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_sync_data);
  33static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_cleanup_data);
  34static int pvr2_context_cleanup_flag;
  35static int pvr2_context_cleaned_flag;
  36static struct task_struct *pvr2_context_thread_ptr;
  37
  38
  39static void pvr2_context_set_notify(struct pvr2_context *mp, int fl)
  40{
  41        int signal_flag = 0;
  42        mutex_lock(&pvr2_context_mutex);
  43        if (fl) {
  44                if (!mp->notify_flag) {
  45                        signal_flag = (pvr2_context_notify_first == NULL);
  46                        mp->notify_prev = pvr2_context_notify_last;
  47                        mp->notify_next = NULL;
  48                        pvr2_context_notify_last = mp;
  49                        if (mp->notify_prev) {
  50                                mp->notify_prev->notify_next = mp;
  51                        } else {
  52                                pvr2_context_notify_first = mp;
  53                        }
  54                        mp->notify_flag = !0;
  55                }
  56        } else {
  57                if (mp->notify_flag) {
  58                        mp->notify_flag = 0;
  59                        if (mp->notify_next) {
  60                                mp->notify_next->notify_prev = mp->notify_prev;
  61                        } else {
  62                                pvr2_context_notify_last = mp->notify_prev;
  63                        }
  64                        if (mp->notify_prev) {
  65                                mp->notify_prev->notify_next = mp->notify_next;
  66                        } else {
  67                                pvr2_context_notify_first = mp->notify_next;
  68                        }
  69                }
  70        }
  71        mutex_unlock(&pvr2_context_mutex);
  72        if (signal_flag) wake_up(&pvr2_context_sync_data);
  73}
  74
  75
  76static void pvr2_context_destroy(struct pvr2_context *mp)
  77{
  78        pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (destroy)",mp);
  79        pvr2_hdw_destroy(mp->hdw);
  80        pvr2_context_set_notify(mp, 0);
  81        mutex_lock(&pvr2_context_mutex);
  82        if (mp->exist_next) {
  83                mp->exist_next->exist_prev = mp->exist_prev;
  84        } else {
  85                pvr2_context_exist_last = mp->exist_prev;
  86        }
  87        if (mp->exist_prev) {
  88                mp->exist_prev->exist_next = mp->exist_next;
  89        } else {
  90                pvr2_context_exist_first = mp->exist_next;
  91        }
  92        if (!pvr2_context_exist_first) {
  93                /* Trigger wakeup on control thread in case it is waiting
  94                   for an exit condition. */
  95                wake_up(&pvr2_context_sync_data);
  96        }
  97        mutex_unlock(&pvr2_context_mutex);
  98        kfree(mp);
  99}
 100
 101
 102static void pvr2_context_notify(struct pvr2_context *mp)
 103{
 104        pvr2_context_set_notify(mp,!0);
 105}
 106
 107
 108static void pvr2_context_check(struct pvr2_context *mp)
 109{
 110        struct pvr2_channel *ch1, *ch2;
 111        pvr2_trace(PVR2_TRACE_CTXT,
 112                   "pvr2_context %p (notify)", mp);
 113        if (!mp->initialized_flag && !mp->disconnect_flag) {
 114                mp->initialized_flag = !0;
 115                pvr2_trace(PVR2_TRACE_CTXT,
 116                           "pvr2_context %p (initialize)", mp);
 117                /* Finish hardware initialization */
 118                if (pvr2_hdw_initialize(mp->hdw,
 119                                        (void (*)(void *))pvr2_context_notify,
 120                                        mp)) {
 121                        mp->video_stream.stream =
 122                                pvr2_hdw_get_video_stream(mp->hdw);
 123                        /* Trigger interface initialization.  By doing this
 124                           here initialization runs in our own safe and
 125                           cozy thread context. */
 126                        if (mp->setup_func) mp->setup_func(mp);
 127                } else {
 128                        pvr2_trace(PVR2_TRACE_CTXT,
 129                                   "pvr2_context %p (thread skipping setup)",
 130                                   mp);
 131                        /* Even though initialization did not succeed,
 132                           we're still going to continue anyway.  We need
 133                           to do this in order to await the expected
 134                           disconnect (which we will detect in the normal
 135                           course of operation). */
 136                }
 137        }
 138
 139        for (ch1 = mp->mc_first; ch1; ch1 = ch2) {
 140                ch2 = ch1->mc_next;
 141                if (ch1->check_func) ch1->check_func(ch1);
 142        }
 143
 144        if (mp->disconnect_flag && !mp->mc_first) {
 145                /* Go away... */
 146                pvr2_context_destroy(mp);
 147                return;
 148        }
 149}
 150
 151
 152static int pvr2_context_shutok(void)
 153{
 154        return pvr2_context_cleanup_flag && (pvr2_context_exist_first == NULL);
 155}
 156
 157
 158static int pvr2_context_thread_func(void *foo)
 159{
 160        struct pvr2_context *mp;
 161
 162        pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread start");
 163
 164        do {
 165                while ((mp = pvr2_context_notify_first) != NULL) {
 166                        pvr2_context_set_notify(mp, 0);
 167                        pvr2_context_check(mp);
 168                }
 169                wait_event_interruptible(
 170                        pvr2_context_sync_data,
 171                        ((pvr2_context_notify_first != NULL) ||
 172                         pvr2_context_shutok()));
 173        } while (!pvr2_context_shutok());
 174
 175        pvr2_context_cleaned_flag = !0;
 176        wake_up(&pvr2_context_cleanup_data);
 177
 178        pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread cleaned up");
 179
 180        wait_event_interruptible(
 181                pvr2_context_sync_data,
 182                kthread_should_stop());
 183
 184        pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread end");
 185
 186        return 0;
 187}
 188
 189
 190int pvr2_context_global_init(void)
 191{
 192        pvr2_context_thread_ptr = kthread_run(pvr2_context_thread_func,
 193                                              NULL,
 194                                              "pvrusb2-context");
 195        return IS_ERR(pvr2_context_thread_ptr) ? -ENOMEM : 0;
 196}
 197
 198
 199void pvr2_context_global_done(void)
 200{
 201        pvr2_context_cleanup_flag = !0;
 202        wake_up(&pvr2_context_sync_data);
 203        wait_event_interruptible(
 204                pvr2_context_cleanup_data,
 205                pvr2_context_cleaned_flag);
 206        kthread_stop(pvr2_context_thread_ptr);
 207}
 208
 209
 210struct pvr2_context *pvr2_context_create(
 211        struct usb_interface *intf,
 212        const struct usb_device_id *devid,
 213        void (*setup_func)(struct pvr2_context *))
 214{
 215        struct pvr2_context *mp = NULL;
 216        mp = kzalloc(sizeof(*mp),GFP_KERNEL);
 217        if (!mp) goto done;
 218        pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (create)",mp);
 219        mp->setup_func = setup_func;
 220        mutex_init(&mp->mutex);
 221        mutex_lock(&pvr2_context_mutex);
 222        mp->exist_prev = pvr2_context_exist_last;
 223        mp->exist_next = NULL;
 224        pvr2_context_exist_last = mp;
 225        if (mp->exist_prev) {
 226                mp->exist_prev->exist_next = mp;
 227        } else {
 228                pvr2_context_exist_first = mp;
 229        }
 230        mutex_unlock(&pvr2_context_mutex);
 231        mp->hdw = pvr2_hdw_create(intf,devid);
 232        if (!mp->hdw) {
 233                pvr2_context_destroy(mp);
 234                mp = NULL;
 235                goto done;
 236        }
 237        pvr2_context_set_notify(mp, !0);
 238 done:
 239        return mp;
 240}
 241
 242
 243static void pvr2_context_reset_input_limits(struct pvr2_context *mp)
 244{
 245        unsigned int tmsk,mmsk;
 246        struct pvr2_channel *cp;
 247        struct pvr2_hdw *hdw = mp->hdw;
 248        mmsk = pvr2_hdw_get_input_available(hdw);
 249        tmsk = mmsk;
 250        for (cp = mp->mc_first; cp; cp = cp->mc_next) {
 251                if (!cp->input_mask) continue;
 252                tmsk &= cp->input_mask;
 253        }
 254        pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk);
 255        pvr2_hdw_commit_ctl(hdw);
 256}
 257
 258
 259static void pvr2_context_enter(struct pvr2_context *mp)
 260{
 261        mutex_lock(&mp->mutex);
 262}
 263
 264
 265static void pvr2_context_exit(struct pvr2_context *mp)
 266{
 267        int destroy_flag = 0;
 268        if (!(mp->mc_first || !mp->disconnect_flag)) {
 269                destroy_flag = !0;
 270        }
 271        mutex_unlock(&mp->mutex);
 272        if (destroy_flag) pvr2_context_notify(mp);
 273}
 274
 275
 276void pvr2_context_disconnect(struct pvr2_context *mp)
 277{
 278        pvr2_hdw_disconnect(mp->hdw);
 279        mp->disconnect_flag = !0;
 280        pvr2_context_notify(mp);
 281}
 282
 283
 284void pvr2_channel_init(struct pvr2_channel *cp,struct pvr2_context *mp)
 285{
 286        pvr2_context_enter(mp);
 287        cp->hdw = mp->hdw;
 288        cp->mc_head = mp;
 289        cp->mc_next = NULL;
 290        cp->mc_prev = mp->mc_last;
 291        if (mp->mc_last) {
 292                mp->mc_last->mc_next = cp;
 293        } else {
 294                mp->mc_first = cp;
 295        }
 296        mp->mc_last = cp;
 297        pvr2_context_exit(mp);
 298}
 299
 300
 301static void pvr2_channel_disclaim_stream(struct pvr2_channel *cp)
 302{
 303        if (!cp->stream) return;
 304        pvr2_stream_kill(cp->stream->stream);
 305        cp->stream->user = NULL;
 306        cp->stream = NULL;
 307}
 308
 309
 310void pvr2_channel_done(struct pvr2_channel *cp)
 311{
 312        struct pvr2_context *mp = cp->mc_head;
 313        pvr2_context_enter(mp);
 314        cp->input_mask = 0;
 315        pvr2_channel_disclaim_stream(cp);
 316        pvr2_context_reset_input_limits(mp);
 317        if (cp->mc_next) {
 318                cp->mc_next->mc_prev = cp->mc_prev;
 319        } else {
 320                mp->mc_last = cp->mc_prev;
 321        }
 322        if (cp->mc_prev) {
 323                cp->mc_prev->mc_next = cp->mc_next;
 324        } else {
 325                mp->mc_first = cp->mc_next;
 326        }
 327        cp->hdw = NULL;
 328        pvr2_context_exit(mp);
 329}
 330
 331
 332int pvr2_channel_limit_inputs(struct pvr2_channel *cp,unsigned int cmsk)
 333{
 334        unsigned int tmsk,mmsk;
 335        int ret = 0;
 336        struct pvr2_channel *p2;
 337        struct pvr2_hdw *hdw = cp->hdw;
 338
 339        mmsk = pvr2_hdw_get_input_available(hdw);
 340        cmsk &= mmsk;
 341        if (cmsk == cp->input_mask) {
 342                /* No change; nothing to do */
 343                return 0;
 344        }
 345
 346        pvr2_context_enter(cp->mc_head);
 347        do {
 348                if (!cmsk) {
 349                        cp->input_mask = 0;
 350                        pvr2_context_reset_input_limits(cp->mc_head);
 351                        break;
 352                }
 353                tmsk = mmsk;
 354                for (p2 = cp->mc_head->mc_first; p2; p2 = p2->mc_next) {
 355                        if (p2 == cp) continue;
 356                        if (!p2->input_mask) continue;
 357                        tmsk &= p2->input_mask;
 358                }
 359                if (!(tmsk & cmsk)) {
 360                        ret = -EPERM;
 361                        break;
 362                }
 363                tmsk &= cmsk;
 364                if ((ret = pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk)) != 0) {
 365                        /* Internal failure changing allowed list; probably
 366                           should not happen, but react if it does. */
 367                        break;
 368                }
 369                cp->input_mask = cmsk;
 370                pvr2_hdw_commit_ctl(hdw);
 371        } while (0);
 372        pvr2_context_exit(cp->mc_head);
 373        return ret;
 374}
 375
 376
 377unsigned int pvr2_channel_get_limited_inputs(struct pvr2_channel *cp)
 378{
 379        return cp->input_mask;
 380}
 381
 382
 383int pvr2_channel_claim_stream(struct pvr2_channel *cp,
 384                              struct pvr2_context_stream *sp)
 385{
 386        int code = 0;
 387        pvr2_context_enter(cp->mc_head); do {
 388                if (sp == cp->stream) break;
 389                if (sp && sp->user) {
 390                        code = -EBUSY;
 391                        break;
 392                }
 393                pvr2_channel_disclaim_stream(cp);
 394                if (!sp) break;
 395                sp->user = cp;
 396                cp->stream = sp;
 397        } while (0);
 398        pvr2_context_exit(cp->mc_head);
 399        return code;
 400}
 401
 402
 403// This is the marker for the real beginning of a legitimate mpeg2 stream.
 404static char stream_sync_key[] = {
 405        0x00, 0x00, 0x01, 0xba,
 406};
 407
 408struct pvr2_ioread *pvr2_channel_create_mpeg_stream(
 409        struct pvr2_context_stream *sp)
 410{
 411        struct pvr2_ioread *cp;
 412        cp = pvr2_ioread_create();
 413        if (!cp) return NULL;
 414        pvr2_ioread_setup(cp,sp->stream);
 415        pvr2_ioread_set_sync_key(cp,stream_sync_key,sizeof(stream_sync_key));
 416        return cp;
 417}
 418