// SPDX-License-Identifier: GPL-2.0-only
/*
*
* Copyright ( C ) 2005 Mike Isely < isely @ pobox . com >
*/
#include "pvrusb2-context.h"
#include "pvrusb2-io.h"
#include "pvrusb2-ioread.h"
#include "pvrusb2-hdw.h"
#include "pvrusb2-debug.h"
#include <linux/wait.h>
#include <linux/kthread.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/slab.h>
static struct pvr2_context *pvr2_context_exist_first;
static struct pvr2_context *pvr2_context_exist_last;
static struct pvr2_context *pvr2_context_notify_first;
static struct pvr2_context *pvr2_context_notify_last;
static DEFINE_MUTEX(pvr2_context_mutex);
static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_sync_data);
static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_cleanup_data);
static int pvr2_context_cleanup_flag;
static int pvr2_context_cleaned_flag;
static struct task_struct *pvr2_context_thread_ptr;
static void pvr2_context_set_notify(struct pvr2_context *mp, int fl)
{
int signal_flag = 0 ;
mutex_lock(&pvr2_context_mutex);
if (fl) {
if (!mp->notify_flag) {
signal_flag = (pvr2_context_notify_first == NULL);
mp->notify_prev = pvr2_context_notify_last;
mp->notify_next = NULL;
pvr2_context_notify_last = mp;
if (mp->notify_prev) {
mp->notify_prev->notify_next = mp;
} else {
pvr2_context_notify_first = mp;
}
mp->notify_flag = !0 ;
}
} else {
if (mp->notify_flag) {
mp->notify_flag = 0 ;
if (mp->notify_next) {
mp->notify_next->notify_prev = mp->notify_prev;
} else {
pvr2_context_notify_last = mp->notify_prev;
}
if (mp->notify_prev) {
mp->notify_prev->notify_next = mp->notify_next;
} else {
pvr2_context_notify_first = mp->notify_next;
}
}
}
mutex_unlock(&pvr2_context_mutex);
if (signal_flag) wake_up(&pvr2_context_sync_data);
}
static void pvr2_context_destroy(struct pvr2_context *mp)
{
pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (destroy)" ,mp);
pvr2_hdw_destroy(mp->hdw);
pvr2_context_set_notify(mp, 0 );
mutex_lock(&pvr2_context_mutex);
if (mp->exist_next) {
mp->exist_next->exist_prev = mp->exist_prev;
} else {
pvr2_context_exist_last = mp->exist_prev;
}
if (mp->exist_prev) {
mp->exist_prev->exist_next = mp->exist_next;
} else {
pvr2_context_exist_first = mp->exist_next;
}
if (!pvr2_context_exist_first) {
/* Trigger wakeup on control thread in case it is waiting
for an exit condition. */
wake_up(&pvr2_context_sync_data);
}
mutex_unlock(&pvr2_context_mutex);
kfree(mp);
}
static void pvr2_context_notify(void *ptr)
{
struct pvr2_context *mp = ptr;
pvr2_context_set_notify(mp,!0 );
}
static void pvr2_context_check(struct pvr2_context *mp)
{
struct pvr2_channel *ch1, *ch2;
pvr2_trace(PVR2_TRACE_CTXT,
"pvr2_context %p (notify)" , mp);
if (!mp->initialized_flag && !mp->disconnect_flag) {
mp->initialized_flag = !0 ;
pvr2_trace(PVR2_TRACE_CTXT,
"pvr2_context %p (initialize)" , mp);
/* Finish hardware initialization */
if (pvr2_hdw_initialize(mp->hdw, pvr2_context_notify, mp)) {
mp->video_stream.stream =
pvr2_hdw_get_video_stream(mp->hdw);
/* Trigger interface initialization. By doing this
here initialization runs in our own safe and
cozy thread context. */
if (mp->setup_func) mp->setup_func(mp);
} else {
pvr2_trace(PVR2_TRACE_CTXT,
"pvr2_context %p (thread skipping setup)" ,
mp);
/* Even though initialization did not succeed,
we ' re still going to continue anyway . We need
to do this in order to await the expected
disconnect ( which we will detect in the normal
course of operation). */
}
}
for (ch1 = mp->mc_first; ch1; ch1 = ch2) {
ch2 = ch1->mc_next;
if (ch1->check_func) ch1->check_func(ch1);
}
if (mp->disconnect_flag && !mp->mc_first) {
/* Go away... */
pvr2_context_destroy(mp);
return ;
}
}
static int pvr2_context_shutok(void )
{
return pvr2_context_cleanup_flag && (pvr2_context_exist_first == NULL);
}
static int pvr2_context_thread_func(void *foo)
{
struct pvr2_context *mp;
pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread start" );
do {
while ((mp = pvr2_context_notify_first) != NULL) {
pvr2_context_set_notify(mp, 0 );
pvr2_context_check(mp);
}
wait_event_interruptible(
pvr2_context_sync_data,
((pvr2_context_notify_first != NULL) ||
pvr2_context_shutok()));
} while (!pvr2_context_shutok());
pvr2_context_cleaned_flag = !0 ;
wake_up(&pvr2_context_cleanup_data);
pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread cleaned up" );
wait_event_interruptible(
pvr2_context_sync_data,
kthread_should_stop());
pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread end" );
return 0 ;
}
int pvr2_context_global_init(void )
{
pvr2_context_thread_ptr = kthread_run(pvr2_context_thread_func,
NULL,
"pvrusb2-context" );
return IS_ERR(pvr2_context_thread_ptr) ? -ENOMEM : 0 ;
}
void pvr2_context_global_done(void )
{
pvr2_context_cleanup_flag = !0 ;
wake_up(&pvr2_context_sync_data);
wait_event_interruptible(
pvr2_context_cleanup_data,
pvr2_context_cleaned_flag);
kthread_stop(pvr2_context_thread_ptr);
}
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 = kzalloc(sizeof (*mp),GFP_KERNEL);
if (!mp) goto done;
pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (create)" ,mp);
mp->setup_func = setup_func;
mutex_init(&mp->mutex);
mutex_lock(&pvr2_context_mutex);
mp->exist_prev = pvr2_context_exist_last;
mp->exist_next = NULL;
pvr2_context_exist_last = mp;
if (mp->exist_prev) {
mp->exist_prev->exist_next = mp;
} else {
pvr2_context_exist_first = mp;
}
mutex_unlock(&pvr2_context_mutex);
mp->hdw = pvr2_hdw_create(intf,devid);
if (!mp->hdw) {
pvr2_context_destroy(mp);
mp = NULL;
goto done;
}
pvr2_context_set_notify(mp, !0 );
done:
return mp;
}
static void pvr2_context_reset_input_limits(struct pvr2_context *mp)
{
unsigned int tmsk,mmsk;
struct pvr2_channel *cp;
struct pvr2_hdw *hdw = mp->hdw;
mmsk = pvr2_hdw_get_input_available(hdw);
tmsk = mmsk;
for (cp = mp->mc_first; cp; cp = cp->mc_next) {
if (!cp->input_mask) continue ;
tmsk &= cp->input_mask;
}
pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk);
pvr2_hdw_commit_ctl(hdw);
}
static void pvr2_context_enter(struct pvr2_context *mp)
{
mutex_lock(&mp->mutex);
}
static void pvr2_context_exit(struct pvr2_context *mp)
{
int destroy_flag = 0 ;
if (!(mp->mc_first || !mp->disconnect_flag)) {
destroy_flag = !0 ;
}
mutex_unlock(&mp->mutex);
if (destroy_flag) pvr2_context_notify(mp);
}
void pvr2_context_disconnect(struct pvr2_context *mp)
{
pvr2_hdw_disconnect(mp->hdw);
if (!pvr2_context_shutok())
pvr2_context_notify(mp);
mp->disconnect_flag = !0 ;
}
void pvr2_channel_init(struct pvr2_channel *cp,struct pvr2_context *mp)
{
pvr2_context_enter(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;
pvr2_context_exit(mp);
}
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_context_enter(mp);
cp->input_mask = 0 ;
pvr2_channel_disclaim_stream(cp);
pvr2_context_reset_input_limits(mp);
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;
pvr2_context_exit(mp);
}
int pvr2_channel_limit_inputs(struct pvr2_channel *cp,unsigned int cmsk)
{
unsigned int tmsk,mmsk;
int ret = 0 ;
struct pvr2_channel *p2;
struct pvr2_hdw *hdw = cp->hdw;
mmsk = pvr2_hdw_get_input_available(hdw);
cmsk &= mmsk;
if (cmsk == cp->input_mask) {
/* No change; nothing to do */
return 0 ;
}
pvr2_context_enter(cp->mc_head);
do {
if (!cmsk) {
cp->input_mask = 0 ;
pvr2_context_reset_input_limits(cp->mc_head);
break ;
}
tmsk = mmsk;
for (p2 = cp->mc_head->mc_first; p2; p2 = p2->mc_next) {
if (p2 == cp) continue ;
if (!p2->input_mask) continue ;
tmsk &= p2->input_mask;
}
if (!(tmsk & cmsk)) {
ret = -EPERM;
break ;
}
tmsk &= cmsk;
if ((ret = pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk)) != 0 ) {
/* Internal failure changing allowed list; probably
should not happen, but react if it does. */
break ;
}
cp->input_mask = cmsk;
pvr2_hdw_commit_ctl(hdw);
} while (0 );
pvr2_context_exit(cp->mc_head);
return ret;
}
unsigned int pvr2_channel_get_limited_inputs(struct pvr2_channel *cp)
{
return cp->input_mask;
}
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 && 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[] = {
0 x00, 0 x00, 0 x01, 0 xba,
};
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;
}
Messung V0.5 in Prozent C=95 H=95 G=94
¤ Dauer der Verarbeitung: 0.9 Sekunden
(vorverarbeitet am 2026-10-11)
¤
*© Formatika GbR, Deutschland