Index: configure
===================================================================
--- configure.orig	2008-11-24 06:58:57.000000000 -0800
+++ configure	2008-11-24 07:00:43.000000000 -0800
@@ -158,6 +158,7 @@
   echo "  --disable-v4l            disable Video4Linux support"
   echo "  --disable-ivtv           disable ivtv support (PVR-x50) req. v4l support"
   echo "  --disable-dvb            disable DVB support"
+  echo "  --disable-r5000          disable support for R5000 USB STBs"
   echo "  --dvb-path=HDRLOC        location of directory containing"
   echo "                           'linux/dvb/frontend.h', not the"
   echo "                           directory with frontend.h [$dvb_path]"
@@ -891,6 +892,7 @@
     hdhomerun
     iptv
     ivtv
+    r5000
     joystick_menu
     libfftw3
     lirc
@@ -1046,6 +1048,7 @@
 dbox2_deps="backend"
 dvb_deps="backend"
 firewire_deps="backend"
+r5000_deps="backend"
 iptv_deps="backend"
 ivtv_deps="backend v4l"
 hdhomerun_deps="backend"
@@ -1179,6 +1182,7 @@
 hdhomerun="yes"
 iptv="yes"
 ivtv="yes"
+r5000="yes"
 joystick_menu="default"
 lamemp3="yes"
 lirc="yes"
@@ -2717,6 +2721,8 @@
 enabled libfftw3 && has_library libfftw3_threads && has_header fftw3.h ||
     disable libfftw3
 
+enabled r5000 && has_library libusb && check_header usb.h || disable r5000
+
 enabled x11 && has_library libX11 || disable x11
 enabled xrandr && has_header X11/extensions/Xrandr.h || disable xrandr
 enabled xv && has_library libXv || disable xv
@@ -2998,6 +3004,7 @@
   echo "DBox2 support             ${dbox2-no}"
   echo "HDHomeRun support         ${hdhomerun-no}"
   echo "IPTV support              ${iptv-no}"
+  echo "R5000 support             ${r5000-no}"
 fi
 
 if enabled frontend; then
Index: libs/libmythtv/cardutil.h
===================================================================
--- libs/libmythtv/cardutil.h.orig	2008-11-24 06:58:12.000000000 -0800
+++ libs/libmythtv/cardutil.h	2008-11-24 07:00:43.000000000 -0800
@@ -53,6 +53,7 @@
         FIREWIRE,
         HDHOMERUN,
         FREEBOX,
+        R5000,
     };
 
     static enum CARD_TYPES toCardType(const QString &name)
@@ -81,6 +82,8 @@
             return HDHOMERUN;
         if ("FREEBOX" == name)
             return FREEBOX;
+        if ("R5000" == name)
+            return R5000;
         return ERROR_UNKNOWN;
     }
 
@@ -89,13 +92,15 @@
         return
             (rawtype != "DVB")       &&
             (rawtype != "FIREWIRE")  && (rawtype != "DBOX2")   &&
-            (rawtype != "HDHOMERUN") && (rawtype != "FREEBOX");
+            (rawtype != "HDHOMERUN") && (rawtype != "FREEBOX") &&
+	    (rawtype != "R5000");
     }
 
     static bool         IsUnscanable(const QString &rawtype)
     {
         return
-            (rawtype == "FIREWIRE")  || (rawtype == "DBOX2");
+            (rawtype == "FIREWIRE")  || (rawtype == "DBOX2") ||
+	    (rawtype == "R5000");
     }
 
     static bool         IsEITCapable(const QString &rawtype)
Index: libs/libmythtv/libmythtv.pro
===================================================================
--- libs/libmythtv/libmythtv.pro.orig	2008-11-24 06:59:14.000000000 -0800
+++ libs/libmythtv/libmythtv.pro	2008-11-24 07:00:43.000000000 -0800
@@ -496,6 +496,23 @@
         DEFINES += USING_DVB
     }
 
+    #Support for R5000 usb device
+    using_r5000 {
+        HEADERS += r5000channel.h           r5000recorder.h
+        HEADERS += r5000signalmonitor.h     r5000device.h
+	HEADERS += r5000/r5000.h            r5000/libusb_augment.h
+	HEADERS += r5000/r5000_internal.h   r5000/r5000init.h
+
+        SOURCES += r5000channel.cpp         r5000recorder.cpp
+        SOURCES += r5000signalmonitor.cpp   r5000device.cpp
+	SOURCES += r5000/r5000.c            r5000/libusb_augment.c
+	SOURCES += r5000/r5k_vip.c          r5000/r5k_directv.c
+	SOURCES += r5000/r5k_sat.c          r5000/r5k_misc.c
+
+	LIBS += -lusb
+        DEFINES += USING_R5000
+    }
+
     DEFINES += USING_BACKEND
 }
 
Index: libs/libmythtv/signalmonitor.h
===================================================================
--- libs/libmythtv/signalmonitor.h.orig	2008-11-24 06:58:12.000000000 -0800
+++ libs/libmythtv/signalmonitor.h	2008-11-24 07:00:43.000000000 -0800
@@ -297,7 +297,8 @@
             (cardtype.upper() == "HDTV")      ||
             (cardtype.upper() == "HDHOMERUN") ||
             (cardtype.upper() == "FIREWIRE")  ||
-            (cardtype.upper() == "FREEBOX"));
+            (cardtype.upper() == "FREEBOX")   ||
+            (cardtype.upper() == "R5000"));
 }
 
 inline bool SignalMonitor::IsSupported(const QString &cardtype)
Index: libs/libmythtv/signalmonitor.cpp
===================================================================
--- libs/libmythtv/signalmonitor.cpp.orig	2008-11-24 06:58:12.000000000 -0800
+++ libs/libmythtv/signalmonitor.cpp	2008-11-24 07:00:43.000000000 -0800
@@ -41,6 +41,11 @@
 #   include "firewirechannel.h"
 #endif
 
+#ifdef USING_R5000
+#   include "r5000signalmonitor.h"
+#   include "r5000channel.h"
+#endif
+
 #undef DBG_SM
 #define DBG_SM(FUNC, MSG) VERBOSE(VB_CHANNEL, \
     "SM("<<channel->GetDevice()<<")::"<<FUNC<<": "<<MSG);
@@ -127,6 +132,14 @@
             signalMonitor = new FirewireSignalMonitor(db_cardnum, fc);
     }
 #endif
+#ifdef USING_R5000
+    if (cardtype.upper() == "R5000")
+    {
+        R5000Channel *fc = dynamic_cast<R5000Channel*>(channel);
+        if (fc)
+            signalMonitor = new R5000SignalMonitor(db_cardnum, fc);
+    }
+#endif
 
     if (!signalMonitor)
     {
Index: libs/libmythtv/cardutil.cpp
===================================================================
--- libs/libmythtv/cardutil.cpp.orig	2008-11-24 06:58:12.000000000 -0800
+++ libs/libmythtv/cardutil.cpp	2008-11-24 07:00:43.000000000 -0800
@@ -1457,7 +1457,8 @@
     if (("FIREWIRE"  == cardtype) ||
         ("FREEBOX"   == cardtype) ||
         ("DBOX2"     == cardtype) ||
-        ("HDHOMERUN" == cardtype))
+        ("HDHOMERUN" == cardtype) ||
+        ("R5000"     == cardtype))
     {
         ret += "MPEG2TS";
     }
@@ -1582,7 +1583,8 @@
     if (("FIREWIRE"  == cardtype) ||
         ("FREEBOX"   == cardtype) ||
         ("DBOX2"     == cardtype) ||
-        ("HDHOMERUN" == cardtype))
+        ("HDHOMERUN" == cardtype) ||
+	("R5000"     == cardtype))
     {
         inputs += "MPEG2TS";
     }
Index: libs/libmythtv/videosource.cpp
===================================================================
--- libs/libmythtv/videosource.cpp.orig	2008-11-24 06:58:12.000000000 -0800
+++ libs/libmythtv/videosource.cpp	2008-11-26 07:04:56.000000000 -0800
@@ -36,6 +36,7 @@
 #include "frequencies.h"
 #include "diseqcsettings.h"
 #include "firewiredevice.h"
+#include "r5000device.h"
 #include "compat.h"
 
 
@@ -1299,6 +1300,62 @@
     }
 };
 
+class R5000Serial : public ComboBoxSetting, public CaptureCardDBStorage
+{
+  public:
+    R5000Serial(const CaptureCard &parent) :
+        ComboBoxSetting(this),
+        CaptureCardDBStorage(this, parent, "videodevice")
+    {
+        setLabel(QObject::tr("Serial #"));
+#ifdef USING_R5000
+        QStringList serials = R5000Device::GetSTBList();
+        for (uint i = 0; i < serials.size(); i++)
+        {
+            addSelection(serials[i]);
+        }
+#endif // USING_FIREWIRE
+    }
+};
+
+class R5000Model : public ComboBoxSetting, public CaptureCardDBStorage
+{
+  public:
+    R5000Model(const CaptureCard  &parent) :
+      ComboBoxSetting(this),
+      CaptureCardDBStorage(this, parent, "firewire_model")
+    {
+        setLabel(QObject::tr("R5000 STB type"));
+        addSelection("VIP211");
+        addSelection("VIP411");
+        addSelection("VIP622");
+        addSelection("VIP722");
+        addSelection("BEV9242");
+        addSelection("DIRECTV");
+        addSelection("STARCHOICE/DSR");
+        addSelection("HDD-200");
+        QString help = QObject::tr(
+            "Choose the type of R5000 enabled STB you are using.");
+        setHelpText(help);
+    }
+};
+class R5000ConfigurationGroup : public VerticalConfigurationGroup
+{
+  public:
+    R5000ConfigurationGroup(CaptureCard& a_parent):
+       VerticalConfigurationGroup(false, true, false, false),
+       parent(a_parent)
+    {
+        setUseLabel(false);
+        addChild(new R5000Serial(parent));
+        addChild(new R5000Model(parent));
+        addChild(new SingleCardInput(parent));
+    };
+
+  private:
+    CaptureCard &parent;
+};
+
 class IPTVHost : public LineEditSetting, public CaptureCardDBStorage
 {
   public:
@@ -1483,6 +1540,10 @@
 #ifdef USING_IPTV
     addTarget("FREEBOX",   new IPTVConfigurationGroup(parent));
 #endif // USING_IPTV
+
+#ifdef USING_R5000
+    addTarget("R5000", new R5000ConfigurationGroup(parent));
+#endif // USING_R5000
 }
 
 void CaptureCardGroup::triggerChanged(const QString& value) 
@@ -1670,6 +1731,10 @@
 #ifdef USING_IPTV
     setting->addSelection(QObject::tr("Network Recorder"), "FREEBOX");
 #endif // USING_IPTV
+
+#ifdef USING_R5000
+    setting->addSelection(QObject::tr("R5000 Capable STB"), "R5000");
+#endif // USING_R5000
 }
 
 class CardID : public SelectLabelSetting, public CardInputDBStorage
Index: libs/libmythtv/tv_rec.cpp
===================================================================
--- libs/libmythtv/tv_rec.cpp.orig	2008-11-24 07:00:42.000000000 -0800
+++ libs/libmythtv/tv_rec.cpp	2008-11-24 07:00:43.000000000 -0800
@@ -50,6 +50,7 @@
 #include "hdhrchannel.h"
 #include "iptvchannel.h"
 #include "firewirechannel.h"
+#include "r5000channel.h"
 
 #include "recorderbase.h"
 #include "NuppelVideoRecorder.h"
@@ -59,6 +60,7 @@
 #include "hdhrrecorder.h"
 #include "iptvrecorder.h"
 #include "firewirerecorder.h"
+#include "r5000recorder.h"
 
 #ifdef USING_V4L
 #include "channel.h"
@@ -199,6 +201,16 @@
         init_run = true;
 #endif
     }    
+    else if (genOpt.cardtype == "R5000")
+    {
+#ifdef USING_R5000
+        channel = new R5000Channel(this, genOpt.videodev, fwOpt.model);
+        if (!channel->Open())
+            return false;
+        InitChannel(genOpt.defaultinput, startchannel);
+        init_run = true;
+#endif
+    }
     else // "V4L" or "MPEG", ie, analog TV
     {
 #ifdef USING_V4L
@@ -1017,6 +1029,12 @@
         recorder->SetOption("mrl", genOpt.videodev);
 #endif // USING_IPTV
     }
+    else if (genOpt.cardtype == "R5000")
+    {
+#ifdef USING_R5000
+        recorder = new R5000Recorder(this, GetR5000Channel());
+#endif // USING_R5000
+    }
     else
     {
 #ifdef USING_V4L
@@ -1230,6 +1248,15 @@
 #endif // USING_FIREWIRE
 }
 
+R5000Channel *TVRec::GetR5000Channel(void)
+{
+#ifdef USING_R5000
+    return dynamic_cast<R5000Channel*>(channel);
+#else
+    return NULL;
+#endif // USING_R5000
+}
+
 Channel *TVRec::GetV4LChannel(void)
 {
 #ifdef USING_V4L
Index: libs/libmythtv/transporteditor.cpp
===================================================================
--- libs/libmythtv/transporteditor.cpp.orig	2008-11-24 06:58:12.000000000 -0800
+++ libs/libmythtv/transporteditor.cpp	2008-11-24 07:00:43.000000000 -0800
@@ -735,7 +735,8 @@
         left->addChild(new Modulation(id, nType));
     }
     else if ((CardUtil::FIREWIRE == nType) ||
-             (CardUtil::FREEBOX  == nType))
+             (CardUtil::FREEBOX  == nType) ||
+             (CardUtil::R5000    == nType))
     {
         left->addChild(new DTVStandard(id, true, true));
     }
Index: libs/libmythtv/tv_rec.h
===================================================================
--- libs/libmythtv/tv_rec.h.orig	2008-11-24 06:58:12.000000000 -0800
+++ libs/libmythtv/tv_rec.h	2008-11-24 07:00:43.000000000 -0800
@@ -39,6 +39,7 @@
 class FirewireChannel;
 class Channel;
 class HDHRChannel;
+class R5000Channel;
 
 class MPEGStreamData;
 class ProgramMapTable;
@@ -281,6 +282,7 @@
     HDHRChannel  *GetHDHRChannel(void);
     DVBChannel   *GetDVBChannel(void);
     FirewireChannel *GetFirewireChannel(void);
+    R5000Channel *GetR5000Channel(void);
     Channel      *GetV4LChannel(void);
 
     bool SetupSignalMonitor(bool enable_table_monitoring, bool notify);
Index: libs/libmythtv/r5000channel.cpp
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libs/libmythtv/r5000channel.cpp	2008-11-27 09:04:39.000000000 -0800
@@ -0,0 +1,199 @@
+/**
+ *  R5000Channel
+ *  Copyright (c) 2005 by Jim Westfall, Dave Abrahams
+ *  Copyright (c) 2006 by Daniel Kristjansson
+ *  Distributed as part of MythTV under GPL v2 and later.
+ */
+
+#include "mythcontext.h"
+#include "tv_rec.h"
+#include "r5000channel.h"
+
+#define LOC QString("R5kChan(%1): ").arg(GetDevice())
+#define LOC_WARN QString("R5kChan(%1), Warning: ").arg(GetDevice())
+#define LOC_ERR QString("R5kChan(%1), Error: ").arg(GetDevice())
+
+R5000Channel::R5000Channel(TVRec *parent, const QString &_videodevice,const QString &_r5ktype) :
+    DTVChannel(parent),
+    videodevice(_videodevice),
+    device(NULL),
+    current_channel(""),
+    current_mpeg_prog(0),
+    isopen(false)
+{
+    int type = R5000Device::GetDeviceType(_r5ktype);
+    device = new R5000Device(type, videodevice);
+
+    InitializeInputs();
+}
+
+bool R5000Channel::SetChannelByString(const QString &channum)
+{
+    QString loc = LOC + QString("SetChannelByString(%1)").arg(channum);
+    bool ok = false;
+    VERBOSE(VB_CHANNEL, loc);
+
+    InputMap::const_iterator it = inputs.find(currentInputID);
+    if (it == inputs.end())
+        return false;
+
+    QString tvformat, modulation, freqtable, freqid, dtv_si_std;
+    int finetune;
+    uint64_t frequency;
+    int mpeg_prog_num;
+    uint atsc_major, atsc_minor, mplexid, tsid, netid;
+    if (!ChannelUtil::GetChannelData(
+        (*it)->sourceid, channum,
+        tvformat, modulation, freqtable, freqid,
+        finetune, frequency,
+        dtv_si_std, mpeg_prog_num, atsc_major, atsc_minor, tsid, netid,
+        mplexid, commfree))
+    {
+        VERBOSE(VB_IMPORTANT, loc + " " + QString(
+                    "Requested channel '%1' is on input '%2' "
+                    "which is in a busy input group")
+                .arg(channum).arg(currentInputID));
+
+        return false;
+    }
+    uint mplexid_restriction;
+    if (!IsInputAvailable(currentInputID, mplexid_restriction))
+    {
+        VERBOSE(VB_IMPORTANT, loc + " " + QString(
+                    "Requested channel '%1' is on input '%2' "
+                    "which is in a busy input group")
+                .arg(channum).arg(currentInputID));
+
+        return false;
+    }
+
+    if (!(*it)->externalChanger.isEmpty())
+    {
+        ok = ChangeExternalChannel(freqid);
+        // -1 resets any state without executing a channel change
+        device->SetChannel(fw_opts.model, 0, mpeg_prog_num);
+        SetSIStandard("mpeg");
+        SetDTVInfo(0,0,0,0,1);
+    }
+    else
+    {
+        ok = isopen && SetChannelByNumber(freqid, mpeg_prog_num);
+    }
+
+    if (ok)
+    {
+        // Set the current channum to the new channel's channum
+        curchannelname = QDeepCopy<QString>(channum);
+        (*it)->startChanNum = QDeepCopy<QString>(channum);
+    }
+
+    VERBOSE(VB_CHANNEL, loc + " " + ((ok) ? "success" : "failure"));
+
+    return ok;
+}
+
+bool R5000Channel::Open(void)
+{
+    VERBOSE(VB_CHANNEL, LOC + "Open()");
+
+    if (inputs.find(currentInputID) == inputs.end())
+        return false;
+
+    if (!device)
+        return false;
+
+    if (isopen)
+        return true;
+
+    if (!device->OpenPort())
+        return false;
+
+    isopen = true;
+
+    return true;
+}
+
+void R5000Channel::Close(void)
+{
+    VERBOSE(VB_CHANNEL, LOC + "Close()");
+    if (isopen)
+    {
+        device->ClosePort();
+        isopen = false;
+    }
+}
+
+QString R5000Channel::GetDevice(void) const
+{
+    return videodevice;
+}
+
+bool R5000Channel::SetPowerState(bool on)
+{
+    if (!isopen)
+    {
+        VERBOSE(VB_IMPORTANT, LOC_ERR +
+                "SetPowerState() called on closed R5000Channel.");
+
+        return false;
+    }
+
+    return device->SetPowerState(on);
+}
+
+R5000Device::PowerState R5000Channel::GetPowerState(void) const
+{
+    if (!isopen)
+    {
+        VERBOSE(VB_IMPORTANT, LOC_ERR +
+                "GetPowerState() called on closed R5000Channel.");
+
+        return R5000Device::kAVCPowerQueryFailed;
+    }
+
+    return device->GetPowerState();
+}
+
+bool R5000Channel::Retune(void)
+{
+    VERBOSE(VB_CHANNEL, LOC + "Retune()");
+
+    if (R5000Device::kAVCPowerOff == GetPowerState())
+    {
+        VERBOSE(VB_IMPORTANT, LOC_ERR +
+                "STB is turned off, must be on to retune.");
+
+        return false;
+    }
+
+    if (current_channel)
+        return SetChannelByNumber(current_channel, current_mpeg_prog);
+
+    return false;
+}
+
+bool R5000Channel::SetChannelByNumber(const QString &channel, int mpeg_prog)
+{
+    VERBOSE(VB_CHANNEL, QString("SetChannelByNumber(%1)").arg(channel));
+    current_channel = channel;
+    current_mpeg_prog = mpeg_prog;
+
+    if (R5000Device::kAVCPowerOff == GetPowerState())
+    {
+        VERBOSE(VB_IMPORTANT, LOC_WARN +
+                "STB is turned off, must be on to set channel.");
+
+        SetSIStandard("mpeg");
+        SetDTVInfo(0,0,0,0,1);
+
+        return true; // signal monitor will call retune later...
+    }
+
+    if (!device->SetChannel(fw_opts.model, channel, mpeg_prog))
+        return false;
+
+    SetSIStandard("mpeg");
+    SetDTVInfo(0,0,0,0,1);
+
+    return true;
+}
Index: libs/libmythtv/r5000device.cpp
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libs/libmythtv/r5000device.cpp	2008-11-27 08:33:05.000000000 -0800
@@ -0,0 +1,425 @@
+/**
+ *  R5000Device
+ *  Copyright (c) 2008 by Alan Nisota
+ *  Copyright (c) 2005 by Jim Westfall
+ *  Distributed as part of MythTV under GPL v2 and later.
+ */
+
+// Qt headers
+#include <qdeepcopy.h>
+
+// MythTV headers
+#include "r5000device.h"
+#include "mythcontext.h"
+#include "pespacket.h"
+
+#define LOC      QString("R5kDev: ")
+#define LOC_WARN QString("R5kDev, Warning: ")
+#define LOC_ERR  QString("R5kDev, Error: ")
+
+static int r5k_init = 0;
+QMap<uint64_t,QString> R5000Device::s_id_to_model;
+QMutex                 R5000Device::s_static_lock;
+
+unsigned int r5000_device_tspacket_handler(unsigned char *tspacket, int len, void *callback_data)
+{
+    R5000Device *fw = (R5000Device*) callback_data;
+    if (! fw)
+        return 0;
+    if (len > 0)
+        fw->BroadcastToListeners(tspacket, len);
+    return 1;
+}
+
+void r5000_msg(char * msg)
+{
+    VERBOSE(VB_IMPORTANT, "R5kLib: "<<msg);
+}
+
+class R5kPriv
+{
+  public:
+    R5kPriv() :
+        reset_timer_on(false),
+        run_port_handler(false), is_port_handler_running(false),
+        channel(-1),
+        is_streaming(false)
+    {
+    }
+
+    bool             reset_timer_on;
+    MythTimer        reset_timer;
+
+    bool             run_port_handler;
+    bool             is_port_handler_running;
+    QMutex           start_stop_port_handler_lock;
+
+    int              channel;
+
+    bool             is_streaming;
+
+    QDateTime        stop_streaming_timer;
+    pthread_t        port_handler_thread;
+
+    static QMutex           s_lock;
+};
+QMutex          R5kPriv::s_lock;
+
+//callback functions
+void *r5000_device_port_handler_thunk(void *param);
+
+R5000Device::R5000Device(int type, QString serial) :
+    m_type(type),
+    m_serial(serial),
+    m_last_channel(""),      m_last_crc(0),
+    m_buffer_cleared(true), m_open_port_cnt(0),
+    m_lock(false),          m_priv(new R5kPriv())
+{
+    QMutexLocker locker(&s_static_lock);
+    usbdev = NULL;
+    if(! r5k_init)
+      r5k_init = r5000_init(r5000_msg);
+}
+
+R5000Device::~R5000Device()
+{
+    if (usbdev)
+    {
+        VERBOSE(VB_IMPORTANT, LOC_ERR + "ctor called with open port");
+        while(usbdev)
+            ClosePort();
+    }
+
+    if (m_priv)
+    {
+        delete m_priv;
+        m_priv = NULL;
+    }
+}
+
+void R5000Device::AddListener(TSDataListener *listener)
+{
+    if (listener)
+    {
+        vector<TSDataListener*>::iterator it =
+            find(m_listeners.begin(), m_listeners.end(), listener);
+
+        if (it == m_listeners.end())
+            m_listeners.push_back(listener);
+    }
+
+    VERBOSE(VB_RECORD, LOC + "AddListener() "<<m_listeners.size());
+    if (!m_listeners.empty())
+    {
+        StartStreaming();
+    }
+}
+
+void R5000Device::RemoveListener(TSDataListener *listener)
+{
+    vector<TSDataListener*>::iterator it = m_listeners.end();
+
+    do
+    {
+        it = find(m_listeners.begin(), m_listeners.end(), listener);
+        if (it != m_listeners.end())
+            m_listeners.erase(it);
+    }
+    while (it != m_listeners.end());
+
+    VERBOSE(VB_RECORD, LOC + "RemoveListener() "<<m_listeners.size());
+    if (m_listeners.empty())
+    {
+        StopStreaming();
+    }
+}
+
+bool R5000Device::StartStreaming(void)
+{
+    if (m_priv->is_streaming)
+        return m_priv->is_streaming;
+
+    if (! usbdev)
+    {
+        VERBOSE(VB_IMPORTANT, LOC_ERR + "Device not open");
+        return false;
+    }
+    if (r5000_start_stream(usbdev))
+    {
+        m_priv->is_streaming = true;
+    }
+    else
+    {
+        VERBOSE(VB_IMPORTANT, LOC_ERR + "Starting A/V streaming ");
+    }
+
+    VERBOSE(VB_RECORD, LOC + "Starting A/V streaming -- done");
+
+    return m_priv->is_streaming;
+}
+
+bool R5000Device::StopStreaming(void)
+{
+    if (m_priv->is_streaming)
+    {
+        VERBOSE(VB_RECORD, LOC + "Stopping A/V streaming -- really");
+
+        m_priv->is_streaming = false;
+
+        r5000_stop_stream(usbdev);
+    }
+
+    VERBOSE(VB_RECORD, LOC + "Stopped A/V streaming");
+
+    return true;
+}
+
+bool R5000Device::SetPowerState(bool on)
+{
+    QMutexLocker locker(&m_lock);
+    QString cmdStr = (on) ? "on" : "off";
+    VERBOSE(VB_RECORD, LOC + QString("Powering %1").arg(cmdStr));
+    r5000_power_on_off(usbdev, on);
+    return true;
+}
+
+R5000Device::PowerState R5000Device::GetPowerState(void)
+{
+    QMutexLocker locker(&m_lock);
+    int on_off;
+
+    VERBOSE(VB_CHANNEL, LOC + "Requesting STB Power State");
+    on_off = r5000_get_power_state(usbdev);
+    VERBOSE(VB_CHANNEL, LOC + (on_off ? "On" : "Off"));
+    return on_off ? kAVCPowerOn : kAVCPowerOff;
+}
+
+bool R5000Device::SetChannel(const QString &panel_model,
+                                const QString &channel, uint mpeg_prog)
+{
+    VERBOSE(VB_CHANNEL, QString("SetChannel(model %1, chan %2 mpeg_prog %3)")
+            .arg(panel_model).arg(channel).arg(mpeg_prog));
+
+    QMutexLocker locker(&m_lock);
+    VERBOSE(VB_CHANNEL, "SetChannel() -- locked");
+    if(! r5000_change_channel(usbdev, channel.ascii(), mpeg_prog))
+        VERBOSE(VB_IMPORTANT, LOC + "Failed to set channel");
+    return true;
+}
+
+void R5000Device::BroadcastToListeners(
+    const unsigned char *data, uint dataSize)
+{
+    if ((dataSize >= TSPacket::SIZE) && (data[0] == SYNC_BYTE) &&
+        ((data[1] & 0x1f) == 0) && (data[2] == 0))
+    {
+        ProcessPATPacket(*((const TSPacket*)data));
+    }
+
+    vector<TSDataListener*>::iterator it = m_listeners.begin();
+    for (; it != m_listeners.end(); ++it)
+        (*it)->AddData(data, dataSize);
+}
+
+void R5000Device::SetLastChannel(const QString &channel)
+{
+    m_buffer_cleared = (channel == m_last_channel);
+    m_last_channel   = channel;
+
+    VERBOSE(VB_IMPORTANT, QString("SetLastChannel(%1): cleared: %2")
+            .arg(channel).arg(m_buffer_cleared ? "yes" : "no"));
+}
+
+void R5000Device::ProcessPATPacket(const TSPacket &tspacket)
+{
+    if (!tspacket.TransportError() && !tspacket.ScramplingControl() &&
+        tspacket.HasPayload() && tspacket.PayloadStart() && !tspacket.PID())
+    {
+        PESPacket pes = PESPacket::View(tspacket);
+        uint crc = pes.CalcCRC();
+        m_buffer_cleared |= (crc != m_last_crc);
+        m_last_crc = crc;
+#if 0
+        VERBOSE(VB_RECORD, LOC +
+                QString("ProcessPATPacket: CRC 0x%1 cleared: %2")
+                .arg(crc,0,16).arg(m_buffer_cleared ? "yes" : "no"));
+#endif
+    }
+    else
+    {
+        VERBOSE(VB_IMPORTANT, LOC_ERR + "Can't handle large PAT's");
+    }
+}
+
+QString R5000Device::GetModelName(uint vendor_id, uint model_id)
+{
+    QMutexLocker locker(&s_static_lock);
+/*
+    if (s_id_to_model.empty())
+        fw_init(s_id_to_model);
+
+    QString ret = s_id_to_model[(((uint64_t) vendor_id) << 32) | model_id];
+
+    if (ret.isEmpty())
+        return "GENERIC";
+*/
+    return "R5000";
+}
+
+bool R5000Device::IsSTBSupported(const QString &panel_model)
+{
+    return true;
+}
+
+bool R5000Device::OpenPort(void)
+{
+    VERBOSE(VB_RECORD, LOC + "Starting Port Handler Thread");
+    QMutexLocker mlocker(&m_lock);
+    VERBOSE(VB_RECORD, LOC + "Starting Port Handler Thread -- locked");
+    if(usbdev) {
+        m_open_port_cnt++;
+        return true;
+    }
+
+    if(m_serial) {
+      VERBOSE(VB_RECORD, LOC + QString("Opening R5000 device type %1 with serial#: "+ m_serial).arg(m_type));
+      usbdev = r5000_open((r5ktype_t)m_type, r5000_device_tspacket_handler, this, m_serial.ascii());
+    } else {
+      VERBOSE(VB_RECORD, LOC + "Opening R5000 device with unknown serial#");
+      usbdev = r5000_open((r5ktype_t)m_type, r5000_device_tspacket_handler, this, NULL);
+    }
+    if(! usbdev) {
+        VERBOSE(VB_IMPORTANT, LOC + "Failed to open R5000 device");
+        return false;
+    }
+
+    VERBOSE(VB_RECORD, LOC + "Starting port handler thread");
+    m_priv->run_port_handler = true;
+    pthread_create(&m_priv->port_handler_thread, NULL,
+                   r5000_device_port_handler_thunk, this);
+
+    VERBOSE(VB_RECORD, LOC + "Waiting for port handler thread to start");
+    while (!m_priv->is_port_handler_running)
+    {
+        m_lock.unlock();
+        usleep(5000);
+        m_lock.lock();
+    }
+
+    VERBOSE(VB_RECORD, LOC + "Port handler thread started");
+
+    m_open_port_cnt++;
+
+    return true;
+}
+
+bool R5000Device::ClosePort(void)
+{
+    VERBOSE(VB_RECORD, LOC + "Stopping Port Handler Thread");
+    QMutexLocker locker(&m_priv->start_stop_port_handler_lock);
+    VERBOSE(VB_RECORD, LOC + "Stopping Port Handler Thread -- locked");
+
+    QMutexLocker mlocker(&m_lock);
+
+    VERBOSE(VB_RECORD, LOC + "ClosePort()");
+
+    if (m_open_port_cnt < 1)
+        return false;
+
+    m_open_port_cnt--;
+
+    if (m_open_port_cnt != 0)
+        return true;
+
+    if (!usbdev)
+        return false;
+
+    VERBOSE(VB_RECORD, LOC + "Waiting for port handler thread to stop");
+    m_priv->run_port_handler = false;
+    while (m_priv->is_port_handler_running)
+    {
+        m_lock.unlock();
+        usleep(5000);
+        m_lock.lock();
+    }
+    VERBOSE(VB_RECORD, LOC + "Joining port handler thread");
+    pthread_join(m_priv->port_handler_thread, NULL);
+
+    r5000_close(usbdev);
+    usbdev = NULL;
+
+    return true;
+}
+
+void *r5000_device_port_handler_thunk(void *param)
+{
+    R5000Device *mon = (R5000Device*) param;
+    mon->RunPortHandler();
+    return NULL;
+}
+
+void R5000Device::RunPortHandler(void)
+{
+    VERBOSE(VB_RECORD, LOC + "RunPortHandler -- start");
+    m_lock.lock();
+    VERBOSE(VB_RECORD, LOC + "RunPortHandler -- got first lock");
+    m_priv->is_port_handler_running = true;
+    m_lock.unlock();
+
+    while (m_priv->run_port_handler)
+    {
+        if (m_priv->is_streaming) {
+            // This will timeout after 10ms regardless of data availability
+            r5000_loop_iterate(usbdev, 10);
+        } else {
+            usleep(10000);
+        }
+    }
+
+    m_lock.lock();
+    m_priv->is_port_handler_running = false;
+    m_lock.unlock();
+    VERBOSE(VB_RECORD, LOC + "RunPortHandler -- end");
+}
+
+QStringList R5000Device::GetSTBList(void)
+{
+    QStringList STBList;
+    int i;
+    r5kenum_t r5k_stbs;
+    if(! r5k_init) {
+        r5k_init = r5000_init(r5000_msg);
+        if(! r5k_init)
+            return STBList;
+    }
+    if (! r5000_find_stbs(&r5k_stbs))
+        VERBOSE(VB_IMPORTANT, LOC + "Locating R5000 devices failed."
+                                    "  This may be a permission problem");
+
+    for (i = 0; i < r5k_stbs.count; i++)
+        STBList.append((char *)r5k_stbs.serial[i]);
+    return STBList;
+}
+
+int R5000Device::GetDeviceType(const QString &r5ktype)
+{
+  QString type = r5ktype.upper();
+  if(type == "DIRECTV") {
+    return R5K_STB_DIRECTV;
+  } else if(type == "VIP211/VIP622/DISH411" ||
+            type == "VIP211/VIP422" ||
+            type == "VIP211" ||
+            type == "VIP411") {
+    return R5K_STB_VIP211;
+  } else if(type == "STARCHOICE/DSR") {
+    return R5K_STB_DSR;
+  } else if(type == "HDD-200") {
+    return R5K_STB_HDD;
+  } else if(type == "VIP622" ||
+            type == "VIP722" ||
+            type == "BEV9242") {
+    return R5K_STB_VIP622;
+  } else {
+    return R5K_STB_VIP211;
+  }
+}
Index: libs/libmythtv/r5000recorder.cpp
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libs/libmythtv/r5000recorder.cpp	2008-11-24 07:00:43.000000000 -0800
@@ -0,0 +1,239 @@
+/**
+ *  R5000Recorder
+ *  Copyright (c) 2005 by Jim Westfall and Dave Abrahams
+ *  Distributed as part of MythTV under GPL v2 and later.
+ */
+
+// MythTV includes
+#include "r5000recorder.h"
+#include "r5000channel.h"
+#include "mythcontext.h"
+#include "mpegtables.h"
+#include "mpegstreamdata.h"
+#include "tv_rec.h"
+
+#define LOC QString("R5000RecBase(%1): ").arg(channel->GetDevice())
+#define LOC_ERR QString("R5000RecBase(%1), Error: ").arg(channel->GetDevice())
+
+R5000Recorder::R5000Recorder(TVRec *rec, R5000Channel *chan) :
+    DTVRecorder(rec), _mpeg_stream_data(NULL),
+    channel(chan), isopen(false)
+{
+    _wait_for_keyframe_option = false;
+}
+
+R5000Recorder::~R5000Recorder()
+{
+    SetStreamData(NULL);
+    Close();
+}
+
+bool R5000Recorder::Open(void)
+{
+    if (!isopen)
+        isopen = channel->GetR5000Device()->OpenPort();
+
+    return isopen;
+}
+
+void R5000Recorder::Close(void)
+{
+    if (isopen)
+    {
+        channel->GetR5000Device()->ClosePort();
+        isopen = false;
+    }
+}
+
+void R5000Recorder::StartStreaming(void)
+{
+    channel->GetR5000Device()->AddListener(this);
+}
+
+void R5000Recorder::StopStreaming(void)
+{
+    channel->GetR5000Device()->RemoveListener(this);
+}
+
+void R5000Recorder::StartRecording(void)
+{
+    VERBOSE(VB_RECORD, LOC + "StartRecording");
+
+    if (!Open())
+    {
+        _error = true;
+        return;
+    }
+
+    _request_recording = true;
+    _recording = true;
+
+    StartStreaming();
+
+    while (_request_recording)
+    {
+        if (!PauseAndWait())
+            usleep(50 * 1000);
+    }
+
+    StopStreaming();
+    FinishRecording();
+
+    _recording = false;
+}
+
+void R5000Recorder::AddData(const unsigned char *data, uint len)
+{
+    uint bufsz = buffer.size();
+    if ((SYNC_BYTE == data[0]) && (TSPacket::SIZE == len) &&
+        (TSPacket::SIZE > bufsz))
+    {
+        if (bufsz)
+            buffer.clear();
+
+        ProcessTSPacket(*(reinterpret_cast<const TSPacket*>(data)));
+        return;
+    }
+
+    buffer.insert(buffer.end(), data, data + len);
+    bufsz += len;
+
+    int sync_at = -1;
+    for (uint i = 0; (i < bufsz) && (sync_at < 0); i++)
+    {
+        if (buffer[i] == SYNC_BYTE)
+            sync_at = i;
+    }
+
+    if (sync_at < 0)
+        return;
+
+    if (bufsz < 30 * TSPacket::SIZE)
+        return; // build up a little buffer
+
+    while (sync_at + TSPacket::SIZE < bufsz)
+    {
+        ProcessTSPacket(*(reinterpret_cast<const TSPacket*>(
+                              &buffer[0] + sync_at)));
+
+        sync_at += TSPacket::SIZE;
+    }
+
+    buffer.erase(buffer.begin(), buffer.begin() + sync_at);
+
+    return;
+}
+
+void R5000Recorder::ProcessTSPacket(const TSPacket &tspacket)
+{
+    if (tspacket.TransportError())
+        return;
+
+    if (tspacket.ScramplingControl())
+        return;
+
+    if (tspacket.HasAdaptationField())
+        GetStreamData()->HandleAdaptationFieldControl(&tspacket);
+
+    if (tspacket.HasPayload())
+    {
+        const unsigned int lpid = tspacket.PID();
+        // Pass or reject packets based on PID, and parse info from them
+        if (lpid == GetStreamData()->VideoPIDSingleProgram())
+        {
+            _buffer_packets = !FindMPEG2Keyframes(&tspacket);
+            BufferedWrite(tspacket);
+        }
+        else if (GetStreamData()->IsAudioPID(lpid))
+        {
+            _buffer_packets = !FindAudioKeyframes(&tspacket);
+            BufferedWrite(tspacket);
+        }
+        else if (GetStreamData()->IsListeningPID(lpid))
+            GetStreamData()->HandleTSTables(&tspacket);
+        else if (GetStreamData()->IsWritingPID(lpid))
+            BufferedWrite(tspacket);
+    }
+}
+
+void R5000Recorder::SetOptionsFromProfile(RecordingProfile *profile,
+                                                 const QString &videodev,
+                                                 const QString &audiodev,
+                                                 const QString &vbidev)
+{
+    (void)videodev;
+    (void)audiodev;
+    (void)vbidev;
+    (void)profile;
+}
+
+// documented in recorderbase.cpp
+bool R5000Recorder::PauseAndWait(int timeout)
+{
+    if (request_pause)
+    {
+        VERBOSE(VB_RECORD, LOC + "PauseAndWait("<<timeout<<") -- pause");
+        if (!paused)
+        {
+            StopStreaming();
+            paused = true;
+            pauseWait.wakeAll();
+            if (tvrec)
+                tvrec->RecorderPaused();
+        }
+        unpauseWait.wait(timeout);
+    }
+    if (!request_pause && paused)
+    {
+        VERBOSE(VB_RECORD, LOC + "PauseAndWait("<<timeout<<") -- unpause");
+        StartStreaming();
+        paused = false;
+    }
+    return paused;
+}
+
+void R5000Recorder::SetStreamData(MPEGStreamData *data)
+{
+    VERBOSE(VB_RECORD, LOC + "SetStreamData("<<data<<") -- begin");
+    if (data == _mpeg_stream_data)
+    {
+        VERBOSE(VB_RECORD, LOC + "SetStreamData("<<data<<") -- end 0");
+        return;
+    }
+
+    MPEGStreamData *old_data = _mpeg_stream_data;
+    _mpeg_stream_data = data;
+    if (old_data)
+        delete old_data;
+
+    if (data)
+    {
+        data->AddMPEGSPListener(this);
+
+        if (data->DesiredProgram() >= 0)
+            data->SetDesiredProgram(data->DesiredProgram());
+    }
+    VERBOSE(VB_RECORD, LOC + "SetStreamData("<<data<<") -- end 1");
+}
+
+void R5000Recorder::HandleSingleProgramPAT(ProgramAssociationTable *pat)
+{
+    if (!pat) {
+        VERBOSE(VB_RECORD, LOC + "HandleSingleProgramPAT(NULL)");
+        return;
+    }
+    int next = (pat->tsheader()->ContinuityCounter()+1)&0xf;
+    pat->tsheader()->SetContinuityCounter(next);
+    BufferedWrite(*(reinterpret_cast<const TSPacket*>(pat->tsheader())));
+}
+
+void R5000Recorder::HandleSingleProgramPMT(ProgramMapTable *pmt)
+{
+    if (!pmt) {
+        VERBOSE(VB_RECORD, LOC + "HandleSingleProgramPMT(NULL)");
+        return;
+    }
+    int next = (pmt->tsheader()->ContinuityCounter()+1)&0xf;
+    pmt->tsheader()->SetContinuityCounter(next);
+    BufferedWrite(*(reinterpret_cast<const TSPacket*>(pmt->tsheader())));
+}
Index: libs/libmythtv/r5000signalmonitor.cpp
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libs/libmythtv/r5000signalmonitor.cpp	2008-11-24 07:00:43.000000000 -0800
@@ -0,0 +1,312 @@
+// -*- Mode: c++ -*-
+// Copyright (c) 2006, Daniel Thor Kristjansson
+
+#include <pthread.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/select.h>
+
+#include "mythcontext.h"
+#include "mythdbcon.h"
+#include "atscstreamdata.h"
+#include "mpegtables.h"
+#include "atsctables.h"
+#include "r5000channel.h"
+#include "r5000signalmonitor.h"
+
+#define LOC QString("R5kSM(%1): ").arg(channel->GetDevice())
+#define LOC_WARN QString("R5kSM(%1), Warning: ").arg(channel->GetDevice())
+#define LOC_ERR QString("R5kSM(%1), Error: ").arg(channel->GetDevice())
+
+const uint R5000SignalMonitor::kPowerTimeout  = 3000; /* ms */
+const uint R5000SignalMonitor::kBufferTimeout = 5000; /* ms */
+
+QMap<void*,uint> R5000SignalMonitor::pat_keys;
+QMutex           R5000SignalMonitor::pat_keys_lock;
+
+/** \fn R5000SignalMonitor::R5000SignalMonitor(int,R5000Channel*,uint,const char*)
+ *  \brief Initializes signal lock and signal values.
+ *
+ *   Start() must be called to actually begin continuous
+ *   signal monitoring. The timeout is set to 3 seconds,
+ *   and the signal threshold is initialized to 0%.
+ *
+ *  \param db_cardnum Recorder number to monitor,
+ *                    if this is less than 0, SIGNAL events will not be
+ *                    sent to the frontend even if SetNotifyFrontend(true)
+ *                    is called.
+ *  \param _channel R5000Channel for card
+ *  \param _flags   Flags to start with
+ *  \param _name    Name for Qt signal debugging
+ */
+R5000SignalMonitor::R5000SignalMonitor(
+    int db_cardnum,
+    R5000Channel *_channel,
+    uint64_t _flags, const char *_name) :
+    DTVSignalMonitor(db_cardnum, _channel, _flags, _name),
+    dtvMonitorRunning(false),
+    stb_needs_retune(true),
+    stb_needs_to_wait_for_pat(false),
+    stb_needs_to_wait_for_power(false)
+{
+    VERBOSE(VB_CHANNEL, LOC + "ctor");
+
+    signalStrength.SetThreshold(65);
+
+    AddFlags(kDTVSigMon_WaitForSig);
+
+    stb_needs_retune =
+        (R5000Device::kAVCPowerOff == _channel->GetPowerState());
+}
+
+/** \fn R5000SignalMonitor::~R5000SignalMonitor()
+ *  \brief Stops signal monitoring and table monitoring threads.
+ */
+R5000SignalMonitor::~R5000SignalMonitor()
+{
+    VERBOSE(VB_CHANNEL, LOC + "dtor");
+    Stop();
+}
+
+void R5000SignalMonitor::deleteLater(void)
+{
+    disconnect(); // disconnect signals we may be sending...
+    Stop();
+    DTVSignalMonitor::deleteLater();
+}
+
+/** \fn R5000SignalMonitor::Stop(void)
+ *  \brief Stop signal monitoring and table monitoring threads.
+ */
+void R5000SignalMonitor::Stop(void)
+{
+    VERBOSE(VB_CHANNEL, LOC + "Stop() -- begin");
+    SignalMonitor::Stop();
+    if (dtvMonitorRunning)
+    {
+        dtvMonitorRunning = false;
+        pthread_join(table_monitor_thread, NULL);
+    }
+    VERBOSE(VB_CHANNEL, LOC + "Stop() -- end");
+}
+
+void R5000SignalMonitor::HandlePAT(const ProgramAssociationTable *pat)
+{
+    AddFlags(kDTVSigMon_PATSeen);
+
+    R5000Channel *fwchan = dynamic_cast<R5000Channel*>(channel);
+    bool crc_bogus = !fwchan->GetR5000Device()->IsSTBBufferCleared();
+    if (crc_bogus && stb_needs_to_wait_for_pat &&
+        (stb_wait_for_pat_timer.elapsed() < (int)kBufferTimeout))
+    {
+        VERBOSE(VB_CHANNEL, LOC + "HandlePAT() ignoring PAT");
+        uint tsid = pat->TransportStreamID();
+        GetStreamData()->SetVersionPAT(tsid, -1,0);
+        return;
+    }
+
+    if (crc_bogus && stb_needs_to_wait_for_pat)
+    {
+        VERBOSE(VB_IMPORTANT, LOC_WARN + "Wait for valid PAT timed out");
+        stb_needs_to_wait_for_pat = false;
+    }
+
+    DTVSignalMonitor::HandlePAT(pat);
+}
+
+void R5000SignalMonitor::HandlePMT(uint pnum, const ProgramMapTable *pmt)
+{
+    VERBOSE(VB_CHANNEL, LOC + "HandlePMT()");
+
+    AddFlags(kDTVSigMon_PMTSeen);
+
+    if (!HasFlags(kDTVSigMon_PATMatch))
+    {
+        GetStreamData()->SetVersionPMT(pnum, -1,0);
+        VERBOSE(VB_CHANNEL, LOC + "HandlePMT() ignoring PMT");
+        return;
+    }
+
+    DTVSignalMonitor::HandlePMT(pnum, pmt);
+}
+
+void *R5000SignalMonitor::TableMonitorThread(void *param)
+{
+    R5000SignalMonitor *mon = (R5000SignalMonitor*) param;
+    mon->RunTableMonitor();
+    return NULL;
+}
+
+void R5000SignalMonitor::RunTableMonitor(void)
+{
+    stb_needs_to_wait_for_pat = true;
+    stb_wait_for_pat_timer.start();
+    dtvMonitorRunning = true;
+
+    VERBOSE(VB_CHANNEL, LOC + "RunTableMonitor(): -- begin");
+
+    R5000Channel *lchan = dynamic_cast<R5000Channel*>(channel);
+    if (!lchan)
+    {
+        VERBOSE(VB_CHANNEL, LOC + "RunTableMonitor(): -- err end");
+        dtvMonitorRunning = false;
+        return;
+    }
+
+    R5000Device *dev = lchan->GetR5000Device();
+
+    dev->OpenPort();
+    dev->AddListener(this);
+
+    while (dtvMonitorRunning && GetStreamData())
+        usleep(100000);
+
+    VERBOSE(VB_CHANNEL, LOC + "RunTableMonitor(): -- shutdown ");
+
+    dev->RemoveListener(this);
+    dev->ClosePort();
+
+    dtvMonitorRunning = false;
+
+    VERBOSE(VB_CHANNEL, LOC + "RunTableMonitor(): -- end");
+}
+
+void R5000SignalMonitor::AddData(const unsigned char *data, uint len)
+{
+    if (!dtvMonitorRunning)
+        return;
+
+    if (GetStreamData())
+        GetStreamData()->ProcessData((unsigned char *)data, len);
+}
+
+/** \fn R5000SignalMonitor::UpdateValues(void)
+ *  \brief Fills in frontend stats and emits status Qt signals.
+ *
+ *   This function uses five ioctl's FE_READ_SNR, FE_READ_SIGNAL_STRENGTH
+ *   FE_READ_BER, FE_READ_UNCORRECTED_BLOCKS, and FE_READ_STATUS to obtain
+ *   statistics from the frontend.
+ *
+ *   This is automatically called by MonitorLoop(), after Start()
+ *   has been used to start the signal monitoring thread.
+ */
+void R5000SignalMonitor::UpdateValues(void)
+{
+    if (!running || exit)
+        return;
+
+    if (dtvMonitorRunning)
+    {
+        EmitR5000Signals();
+        if (IsAllGood())
+            emit AllGood();
+        // TODO dtv signals...
+
+        update_done = true;
+        return;
+    }
+
+    if (stb_needs_to_wait_for_power &&
+        (stb_wait_for_power_timer.elapsed() < (int)kPowerTimeout))
+    {
+        return;
+    }
+    stb_needs_to_wait_for_power = false;
+
+    R5000Channel *fwchan = dynamic_cast<R5000Channel*>(channel);
+
+    if (HasFlags(kFWSigMon_WaitForPower) && !HasFlags(kFWSigMon_PowerMatch))
+    {
+        bool retried = false;
+        while (true)
+        {
+            R5000Device::PowerState power = fwchan->GetPowerState();
+            if (R5000Device::kAVCPowerOn == power)
+            {
+                AddFlags(kFWSigMon_PowerSeen | kFWSigMon_PowerMatch);
+            }
+            else if (R5000Device::kAVCPowerOff == power)
+            {
+                AddFlags(kFWSigMon_PowerSeen);
+                fwchan->SetPowerState(true);
+                stb_wait_for_power_timer.start();
+                stb_needs_to_wait_for_power = true;
+            }
+            else
+            {
+                bool qfailed = (R5000Device::kAVCPowerQueryFailed == power);
+                if (qfailed && !retried)
+                {
+                    retried = true;
+                    continue;
+                }
+
+                VERBOSE(VB_RECORD, "Can't determine if STB is power on, "
+                        "assuming it is...");
+                AddFlags(kFWSigMon_PowerSeen | kFWSigMon_PowerMatch);
+            }
+            break;
+        }
+    }
+
+    bool isLocked = !HasFlags(kFWSigMon_WaitForPower) ||
+        HasFlags(kFWSigMon_WaitForPower | kFWSigMon_PowerMatch);
+
+    if (isLocked && stb_needs_retune)
+    {
+        fwchan->Retune();
+        isLocked = stb_needs_retune = false;
+    }
+
+    // Set SignalMonitorValues from info from card.
+    {
+        QMutexLocker locker(&statusLock);
+        signalStrength.SetValue(isLocked ? 100 : 0);
+        signalLock.SetValue(isLocked ? 1 : 0);
+    }
+
+    EmitR5000Signals();
+    if (IsAllGood())
+        emit AllGood();
+
+    // Start table monitoring if we are waiting on any table
+    // and we have a lock.
+    if (isLocked && GetStreamData() &&
+        HasAnyFlag(kDTVSigMon_WaitForPAT | kDTVSigMon_WaitForPMT |
+                   kDTVSigMon_WaitForMGT | kDTVSigMon_WaitForVCT |
+                   kDTVSigMon_WaitForNIT | kDTVSigMon_WaitForSDT))
+    {
+        pthread_create(&table_monitor_thread, NULL,
+                       TableMonitorThread, this);
+
+        VERBOSE(VB_CHANNEL, LOC + "UpdateValues() -- "
+                "Waiting for table monitor to start");
+
+        while (!dtvMonitorRunning)
+            usleep(50);
+
+        VERBOSE(VB_CHANNEL, LOC + "UpdateValues() -- "
+                "Table monitor started");
+    }
+
+    update_done = true;
+}
+
+#define EMIT(SIGNAL_FUNC, SIGNAL_VAL) \
+    do { statusLock.lock(); \
+         SignalMonitorValue val = SIGNAL_VAL; \
+         statusLock.unlock(); \
+         emit SIGNAL_FUNC(val); } while (false)
+
+/** \fn R5000SignalMonitor::EmitR5000Signals(void)
+ *  \brief Emits signals for lock, signal strength, etc.
+ */
+void R5000SignalMonitor::EmitR5000Signals(void)
+{
+    // Emit signals..
+    EMIT(StatusSignalLock, signalLock);
+    if (HasFlags(kDTVSigMon_WaitForSig))
+        EMIT(StatusSignalStrength, signalStrength);
+}
+
+#undef EMIT
Index: libs/libmythtv/r5000channel.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libs/libmythtv/r5000channel.h	2008-11-27 08:31:03.000000000 -0800
@@ -0,0 +1,52 @@
+/**
+ *  R5000Channel
+ *  Copyright (c) 2008 by Alan Nisota
+ *  Copyright (c) 2005 by Jim Westfall and Dave Abrahams
+ *  Distributed as part of MythTV under GPL v2 and later.
+ */
+
+#ifndef _R5000CHANNEL_H_
+#define _R5000CHANNEL_H_
+
+#include "tv_rec.h"
+#include "dtvchannel.h"
+#include "r5000device.h"
+
+class R5000Channel : public DTVChannel
+{
+  public:
+    R5000Channel(TVRec *parent, const QString &videodevice,
+                    const QString &_r5ktype);
+    ~R5000Channel() { Close(); }
+
+    // Commands
+    virtual bool Open(void);
+    virtual void Close(void);
+
+    virtual bool TuneMultiplex(uint /*mplexid*/, QString /*inputname*/)
+        { return false; }
+    virtual bool Tune(const DTVMultiplex &/*tuning*/, QString /*inputname*/)
+        { return false; }
+    virtual bool Retune(void);
+
+    // Sets
+    virtual bool SetChannelByString(const QString &chan);
+    virtual bool SetChannelByNumber(const QString &channel, int mpeg_prog);
+    virtual bool SetPowerState(bool on);
+
+    // Gets
+    virtual bool IsOpen(void) const { return isopen; }
+    virtual R5000Device::PowerState GetPowerState(void) const;
+    virtual QString GetDevice(void) const;
+    virtual R5000Device *GetR5000Device(void) { return device; }
+
+  protected:
+    QString            videodevice;
+    FireWireDBOptions  fw_opts;
+    R5000Device    *device;
+    QString            current_channel;
+    uint               current_mpeg_prog;
+    bool               isopen;
+};
+
+#endif // _FIREWIRECHANNEL_H_
Index: libs/libmythtv/r5000device.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libs/libmythtv/r5000device.h	2008-11-27 08:34:43.000000000 -0800
@@ -0,0 +1,110 @@
+/**
+ *  R5000Device
+ *  Copyright (c) 2005 by Jim Westfall
+ *  Distributed as part of MythTV under GPL v2 and later.
+ */
+
+#ifndef _R5000_DEVICE_H_
+#define _R5000_DEVICE_H_
+
+// C++ headers
+#include <vector>
+using namespace std;
+
+// Qt headers
+#include <qstring.h>
+#include <qmutex.h>
+
+// MythTV headers
+#include "streamlisteners.h"
+
+extern "C" {
+#include "r5000/r5000.h"
+}
+
+class TSPacket;
+class R5kPriv;
+class R5000Device
+{
+  public:
+
+    // Public enums
+    typedef enum
+    {
+        kAVCPowerOn,
+        kAVCPowerOff,
+        kAVCPowerUnknown,
+        kAVCPowerQueryFailed,
+    } PowerState;
+
+
+    // AVC param 0
+    typedef enum
+    {
+        kAVCPowerStateOn           = 0x70,
+        kAVCPowerStateOff          = 0x60,
+        kAVCPowerStateQuery        = 0x7f,
+    } IEEE1394UnitPowerParam0;
+
+    R5000Device(int type, QString serial);
+    ~R5000Device();
+
+    bool OpenPort(void);
+    bool ClosePort(void);
+    void RunPortHandler(void);
+
+    // Commands
+    virtual bool ResetBus(void) { return false; }
+
+    virtual void AddListener(TSDataListener*);
+    virtual void RemoveListener(TSDataListener*);
+
+    // Sets
+    virtual bool SetPowerState(bool on);
+    virtual bool SetChannel(const QString &panel_model,
+                            const QString &channel, uint mpeg_prog);
+
+    // Gets
+    bool IsSTBBufferCleared(void) const { return m_buffer_cleared; }
+
+    // non-const Gets
+    virtual PowerState GetPowerState(void);
+
+    // Statics
+    static bool IsSTBSupported(const QString &model);
+    static QString GetModelName(uint vendorid, uint modelid);
+    static QStringList GetSTBList(void);
+    static int GetDeviceType(const QString &r5ktype);
+    void BroadcastToListeners(
+        const unsigned char *data, uint dataSize);
+
+  protected:
+
+    bool GetSubunitInfo(uint8_t table[32]);
+
+    void SetLastChannel(const QString &channel);
+    void ProcessPATPacket(const TSPacket&);
+    bool StartStreaming(void);
+    bool StopStreaming(void);
+
+    int                      m_type;
+    QString                  m_serial;
+    uint                     m_subunitid;
+    uint                     m_speed;
+    QString                  m_last_channel;
+    uint                     m_last_crc;
+    bool                     m_buffer_cleared;
+
+    uint                     m_open_port_cnt;
+    vector<TSDataListener*>  m_listeners;
+    mutable QMutex           m_lock;
+
+    /// Vendor ID + Model ID to R5000Device STB model string
+    static QMap<uint64_t,QString> s_id_to_model;
+    static QMutex                 s_static_lock;
+private:
+    r5kdev_t                 *usbdev;
+    R5kPriv                  *m_priv;
+};
+
+#endif // _FIREWIRE_DEVICE_H_
Index: libs/libmythtv/r5000recorder.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libs/libmythtv/r5000recorder.h	2008-11-24 07:00:43.000000000 -0800
@@ -0,0 +1,72 @@
+/**
+ *  R5000Recorder
+ *  Copyright (c) 2005 by Jim Westfall
+ *  Distributed as part of MythTV under GPL v2 and later.
+ */
+
+#ifndef _R5000RECORDER_H_
+#define _R5000RECORDER_H_
+
+// MythTV headers
+#include "dtvrecorder.h"
+#include "tspacket.h"
+#include "streamlisteners.h"
+
+class TVRec;
+class R5000Channel;
+
+/** \class R5000Recorder
+ *  \brief This is a specialization of DTVRecorder used to
+ *         handle DVB and ATSC streams from a firewire input.
+ *
+ *  \sa DTVRecorder
+ */
+class R5000Recorder : public DTVRecorder,
+                         public MPEGSingleProgramStreamListener,
+                         public TSDataListener
+{
+    friend class MPEGStreamData;
+    friend class TSPacketProcessor;
+
+  public:
+    R5000Recorder(TVRec *rec, R5000Channel *chan);
+    virtual ~R5000Recorder();
+
+    // Commands
+    bool Open(void);
+    void Close(void);
+
+    void StartStreaming(void);
+    void StopStreaming(void);
+
+    void StartRecording(void);
+    bool PauseAndWait(int timeout = 100);
+
+    void AddData(const unsigned char *data, uint dataSize);
+    void ProcessTSPacket(const TSPacket &tspacket);
+
+    // Sets
+    void SetOptionsFromProfile(RecordingProfile *profile,
+                               const QString &videodev,
+                               const QString &audiodev,
+                               const QString &vbidev);
+    void SetStreamData(MPEGStreamData*);
+
+    // Gets
+    MPEGStreamData *GetStreamData(void) { return _mpeg_stream_data; }
+
+    // MPEG Single Program
+    void HandleSingleProgramPAT(ProgramAssociationTable*);
+    void HandleSingleProgramPMT(ProgramMapTable*);
+
+  protected:
+    R5000Recorder(TVRec *rec);
+
+  private:
+    MPEGStreamData        *_mpeg_stream_data;
+    R5000Channel       *channel;
+    bool                   isopen;
+    vector<unsigned char>  buffer;
+};
+
+#endif //  _R5000RECORDER_H_
Index: libs/libmythtv/r5000signalmonitor.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libs/libmythtv/r5000signalmonitor.h	2008-11-24 07:00:43.000000000 -0800
@@ -0,0 +1,67 @@
+// -*- Mode: c++ -*-
+
+#ifndef _R5000SIGNALMONITOR_H_
+#define _R5000SIGNALMONITOR_H_
+
+#include <qmap.h>
+#include <qmutex.h>
+#include <qdatetime.h>
+
+#include "dtvsignalmonitor.h"
+#include "firewiredevice.h"
+#include "util.h"
+
+class R5000Channel;
+
+class R5000SignalMonitor : public DTVSignalMonitor, public TSDataListener
+{
+    Q_OBJECT
+
+  public:
+    R5000SignalMonitor(int db_cardnum, R5000Channel *_channel,
+                          uint64_t _flags = kFWSigMon_WaitForPower,
+                          const char *_name = "R5000SignalMonitor");
+
+    virtual void HandlePAT(const ProgramAssociationTable*);
+    virtual void HandlePMT(uint, const ProgramMapTable*);
+
+    void Stop(void);
+
+  public slots:
+    void deleteLater(void);
+
+  protected:
+    R5000SignalMonitor(void);
+    R5000SignalMonitor(const R5000SignalMonitor&);
+    virtual ~R5000SignalMonitor();
+
+    virtual void UpdateValues(void);
+    void EmitR5000Signals(void);
+
+    static void *TableMonitorThread(void *param);
+    void RunTableMonitor(void);
+
+    bool SupportsTSMonitoring(void);
+
+    void AddData(const unsigned char *data, uint dataSize);
+
+  public:
+    static const uint kPowerTimeout;
+    static const uint kBufferTimeout;
+
+  protected:
+    bool               dtvMonitorRunning;
+    pthread_t          table_monitor_thread;
+    bool               stb_needs_retune;
+    bool               stb_needs_to_wait_for_pat;
+    bool               stb_needs_to_wait_for_power;
+    MythTimer          stb_wait_for_pat_timer;
+    MythTimer          stb_wait_for_power_timer;
+
+    vector<unsigned char> buffer;
+
+    static QMap<void*,uint> pat_keys;
+    static QMutex           pat_keys_lock;
+};
+
+#endif // _R5000SIGNALMONITOR_H_
Index: libs/libmythtv/r5000/libusb_augment.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libs/libmythtv/r5000/libusb_augment.c	2008-11-26 06:56:44.000000000 -0800
@@ -0,0 +1,311 @@
+/* Copyright 2008 Alan Nisota <alannisota@gmail.com>
+ * 2005-10-19/lindi: downloaded from http://www.gaesi.org/~nmct/cvista/cvista/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+// libusb_augment.c
+// $Revision$
+// $Date$
+
+// Hopefully, the functions in this file will become part of libusb.
+
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <usb.h>
+#include <linux/usbdevice_fs.h>
+#include <string.h>
+#include <signal.h>
+#define LIBUSB_AUGMENT
+#include "libusb_augment.h"
+
+// Taken from libusb file usbi.h because usb.h
+// hides the definition of usb_dev_handle.
+extern int usb_debug;
+
+struct usb_dev_handle {
+  int fd;
+
+  struct usb_bus *bus;
+  struct usb_device *device;
+
+  int config;
+  int interface;
+  int altsetting;
+
+  /* Added by RMT so implementations can store other per-open-device data */
+  void *impl_info;
+};
+
+// Taken from libusb file error.h to supply error handling macro definition.
+typedef enum {
+  USB_ERROR_TYPE_NONE = 0,
+  USB_ERROR_TYPE_STRING,
+  USB_ERROR_TYPE_ERRNO,
+} usb_error_type_t;
+
+extern char usb_error_str[1024];
+extern usb_error_type_t usb_error_type;
+
+#define USB_ERROR_STR(format, args...) \
+        do { \
+          usb_error_type = USB_ERROR_TYPE_STRING; \
+          snprintf(usb_error_str, sizeof(usb_error_str) - 1, format, ## args); \
+          if (usb_debug >= 2) \
+            fprintf(stderr, "USB error: %s\n", usb_error_str); \
+        } while (0)
+
+static int urb_signr = 0;
+void (*urb_completion_callback)(struct usbdevfs_urb *) = NULL;
+#define USB_ASYNC_COMPLETION_SIGNAL (SIGRTMIN + 5)
+
+void urb_completion_handler(int signum, siginfo_t *info, void *context)
+{
+   struct usbdevfs_urb *urb = (struct usbdevfs_urb *)info->si_addr;
+   struct usbdevfs_urb *context1;
+   usb_dev_handle *dev = (usb_dev_handle *)urb->usercontext;
+   int ret;
+   if (info->si_code != SI_ASYNCIO ||
+       info->si_signo != USB_ASYNC_COMPLETION_SIGNAL) {
+       return;
+   }
+   if(info->si_errno != 0) {
+     USB_ERROR_STR("Async URB Completion failed: %s", strerror(info->si_errno));
+     return;
+   }
+   ret = ioctl(dev->fd, USBDEVFS_REAPURB, &context1);
+   if(ret  < 0) {
+     USB_ERROR_STR("Failed to read URB: %s", strerror(-ret));
+     return;
+   }
+   if(context1 != urb) {
+     USB_ERROR_STR("Reaped unexpected urb");
+     return;
+   }
+   if(urb_completion_callback)
+     urb_completion_callback(urb);
+}
+
+int usbdevfs_urb_signal_completion(void (*cb)( struct usbdevfs_urb *))
+{
+  urb_completion_callback = cb;
+  urb_signr = USB_ASYNC_COMPLETION_SIGNAL;
+  struct sigaction usb_linux_sa;
+  usb_linux_sa.sa_sigaction = urb_completion_handler;
+  sigfillset(&usb_linux_sa.sa_mask);
+  usb_linux_sa.sa_flags = SA_SIGINFO;
+  usb_linux_sa.sa_flags |= SA_ONSTACK;
+  sigaction(USB_ASYNC_COMPLETION_SIGNAL, &usb_linux_sa, NULL);
+  return 0;
+}
+
+struct usbdevfs_urb *usb_bulk_setup(
+                struct usbdevfs_urb *iso_urb, // URB pointer-pointer.
+                unsigned char ep,  // Device endpoint.
+                char *bytes,       // Data buffer pointer.
+                int size) {        // Size of the buffer.
+  struct usbdevfs_urb *local_urb;
+
+  // No more than 16384 bytes can be transferred at a time.
+  if (size > 16384) {
+    USB_ERROR_STR("error on transfer size: %s", strerror(EINVAL));
+    return NULL;
+  }
+  local_urb = iso_urb;
+  if (!local_urb) {
+    local_urb = (struct usbdevfs_urb *) calloc(1, sizeof(struct usbdevfs_urb));
+    if (!local_urb) {
+      USB_ERROR_STR("error on packet size: %s", strerror(EINVAL));
+      return NULL;
+    }
+  }
+  local_urb->type = USBDEVFS_URB_TYPE_BULK;
+  local_urb->endpoint = ep;
+  local_urb->status = 0;
+  local_urb->flags = 0;
+  local_urb->buffer = bytes;
+  local_urb->buffer_length = size;
+  local_urb->actual_length = 0;
+  local_urb->start_frame = 0;
+  local_urb->number_of_packets = 0;
+  local_urb->error_count = 0;
+  local_urb->signr = urb_signr;
+  local_urb->usercontext = (void *) 0;
+  return local_urb;
+}
+// Reading and writing are the same except for the endpoint
+int usb_isochronous_setup(struct usbdevfs_urb **iso_urb, // URB pointer-pointer.
+                          unsigned char ep,  // Device endpoint.
+                          int pktsize,       // Endpoint packet size.
+                          char *bytes,       // Data buffer pointer.
+                          int size) {        // Size of the buffer.
+  struct usbdevfs_urb *local_urb;
+  // int ret
+  // was unused /lindi
+  int pktcount, fullpkts, partpktsize, packets, urb_size;
+
+  // No more than 32768 bytes can be transferred at a time.
+  if (size > 32768) {
+    USB_ERROR_STR("error on transfer size: %s", strerror(EINVAL));
+    return -EINVAL;
+  }
+
+  // Determine the number of packets that need to be created based upon the
+  // amount of data to be transferred, and the maximum packet size of the
+  // endpoint.
+
+  // Find integral number of full packets.
+  //fprintf(stderr, "buf size: %d\n", size);
+  //fprintf(stderr, "iso size: %d\n", pktsize);
+  fullpkts = size / pktsize;
+  //fprintf(stderr, "Number of full packets: %d\n", fullpkts);
+  // Find length of partial packet.
+  partpktsize = size % pktsize;
+  //fprintf(stderr, "Size of partial packet: %d\n", partpktsize);
+  // Find total number of packets to be transfered.
+  packets = fullpkts + ((partpktsize > 0) ? 1 : 0);
+  //fprintf(stderr, "Total number of packets: %d\n", packets);
+  // Limit the number of packets transfered according to
+  // the Linux usbdevfs maximum read/write buffer size.
+  if ((packets < 1) || (packets > 128)) {
+    USB_ERROR_STR("error on packet size: %s", strerror(EINVAL));
+    return -EINVAL;
+  }
+
+  // If necessary, allocate the urb and packet
+  // descriptor structures from the heap.
+  local_urb = *iso_urb;
+  if (!local_urb) {
+    urb_size = sizeof(struct usbdevfs_urb) +
+      packets * sizeof(struct usb_iso_packet_desc);
+    local_urb = (struct usbdevfs_urb *) calloc(1, urb_size);
+    if (!local_urb) {
+      USB_ERROR_STR("error on packet size: %s", strerror(EINVAL));
+      return -ENOMEM;
+    }
+  }
+
+  // Set up each packet for the data to be transferred.
+  for (pktcount = 0; pktcount < fullpkts; pktcount++) {
+    local_urb->iso_frame_desc[pktcount].length = pktsize;
+    local_urb->iso_frame_desc[pktcount].actual_length = 0;
+    local_urb->iso_frame_desc[pktcount].status = 0;
+  }
+
+  // Set up the last packet for the partial data to be transferred.
+  if (partpktsize > 0) {
+    local_urb->iso_frame_desc[pktcount].length = partpktsize;
+    local_urb->iso_frame_desc[pktcount].actual_length = 0;
+    local_urb->iso_frame_desc[pktcount++].status = 0;
+  }
+
+  // Set up the URB structure.
+  local_urb->type = USBDEVFS_URB_TYPE_ISO;
+  //fprintf(stderr, "type: %d\n", local_urb->type);
+  local_urb->endpoint = ep;
+  //fprintf(stderr, "endpoint: 0x%x\n", local_urb->endpoint);
+  local_urb->status = 0;
+  local_urb->flags = USBDEVFS_URB_ISO_ASAP; // Additional flags here?
+  //fprintf(stderr, "flags: %d\n", local_urb->flags);
+  local_urb->buffer = bytes;
+  //fprintf(stderr, "buffer: 0x%x\n", local_urb->buffer);
+  local_urb->buffer_length = size;
+  //fprintf(stderr, "buffer_length: %d\n", local_urb->buffer_length);
+  local_urb->actual_length = 0;
+  local_urb->start_frame = 0;
+  //fprintf(stderr, "start_frame: %d\n", local_urb->start_frame);
+  local_urb->number_of_packets = pktcount;
+  //fprintf(stderr, "number_of_packets: %d\n", local_urb->number_of_packets);
+  local_urb->error_count = 0;
+  local_urb->signr = 0;
+  //fprintf(stderr, "signr: %d\n", local_urb->signr);
+  local_urb->usercontext = (void *) 0;
+  *iso_urb = local_urb;
+  return 0;
+}
+
+
+int usb_urb_submit(usb_dev_handle *dev,     // Open usb device handle.
+                   struct usbdevfs_urb *iso_urb,        // Pointer to URB.
+                   struct timeval *tv_submit) { // Time structure pointer.
+  int ret;
+
+  iso_urb->usercontext = dev;
+  // Get actual time, of the URB submission.
+  if(tv_submit)
+    gettimeofday(tv_submit, NULL);
+  // Submit the URB through an IOCTL call.
+  ret = ioctl(dev->fd, USBDEVFS_SUBMITURB, iso_urb);
+  //fprintf(stderr, "start_frame now: %d\n", iso_urb->start_frame);
+  //fprintf(stderr, "submit ioctl return value: %d\n", ret);
+  if (ret < 0) {
+    //fprintf(stderr, "error submitting URB: %s\n", strerror(errno));
+    USB_ERROR_STR("error submitting URB: %s", strerror(errno));
+    return -errno;
+  }
+  return ret;
+}
+
+
+int usb_urb_reap(usb_dev_handle *dev,     // Open usb device handle.
+                 struct usbdevfs_urb *iso_urb,        // Pointer to URB.
+                 int timeout) {           // Attempt timeout (usec).
+  void *context = NULL;
+  int ret;
+  struct pollfd ufd[1];
+
+  // Get actual time, and add the timeout value. The result is the absolute
+  // time where we have to quit waiting for an isochronous message.
+  ufd[0].fd = dev->fd;
+  ufd[0].events = POLLIN | POLLOUT;
+  ufd[0].revents = 0;
+  ret = poll(ufd, 1, timeout);
+  if(ret <= 0)
+    return -ETIMEDOUT;
+
+  //fprintf(stderr, "preparing to reap\n");
+  ret = ioctl(dev->fd, USBDEVFS_REAPURB, &context);
+
+  /*
+   * If there was an error, that wasn"t EAGAIN (no completion), then
+   * something happened during the reaping and we should return that
+   * error now
+   */
+  //fprintf(stderr, "reap ioctl return value: %d\n", ret);
+  if (ret < 0) {
+    USB_ERROR_STR("error reaping interrupt URB: %s",
+                  strerror(errno));
+    return -errno;
+  }
+
+  //fprintf(stderr, "actual_length: %d\n", iso_urb->actual_length);
+  //fprintf(stderr, "URB status: %d\n", iso_urb->status);
+  //fprintf(stderr, "error count: %d\n", iso_urb->error_count);
+
+  //fprintf(stderr, "waiting done\n");
+  if(context != NULL && iso_urb != context) {
+    fprintf(stderr, "Expected urb: %p but got %p\n", iso_urb, context);
+    return -1;
+  }
+  //fprintf(stderr, "Total bytes: %d\n", bytesdone);
+  return iso_urb->actual_length;
+}
+
+int usb_urb_cancel(usb_dev_handle *dev, struct usbdevfs_urb *iso_urb)
+{
+  return ioctl(dev->fd, USBDEVFS_DISCARDURB, iso_urb);
+}
Index: libs/libmythtv/r5000/libusb_augment.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libs/libmythtv/r5000/libusb_augment.h	2008-11-26 06:56:44.000000000 -0800
@@ -0,0 +1,36 @@
+/* Copyright 2008 Alan Nisota <alannisota@gmail.com>
+ * 2005-10-19/lindi: downloaded from http://www.gaesi.org/~nmct/cvista/cvista/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef LIBUSB_AUGMENT
+// Taken from libusb file linux.h to provide the URB structure definitions.
+struct usb_iso_packet_desc {
+  unsigned int length;
+  unsigned int actual_length;
+  unsigned int status;
+};
+#endif
+
+int usbdevfs_urb_signal_completion(void (*cb)( struct usbdevfs_urb *));
+struct usbdevfs_urb *usb_bulk_setup(struct usbdevfs_urb *iso_urb, unsigned char ep,
+                   char *bytes, int size);
+int usb_isochronous_setup(struct usbdevfs_urb **iso_urb, unsigned char ep,
+                          int pktsize, char *bytes, int size);
+int usb_urb_submit(usb_dev_handle *dev, struct usbdevfs_urb *iso_urb,
+                   struct timeval *tv_rsubmit);
+int usb_urb_reap(usb_dev_handle *dev, struct usbdevfs_urb *iso_urb,
+                 int timeout_usec);
+int usb_urb_cancel(usb_dev_handle *dev, struct usbdevfs_urb *iso_urb);
Index: libs/libmythtv/r5000/r5000.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libs/libmythtv/r5000/r5000.c	2008-11-27 08:39:20.000000000 -0800
@@ -0,0 +1,521 @@
+/* Copyright 2008 Alan Nisota <alannisota@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+
+#ifdef R5K_DEBUG
+char strmfile[256] = "/tmp/strm";
+int fd = -1;
+#endif
+
+#ifdef R5K_RAWUSB
+int usbfd = -1;
+#endif
+#include "r5000_internal.h"
+#include "libusb_augment.h"
+#include "r5000init.h"
+
+#define R5K_WARM_VID 0x0547
+#define R5K_WARM_PID 0x1002
+
+#define MAX_URBS_IN_FLIGHT 128
+#define R5K_URB_BUFFER_SIZE (1 << 14)
+
+static int r5000_usb_init = 0;
+static struct {
+  unsigned char serial[R5K_MAX_DEVS][8];
+  struct usb_device *dev[R5K_MAX_DEVS];
+  int count;
+} r5000_dev_map;
+
+static void (*msgcb)(char *msg);
+
+enum {
+  R5K_PMT_START = 0x01,
+  R5K_PMT_READY = 0x02
+};
+
+void r5000_print(const char *fmt, ...)
+{
+  va_list args;
+  char logmsg[1024];
+  va_start(args,fmt);
+  vsnprintf(logmsg,sizeof(logmsg),fmt,args);
+  va_end(args);
+  if(msgcb) {
+    //msgcb routine must ensure thread safeness
+    msgcb(logmsg);
+  } else {
+    fprintf(stderr, logmsg);
+  }
+}
+
+int r5000_create_urbs(r5kdev_t *r5kdev)
+{
+  int i;
+  r5kdev->urbs = (struct usbdevfs_urb *)malloc(sizeof(struct usbdevfs_urb) * MAX_URBS_IN_FLIGHT);
+  r5kdev->buffer = malloc(R5K_URB_BUFFER_SIZE * MAX_URBS_IN_FLIGHT);
+  for(i = 0; i < MAX_URBS_IN_FLIGHT; i++) {
+    usb_bulk_setup(&r5kdev->urbs[i], 0x82, r5kdev->buffer + (R5K_URB_BUFFER_SIZE*i), R5K_URB_BUFFER_SIZE);
+  }
+  r5kdev->nexturb = 0;
+  return 1;
+}
+
+int r5000_free_urbs(r5kdev_t *r5kdev)
+{
+  free(r5kdev->urbs);
+  free(r5kdev->buffer);
+  return 0;
+}
+
+usb_dev_handle *r5000_locate_device(
+    unsigned short vendor_id, unsigned short product_id, int skip)
+{
+  struct usb_bus *bus;
+  struct usb_device *dev;
+  usb_dev_handle *device_handle = 0;
+  usb_find_busses();
+  usb_find_devices();
+
+  for (bus = usb_get_busses(); bus && !device_handle; bus = bus->next)
+  {
+    for (dev = bus->devices; dev && !device_handle; dev = dev->next)
+    {
+      if (dev->descriptor.idVendor == vendor_id &&
+          dev->descriptor.idProduct == product_id)
+      {
+        device_handle = usb_open(dev);
+        if(device_handle && skip) {
+          usb_close(device_handle);
+          device_handle = NULL;
+          skip--;
+        }
+      }
+    }
+  }
+
+  if (device_handle) {
+    int open_status = usb_set_configuration(device_handle,1);
+
+    open_status = usb_claim_interface(device_handle,0);
+
+    open_status = usb_set_altinterface(device_handle,0);
+  }
+  return (device_handle);
+}
+
+int r5000_dev_init(r5kdev_t *r5kdev) {
+  int i, bytes;
+  unsigned char *ptr, *serial = r5kdev->serial;
+  unsigned char datain[0x80];
+  bytes = usb_bulk_read(r5kdev->handle, 129, datain, sizeof(datain), 5000);
+  for(i = 0; i < R5K_INIT_SERIAL; i++) {
+    //PRINTHEX("Write:\n", r5kinit[i].data, r5kinit[i].wlen);
+    if(r5kinit[i].wsleep) usleep(r5kinit[i].wsleep);
+    usb_bulk_write(r5kdev->handle, 1, r5kinit[i].data, r5kinit[i].wlen, 5000);
+    if(r5kinit[i].rsleep) usleep(r5kinit[i].rsleep);
+    bytes = usb_bulk_read(r5kdev->handle, 129, datain, sizeof(datain), 5000);
+    //PRINTHEX("Read:\n", datain, bytes);
+    if(r5kinit[i].rlen > 0 && bytes != r5kinit[i].rlen) {
+      r5000_print("R5000 initialization failed at stage %d:\n\tExpected %d bytes, but got %d bytes\n", i, r5kinit[i].rlen, bytes);
+      return 0;
+    }
+  }
+
+  //last read is serial #
+  if (datain[0] != 0x08) {
+    r5000_print("R5000 initialization failed reading serial #\n");
+    return 0;
+  }
+  for(ptr = datain + 6; ptr < datain + 13; ptr++) {
+    *serial++ = ( *ptr >= '0' && *ptr <= 'z' ) ? *ptr : '*';
+  }
+  *serial = 0;
+
+  //complete initialization now
+  for(; i < R5K_INIT_MAX; i++) {
+    //PRINTHEX("Write:\n", r5kinit[i].data, r5kinit[i].wlen);
+    if(r5kinit[i].wsleep) usleep(r5kinit[i].wsleep);
+    usb_bulk_write(r5kdev->handle, 1, r5kinit[i].data, r5kinit[i].wlen, 5000);
+    if(r5kinit[i].rsleep) usleep(r5kinit[i].rsleep);
+    bytes = usb_bulk_read(r5kdev->handle, 129, datain, sizeof(datain), 5000);
+    //PRINTHEX("Read:\n", datain, bytes);
+    if(r5kinit[i].rlen > 0 && bytes != r5kinit[i].rlen) {
+      r5000_print("R5000 initialization failed at stage %d:\n\tExpected %d bytes, but got %d bytes\n", i, r5kinit[i].rlen, bytes);
+      return 0;
+    }
+  }
+  return 1;
+}
+
+int r5000_start_stream(r5kdev_t *r5kdev)
+{
+  unsigned char data[0x80];
+  int bytes;
+  int i;
+  struct usb_dev_handle *handle = (struct usb_dev_handle *)r5kdev->handle;
+
+  if(! r5kdev->urbs)
+    r5000_create_urbs(r5kdev);
+
+  data[0] = 0x30;
+  usb_bulk_write(handle, 1, data, 1, 5000);
+  bytes = usb_bulk_read(handle, 129, data, 2, 5000);
+
+  //0x50 sets byte mode.  Use '0x60' to set word mode
+  data[0] = r5kdev->read_words ? 0x60: 0x50;
+  usb_bulk_write(handle, 1, data, 1, 5000);
+
+  for(i=0; i < MAX_URBS_IN_FLIGHT; i++) {
+    usb_urb_submit(handle, &r5kdev->urbs[i], NULL);
+  }
+  r5kdev->nexturb = 0;
+  r5kdev->streaming = 1;
+  if(r5kdev->start_stream)
+    r5kdev->start_stream(r5kdev);
+  return 1;
+}
+
+int r5000_stop_stream(r5kdev_t *r5kdev)
+{
+  struct usb_dev_handle *handle = (struct usb_dev_handle *)r5kdev->handle;
+  struct usbdevfs_urb *urbs = (struct usbdevfs_urb *)r5kdev->urbs;
+  int i;
+
+  if(r5kdev->streaming) {
+    for(i=0; i < MAX_URBS_IN_FLIGHT; i++)
+      usb_urb_cancel(handle, &urbs[i]);
+    r5kdev->streaming = 0;
+  }
+  return 1;
+}
+
+/* r5000_init must be called from a thread-safe context */
+int r5000_init(void (*_msgcb)(char *str))
+{
+  int bus_count, dev_count;
+  r5kdev_t r5kd;
+  int count = 0;
+
+  msgcb = _msgcb;
+
+  if(r5000_usb_init)
+    return 1;
+  usb_init();
+  bus_count = usb_find_busses();
+  dev_count = usb_find_devices();
+  if(bus_count == 0 || dev_count ==0) {
+    r5000_print("R5000 failed to locate any USB devices.  Are you sure you have permissions set properly?\n");
+    return 0;
+  }
+  while(r5000_dev_map.count < R5K_MAX_DEVS) {
+    r5kd.handle = r5000_locate_device(R5K_WARM_VID, R5K_WARM_PID, count);
+    if(! r5kd.handle)
+      break;
+    if (r5000_dev_init(&r5kd)) {
+      memcpy(r5000_dev_map.serial[r5000_dev_map.count], r5kd.serial, 8);
+      r5000_dev_map.dev[r5000_dev_map.count] = usb_device(r5kd.handle);
+      r5000_dev_map.count++;
+    }
+    count++;
+    usb_close(r5kd.handle);
+  }
+  if(! r5000_dev_map.count) {
+    r5000_print("R5000 failed to locate any R5000 devices.  Are you sure you have permissions set properly?\n");
+    return 0;
+  }
+  r5000_usb_init = 1;
+  return 1;
+}
+
+r5kdev_t *r5000_open(r5ktype_t type,
+                     unsigned int (*cb)(unsigned char *buffer, int len, void *callback_data),
+                     void *cb_data,
+                     const char *serial)
+{
+  r5kdev_t *r5kdev, r5kd;
+  int count = 0;
+  memset(&r5kd, 0, sizeof(r5kdev_t));
+
+  if(! r5000_usb_init) {
+    r5000_print("R5000 was not initialized before r5000_open().  Please call r5000_init() first\n");
+    return NULL;
+  }
+  for(count = 0; count < r5000_dev_map.count; count++) {
+    if(! serial || memcmp(r5000_dev_map.serial[count], serial, 8) == 0) {
+      r5kd.handle = usb_open(r5000_dev_map.dev[count]);
+      if(! r5kd.handle)
+        return NULL;
+      usb_set_configuration(r5kd.handle,1);
+      usb_claim_interface(r5kd.handle,0);
+      usb_set_altinterface(r5kd.handle,0);
+      if(! r5000_dev_init(&r5kd)) {
+        usb_close(r5kd.handle);
+        return NULL;
+      }
+      break;
+    }
+  }
+  if(count == r5000_dev_map.count) {
+    //We can't get here unless a serial was specified
+    r5000_print("Could not locate R5000 device with serial '%s'\n", serial);
+    return NULL;
+  }
+#ifdef R5K_DEBUG
+  fd = open(strmfile, O_RDONLY);
+#endif
+#ifdef R5K_RAWUSB
+  usbfd = open("raw.av", O_WRONLY | O_CREAT | O_TRUNC, 0666);
+#endif
+  r5kdev = (r5kdev_t *)malloc(sizeof(r5kdev_t));
+  *r5kdev = r5kd;
+  r5kdev->urbs = NULL;
+  r5kdev->cb = cb;
+  r5kdev->cb_data = cb_data;
+  r5kdev->stb_type = type;
+  switch(type) {
+    case R5K_STB_VIP211:
+    case R5K_STB_VIP622:
+      vip_init(r5kdev);
+      break;
+    case R5K_STB_DIRECTV:
+      directv_init(r5kdev);
+      break;
+    case R5K_STB_DSR:
+    case R5K_STB_HDD:
+      sat_init(r5kdev);
+      break;
+    default:
+      r5000_print("Unknown STB type %d specified.\n", type);
+      r5kdev->stb_type = R5K_STB_VIP211;
+      vip_init(r5kdev);
+      break;
+  }
+  return r5kdev;
+}
+
+int r5000_close(r5kdev_t *r5kdev)
+{
+  if(! r5kdev)
+    return 1;
+  if(r5kdev->urbs) {
+    if(r5kdev->streaming)
+      r5000_stop_stream(r5kdev);
+    r5000_free_urbs(r5kdev);
+  }
+  usb_close(r5kdev->handle);
+#ifdef R5K_RAWUSB
+  if(usbfd >= 0) close(usbfd);
+#endif
+  switch(r5kdev->stb_type) {
+    case R5K_STB_VIP211:
+    case R5K_STB_VIP622:
+      vip_free(r5kdev);
+      break;
+    case R5K_STB_DIRECTV:
+      directv_free(r5kdev);
+      break;
+    case R5K_STB_DSR:
+    case R5K_STB_HDD:
+      sat_free(r5kdev);
+      break;
+  }
+  free(r5kdev);
+  return 1;
+}
+
+int r5000_loop_iterate(r5kdev_t *r5kdev, int timeout_usec)
+{
+  struct usb_dev_handle *handle = (struct usb_dev_handle *)r5kdev->handle;
+  struct usbdevfs_urb *urbs = (struct usbdevfs_urb *)r5kdev->urbs;
+  int len;
+  unsigned char *buf;
+  if(! r5kdev->streaming)
+    return -1;
+  len = usb_urb_reap(handle, &urbs[r5kdev->nexturb], timeout_usec);
+  if(len <= 0) {
+    if(len != -ETIMEDOUT)
+      r5000_print("(%d) Reap failed at %08x: %s\n", r5kdev->nexturb, r5kdev->bytes_read, strerror(errno));
+    return len;
+  }
+  buf = r5kdev->buffer + (R5K_URB_BUFFER_SIZE*r5kdev->nexturb);
+#ifdef R5K_RAWUSB
+  if(usbfd >= 0) write(usbfd, buf, len);
+#endif
+#ifdef R5K_DEBUG
+  if(fd >= 0)
+    len = read(fd, buf, len);
+  if(len == 0)
+    r5000_print("hit end of debug file\n");
+#endif
+  r5kdev->process_block(r5kdev, buf, len);
+  r5kdev->bytes_read += len;
+  usb_urb_submit(handle, &urbs[r5kdev->nexturb], NULL);
+  r5kdev->nexturb = (r5kdev->nexturb + 1) % MAX_URBS_IN_FLIGHT;
+  return 0;
+}
+
+//use this to read a status frame.  It doesn't do anything special
+//but makes it obvious what data is expected
+int r5000_read_status(r5kdev_t *r5kdev, unsigned char *buf)
+{
+  return usb_bulk_read(r5kdev->handle, 129, buf, 128, 5000);
+}
+
+int r5000_get_power_state(r5kdev_t *r5kdev)
+{
+  unsigned char data1[1]  = { 0x30 };
+  unsigned char data2[0x80];
+  int count = 10;
+  while(count--) {
+    r5000_read_status(r5kdev, data2);
+    usb_bulk_write(r5kdev->handle, 1, data1, 1, 5000);
+    usleep(100000);
+    if(usb_bulk_read(r5kdev->handle, 1, data2, 2, 5000) == 2 &&
+       data2[0] == 0x0a && (data2[1] & 0x4e) == 0x4c) {
+      // The following boxes are known to be power active low:
+      // 4DTV 922, Dish 622/722, Bell 9242, D*
+      if(r5kdev->power_active_low)
+        return (!(data2[1] == 0x4d));
+      else
+        return (!!(data2[1] == 0x4d));
+    }
+    usleep(100000);
+  }
+  r5000_print("R5000 failed to read power state.  Assuming ON state\n");
+  return 1;
+}
+
+int r5000_send_pwr_cmd(r5kdev_t *r5kdev, unsigned char *data)
+{
+  unsigned char data1[1]  = { 0x30 };
+  unsigned char data3[0x80];
+  int len;
+  usb_bulk_write(r5kdev->handle, 1, data1, 1, 5000);
+  usleep(100000);
+  usb_bulk_write(r5kdev->handle, 1, data, r5kdev->button->len, 5000);
+  usleep(100000);
+  len = usb_bulk_read(r5kdev->handle, 1, data3, 2, 5000);
+  usleep(100000);
+  return len;
+}
+
+int r5000_wait_pwr(r5kdev_t *r5kdev, int on_off)
+{
+  int new_state, count = 20;
+  while(count-- && (new_state = r5000_get_power_state(r5kdev)) != on_off)
+    usleep(100000);
+  //r5000_print("End state: %s\n", !on_off ? "On" : "Off");
+  return new_state;
+}
+
+int r5000_toggle_on_off(r5kdev_t *r5kdev)
+{
+  unsigned on_off;
+  on_off = r5000_get_power_state(r5kdev);
+  if(! r5kdev->button) {
+    r5000_print("No button IR commands defined for this device!\n");
+    return on_off;
+  }
+  //r5000_print("Start state: %s\n", on_off ? "On" : "Off");
+  r5000_send_pwr_cmd(r5kdev, r5kdev->button->power);
+  on_off = r5000_wait_pwr(r5kdev, ! on_off);
+  //r5000_print("End state: %s\n", on_off ? "On" : "Off");
+  return on_off;
+}
+
+int r5000_power_on_off(r5kdev_t *r5kdev, int turn_on)
+{
+  unsigned char *pwr_command;
+  int on_off = r5000_get_power_state(r5kdev);
+  if(on_off == turn_on) {
+    return on_off;
+  }
+  if(! r5kdev->button) {
+    r5000_print("No button IR commands defined for this device!\n");
+    return on_off;
+  }
+  if(r5kdev->discrete_power) {
+    //r5000_print("Using discrete power commands\n");
+    if(turn_on) {
+      pwr_command = r5kdev->button->power_on;
+    } else {
+      pwr_command = r5kdev->button->power_off;
+    }
+  } else {
+    pwr_command = r5kdev->button->power;
+  }
+  r5000_send_pwr_cmd(r5kdev, pwr_command);
+  return r5000_wait_pwr(r5kdev, turn_on);
+}
+
+int r5000_change_channel(r5kdev_t *r5kdev, const char *chan, int mpeg_prog)
+{
+  unsigned char data2[0x80];
+  unsigned char *ptr;
+  const char *p;
+  if(! r5kdev)
+    return 0;
+  if (chan) {
+    if(! r5kdev->button) {
+      r5000_print("No button IR commands defined for this device!\n");
+    } else {
+      r5000_read_status(r5kdev, data2);
+      usb_bulk_write(r5kdev->handle, 1, r5kdev->button->clear, r5kdev->button->len, 5000);
+      usleep(r5kdev->button->delay);
+      for(p = chan; *p; p++) {
+        switch(*p) {
+          case '0' : ptr = r5kdev->button->b0; break;
+          case '1' : ptr = r5kdev->button->b1; break;
+          case '2' : ptr = r5kdev->button->b2; break;
+          case '3' : ptr = r5kdev->button->b3; break;
+          case '4' : ptr = r5kdev->button->b4; break;
+          case '5' : ptr = r5kdev->button->b5; break;
+          case '6' : ptr = r5kdev->button->b6; break;
+          case '7' : ptr = r5kdev->button->b7; break;
+          case '8' : ptr = r5kdev->button->b8; break;
+          case '9' : ptr = r5kdev->button->b9; break;
+        }
+        usb_bulk_write(r5kdev->handle, 1, ptr, r5kdev->button->len, 5000);
+        usleep(r5kdev->button->delay);
+      }
+      usb_bulk_write(r5kdev->handle, 1, r5kdev->button->enter, r5kdev->button->len, 5000);
+    }
+  }
+  r5kdev->channel = mpeg_prog;
+  if(r5kdev->change_channel)
+    r5kdev->change_channel(r5kdev);
+  return 1;
+}
+
+int r5000_find_stbs(r5kenum_t *devs)
+{
+  devs->count = 0;
+  if(! r5000_usb_init) {
+    r5000_print("R5000 was not initialized before r5000_find_stbs().  Please call r5000_init() first\n");
+    return 0;
+  }
+  devs->count = r5000_dev_map.count;
+  memcpy(devs->serial, r5000_dev_map.serial, 8 * r5000_dev_map.count);
+  return 1;
+}
Index: libs/libmythtv/r5000/r5000.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libs/libmythtv/r5000/r5000.h	2008-11-27 08:36:41.000000000 -0800
@@ -0,0 +1,50 @@
+/* Copyright 2008 Alan Nisota <alannisota@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef R5000_H
+#define R5000_H
+
+#ifndef r5kdev_t
+#define r5kdev_t void
+#endif
+
+#define R5K_MAX_DEVS 10
+typedef struct {
+  unsigned char serial[R5K_MAX_DEVS][8];
+  int count;
+} r5kenum_t;
+
+typedef enum {
+  R5K_STB_VIP211 = 0,
+  R5K_STB_DIRECTV,
+  R5K_STB_HDD,
+  R5K_STB_DSR,
+  R5K_STB_VIP622,
+  R5K_STB_MAX,
+} r5ktype_t;
+
+extern int r5000_init(void (*_msgcb)(char *str));
+extern r5kdev_t *r5000_open(r5ktype_t type, unsigned int (*cb)(unsigned char *buffer, int len, void *callback_data), void *cb_data, const char *serial);
+extern int r5000_close(r5kdev_t *r5kdev);
+extern int r5000_start_stream(r5kdev_t *r5kdev);
+extern int r5000_stop_stream(r5kdev_t *r5kdev);
+extern int r5000_loop_iterate(r5kdev_t *r5kdev, int timeout_usec);
+extern int r5000_get_power_state(r5kdev_t *r5kdev);
+extern int r5000_toggle_on_off(r5kdev_t *r5kdev);
+extern int r5000_power_on_off(r5kdev_t *r5kdev, int turn_on);
+extern int r5000_change_channel(r5kdev_t *r5kdev, const char *chan, int mpeg_prog);
+extern int r5000_find_stbs(r5kenum_t *devs);
+#endif //R5000_H
Index: libs/libmythtv/r5000/r5000_internal.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libs/libmythtv/r5000/r5000_internal.h	2008-11-27 08:12:36.000000000 -0800
@@ -0,0 +1,117 @@
+/* Copyright 2008 Alan Nisota <alannisota@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef R5000_INT_H
+#define R5000_INT_H
+
+#include <usb.h>
+#include <linux/usbdevice_fs.h>
+
+#define PRINTHEX(str, data, len) if(1) do { \
+  int _i; \
+  fprintf(stderr, str); \
+  for(_i = 0; _i < (len); _i++) { \
+    fprintf(stderr, "%02x ", (data)[_i]); \
+    if((_i % 16) == 15) fprintf(stderr, "\n"); \
+  } \
+  if(_i % 16) fprintf(stderr, "\n"); \
+} while(0)
+
+#define R5K_MAX_BUTTON_SIZE 0x80
+#define R5K_MAX_PIDS 10
+#define IS_VIDEO(x) ((x) == 0x02 || (x) == 0x1b || (x) == 0x80)
+
+struct r5k_descriptor {
+  unsigned char d[10];
+};
+
+struct r5k_epid {
+  int pid;
+  unsigned char id;
+  struct r5k_descriptor desc;
+};
+
+struct r5000_buttons {
+  int len;
+  int delay;
+  unsigned char b0[R5K_MAX_BUTTON_SIZE];
+  unsigned char b1[R5K_MAX_BUTTON_SIZE];
+  unsigned char b2[R5K_MAX_BUTTON_SIZE];
+  unsigned char b3[R5K_MAX_BUTTON_SIZE];
+  unsigned char b4[R5K_MAX_BUTTON_SIZE];
+  unsigned char b5[R5K_MAX_BUTTON_SIZE];
+  unsigned char b6[R5K_MAX_BUTTON_SIZE];
+  unsigned char b7[R5K_MAX_BUTTON_SIZE];
+  unsigned char b8[R5K_MAX_BUTTON_SIZE];
+  unsigned char b9[R5K_MAX_BUTTON_SIZE];
+  unsigned char clear[R5K_MAX_BUTTON_SIZE];
+  unsigned char enter[R5K_MAX_BUTTON_SIZE];
+  unsigned char power[R5K_MAX_BUTTON_SIZE];
+  unsigned char power_on[R5K_MAX_BUTTON_SIZE];
+  unsigned char power_off[R5K_MAX_BUTTON_SIZE];
+};
+
+struct r5kdev {
+  usb_dev_handle *handle;
+  unsigned char serial[8];
+  struct usbdevfs_urb *urbs;
+  unsigned char *buffer;
+  int stb_type;
+  int read_words;
+  void (*process_block)(struct r5kdev *r5kdev, unsigned char *buf, int len);
+  void (*start_stream)(struct r5kdev *r5kdev);
+  void (*change_channel)(struct r5kdev *r5kdev);
+  void *stbdata;
+  unsigned int (*cb)(unsigned char *buffer, int len, void *callback_data);
+  void *cb_data;
+  int nexturb;
+  int streaming;
+  int bytes_read;
+  struct r5000_buttons *button;
+  int power_active_low;
+  int discrete_power;
+  int channel;
+  unsigned char pmt_pkt[188];
+  unsigned char num_pmt_entries;
+  unsigned char pmt_state;
+  unsigned char pmt_next_cc;
+  unsigned char pmt_version;
+  struct r5k_epid pmt[R5K_MAX_PIDS];
+} r5kdev_t;
+#define r5kdev_t struct r5kdev
+
+#include "r5000.h"
+
+extern void r5000_print(const char *fmt, ...);
+extern unsigned char r5000_pat_pkt[188];
+extern struct r5k_descriptor r5000_pmt_audio_desc;
+extern struct r5k_descriptor r5000_pmt_video_desc;
+extern void r5000_reset_pmt(r5kdev_t *r5kdev);
+extern void r5000_send_pmt(r5kdev_t *r5kdev);
+
+//Support for all DirecTV boxes
+extern void directv_init(r5kdev_t *r5kdev);
+extern void directv_free(r5kdev_t *r5kdev);
+
+//Support for ViP series Dish Network boxes
+extern void vip_init(r5kdev_t *r5kdev);
+extern void vip_free(r5kdev_t *r5kdev);
+
+//Support for HDD and DSR series satellite/cable boxes
+extern void sat_init(r5kdev_t *r5kdev);
+extern void sat_free(r5kdev_t *r5kdev);
+
+#endif
Index: libs/libmythtv/r5000/r5k_vip.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libs/libmythtv/r5000/r5k_vip.c	2008-11-26 17:03:56.000000000 -0800
@@ -0,0 +1,184 @@
+/* Copyright 2008 Alan Nisota <alannisota@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+//Support for Dish Network ViP211/ViP422 boxes
+#include <stdio.h>
+#include <string.h>
+#include "r5000_internal.h"
+#include "r5k_vip_buttons.h"
+struct r5000_vip {
+  unsigned char leftover[188];
+  int offset;
+};
+
+void vip_add_pmt(r5kdev_t *r5kdev, int pid, int table_id)
+{
+  int i;
+  int is_video = IS_VIDEO(table_id);
+
+  for(i = 0; i < r5kdev->num_pmt_entries; i++) {
+    if(r5kdev->pmt[i].pid == pid) {
+      if(r5kdev->pmt[i].id == table_id)
+        return;
+      //different table_id for existing pid.  Reset
+      r5000_reset_pmt(r5kdev);
+      break;
+    }
+    if(is_video) {
+      if(IS_VIDEO(r5kdev->pmt[i].id)) {
+        //a video entry already exists.  Reset
+        r5000_reset_pmt(r5kdev);
+        break;
+      }
+    }
+  }
+  //Didn't find this PID, add it
+  if(i == R5K_MAX_PIDS)
+    return;
+  r5000_print("Adding %04x: %02x @ %08x\n", pid, table_id, r5kdev->bytes_read);
+  r5kdev->pmt[r5kdev->num_pmt_entries].pid = pid;
+  r5kdev->pmt[r5kdev->num_pmt_entries].id  = table_id;
+  r5kdev->pmt[r5kdev->num_pmt_entries].desc = is_video ? r5000_pmt_video_desc : r5000_pmt_audio_desc;
+  r5kdev->num_pmt_entries++;
+  r5kdev->pmt_state |= is_video ? 0x01 : 0x02;
+}
+
+void vip_force_pmt(r5kdev_t *r5kdev, unsigned char *buf)
+{
+  int stream_id, pid, afc, af_size = 0;
+  pid = ((buf[1] << 8) | buf[2]) & 0x1fff;
+  if(pid == 0x1fff)
+    return;
+  if(pid == 0) {
+    r5kdev->cb(r5000_pat_pkt, 188, r5kdev->cb_data);
+    if(r5kdev->pmt_state == 0x03)
+      r5000_send_pmt(r5kdev);
+  } else {
+    if(pid != 0x21)
+      r5kdev->cb(buf, 188, r5kdev->cb_data);
+    //Only interested in PES packets starting in this TS packet
+    if (!buf[1] & 0x40)
+      return;
+    afc = (buf[3]>>4) & 0x03;
+    if(afc == 0x2)
+      return;
+    if(afc == 0x3)
+      af_size = buf[4]+1;
+    if(buf[4+af_size] != 0x00 || buf[5+af_size] != 0x00 || buf[6+af_size] != 0x01)
+      return;
+    //We have a PES packet
+    stream_id = buf[7+af_size];
+    if((stream_id & 0xf0) == 0xe0) {
+      //Video stream (we need the adaptation field)
+      if(afc != 0x03)
+        return;
+      if(0) {
+        PRINTHEX("", buf, af_size+8);
+      }
+      if(buf[5] & 0x02) {
+        // Has private data, this is MPEG4
+        vip_add_pmt(r5kdev, pid, 0x1b);
+      } else if(buf[5] & 0x10) {
+        // Has PCR and no private data, this is MPEG2
+        vip_add_pmt(r5kdev, pid, 0x02);
+      }
+    } else if((stream_id & 0xf0) == 0xc0) {
+      //Audio stream
+      vip_add_pmt(r5kdev, pid, 0x04);
+    } else if(stream_id  == 0xbd) {
+      //Audio stream
+      vip_add_pmt(r5kdev, pid, 0x81);
+    }
+  }
+}
+
+static void vip_process_block(r5kdev_t *r5kdev, unsigned char *buf, int len)
+{
+  struct r5000_vip *vip = (struct r5000_vip *)r5kdev->stbdata;
+
+  int pos;
+  int sync = 1;
+  if(! r5kdev->streaming)
+    return;
+  if(len <= 0)
+    return;
+
+  pos = vip->offset;
+  while(pos < len) {
+      if(buf[pos] != 0x47 || (pos <len-1 && buf[pos+1] == 0xff))
+        goto nosync;
+      // If we get here, buf[pos] == 0x47
+      if(vip->offset) {
+        //previous data exists and is part of a good packet
+        memcpy(vip->leftover+188-vip->offset, buf, vip->offset);
+        vip_force_pmt(r5kdev, vip->leftover);
+        vip->offset = 0;
+      }
+      if(pos+188 < len) {
+        //at least one full packet is available
+        if(buf[pos+188] != 0x47)
+          goto nosync;
+      } else {
+        //Out of data, but the partial packet may be ok.
+        memcpy(vip->leftover, buf+pos, len-pos);
+        vip->offset = 188-(len-pos);
+        break;
+      }
+      //If we get here, we have a good packet
+      vip_force_pmt(r5kdev, buf+pos);
+      if(! sync)
+        r5000_print("(%d) Found sync at %08x\n", r5kdev->nexturb, r5kdev->bytes_read+pos);
+      sync = 1;
+      pos+=188;
+      continue;
+  nosync:
+      vip->offset=0;
+      if(sync)
+        r5000_print("(%d)Lost sync at %08x\n", r5kdev->nexturb, r5kdev->bytes_read+pos);
+      sync = 0;
+      pos++;
+  }
+}
+
+static void vip_start_stream(r5kdev_t *r5kdev)
+{
+  struct r5000_vip *vip = (struct r5000_vip *)r5kdev->stbdata;
+  vip->offset = 0;
+}
+
+static void vip_change_channel(r5kdev_t *r5kdev)
+{
+  r5000_reset_pmt(r5kdev);
+}
+
+void vip_init(r5kdev_t *r5kdev)
+{
+  struct r5000_vip *vip = (struct r5000_vip *)malloc(sizeof(struct r5000_vip));
+  r5kdev->stbdata = vip;
+  r5kdev->process_block = vip_process_block;
+  r5kdev->start_stream  = vip_start_stream;
+  r5kdev->button = &vip_button_cmd;
+  r5kdev->change_channel = &vip_change_channel;
+  r5kdev->discrete_power = 1;
+  if(r5kdev->stb_type == R5K_STB_VIP622)
+    r5kdev->power_active_low = 1;
+}
+
+void vip_free(r5kdev_t *r5kdev)
+{
+  free(r5kdev->stbdata);
+}
+
Index: libs/libmythtv/r5000/r5k_directv.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libs/libmythtv/r5000/r5k_directv.c	2008-11-26 06:56:44.000000000 -0800
@@ -0,0 +1,411 @@
+/* Copyright 2008 Alan Nisota <alannisota@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+//Support for DirecTV boxes
+#include <netinet/in.h>
+#include <string.h>
+
+#include "r5000_internal.h"
+
+#define DTV_VPID 0x1322
+#define DTV_APID 0x1333
+#define DTV_PAT_PMT_COUNT 5000
+
+struct r5000_dtv {
+  unsigned char vbuf[188*2+4];
+  unsigned char abuf[188*2+6];
+  unsigned char *video;
+  unsigned char *audio;
+  unsigned int vpos;
+  unsigned int apos;
+  unsigned int vstart;
+  unsigned int astart;
+  unsigned int video_cc;
+  unsigned int audio_cc;
+  unsigned int vstate;
+  unsigned int pic_pos;
+  int alen;
+  unsigned int pat_pmt_count;
+};
+
+struct r5000_buttons directv_button_cmd =
+{
+0x49, //len
+400000, //delay
+//button 0:
+{
+    0x00, 0x43, 0x0a, 0x80, 0xca, 0x00, 0x5a, 0x2d, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f,
+    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x05, 0x05, 0x0f,
+    0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x05,
+    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x05, 0x05, 0x0f,
+    0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05,
+},
+//button 1:
+{
+    0x00, 0x43, 0x0a, 0x80, 0xca, 0x00, 0x5a, 0x2d, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f,
+    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x05, 0x05, 0x0f,
+    0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x05,
+    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x05, 0x05, 0x0f,
+    0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05,
+},
+//button 2:
+{
+    0x00, 0x43, 0x0a, 0x80, 0xca, 0x00, 0x5a, 0x2d, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f,
+    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x05, 0x05, 0x0f,
+    0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x05,
+    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f,
+    0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05,
+},
+//button 3:
+{
+    0x00, 0x43, 0x0a, 0x80, 0xca, 0x00, 0x5a, 0x2d, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f,
+    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x05, 0x05, 0x0f,
+    0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x05,
+    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f,
+    0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05,
+},
+//button 4:
+{
+    0x00, 0x43, 0x0a, 0x80, 0xca, 0x00, 0x5a, 0x2d, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f,
+    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x05, 0x05, 0x0f,
+    0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f,
+    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x05,
+    0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05,
+},
+//button 5:
+{
+    0x00, 0x43, 0x0a, 0x80, 0xca, 0x00, 0x5a, 0x2d, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f,
+    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x05, 0x05, 0x0f,
+    0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f,
+    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x05,
+    0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05,
+},
+//button 6:
+{
+    0x00, 0x43, 0x0a, 0x80, 0xca, 0x00, 0x5a, 0x2d, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f,
+    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x05, 0x05, 0x0f,
+    0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x05, 0x05, 0x0f,
+    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x05,
+    0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05,
+},
+//button 7:
+{
+    0x00, 0x43, 0x0a, 0x80, 0xca, 0x00, 0x5a, 0x2d, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f,
+    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x05, 0x05, 0x0f,
+    0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x05, 0x05, 0x0f,
+    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x05,
+    0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05,
+},
+//button 8:
+{
+    0x00, 0x43, 0x0a, 0x80, 0xca, 0x00, 0x5a, 0x2d, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f,
+    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x05, 0x05, 0x0f,
+    0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f,
+    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x05, 0x05, 0x05,
+    0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05,
+},
+//button 9:
+{
+    0x00, 0x43, 0x0a, 0x80, 0xca, 0x00, 0x5a, 0x2d, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f,
+    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x05, 0x05, 0x0f,
+    0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f,
+    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x05, 0x05, 0x05,
+    0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05,
+},
+//Clear:
+{
+    0x00, 0x43, 0x0a, 0x80, 0xca, 0x00, 0x5a, 0x2d, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f,
+    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x05, 0x05, 0x0f,
+    0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f,
+    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+    0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05,
+},
+//Enter:
+{
+    0x00, 0x43, 0x0a, 0x80, 0xca, 0x00, 0x5a, 0x2d, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f,
+    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x05, 0x05, 0x0f,
+    0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f,
+    0x05, 0x0f, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x05,
+    0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05,
+},
+//Power
+{
+    0x00, 0x43, 0x0a, 0x80, 0xca, 0x00, 0x5a, 0x2d, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f,
+    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x05, 0x05, 0x0f,
+    0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x05,
+    0x05, 0x0f, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x0f, 0x05, 0x05, 0x05, 0x0f,
+    0x05, 0x05, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x0f, 0x05,
+}
+};
+
+static int directv_make_empty_pes(unsigned char *ptr, unsigned char type)
+{
+  ptr[0] = 0x00;
+  ptr[1] = 0x00;
+  ptr[2] = 0x01;
+  ptr[3] = type;
+  ptr[4] = 0x00;
+  ptr[5] = 0x00;
+  ptr[6] = 0x80;
+  ptr[7] = 0x00; //2 MSB represent PTS/DTS flag
+  ptr[8] = 0x0a;
+  ptr[9]  = 0xff;
+  ptr[10] = 0xff;
+  ptr[11] = 0xff;
+  ptr[12] = 0xff;
+  ptr[13] = 0xff;
+  ptr[14] = 0xff;
+  ptr[15] = 0xff;
+  ptr[16] = 0xff;
+  ptr[17] = 0xff;
+  ptr[18] = 0xff;
+  return 19;
+}
+
+static void directv_make_pespts(unsigned char *outptr, unsigned int pts)
+{
+        pts = htonl(pts);
+        unsigned char *inpts = (unsigned char *)&pts;
+        outptr[0] = 0x01 |
+                    ((inpts[0] & 0xC0) >>5);
+        outptr[1] = ((inpts[0] & 0x3F) << 2) |
+                    ((inpts[1] & 0xC0) >> 6);
+        outptr[2] = 0x01 | ((inpts[1] & 0x3F) << 2) |
+                    ((inpts[2] & 0x80) >> 6);
+        outptr[3] = ((inpts[2] & 0x7F) << 1) |
+                    ((inpts[3] & 0x80) >> 7);
+        outptr[4] = 0x01 | ((inpts[3] & 0x7F) << 1);
+}
+
+static void directv_update_video_pes(unsigned char *ptr, int pos)
+{
+  //pos points at the 1st char after a pic start code
+  int picture_coding_type;
+  int hdr_len;
+  unsigned int pts1, dts1;
+  unsigned char *buf = ptr + pos;
+  picture_coding_type = (buf[1] >> 3) & 0x07;
+  hdr_len = (picture_coding_type > 1) ? 5 : 4;
+  if(buf[hdr_len + 3] == 0xb5)
+    hdr_len += 9;
+  if(buf[hdr_len + 3] == 0xb2) {
+    pts1 = ((buf[hdr_len+6] & 0x03)   << 30) +
+           ((buf[hdr_len+7] & 0x7f) << 23) +
+           ((buf[hdr_len+8])          << 15) +
+           ((buf[hdr_len+9] & 0x7f) << 8) +
+           buf[hdr_len+10];
+    dts1 = ((buf[hdr_len+13] & 0x03)   << 30) +
+           ((buf[hdr_len+14] & 0x7f) << 23) +
+           ((buf[hdr_len+15])          << 15) +
+           ((buf[hdr_len+16] & 0x7f) << 8) +
+           buf[hdr_len+17];
+    //NOTE:  This is wrong.  DSS timestamps only have a resolution of 2^32/300
+    //r5000_print("pts: %08x/%f dts: %08x/%f\n", pts1, pts1 / 27000000.0, dts1, dts1 / 27000000.0);
+    ptr[7] |= 0xc0;
+    directv_make_pespts(ptr+9, pts1/300);
+    ptr[9] |= 0x30;
+    directv_make_pespts(ptr+14, dts1/300);
+    ptr[14] |= 0x10;
+  }
+}
+
+static void directv_fix_audio_pts(unsigned char *ptr)
+{
+  unsigned int pts = ((ptr[0] & 0x06) << 29) +
+                     (ptr[1] << 22) +
+                     ((ptr[2] & 0xfe) << 14) +
+                     (ptr[3] << 7) +
+                     (ptr[4] >> 1);
+  directv_make_pespts(ptr, pts/300);
+  ptr[0] |= 0x20;
+}
+
+
+static void directv_write_ts(r5kdev_t *r5kdev, unsigned char *ptr, int len, int pid, int start, int *cc)
+{
+  int stuff = 184 - len;
+  //Note:  we know that there are 188 bytes allocated before 'ptr'
+  //       that we can use for the header
+  if(stuff > 0) {
+    int stuff1 = stuff;
+    while(stuff1 > 2) {
+      *--ptr = 0xff;
+      stuff1--;
+    }
+    if(stuff1 == 2) {
+      *--ptr = 0x00;
+    }
+    *--ptr = stuff - 1;
+    *--ptr = 0x30 | *cc;
+  } else {
+    *--ptr = 0x10 | *cc;
+  }
+  *--ptr = pid & 0xff;
+  *--ptr = (start << 6) | (pid >> 8);
+  *--ptr = 0x47;
+  r5kdev->cb(ptr, 188, r5kdev->cb_data);
+  *cc = (*cc+1) & 0x0f;
+}
+
+static void directv_process_block(r5kdev_t *r5kdev, unsigned char *buf, int len)
+{
+  int i;
+  struct r5000_dtv *dtv = (struct r5000_dtv *)r5kdev->stbdata;
+  for(i = 0; i < len; i+=2) {
+    unsigned char data = buf[i];
+    unsigned char type = buf[i+1];
+    if(0xff == type) {
+      //video
+      dtv->video[dtv->vpos++] = data;
+      if(dtv->vpos > 4 && dtv->video[dtv->vpos-2] == 0x01 &&
+         dtv->video[dtv->vpos-3] == 0x00 && dtv->video[dtv->vpos-4] == 0x00) {
+        if (data == 0xe0) {
+          //HD video header (PES)
+          directv_write_ts(r5kdev, dtv->video, dtv->vpos - 4, DTV_VPID, dtv->vstart, &dtv->video_cc);
+          dtv->pat_pmt_count++;
+          dtv->video[0] = 0x00;
+          dtv->video[1] = 0x00;
+          dtv->video[2] = 0x01;
+          dtv->video[3] = 0xe0;
+          dtv->vpos = 4;
+          dtv->vstart = 1;
+          dtv->vstate = data;
+        } else if (data == 0xb3 || data == 0x00) {
+          if (dtv->vstate == 0xff) {
+            dtv->vstate = data;
+            directv_write_ts(r5kdev, dtv->video, dtv->vpos - 4, DTV_VPID, dtv->vstart, &dtv->video_cc);
+            dtv->pat_pmt_count++;
+            //Create a PES header, but no PTS/DTS info yet so just use stuffing bytes
+            dtv->vpos = directv_make_empty_pes(dtv->video, 0xe0);
+            dtv->video[dtv->vpos++] = 0x00;
+            dtv->video[dtv->vpos++] = 0x00;
+            dtv->video[dtv->vpos++] = 0x01;
+            dtv->video[dtv->vpos++] = data;
+            dtv->vstart = 1;
+            dtv->pic_pos = dtv->vpos;
+          }
+        }
+      }
+      if(dtv->vpos == 188) {
+        if (dtv->vstate == 0x00)
+          //We found pic frame without a PES header (SD)
+          directv_update_video_pes(dtv->video, dtv->pic_pos);
+        directv_write_ts(r5kdev, dtv->video, 184, DTV_VPID, dtv->vstart, &dtv->video_cc);
+        dtv->pat_pmt_count++;
+        dtv->video[0] = dtv->video[184];
+        dtv->video[1] = dtv->video[185];
+        dtv->video[2] = dtv->video[186];
+        dtv->video[3] = dtv->video[187];
+        dtv->vpos = 4;
+        dtv->vstart = 0;
+        dtv->vstate = 0xff;
+      }
+    } else if(0xfe == type) {
+      //audio
+      dtv->audio[dtv->apos++] = data;
+      dtv->alen--;
+      if(dtv->alen <= 0 && dtv->apos > 6 && dtv->audio[dtv->apos-3] == 0xbd && dtv->audio[dtv->apos-4] == 0x01 &&
+         dtv->audio[dtv->apos-5] == 0x00 && dtv->audio[dtv->apos-6] == 0x00) {
+        dtv->alen = (dtv->audio[dtv->apos-2] << 8) + data;
+        directv_write_ts(r5kdev, dtv->audio, dtv->apos - 6, DTV_APID, dtv->astart, &dtv->audio_cc);
+        dtv->pat_pmt_count++;
+        dtv->audio[0] = 0x00;
+        dtv->audio[1] = 0x00;
+        dtv->audio[2] = 0x01;
+        dtv->audio[3] = 0xbd;
+        dtv->audio[4] = dtv->audio[dtv->apos-2];
+        dtv->audio[5] = data;
+        dtv->apos = 6;
+        dtv->astart = 1;
+        r5kdev->pmt[1].id = 0x81; //AC3
+      } else if(dtv->alen <= 0 && dtv->apos > 6 && dtv->audio[dtv->apos-3] == 0xc0 && dtv->audio[dtv->apos-4] == 0x01 &&
+           dtv->audio[dtv->apos-5] == 0x00 && dtv->audio[dtv->apos-6] == 0x00) {
+        dtv->alen = (dtv->audio[dtv->apos-2] << 8) + data;
+        directv_write_ts(r5kdev, dtv->audio, dtv->apos - 6, DTV_APID, dtv->astart, &dtv->audio_cc);
+        dtv->pat_pmt_count++;
+        dtv->audio[0] = 0x00;
+        dtv->audio[1] = 0x00;
+        dtv->audio[2] = 0x01;
+        dtv->audio[3] = 0xc0;
+        dtv->audio[4] = (dtv->alen + 3) >> 8;
+        dtv->audio[5] = (dtv->alen + 3) & 0xff;
+        dtv->audio[6] = 0x80;
+        dtv->audio[7] = 0x80;
+        dtv->audio[8] = 0x05;
+        dtv->apos = 9;
+        dtv->astart = 1;
+        r5kdev->pmt[1].id = 0x04; //MP2
+      } else if(dtv->apos == 190) {
+        if(dtv->astart && dtv->audio[3] == 0xc0)
+          directv_fix_audio_pts(dtv->audio+9);
+        directv_write_ts(r5kdev, dtv->audio, 184, DTV_APID, dtv->astart, &dtv->audio_cc);
+        dtv->pat_pmt_count++;
+        dtv->audio[0] = dtv->audio[184];
+        dtv->audio[1] = dtv->audio[185];
+        dtv->audio[2] = dtv->audio[186];
+        dtv->audio[3] = dtv->audio[187];
+        dtv->audio[4] = dtv->audio[188];
+        dtv->audio[5] = dtv->audio[189];
+        dtv->apos = 6;
+        dtv->astart = 0;
+      }
+    }
+  }
+  if(r5kdev->pmt[1].id && dtv->pat_pmt_count > DTV_PAT_PMT_COUNT) {
+    r5kdev->cb(r5000_pat_pkt, 188, r5kdev->cb_data);
+    r5000_send_pmt(r5kdev);
+    dtv->pat_pmt_count = 0;
+  }
+}
+
+static void directv_start_stream(r5kdev_t *r5kdev)
+{
+  struct r5000_dtv *dtv = (struct r5000_dtv *)r5kdev->stbdata;
+  dtv->vstart = 0;
+  dtv->astart = 0;
+  dtv->vpos = 0;
+  dtv->apos = 0;
+  dtv->video_cc = 0;
+  dtv->audio_cc = 0;
+  dtv->vstate = 0;
+  dtv->pat_pmt_count = DTV_PAT_PMT_COUNT;
+}
+void directv_init(r5kdev_t *r5kdev)
+{
+  struct r5000_dtv *dtv = (struct r5000_dtv *)calloc(1, sizeof(struct r5000_dtv));
+  dtv->video = dtv->vbuf + 188;
+  dtv->audio = dtv->abuf + 188;
+  dtv->pat_pmt_count = DTV_PAT_PMT_COUNT;
+  dtv->vstate = 0xff;
+  r5kdev->stbdata = dtv;
+  r5kdev->pmt[0].id = 0x02; //MPEG2
+  r5kdev->pmt[0].pid = DTV_VPID;
+  r5kdev->pmt[0].desc = r5000_pmt_video_desc;
+  r5kdev->pmt[1].id = 0x00;
+  r5kdev->pmt[1].pid = DTV_APID;
+  r5kdev->pmt[1].desc = r5000_pmt_audio_desc;
+  r5kdev->num_pmt_entries = 2;
+  r5kdev->process_block = directv_process_block;
+  r5kdev->start_stream  = directv_start_stream;
+  r5kdev->button = &directv_button_cmd;
+  r5kdev->power_active_low = 1;
+  r5kdev->read_words = 1;
+}
+
+void directv_free(r5kdev_t *r5kdev)
+{
+  free(r5kdev->stbdata);
+}
Index: libs/libmythtv/r5000/r5k_misc.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libs/libmythtv/r5000/r5k_misc.c	2008-11-26 06:56:44.000000000 -0800
@@ -0,0 +1,157 @@
+/* Copyright 2008 Alan Nisota <alannisota@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "r5000_internal.h"
+
+unsigned char r5000_pat_pkt[188] = {
+0x47, 0x40, 0x00, 0x13, 0x00, 0x00, 0xb0, 0x0d, 0x00, 0x06, 0xc5, 0x00, 0x00, 0x00, 0x01, 0xe0,
+0x21, 0x19, 0x3a, 0x82, 0xc4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+
+struct r5k_descriptor r5000_pmt_audio_desc = {{0x06, 0x0a, 0x04, 0x65, 0x6e, 0x67, 0x00}};
+struct r5k_descriptor r5000_pmt_video_desc = {{0x00}};
+
+//taken and adapted from libdtv, (c) Rolf Hakenes
+// CRC32 lookup table for polynomial 0x04c11db7
+static unsigned int crc_table[256] = {
+   0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
+   0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
+   0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
+   0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
+   0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
+   0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
+   0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
+   0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
+   0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
+   0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
+   0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
+   0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
+   0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
+   0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
+   0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
+   0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
+   0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
+   0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
+   0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
+   0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
+   0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
+   0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
+   0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
+   0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
+   0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
+   0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
+   0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
+   0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
+   0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
+   0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
+   0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
+   0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
+   0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
+   0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
+   0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
+   0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
+   0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
+   0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
+   0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
+   0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
+   0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
+   0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
+   0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4};
+
+static void r5000_calc_crc(unsigned char *out, const unsigned char *d, int len)
+{
+   register int i;
+   unsigned int crc = 0xFFFFFFFF;
+   const unsigned char *u=(unsigned char*)d; // Saves '& 0xff'
+
+   for (i=0; i<len; i++)
+      crc = (crc << 8) ^ crc_table[((crc >> 24) ^ *u++)];
+
+   *out++ = (crc >> 24) & 0xff;
+   *out++ = (crc >> 16) & 0xff;
+   *out++ = (crc >> 8) & 0xff;
+   *out++ = (crc >> 0) & 0xff;
+}
+
+static void r5000_build_pmt(r5kdev_t *r5kdev)
+{
+  int i;
+  unsigned char *ptr;
+  unsigned char ts[188] = {
+  0x47, 0x40, 0x21, 0x10,
+  0x00, 0x02,
+  0xb0, 0x00,  //length
+  0x00, 0x01,
+  0xc1,        //version<<1 (5 bits) | current_next
+  0x00, 0x00,  //section/last_section
+  0xf0, 0x00,  //PCR_PID
+  0xf0, 0x00}; //Program Info Length
+
+  ts[3] = 0x10 | r5kdev->pmt_next_cc;
+
+  //Change the PMT version every time we rebuild the PMT
+  ts[10] |= r5kdev->pmt_version << 1;
+  r5kdev->pmt_version = (r5kdev->pmt_version + 1) % 32;
+
+  r5000_print("Building PMT\n");
+  ptr = ts + 17;
+  for(i = 0; i < r5kdev->num_pmt_entries; i++) {
+    *ptr++ = r5kdev->pmt[i].id;
+    *ptr++ = 0xe0 | (r5kdev->pmt[i].pid>>8);
+    *ptr++ = r5kdev->pmt[i].pid & 0xff;
+    *ptr++ = 0xf0;
+    memcpy(ptr, r5kdev->pmt[i].desc.d, r5kdev->pmt[i].desc.d[0]+1);
+    ptr += r5kdev->pmt[i].desc.d[0]+1;
+    if(IS_VIDEO(r5kdev->pmt[i].id)) {
+      ts[13] = 0xe0 | (r5kdev->pmt[i].pid >> 8);
+      ts[14] = r5kdev->pmt[i].pid & 0xff;
+    }
+  }
+  ts[7] = (ptr - ts) - 8/*header*/ + 4/*CRC*/;
+  r5000_calc_crc(ptr, ts + 5, (ptr - ts) - 5);
+  memset(ptr+4, 0xff, 188 - (ptr + 4 - ts));
+  memcpy(r5kdev->pmt_pkt, ts, 188);
+  //PRINTHEX("PMT", r5kdev->pmt_pkt, 188);
+}
+
+void r5000_reset_pmt(r5kdev_t *r5kdev)
+{
+  r5kdev->num_pmt_entries = 0;
+  r5kdev->pmt_state = 0;
+  r5kdev->pmt_pkt[0] = 0;
+}
+
+void r5000_send_pmt(r5kdev_t *r5kdev)
+{
+  if(r5kdev->pmt_pkt[0] != 0x47) {
+    r5000_build_pmt(r5kdev);
+  }
+  r5kdev->pmt_pkt[3] = 0x10 | r5kdev->pmt_next_cc;
+  r5kdev->pmt_next_cc = (r5kdev->pmt_next_cc + 1) & 0x0f;
+  r5kdev->cb(r5kdev->pmt_pkt, 188, r5kdev->cb_data);
+}
Index: libs/libmythtv/r5000/r5k_sat.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libs/libmythtv/r5000/r5k_sat.c	2008-11-26 06:56:44.000000000 -0800
@@ -0,0 +1,469 @@
+/* Copyright 2008 Alan Nisota <alannisota@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+//Support for Dish Network ViP211/ViP422 boxes
+#include <stdio.h>
+#include <string.h>
+#include "r5000_internal.h"
+
+#define DTV_PAT_PMT_COUNT 5000
+#define MAX_PKT_SIZE 272
+#define MAX_PAT_SECTIONS 1
+#define MAX_SIDS 200
+#define MAX_EPIDS 5
+
+struct pat {
+  int version;
+  unsigned char last_section;
+  unsigned char section_seen[MAX_PAT_SECTIONS];
+  unsigned int crc[MAX_PAT_SECTIONS];
+  unsigned int pmts[MAX_SIDS];
+  int pmt_count;
+};
+
+struct sids {
+  unsigned short sid;
+  unsigned int crc;
+  unsigned char seen_name;
+  struct r5k_epid epid[MAX_EPIDS];
+  unsigned char epid_count;
+};
+
+struct r5000_sat {
+  unsigned char buf[MAX_PKT_SIZE];
+  unsigned int pos;
+  int offset;
+  unsigned char sync;
+  unsigned char bytesize;
+  unsigned char sync_byte;
+  unsigned int has_sync_byte;
+  unsigned int pat_pmt_count;
+  unsigned int packet_size;
+  unsigned int allowed_packet_size[8];
+
+  unsigned int current_sid;
+  struct pat pats;
+  struct sids *sids;
+};
+
+static int sat_read_pat_pkt(unsigned char *pes, struct pat *pat, unsigned int size) {
+  unsigned int sec, end, crc, current_next;
+  int version;
+  unsigned char *ptr, last_sec;
+
+  if (pes[0] != 0x00) {
+    r5000_print("read_pat: expected PAT table 0x00 but got 0x%02x\n", pes[0]);
+    return -1;
+  }
+  end = (((pes[1] & 0x03) << 8 | pes[2]) + 3 - 4);
+  if(end > size-4) {
+    r5000_print("read_pat: invalid PAT table size (%d > %d)\n", end, size-4);
+    return -1;
+  }
+  crc = (pes[end]<<24) | (pes[end+1]<<16) | (pes[end+2]<<8) | pes[end+3];
+  version = (pes[5] >> 1) & 0x1f;
+  current_next = pes[5] & 0x01;
+  sec = pes[6];
+  last_sec = pes[7];
+  if(! current_next)
+    //ignore inactive PAT
+    return 0;
+  if(last_sec >= MAX_PAT_SECTIONS) {
+    r5000_print("read_pat: illegal section count %d > %d\n",
+             last_sec, MAX_PAT_SECTIONS);
+    return -1;
+  }
+  if (pat->version != version || last_sec != pat->last_section ||
+      pat->crc[sec] != crc) {
+    pat->version = version;
+    pat->last_section = last_sec;
+    pat->pmt_count = 0;
+    memset(pat->section_seen, 0, sizeof(pat->section_seen));
+  }
+  if(pat->section_seen[sec])
+    return 0;
+  pat->crc[sec] = crc;
+  pat->section_seen[sec] = 1;
+  for(ptr = pes + 8; ptr < pes + end; ptr += 4) {
+    int sid, pid;
+    sid = (ptr[0] << 8) | ptr[1];
+    pid = ((ptr[2] & 0x1F) << 8) | ptr[3];
+    r5000_print("found PID: %04x for sid: %d\n", pid, sid);
+    if(sid != 0) {
+      pat->pmts[pat->pmt_count++] = (sid << 16) | pid;
+    }
+  }
+  return 1;
+}
+
+static struct sids *sat_read_pmt_pkt(unsigned char *buf, struct sids *sids,
+                                     unsigned int size) {
+  //
+  // NOTE we aren't using last_sec here yet!
+  //
+
+  struct sids *sidptr = sids;
+  unsigned int count, skip, pos, crc, current_next;
+  int sid, sec, last_sec, pcrpid, epid, type;
+  if (buf[0] != 0x02) {
+    r5000_print("read_pmt expected table 0x02 but got 0x%02x\n", buf[0]);
+    return NULL;
+  }
+  count = (((buf[1] & 0x03) << 8) | buf[2]) + 3 - 4;
+  sid = (buf[3] << 8) | buf[4];
+  current_next = buf[5] & 0x01;
+  crc = (buf[count]<<24) | (buf[count+1]<<16) | (buf[count+2]<<8) | buf[count+3];
+  sec = buf[6];
+  last_sec = buf[7];
+  pcrpid = ((buf[8] & 0x1F) << 8) | buf[9];
+  skip = ((buf[10] & 0x03) << 8) | buf[11];
+  if(skip > count - 12 || count > size) {
+    r5000_print("skip: %d > count: %d - 12 || count > size: %d\n",
+           skip, count, size);
+    return NULL;
+  }
+  if(! current_next) {
+    r5000_print("read_pmt ignoring future PMT\n");
+    return NULL;
+  }
+  while(sidptr->sid != 0) {
+    if(sidptr->sid == sid)
+      break;
+    sidptr++;
+  }
+  if(sidptr->sid == 0) {
+    // We weren't expecting this sid
+    r5000_print("read_pmt found unexpected sid: %d\n", sidptr->sid);
+    return sidptr;
+  }
+  if(sidptr->crc == crc) {
+    //sid is unchanged
+    //r5000_print("read_pmt found identical crc (%08x) for %d\n", crc, sidptr->sid);
+    return NULL;
+  }
+  memset(sidptr, 0, sizeof(struct sids));
+
+  r5000_print("read_pmt: sid: %d pcrpid: %d skip: %d count: %d\n", sid, pcrpid, skip, count);
+  sidptr->sid = sid;
+  sidptr->crc = crc;
+  sidptr->epid_count = 0;
+  for(pos = 12 + skip; pos < count;) {
+    struct r5k_epid *pidptr = &sidptr->epid[sidptr->epid_count];
+    type = buf[pos];
+    epid = ((buf[pos+1] & 0x1F) << 8) | buf[pos+2];
+    skip = ((buf[pos+3] & 0x03) << 8) | buf[pos+4];
+    pidptr->pid = epid;
+    pidptr->id  = type == 0x80 ? 0x02 : type;
+    memcpy(pidptr->desc.d, buf + pos + 4, skip + 1);
+    sidptr->epid_count++;
+    r5000_print("read_pmt: epid %04x (type %02x, skip=%d) mapped to sid %d\n", epid, type, skip, sid);
+    pos += 5 + skip;
+  }
+  return sidptr;
+}
+
+void sat_find_chname(unsigned char *buf, struct sids *sids, unsigned int size) {
+  struct sids *sidptr = sids;
+  unsigned int count;
+  int sid;
+  char str[20], *strptr = str;
+  while(size) {
+    count = (((buf[1] & 0x03) << 8) | buf[2]) + 3;
+    if(buf[0] == 0x41) {
+      buf += count;
+      size -= count;
+      continue;
+    }
+    if(buf[0] == 0xc1) {
+      unsigned char *ptr = buf+16;
+      sid = (buf[7]<<8) | buf[8];
+      while(sidptr->sid != 0) {
+        if(sidptr->sid == sid)
+          break;
+        sidptr++;
+      }
+      if(sidptr->sid == 0) {
+        // We weren't expecting this sid
+        return;
+      }
+      if(sidptr->seen_name)
+        return;
+      sidptr->seen_name = 1;
+      while(*ptr >= 0x20 && *ptr < 0x7b && strptr-str < sizeof(str)-1) {
+        *strptr++ = *ptr++;
+      }
+      *strptr = 0;
+      r5000_print("sid: %d is channel %s\n", sid, str);
+      return;
+    }
+    return;
+  }
+}
+
+int sat_add_pmt(r5kdev_t *r5kdev, struct r5k_epid *epid)
+{
+  int i;
+  int is_video = IS_VIDEO(epid->id);
+
+  for(i = 0; i < r5kdev->num_pmt_entries; i++) {
+    if(r5kdev->pmt[i].pid == epid->pid) {
+      if(r5kdev->pmt[i].id == epid->id)
+        return 0;
+      //different table_id for existing pid.  Reset
+      r5000_reset_pmt(r5kdev);
+      break;
+    }
+    if(is_video) {
+      if(IS_VIDEO(r5kdev->pmt[i].id)) {
+        //a video entry already exists.  Reset
+        r5000_reset_pmt(r5kdev);
+        break;
+      }
+    }
+  }
+  //Didn't find this PID, add it
+  if(i == R5K_MAX_PIDS)
+    return 0;
+  r5000_print("Adding %04x: %02x (desc_len=%d) @ %08x\n", epid->pid, epid->id, epid->desc.d[0], r5kdev->bytes_read);
+  r5kdev->pmt[r5kdev->num_pmt_entries++] = *epid;
+  r5kdev->pmt_state |= is_video ? 0x01 : 0x02;
+  return 1;
+}
+
+void sat_process_ts(r5kdev_t *r5kdev, unsigned char *buf)
+{
+  struct r5000_sat *sat = (struct r5000_sat *)r5kdev->stbdata;
+  int pid;
+  int i, j, k;
+  pid = ((buf[1] << 8) | buf[2]) & 0x1fff;
+  if(pid == 0x1fff)
+    return;
+  if(pid == 0 && (buf[1] & 0x40)) {
+    //Always read PAT in case we changed transponders
+    if(sat_read_pat_pkt(buf+buf[4]+5, &sat->pats, 188-buf[4]-5) > 0) {
+      r5000_print("Found new PAT\n");
+      if(sat->pats.pmt_count) {
+        sat->sids = realloc(sat->sids, sizeof(struct sids)*(sat->pats.pmt_count+1));
+        memset(sat->sids, 0, sizeof(struct sids)*(sat->pats.pmt_count+1));
+        //Pre load sids so we don't overrun if the PMT changes before the PAT
+        for(i = 0; i < sat->pats.pmt_count; i++) {
+          sat->sids[i].sid = sat->pats.pmts[i]>>16;
+        }
+        sat->sids[i].sid = 0;
+        r5000_reset_pmt(r5kdev);
+      }
+    } else if(r5kdev->pmt_state == 0x03) {
+      r5kdev->cb(r5000_pat_pkt, 188, r5kdev->cb_data);
+      r5000_send_pmt(r5kdev);
+      sat->pat_pmt_count = 0;
+    }
+    return;
+  }
+  //Always reread PMT in case the pids have changed
+  for(i = 0; i < sat->pats.pmt_count; i++) {
+    if(pid == (unsigned short)(sat->pats.pmts[i])) {
+#if 0
+    {
+      char s[80];
+      FILE *fh;
+      sprintf(s, "0x%04x.ts", pid);
+      fh= fopen(s, "a");
+      fwrite(buf, 188, 1, fh);
+      fclose(fh);
+    }
+#endif
+      //r5000_print("PID: %04x - %04x (%d) %02x %02x %02x %02x %02x\n", pid, (unsigned short)(sat->pats.pmts[i]), i, buf[1], buf[2], buf[3], buf[4], buf[5]);
+      if(buf[1] & 0x40 && buf[4]+5 < 188) {
+        if(buf[buf[4]+5] == 0x02) {
+          struct sids *sidptr;
+          if((sidptr = sat_read_pmt_pkt(buf+buf[4]+5, sat->sids, 188-buf[4]-5))) {
+            if(sidptr->sid == 0) {
+              //Unexpected sid, reset PAT;
+              memset(&sat->pats, 0, sizeof(struct pat));
+            } else if(sat->current_sid == sidptr->sid) {
+              r5000_reset_pmt(r5kdev);
+              sat->current_sid = 0;
+            }
+          }
+        //} else if(buf[buf[4]+5] == 0xc1 || buf[buf[4]+5] == 0x41) {
+        //  sat_find_chname(buf+buf[4]+5, sat->sids, 188-buf[4]-5);
+        }
+      }
+      return;
+    }
+    if(! sat->current_sid) {
+      for(j = 0; j < sat->sids[i].epid_count; j++) {
+        if(pid == sat->sids[i].epid[j].pid &&
+           IS_VIDEO(sat->sids[i].epid[j].id) &&
+           (!r5kdev->channel || r5kdev->channel == sat->sids[i].sid)) {
+          //Found unencrypted video.  Choose this one
+          // Assume this is MPEG2.  Don't know what MPEG4 looks like yet
+          sat->current_sid = sat->sids[i].sid;
+          r5000_print("Found decrypted sid %d\n", sat->current_sid);
+          if (sat_add_pmt(r5kdev, &sat->sids[i].epid[j])) {
+            //This is a new Video PID
+            for(k = 0; k < sat->sids[i].epid_count; k++) {
+              if(sat->sids[i].epid[k].id == 0x81 ||
+                 sat->sids[i].epid[k].id == 0xbd) {
+                sat_add_pmt(r5kdev, &sat->sids[i].epid[k]);
+              }
+            }
+            r5kdev->cb(r5000_pat_pkt, 188, r5kdev->cb_data);
+            r5000_send_pmt(r5kdev);
+            sat->pat_pmt_count = 0;
+          }
+          r5kdev->cb(sat->buf, 188, r5kdev->cb_data);
+          return;
+        }
+      }
+    }
+  }
+  for(i = 0; i < r5kdev->num_pmt_entries; i++) {
+    if(pid == r5kdev->pmt[i].pid) {
+      r5kdev->cb(sat->buf, 188, r5kdev->cb_data);
+      sat->pat_pmt_count++;
+      return;
+    }
+  }
+#if 0
+  //r5000_print("Ignoring PID: %04x\n", pid);
+  {
+    char s[80];
+    FILE *fh;
+    sprintf(s, "0x%04x.ts", pid);
+    fh= fopen(s, "a");
+    fwrite(buf, 188, 1, fh);
+    fclose(fh);
+  }
+#endif
+}
+
+static void sat_process_block(r5kdev_t *r5kdev, unsigned char *buf, int len)
+{
+  struct r5000_sat *sat = (struct r5000_sat *)r5kdev->stbdata;
+
+  int pos;
+  if(! r5kdev->streaming)
+    return;
+  if(len <= 0)
+    return;
+
+  for(pos = sat->offset; pos < len; pos += sat->bytesize) {
+    if(! sat->sync) {
+      if(buf[pos] == 0x47) {
+        int i;
+        if (pos + (sat->allowed_packet_size[0]<<1) < len) {
+          if((buf[pos+1] & 0xfc) == 0xfc && (buf[pos+3] & 0xfc) == 0xfc &&
+             (buf[pos+5] & 0xfc) == 0xfc && (buf[pos+7] & 0xfc) == 0xfc) {
+             sat->bytesize = 2;
+          }
+          for(i=0; sat->allowed_packet_size[i] != 0; i++) {
+            if(buf[pos+(sat->allowed_packet_size[i] * sat->bytesize)] == 0x47) {
+              r5000_print("(%d) Found %d byte sync at %08x: bytesize = %d\n",
+                    r5kdev->nexturb, sat->allowed_packet_size[i],
+                    r5kdev->bytes_read+pos, sat->bytesize);
+              sat->packet_size = sat->allowed_packet_size[i];
+              sat->sync = 1;
+              sat->buf[0] = 0x47;
+              sat->pos = 1;
+              break;
+            }
+          }
+        }
+      }
+      continue;
+    }
+    if (sat->pos == 0 && buf[pos] != 0x47) {
+      sat->sync = 0;
+      sat->bytesize = 1;
+      r5000_print("(%d)Lost sync at %08x\n", r5kdev->nexturb,
+             r5kdev->bytes_read+pos);
+      continue;
+    }
+    if(sat->pos == 3 && (buf[pos] & 0xc0) != 0x00) {
+      // Encrypted channel, skip this packet
+      sat->pos = 0;
+      pos += sat->bytesize * (sat->packet_size - 4); //Loop adds additional 2
+      continue;
+    }
+    sat->buf[sat->pos++] = buf[pos];
+    if (sat->pos == sat->packet_size) {
+      sat_process_ts(r5kdev, sat->buf);
+      //if packet size > 188, assume remaining bytes are parity and ignore
+      sat->pos = 0;
+    }
+  }
+  sat->offset = pos - len;
+  if(r5kdev->pmt_state == 0x03 && sat->pat_pmt_count > DTV_PAT_PMT_COUNT) {
+    r5kdev->cb(r5000_pat_pkt, 188, r5kdev->cb_data);
+    r5000_send_pmt(r5kdev);
+    sat->pat_pmt_count = 0;
+  }
+}
+
+static void sat_start_stream(r5kdev_t *r5kdev)
+{
+  struct r5000_sat *sat = (struct r5000_sat *)r5kdev->stbdata;
+  sat->offset = 0;
+  sat->sync = 0;
+  sat->pos = 0;
+  sat->bytesize = 1;
+
+  //Clear any defined PAT or PMT data
+  if(sat->sids) {
+    free(sat->sids);
+    sat->sids = 0;
+  }
+  memset(&sat->pats, 0, sizeof(struct pat));
+  sat->pat_pmt_count = DTV_PAT_PMT_COUNT;
+}
+
+static void sat_change_channel(r5kdev_t *r5kdev)
+{
+  r5000_reset_pmt(r5kdev);
+}
+
+void sat_init(r5kdev_t *r5kdev)
+{
+  struct r5000_sat *sat =
+                   (struct r5000_sat *)calloc(1, sizeof(struct r5000_sat));
+  r5kdev->stbdata = sat;
+  sat->pat_pmt_count = DTV_PAT_PMT_COUNT;
+  if(r5kdev->stb_type == R5K_STB_HDD) {
+    sat->sync_byte = 0xff;
+    sat->has_sync_byte = 1;
+    sat->allowed_packet_size[0] = 272;
+    sat->allowed_packet_size[1] = 233;
+    sat->allowed_packet_size[2] = 204;
+    sat->allowed_packet_size[3] = 188;
+    sat->allowed_packet_size[4] = 0;
+  } else {
+    sat->allowed_packet_size[0] = 188;
+    sat->allowed_packet_size[1] = 0;
+  }
+  r5kdev->process_block = sat_process_block;
+  r5kdev->start_stream  = sat_start_stream;
+  r5kdev->change_channel = &sat_change_channel;
+}
+
+void sat_free(r5kdev_t *r5kdev)
+{
+  struct r5000_sat *sat = (struct r5000_sat *)r5kdev->stbdata;
+  if(sat->sids)
+    free(sat->sids);
+  free(r5kdev->stbdata);
+}
+
Index: libs/libmythtv/r5000/r5000init.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libs/libmythtv/r5000/r5000init.h	2008-11-26 06:56:44.000000000 -0800
@@ -0,0 +1,77 @@
+/* Copyright 2008 Alan Nisota <alannisota@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef R5000_INIT_H
+#define R5000_INIT_H
+
+#define R5K_INIT_MAX 9
+#define R5K_INIT_SERIAL 8
+#define R5K_DEFAULT_SLEEP 100000
+struct {
+  int rsleep;
+  int rlen;
+  int wsleep;
+  int wlen;
+  unsigned char data[0x40];
+} r5kinit[R5K_INIT_MAX] = {
+// 0
+{R5K_DEFAULT_SLEEP, -1, 0, 64,
+    {0x30}},
+// 1
+{R5K_DEFAULT_SLEEP, 1, 0, 64,
+    {0x08, 0x00, 0x20, 0x00, 0x00, 0x3a, 0xd4, 0x29, 0x7c, 0x56, 0x31, 0x44, 0x86, 0x6d, 0x0d, 0x0d,
+     0x1b, 0x0a, 0xad, 0x0f, 0xd0, 0x2e, 0x94, 0x3f, 0xd4, 0x08, 0xa2, 0x4b, 0x68, 0x14, 0x1f, 0x13,
+     0x04, 0x62, 0x1b, 0x14, 0xb9, 0x69, 0xcc, 0x25, 0x91, 0x06, 0xc9, 0x26, 0xf9, 0x41, 0x64, 0x46,
+     0x7d, 0x17, 0x61, 0x09, 0x5c, 0x5b, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+// 2
+{R5K_DEFAULT_SLEEP, 1, 0, 64,
+    {0x08, 0x00, 0x5a, 0x00, 0x00, 0x06, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+// 3
+{R5K_DEFAULT_SLEEP, 1, 0, 64,
+    {0x08, 0x00, 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+// 4
+{R5K_DEFAULT_SLEEP, 1, R5K_DEFAULT_SLEEP, 64,
+    {0x08, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+// 5
+{R5K_DEFAULT_SLEEP, 26, 0, 6,
+    {0x08, 0x01, 0x00, 0x00, 0x01, 0x14}},
+// 6
+{R5K_DEFAULT_SLEEP, 1, 0, 64,
+    {0x08, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+// 7
+{R5K_DEFAULT_SLEEP, 22, 0, 64,
+    {0x08, 0x01, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+// 8
+{R5K_DEFAULT_SLEEP, 4, 0, 1,
+    {0x20}}
+};
+
+#endif
Index: contrib/r5000/ehci-2.6.25.patch
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ contrib/r5000/ehci-2.6.25.patch	2008-11-26 07:53:52.000000000 -0800
@@ -0,0 +1,11 @@
+--- a/drivers/usb/host/ehci-q.c	2007-06-11 11:37:06.000000000 -0700
++++ b/drivers/usb/host/ehci-q.c	2008-02-05 19:46:08.000000000 -0800
+@@ -751,7 +751,7 @@
+ 			info2 |= (EHCI_TUNE_MULT_HS << 30);
+ 		} else if (type == PIPE_BULK) {
+ 			info1 |= (EHCI_TUNE_RL_HS << 28);
+-			info1 |= 512 << 16;	/* usb2 fixed maxpacket */
++			info1 |= ((maxp == 1024) ? 1024 : 512) << 16;	/* usb2 fixed maxpacket */
+ 			info2 |= (EHCI_TUNE_MULT_HS << 30);
+ 		} else {		/* PIPE_INTERRUPT */
+ 			info1 |= max_packet (maxp) << 16;
Index: contrib/r5000/r5000.hex
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ contrib/r5000/r5000.hex	2008-11-26 07:53:52.000000000 -0800
@@ -0,0 +1,512 @@
+:10000000020BBEAAAAAAAAAAAAAAAA020EFBAAAA76
+:10001000AAAAAA020E76AAAAAAAAAA0208B8AAAAF4
+:10002000AAAAAAAAAAAAAAAAAAAAAA020B31AAAAF0
+:10003000AAAAAA020FDEAAAAAAAAAAAAAAAAAAAA2F
+:10004000AAAAAA020800AAAAAAAAAA02069DAAAA5D
+:10005000AAAAAA020800AAAAAAAAAAAAAAAAAAAAF4
+:10006000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF0
+:10007000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE0
+:1000800090E6B9E0700302015B14700302020424DD
+:10009000FE700302029924FB7003020155147003E1
+:1000A00002014F147003020143147003020149243A
+:1000B000056003020305120FE6400302031190E6F8
+:1000C000BBE024FE602C14604724FD6016146031F0
+:1000D00024067065E52490E6B3F0E52590E6B4F0DB
+:1000E000020311E52C90E6B3F0E52D90E6B4F002A2
+:1000F0000311E52690E6B3F0E52790E6B4F002039D
+:1001000011E52890E6B3F0E52990E6B4F00203117A
+:1001100090E6BAE0FF120ECFAA06A9077B01EA49D2
+:10012000600DEE90E6B3F0EF90E6B4F002031190AC
+:10013000E6A0E04401F002031190E6A0E04401F0E3
+:10014000020311120FA7020311120FD6020311129C
+:100150000FCE020311120F95020311120FE8400394
+:1001600002031190E6B8E0247F602B14603C240267
+:1001700060030201FAA200E433FF25E0FFA204E4D9
+:10018000334F90E740F0E4A3F090E68AF090E68BDE
+:100190007402F0020311E490E740F0A3F090E68AC5
+:1001A000F090E68B7402F002031190E6BCE0547EFE
+:1001B000FF7E00E0D3948040067C007D0180047CBB
+:1001C000007D00EC4EFEED4F24C4F582740F3EF529
+:1001D00083E493FF3395E0FEEF24A1FFEE34E68F36
+:1001E00082F583E0540190E740F0E4A3F090E68AC2
+:1001F000F090E68B7402F002031190E6A0E0440157
+:10020000F0020311120FEA400302031190E6B8E076
+:1002100024FE601D2402600302031190E6BAE0B4DC
+:100220000105C20002031190E6A0E04401F00203C0
+:100230001190E6BAE0705990E6BCE0547EFF7E0073
+:10024000E0D3948040067C007D0180047C007D002A
+:10025000EC4EFEED4F24C4F582740F3EF583E4931B
+:10026000FF3395E0FEEF24A1FFEE34E68F82F583A5
+:10027000E054FEF090E6BCE05480FF131313541FCB
+:10028000FFE0540F2F90E683F0E04420F0020311CA
+:1002900090E6A0E04401F08078120FEC507390E6F5
+:1002A000B8E024FE60202402706790E6BAE0B40152
+:1002B00004D200805C90E6BAE06402605490E6A04C
+:1002C000E04401F0804B90E6BCE0547EFF7E00E00D
+:1002D000D3948040067C007D0180047C007D00EC8E
+:1002E0004EFEED4F24C4F582740F3EF583E493FF78
+:1002F0003395E0FEEF24A1FFEE34E68F82F583E034
+:100300004401F0800C120FEE500790E6A0E044018B
+:10031000F090E6A0E04480F022E4F517F516F5151C
+:10032000F514C205C200C204C201120A9D7E0A7FF2
+:10033000008E248F25752C0A752D1275220A7523BF
+:100340001C752A0A752B43752F0A753071EE54E01F
+:10035000700302045D7518007519808E1A8F1BC317
+:10036000749B9FFF740A9ECF2402CF3400FEE48F5B
+:10037000138E12F511F510F50FF50EF50DF50CAF06
+:1003800013AE12AD11AC10AB0FAA0EA90DA80CC381
+:10039000120E105026E519250FF582E518350EF5D9
+:1003A0008374CDF0E50F2401F50FE4350EF50EE46E
+:1003B000350DF50DE4350CF50C80C4E4F50FF50EA4
+:1003C000F50DF50CAF13AE12AD11AC10AB0FAA0EBC
+:1003D000A90DA80CC3120E105031AE0EAF0FE51BC5
+:1003E0002FF582E51A3EF583E0FDE5192FF582E54C
+:1003F000183EF583EDF0EF2401F50FE43EF50EE431
+:10040000350DF50DE4350CF50C80B98518248519EA
+:100410002574002480FF740A34FFFEC3E52D9FF588
+:100420002DE52C9EF52CC3E5279FF527E5269EF5A7
+:1004300026C3E5299FF529E5289EF528C3E5239FD6
+:10044000F523E5229EF522C3E52B9FF52BE52A9E99
+:10045000F52AC3E5309FF530E52F9EF52FD2E8430E
+:10046000D82090E668E04409F0E04402F090E65EAF
+:10047000E0440CF043E80443E80290E6507404F0D2
+:10048000000000E4F5B290E67AF043B288F58043CC
+:10049000A82E438804438910758B6F758DFE90E6F6
+:1004A0008F04F075AF07759DE1E4F59E90E65CE082
+:1004B000443DF0D2AF90E680E020E105D207120E75
+:1004C0004790E680E04408F00000000000000000D3
+:1004D00000E054F7F0538EF8C20530010512008099
+:1004E000C201300529120FE25024C2051207DB2099
+:1004F000001690E682E030E704E020E1EF90E6822B
+:10050000E030E604E020E0E4120EA3120FE4120F44
+:10051000F080C7C0E0C0F0C083C082C0D075D000FA
+:10052000C000C001C002C003C004C005C006C007AF
+:10053000759AE7759B80759DE130060302061A7572
+:100540000A00750B0090E67BE0FF7432250BF8A6DD
+:1005500007050BE50B7002050AE59B2480C3940692
+:1005600040E3E532120E2105F900058908059F10C8
+:1005700005DF2005B93005D240061250060A600595
+:10058000DACC05CCFF0000061A159B5391EF90E6DC
+:100590005F7408F090E68DE4F0120D670206827435
+:1005A000FD259BF59B5391EF90E65F7408F090E674
+:1005B0008DE4F0120D6702068290E7C0740AF0A382
+:1005C000E580F090E68F7402F00206745380F70223
+:1005D000067490E68F7401F08040753900803B907E
+:1005E000E7C074DDF0A37431F0A3742EF0A3743669
+:1005F000F090E68F7404F0807B53A8F785368A75F7
+:10060000CDFE75CC6F759E00801090E618740DF0CD
+:10061000806290E618740CF0805A90E67BE090E6D9
+:100620007CF0E59B2480FF90E68DE0FEEFC39E40CA
+:10063000E9E53324FEFFE59EC39F5006E4F0D206B1
+:100640008032C206759DE1759E00438902758E0059
+:10065000858A8C75C800E5367006E53542808005D0
+:10066000E535F4528043800890E67CE0F538438815
+:100670001043C8045391EF90E65F7408F090E68D44
+:10068000E4F0D007D006D005D004D003D002D001CA
+:10069000D000D0D0D082D083D0F0D0E032C0E0C043
+:1006A00083C082C0D075D000C000C004C005C006A1
+:1006B000C0075391DF90E678E05404FF7E003002DB
+:1006C0000BE0F430E1067C007D0180047C007D00BD
+:1006D000EC4EFEED4F4E6008E4FF12096F0207C8B2
+:1006E000E53224F860030207C8E53314604904606A
+:1006F000030207C8E59B2480FFBF050B059B90E61E
+:1007000079E534F00207C8E5372406FFE433FEAD8F
+:100710009BED2480FDE434FFFCC3ED9FEE6480F884
+:10072000EC648098500B90E67BE090E679F002074D
+:10073000C81209660207C8E536701CE59B2480C311
+:100740009406500B059B90E679E534F00207C875D6
+:100750003601120D67807130020F90E679E075085E
+:1007600000F509C202059B805FE5372406FFE433EC
+:10077000FEAD9BED2480FDE434FFFCC3ED9FEE64F1
+:1007800080F8EC648098503DE5372405FFE59B2414
+:1007900080B5071490E678E04440F090E679E09068
+:1007A000E67BF07F0112096FE5372404FFE59B2407
+:1007B00080B5070790E678E04420F090E679E09075
+:1007C000E67BF08003120966D007D006D005D0047E
+:1007D000D000D0D0D082D083D0E03290E682E04406
+:1007E000C0F090E681F04387010000000000227411
+:1007F00000F58690FDA57C05A3E582458370F9226E
+:10080000020F3900020F7F00020F6900020F510032
+:10081000020DA200020DD900020FF100020FF2003A
+:10082000020FF300020FF400020C4A00020513004D
+:10083000020FF500020FF600020FF700020FF8009A
+:10084000020FF900020FF200020FFA00020FFB0084
+:10085000020FFC00020FFD00020FFE00020FFF005E
+:1008600002100000020FF200020FF200020FF2006D
+:100870000210010002100200021003000210040026
+:10088000020F1C00021005000210060002100700F3
+:10089000021008000210090002100A0002100B00EA
+:1008A00002100C0002100D0002100E0002100F00CA
+:1008B0000210100002101100C0E0C083C082C0D03E
+:1008C0005388BF758B6F758DFEE59EC3947A507209
+:1008D000300313E58020E10E43800890E67CE53884
+:1008E000F0753800C203200313E58030E10E538019
+:1008F000F790E67CE538F0753800D203E538F470FF
+:100900003AE59EC3947A503390E67CE538F075382A
+:1009100000E4F00521E52194004020E59E94185064
+:100920001A759E00752100753800B2035388BF7593
+:100930003D0090E65EE04404F080224388400538A4
+:10094000801B53A8F75388F75388BF753800C2033C
+:10095000752100753D0290E65EE04404F0D0D0D0F1
+:1009600082D083D0E03290E678E04440F07F01EF1F
+:1009700014603E0460030209FF90E7C074FFF0A317
+:10098000E59BF090E678E090E7C2F090E678E0F43E
+:1009900030E604E04440F0E490E679F00000000026
+:1009A000000090E678E090E7C3F090E68F7405F0E1
+:1009B00022E533700D90E7C07410F090E68F74015B
+:1009C000F022759B80759DE7759EC090E67BE09058
+:1009D000E67CF0E5372406FFE433FEAD9BED248092
+:1009E000FDE434FFFCC3ED9FEE6480F8EC64809876
+:1009F00040D9E537240690E68FF05380F7D20222E3
+:100A00001201000200000040470502100000010230
+:100A100000010A0600020000004001000902270050
+:100A2000010100A0320904000003FF0000000705D7
+:100A30000102400000070581024000000705820214
+:100A400000040009022E00010100A0320904000088
+:100A500004FF000000070502024000000705040231
+:100A6000400000070586024000000705880240009C
+:100A7000000403090410034E00650078007400634D
+:100A8000006F006D001603480044002D0055005310
+:100A90000042002000440056005200000090E60B87
+:100AA0007403F000000090E6047480F0000000740D
+:100AB00002F000000014F0000000E4F000000090DC
+:100AC000E6007412F090E6017443F090E61074A012
+:100AD000F090E68D7401F090E61174A0F090E618A5
+:100AE000740DF090E6137420F090E614F090E61583
+:100AF000F090E61274E8F0E490E602F00000009056
+:100B0000E603F090E670F090E609F000000090E651
+:100B1000207404F0000000E490E621F00000009052
+:100B2000E630749AF0000000E490E631F000000036
+:100B300022C0E0C083C082C0D053C8FBE538D39444
+:100B400000400D75CDFE75CC6F153843C80480622A
+:100B5000E59EC39533502790E67CE0F538E5883074
+:100B6000E40AE535F452805388EF8007E53542808A
+:100B700043881075CDFE75CC6F43C8048034759ED4
+:100B800000E537B47719753700759DE1059E753816
+:100B90000075CDFE75CC6F43881043C80480135395
+:100BA00088EF53C8FB90E68DE4F0E535F4528043BE
+:100BB000A80853C87FD0D0D082D083D0E032787FCD
+:100BC000E4F6D8FD75813D020C05020319E493A3F8
+:100BD000F8E493A34003F68001F208DFF48029E4EF
+:100BE00093A3F85407240CC8C333C4540F4420C83B
+:100BF000834004F456800146F6DFE4800B010204D2
+:100C00000810204080900FB9E47E019360BCA3FFE0
+:100C1000543F30E509541FFEE493A360010ECF5406
+:100C2000C025E060A840B8E493A3FAE493A3F8E4F5
+:100C300093A3C8C582C8CAC583CAF0A3C8C582C861
+:100C4000CAC583CADFE9DEE780BEC0E0C0F0C0836A
+:100C5000C082C0D075D000C000C001C002C003C0B7
+:100C600004C005C006C00790E65F7404F05391EF1E
+:100C7000300605120CBF802CE53D7005F59E43A89B
+:100C800008438008E53D6401601AE53DB4020512A1
+:100C90000CBF801090E7C074CCF0A3E539F090E66B
+:100CA0008F7440F0D007D006D005D004D003D00216
+:100CB000D001D000D0D0D082D083D0F0D0E0327537
+:100CC0009AE720062AE490E7C0F0A3E59EF0A3741B
+:100CD000A0F0A37402F0A37497F0E4A3F0759BC690
+:100CE000F59E90E7C1E0D3943A400CD20680087597
+:100CF0009BC0C206759E3A90E7C1E0FF90E67CE09B
+:100D000090E67BF0AE9BEEC3748094815006E59E26
+:100D1000C39F40E890E68F7440F05380F7E4F53DC0
+:100D2000228E1C8F1D90E600E054187012E51D24E1
+:100D300001FFE4351CC313F51CEF13F51D8015905E
+:100D4000E600E05418FFBF100BE51D25E0F51DE59A
+:100D50001C33F51CE51D151DAE1C7002151C4E60E4
+:100D6000051207EF80EE2290E678E0F430E62DE001
+:100D70004480F0E53224F0601C24087024E533B48C
+:100D8000010CE536B4010790E67974A1F02290E6F3
+:100D90007974A0F02290E679E534F022E4FF12099C
+:100DA0006F22C0E0C083C08290E680E030E70E850D
+:100DB0002226852327852A28852B29800C852A260B
+:100DC000852B278522288523295391EF90E65D7492
+:100DD00010F0D082D083D0E032C0E0C083C08290D7
+:100DE000E680E030E70E852226852327852A2885A0
+:100DF0002B29800C852A26852B27852228852329C7
+:100E00005391EF90E65D7420F0D082D083D0E03231
+:100E1000EB9FF5F0EA9E42F0E99D42F0E89C45F038
+:100E200022D083D082F8E4937012740193700DA3E2
+:100E3000A393F8740193F5828883E4737402936832
+:100E400060EFA3A3A380DF30070990E680E0440AA7
+:100E5000F0800790E680E04408F07FDC7E05120D0C
+:100E60002190E65D74FFF090E65FF05391EF90E61D
+:100E700080E054F7F022C0E0C083C082C0D0E59E7D
+:100E8000C3947A501543884043800890E68FE4F07D
+:100E900090E65EE054FBF0753D01D0D0D082D08367
+:100EA000D0E03290E682E030E004E020E60B90E60D
+:100EB00082E030E119E030E71590E680E04401F08F
+:100EC0007F147E00120D2190E680E054FEF022A9EE
+:100ED00007AE2FAF308F828E83A3E064037017AD0F
+:100EE0000119ED7001228F828E83E07C002FFDECD2
+:100EF0003EFEAF0580DF7E007F0022C0E0C0D0E56F
+:100F000036D394004011E58055356007E535F4523D
+:100F1000808004E5354280D0D0D0E032C0E0C0838C
+:100F2000C0825380F77539015391BF90E651E05468
+:100F3000FEF0D082D083D0E032C0E0C083C082D245
+:100F4000015391EF90E65D7401F0D082D083D0E040
+:100F500032C0E0C083C082D2055391EF90E65D7449
+:100F600008F0D082D083D0E032C0E0C083C082538A
+:100F700091EF90E65D7404F0D082D083D0E032C06F
+:100F8000E0C083C0825391EF90E65D7402F0D0829E
+:100F9000D083D0E03290E740E53CF0E490E68AF080
+:100FA00090E68B04F0D32290E740E531F0E490E640
+:100FB0008AF090E68B04F0D322C182013D00C10685
+:100FC000013900000001020203030404050590E654
+:100FD000BAE0F53CD32290E6BAE0F531D32253D8FB
+:100FE000EF32D322D322D322D322D322D322D3222D
+:100FF00022323232323232323232323232323232E1
+:1010000032323232323232323232323232323232C0
+:101010003232AAAAAAAAAAAAAAAAAAAAAAAAAAAA20
+:10102000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA20
+:10103000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA10
+:10104000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA00
+:10105000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF0
+:10106000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE0
+:10107000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD0
+:10108000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC0
+:10109000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB0
+:1010A000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0
+:1010B000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA90
+:1010C000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA80
+:1010D000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA70
+:1010E000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA60
+:1010F000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA50
+:10110000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3F
+:10111000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2F
+:10112000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1F
+:10113000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0F
+:10114000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFF
+:10115000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEF
+:10116000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADF
+:10117000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACF
+:10118000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABF
+:10119000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF
+:1011A000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9F
+:1011B000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8F
+:1011C000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7F
+:1011D000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6F
+:1011E000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5F
+:1011F000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4F
+:10120000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3E
+:10121000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2E
+:10122000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1E
+:10123000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0E
+:10124000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFE
+:10125000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE
+:10126000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADE
+:10127000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACE
+:10128000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABE
+:10129000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE
+:1012A000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9E
+:1012B000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8E
+:1012C000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7E
+:1012D000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6E
+:1012E000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5E
+:1012F000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4E
+:10130000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3D
+:10131000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2D
+:10132000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1D
+:10133000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0D
+:10134000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFD
+:10135000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAED
+:10136000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADD
+:10137000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD
+:10138000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABD
+:10139000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD
+:1013A000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9D
+:1013B000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8D
+:1013C000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7D
+:1013D000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6D
+:1013E000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5D
+:1013F000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4D
+:10140000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3C
+:10141000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2C
+:10142000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1C
+:10143000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0C
+:10144000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFC
+:10145000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEC
+:10146000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADC
+:10147000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACC
+:10148000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABC
+:10149000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC
+:1014A000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9C
+:1014B000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8C
+:1014C000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7C
+:1014D000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6C
+:1014E000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5C
+:1014F000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4C
+:10150000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3B
+:10151000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2B
+:10152000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1B
+:10153000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0B
+:10154000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFB
+:10155000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEB
+:10156000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADB
+:10157000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACB
+:10158000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB
+:10159000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB
+:1015A000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9B
+:1015B000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8B
+:1015C000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7B
+:1015D000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6B
+:1015E000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5B
+:1015F000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4B
+:10160000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3A
+:10161000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2A
+:10162000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1A
+:10163000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0A
+:10164000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFA
+:10165000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEA
+:10166000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADA
+:10167000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA
+:10168000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABA
+:10169000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+:1016A000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9A
+:1016B000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8A
+:1016C000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7A
+:1016D000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6A
+:1016E000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5A
+:1016F000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4A
+:10170000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA39
+:10171000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA29
+:10172000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA19
+:10173000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA09
+:10174000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF9
+:10175000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9
+:10176000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD9
+:10177000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC9
+:10178000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB9
+:10179000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9
+:1017A000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA99
+:1017B000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA89
+:1017C000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA79
+:1017D000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA69
+:1017E000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA59
+:1017F000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA49
+:10180000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA38
+:10181000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA28
+:10182000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA18
+:10183000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA08
+:10184000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF8
+:10185000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE8
+:10186000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8
+:10187000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC8
+:10188000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8
+:10189000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8
+:1018A000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA98
+:1018B000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA88
+:1018C000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA78
+:1018D000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA68
+:1018E000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA58
+:1018F000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA48
+:10190000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA37
+:10191000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA27
+:10192000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA17
+:10193000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA07
+:10194000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF7
+:10195000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE7
+:10196000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD7
+:10197000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC7
+:10198000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB7
+:10199000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7
+:1019A000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA97
+:1019B000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA87
+:1019C000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA77
+:1019D000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA67
+:1019E000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA57
+:1019F000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA47
+:101A0000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA36
+:101A1000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA26
+:101A2000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA16
+:101A3000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA06
+:101A4000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF6
+:101A5000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE6
+:101A6000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD6
+:101A7000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC6
+:101A8000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB6
+:101A9000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6
+:101AA000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA96
+:101AB000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA86
+:101AC000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA76
+:101AD000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA66
+:101AE000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA56
+:101AF000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA46
+:101B0000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA35
+:101B1000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA25
+:101B2000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA15
+:101B3000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA05
+:101B4000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF5
+:101B5000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE5
+:101B6000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD5
+:101B7000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC5
+:101B8000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5
+:101B9000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5
+:101BA000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA95
+:101BB000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA85
+:101BC000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA75
+:101BD000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA65
+:101BE000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA55
+:101BF000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA45
+:101C0000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA34
+:101C1000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA24
+:101C2000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA14
+:101C3000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA04
+:101C4000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF4
+:101C5000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE4
+:101C6000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4
+:101C7000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC4
+:101C8000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4
+:101C9000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4
+:101CA000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA94
+:101CB000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA84
+:101CC000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA74
+:101CD000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA64
+:101CE000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA54
+:101CF000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA44
+:101D0000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA33
+:101D1000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA23
+:101D2000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA13
+:101D3000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA03
+:101D4000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF3
+:101D5000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE3
+:101D6000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD3
+:101D7000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC3
+:101D8000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB3
+:101D9000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3
+:101DA000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA93
+:101DB000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA83
+:101DC000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA73
+:101DD000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA63
+:101DE000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA53
+:101DF000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA43
+:101E0000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA32
+:101E1000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA22
+:101E2000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA12
+:101E3000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA02
+:101E4000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF2
+:101E5000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE2
+:101E6000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2
+:101E7000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC2
+:101E8000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2
+:101E9000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2
+:101EA000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA92
+:101EB000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA82
+:101EC000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA72
+:101ED000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA62
+:101EE000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA52
+:101EF000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA42
+:101F0000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA31
+:101F1000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA21
+:101F2000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA11
+:101F3000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA01
+:101F4000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF1
+:101F5000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE1
+:101F6000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD1
+:101F7000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC1
+:101F8000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB1
+:101F9000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1
+:101FA000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA91
+:101FB000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA81
+:101FC000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA71
+:101FD000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA61
+:101FE000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA51
+:101FF000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA41
Index: contrib/r5000/r5ktest.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ contrib/r5000/r5ktest.c	2008-11-27 08:45:29.000000000 -0800
@@ -0,0 +1,346 @@
+/* Copyright 2007  Alan Nisota <alannisota@gmail.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, or (at your option) any later version.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <getopt.h>
+#include "r5000/r5000.h"
+
+enum {
+  TOGGLE_POWER,
+  CHANGE_CHANNEL,
+  READ_STREAM,
+  MULTI_READ,
+  MULTI_OPEN,
+  OPEN_CLOSE,
+  SCAN_DEVICES,
+  TURN_ON,
+  TURN_OFF,
+  MAX_CMD,
+};
+char cmd_names[][20] = {
+  {"TOGGLE_POWER"},
+  {"CHANGE_CHANNEL"},
+  {"READ_STREAM"},
+  {"MULTI_READ"},
+  {"MULTI_OPEN"},
+  {"OPEN_CLOSE"},
+  {"SCAN_DEVICES"},
+  {"TURN_ON"},
+  {"TURN_OFF"},
+};
+
+char stb_names[][20] = {
+  {"vip211"},
+  {"directv"},
+  {"hdd"},
+  {"dsr"},
+  {"vip622"},
+  {0},
+};
+
+extern char strmfile[255];
+void help(char *cmdline) {
+  int i;
+  printf("%s <-t type> <--m command> [--c channel] [--s serial] [-h]\n", cmdline);
+  printf("\t-t/--type type     : set STB type to type.  'type' can be either\n");
+  printf("\t                     a numerical value or string.  Valid types are:\n");
+  for(i = 0; stb_names[i][0] != 0; i++) {
+    printf("\t\t%d: %s\n", i, stb_names[i]);
+  }
+  printf("\t-m/--cmd command   : set command to execute.  'command' can be either\n");
+  printf("\t                     a numerical value or string.  Valid commands are:\n");
+  for(i = 0; i < MAX_CMD; i++) {
+    printf("\t\t%d: %s\n", i, cmd_names[i]);
+  }
+  printf("\t-c/--channel       : set channel to read (this does NOT tune\n");
+  printf("\t-s/--serial serial : set serial number\n");
+  printf("\t-h/--help          : show this help message\n");
+  exit(0);
+}
+
+void printstr(char *str)
+{
+  fprintf(stderr, "%s", str);
+}
+
+static unsigned char buffer[1000189];
+static unsigned char *ptr = buffer;
+unsigned int r5000_device_tspacket_handler(unsigned char *tspacket, int len, void *callback_data)
+{
+    int fd = *(int *) callback_data;
+    if (len <= 0)
+      return 0;
+    if(memcpy(ptr, tspacket, len));
+    ptr+=len;
+    if(ptr-buffer > 1000000) {
+      write(fd, buffer, ptr-buffer);
+      ptr = buffer;
+    }
+    return 1;
+}
+
+int main(int argc, char *argv[])
+{
+  r5kdev_t *usbdev = NULL;
+  int glblfd, i, j;
+  unsigned char buf[0x80];
+  int stb = -1;
+  int cmd = -1;
+  const char *channel = NULL;
+  const char *serial = NULL;
+  int runtime = 10;
+  struct option long_options[] = {
+    {"type", required_argument, NULL, 't'},
+    {"cmd", required_argument, NULL, 'm'},
+    {"channel", required_argument, NULL, 'c'},
+    {"serial", required_argument, NULL, 's'},
+    {"time", required_argument, NULL, 'i'},
+    {"dbgfile", required_argument, NULL, 'D'},
+    {"help", no_argument , NULL, 'h'},
+    {0, 0, 0, 0}
+  };
+  while (1) {
+    char c;
+    c = getopt_long (argc, argv,
+                     "t:m:c:s:hD:i:",
+                     long_options, NULL);
+    if(c == EOF)
+      break;
+    switch(c) {
+      case 't':
+      {
+        char *ptr;
+        int i;
+        stb = strtol(optarg, &ptr, 10);
+        if(ptr == optarg) {
+          stb = -1;
+          for(i = 0; stb_names[i][0] != 0; i++) {
+            if(strncasecmp(optarg, stb_names[i], strlen(stb_names[i])) == 0) {
+              stb = i;
+              break;
+            }
+          }
+          if(stb == -1) {
+            fprintf(stderr, "Unknown STB type: %s\n", optarg);
+            exit(-1);
+          }
+        } else if(stb < 0 || stb >= R5K_STB_MAX) {
+          fprintf(stderr, "Illegal STB type: %d\n", stb);
+          exit(-1);
+        }
+        break;
+      }
+      case 'm':
+      {
+        char *ptr;
+        int i;
+        cmd = strtol(optarg, &ptr, 10);
+        if(ptr == optarg) {
+          cmd = -1;
+          for(i = 0; i < MAX_CMD; i++) {
+            if(strncasecmp(optarg, cmd_names[i], strlen(cmd_names[i])) == 0) {
+              cmd = i;
+              break;
+            }
+          }
+          if(cmd == -1) {
+            fprintf(stderr, "Unknown command: %d\n", optarg);
+            exit(-1);
+          }
+        } else if(cmd < 0 || cmd >= MAX_CMD) {
+          fprintf(stderr, "Illegal command: %d\n", cmd);
+          exit(-1);
+        }
+        break;
+      }
+      case 'c':
+      {
+        int ok = 1;
+        char *p;
+        if(! strlen(optarg)) {
+          fprintf(stderr, "Couldn't parse channel '%s'\n", channel);
+          ok = 0;
+        }
+        for(p = optarg; *p; p++) {
+          if(*p < '0' || *p > '9') {
+            fprintf(stderr, "Couldn't parse channel '%s'\n", channel);
+            ok = 0;
+            break;
+          }
+        }
+        if(ok)
+          channel = optarg;
+        break;
+      }
+      case 'i':
+      {
+        char *ptr;
+        runtime = strtol(optarg, &ptr, 0);
+        if(optarg == ptr) {
+          fprintf(stderr, "Couldn't parse time '%s'\n", optarg);
+        }
+        break;
+      }
+      case 's':
+        serial = optarg;
+        break;
+      case 'h':
+        help(argv[0]);
+        break;
+      case 'D':
+        #ifdef R5K_DEBUG
+          strncpy(strmfile, optarg, 255);
+        #else
+          fprintf(stderr, "--dbgfile only supported in debug mode.\nMake sure you know what you are doing!\n");
+          exit(-1);
+        #endif
+    }
+  }
+  if(cmd == -1 || stb == -1) {
+    fprintf(stderr, "Must specify --cmd and --stb\n");
+    exit(-1);
+  }
+  r5000_init(printstr);
+  if(cmd == SCAN_DEVICES) {
+    r5kenum_t devs;
+    printf("Scanning for R5000 devices\n");
+    if(! r5000_find_stbs(&devs)) {
+      printf("Failed to initialize r5000 devices\n");
+    }
+    for(i=0; i < devs.count; i++) {
+      printf("Found: %s\n", devs.serial[i]);
+    }
+    goto end;
+  }
+  usbdev = r5000_open(stb, r5000_device_tspacket_handler, &glblfd, serial);
+  if(! usbdev) {
+    fprintf(stderr, "R5000 device could not be found/opened\n");
+    exit(-1);
+  }
+  glblfd=open("raw.ts", O_WRONLY | O_TRUNC | O_CREAT, 0666);
+  if(cmd == TOGGLE_POWER) {
+    int new_state;
+    printf("Toggling On/Off\n");
+    new_state = r5000_toggle_on_off(usbdev);
+    printf("Turned power %s\n", new_state ? "On" : "Off");
+  }
+  if(cmd == TURN_ON) {
+    int new_state;
+    printf("Turning STB On\n");
+    new_state = r5000_power_on_off(usbdev, 1);
+    printf("Turned power %s\n", new_state ? "On" : "Off");
+  }
+  if(cmd == TURN_OFF) {
+    int new_state;
+    printf("Turning STB Off\n");
+    new_state = r5000_power_on_off(usbdev, 0);
+    printf("Turned power %s\n", new_state ? "On" : "Off");
+  }
+  if(cmd == CHANGE_CHANNEL) {
+    if(! channel) {
+      printf("Must specify a channel when using '-m CHANGE_CHANNEL'\n");
+      exit(-1);
+    }
+    printf("Setting channel %s\n", channel);
+    r5000_change_channel(usbdev, channel, 0);
+  }
+  if(cmd == OPEN_CLOSE) {
+    int on_off;
+    printf("Doing open/close\n");
+    on_off = r5000_get_power_state(usbdev);
+    printf("State1: %d\n", on_off);
+    r5000_close(usbdev);
+    printf("Closed\n");
+    sleep(1);
+    usbdev = r5000_open(stb, r5000_device_tspacket_handler, &glblfd, serial);
+    printf("ReOpened\n");
+    on_off = r5000_get_power_state(usbdev);
+    printf("State2: %d\n", on_off);
+  }
+  if(cmd == READ_STREAM || cmd == MULTI_READ) {
+    if(channel) {
+      uint c;
+      char *ptr;
+      printf("Setting channel %d\n");
+      c = strtol(channel, &ptr, 0);
+      r5000_change_channel(usbdev, NULL, c);
+    }
+    printf("Reading stream\n");
+    r5000_start_stream(usbdev);
+    time_t t = time(NULL);
+    while (time(NULL) - t < runtime)
+    {
+      // This will timeout after 1ms regardless of data availability
+      //usleep(10000);
+      r5000_loop_iterate(usbdev, 10);
+    }
+    r5000_stop_stream(usbdev);
+  }
+  if(cmd == MULTI_READ) {
+    printf("Reading stream again\n");
+    close(glblfd);
+    glblfd=open("raw_1.ts", O_WRONLY | O_TRUNC | O_CREAT, 0666);
+    sleep(1);
+    r5000_start_stream(usbdev);
+    time_t t = time(NULL);
+    while (time(NULL) - t < 10)
+    {
+      // This will timeout after 1ms regardless of data availability
+      //usleep(10000);
+      r5000_loop_iterate(usbdev, 10);
+    }
+    r5000_stop_stream(usbdev);
+  }
+  if(cmd == MULTI_OPEN) {
+    printf("Doing multi-open\n");
+    while(usbdev) {
+      time_t t;
+      r5000_start_stream(usbdev);
+      t = time(NULL);
+      while (time(NULL) - t < 2)
+      {
+        // This will timeout after 1ms regardless of data availability
+        //usleep(10000);
+        r5000_loop_iterate(usbdev, 10);
+      }
+      r5000_stop_stream(usbdev);
+      r5000_close(usbdev);
+      printf("Closed\n");
+      sleep(1);
+      usbdev = r5000_open(stb, r5000_device_tspacket_handler, &glblfd, serial);
+      printf("ReOpened %s\n", serial);
+    }
+    r5000_start_stream(usbdev);
+    time_t t = time(NULL);
+    while (time(NULL) - t < 10)
+    {
+      // This will timeout after 1ms regardless of data availability
+      //usleep(10000);
+      r5000_loop_iterate(usbdev, 10);
+    }
+    r5000_stop_stream(usbdev);
+  }
+end:
+  r5000_close(usbdev);
+  if(ptr != buffer)
+    write(glblfd, buffer, ptr-buffer);
+  close(glblfd);
+}
Index: libs/libmythtv/r5000/r5k_vip_buttons.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libs/libmythtv/r5000/r5k_vip_buttons.h	2008-11-27 08:11:51.000000000 -0800
@@ -0,0 +1,110 @@
+struct r5000_buttons vip_button_cmd =
+{
+0x40, //len
+400000, //delay
+//button 0:
+{
+ 0x00, 0x23, 0x0a, 0x80, 0x00, 0x77, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x1a,
+ 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x1a,
+ 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x1a,
+ 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e,
+},
+//button 1:
+{
+ 0x00, 0x23, 0x0a, 0x80, 0x00, 0x77, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e,
+ 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x1a,
+ 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a,
+ 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e,
+},
+//button 2:
+{
+ 0x00, 0x23, 0x0a, 0x80, 0x00, 0x77, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e,
+ 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x1a,
+ 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a,
+ 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e,
+},
+//button 3:
+{
+ 0x00, 0x23, 0x0a, 0x80, 0x00, 0x77, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e,
+ 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x1a,
+ 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a,
+ 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e,
+},
+//button 4:
+{
+ 0x00, 0x23, 0x0a, 0x80, 0x00, 0x77, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x1a,
+ 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x1a,
+ 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e,
+ 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e,
+},
+//button 5:
+{
+ 0x00, 0x23, 0x0a, 0x80, 0x00, 0x77, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x1a,
+ 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x1a,
+ 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e,
+ 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e,
+},
+//button 6:
+{
+ 0x00, 0x23, 0x0a, 0x80, 0x00, 0x77, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x1a,
+ 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x1a,
+ 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e,
+ 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e,
+},
+//button 7:
+{
+ 0x00, 0x23, 0x0a, 0x80, 0x00, 0x77, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e,
+ 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x1a,
+ 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e,
+ 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e,
+},
+//button 8:
+{
+ 0x00, 0x23, 0x0a, 0x80, 0x00, 0x77, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e,
+ 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x1a,
+ 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e,
+ 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e,
+},
+//button 9:
+{
+ 0x00, 0x23, 0x0a, 0x80, 0x00, 0x77, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e,
+ 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x1a,
+ 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e,
+ 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e,
+},
+//Clear:
+{
+ 0x00, 0x23, 0x0a, 0x80, 0x00, 0x77, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x1a,
+ 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x1a,
+ 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x1a,
+ 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e,
+},
+//Enter:
+{
+ 0x00, 0x23, 0x0a, 0x80, 0x00, 0x77, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x1a,
+ 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x1a,
+ 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x1a,
+ 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e,
+},
+//Power
+{
+ 0x00, 0x23, 0x0a, 0x80, 0x00, 0x77, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a,
+ 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x1a,
+ 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a,
+ 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e,
+},
+//Power-On
+{
+ 0x00, 0x23, 0x0a, 0x80, 0x00, 0x77, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a,
+ 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x1a,
+ 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a,
+ 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e,
+},
+//Power-Off
+{
+ 0x00, 0x23, 0x0a, 0x80, 0x00, 0x77, 0x04, 0x3a, 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e,
+ 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x1a,
+ 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x3a, 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x1a,
+ 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e,
+},
+};
Index: contrib/r5000/README
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ contrib/r5000/README	2008-11-27 07:41:18.000000000 -0800
@@ -0,0 +1,77 @@
+ehci-2.6.25.patch:
+  This patch is needed for all kernels earlier than 2.6.26.  It adds support
+for 1024 byte frames which the R5000 needs.  As of 2.6.26 the patch is already
+in the kernel.
+
+r5ktest:
+Compile r5ktest via:
+  gcc -g -o r5ktest -I../../libs/libmythtv r5ktest.c ../../libs/libmythtv/r5000/*.c -lusb
+
+###############################################################################
+###############################################################################
+In order to use the R5000 in linux, you must 1st load the firmware into the
+device.
+Make sure you have the following installed:
+  lsusb (in Debian/Ubuntu this is in the usbutils package)
+  fxload
+1) Locate the device using lsusb:
+lsusb
+...
+Bus 001 Device 026: ID 04b4:8613 Cypress Semiconductor Corp. CY7C68013 EZ-USB FX2 USB 2.0 Development Kit
+2)Install the firmware (this often needs to be done via sudo/root)
+(depending on where usbfs is mounted):
+fxload -t fx2 -D /dev/bus/usb/001/026 -I r5000.hex -m 0666
+or
+fxload -t fx2 -D /proc/bus/usb/001/026 -I r5000.hex -m 0666
+(note that 001 is the Bus and 026 is the Device from lsusb)
+
+3) Ensure that the firmware loaded:
+lsusb
+...
+Bus 001 Device 028: ID 0547:1002 Anchor Chips, Inc.
+(Note that is has a completely different name now)
+
+4)ensure that the usbtest module did not load:
+lsmod | grep usbtest
+If you see the usbtest module you'll need to blacklist it and/or rmmod it
+###############################################################################
+###############################################################################
+
+Before trying to use the R5000 in mythtv, make sure it is working in r5ktest
+./r5ktest -t 0 -m 6
+Scanning for R5000 devices
+Found: xxxxxxx
+
+If you see something like:
+    R5000 initialization failed at stage 1:
+	Expected 1 bytes, but got -1 bytes
+    R5000 failed to locate any R5000 devices.  Are you sure you have
+        permissions set properly?
+Then you likely do not have permissions to access the usb device.  Try running
+as root.
+
+next do:
+./r5ktest --help
+and choose the correct type for your STB:
+   vip211 : VIP211 and VIP411
+   vip622 : VIP622, VIP722, BEV9242
+   hdd:
+   dsr:
+   directv:
+
+Now try a few commands to make sure they work properly:
+   ./r5ktest --type vip211 --cmd TURN_ON
+   ./r5ktest --type vip211 --cmd TURN_OFF
+   ./r5ktest --type vip211 --cmd CHANGE_CHANNEL -c <channel number>
+   ./r5ktest --type vip211 --cmd OPEN_CLOSE
+
+###############################################################################
+###############################################################################
+
+Configure mythtv:
+Go into myth-setup and select the R5000 device, and the relevant STB.
+Configure a Video Source
+Configure an Input
+To enter channels, the easiest way is to set up scehdules-direct with the provider/channels you receieve then import them from the Card-Input menu
+If you choose to enter them yourself, make sure to set the frequency to whatever needs to be punched into the remote to select the channel
+

