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