Ticket #4752: r5000_r2.patch

File r5000_r2.patch, 89.6 KB (added by alannisota@…, 16 years ago)

Rev 2 of the Myth patch

  • configure

    old new  
    158158  echo "  --disable-v4l            disable Video4Linux support"
    159159  echo "  --disable-ivtv           disable ivtv support (PVR-x50) req. v4l support"
    160160  echo "  --disable-dvb            disable DVB support"
     161  echo "  --disable-r5000          disable support for R5000 USB STBs"
    161162  echo "  --dvb-path=HDRLOC        location of directory containing"
    162163  echo "                           'linux/dvb/frontend.h', not the"
    163164  echo "                           directory with frontend.h [$dvb_path]"
     
    870871    hdhomerun
    871872    iptv
    872873    ivtv
     874    r5000
    873875    joystick_menu
    874876    lirc
    875877    opengl_vsync
     
    10221024dbox2_deps="backend"
    10231025dvb_deps="backend"
    10241026firewire_deps="backend"
     1027r5000_deps="backend"
    10251028iptv_deps="backend"
    10261029ivtv_deps="backend v4l"
    10271030hdhomerun_deps="backend"
     
    11531156hdhomerun="yes"
    11541157iptv="yes"
    11551158ivtv="yes"
     1159r5000="yes"
    11561160joystick_menu="default"
    11571161lamemp3="yes"
    11581162lirc="yes"
     
    29282932  echo "DBox2 support             ${dbox2-no}"
    29292933  echo "HDHomeRun support         ${hdhomerun-no}"
    29302934  echo "IPTV support              ${iptv-no}"
     2935  echo "R5000 support             ${r5000-no}"
    29312936fi
    29322937
    29332938if enabled frontend; then
  • libs/libmythtv/cardutil.h

    old new  
    5353        FIREWIRE,
    5454        HDHOMERUN,
    5555        FREEBOX,
     56        R5000,
    5657    };
    5758
    5859    static enum CARD_TYPES toCardType(const QString &name)
     
    8182            return HDHOMERUN;
    8283        if ("FREEBOX" == name)
    8384            return FREEBOX;
     85        if ("R5000" == name)
     86            return R5000;
    8487        return ERROR_UNKNOWN;
    8588    }
    8689
     
    8992        return
    9093            (rawtype != "DVB")       &&
    9194            (rawtype != "FIREWIRE")  && (rawtype != "DBOX2")   &&
    92             (rawtype != "HDHOMERUN") && (rawtype != "FREEBOX");
     95            (rawtype != "HDHOMERUN") && (rawtype != "FREEBOX") &&
     96            (rawtype != "R5000");
    9397    }
    9498
    9599    static bool         IsUnscanable(const QString &rawtype)
    96100    {
    97101        return
    98             (rawtype == "FIREWIRE")  || (rawtype == "DBOX2");
     102            (rawtype == "FIREWIRE")  || (rawtype == "DBOX2") ||
     103            (rawtype == "R5000");
    99104    }
    100105
    101106    static bool         IsEITCapable(const QString &rawtype)
  • libs/libmythtv/libmythtv.pro

    old new  
    489489        DEFINES += USING_DVB
    490490    }
    491491
     492    #Support for R5000 usb device
     493    using_r5000 {
     494        HEADERS += r5000channel.h           r5000recorder.h
     495        HEADERS += r5000signalmonitor.h     r5000device.h
     496        HEADERS += r5000/r5000.h            r5000/libusb_augment.h
     497
     498        SOURCES += r5000channel.cpp         r5000recorder.cpp
     499        SOURCES += r5000signalmonitor.cpp   r5000device.cpp
     500        SOURCES += r5000/r5000.c            r5000/libusb_augment.c
     501
     502        LIBS += -lusb
     503        DEFINES += USING_R5000
     504    }
     505
    492506    DEFINES += USING_BACKEND
    493507}
    494508
  • libs/libmythtv/signalmonitor.h

    old new  
    297297            (cardtype.upper() == "HDTV")      ||
    298298            (cardtype.upper() == "HDHOMERUN") ||
    299299            (cardtype.upper() == "FIREWIRE")  ||
    300             (cardtype.upper() == "FREEBOX"));
     300            (cardtype.upper() == "FREEBOX")   ||
     301            (cardtype.upper() == "R5000"));
    301302}
    302303
    303304inline bool SignalMonitor::IsSupported(const QString &cardtype)
  • libs/libmythtv/signalmonitor.cpp

    old new  
    4141#   include "firewirechannel.h"
    4242#endif
    4343
     44#ifdef USING_R5000
     45#   include "r5000signalmonitor.h"
     46#   include "r5000channel.h"
     47#endif
     48
    4449#undef DBG_SM
    4550#define DBG_SM(FUNC, MSG) VERBOSE(VB_CHANNEL, \
    4651    "SM("<<channel->GetDevice()<<")::"<<FUNC<<": "<<MSG);
     
    127132            signalMonitor = new FirewireSignalMonitor(db_cardnum, fc);
    128133    }
    129134#endif
     135#ifdef USING_R5000
     136    if (cardtype.upper() == "R5000")
     137    {
     138        R5000Channel *fc = dynamic_cast<R5000Channel*>(channel);
     139        if (fc)
     140            signalMonitor = new R5000SignalMonitor(db_cardnum, fc);
     141    }
     142#endif
    130143
    131144    if (!signalMonitor)
    132145    {
  • libs/libmythtv/cardutil.cpp

    old new  
    14571457    if (("FIREWIRE"  == cardtype) ||
    14581458        ("FREEBOX"   == cardtype) ||
    14591459        ("DBOX2"     == cardtype) ||
    1460         ("HDHOMERUN" == cardtype))
     1460        ("HDHOMERUN" == cardtype) ||
     1461        ("R5000"     == cardtype))
    14611462    {
    14621463        ret += "MPEG2TS";
    14631464    }
     
    15821583    if (("FIREWIRE"  == cardtype) ||
    15831584        ("FREEBOX"   == cardtype) ||
    15841585        ("DBOX2"     == cardtype) ||
    1585         ("HDHOMERUN" == cardtype))
     1586        ("HDHOMERUN" == cardtype) ||
     1587        ("R5000"     == cardtype))
    15861588    {
    15871589        inputs += "MPEG2TS";
    15881590    }
  • libs/libmythtv/videosource.cpp

    old new  
    3636#include "frequencies.h"
    3737#include "diseqcsettings.h"
    3838#include "firewiredevice.h"
     39#include "r5000device.h"
    3940#include "compat.h"
    4041
    4142
     
    12971298    }
    12981299};
    12991300
     1301class R5000Serial : public ComboBoxSetting, public CaptureCardDBStorage
     1302{
     1303  public:
     1304    R5000Serial(const CaptureCard &parent) :
     1305        ComboBoxSetting(this),
     1306        CaptureCardDBStorage(this, parent, "videodevice")
     1307    {
     1308        setLabel(QObject::tr("Serial #"));
     1309#ifdef USING_R5000
     1310        QStringList serials = R5000Device::GetSTBList();
     1311        for (uint i = 0; i < serials.size(); i++)
     1312        {
     1313            addSelection(serials[i]);
     1314        }
     1315#endif // USING_FIREWIRE
     1316    }
     1317};
     1318
     1319class R5000ConfigurationGroup : public VerticalConfigurationGroup
     1320{
     1321  public:
     1322    R5000ConfigurationGroup(CaptureCard& a_parent):
     1323       VerticalConfigurationGroup(false, true, false, false),
     1324       parent(a_parent)
     1325    {
     1326        setUseLabel(false);
     1327        addChild(new R5000Serial(parent));
     1328        addChild(new SignalTimeout(parent, 2000, 1000));
     1329        addChild(new ChannelTimeout(parent, 9000, 1750));
     1330        addChild(new SingleCardInput(parent));
     1331    };
     1332
     1333  private:
     1334    CaptureCard &parent;
     1335};
     1336
    13001337class IPTVHost : public LineEditSetting, public CaptureCardDBStorage
    13011338{
    13021339  public:
     
    14811518#ifdef USING_IPTV
    14821519    addTarget("FREEBOX",   new IPTVConfigurationGroup(parent));
    14831520#endif // USING_IPTV
     1521
     1522#ifdef USING_R5000
     1523    addTarget("R5000", new R5000ConfigurationGroup(parent));
     1524#endif // USING_R5000
    14841525}
    14851526
    14861527void CaptureCardGroup::triggerChanged(const QString& value)
     
    16681709#ifdef USING_IPTV
    16691710    setting->addSelection(QObject::tr("Network Recorder"), "FREEBOX");
    16701711#endif // USING_IPTV
     1712
     1713#ifdef USING_R5000
     1714    setting->addSelection(QObject::tr("R5000 Capable STB"), "R5000");
     1715#endif // USING_R5000
    16711716}
    16721717
    16731718class CardID : public SelectLabelSetting, public CardInputDBStorage
  • libs/libmythtv/tv_rec.cpp

    old new  
    5050#include "hdhrchannel.h"
    5151#include "iptvchannel.h"
    5252#include "firewirechannel.h"
     53#include "r5000channel.h"
    5354
    5455#include "recorderbase.h"
    5556#include "NuppelVideoRecorder.h"
     
    5960#include "hdhrrecorder.h"
    6061#include "iptvrecorder.h"
    6162#include "firewirerecorder.h"
     63#include "r5000recorder.h"
    6264
    6365#ifdef USING_V4L
    6466#include "channel.h"
     
    199201        init_run = true;
    200202#endif
    201203    }   
     204    else if (genOpt.cardtype == "R5000")
     205    {
     206#ifdef USING_R5000
     207        channel = new R5000Channel(this, genOpt.videodev, fwOpt);
     208        if (!channel->Open())
     209            return false;
     210        InitChannel(genOpt.defaultinput, startchannel);
     211        init_run = true;
     212#endif
     213    }
    202214    else // "V4L" or "MPEG", ie, analog TV
    203215    {
    204216#ifdef USING_V4L
     
    10171029        recorder->SetOption("mrl", genOpt.videodev);
    10181030#endif // USING_IPTV
    10191031    }
     1032    else if (genOpt.cardtype == "R5000")
     1033    {
     1034#ifdef USING_R5000
     1035        recorder = new R5000Recorder(this, GetR5000Channel());
     1036#endif // USING_R5000
     1037    }
    10201038    else
    10211039    {
    10221040#ifdef USING_V4L
     
    12301248#endif // USING_FIREWIRE
    12311249}
    12321250
     1251R5000Channel *TVRec::GetR5000Channel(void)
     1252{
     1253#ifdef USING_R5000
     1254    return dynamic_cast<R5000Channel*>(channel);
     1255#else
     1256    return NULL;
     1257#endif // USING_R5000
     1258}
     1259
    12331260Channel *TVRec::GetV4LChannel(void)
    12341261{
    12351262#ifdef USING_V4L
  • libs/libmythtv/transporteditor.cpp

    old new  
    735735        left->addChild(new Modulation(id, nType));
    736736    }
    737737    else if ((CardUtil::FIREWIRE == nType) ||
    738              (CardUtil::FREEBOX  == nType))
     738             (CardUtil::FREEBOX  == nType) ||
     739             (CardUtil::R5000    == nType))
    739740    {
    740741        left->addChild(new DTVStandard(id, true, true));
    741742    }
  • libs/libmythtv/tv_rec.h

    old new  
    3939class FirewireChannel;
    4040class Channel;
    4141class HDHRChannel;
     42class R5000Channel;
    4243
    4344class MPEGStreamData;
    4445class ProgramMapTable;
     
    281282    HDHRChannel  *GetHDHRChannel(void);
    282283    DVBChannel   *GetDVBChannel(void);
    283284    FirewireChannel *GetFirewireChannel(void);
     285    R5000Channel *GetR5000Channel(void);
    284286    Channel      *GetV4LChannel(void);
    285287
    286288    bool SetupSignalMonitor(bool enable_table_monitoring, bool notify);
  • new file libs/libmythtv/r5000channel.cpp

    - +  
     1/**
     2 *  R5000Channel
     3 *  Copyright (c) 2005 by Jim Westfall, Dave Abrahams
     4 *  Copyright (c) 2006 by Daniel Kristjansson
     5 *  Distributed as part of MythTV under GPL v2 and later.
     6 */
     7
     8#include "mythcontext.h"
     9#include "tv_rec.h"
     10#include "r5000channel.h"
     11
     12#define LOC QString("R5kChan(%1): ").arg(GetDevice())
     13#define LOC_WARN QString("R5kChan(%1), Warning: ").arg(GetDevice())
     14#define LOC_ERR QString("R5kChan(%1), Error: ").arg(GetDevice())
     15
     16R5000Channel::R5000Channel(TVRec *parent, const QString &_videodevice,const FireWireDBOptions &firewire_opts) :
     17    DTVChannel(parent),
     18    videodevice(_videodevice),
     19    fw_opts(firewire_opts),
     20    device(NULL),
     21    current_channel(0),
     22    isopen(false)
     23{
     24    device = new R5000Device(videodevice, 0, fw_opts.speed);
     25
     26    InitializeInputs();
     27}
     28
     29bool R5000Channel::SetChannelByString(const QString &channum)
     30{
     31    QString loc = LOC + QString("SetChannelByString(%1)").arg(channum);
     32    bool ok = true;
     33    VERBOSE(VB_CHANNEL, loc);
     34
     35    InputMap::const_iterator it = inputs.find(currentInputID);
     36    if (it == inputs.end())
     37        return false;
     38
     39    QString tvformat, modulation, freqtable, freqid, dtv_si_std;
     40    int finetune;
     41    uint64_t frequency;
     42    int mpeg_prog_num;
     43    uint atsc_major, atsc_minor, mplexid, tsid, netid;
     44    if (!ChannelUtil::GetChannelData(
     45        (*it)->sourceid, channum,
     46        tvformat, modulation, freqtable, freqid,
     47        finetune, frequency,
     48        dtv_si_std, mpeg_prog_num, atsc_major, atsc_minor, tsid, netid,
     49        mplexid, commfree))
     50    {
     51        VERBOSE(VB_IMPORTANT, loc + " " + QString(
     52                    "Requested channel '%1' is on input '%2' "
     53                    "which is in a busy input group")
     54                .arg(channum).arg(currentInputID));
     55
     56        return false;
     57    }
     58    uint mplexid_restriction;
     59    if (!IsInputAvailable(currentInputID, mplexid_restriction))
     60    {
     61        VERBOSE(VB_IMPORTANT, loc + " " + QString(
     62                    "Requested channel '%1' is on input '%2' "
     63                    "which is in a busy input group")
     64                .arg(channum).arg(currentInputID));
     65
     66        return false;
     67    }
     68
     69    uint ichan = freqid.toUInt(&ok);
     70    ok = isopen && SetChannelByNumber(ichan);
     71
     72    if (ok)
     73    {
     74        // Set the current channum to the new channel's channum
     75        curchannelname = QDeepCopy<QString>(channum);
     76        (*it)->startChanNum = QDeepCopy<QString>(channum);
     77    }
     78
     79    VERBOSE(VB_CHANNEL, loc + " " + ((ok) ? "success" : "failure"));
     80
     81    return ok;
     82}
     83
     84bool R5000Channel::Open(void)
     85{
     86    VERBOSE(VB_CHANNEL, LOC + "Open()");
     87
     88    if (inputs.find(currentInputID) == inputs.end())
     89        return false;
     90
     91    if (!device)
     92        return false;
     93
     94    if (isopen)
     95        return true;
     96
     97    if (!device->OpenPort())
     98        return false;
     99
     100    isopen = true;
     101
     102    return true;
     103}
     104
     105void R5000Channel::Close(void)
     106{
     107    VERBOSE(VB_CHANNEL, LOC + "Close()");
     108    if (isopen)
     109    {
     110        device->ClosePort();
     111        isopen = false;
     112    }
     113}
     114
     115QString R5000Channel::GetDevice(void) const
     116{
     117    return videodevice;
     118}
     119
     120bool R5000Channel::SetPowerState(bool on)
     121{
     122    if (!isopen)
     123    {
     124        VERBOSE(VB_IMPORTANT, LOC_ERR +
     125                "SetPowerState() called on closed R5000Channel.");
     126
     127        return false;
     128    }
     129
     130    return device->SetPowerState(on);
     131}
     132
     133R5000Device::PowerState R5000Channel::GetPowerState(void) const
     134{
     135    if (!isopen)
     136    {
     137        VERBOSE(VB_IMPORTANT, LOC_ERR +
     138                "GetPowerState() called on closed R5000Channel.");
     139
     140        return R5000Device::kAVCPowerQueryFailed;
     141    }
     142
     143    return device->GetPowerState();
     144}
     145
     146bool R5000Channel::Retune(void)
     147{
     148    VERBOSE(VB_CHANNEL, LOC + "Retune()");
     149
     150    if (R5000Device::kAVCPowerOff == GetPowerState())
     151    {
     152        VERBOSE(VB_IMPORTANT, LOC_ERR +
     153                "STB is turned off, must be on to retune.");
     154
     155        return false;
     156    }
     157
     158    if (current_channel)
     159        return SetChannelByNumber(current_channel);
     160
     161    return false;
     162}
     163
     164bool R5000Channel::SetChannelByNumber(int channel)
     165{
     166    VERBOSE(VB_CHANNEL, QString("SetChannelByNumber(%1)").arg(channel));
     167    current_channel = channel;
     168
     169    if (R5000Device::kAVCPowerOff == GetPowerState())
     170    {
     171        VERBOSE(VB_IMPORTANT, LOC_WARN +
     172                "STB is turned off, must be on to set channel.");
     173
     174        SetSIStandard("mpeg");
     175        SetDTVInfo(0,0,0,0,1);
     176
     177        return true; // signal monitor will call retune later...
     178    }
     179
     180    if (!device->SetChannel(fw_opts.model, 0, channel))
     181        return false;
     182
     183    SetSIStandard("mpeg");
     184    SetDTVInfo(0,0,0,0,1);
     185
     186    return true;
     187}
  • new file libs/libmythtv/r5000device.cpp

    - +  
     1/**
     2 *  R5000Device
     3 *  Copyright (c) 2008 by Alan Nisota
     4 *  Copyright (c) 2005 by Jim Westfall
     5 *  Distributed as part of MythTV under GPL v2 and later.
     6 */
     7
     8// Qt headers
     9#include <qdeepcopy.h>
     10
     11// MythTV headers
     12#include "r5000device.h"
     13#include "mythcontext.h"
     14#include "pespacket.h"
     15
     16#define LOC      QString("R5kDev: ")
     17#define LOC_WARN QString("R5kDev, Warning: ")
     18#define LOC_ERR  QString("R5kDev, Error: ")
     19
     20QMap<uint64_t,QString> R5000Device::s_id_to_model;
     21QMutex                 R5000Device::s_static_lock;
     22
     23unsigned int r5000_device_tspacket_handler(unsigned char *tspacket, int len, void *callback_data)
     24{
     25    R5000Device *fw = (R5000Device*) callback_data;
     26    if (! fw)
     27        return 0;
     28    if (len > 0)
     29        fw->BroadcastToListeners(tspacket, len);
     30    return 1;
     31}
     32
     33
     34class R5kPriv
     35{
     36  public:
     37    R5kPriv() :
     38        reset_timer_on(false),
     39        run_port_handler(false), is_port_handler_running(false),
     40        channel(-1),
     41        is_streaming(false)
     42    {
     43    }
     44
     45    bool             reset_timer_on;
     46    MythTimer        reset_timer;
     47
     48    bool             run_port_handler;
     49    bool             is_port_handler_running;
     50    QMutex           start_stop_port_handler_lock;
     51
     52    int              channel;
     53
     54    bool             is_streaming;
     55
     56    QDateTime        stop_streaming_timer;
     57    pthread_t        port_handler_thread;
     58
     59    static QMutex           s_lock;
     60};
     61QMutex          R5kPriv::s_lock;
     62
     63//callback functions
     64void *r5000_device_port_handler_thunk(void *param);
     65
     66R5000Device::R5000Device(QString serial, uint subunitid, uint speed) :
     67    m_serial(serial),       m_subunitid(subunitid),
     68    m_speed(speed),
     69    m_last_channel(0),      m_last_crc(0),
     70    m_buffer_cleared(true), m_open_port_cnt(0),
     71    m_lock(false),          m_priv(new R5kPriv())
     72{
     73  usbdev = NULL;
     74}
     75
     76R5000Device::~R5000Device()
     77{
     78    if (usbdev)
     79    {
     80        VERBOSE(VB_IMPORTANT, LOC_ERR + "ctor called with open port");
     81        while(usbdev)
     82            ClosePort();
     83    }
     84
     85    if (m_priv)
     86    {
     87        delete m_priv;
     88        m_priv = NULL;
     89    }
     90}
     91
     92void R5000Device::AddListener(TSDataListener *listener)
     93{
     94    if (listener)
     95    {
     96        vector<TSDataListener*>::iterator it =
     97            find(m_listeners.begin(), m_listeners.end(), listener);
     98
     99        if (it == m_listeners.end())
     100            m_listeners.push_back(listener);
     101    }
     102
     103    VERBOSE(VB_RECORD, LOC + "AddListener() "<<m_listeners.size());
     104    if (!m_listeners.empty())
     105    {
     106        StartStreaming();
     107    }
     108}
     109
     110void R5000Device::RemoveListener(TSDataListener *listener)
     111{
     112    vector<TSDataListener*>::iterator it = m_listeners.end();
     113
     114    do
     115    {
     116        it = find(m_listeners.begin(), m_listeners.end(), listener);
     117        if (it != m_listeners.end())
     118            m_listeners.erase(it);
     119    }
     120    while (it != m_listeners.end());
     121
     122    VERBOSE(VB_RECORD, LOC + "RemoveListener() "<<m_listeners.size());
     123    if (m_listeners.empty())
     124    {
     125        StopStreaming();
     126    }
     127}
     128
     129bool R5000Device::StartStreaming(void)
     130{
     131    if (m_priv->is_streaming)
     132        return m_priv->is_streaming;
     133
     134    if (! usbdev)
     135    {
     136        VERBOSE(VB_IMPORTANT, LOC_ERR + "Device not open");
     137        return false;
     138    }
     139    if (r5000_start_stream(usbdev))
     140    {
     141        m_priv->is_streaming = true;
     142    }
     143    else
     144    {
     145        VERBOSE(VB_IMPORTANT, LOC_ERR + "Starting A/V streaming ");
     146    }
     147
     148    VERBOSE(VB_RECORD, LOC + "Starting A/V streaming -- done");
     149
     150    return m_priv->is_streaming;
     151}
     152
     153bool R5000Device::StopStreaming(void)
     154{
     155    if (m_priv->is_streaming)
     156    {
     157        VERBOSE(VB_RECORD, LOC + "Stopping A/V streaming -- really");
     158
     159        m_priv->is_streaming = false;
     160
     161        r5000_stop_stream(usbdev);
     162    }
     163
     164    VERBOSE(VB_RECORD, LOC + "Stopped A/V streaming");
     165
     166    return true;
     167}
     168
     169bool R5000Device::SetPowerState(bool on)
     170{
     171    QMutexLocker locker(&m_lock);
     172    QString cmdStr = (on) ? "on" : "off";
     173    VERBOSE(VB_RECORD, LOC + QString("Powering %1").arg(cmdStr));
     174    if(r5000_get_power_state(usbdev) != on)
     175      r5000_toggle_on_off(usbdev);
     176    return true;
     177}
     178
     179R5000Device::PowerState R5000Device::GetPowerState(void)
     180{
     181    QMutexLocker locker(&m_lock);
     182    int on_off;
     183
     184    VERBOSE(VB_CHANNEL, LOC + "Requesting STB Power State");
     185    on_off = r5000_get_power_state(usbdev);
     186    VERBOSE(VB_CHANNEL, LOC + (on_off ? "On" : "Off"));
     187    return on_off ? kAVCPowerOn : kAVCPowerOff;
     188}
     189
     190bool R5000Device::SetChannel(const QString &panel_model,
     191                                uint alt_method, uint channel)
     192{
     193    VERBOSE(VB_CHANNEL, QString("SetChannel(model %1, alt %2, chan %3)")
     194            .arg(panel_model).arg(alt_method).arg(channel));
     195
     196    QMutexLocker locker(&m_lock);
     197    VERBOSE(VB_CHANNEL, "SetChannel() -- locked");
     198    r5000_change_channel(usbdev, channel);
     199    return true;
     200}
     201
     202void R5000Device::BroadcastToListeners(
     203    const unsigned char *data, uint dataSize)
     204{
     205    if ((dataSize >= TSPacket::SIZE) && (data[0] == SYNC_BYTE) &&
     206        ((data[1] & 0x1f) == 0) && (data[2] == 0))
     207    {
     208        ProcessPATPacket(*((const TSPacket*)data));
     209    }
     210
     211    vector<TSDataListener*>::iterator it = m_listeners.begin();
     212    for (; it != m_listeners.end(); ++it)
     213        (*it)->AddData(data, dataSize);
     214}
     215
     216void R5000Device::SetLastChannel(const uint channel)
     217{
     218    m_buffer_cleared = (channel == m_last_channel);
     219    m_last_channel   = channel;
     220
     221    VERBOSE(VB_IMPORTANT, QString("SetLastChannel(%1): cleared: %2")
     222            .arg(channel).arg(m_buffer_cleared ? "yes" : "no"));
     223}
     224
     225void R5000Device::ProcessPATPacket(const TSPacket &tspacket)
     226{
     227    if (!tspacket.TransportError() && !tspacket.ScramplingControl() &&
     228        tspacket.HasPayload() && tspacket.PayloadStart() && !tspacket.PID())
     229    {
     230        PESPacket pes = PESPacket::View(tspacket);
     231        uint crc = pes.CalcCRC();
     232        m_buffer_cleared |= (crc != m_last_crc);
     233        m_last_crc = crc;
     234#if 0
     235        VERBOSE(VB_RECORD, LOC +
     236                QString("ProcessPATPacket: CRC 0x%1 cleared: %2")
     237                .arg(crc,0,16).arg(m_buffer_cleared ? "yes" : "no"));
     238#endif
     239    }
     240    else
     241    {
     242        VERBOSE(VB_IMPORTANT, LOC_ERR + "Can't handle large PAT's");
     243    }
     244}
     245
     246QString R5000Device::GetModelName(uint vendor_id, uint model_id)
     247{
     248    QMutexLocker locker(&s_static_lock);
     249/*
     250    if (s_id_to_model.empty())
     251        fw_init(s_id_to_model);
     252
     253    QString ret = s_id_to_model[(((uint64_t) vendor_id) << 32) | model_id];
     254
     255    if (ret.isEmpty())
     256        return "GENERIC";
     257*/
     258    return "R5000";
     259}
     260
     261bool R5000Device::IsSTBSupported(const QString &panel_model)
     262{
     263    return true;
     264}
     265
     266bool R5000Device::OpenPort(void)
     267{
     268    VERBOSE(VB_RECORD, LOC + "Starting Port Handler Thread");
     269    QMutexLocker mlocker(&m_lock);
     270    VERBOSE(VB_RECORD, LOC + "Starting Port Handler Thread -- locked");
     271    if(usbdev) {
     272        m_open_port_cnt++;
     273        return true;
     274    }
     275
     276    if(m_serial) {
     277      VERBOSE(VB_RECORD, LOC + "Opening R5000 device with serial#: "+ m_serial);
     278      usbdev = r5000_open(r5000_device_tspacket_handler, this, m_serial.ascii());
     279    } else {
     280      VERBOSE(VB_RECORD, LOC + "Opening R5000 device with unknown serial#");
     281      usbdev = r5000_open(r5000_device_tspacket_handler, this, NULL);
     282    }
     283    if(! usbdev)
     284        return false;
     285
     286    VERBOSE(VB_RECORD, LOC + "Starting port handler thread");
     287    m_priv->run_port_handler = true;
     288    pthread_create(&m_priv->port_handler_thread, NULL,
     289                   r5000_device_port_handler_thunk, this);
     290
     291    VERBOSE(VB_RECORD, LOC + "Waiting for port handler thread to start");
     292    while (!m_priv->is_port_handler_running)
     293    {
     294        m_lock.unlock();
     295        usleep(5000);
     296        m_lock.lock();
     297    }
     298
     299    VERBOSE(VB_RECORD, LOC + "Port handler thread started");
     300
     301    m_open_port_cnt++;
     302
     303    return true;
     304}
     305
     306bool R5000Device::ClosePort(void)
     307{
     308    VERBOSE(VB_RECORD, LOC + "Stopping Port Handler Thread");
     309    QMutexLocker locker(&m_priv->start_stop_port_handler_lock);
     310    VERBOSE(VB_RECORD, LOC + "Stopping Port Handler Thread -- locked");
     311
     312    QMutexLocker mlocker(&m_lock);
     313
     314    VERBOSE(VB_RECORD, LOC + "ClosePort()");
     315
     316    if (m_open_port_cnt < 1)
     317        return false;
     318
     319    m_open_port_cnt--;
     320
     321    if (m_open_port_cnt != 0)
     322        return true;
     323
     324    if (!usbdev)
     325        return false;
     326
     327    VERBOSE(VB_RECORD, LOC + "Waiting for port handler thread to stop");
     328    m_priv->run_port_handler = false;
     329    while (m_priv->is_port_handler_running)
     330    {
     331        m_lock.unlock();
     332        usleep(5000);
     333        m_lock.lock();
     334    }
     335    VERBOSE(VB_RECORD, LOC + "Joining port handler thread");
     336    pthread_join(m_priv->port_handler_thread, NULL);
     337
     338    r5000_close(usbdev);
     339    usbdev = NULL;
     340
     341    return true;
     342}
     343
     344void *r5000_device_port_handler_thunk(void *param)
     345{
     346    R5000Device *mon = (R5000Device*) param;
     347    mon->RunPortHandler();
     348    return NULL;
     349}
     350
     351void R5000Device::RunPortHandler(void)
     352{
     353    VERBOSE(VB_RECORD, LOC + "RunPortHandler -- start");
     354    m_lock.lock();
     355    VERBOSE(VB_RECORD, LOC + "RunPortHandler -- got first lock");
     356    m_priv->is_port_handler_running = true;
     357    m_lock.unlock();
     358
     359    while (m_priv->run_port_handler)
     360    {
     361        if (m_priv->is_streaming) {
     362            // This will timeout after 10ms regardless of data availability
     363            r5000_loop_iterate(usbdev, 10);
     364        } else {
     365            usleep(10000);
     366        }
     367    }
     368
     369    m_lock.lock();
     370    m_priv->is_port_handler_running = false;
     371    m_lock.unlock();
     372    VERBOSE(VB_RECORD, LOC + "RunPortHandler -- end");
     373}
     374
     375QStringList R5000Device::GetSTBList(void)
     376{
     377    QStringList STBList;
     378    int i;
     379    r5kenum_t r5k_stbs;
     380    r5000_find_stbs(&r5k_stbs);
     381    for (i = 0; i < r5k_stbs.count; i++)
     382        STBList.append((char *)r5k_stbs.serial[i]);
     383    return STBList;
     384}
  • new file libs/libmythtv/r5000recorder.cpp

    - +  
     1/**
     2 *  R5000Recorder
     3 *  Copyright (c) 2005 by Jim Westfall and Dave Abrahams
     4 *  Distributed as part of MythTV under GPL v2 and later.
     5 */
     6
     7// MythTV includes
     8#include "r5000recorder.h"
     9#include "r5000channel.h"
     10#include "mythcontext.h"
     11#include "mpegtables.h"
     12#include "mpegstreamdata.h"
     13#include "tv_rec.h"
     14
     15#define LOC QString("R5000RecBase(%1): ").arg(channel->GetDevice())
     16#define LOC_ERR QString("R5000RecBase(%1), Error: ").arg(channel->GetDevice())
     17
     18R5000Recorder::R5000Recorder(TVRec *rec, R5000Channel *chan) :
     19    DTVRecorder(rec), _mpeg_stream_data(NULL),
     20    channel(chan), isopen(false)
     21{
     22}
     23
     24R5000Recorder::~R5000Recorder()
     25{
     26    SetStreamData(NULL);
     27    Close();
     28}
     29
     30bool R5000Recorder::Open(void)
     31{
     32    if (!isopen)
     33        isopen = channel->GetR5000Device()->OpenPort();
     34
     35    return isopen;
     36}
     37
     38void R5000Recorder::Close(void)
     39{
     40    if (isopen)
     41    {
     42        channel->GetR5000Device()->ClosePort();
     43        isopen = false;
     44    }
     45}
     46
     47void R5000Recorder::StartStreaming(void)
     48{
     49    channel->GetR5000Device()->AddListener(this);
     50}
     51
     52void R5000Recorder::StopStreaming(void)
     53{
     54    channel->GetR5000Device()->RemoveListener(this);
     55}
     56
     57void R5000Recorder::StartRecording(void)
     58{
     59    VERBOSE(VB_RECORD, LOC + "StartRecording");
     60
     61    if (!Open())
     62    {
     63        _error = true;
     64        return;
     65    }
     66
     67    _request_recording = true;
     68    _recording = true;
     69
     70    StartStreaming();
     71
     72    while (_request_recording)
     73    {
     74        if (!PauseAndWait())
     75            usleep(50 * 1000);
     76    }
     77
     78    StopStreaming();
     79    FinishRecording();
     80
     81    _recording = false;
     82}
     83
     84void R5000Recorder::AddData(const unsigned char *data, uint len)
     85{
     86    uint bufsz = buffer.size();
     87    if ((SYNC_BYTE == data[0]) && (TSPacket::SIZE == len) &&
     88        (TSPacket::SIZE > bufsz))
     89    {
     90        if (bufsz)
     91            buffer.clear();
     92
     93        ProcessTSPacket(*(reinterpret_cast<const TSPacket*>(data)));
     94        return;
     95    }
     96
     97    buffer.insert(buffer.end(), data, data + len);
     98    bufsz += len;
     99
     100    int sync_at = -1;
     101    for (uint i = 0; (i < bufsz) && (sync_at < 0); i++)
     102    {
     103        if (buffer[i] == SYNC_BYTE)
     104            sync_at = i;
     105    }
     106
     107    if (sync_at < 0)
     108        return;
     109
     110    if (bufsz < 30 * TSPacket::SIZE)
     111        return; // build up a little buffer
     112
     113    while (sync_at + TSPacket::SIZE < bufsz)
     114    {
     115        ProcessTSPacket(*(reinterpret_cast<const TSPacket*>(
     116                              &buffer[0] + sync_at)));
     117
     118        sync_at += TSPacket::SIZE;
     119    }
     120
     121    buffer.erase(buffer.begin(), buffer.begin() + sync_at);
     122
     123    return;
     124}
     125
     126void R5000Recorder::ProcessTSPacket(const TSPacket &tspacket)
     127{
     128    if (tspacket.TransportError())
     129        return;
     130
     131    if (tspacket.ScramplingControl())
     132        return;
     133
     134    if (tspacket.HasAdaptationField())
     135        GetStreamData()->HandleAdaptationFieldControl(&tspacket);
     136
     137    if (tspacket.HasPayload())
     138    {
     139        const unsigned int lpid = tspacket.PID();
     140        // Pass or reject packets based on PID, and parse info from them
     141        if (lpid == GetStreamData()->VideoPIDSingleProgram())
     142        {
     143            _buffer_packets = !FindMPEG2Keyframes(&tspacket);
     144            BufferedWrite(tspacket);
     145        }
     146        else if (GetStreamData()->IsAudioPID(lpid))
     147        {
     148            _buffer_packets = !FindAudioKeyframes(&tspacket);
     149            BufferedWrite(tspacket);
     150        }
     151        else if (GetStreamData()->IsListeningPID(lpid))
     152            GetStreamData()->HandleTSTables(&tspacket);
     153        else if (GetStreamData()->IsWritingPID(lpid))
     154            BufferedWrite(tspacket);
     155    }
     156}
     157
     158void R5000Recorder::SetOptionsFromProfile(RecordingProfile *profile,
     159                                                 const QString &videodev,
     160                                                 const QString &audiodev,
     161                                                 const QString &vbidev)
     162{
     163    (void)videodev;
     164    (void)audiodev;
     165    (void)vbidev;
     166    (void)profile;
     167}
     168
     169// documented in recorderbase.cpp
     170bool R5000Recorder::PauseAndWait(int timeout)
     171{
     172    if (request_pause)
     173    {
     174        VERBOSE(VB_RECORD, LOC + "PauseAndWait("<<timeout<<") -- pause");
     175        if (!paused)
     176        {
     177            StopStreaming();
     178            paused = true;
     179            pauseWait.wakeAll();
     180            if (tvrec)
     181                tvrec->RecorderPaused();
     182        }
     183        unpauseWait.wait(timeout);
     184    }
     185    if (!request_pause && paused)
     186    {
     187        VERBOSE(VB_RECORD, LOC + "PauseAndWait("<<timeout<<") -- unpause");
     188        StartStreaming();
     189        paused = false;
     190    }
     191    return paused;
     192}
     193
     194void R5000Recorder::SetStreamData(MPEGStreamData *data)
     195{
     196    VERBOSE(VB_RECORD, LOC + "SetStreamData("<<data<<") -- begin");
     197    if (data == _mpeg_stream_data)
     198    {
     199        VERBOSE(VB_RECORD, LOC + "SetStreamData("<<data<<") -- end 0");
     200        return;
     201    }
     202
     203    MPEGStreamData *old_data = _mpeg_stream_data;
     204    _mpeg_stream_data = data;
     205    if (old_data)
     206        delete old_data;
     207
     208    if (data)
     209    {
     210        data->AddMPEGSPListener(this);
     211
     212        if (data->DesiredProgram() >= 0)
     213            data->SetDesiredProgram(data->DesiredProgram());
     214    }
     215    VERBOSE(VB_RECORD, LOC + "SetStreamData("<<data<<") -- end 1");
     216}
     217
     218void R5000Recorder::HandleSingleProgramPAT(ProgramAssociationTable *pat)
     219{
     220    if (!pat) {
     221        VERBOSE(VB_RECORD, LOC + "HandleSingleProgramPAT(NULL)");
     222        return;
     223    }
     224    int next = (pat->tsheader()->ContinuityCounter()+1)&0xf;
     225    pat->tsheader()->SetContinuityCounter(next);
     226    BufferedWrite(*(reinterpret_cast<const TSPacket*>(pat->tsheader())));
     227}
     228
     229void R5000Recorder::HandleSingleProgramPMT(ProgramMapTable *pmt)
     230{
     231    if (!pmt) {
     232        VERBOSE(VB_RECORD, LOC + "HandleSingleProgramPMT(NULL)");
     233        return;
     234    }
     235    int next = (pmt->tsheader()->ContinuityCounter()+1)&0xf;
     236    pmt->tsheader()->SetContinuityCounter(next);
     237    BufferedWrite(*(reinterpret_cast<const TSPacket*>(pmt->tsheader())));
     238}
  • new file libs/libmythtv/r5000signalmonitor.cpp

    - +  
     1// -*- Mode: c++ -*-
     2// Copyright (c) 2006, Daniel Thor Kristjansson
     3
     4#include <pthread.h>
     5#include <fcntl.h>
     6#include <unistd.h>
     7#include <sys/select.h>
     8
     9#include "mythcontext.h"
     10#include "mythdbcon.h"
     11#include "atscstreamdata.h"
     12#include "mpegtables.h"
     13#include "atsctables.h"
     14#include "r5000channel.h"
     15#include "r5000signalmonitor.h"
     16
     17#define LOC QString("R5kSM(%1): ").arg(channel->GetDevice())
     18#define LOC_WARN QString("R5kSM(%1), Warning: ").arg(channel->GetDevice())
     19#define LOC_ERR QString("R5kSM(%1), Error: ").arg(channel->GetDevice())
     20
     21const uint R5000SignalMonitor::kPowerTimeout  = 3000; /* ms */
     22const uint R5000SignalMonitor::kBufferTimeout = 5000; /* ms */
     23
     24QMap<void*,uint> R5000SignalMonitor::pat_keys;
     25QMutex           R5000SignalMonitor::pat_keys_lock;
     26
     27/** \fn R5000SignalMonitor::R5000SignalMonitor(int,R5000Channel*,uint,const char*)
     28 *  \brief Initializes signal lock and signal values.
     29 *
     30 *   Start() must be called to actually begin continuous
     31 *   signal monitoring. The timeout is set to 3 seconds,
     32 *   and the signal threshold is initialized to 0%.
     33 *
     34 *  \param db_cardnum Recorder number to monitor,
     35 *                    if this is less than 0, SIGNAL events will not be
     36 *                    sent to the frontend even if SetNotifyFrontend(true)
     37 *                    is called.
     38 *  \param _channel R5000Channel for card
     39 *  \param _flags   Flags to start with
     40 *  \param _name    Name for Qt signal debugging
     41 */
     42R5000SignalMonitor::R5000SignalMonitor(
     43    int db_cardnum,
     44    R5000Channel *_channel,
     45    uint64_t _flags, const char *_name) :
     46    DTVSignalMonitor(db_cardnum, _channel, _flags, _name),
     47    dtvMonitorRunning(false),
     48    stb_needs_retune(true),
     49    stb_needs_to_wait_for_pat(false),
     50    stb_needs_to_wait_for_power(false)
     51{
     52    VERBOSE(VB_CHANNEL, LOC + "ctor");
     53
     54    signalStrength.SetThreshold(65);
     55
     56    AddFlags(kDTVSigMon_WaitForSig);
     57
     58    stb_needs_retune =
     59        (R5000Device::kAVCPowerOff == _channel->GetPowerState());
     60}
     61
     62/** \fn R5000SignalMonitor::~R5000SignalMonitor()
     63 *  \brief Stops signal monitoring and table monitoring threads.
     64 */
     65R5000SignalMonitor::~R5000SignalMonitor()
     66{
     67    VERBOSE(VB_CHANNEL, LOC + "dtor");
     68    Stop();
     69}
     70
     71void R5000SignalMonitor::deleteLater(void)
     72{
     73    disconnect(); // disconnect signals we may be sending...
     74    Stop();
     75    DTVSignalMonitor::deleteLater();
     76}
     77
     78/** \fn R5000SignalMonitor::Stop(void)
     79 *  \brief Stop signal monitoring and table monitoring threads.
     80 */
     81void R5000SignalMonitor::Stop(void)
     82{
     83    VERBOSE(VB_CHANNEL, LOC + "Stop() -- begin");
     84    SignalMonitor::Stop();
     85    if (dtvMonitorRunning)
     86    {
     87        dtvMonitorRunning = false;
     88        pthread_join(table_monitor_thread, NULL);
     89    }
     90    VERBOSE(VB_CHANNEL, LOC + "Stop() -- end");
     91}
     92
     93void R5000SignalMonitor::HandlePAT(const ProgramAssociationTable *pat)
     94{
     95    AddFlags(kDTVSigMon_PATSeen);
     96
     97    R5000Channel *fwchan = dynamic_cast<R5000Channel*>(channel);
     98    bool crc_bogus = !fwchan->GetR5000Device()->IsSTBBufferCleared();
     99    if (crc_bogus && stb_needs_to_wait_for_pat &&
     100        (stb_wait_for_pat_timer.elapsed() < (int)kBufferTimeout))
     101    {
     102        VERBOSE(VB_CHANNEL, LOC + "HandlePAT() ignoring PAT");
     103        uint tsid = pat->TransportStreamID();
     104        GetStreamData()->SetVersionPAT(tsid, -1,0);
     105        return;
     106    }
     107
     108    if (crc_bogus && stb_needs_to_wait_for_pat)
     109    {
     110        VERBOSE(VB_IMPORTANT, LOC_WARN + "Wait for valid PAT timed out");
     111        stb_needs_to_wait_for_pat = false;
     112    }
     113
     114    DTVSignalMonitor::HandlePAT(pat);
     115}
     116
     117void R5000SignalMonitor::HandlePMT(uint pnum, const ProgramMapTable *pmt)
     118{
     119    VERBOSE(VB_CHANNEL, LOC + "HandlePMT()");
     120
     121    AddFlags(kDTVSigMon_PMTSeen);
     122
     123    if (!HasFlags(kDTVSigMon_PATMatch))
     124    {
     125        GetStreamData()->SetVersionPMT(pnum, -1,0);
     126        VERBOSE(VB_CHANNEL, LOC + "HandlePMT() ignoring PMT");
     127        return;
     128    }
     129
     130    DTVSignalMonitor::HandlePMT(pnum, pmt);
     131}
     132
     133void *R5000SignalMonitor::TableMonitorThread(void *param)
     134{
     135    R5000SignalMonitor *mon = (R5000SignalMonitor*) param;
     136    mon->RunTableMonitor();
     137    return NULL;
     138}
     139
     140void R5000SignalMonitor::RunTableMonitor(void)
     141{
     142    stb_needs_to_wait_for_pat = true;
     143    stb_wait_for_pat_timer.start();
     144    dtvMonitorRunning = true;
     145
     146    VERBOSE(VB_CHANNEL, LOC + "RunTableMonitor(): -- begin");
     147
     148    R5000Channel *lchan = dynamic_cast<R5000Channel*>(channel);
     149    if (!lchan)
     150    {
     151        VERBOSE(VB_CHANNEL, LOC + "RunTableMonitor(): -- err end");
     152        dtvMonitorRunning = false;
     153        return;
     154    }
     155
     156    R5000Device *dev = lchan->GetR5000Device();
     157
     158    dev->OpenPort();
     159    dev->AddListener(this);
     160
     161    while (dtvMonitorRunning && GetStreamData())
     162        usleep(100000);
     163
     164    VERBOSE(VB_CHANNEL, LOC + "RunTableMonitor(): -- shutdown ");
     165
     166    dev->RemoveListener(this);
     167    dev->ClosePort();
     168
     169    dtvMonitorRunning = false;
     170
     171    VERBOSE(VB_CHANNEL, LOC + "RunTableMonitor(): -- end");
     172}
     173
     174void R5000SignalMonitor::AddData(const unsigned char *data, uint len)
     175{
     176    if (!dtvMonitorRunning)
     177        return;
     178
     179    if (GetStreamData())
     180        GetStreamData()->ProcessData((unsigned char *)data, len);
     181}
     182
     183/** \fn R5000SignalMonitor::UpdateValues(void)
     184 *  \brief Fills in frontend stats and emits status Qt signals.
     185 *
     186 *   This function uses five ioctl's FE_READ_SNR, FE_READ_SIGNAL_STRENGTH
     187 *   FE_READ_BER, FE_READ_UNCORRECTED_BLOCKS, and FE_READ_STATUS to obtain
     188 *   statistics from the frontend.
     189 *
     190 *   This is automatically called by MonitorLoop(), after Start()
     191 *   has been used to start the signal monitoring thread.
     192 */
     193void R5000SignalMonitor::UpdateValues(void)
     194{
     195    if (!running || exit)
     196        return;
     197
     198    if (dtvMonitorRunning)
     199    {
     200        EmitR5000Signals();
     201        if (IsAllGood())
     202            emit AllGood();
     203        // TODO dtv signals...
     204
     205        update_done = true;
     206        return;
     207    }
     208
     209    if (stb_needs_to_wait_for_power &&
     210        (stb_wait_for_power_timer.elapsed() < (int)kPowerTimeout))
     211    {
     212        return;
     213    }
     214    stb_needs_to_wait_for_power = false;
     215
     216    R5000Channel *fwchan = dynamic_cast<R5000Channel*>(channel);
     217
     218    if (HasFlags(kFWSigMon_WaitForPower) && !HasFlags(kFWSigMon_PowerMatch))
     219    {
     220        bool retried = false;
     221        while (true)
     222        {
     223            R5000Device::PowerState power = fwchan->GetPowerState();
     224            if (R5000Device::kAVCPowerOn == power)
     225            {
     226                AddFlags(kFWSigMon_PowerSeen | kFWSigMon_PowerMatch);
     227            }
     228            else if (R5000Device::kAVCPowerOff == power)
     229            {
     230                AddFlags(kFWSigMon_PowerSeen);
     231                fwchan->SetPowerState(true);
     232                stb_wait_for_power_timer.start();
     233                stb_needs_to_wait_for_power = true;
     234            }
     235            else
     236            {
     237                bool qfailed = (R5000Device::kAVCPowerQueryFailed == power);
     238                if (qfailed && !retried)
     239                {
     240                    retried = true;
     241                    continue;
     242                }
     243
     244                VERBOSE(VB_RECORD, "Can't determine if STB is power on, "
     245                        "assuming it is...");
     246                AddFlags(kFWSigMon_PowerSeen | kFWSigMon_PowerMatch);
     247            }
     248            break;
     249        }
     250    }
     251
     252    bool isLocked = !HasFlags(kFWSigMon_WaitForPower) ||
     253        HasFlags(kFWSigMon_WaitForPower | kFWSigMon_PowerMatch);
     254
     255    if (isLocked && stb_needs_retune)
     256    {
     257        fwchan->Retune();
     258        isLocked = stb_needs_retune = false;
     259    }
     260
     261    // Set SignalMonitorValues from info from card.
     262    {
     263        QMutexLocker locker(&statusLock);
     264        signalStrength.SetValue(isLocked ? 100 : 0);
     265        signalLock.SetValue(isLocked ? 1 : 0);
     266    }
     267
     268    EmitR5000Signals();
     269    if (IsAllGood())
     270        emit AllGood();
     271
     272    // Start table monitoring if we are waiting on any table
     273    // and we have a lock.
     274    if (isLocked && GetStreamData() &&
     275        HasAnyFlag(kDTVSigMon_WaitForPAT | kDTVSigMon_WaitForPMT |
     276                   kDTVSigMon_WaitForMGT | kDTVSigMon_WaitForVCT |
     277                   kDTVSigMon_WaitForNIT | kDTVSigMon_WaitForSDT))
     278    {
     279        pthread_create(&table_monitor_thread, NULL,
     280                       TableMonitorThread, this);
     281
     282        VERBOSE(VB_CHANNEL, LOC + "UpdateValues() -- "
     283                "Waiting for table monitor to start");
     284
     285        while (!dtvMonitorRunning)
     286            usleep(50);
     287
     288        VERBOSE(VB_CHANNEL, LOC + "UpdateValues() -- "
     289                "Table monitor started");
     290    }
     291
     292    update_done = true;
     293}
     294
     295#define EMIT(SIGNAL_FUNC, SIGNAL_VAL) \
     296    do { statusLock.lock(); \
     297         SignalMonitorValue val = SIGNAL_VAL; \
     298         statusLock.unlock(); \
     299         emit SIGNAL_FUNC(val); } while (false)
     300
     301/** \fn R5000SignalMonitor::EmitR5000Signals(void)
     302 *  \brief Emits signals for lock, signal strength, etc.
     303 */
     304void R5000SignalMonitor::EmitR5000Signals(void)
     305{
     306    // Emit signals..
     307    EMIT(StatusSignalLock, signalLock);
     308    if (HasFlags(kDTVSigMon_WaitForSig))
     309        EMIT(StatusSignalStrength, signalStrength);
     310}
     311
     312#undef EMIT
  • new file libs/libmythtv/r5000channel.h

    - +  
     1/**
     2 *  R5000Channel
     3 *  Copyright (c) 2008 by Alan Nisota
     4 *  Copyright (c) 2005 by Jim Westfall and Dave Abrahams
     5 *  Distributed as part of MythTV under GPL v2 and later.
     6 */
     7
     8#ifndef _R5000CHANNEL_H_
     9#define _R5000CHANNEL_H_
     10
     11#include "tv_rec.h"
     12#include "dtvchannel.h"
     13#include "r5000device.h"
     14
     15class R5000Channel : public DTVChannel
     16{
     17  public:
     18    R5000Channel(TVRec *parent, const QString &videodevice,
     19                    const FireWireDBOptions &firewire_opts);
     20    ~R5000Channel() { Close(); }
     21
     22    // Commands
     23    virtual bool Open(void);
     24    virtual void Close(void);
     25
     26    virtual bool TuneMultiplex(uint /*mplexid*/, QString /*inputname*/)
     27        { return false; }
     28    virtual bool Tune(const DTVMultiplex &/*tuning*/, QString /*inputname*/)
     29        { return false; }
     30    virtual bool Retune(void);
     31
     32    // Sets
     33    virtual bool SetChannelByString(const QString &chan);
     34    virtual bool SetChannelByNumber(int channel);
     35    virtual bool SetPowerState(bool on);
     36
     37    // Gets
     38    virtual bool IsOpen(void) const { return isopen; }
     39    virtual R5000Device::PowerState GetPowerState(void) const;
     40    virtual QString GetDevice(void) const;
     41    virtual R5000Device *GetR5000Device(void) { return device; }
     42
     43  protected:
     44    QString            videodevice;
     45    FireWireDBOptions  fw_opts;
     46    R5000Device    *device;
     47    uint               current_channel;
     48    bool               isopen;
     49};
     50
     51#endif // _FIREWIRECHANNEL_H_
  • new file libs/libmythtv/r5000device.h

    - +  
     1/**
     2 *  R5000Device
     3 *  Copyright (c) 2005 by Jim Westfall
     4 *  Distributed as part of MythTV under GPL v2 and later.
     5 */
     6
     7#ifndef _R5000_DEVICE_H_
     8#define _R5000_DEVICE_H_
     9
     10// C++ headers
     11#include <vector>
     12using namespace std;
     13
     14// Qt headers
     15#include <qstring.h>
     16#include <qmutex.h>
     17
     18// MythTV headers
     19#include "streamlisteners.h"
     20
     21extern "C" {
     22#include "r5000/r5000.h"
     23}
     24
     25class TSPacket;
     26class R5kPriv;
     27class R5000Device
     28{
     29  public:
     30
     31    // Public enums
     32    typedef enum
     33    {
     34        kAVCPowerOn,
     35        kAVCPowerOff,
     36        kAVCPowerUnknown,
     37        kAVCPowerQueryFailed,
     38    } PowerState;
     39
     40
     41    // AVC param 0
     42    typedef enum
     43    {
     44        kAVCPowerStateOn           = 0x70,
     45        kAVCPowerStateOff          = 0x60,
     46        kAVCPowerStateQuery        = 0x7f,
     47    } IEEE1394UnitPowerParam0;
     48
     49    R5000Device(QString serial, uint subunitid, uint speed);
     50    ~R5000Device();
     51
     52    bool OpenPort(void);
     53    bool ClosePort(void);
     54    void RunPortHandler(void);
     55
     56    // Commands
     57    virtual bool ResetBus(void) { return false; }
     58
     59    virtual void AddListener(TSDataListener*);
     60    virtual void RemoveListener(TSDataListener*);
     61
     62    // Sets
     63    virtual bool SetPowerState(bool on);
     64    virtual bool SetChannel(const QString &panel_model,
     65                            uint alt_method, uint channel);
     66
     67    // Gets
     68    bool IsSTBBufferCleared(void) const { return m_buffer_cleared; }
     69
     70    // non-const Gets
     71    virtual PowerState GetPowerState(void);
     72
     73    // Statics
     74    static bool IsSTBSupported(const QString &model);
     75    static QString GetModelName(uint vendorid, uint modelid);
     76    static QStringList GetSTBList(void);
     77    void BroadcastToListeners(
     78        const unsigned char *data, uint dataSize);
     79
     80  protected:
     81
     82    bool GetSubunitInfo(uint8_t table[32]);
     83
     84    void SetLastChannel(uint channel);
     85    void ProcessPATPacket(const TSPacket&);
     86    bool StartStreaming(void);
     87    bool StopStreaming(void);
     88
     89    QString                  m_serial;
     90    uint                     m_subunitid;
     91    uint                     m_speed;
     92    uint                     m_last_channel;
     93    uint                     m_last_crc;
     94    bool                     m_buffer_cleared;
     95
     96    uint                     m_open_port_cnt;
     97    vector<TSDataListener*>  m_listeners;
     98    mutable QMutex           m_lock;
     99
     100    /// Vendor ID + Model ID to R5000Device STB model string
     101    static QMap<uint64_t,QString> s_id_to_model;
     102    static QMutex                 s_static_lock;
     103private:
     104    r5kdev_t                 *usbdev;
     105    R5kPriv                  *m_priv;
     106};
     107
     108#endif // _FIREWIRE_DEVICE_H_
  • new file libs/libmythtv/r5000recorder.h

    - +  
     1/**
     2 *  R5000Recorder
     3 *  Copyright (c) 2005 by Jim Westfall
     4 *  Distributed as part of MythTV under GPL v2 and later.
     5 */
     6
     7#ifndef _R5000RECORDER_H_
     8#define _R5000RECORDER_H_
     9
     10// MythTV headers
     11#include "dtvrecorder.h"
     12#include "tspacket.h"
     13#include "streamlisteners.h"
     14
     15class TVRec;
     16class R5000Channel;
     17
     18/** \class R5000Recorder
     19 *  \brief This is a specialization of DTVRecorder used to
     20 *         handle DVB and ATSC streams from a firewire input.
     21 *
     22 *  \sa DTVRecorder
     23 */
     24class R5000Recorder : public DTVRecorder,
     25                         public MPEGSingleProgramStreamListener,
     26                         public TSDataListener
     27{
     28    friend class MPEGStreamData;
     29    friend class TSPacketProcessor;
     30
     31  public:
     32    R5000Recorder(TVRec *rec, R5000Channel *chan);
     33    virtual ~R5000Recorder();
     34
     35    // Commands
     36    bool Open(void);
     37    void Close(void);
     38
     39    void StartStreaming(void);
     40    void StopStreaming(void);
     41
     42    void StartRecording(void);
     43    bool PauseAndWait(int timeout = 100);
     44
     45    void AddData(const unsigned char *data, uint dataSize);
     46    void ProcessTSPacket(const TSPacket &tspacket);
     47
     48    // Sets
     49    void SetOptionsFromProfile(RecordingProfile *profile,
     50                               const QString &videodev,
     51                               const QString &audiodev,
     52                               const QString &vbidev);
     53    void SetStreamData(MPEGStreamData*);
     54
     55    // Gets
     56    MPEGStreamData *GetStreamData(void) { return _mpeg_stream_data; }
     57
     58    // MPEG Single Program
     59    void HandleSingleProgramPAT(ProgramAssociationTable*);
     60    void HandleSingleProgramPMT(ProgramMapTable*);
     61
     62  protected:
     63    R5000Recorder(TVRec *rec);
     64
     65  private:
     66    MPEGStreamData        *_mpeg_stream_data;
     67    R5000Channel       *channel;
     68    bool                   isopen;
     69    vector<unsigned char>  buffer;
     70};
     71
     72#endif //  _R5000RECORDER_H_
  • new file libs/libmythtv/r5000signalmonitor.h

    - +  
     1// -*- Mode: c++ -*-
     2
     3#ifndef _R5000SIGNALMONITOR_H_
     4#define _R5000SIGNALMONITOR_H_
     5
     6#include <qmap.h>
     7#include <qmutex.h>
     8#include <qdatetime.h>
     9
     10#include "dtvsignalmonitor.h"
     11#include "firewiredevice.h"
     12#include "util.h"
     13
     14class R5000Channel;
     15
     16class R5000SignalMonitor : public DTVSignalMonitor, public TSDataListener
     17{
     18    Q_OBJECT
     19
     20  public:
     21    R5000SignalMonitor(int db_cardnum, R5000Channel *_channel,
     22                          uint64_t _flags = kFWSigMon_WaitForPower,
     23                          const char *_name = "R5000SignalMonitor");
     24
     25    virtual void HandlePAT(const ProgramAssociationTable*);
     26    virtual void HandlePMT(uint, const ProgramMapTable*);
     27
     28    void Stop(void);
     29
     30  public slots:
     31    void deleteLater(void);
     32
     33  protected:
     34    R5000SignalMonitor(void);
     35    R5000SignalMonitor(const R5000SignalMonitor&);
     36    virtual ~R5000SignalMonitor();
     37
     38    virtual void UpdateValues(void);
     39    void EmitR5000Signals(void);
     40
     41    static void *TableMonitorThread(void *param);
     42    void RunTableMonitor(void);
     43
     44    bool SupportsTSMonitoring(void);
     45
     46    void AddData(const unsigned char *data, uint dataSize);
     47
     48  public:
     49    static const uint kPowerTimeout;
     50    static const uint kBufferTimeout;
     51
     52  protected:
     53    bool               dtvMonitorRunning;
     54    pthread_t          table_monitor_thread;
     55    bool               stb_needs_retune;
     56    bool               stb_needs_to_wait_for_pat;
     57    bool               stb_needs_to_wait_for_power;
     58    MythTimer          stb_wait_for_pat_timer;
     59    MythTimer          stb_wait_for_power_timer;
     60
     61    vector<unsigned char> buffer;
     62
     63    static QMap<void*,uint> pat_keys;
     64    static QMutex           pat_keys_lock;
     65};
     66
     67#endif // _R5000SIGNALMONITOR_H_
  • new file libs/libmythtv/r5000/libusb_augment.c

    - +  
     1// 2005-10-19/lindi: downloaded from http://www.gaesi.org/~nmct/cvista/cvista/
     2
     3// libusb_augment.c
     4// $Revision$
     5// $Date$
     6
     7// Hopefully, the functions in this file will become part of libusb.
     8
     9#include <stdio.h>
     10#include <sys/ioctl.h>
     11#include <errno.h>
     12#include <sys/time.h>
     13#include <sys/poll.h>
     14#include <usb.h>
     15#include <linux/usbdevice_fs.h>
     16#include <string.h>
     17#include <signal.h>
     18#define LIBUSB_AUGMENT
     19#include "libusb_augment.h"
     20
     21// Taken from libusb file usbi.h because usb.h
     22// hides the definition of usb_dev_handle.
     23extern int usb_debug;
     24
     25struct usb_dev_handle {
     26  int fd;
     27
     28  struct usb_bus *bus;
     29  struct usb_device *device;
     30
     31  int config;
     32  int interface;
     33  int altsetting;
     34
     35  /* Added by RMT so implementations can store other per-open-device data */
     36  void *impl_info;
     37};
     38
     39// Taken from libusb file error.h to supply error handling macro definition.
     40typedef enum {
     41  USB_ERROR_TYPE_NONE = 0,
     42  USB_ERROR_TYPE_STRING,
     43  USB_ERROR_TYPE_ERRNO,
     44} usb_error_type_t;
     45
     46extern char usb_error_str[1024];
     47extern usb_error_type_t usb_error_type;
     48
     49#define USB_ERROR_STR(format, args...) \
     50        do { \
     51          usb_error_type = USB_ERROR_TYPE_STRING; \
     52          snprintf(usb_error_str, sizeof(usb_error_str) - 1, format, ## args); \
     53          if (usb_debug >= 2) \
     54            fprintf(stderr, "USB error: %s\n", usb_error_str); \
     55        } while (0)
     56
     57static int urb_signr = 0;
     58void (*urb_completion_callback)(struct usbdevfs_urb *) = NULL;
     59#define USB_ASYNC_COMPLETION_SIGNAL (SIGRTMIN + 5)
     60
     61void urb_completion_handler(int signum, siginfo_t *info, void *context)
     62{
     63   struct usbdevfs_urb *urb = (struct usbdevfs_urb *)info->si_addr;
     64   struct usbdevfs_urb *context1;
     65   usb_dev_handle *dev = (usb_dev_handle *)urb->usercontext;
     66   int ret;
     67   if (info->si_code != SI_ASYNCIO ||
     68       info->si_signo != USB_ASYNC_COMPLETION_SIGNAL) {
     69       return;
     70   }
     71   if(info->si_errno != 0) {
     72     USB_ERROR_STR("Async URB Completion failed: %s", strerror(info->si_errno));
     73     return;
     74   }
     75   ret = ioctl(dev->fd, USBDEVFS_REAPURB, &context1);
     76   if(ret  < 0) {
     77     USB_ERROR_STR("Failed to read URB: %s", strerror(-ret));
     78     return;
     79   }
     80   if(context1 != urb) {
     81     USB_ERROR_STR("Reaped unexpected urb");
     82     return;
     83   }
     84   if(urb_completion_callback)
     85     urb_completion_callback(urb);
     86}
     87
     88int usbdevfs_urb_signal_completion(void (*cb)( struct usbdevfs_urb *))
     89{
     90  urb_completion_callback = cb;
     91  urb_signr = USB_ASYNC_COMPLETION_SIGNAL;
     92  struct sigaction usb_linux_sa;
     93  usb_linux_sa.sa_sigaction = urb_completion_handler;
     94  sigfillset(&usb_linux_sa.sa_mask);
     95  usb_linux_sa.sa_flags = SA_SIGINFO;
     96  usb_linux_sa.sa_flags |= SA_ONSTACK;
     97  sigaction(USB_ASYNC_COMPLETION_SIGNAL, &usb_linux_sa, NULL);
     98  return 0;
     99}
     100
     101struct usbdevfs_urb *usb_bulk_setup(
     102                struct usbdevfs_urb *iso_urb, // URB pointer-pointer.
     103                unsigned char ep,  // Device endpoint.
     104                char *bytes,       // Data buffer pointer.
     105                int size) {        // Size of the buffer.
     106  struct usbdevfs_urb *local_urb;
     107
     108  // No more than 16384 bytes can be transferred at a time.
     109  if (size > 16384) {
     110    USB_ERROR_STR("error on transfer size: %s", strerror(EINVAL));
     111    return NULL;
     112  }
     113  local_urb = iso_urb;
     114  if (!local_urb) {
     115    local_urb = (struct usbdevfs_urb *) calloc(1, sizeof(struct usbdevfs_urb));
     116    if (!local_urb) {
     117      USB_ERROR_STR("error on packet size: %s", strerror(EINVAL));
     118      return NULL;
     119    }
     120  }
     121  local_urb->type = USBDEVFS_URB_TYPE_BULK;
     122  local_urb->endpoint = ep;
     123  local_urb->status = 0;
     124  local_urb->flags = 0;
     125  local_urb->buffer = bytes;
     126  local_urb->buffer_length = size;
     127  local_urb->actual_length = 0;
     128  local_urb->start_frame = 0;
     129  local_urb->number_of_packets = 0;
     130  local_urb->error_count = 0;
     131  local_urb->signr = urb_signr;
     132  local_urb->usercontext = (void *) 0;
     133  return local_urb;
     134}
     135// Reading and writing are the same except for the endpoint
     136int usb_isochronous_setup(struct usbdevfs_urb **iso_urb, // URB pointer-pointer.
     137                          unsigned char ep,  // Device endpoint.
     138                          int pktsize,       // Endpoint packet size.
     139                          char *bytes,       // Data buffer pointer.
     140                          int size) {        // Size of the buffer.
     141  struct usbdevfs_urb *local_urb;
     142  // int ret
     143  // was unused /lindi
     144  int pktcount, fullpkts, partpktsize, packets, urb_size;
     145
     146  // No more than 32768 bytes can be transferred at a time.
     147  if (size > 32768) {
     148    USB_ERROR_STR("error on transfer size: %s", strerror(EINVAL));
     149    return -EINVAL;
     150  }
     151
     152  // Determine the number of packets that need to be created based upon the
     153  // amount of data to be transferred, and the maximum packet size of the
     154  // endpoint.
     155
     156  // Find integral number of full packets.
     157  //fprintf(stderr, "buf size: %d\n", size);
     158  //fprintf(stderr, "iso size: %d\n", pktsize);
     159  fullpkts = size / pktsize;
     160  //fprintf(stderr, "Number of full packets: %d\n", fullpkts);
     161  // Find length of partial packet.
     162  partpktsize = size % pktsize;
     163  //fprintf(stderr, "Size of partial packet: %d\n", partpktsize);
     164  // Find total number of packets to be transfered.
     165  packets = fullpkts + ((partpktsize > 0) ? 1 : 0);
     166  //fprintf(stderr, "Total number of packets: %d\n", packets);
     167  // Limit the number of packets transfered according to
     168  // the Linux usbdevfs maximum read/write buffer size.
     169  if ((packets < 1) || (packets > 128)) {
     170    USB_ERROR_STR("error on packet size: %s", strerror(EINVAL));
     171    return -EINVAL;
     172  }
     173
     174  // If necessary, allocate the urb and packet
     175  // descriptor structures from the heap.
     176  local_urb = *iso_urb;
     177  if (!local_urb) {
     178    urb_size = sizeof(struct usbdevfs_urb) +
     179      packets * sizeof(struct usb_iso_packet_desc);
     180    local_urb = (struct usbdevfs_urb *) calloc(1, urb_size);
     181    if (!local_urb) {
     182      USB_ERROR_STR("error on packet size: %s", strerror(EINVAL));
     183      return -ENOMEM;
     184    }
     185  }
     186
     187  // Set up each packet for the data to be transferred.
     188  for (pktcount = 0; pktcount < fullpkts; pktcount++) {
     189    local_urb->iso_frame_desc[pktcount].length = pktsize;
     190    local_urb->iso_frame_desc[pktcount].actual_length = 0;
     191    local_urb->iso_frame_desc[pktcount].status = 0;
     192  }
     193
     194  // Set up the last packet for the partial data to be transferred.
     195  if (partpktsize > 0) {
     196    local_urb->iso_frame_desc[pktcount].length = partpktsize;
     197    local_urb->iso_frame_desc[pktcount].actual_length = 0;
     198    local_urb->iso_frame_desc[pktcount++].status = 0;
     199  }
     200
     201  // Set up the URB structure.
     202  local_urb->type = USBDEVFS_URB_TYPE_ISO;
     203  //fprintf(stderr, "type: %d\n", local_urb->type);
     204  local_urb->endpoint = ep;
     205  //fprintf(stderr, "endpoint: 0x%x\n", local_urb->endpoint);
     206  local_urb->status = 0;
     207  local_urb->flags = USBDEVFS_URB_ISO_ASAP; // Additional flags here?
     208  //fprintf(stderr, "flags: %d\n", local_urb->flags);
     209  local_urb->buffer = bytes;
     210  //fprintf(stderr, "buffer: 0x%x\n", local_urb->buffer);
     211  local_urb->buffer_length = size;
     212  //fprintf(stderr, "buffer_length: %d\n", local_urb->buffer_length);
     213  local_urb->actual_length = 0;
     214  local_urb->start_frame = 0;
     215  //fprintf(stderr, "start_frame: %d\n", local_urb->start_frame);
     216  local_urb->number_of_packets = pktcount;
     217  //fprintf(stderr, "number_of_packets: %d\n", local_urb->number_of_packets);
     218  local_urb->error_count = 0;
     219  local_urb->signr = 0;
     220  //fprintf(stderr, "signr: %d\n", local_urb->signr);
     221  local_urb->usercontext = (void *) 0;
     222  *iso_urb = local_urb;
     223  return 0;
     224}
     225
     226
     227int usb_urb_submit(usb_dev_handle *dev,     // Open usb device handle.
     228                   struct usbdevfs_urb *iso_urb,        // Pointer to URB.
     229                   struct timeval *tv_submit) { // Time structure pointer.
     230  int ret;
     231
     232  iso_urb->usercontext = dev;
     233  // Get actual time, of the URB submission.
     234  if(tv_submit)
     235    gettimeofday(tv_submit, NULL);
     236  // Submit the URB through an IOCTL call.
     237  ret = ioctl(dev->fd, USBDEVFS_SUBMITURB, iso_urb);
     238  //fprintf(stderr, "start_frame now: %d\n", iso_urb->start_frame);
     239  //fprintf(stderr, "submit ioctl return value: %d\n", ret);
     240  if (ret < 0) {
     241    //fprintf(stderr, "error submitting URB: %s\n", strerror(errno));
     242    USB_ERROR_STR("error submitting URB: %s", strerror(errno));
     243    return -errno;
     244  }
     245  return ret;
     246}
     247
     248
     249int usb_urb_reap(usb_dev_handle *dev,     // Open usb device handle.
     250                 struct usbdevfs_urb *iso_urb,        // Pointer to URB.
     251                 int timeout) {           // Attempt timeout (usec).
     252  struct timeval tv_ref, tv_msec, tv;
     253  void *context;
     254  int waiting, ret;
     255  struct pollfd ufd[1];
     256
     257  // Get actual time, and add the timeout value. The result is the absolute
     258  // time where we have to quit waiting for an isochronous message.
     259  ufd[0].fd = dev->fd;
     260  ufd[0].events = POLLIN | POLLOUT;
     261  ufd[0].revents = 0;
     262  ret = poll(ufd, 1, timeout);
     263  if(ret <= 0)
     264    return -ETIMEDOUT;
     265
     266  //fprintf(stderr, "preparing to reap\n");
     267  ret = ioctl(dev->fd, USBDEVFS_REAPURB, &context);
     268
     269  /*
     270   * If there was an error, that wasn"t EAGAIN (no completion), then
     271   * something happened during the reaping and we should return that
     272   * error now
     273   */
     274  //fprintf(stderr, "reap ioctl return value: %d\n", ret);
     275  if (ret < 0) {
     276    USB_ERROR_STR("error reaping interrupt URB: %s",
     277                  strerror(errno));
     278    return -errno;
     279  }
     280
     281  //fprintf(stderr, "actual_length: %d\n", iso_urb->actual_length);
     282  //fprintf(stderr, "URB status: %d\n", iso_urb->status);
     283  //fprintf(stderr, "error count: %d\n", iso_urb->error_count);
     284
     285  //fprintf(stderr, "waiting done\n");
     286  if(iso_urb != context) {
     287    fprintf(stderr, "Expected urb: %p but got %p\n", iso_urb, context);
     288    return -1;
     289  }
     290  //fprintf(stderr, "Total bytes: %d\n", bytesdone);
     291  return iso_urb->actual_length;
     292}
     293
     294int usb_urb_cancel(usb_dev_handle *dev, struct usbdevfs_urb *iso_urb)
     295{
     296  return ioctl(dev->fd, USBDEVFS_DISCARDURB, iso_urb);
     297}
  • new file libs/libmythtv/r5000/libusb_augment.h

    - +  
     1// libusb_augment.h
     2// $Revision$
     3// $Date$
     4
     5#ifdef LIBUSB_AUGMENT
     6// Taken from libusb file linux.h to provide the URB structure definitions.
     7struct usb_iso_packet_desc {
     8  unsigned int length;
     9  unsigned int actual_length;
     10  unsigned int status;
     11};
     12#endif
     13
     14int usbdevfs_urb_signal_completion(void (*cb)( struct usbdevfs_urb *));
     15struct usbdevfs_urb *usb_bulk_setup(struct usbdevfs_urb *iso_urb, unsigned char ep,
     16                   char *bytes, int size);
     17int usb_isochronous_setup(struct usbdevfs_urb **iso_urb, unsigned char ep,
     18                          int pktsize, char *bytes, int size);
     19int usb_urb_submit(usb_dev_handle *dev, struct usbdevfs_urb *iso_urb,
     20                   struct timeval *tv_rsubmit);
     21int usb_urb_reap(usb_dev_handle *dev, struct usbdevfs_urb *iso_urb,
     22                 int timeout_usec);
     23int usb_urb_cancel(usb_dev_handle *dev, struct usbdevfs_urb *iso_urb);
  • new file libs/libmythtv/r5000/r5000.c

    - +  
     1//#define R5K_DEBUG
     2#include <stdio.h>
     3#include <sys/types.h>
     4#include <sys/stat.h>
     5#include <fcntl.h>
     6#include <string.h>
     7#include <errno.h>
     8
     9       #include <sys/time.h>
     10       #include <time.h>
     11
     12
     13#include <usb.h>
     14#include <linux/usbdevice_fs.h>
     15#include "libusb_augment.h"
     16#include "r5000buttons.h"
     17
     18#define R5K_WARM_VID 0x0547
     19#define R5K_WARM_PID 0x1002
     20
     21#define MAX_URBS_IN_FLIGHT 128
     22#define R5K_URB_BUFFER_SIZE (1 << 14)
     23#define R5K_MAX_PIDS 10
     24
     25struct r5kdev {
     26  usb_dev_handle *handle;
     27  struct usbdevfs_urb *urbs;
     28  unsigned char *buffer;
     29  unsigned char leftover[188];
     30  unsigned int (*cb)(unsigned char *buffer, int len, void *callback_data);
     31  void *cb_data;
     32  int nexturb;
     33  int streaming;
     34  int offset;
     35  int bytes_read;
     36#ifdef R5K_DEBUG
     37  int fd;
     38#endif
     39  unsigned char pmt_pkt[188];
     40  unsigned char num_pmt_entries;
     41  unsigned char pmt_state;
     42  unsigned char pmt_next_cc;
     43  struct {
     44    int pid;
     45    unsigned char id;
     46  } pmt[R5K_MAX_PIDS];
     47} r5kdev_t;
     48#define r5kdev_t struct r5kdev
     49#include "r5000.h"
     50
     51static r5kdev_t *glbl_r5kdev;
     52static int r5000_usb_init = 0;
     53static unsigned char r5000_pat_pkt[188] = {
     540x47, 0x40, 0x00, 0x13, 0x00, 0x00, 0xb0, 0x0d, 0x00, 0x06, 0xc5, 0x00, 0x00, 0x00, 0x01, 0xe0,
     550x21, 0x19, 0x3a, 0x82, 0xc4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     560xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     570xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     580xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     590xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     600xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     610xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     620xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     630xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     640xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     650xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
     66enum {
     67  R5K_PMT_START = 0x01,
     68  R5K_PMT_READY = 0x02
     69};
     70#if 0
     71static int r5000_pmt_state = 0;
     72static unsigned char r5000_pmt[188];
     73static int r5000_pmt_count = 0;
     74static unsigned char r5000_pmt_partial[188*20];
     75static int r5000_pmt_partial_count = 0;
     76static int r5000_pmt_cc;
     77#endif
     78
     79int r5000_create_urbs(r5kdev_t *r5kdev)
     80{
     81  int i;
     82  r5kdev->urbs = (struct usbdevfs_urb *)malloc(sizeof(struct usbdevfs_urb) * MAX_URBS_IN_FLIGHT);
     83  r5kdev->buffer = malloc(R5K_URB_BUFFER_SIZE * MAX_URBS_IN_FLIGHT);
     84  for(i = 0; i < MAX_URBS_IN_FLIGHT; i++) {
     85    usb_bulk_setup(&r5kdev->urbs[i], 0x82, r5kdev->buffer + (R5K_URB_BUFFER_SIZE*i), R5K_URB_BUFFER_SIZE);
     86  }
     87  r5kdev->nexturb = 0;
     88  return 0;
     89}
     90
     91int r5000_free_urbs(r5kdev_t *r5kdev)
     92{
     93  free(r5kdev->urbs);
     94  free(r5kdev->buffer);
     95  return 0;
     96}
     97
     98usb_dev_handle *r5000_locate_device(
     99    unsigned short vendor_id, unsigned short product_id, int skip)
     100{
     101  struct usb_bus *bus;
     102  struct usb_device *dev;
     103  usb_dev_handle *device_handle = 0;
     104  usb_find_busses();
     105  usb_find_devices();
     106
     107   for (bus = usb_get_busses(); bus && !device_handle; bus = bus->next)
     108  {
     109    for (dev = bus->devices; dev && !device_handle; dev = dev->next)
     110    {
     111      if (dev->descriptor.idVendor == vendor_id &&
     112          dev->descriptor.idProduct == product_id)
     113      {
     114        device_handle = usb_open(dev);
     115        if(device_handle && skip) {
     116          usb_close(device_handle);
     117          device_handle = NULL;
     118          skip--;
     119        }
     120        //else {
     121        //  printf("XSV Device Found @ Address %s \n", dev->filename);
     122        //  printf("XSV Vendor ID 0x0%x\n",dev->descriptor.idVendor);
     123        //  printf("XSV Product ID 0x0%x\n",dev->descriptor.idProduct);
     124        //}
     125      }
     126    }
     127  }
     128
     129  if (device_handle) {
     130    int open_status = usb_set_configuration(device_handle,1);
     131    //printf("conf_stat=%d\n",open_status);
     132
     133    open_status = usb_claim_interface(device_handle,0);
     134    //printf("claim_stat=%d\n",open_status);
     135
     136    open_status = usb_set_altinterface(device_handle,0);
     137    //printf("alt_stat=%d\n",open_status);
     138  }
     139  return (device_handle);
     140}
     141
     142int r5000_get_serial_number(r5kdev_t *r5kdev, char serial[8])
     143{
     144  unsigned char cmd1[] = {
     145   0x08, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     146   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     147   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     148   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
     149  };
     150  unsigned char cmd2[]  = {
     151    0x08, 0x01, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     152    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     153    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     154    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     155  };
     156  int len;
     157  unsigned char buf[0x40];
     158  usb_bulk_read(r5kdev->handle, 129, buf, 0x40, 5000);
     159  usb_bulk_write(r5kdev->handle, 1, cmd1, sizeof(cmd1), 5000);
     160  usleep(100000);
     161  usb_bulk_read(r5kdev->handle, 129, buf, 0x40, 5000);
     162
     163  usb_bulk_write(r5kdev->handle, 1, cmd2, sizeof(cmd2), 5000);
     164  usleep(100000);
     165  len = usb_bulk_read(r5kdev->handle, 129, buf, 0x40, 5000);
     166
     167  if (len == 0x16 && buf[0] == 0x08) {
     168    //The device responded appropriately
     169    memcpy(serial, buf+6, 7);
     170    serial[7] = 0;
     171    return 1;
     172  }
     173  return 0;
     174}
     175
     176int r5000_start_stream(r5kdev_t *r5kdev)
     177{
     178  unsigned char data[0x80];
     179  int bytes;
     180  int i;
     181  struct usb_dev_handle *handle = (struct usb_dev_handle *)r5kdev->handle;
     182
     183  if(! r5kdev->urbs)
     184    r5000_create_urbs(r5kdev);
     185/*
     186  data[0] = 0x20;
     187  usb_bulk_write(handle, 1, data, 1, 5000);
     188
     189  bytes = usb_bulk_read(handle, 129, data, 4, 5000);
     190  bytes = usb_bulk_read(handle, 129, data, 128, 5000);
     191*/
     192  data[0] = 0x30;
     193  usb_bulk_write(handle, 1, data, 1, 5000);
     194  bytes = usb_bulk_read(handle, 129, data, 2, 5000);
     195
     196  data[0] = 0x50;
     197  usb_bulk_write(handle, 1, data, 1, 5000);
     198
     199  for(i=0; i < MAX_URBS_IN_FLIGHT; i++) {
     200    usb_urb_submit(handle, &r5kdev->urbs[i], NULL);
     201  }
     202  r5kdev->nexturb = 0;
     203  r5kdev->streaming = 1;
     204  r5kdev->offset = 0;
     205  return 1;
     206}
     207
     208int r5000_stop_stream(r5kdev_t *r5kdev)
     209{
     210  struct usb_dev_handle *handle = (struct usb_dev_handle *)r5kdev->handle;
     211  struct usbdevfs_urb *urbs = (struct usbdevfs_urb *)r5kdev->urbs;
     212  int i;
     213
     214  if(r5kdev->streaming) {
     215    for(i=0; i < MAX_URBS_IN_FLIGHT; i++)
     216      usb_urb_cancel(handle, &urbs[i]);
     217    r5kdev->streaming = 0;
     218  }
     219  return 0;
     220}
     221
     222r5kdev_t *r5000_open(unsigned int (*cb)(unsigned char *buffer, int len, void *callback_data), void *cb_data, const char *serial)
     223{
     224  r5kdev_t *r5kdev;
     225  struct usb_dev_handle *handle;
     226  int count = 0;
     227
     228  if(! r5000_usb_init) {
     229    usb_init();
     230    r5000_usb_init = 1;
     231  }
     232
     233  while(count < R5K_MAX_DEVS) {
     234    handle = r5000_locate_device(R5K_WARM_VID, R5K_WARM_PID, count);
     235    if(! handle);
     236      break;
     237    if(serial) {
     238      char ser1[8];
     239      r5kdev_t r5kdevtmp;
     240      r5kdevtmp.handle = handle;
     241      if(r5000_get_serial_number(&r5kdevtmp, ser1)) {
     242        if(memcmp(serial, ser1, 8) == 0)
     243          break;
     244        count++;
     245      }
     246      usb_close(handle);
     247    } else
     248      break;
     249  }
     250  if(! handle)
     251    return NULL;
     252  r5kdev = (r5kdev_t *)calloc(1, sizeof(r5kdev_t));
     253  r5kdev->handle = handle;
     254  r5kdev->urbs = NULL;
     255  r5kdev->cb = cb;
     256  r5kdev->cb_data = cb_data;
     257#ifdef R5K_DEBUG
     258   r5kdev->fd = open("r5kdebug.raw", O_WRONLY | O_TRUNC | O_CREAT, 0666);
     259#endif
     260  glbl_r5kdev = r5kdev;
     261  return r5kdev;
     262}
     263
     264int r5000_close(r5kdev_t *r5kdev)
     265{
     266  if(! r5kdev)
     267    return 0;
     268  if(r5kdev->urbs) {
     269    if(r5kdev->streaming)
     270      r5000_stop_stream(r5kdev);
     271    r5000_free_urbs(r5kdev);
     272  }
     273#ifdef R5K_DEBUG
     274    if(r5kdev->fd >= 0)
     275      close(r5kdev->fd);
     276#endif
     277  usb_close(r5kdev->handle);
     278  free(r5kdev);
     279  return 0;
     280}
     281#if 0
     282void r5000_force_pmt(r5kdev_t *r5kdev, unsigned char *buf)
     283{
     284  int pid = ((buf[1] << 8) | buf[2]) & 0x1fff;
     285  if(pid == 0x00 && r5000_pmt_state & R5K_PMT_READY) {
     286    int i;
     287    for(i = 0; i < r5000_pmt_count; i++) {
     288      unsigned char *ptr = r5000_pmt + 188*i;
     289      r5000_pmt_cc = (r5000_pmt_cc +1) &0xf;
     290      ptr[3] = (ptr[3] & 0xf0) | r5000_pmt_cc;
     291      r5kdev->cb(ptr, 188, r5kdev->cb_data);
     292    }
     293  } else if(pid == 0x21) {
     294    r5000_pmt_cc = buf[3] & 0xf;
     295    if(buf[1] & 0x40 && buf[4] == 0x00 && buf[5] == 0x02) {
     296      if(r5000_pmt_state & R5K_PMT_START) {
     297        memcpy(r5000_pmt, r5000_pmt_partial, 188 * r5000_pmt_partial_count);
     298        r5000_pmt_count = r5000_pmt_partial_count;
     299        r5000_pmt_state |= R5K_PMT_READY;
     300      }
     301      memcpy(r5000_pmt_partial, buf, 188);
     302      r5000_pmt_partial_count = 1;
     303      r5000_pmt_state |= R5K_PMT_START;
     304    } else if(r5000_pmt_state & R5K_PMT_START) {
     305      memcpy(r5000_pmt_partial + 188*r5000_pmt_partial_count, buf, 188);
     306      r5000_pmt_partial_count++;
     307    }
     308  }
     309}
     310#else
     311//taken and adapted from libdtv, (c) Rolf Hakenes
     312// CRC32 lookup table for polynomial 0x04c11db7
     313static unsigned int crc_table[256] = {
     314   0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
     315   0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
     316   0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
     317   0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
     318   0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
     319   0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
     320   0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
     321   0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
     322   0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
     323   0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
     324   0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
     325   0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
     326   0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
     327   0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
     328   0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
     329   0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
     330   0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
     331   0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
     332   0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
     333   0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
     334   0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
     335   0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
     336   0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
     337   0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
     338   0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
     339   0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
     340   0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
     341   0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
     342   0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
     343   0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
     344   0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
     345   0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
     346   0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
     347   0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
     348   0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
     349   0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
     350   0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
     351   0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
     352   0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
     353   0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
     354   0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
     355   0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
     356   0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4};
     357
     358void r5000_calc_crc(unsigned char *out, const unsigned char *d, int len)
     359{
     360   register int i;
     361   unsigned int crc = 0xFFFFFFFF;
     362   const unsigned char *u=(unsigned char*)d; // Saves '& 0xff'
     363
     364   for (i=0; i<len; i++)
     365      crc = (crc << 8) ^ crc_table[((crc >> 24) ^ *u++)];
     366
     367   *out++ = (crc >> 24) & 0xff;
     368   *out++ = (crc >> 16) & 0xff;
     369   *out++ = (crc >> 8) & 0xff;
     370   *out++ = (crc >> 0) & 0xff;
     371}
     372
     373#define IS_VIDEO(x) ((x) == 0x02 || (x) == 0x1b)
     374void r5000_send_pmt(r5kdev_t *r5kdev)
     375{
     376  if(r5kdev->pmt_pkt[0] != 0x47) {
     377    int i;
     378    unsigned char *ptr;
     379    unsigned char ts[188] = {
     380    0x47, 0x40, 0x21, 0x10,
     381    0x00, 0x02,
     382    0xb0, 0x00,  //length
     383    0x00, 0x01,
     384    0xc3, 0x00, 0x00,
     385    0xf0, 0x00,  //PCR_PID
     386    0xf0, 0x00}; //Program Info Length
     387    ts[3] = 0x10 | r5kdev->pmt_next_cc;
     388    printf("Building PMT\n");
     389    ptr = ts + 17;
     390    for(i = 0; i < r5kdev->num_pmt_entries; i++) {
     391      *ptr++ = r5kdev->pmt[i].id;
     392      *ptr++ = 0xe0 | (r5kdev->pmt[i].pid>>8);
     393      *ptr++ = r5kdev->pmt[i].pid & 0xff;
     394      *ptr++ = 0xf0;
     395      if(IS_VIDEO(r5kdev->pmt[i].id)) {
     396        *ptr++ = 0x00;
     397        ts[13] = 0xe0 | (r5kdev->pmt[i].pid >> 8);
     398        ts[14] = r5kdev->pmt[i].pid & 0xff;
     399      } else {
     400        *ptr++ = 0x06;
     401        *ptr++ = 0x0a;
     402        *ptr++ = 0x04;
     403        *ptr++ = 0x65;
     404        *ptr++ = 0x6e;
     405        *ptr++ = 0x67;
     406        *ptr++ = 0x00;
     407      }
     408    }
     409    ts[7] = (ptr - ts) - 8/*header*/ + 4/*CRC*/;
     410    r5000_calc_crc(ptr, ts + 5, (ptr - ts) - 5);
     411    memset(ptr+4, 0xff, 188 - (ptr - ts - 4));
     412    memcpy(r5kdev->pmt_pkt, ts, 188);
     413  }
     414  r5kdev->pmt_pkt[3] = 0x10 | r5kdev->pmt_next_cc;
     415  r5kdev->pmt_next_cc = (r5kdev->pmt_next_cc + 1) & 0x0f;
     416  r5kdev->cb(r5kdev->pmt_pkt, 188, r5kdev->cb_data);
     417}
     418
     419void r5000_add_pmt(r5kdev_t *r5kdev, int pid, int table_id)
     420{
     421  int i;
     422  int is_video = IS_VIDEO(table_id);
     423
     424  for(i = 0; i < r5kdev->num_pmt_entries; i++) {
     425    if(r5kdev->pmt[i].pid == pid) {
     426      if(r5kdev->pmt[i].id == table_id)
     427        return;
     428      //different table_id for existing pid.  Reset
     429      r5kdev->num_pmt_entries = 0;
     430      r5kdev->pmt_state = 0;
     431      r5kdev->pmt_pkt[0] = 0;
     432      break;
     433    }
     434    if(is_video) {
     435      if(IS_VIDEO(r5kdev->pmt[i].id)) {
     436        //a video entry already exists.  Reset
     437        r5kdev->num_pmt_entries = 0;
     438        r5kdev->pmt_state = 0;
     439        r5kdev->pmt_pkt[0] = 0;
     440        break;
     441      }
     442    }
     443  }
     444  //Didn't find this PID, add it
     445  if(i == R5K_MAX_PIDS)
     446    return;
     447  printf("Adding %04x: %02x @ %08x\n", pid, table_id, r5kdev->bytes_read);
     448  r5kdev->pmt[r5kdev->num_pmt_entries].pid = pid;
     449  r5kdev->pmt[r5kdev->num_pmt_entries].id  = table_id;
     450  r5kdev->num_pmt_entries++;
     451  r5kdev->pmt_state |= is_video ? 0x01 : 0x02;
     452}
     453
     454void r5000_force_pmt(r5kdev_t *r5kdev, unsigned char *buf)
     455{
     456  int stream_id, pid, afc, af_size = 0;
     457  pid = ((buf[1] << 8) | buf[2]) & 0x1fff;
     458  if(pid == 0x1fff)
     459    return;
     460  if(pid == 0) {
     461    r5kdev->cb(r5000_pat_pkt, 188, r5kdev->cb_data);
     462    if(r5kdev->pmt_state == 0x03)
     463      r5000_send_pmt(r5kdev);
     464  } else {
     465    if(pid != 0x21)
     466      r5kdev->cb(buf, 188, r5kdev->cb_data);
     467    //Only interested in PES packets starting in this TS packet
     468    if (!buf[1] & 0x40)
     469      return;
     470    afc = (buf[3]>>4) & 0x03;
     471    if(afc == 0x2)
     472      return;
     473    if(afc == 0x3)
     474      af_size = buf[4]+1;
     475    if(buf[4+af_size] != 0x00 || buf[5+af_size] != 0x00 || buf[6+af_size] != 0x01)
     476      return;
     477    //We have a PES packet
     478    stream_id = buf[7+af_size];
     479    if((stream_id & 0xf0) == 0xe0) {
     480      //Video stream (we need the adaptation field)
     481      if(afc != 0x03)
     482        return;
     483      if(0) {
     484        int i;
     485        for(i = 0; i < af_size+8; i++) {
     486          printf("%02x ", buf[i]);
     487          if((i %16) == 15)
     488            printf("\n");
     489        }
     490        printf("\n");
     491      }
     492      if(buf[5] & 0x02) {
     493        // Has private data, this is MPEG4
     494        r5000_add_pmt(r5kdev, pid, 0x1b);
     495      } else if(buf[5] & 0x10) {
     496        // Has PCR and no private data, this is MPEG2
     497        r5000_add_pmt(r5kdev, pid, 0x02);
     498      }
     499    } else if((stream_id & 0xf0) == 0xc0) {
     500      //Audio stream
     501      r5000_add_pmt(r5kdev, pid, 0x04);
     502    } else if(stream_id  == 0xbd) {
     503      //Audio stream
     504      r5000_add_pmt(r5kdev, pid, 0x81);
     505    }
     506  }
     507}
     508#endif
     509void r5000_process_block(r5kdev_t *r5kdev, unsigned char *buf, int len)
     510{
     511  int pos;
     512  int sync = 1;
     513  if(! r5kdev->streaming)
     514    return;
     515  if(len <= 0)
     516    return;
     517
     518  pos = r5kdev->offset;
     519#ifdef R5K_DEBUG
     520  if(r5kdev->fd >= 0)
     521    write(r5kdev->fd, buf, len);
     522#endif
     523  while(pos < len) {
     524      if(buf[pos] != 0x47 || (pos <len-1 && buf[pos+1] == 0xff))
     525        goto nosync;
     526      // If we get here, buf[pos] == 0x47
     527      if(r5kdev->offset) {
     528        //previous data exists and is part of a good packet
     529        memcpy(r5kdev->leftover+188-r5kdev->offset, buf, r5kdev->offset);
     530        r5000_force_pmt(r5kdev, r5kdev->leftover);
     531        r5kdev->offset = 0;
     532      }
     533      if(pos+188 < len) {
     534        //at least one full packet is available
     535        if(buf[pos+188] != 0x47)
     536          goto nosync;
     537      } else {
     538        //Out of data, but the partial packet may be ok.
     539        memcpy(r5kdev->leftover, buf+pos, len-pos);
     540        r5kdev->offset = 188-(len-pos);
     541        break;
     542      }
     543      //If we get here, we have a good packet
     544      r5000_force_pmt(r5kdev, buf+pos);
     545      if(! sync)
     546        printf("(%d) Found sync at %08x\n", r5kdev->nexturb, r5kdev->bytes_read+pos);
     547      sync = 1;
     548      pos+=188;
     549      continue;
     550  nosync:
     551      r5kdev->offset=0;
     552      if(sync)
     553        printf("(%d)Lost sync at %08x\n", r5kdev->nexturb, r5kdev->bytes_read+pos);
     554      sync = 0;
     555      pos++;
     556  }
     557  r5kdev->bytes_read += len;
     558}
     559
     560int r5000_loop_iterate(r5kdev_t *r5kdev, int timeout_usec)
     561{
     562  struct usb_dev_handle *handle = (struct usb_dev_handle *)r5kdev->handle;
     563  struct usbdevfs_urb *urbs = (struct usbdevfs_urb *)r5kdev->urbs;
     564  int len;
     565  unsigned char *buf;
     566  struct timeval tv1, tv2;
     567  if(! r5kdev->streaming)
     568    return -1;
     569  gettimeofday(&tv1, NULL);
     570  len = usb_urb_reap(handle, &urbs[r5kdev->nexturb], timeout_usec);
     571  gettimeofday(&tv2, NULL);
     572  //printf("%u.%06u (%u.%06u) Read %d bytes\n", (unsigned int)tv1.tv_sec, (unsigned int)tv1.tv_usec, (unsigned int)tv2.tv_sec, (unsigned int)tv2.tv_usec, len);
     573  if(len <= 0) {
     574    if(len != -ETIMEDOUT)
     575      printf("(%d) Reap failed at %08x: %s\n", r5kdev->nexturb, r5kdev->bytes_read, strerror(errno));
     576    return len;
     577  }
     578  buf = r5kdev->buffer + (R5K_URB_BUFFER_SIZE*r5kdev->nexturb);
     579  r5000_process_block(r5kdev, buf, len);
     580  usb_urb_submit(handle, &urbs[r5kdev->nexturb], NULL);
     581  r5kdev->nexturb = (r5kdev->nexturb + 1) % MAX_URBS_IN_FLIGHT;
     582  return 0;
     583}
     584
     585int r5000_read_status(r5kdev_t *r5kdev, unsigned char *buf)
     586{
     587  return usb_bulk_read(r5kdev->handle, 129, buf, 128, 5000);
     588}
     589
     590int r5000_get_power_state(r5kdev_t *r5kdev)
     591{
     592  unsigned char data1[1]  = { 0x30 };
     593  unsigned char data2[0x80];
     594  while(1) {
     595    r5000_read_status(r5kdev, data2);
     596    usb_bulk_write(r5kdev->handle, 1, data1, 1, 5000);
     597    usleep(100000);
     598    if(usb_bulk_read(r5kdev->handle, 1, data2, 2, 5000) == 2 &&
     599       data2[0] == 0x0a && (data2[1] & 0x4e) == 0x4c)
     600      return (!!(data2[1] == 0x4d));
     601    usleep(100000);
     602  }
     603}
     604
     605int r5000_toggle_on_off(r5kdev_t *r5kdev)
     606{
     607  unsigned char data1[1]  = { 0x30 };
     608  unsigned char data3[0x80], on_off;
     609  int len;
     610  on_off = r5000_get_power_state(r5kdev);
     611  //printf("Start state: %s\n", on_off ? "On" : "Off");
     612  usb_bulk_write(r5kdev->handle, 1, data1, 1, 5000);
     613  usleep(100000);
     614  usb_bulk_write(r5kdev->handle, 1, (unsigned char *)r5000_button_cmd[R5K_BUTTON_POWER], 0x40, 5000);
     615  usleep(100000);
     616  len = usb_bulk_read(r5kdev->handle, 1, data3, 2, 5000);
     617  usleep(100000);
     618  while(r5000_get_power_state(r5kdev) == on_off)
     619    usleep(100000);
     620  //printf("End state: %s\n", !on_off ? "On" : "Off");
     621  return !on_off;
     622}
     623
     624void r5000_change_channel(r5kdev_t *r5kdev, int chan)
     625{
     626  int pos = 1000000, digit;
     627  unsigned char data2[0x80];
     628  r5000_read_status(r5kdev, data2);
     629  usb_bulk_write(r5kdev->handle, 1, (unsigned char *)r5000_button_cmd[R5K_BUTTON_CLEAR], 0x40, 5000);
     630  usleep(R5000_BUTTON_DELAY);
     631  while(pos != 0 && (chan/pos) == 0)
     632    pos /= 10;
     633  if(pos == 0)
     634    pos = 1;
     635  while(pos != 0) {
     636    digit = chan / pos;
     637    usb_bulk_write(r5kdev->handle, 1, (unsigned char *)r5000_button_cmd[digit], 0x40, 5000);
     638    chan -= digit*pos;
     639    pos/= 10;
     640    usleep(R5000_BUTTON_DELAY);
     641  }
     642  usb_bulk_write(r5kdev->handle, 1, (unsigned char *)r5000_button_cmd[R5K_BUTTON_ENTER], 0x40, 5000);
     643}
     644
     645void r5000_find_stbs(r5kenum_t *devs)
     646{
     647  r5kdev_t r5kdev;
     648  devs->count = 0;
     649  if(! r5000_usb_init) {
     650    usb_init();
     651    r5000_usb_init = 1;
     652  }
     653  while(devs->count < R5K_MAX_DEVS) {
     654    r5kdev.handle = r5000_locate_device(R5K_WARM_VID, R5K_WARM_PID, devs->count);
     655    if(! r5kdev.handle)
     656      break;
     657    if(r5000_get_serial_number(&r5kdev, devs->serial[devs->count]))
     658      devs->count++;
     659    usb_close(r5kdev.handle);
     660  }
     661}
  • new file libs/libmythtv/r5000/r5000.h

    - +  
     1#ifndef R5000_H
     2#define R5000_H
     3
     4#ifndef r5kdev_t
     5#define r5kdev_t void
     6#endif
     7
     8#define R5K_MAX_DEVS 10
     9typedef struct {
     10  unsigned char serial[R5K_MAX_DEVS][8];
     11  int count;
     12} r5kenum_t;
     13
     14extern r5kdev_t *r5000_open(unsigned int (*cb)(unsigned char *buffer, int len, void *callback_data), void *cb_data, const char *serial);
     15extern int r5000_close(r5kdev_t *r5kdev);
     16extern int r5000_start_stream(r5kdev_t *r5kdev);
     17extern int r5000_stop_stream(r5kdev_t *r5kdev);
     18extern int r5000_loop_iterate(r5kdev_t *r5kdev, int timeout_usec);
     19extern int r5000_get_power_state(r5kdev_t *r5kdev);
     20extern int r5000_toggle_on_off(r5kdev_t *r5kdev);
     21extern void r5000_change_channel(r5kdev_t *r5kdev, int chan);
     22extern void r5000_find_stbs(r5kenum_t *devs);
     23#endif //R5000_H
  • new file libs/libmythtv/r5000/r5000buttons.h

    - +  
     1///This has only been verified for a VIP211.  Quite possibly different buttons are needed for different receivers.
     2//
     3#define R5000_BUTTON_DELAY 400000
     4enum {
     5  R5K_BUTTON_0 = 0,
     6  R5K_BUTTON_1,
     7  R5K_BUTTON_2,
     8  R5K_BUTTON_3,
     9  R5K_BUTTON_4,
     10  R5K_BUTTON_5,
     11  R5K_BUTTON_6,
     12  R5K_BUTTON_7,
     13  R5K_BUTTON_8,
     14  R5K_BUTTON_9,
     15  R5K_BUTTON_CLEAR,
     16  R5K_BUTTON_ENTER,
     17  R5K_BUTTON_POWER,
     18  R5K_BUTTON_MAX
     19};
     20const unsigned char r5000_button_cmd[R5K_BUTTON_MAX][0x40] =
     21{
     22//button 0:
     23{
     24 0x00, 0x23, 0x0a, 0x80, 0x00, 0x77, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x1a,
     25 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x1a,
     26 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x1a,
     27 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e,
     28},
     29//button 1:
     30{
     31 0x00, 0x23, 0x0a, 0x80, 0x00, 0x77, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e,
     32 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x1a,
     33 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a,
     34 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e,
     35},
     36//button 2:
     37{
     38 0x00, 0x23, 0x0a, 0x80, 0x00, 0x77, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e,
     39 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x1a,
     40 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a,
     41 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e,
     42},
     43//button 3:
     44{
     45 0x00, 0x23, 0x0a, 0x80, 0x00, 0x77, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e,
     46 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x1a,
     47 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a,
     48 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e,
     49},
     50//button 4:
     51{
     52 0x00, 0x23, 0x0a, 0x80, 0x00, 0x77, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x1a,
     53 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x1a,
     54 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e,
     55 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e,
     56},
     57//button 5:
     58{
     59 0x00, 0x23, 0x0a, 0x80, 0x00, 0x77, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x1a,
     60 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x1a,
     61 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e,
     62 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e,
     63},
     64//button 6:
     65{
     66 0x00, 0x23, 0x0a, 0x80, 0x00, 0x77, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x1a,
     67 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x1a,
     68 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e,
     69 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e,
     70},
     71//button 7:
     72{
     73 0x00, 0x23, 0x0a, 0x80, 0x00, 0x77, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e,
     74 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x1a,
     75 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e,
     76 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e,
     77},
     78//button 8:
     79{
     80 0x00, 0x23, 0x0a, 0x80, 0x00, 0x77, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e,
     81 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x1a,
     82 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e,
     83 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e,
     84},
     85//button 9:
     86{
     87 0x00, 0x23, 0x0a, 0x80, 0x00, 0x77, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e,
     88 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x1a,
     89 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e,
     90 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e,
     91},
     92//Clear:
     93{
     94 0x00, 0x23, 0x0a, 0x80, 0x00, 0x77, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x1a,
     95 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x1a,
     96 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x1a,
     97 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e,
     98},
     99//Enter:
     100{
     101 0x00, 0x23, 0x0a, 0x80, 0x00, 0x77, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x1a,
     102 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x1a,
     103 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x1a,
     104 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e,
     105},
     106//Power
     107{
     108 0x00, 0x23, 0x0a, 0x80, 0x00, 0x77, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a,
     109 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x1a,
     110 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x3a, 0x04, 0x1a, 0x04, 0x1a, 0x04, 0x1a,
     111 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x1a, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e, 0x04, 0x0e,
     112}
     113};