62f5fdace7
Trying to temporarily check that the stream is not claimed during open of the radio device is at best a race condition. What's to stop another app from claiming the stream anyway the instant after the check is done? The implementation for this was dicey anyway. So it's removed. The only "price" for this is that if /dev/radioX is opened while streaming video, then the video stream is just going to switch to radio mode anyway. If a user does this, he gets what he expects... Signed-off-by: Mike Isely <isely@pobox.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
236 lines
5.5 KiB
C
236 lines
5.5 KiB
C
/*
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
*/
|
|
|
|
#include "pvrusb2-context.h"
|
|
#include "pvrusb2-io.h"
|
|
#include "pvrusb2-ioread.h"
|
|
#include "pvrusb2-hdw.h"
|
|
#include "pvrusb2-debug.h"
|
|
#include <linux/errno.h>
|
|
#include <linux/string.h>
|
|
#include <linux/slab.h>
|
|
#include <asm/semaphore.h>
|
|
|
|
|
|
static void pvr2_context_destroy(struct pvr2_context *mp)
|
|
{
|
|
if (mp->hdw) pvr2_hdw_destroy(mp->hdw);
|
|
pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr_main id=%p",mp);
|
|
flush_workqueue(mp->workqueue);
|
|
destroy_workqueue(mp->workqueue);
|
|
kfree(mp);
|
|
}
|
|
|
|
|
|
static void pvr2_context_trigger_poll(struct pvr2_context *mp)
|
|
{
|
|
queue_work(mp->workqueue,&mp->workpoll);
|
|
}
|
|
|
|
|
|
static void pvr2_context_poll(struct work_struct *work)
|
|
{
|
|
struct pvr2_context *mp =
|
|
container_of(work, struct pvr2_context, workpoll);
|
|
pvr2_context_enter(mp); do {
|
|
pvr2_hdw_poll(mp->hdw);
|
|
} while (0); pvr2_context_exit(mp);
|
|
}
|
|
|
|
|
|
static void pvr2_context_setup(struct work_struct *work)
|
|
{
|
|
struct pvr2_context *mp =
|
|
container_of(work, struct pvr2_context, workinit);
|
|
|
|
pvr2_context_enter(mp); do {
|
|
if (!pvr2_hdw_dev_ok(mp->hdw)) break;
|
|
pvr2_hdw_setup(mp->hdw);
|
|
pvr2_hdw_setup_poll_trigger(
|
|
mp->hdw,
|
|
(void (*)(void *))pvr2_context_trigger_poll,
|
|
mp);
|
|
if (!pvr2_hdw_dev_ok(mp->hdw)) break;
|
|
if (!pvr2_hdw_init_ok(mp->hdw)) break;
|
|
mp->video_stream.stream = pvr2_hdw_get_video_stream(mp->hdw);
|
|
if (mp->setup_func) {
|
|
mp->setup_func(mp);
|
|
}
|
|
} while (0); pvr2_context_exit(mp);
|
|
}
|
|
|
|
|
|
struct pvr2_context *pvr2_context_create(
|
|
struct usb_interface *intf,
|
|
const struct usb_device_id *devid,
|
|
void (*setup_func)(struct pvr2_context *))
|
|
{
|
|
struct pvr2_context *mp = NULL;
|
|
mp = kmalloc(sizeof(*mp),GFP_KERNEL);
|
|
if (!mp) goto done;
|
|
memset(mp,0,sizeof(*mp));
|
|
pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_main id=%p",mp);
|
|
mp->setup_func = setup_func;
|
|
mutex_init(&mp->mutex);
|
|
mp->hdw = pvr2_hdw_create(intf,devid);
|
|
if (!mp->hdw) {
|
|
pvr2_context_destroy(mp);
|
|
mp = NULL;
|
|
goto done;
|
|
}
|
|
|
|
mp->workqueue = create_singlethread_workqueue("pvrusb2");
|
|
INIT_WORK(&mp->workinit, pvr2_context_setup);
|
|
INIT_WORK(&mp->workpoll, pvr2_context_poll);
|
|
queue_work(mp->workqueue,&mp->workinit);
|
|
done:
|
|
return mp;
|
|
}
|
|
|
|
|
|
void pvr2_context_enter(struct pvr2_context *mp)
|
|
{
|
|
mutex_lock(&mp->mutex);
|
|
pvr2_trace(PVR2_TRACE_CREG,"pvr2_context_enter(id=%p)",mp);
|
|
}
|
|
|
|
|
|
void pvr2_context_exit(struct pvr2_context *mp)
|
|
{
|
|
int destroy_flag = 0;
|
|
if (!(mp->mc_first || !mp->disconnect_flag)) {
|
|
destroy_flag = !0;
|
|
}
|
|
pvr2_trace(PVR2_TRACE_CREG,"pvr2_context_exit(id=%p) outside",mp);
|
|
mutex_unlock(&mp->mutex);
|
|
if (destroy_flag) pvr2_context_destroy(mp);
|
|
}
|
|
|
|
|
|
static void pvr2_context_run_checks(struct pvr2_context *mp)
|
|
{
|
|
struct pvr2_channel *ch1,*ch2;
|
|
for (ch1 = mp->mc_first; ch1; ch1 = ch2) {
|
|
ch2 = ch1->mc_next;
|
|
if (ch1->check_func) {
|
|
ch1->check_func(ch1);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void pvr2_context_disconnect(struct pvr2_context *mp)
|
|
{
|
|
pvr2_context_enter(mp); do {
|
|
pvr2_hdw_disconnect(mp->hdw);
|
|
mp->disconnect_flag = !0;
|
|
pvr2_context_run_checks(mp);
|
|
} while (0); pvr2_context_exit(mp);
|
|
}
|
|
|
|
|
|
void pvr2_channel_init(struct pvr2_channel *cp,struct pvr2_context *mp)
|
|
{
|
|
cp->hdw = mp->hdw;
|
|
cp->mc_head = mp;
|
|
cp->mc_next = NULL;
|
|
cp->mc_prev = mp->mc_last;
|
|
if (mp->mc_last) {
|
|
mp->mc_last->mc_next = cp;
|
|
} else {
|
|
mp->mc_first = cp;
|
|
}
|
|
mp->mc_last = cp;
|
|
}
|
|
|
|
|
|
static void pvr2_channel_disclaim_stream(struct pvr2_channel *cp)
|
|
{
|
|
if (!cp->stream) return;
|
|
pvr2_stream_kill(cp->stream->stream);
|
|
cp->stream->user = NULL;
|
|
cp->stream = NULL;
|
|
}
|
|
|
|
|
|
void pvr2_channel_done(struct pvr2_channel *cp)
|
|
{
|
|
struct pvr2_context *mp = cp->mc_head;
|
|
pvr2_channel_disclaim_stream(cp);
|
|
if (cp->mc_next) {
|
|
cp->mc_next->mc_prev = cp->mc_prev;
|
|
} else {
|
|
mp->mc_last = cp->mc_prev;
|
|
}
|
|
if (cp->mc_prev) {
|
|
cp->mc_prev->mc_next = cp->mc_next;
|
|
} else {
|
|
mp->mc_first = cp->mc_next;
|
|
}
|
|
cp->hdw = NULL;
|
|
}
|
|
|
|
|
|
int pvr2_channel_claim_stream(struct pvr2_channel *cp,
|
|
struct pvr2_context_stream *sp)
|
|
{
|
|
int code = 0;
|
|
pvr2_context_enter(cp->mc_head); do {
|
|
if (sp == cp->stream) break;
|
|
if (sp->user) {
|
|
code = -EBUSY;
|
|
break;
|
|
}
|
|
pvr2_channel_disclaim_stream(cp);
|
|
if (!sp) break;
|
|
sp->user = cp;
|
|
cp->stream = sp;
|
|
} while (0); pvr2_context_exit(cp->mc_head);
|
|
return code;
|
|
}
|
|
|
|
|
|
// This is the marker for the real beginning of a legitimate mpeg2 stream.
|
|
static char stream_sync_key[] = {
|
|
0x00, 0x00, 0x01, 0xba,
|
|
};
|
|
|
|
struct pvr2_ioread *pvr2_channel_create_mpeg_stream(
|
|
struct pvr2_context_stream *sp)
|
|
{
|
|
struct pvr2_ioread *cp;
|
|
cp = pvr2_ioread_create();
|
|
if (!cp) return NULL;
|
|
pvr2_ioread_setup(cp,sp->stream);
|
|
pvr2_ioread_set_sync_key(cp,stream_sync_key,sizeof(stream_sync_key));
|
|
return cp;
|
|
}
|
|
|
|
|
|
/*
|
|
Stuff for Emacs to see, in order to encourage consistent editing style:
|
|
*** Local Variables: ***
|
|
*** mode: c ***
|
|
*** fill-column: 75 ***
|
|
*** tab-width: 8 ***
|
|
*** c-basic-offset: 8 ***
|
|
*** End: ***
|
|
*/
|