/*
* Copyright (C) 2005-2008 by Pieter Palmers
*
* This file is part of FFADO
* FFADO = Free FireWire (pro-)audio drivers for Linux
*
* FFADO is based upon FreeBoB.
*
* 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, or
* (at your option) version 3 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, see .
*
*/
#ifndef __FFADO_ISOHANDLERMANAGER__
#define __FFADO_ISOHANDLERMANAGER__
#include "config.h"
#include "debugmodule/debugmodule.h"
#include "libutil/Thread.h"
#include
#include
#include
#include
class Ieee1394Service;
//class IsoHandler;
//enum IsoHandler::EHandlerType;
namespace Streaming {
class StreamProcessor;
typedef std::vector StreamProcessorVector;
typedef std::vector::iterator StreamProcessorVectorIterator;
}
/*!
\brief The ISO Handler management class
This class manages the use of ISO handlers by ISO streams.
You can register an Streaming::StreamProcessor with an IsoHandlerManager. This
manager will assign an IsoHandler to the stream. If nescessary
the manager allocates a new handler. If there is already a handler
that can handle the Streaming::StreamProcessor (e.g. in case of multichannel receive),
it can be assigned.
*/
class IsoHandlerManager
{
friend class IsoTask;
////
/*!
\brief The Base Class for ISO Handlers
These classes perform the actual ISO communication through libraw1394.
They are different from Streaming::StreamProcessors because one handler can provide multiple
streams with packets in case of ISO multichannel receive.
*/
class IsoHandler
{
public:
enum EHandlerType {
eHT_Receive,
eHT_Transmit
};
IsoHandler(IsoHandlerManager& manager, enum EHandlerType t);
IsoHandler(IsoHandlerManager& manager, enum EHandlerType t,
unsigned int buf_packets, unsigned int max_packet_size, int irq);
IsoHandler(IsoHandlerManager& manager, enum EHandlerType t,
unsigned int buf_packets, unsigned int max_packet_size, int irq, enum raw1394_iso_speed speed);
~IsoHandler();
private: // the ISO callback interface
static enum raw1394_iso_disposition
iso_receive_handler(raw1394handle_t handle, unsigned char *data,
unsigned int length, unsigned char channel,
unsigned char tag, unsigned char sy, unsigned int cycle,
unsigned int dropped);
enum raw1394_iso_disposition
putPacket(unsigned char *data, unsigned int length,
unsigned char channel, unsigned char tag, unsigned char sy,
unsigned int cycle, unsigned int dropped);
static enum raw1394_iso_disposition iso_transmit_handler(raw1394handle_t handle,
unsigned char *data, unsigned int *length,
unsigned char *tag, unsigned char *sy,
int cycle, unsigned int dropped);
enum raw1394_iso_disposition
getPacket(unsigned char *data, unsigned int *length,
unsigned char *tag, unsigned char *sy,
int cycle, unsigned int dropped, unsigned int skipped);
public:
/**
* Iterate the handler, transporting ISO packets to the client(s)
* @return true if success
*/
bool iterate();
/**
* Iterate the handler, transporting ISO packets to the client(s)
* @param ctr_now the CTR time at which the iterate call is done.
* @return true if success
*/
bool iterate(uint32_t ctr_now);
int getFileDescriptor() { return raw1394_get_fd(m_handle);};
bool init();
void setVerboseLevel(int l);
// the enable/disable functions should only be used from within the loop that iterates()
// but not from within the iterate callback. use the requestEnable / requestDisable functions
// for that
bool enable() {return enable(-1);};
bool enable(int cycle);
bool disable();
// functions to request enable or disable at the next opportunity
bool requestEnable(int cycle = -1);
bool requestDisable();
// Manually set the start cycle for the iso handler
void setIsoStartCycle(signed int cycle = -1);
/**
* updates the internal state if required
*/
void updateState();
enum EHandlerType getType() {return m_type;};
const char *getTypeString() {return eHTToString(m_type); };
// pretty printing
const char *eHTToString(enum EHandlerType);
bool isEnabled()
{return m_State == eHS_Running;};
// no setter functions, because those would require a re-init
unsigned int getMaxPacketSize() { return m_max_packet_size;};
unsigned int getNbBuffers() { return m_buf_packets;};
int getIrqInterval() { return m_irq_interval;};
void dumpInfo();
bool inUse() {return (m_Client != 0) ;};
bool isStreamRegistered(Streaming::StreamProcessor *s) {return (m_Client == s);};
bool registerStream(Streaming::StreamProcessor *);
bool unregisterStream(Streaming::StreamProcessor *);
bool canIterateClient(); // FIXME: implement with functor
/**
* @brief get last cycle number seen by handler
* @return cycle number
*/
int getLastCycle() {return m_last_cycle;};
/**
* @brief returns the CTR value saved at the last iterate() call
* @return CTR value saved at last iterate() call
*/
uint32_t getLastIterateTime() {return m_last_now;};
/**
* @brief returns the CTR value saved at the last iterate handler call
* @return CTR value saved at last iterate handler call
*/
uint32_t getLastPacketTime() {return m_last_packet_handled_at;};
/**
* @brief set iso receive mode. doesn't have any effect if the stream is running
* @param m receive mode
*/
void setReceiveMode(enum raw1394_iso_dma_recv_mode m)
{m_receive_mode = m;}
void notifyOfDeath();
bool handleBusReset();
private:
IsoHandlerManager& m_manager;
enum EHandlerType m_type;
raw1394handle_t m_handle;
unsigned int m_buf_packets;
unsigned int m_max_packet_size;
int m_irq_interval;
int m_last_cycle;
uint32_t m_last_now;
uint32_t m_last_packet_handled_at;
enum raw1394_iso_dma_recv_mode m_receive_mode;
Streaming::StreamProcessor *m_Client; // FIXME: implement with functors
enum raw1394_iso_speed m_speed;
// the state machine
enum EHandlerStates {
eHS_Stopped,
eHS_Running,
};
enum EHandlerStates m_State;
enum EHandlerStates m_NextState;
int m_switch_on_cycle;
pthread_mutex_t m_disable_lock;
public:
unsigned int m_packets;
#ifdef DEBUG
unsigned int m_dropped;
unsigned int m_skipped;
int m_min_ahead;
#endif
unsigned int m_deferred_cycles;
protected:
DECLARE_DEBUG_MODULE;
};
typedef std::vector IsoHandlerVector;
typedef std::vector::iterator IsoHandlerVectorIterator;
////
// threads that will handle the packet framing
// one thread per direction, as a compromise for one per
// channel and one for all
class IsoTask : public Util::RunnableInterface
{
friend class IsoHandlerManager;
public:
IsoTask(IsoHandlerManager& manager, enum IsoHandler::EHandlerType);
virtual ~IsoTask();
private:
bool Init();
bool Execute();
/**
* @brief requests the thread to sync it's stream map with the manager
*/
void requestShadowMapUpdate();
enum eActivityResult {
eAR_Activity,
eAR_Timeout,
eAR_Interrupted,
eAR_Error
};
/**
* @brief signals that something happened in one of the clients of this task
*/
void signalActivity();
/**
* @brief wait until something happened in one of the clients of this task
*/
enum eActivityResult waitForActivity();
/**
* @brief This should be called when a busreset has happened.
*/
bool handleBusReset();
void setVerboseLevel(int i);
protected:
IsoHandlerManager& m_manager;
// the event request structure
int32_t request_update;
// static allocation due to RT constraints
// this is the map used by the actual thread
// it is a shadow of the m_StreamProcessors vector
struct pollfd m_poll_fds_shadow[ISOHANDLERMANAGER_MAX_ISO_HANDLERS_PER_PORT];
IsoHandler * m_IsoHandler_map_shadow[ISOHANDLERMANAGER_MAX_ISO_HANDLERS_PER_PORT];
unsigned int m_poll_nfds_shadow;
IsoHandler * m_SyncIsoHandler;
// updates the streams map
void updateShadowMapHelper();
#ifdef DEBUG
uint64_t m_last_loop_entry;
int m_successive_short_loops;
#endif
enum IsoHandler::EHandlerType m_handlerType;
bool m_running;
bool m_in_busreset;
// activity signaling
sem_t m_activity_semaphore;
long long int m_activity_wait_timeout_nsec;
// debug stuff
DECLARE_DEBUG_MODULE;
};
//// the IsoHandlerManager itself
public:
IsoHandlerManager(Ieee1394Service& service);
IsoHandlerManager(Ieee1394Service& service, bool run_rt, int rt_prio);
virtual ~IsoHandlerManager();
bool setThreadParameters(bool rt, int priority);
void setVerboseLevel(int l); ///< set the verbose level
void dumpInfo(); ///< print some information about the manager to stdout/stderr
void dumpInfoForStream(Streaming::StreamProcessor *); ///< print some info about the stream's handler
bool registerStream(Streaming::StreamProcessor *); ///< register an iso stream with the manager
bool unregisterStream(Streaming::StreamProcessor *); ///< unregister an iso stream from the manager
bool startHandlers(); ///< start the managed ISO handlers
bool startHandlers(int cycle); ///< start the managed ISO handlers
bool stopHandlers(); ///< stop the managed ISO handlers
bool reset(); ///< reset the ISO manager and all streams
bool init();
/**
* @brief signals that something happened in one of the clients
*/
void signalActivityTransmit();
void signalActivityReceive();
///> disables the handler attached to the stream
bool stopHandlerForStream(Streaming::StreamProcessor *);
///> starts the handler attached to the specific stream
bool startHandlerForStream(Streaming::StreamProcessor *);
///> starts the handler attached to the specific stream on a specific cycle
bool startHandlerForStream(Streaming::StreamProcessor *, int cycle);
///> Directly tells the handler attached to the stream to start on
///> the given cycle regardless of what is passed to
///> startHandlerForStream().
void setIsoStartCycleForStream(Streaming::StreamProcessor *stream, signed int cycle);
/**
* returns the latency of a wake-up for this stream.
* The latency is the time it takes for a packet is delivered to the
* stream after it has been received (was on the wire).
* expressed in cycles
*/
int getPacketLatencyForStream(Streaming::StreamProcessor *);
/**
* Enables the isohandler manager to ignore missed packets. This
* behaviour is needed by some interfaces which don't send empty
* placeholder packets when no data needs to be sent.
*/
void setMissedCyclesOK(bool ok) { m_MissedCyclesOK = ok; };
private:
IsoHandler * getHandlerForStream(Streaming::StreamProcessor *stream);
void requestShadowMapUpdate();
public:
Ieee1394Service& get1394Service() {return m_service;};
/**
* This should be called when a busreset has happened.
*/
bool handleBusReset();
// the state machine
private:
enum eHandlerStates {
E_Created,
E_Running,
E_Error
};
enum eHandlerStates m_State;
const char *eHSToString(enum eHandlerStates);
private:
Ieee1394Service& m_service;
// note: there is a disctinction between streams and handlers
// because one handler can serve multiple streams (in case of
// multichannel receive)
// only streams are allowed to be registered externally.
// we allocate a handler if we need one, otherwise the stream
// is assigned to another handler
// the collection of handlers
IsoHandlerVector m_IsoHandlers;
bool registerHandler(IsoHandler *);
bool unregisterHandler(IsoHandler *);
void pruneHandlers();
// the collection of streams
Streaming::StreamProcessorVector m_StreamProcessors;
// handler thread/task
bool m_realtime;
int m_priority;
Util::Thread * m_IsoThreadTransmit;
IsoTask * m_IsoTaskTransmit;
Util::Thread * m_IsoThreadReceive;
IsoTask * m_IsoTaskReceive;
bool m_MissedCyclesOK;
// debug stuff
DECLARE_DEBUG_MODULE;
};
#endif /* __FFADO_ISOHANDLERMANAGER__ */