683 lines
18 KiB
C
683 lines
18 KiB
C
/*
|
|
*
|
|
*
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
#include "pvrusb2-io.h"
|
|
#include "pvrusb2-debug.h"
|
|
#include <linux/errno.h>
|
|
#include <linux/string.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/mutex.h>
|
|
|
|
static const char *pvr2_buffer_state_decode(enum pvr2_buffer_state);
|
|
|
|
#define BUFFER_SIG 0x47653271
|
|
|
|
// #define SANITY_CHECK_BUFFERS
|
|
|
|
|
|
#ifdef SANITY_CHECK_BUFFERS
|
|
#define BUFFER_CHECK(bp) do { \
|
|
if ((bp)->signature != BUFFER_SIG) { \
|
|
pvr2_trace(PVR2_TRACE_ERROR_LEGS, \
|
|
"Buffer %p is bad at %s:%d", \
|
|
(bp), __FILE__, __LINE__); \
|
|
pvr2_buffer_describe(bp, "BadSig"); \
|
|
BUG(); \
|
|
} \
|
|
} while (0)
|
|
#else
|
|
#define BUFFER_CHECK(bp) do {} while (0)
|
|
#endif
|
|
|
|
struct pvr2_stream {
|
|
/* Buffers queued for reading */
|
|
struct list_head queued_list;
|
|
unsigned int q_count;
|
|
unsigned int q_bcount;
|
|
/* Buffers with retrieved data */
|
|
struct list_head ready_list;
|
|
unsigned int r_count;
|
|
unsigned int r_bcount;
|
|
/* Buffers available for use */
|
|
struct list_head idle_list;
|
|
unsigned int i_count;
|
|
unsigned int i_bcount;
|
|
/* Pointers to all buffers */
|
|
struct pvr2_buffer **buffers;
|
|
/* Array size of buffers */
|
|
unsigned int buffer_slot_count;
|
|
/* Total buffers actually in circulation */
|
|
unsigned int buffer_total_count;
|
|
/* Designed number of buffers to be in circulation */
|
|
unsigned int buffer_target_count;
|
|
/* Executed when ready list become non-empty */
|
|
pvr2_stream_callback callback_func;
|
|
void *callback_data;
|
|
/* Context for transfer endpoint */
|
|
struct usb_device *dev;
|
|
int endpoint;
|
|
/* Overhead for mutex enforcement */
|
|
spinlock_t list_lock;
|
|
struct mutex mutex;
|
|
/* Tracking state for tolerating errors */
|
|
unsigned int fail_count;
|
|
unsigned int fail_tolerance;
|
|
|
|
unsigned int buffers_processed;
|
|
unsigned int buffers_failed;
|
|
unsigned int bytes_processed;
|
|
};
|
|
|
|
struct pvr2_buffer {
|
|
int id;
|
|
int signature;
|
|
enum pvr2_buffer_state state;
|
|
void *ptr; /* Pointer to storage area */
|
|
unsigned int max_count; /* Size of storage area */
|
|
unsigned int used_count; /* Amount of valid data in storage area */
|
|
int status; /* Transfer result status */
|
|
struct pvr2_stream *stream;
|
|
struct list_head list_overhead;
|
|
struct urb *purb;
|
|
};
|
|
|
|
static const char *pvr2_buffer_state_decode(enum pvr2_buffer_state st)
|
|
{
|
|
switch (st) {
|
|
case pvr2_buffer_state_none: return "none";
|
|
case pvr2_buffer_state_idle: return "idle";
|
|
case pvr2_buffer_state_queued: return "queued";
|
|
case pvr2_buffer_state_ready: return "ready";
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
#ifdef SANITY_CHECK_BUFFERS
|
|
static void pvr2_buffer_describe(struct pvr2_buffer *bp, const char *msg)
|
|
{
|
|
pvr2_trace(PVR2_TRACE_INFO,
|
|
"buffer%s%s %p state=%s id=%d status=%d stream=%p purb=%p sig=0x%x",
|
|
(msg ? " " : ""),
|
|
(msg ? msg : ""),
|
|
bp,
|
|
(bp ? pvr2_buffer_state_decode(bp->state) : "(invalid)"),
|
|
(bp ? bp->id : 0),
|
|
(bp ? bp->status : 0),
|
|
(bp ? bp->stream : NULL),
|
|
(bp ? bp->purb : NULL),
|
|
(bp ? bp->signature : 0));
|
|
}
|
|
#endif /* SANITY_CHECK_BUFFERS */
|
|
|
|
static void pvr2_buffer_remove(struct pvr2_buffer *bp)
|
|
{
|
|
unsigned int *cnt;
|
|
unsigned int *bcnt;
|
|
unsigned int ccnt;
|
|
struct pvr2_stream *sp = bp->stream;
|
|
switch (bp->state) {
|
|
case pvr2_buffer_state_idle:
|
|
cnt = &sp->i_count;
|
|
bcnt = &sp->i_bcount;
|
|
ccnt = bp->max_count;
|
|
break;
|
|
case pvr2_buffer_state_queued:
|
|
cnt = &sp->q_count;
|
|
bcnt = &sp->q_bcount;
|
|
ccnt = bp->max_count;
|
|
break;
|
|
case pvr2_buffer_state_ready:
|
|
cnt = &sp->r_count;
|
|
bcnt = &sp->r_bcount;
|
|
ccnt = bp->used_count;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
list_del_init(&bp->list_overhead);
|
|
(*cnt)--;
|
|
(*bcnt) -= ccnt;
|
|
pvr2_trace(PVR2_TRACE_BUF_FLOW,
|
|
"/*---TRACE_FLOW---*/ bufferPool %8s dec cap=%07d cnt=%02d",
|
|
pvr2_buffer_state_decode(bp->state), *bcnt, *cnt);
|
|
bp->state = pvr2_buffer_state_none;
|
|
}
|
|
|
|
static void pvr2_buffer_set_none(struct pvr2_buffer *bp)
|
|
{
|
|
unsigned long irq_flags;
|
|
struct pvr2_stream *sp;
|
|
BUFFER_CHECK(bp);
|
|
sp = bp->stream;
|
|
pvr2_trace(PVR2_TRACE_BUF_FLOW,
|
|
"/*---TRACE_FLOW---*/ bufferState %p %6s --> %6s",
|
|
bp,
|
|
pvr2_buffer_state_decode(bp->state),
|
|
pvr2_buffer_state_decode(pvr2_buffer_state_none));
|
|
spin_lock_irqsave(&sp->list_lock, irq_flags);
|
|
pvr2_buffer_remove(bp);
|
|
spin_unlock_irqrestore(&sp->list_lock, irq_flags);
|
|
}
|
|
|
|
static int pvr2_buffer_set_ready(struct pvr2_buffer *bp)
|
|
{
|
|
int fl;
|
|
unsigned long irq_flags;
|
|
struct pvr2_stream *sp;
|
|
BUFFER_CHECK(bp);
|
|
sp = bp->stream;
|
|
pvr2_trace(PVR2_TRACE_BUF_FLOW,
|
|
"/*---TRACE_FLOW---*/ bufferState %p %6s --> %6s",
|
|
bp,
|
|
pvr2_buffer_state_decode(bp->state),
|
|
pvr2_buffer_state_decode(pvr2_buffer_state_ready));
|
|
spin_lock_irqsave(&sp->list_lock, irq_flags);
|
|
fl = (sp->r_count == 0);
|
|
pvr2_buffer_remove(bp);
|
|
list_add_tail(&bp->list_overhead, &sp->ready_list);
|
|
bp->state = pvr2_buffer_state_ready;
|
|
(sp->r_count)++;
|
|
sp->r_bcount += bp->used_count;
|
|
pvr2_trace(PVR2_TRACE_BUF_FLOW,
|
|
"/*---TRACE_FLOW---*/ bufferPool %8s inc cap=%07d cnt=%02d",
|
|
pvr2_buffer_state_decode(bp->state),
|
|
sp->r_bcount, sp->r_count);
|
|
spin_unlock_irqrestore(&sp->list_lock, irq_flags);
|
|
return fl;
|
|
}
|
|
|
|
static void pvr2_buffer_set_idle(struct pvr2_buffer *bp)
|
|
{
|
|
unsigned long irq_flags;
|
|
struct pvr2_stream *sp;
|
|
BUFFER_CHECK(bp);
|
|
sp = bp->stream;
|
|
pvr2_trace(PVR2_TRACE_BUF_FLOW,
|
|
"/*---TRACE_FLOW---*/ bufferState %p %6s --> %6s",
|
|
bp,
|
|
pvr2_buffer_state_decode(bp->state),
|
|
pvr2_buffer_state_decode(pvr2_buffer_state_idle));
|
|
spin_lock_irqsave(&sp->list_lock, irq_flags);
|
|
pvr2_buffer_remove(bp);
|
|
list_add_tail(&bp->list_overhead, &sp->idle_list);
|
|
bp->state = pvr2_buffer_state_idle;
|
|
(sp->i_count)++;
|
|
sp->i_bcount += bp->max_count;
|
|
pvr2_trace(PVR2_TRACE_BUF_FLOW,
|
|
"/*---TRACE_FLOW---*/ bufferPool %8s inc cap=%07d cnt=%02d",
|
|
pvr2_buffer_state_decode(bp->state),
|
|
sp->i_bcount, sp->i_count);
|
|
spin_unlock_irqrestore(&sp->list_lock, irq_flags);
|
|
}
|
|
|
|
static void pvr2_buffer_set_queued(struct pvr2_buffer *bp)
|
|
{
|
|
unsigned long irq_flags;
|
|
struct pvr2_stream *sp;
|
|
BUFFER_CHECK(bp);
|
|
sp = bp->stream;
|
|
pvr2_trace(PVR2_TRACE_BUF_FLOW,
|
|
"/*---TRACE_FLOW---*/ bufferState %p %6s --> %6s",
|
|
bp,
|
|
pvr2_buffer_state_decode(bp->state),
|
|
pvr2_buffer_state_decode(pvr2_buffer_state_queued));
|
|
spin_lock_irqsave(&sp->list_lock, irq_flags);
|
|
pvr2_buffer_remove(bp);
|
|
list_add_tail(&bp->list_overhead, &sp->queued_list);
|
|
bp->state = pvr2_buffer_state_queued;
|
|
(sp->q_count)++;
|
|
sp->q_bcount += bp->max_count;
|
|
pvr2_trace(PVR2_TRACE_BUF_FLOW,
|
|
"/*---TRACE_FLOW---*/ bufferPool %8s inc cap=%07d cnt=%02d",
|
|
pvr2_buffer_state_decode(bp->state),
|
|
sp->q_bcount, sp->q_count);
|
|
spin_unlock_irqrestore(&sp->list_lock, irq_flags);
|
|
}
|
|
|
|
static void pvr2_buffer_wipe(struct pvr2_buffer *bp)
|
|
{
|
|
if (bp->state == pvr2_buffer_state_queued) {
|
|
usb_kill_urb(bp->purb);
|
|
}
|
|
}
|
|
|
|
static int pvr2_buffer_init(struct pvr2_buffer *bp,
|
|
struct pvr2_stream *sp,
|
|
unsigned int id)
|
|
{
|
|
memset(bp, 0, sizeof(*bp));
|
|
bp->signature = BUFFER_SIG;
|
|
bp->id = id;
|
|
pvr2_trace(PVR2_TRACE_BUF_POOL,
|
|
"/*---TRACE_FLOW---*/ bufferInit %p stream=%p", bp, sp);
|
|
bp->stream = sp;
|
|
bp->state = pvr2_buffer_state_none;
|
|
INIT_LIST_HEAD(&bp->list_overhead);
|
|
bp->purb = usb_alloc_urb(0, GFP_KERNEL);
|
|
if (! bp->purb) return -ENOMEM;
|
|
#ifdef SANITY_CHECK_BUFFERS
|
|
pvr2_buffer_describe(bp, "create");
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static void pvr2_buffer_done(struct pvr2_buffer *bp)
|
|
{
|
|
#ifdef SANITY_CHECK_BUFFERS
|
|
pvr2_buffer_describe(bp, "delete");
|
|
#endif
|
|
pvr2_buffer_wipe(bp);
|
|
pvr2_buffer_set_none(bp);
|
|
bp->signature = 0;
|
|
bp->stream = NULL;
|
|
usb_free_urb(bp->purb);
|
|
pvr2_trace(PVR2_TRACE_BUF_POOL, "/*---TRACE_FLOW---*/ bufferDone %p",
|
|
bp);
|
|
}
|
|
|
|
static int pvr2_stream_buffer_count(struct pvr2_stream *sp, unsigned int cnt)
|
|
{
|
|
int ret;
|
|
unsigned int scnt;
|
|
|
|
/* Allocate buffers pointer array in multiples of 32 entries */
|
|
if (cnt == sp->buffer_total_count) return 0;
|
|
|
|
pvr2_trace(PVR2_TRACE_BUF_POOL,
|
|
"/*---TRACE_FLOW---*/ poolResize stream=%p cur=%d adj=%+d",
|
|
sp,
|
|
sp->buffer_total_count,
|
|
cnt-sp->buffer_total_count);
|
|
|
|
scnt = cnt & ~0x1f;
|
|
if (cnt > scnt) scnt += 0x20;
|
|
|
|
if (cnt > sp->buffer_total_count) {
|
|
if (scnt > sp->buffer_slot_count) {
|
|
struct pvr2_buffer **nb;
|
|
|
|
nb = kmalloc_array(scnt, sizeof(*nb), GFP_KERNEL);
|
|
if (!nb) return -ENOMEM;
|
|
if (sp->buffer_slot_count) {
|
|
memcpy(nb, sp->buffers,
|
|
sp->buffer_slot_count * sizeof(*nb));
|
|
kfree(sp->buffers);
|
|
}
|
|
sp->buffers = nb;
|
|
sp->buffer_slot_count = scnt;
|
|
}
|
|
while (sp->buffer_total_count < cnt) {
|
|
struct pvr2_buffer *bp;
|
|
bp = kmalloc(sizeof(*bp), GFP_KERNEL);
|
|
if (!bp) return -ENOMEM;
|
|
ret = pvr2_buffer_init(bp, sp, sp->buffer_total_count);
|
|
if (ret) {
|
|
kfree(bp);
|
|
return -ENOMEM;
|
|
}
|
|
sp->buffers[sp->buffer_total_count] = bp;
|
|
(sp->buffer_total_count)++;
|
|
pvr2_buffer_set_idle(bp);
|
|
}
|
|
} else {
|
|
while (sp->buffer_total_count > cnt) {
|
|
struct pvr2_buffer *bp;
|
|
bp = sp->buffers[sp->buffer_total_count - 1];
|
|
/* Paranoia */
|
|
sp->buffers[sp->buffer_total_count - 1] = NULL;
|
|
(sp->buffer_total_count)--;
|
|
pvr2_buffer_done(bp);
|
|
kfree(bp);
|
|
}
|
|
if (scnt < sp->buffer_slot_count) {
|
|
struct pvr2_buffer **nb = NULL;
|
|
if (scnt) {
|
|
nb = kmemdup(sp->buffers, scnt * sizeof(*nb),
|
|
GFP_KERNEL);
|
|
if (!nb) return -ENOMEM;
|
|
}
|
|
kfree(sp->buffers);
|
|
sp->buffers = nb;
|
|
sp->buffer_slot_count = scnt;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int pvr2_stream_achieve_buffer_count(struct pvr2_stream *sp)
|
|
{
|
|
struct pvr2_buffer *bp;
|
|
unsigned int cnt;
|
|
|
|
if (sp->buffer_total_count == sp->buffer_target_count) return 0;
|
|
|
|
pvr2_trace(PVR2_TRACE_BUF_POOL,
|
|
"/*---TRACE_FLOW---*/ poolCheck stream=%p cur=%d tgt=%d",
|
|
sp, sp->buffer_total_count, sp->buffer_target_count);
|
|
|
|
if (sp->buffer_total_count < sp->buffer_target_count) {
|
|
return pvr2_stream_buffer_count(sp, sp->buffer_target_count);
|
|
}
|
|
|
|
cnt = 0;
|
|
while ((sp->buffer_total_count - cnt) > sp->buffer_target_count) {
|
|
bp = sp->buffers[sp->buffer_total_count - (cnt + 1)];
|
|
if (bp->state != pvr2_buffer_state_idle) break;
|
|
cnt++;
|
|
}
|
|
if (cnt) {
|
|
pvr2_stream_buffer_count(sp, sp->buffer_total_count - cnt);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void pvr2_stream_internal_flush(struct pvr2_stream *sp)
|
|
{
|
|
struct list_head *lp;
|
|
struct pvr2_buffer *bp1;
|
|
while ((lp = sp->queued_list.next) != &sp->queued_list) {
|
|
bp1 = list_entry(lp, struct pvr2_buffer, list_overhead);
|
|
pvr2_buffer_wipe(bp1);
|
|
/* At this point, we should be guaranteed that no
|
|
completion callback may happen on this buffer. But it's
|
|
possible that it might have completed after we noticed
|
|
it but before we wiped it. So double check its status
|
|
here first. */
|
|
if (bp1->state != pvr2_buffer_state_queued) continue;
|
|
pvr2_buffer_set_idle(bp1);
|
|
}
|
|
if (sp->buffer_total_count != sp->buffer_target_count) {
|
|
pvr2_stream_achieve_buffer_count(sp);
|
|
}
|
|
}
|
|
|
|
static void pvr2_stream_init(struct pvr2_stream *sp)
|
|
{
|
|
spin_lock_init(&sp->list_lock);
|
|
mutex_init(&sp->mutex);
|
|
INIT_LIST_HEAD(&sp->queued_list);
|
|
INIT_LIST_HEAD(&sp->ready_list);
|
|
INIT_LIST_HEAD(&sp->idle_list);
|
|
}
|
|
|
|
static void pvr2_stream_done(struct pvr2_stream *sp)
|
|
{
|
|
mutex_lock(&sp->mutex); do {
|
|
pvr2_stream_internal_flush(sp);
|
|
pvr2_stream_buffer_count(sp, 0);
|
|
} while (0); mutex_unlock(&sp->mutex);
|
|
}
|
|
|
|
static void buffer_complete(struct urb *urb)
|
|
{
|
|
struct pvr2_buffer *bp = urb->context;
|
|
struct pvr2_stream *sp;
|
|
unsigned long irq_flags;
|
|
BUFFER_CHECK(bp);
|
|
sp = bp->stream;
|
|
bp->used_count = 0;
|
|
bp->status = 0;
|
|
pvr2_trace(PVR2_TRACE_BUF_FLOW,
|
|
"/*---TRACE_FLOW---*/ bufferComplete %p stat=%d cnt=%d",
|
|
bp, urb->status, urb->actual_length);
|
|
spin_lock_irqsave(&sp->list_lock, irq_flags);
|
|
if ((!(urb->status)) ||
|
|
(urb->status == -ENOENT) ||
|
|
(urb->status == -ECONNRESET) ||
|
|
(urb->status == -ESHUTDOWN)) {
|
|
(sp->buffers_processed)++;
|
|
sp->bytes_processed += urb->actual_length;
|
|
bp->used_count = urb->actual_length;
|
|
if (sp->fail_count) {
|
|
pvr2_trace(PVR2_TRACE_TOLERANCE,
|
|
"stream %p transfer ok - fail count reset",
|
|
sp);
|
|
sp->fail_count = 0;
|
|
}
|
|
} else if (sp->fail_count < sp->fail_tolerance) {
|
|
// We can tolerate this error, because we're below the
|
|
// threshold...
|
|
(sp->fail_count)++;
|
|
(sp->buffers_failed)++;
|
|
pvr2_trace(PVR2_TRACE_TOLERANCE,
|
|
"stream %p ignoring error %d - fail count increased to %u",
|
|
sp, urb->status, sp->fail_count);
|
|
} else {
|
|
(sp->buffers_failed)++;
|
|
bp->status = urb->status;
|
|
}
|
|
spin_unlock_irqrestore(&sp->list_lock, irq_flags);
|
|
pvr2_buffer_set_ready(bp);
|
|
if (sp->callback_func) {
|
|
sp->callback_func(sp->callback_data);
|
|
}
|
|
}
|
|
|
|
struct pvr2_stream *pvr2_stream_create(void)
|
|
{
|
|
struct pvr2_stream *sp;
|
|
sp = kzalloc(sizeof(*sp), GFP_KERNEL);
|
|
if (!sp) return sp;
|
|
pvr2_trace(PVR2_TRACE_INIT, "pvr2_stream_create: sp=%p", sp);
|
|
pvr2_stream_init(sp);
|
|
return sp;
|
|
}
|
|
|
|
void pvr2_stream_destroy(struct pvr2_stream *sp)
|
|
{
|
|
if (!sp) return;
|
|
pvr2_trace(PVR2_TRACE_INIT, "pvr2_stream_destroy: sp=%p", sp);
|
|
pvr2_stream_done(sp);
|
|
kfree(sp);
|
|
}
|
|
|
|
void pvr2_stream_setup(struct pvr2_stream *sp,
|
|
struct usb_device *dev,
|
|
int endpoint,
|
|
unsigned int tolerance)
|
|
{
|
|
mutex_lock(&sp->mutex); do {
|
|
pvr2_stream_internal_flush(sp);
|
|
sp->dev = dev;
|
|
sp->endpoint = endpoint;
|
|
sp->fail_tolerance = tolerance;
|
|
} while (0); mutex_unlock(&sp->mutex);
|
|
}
|
|
|
|
void pvr2_stream_set_callback(struct pvr2_stream *sp,
|
|
pvr2_stream_callback func,
|
|
void *data)
|
|
{
|
|
unsigned long irq_flags;
|
|
mutex_lock(&sp->mutex);
|
|
do {
|
|
spin_lock_irqsave(&sp->list_lock, irq_flags);
|
|
sp->callback_data = data;
|
|
sp->callback_func = func;
|
|
spin_unlock_irqrestore(&sp->list_lock, irq_flags);
|
|
} while (0);
|
|
mutex_unlock(&sp->mutex);
|
|
}
|
|
|
|
void pvr2_stream_get_stats(struct pvr2_stream *sp,
|
|
struct pvr2_stream_stats *stats,
|
|
int zero_counts)
|
|
{
|
|
unsigned long irq_flags;
|
|
spin_lock_irqsave(&sp->list_lock, irq_flags);
|
|
if (stats) {
|
|
stats->buffers_in_queue = sp->q_count;
|
|
stats->buffers_in_idle = sp->i_count;
|
|
stats->buffers_in_ready = sp->r_count;
|
|
stats->buffers_processed = sp->buffers_processed;
|
|
stats->buffers_failed = sp->buffers_failed;
|
|
stats->bytes_processed = sp->bytes_processed;
|
|
}
|
|
if (zero_counts) {
|
|
sp->buffers_processed = 0;
|
|
sp->buffers_failed = 0;
|
|
sp->bytes_processed = 0;
|
|
}
|
|
spin_unlock_irqrestore(&sp->list_lock, irq_flags);
|
|
}
|
|
|
|
/* Query / set the nominal buffer count */
|
|
int pvr2_stream_get_buffer_count(struct pvr2_stream *sp)
|
|
{
|
|
return sp->buffer_target_count;
|
|
}
|
|
|
|
int pvr2_stream_set_buffer_count(struct pvr2_stream *sp, unsigned int cnt)
|
|
{
|
|
int ret;
|
|
if (sp->buffer_target_count == cnt) return 0;
|
|
mutex_lock(&sp->mutex);
|
|
do {
|
|
sp->buffer_target_count = cnt;
|
|
ret = pvr2_stream_achieve_buffer_count(sp);
|
|
} while (0);
|
|
mutex_unlock(&sp->mutex);
|
|
return ret;
|
|
}
|
|
|
|
struct pvr2_buffer *pvr2_stream_get_idle_buffer(struct pvr2_stream *sp)
|
|
{
|
|
struct list_head *lp = sp->idle_list.next;
|
|
if (lp == &sp->idle_list) return NULL;
|
|
return list_entry(lp, struct pvr2_buffer, list_overhead);
|
|
}
|
|
|
|
struct pvr2_buffer *pvr2_stream_get_ready_buffer(struct pvr2_stream *sp)
|
|
{
|
|
struct list_head *lp = sp->ready_list.next;
|
|
if (lp == &sp->ready_list) return NULL;
|
|
return list_entry(lp, struct pvr2_buffer, list_overhead);
|
|
}
|
|
|
|
struct pvr2_buffer *pvr2_stream_get_buffer(struct pvr2_stream *sp, int id)
|
|
{
|
|
if (id < 0) return NULL;
|
|
if (id >= sp->buffer_total_count) return NULL;
|
|
return sp->buffers[id];
|
|
}
|
|
|
|
int pvr2_stream_get_ready_count(struct pvr2_stream *sp)
|
|
{
|
|
return sp->r_count;
|
|
}
|
|
|
|
void pvr2_stream_kill(struct pvr2_stream *sp)
|
|
{
|
|
struct pvr2_buffer *bp;
|
|
mutex_lock(&sp->mutex);
|
|
do {
|
|
pvr2_stream_internal_flush(sp);
|
|
while ((bp = pvr2_stream_get_ready_buffer(sp)) != NULL) {
|
|
pvr2_buffer_set_idle(bp);
|
|
}
|
|
if (sp->buffer_total_count != sp->buffer_target_count) {
|
|
pvr2_stream_achieve_buffer_count(sp);
|
|
}
|
|
} while (0);
|
|
mutex_unlock(&sp->mutex);
|
|
}
|
|
|
|
int pvr2_buffer_queue(struct pvr2_buffer *bp)
|
|
{
|
|
#undef SEED_BUFFER
|
|
#ifdef SEED_BUFFER
|
|
unsigned int idx;
|
|
unsigned int val;
|
|
#endif
|
|
int ret = 0;
|
|
struct pvr2_stream *sp;
|
|
if (!bp) return -EINVAL;
|
|
sp = bp->stream;
|
|
mutex_lock(&sp->mutex);
|
|
do {
|
|
pvr2_buffer_wipe(bp);
|
|
if (!sp->dev) {
|
|
ret = -EIO;
|
|
break;
|
|
}
|
|
pvr2_buffer_set_queued(bp);
|
|
#ifdef SEED_BUFFER
|
|
for (idx = 0; idx < (bp->max_count) / 4; idx++) {
|
|
val = bp->id << 24;
|
|
val |= idx;
|
|
((unsigned int *)(bp->ptr))[idx] = val;
|
|
}
|
|
#endif
|
|
bp->status = -EINPROGRESS;
|
|
usb_fill_bulk_urb(bp->purb, // struct urb *urb
|
|
sp->dev, // struct usb_device *dev
|
|
// endpoint (below)
|
|
usb_rcvbulkpipe(sp->dev, sp->endpoint),
|
|
bp->ptr, // void *transfer_buffer
|
|
bp->max_count, // int buffer_length
|
|
buffer_complete,
|
|
bp);
|
|
usb_submit_urb(bp->purb, GFP_KERNEL);
|
|
} while (0);
|
|
mutex_unlock(&sp->mutex);
|
|
return ret;
|
|
}
|
|
|
|
int pvr2_buffer_set_buffer(struct pvr2_buffer *bp, void *ptr, unsigned int cnt)
|
|
{
|
|
int ret = 0;
|
|
unsigned long irq_flags;
|
|
struct pvr2_stream *sp;
|
|
if (!bp) return -EINVAL;
|
|
sp = bp->stream;
|
|
mutex_lock(&sp->mutex);
|
|
do {
|
|
spin_lock_irqsave(&sp->list_lock, irq_flags);
|
|
if (bp->state != pvr2_buffer_state_idle) {
|
|
ret = -EPERM;
|
|
} else {
|
|
bp->ptr = ptr;
|
|
bp->stream->i_bcount -= bp->max_count;
|
|
bp->max_count = cnt;
|
|
bp->stream->i_bcount += bp->max_count;
|
|
pvr2_trace(PVR2_TRACE_BUF_FLOW,
|
|
"/*---TRACE_FLOW---*/ bufferPool %8s cap cap=%07d cnt=%02d",
|
|
pvr2_buffer_state_decode(
|
|
pvr2_buffer_state_idle),
|
|
bp->stream->i_bcount, bp->stream->i_count);
|
|
}
|
|
spin_unlock_irqrestore(&sp->list_lock, irq_flags);
|
|
} while (0);
|
|
mutex_unlock(&sp->mutex);
|
|
return ret;
|
|
}
|
|
|
|
unsigned int pvr2_buffer_get_count(struct pvr2_buffer *bp)
|
|
{
|
|
return bp->used_count;
|
|
}
|
|
|
|
int pvr2_buffer_get_status(struct pvr2_buffer *bp)
|
|
{
|
|
return bp->status;
|
|
}
|
|
|
|
int pvr2_buffer_get_id(struct pvr2_buffer *bp)
|
|
{
|
|
return bp->id;
|
|
}
|