Ticket #10277: firewire_noinput_fix_final.patch

File firewire_noinput_fix_final.patch, 12.4 KB (added by Peter Bennett <pgbennett@…>, 9 years ago)

patch to fix this problem

  • mythtv/libs/libmythtv/firewiredevice.cpp

    a b  
    3030    m_speed(speed),
    3131    m_last_channel(0),      m_last_crc(0),
    3232    m_buffer_cleared(true), m_open_port_cnt(0),
    33     m_lock()
     33    m_lock(), m_is_no_input(false), m_error(false)
    3434{
    3535}
    3636
     
    6666    VERBOSE(VB_RECORD, LOC + "RemoveListener() "<<m_listeners.size());
    6767}
    6868
     69TSDataListener * FirewireDevice::GetTopListener(void) const
     70{
     71    if (m_listeners.empty())
     72        return NULL;
     73    return m_listeners.back();
     74}
     75
     76bool  FirewireDevice::GetAndResetIsNoInput(void)
     77{
     78    if (!m_is_no_input)
     79        return false;
     80    QMutexLocker locker(&m_lock);
     81    bool ret = m_is_no_input;
     82    m_is_no_input = false;
     83    return ret;
     84}
     85
     86
    6987bool FirewireDevice::SetPowerState(bool on)
    7088{
    7189    QMutexLocker locker(&m_lock);
  • mythtv/libs/libmythtv/firewiredevice.h

    a b  
    199199
    200200    virtual void AddListener(TSDataListener*);
    201201    virtual void RemoveListener(TSDataListener*);
     202    bool GetAndResetIsNoInput(void);
    202203
    203204    // Sets
    204205    virtual bool SetPowerState(bool on);
     
    208209    // Gets
    209210    virtual bool IsPortOpen(void) const = 0;
    210211    bool IsSTBBufferCleared(void) const { return m_buffer_cleared; }
     212    TSDataListener * GetTopListener(void) const;
     213    bool IsError(void) const { return m_error; }
    211214
    212215    // non-const Gets
    213216    virtual PowerState GetPowerState(void);
     
    240243    uint                     m_open_port_cnt;
    241244    vector<TSDataListener*>  m_listeners;
    242245    mutable QMutex           m_lock;
     246    // Indicator for trapping "No Input in xxx msec" errors
     247    bool                     m_is_no_input;
     248    bool                     m_error;
    243249
    244250    /// Vendor ID + Model ID to FirewireDevice STB model string
    245251    static QMap<uint64_t,QString> s_id_to_model;
  • mythtv/libs/libmythtv/firewirerecorder.cpp

    a b  
    2828
    2929bool FirewireRecorder::Open(void)
    3030{
    31     if (!isopen)
    32         isopen = channel->GetFirewireDevice()->OpenPort();
    33 
     31    isopen = channel->GetFirewireDevice()->OpenPort();
    3432    return isopen;
    3533}
    3634
    37 void FirewireRecorder::Close(void)
     35bool FirewireRecorder::Close(void)
    3836{
    39     if (isopen)
    40     {
    41         channel->GetFirewireDevice()->ClosePort();
    42         isopen = false;
    43     }
     37    channel->GetFirewireDevice()->ClosePort();
     38    isopen = channel->GetFirewireDevice()->IsPortOpen();
     39    return isopen;
    4440}
    4541
    4642void FirewireRecorder::StartStreaming(void)
     
    5551
    5652void FirewireRecorder::StartRecording(void)
    5753{
    58     VERBOSE(VB_RECORD, LOC + "StartRecording");
    59 
    60     if (!Open())
    61     {
    62         _error = true;
    63         return;
    64     }
    65 
    66     _request_recording = true;
    67     _recording = true;
    68 
    69     StartStreaming();
    70 
    71     while (_request_recording)
    72     {
    73         if (!PauseAndWait())
    74             usleep(50 * 1000);
     54    bool is_no_input = false;
     55    bool must_save_listeners = false;
     56    vector<TSDataListener*>  saved_listeners;
     57    int number_opens = 1;
     58    time_t time_last_reset = 0;
     59
     60    VERBOSE(VB_RECORD, LOC + "StartRecording " + curRecording->GetTitle());
     61
     62    // This loop contains logic to handle a firewire no input error
     63    // When the firewire device reports no input received for 400 msec
     64    // this is indication of a problem that does not go away. To fix the error
     65    // we close the firewire port and open it again in this loop.
     66    do {
     67        while(number_opens > 0)
     68        {
     69            if (!Open())
     70            {
     71                _error = true;
     72                return;
     73            }
     74            number_opens--;
     75        }
     76
     77        _request_recording = true;
     78        _recording = true;
     79
     80        if (saved_listeners.empty())
     81            StartStreaming();
     82        else
     83        {
     84            // Restore Listeners
     85            int count = 0;
     86            TSDataListener* listener;
     87            listener = saved_listeners.back();
     88            while (listener!=NULL)
     89            {
     90                VERBOSE(VB_RECORD, LOC + QString("Restoring listener %1").arg(++count));
     91                channel->GetFirewireDevice()->AddListener(listener);
     92                saved_listeners.pop_back();
     93                if (saved_listeners.empty())
     94                    listener = NULL;
     95                else
     96                    listener = saved_listeners.back();
     97            }
     98        }
     99        // If adding listeners caused a failure, cancel recording
     100        if (channel->GetFirewireDevice()->IsError()) {
     101            _error = true;
     102            StopRecording();
     103        }
     104
     105        while (_request_recording)
     106        {
     107            if (is_no_input)
     108            {
     109                time_t now = time(NULL);
     110                time_t elapsed = now - time_last_reset;
     111                // if less than 30 seconds since last reset, ignore the error
     112                // and let bus reset maybe fix it
     113                if (elapsed <= 30) {
     114                    VERBOSE(VB_RECORD, LOC + QString("Repeated Firewire No Input Error - Try Bus Reset."));
     115                    is_no_input = false;
     116                }
     117                // if between 30 seconds and 5 minutes since last reset - we
     118                // have a persistent error - fail the recording
     119                if (elapsed > 30 && elapsed <= 300) {
     120                    VERBOSE(VB_IMPORTANT, LOC + QString("Repeated Firewire No Input Error - Recording Fails."));
     121                    is_no_input = false;
     122                    _error = true;
     123                    StopRecording();
     124                }
     125                // if more than 5 minutes since last reset, or this is the first,
     126                // proceed with reset
     127                if (elapsed > 300) {
     128                    VERBOSE(VB_IMPORTANT, LOC + QString("Firewire No Input Error - Resetting device."));
     129                    StopRecording();
     130                    is_no_input = false;
     131                    must_save_listeners = true;
     132                    time_last_reset = now;
     133                }
     134            }
     135
     136            if (!PauseAndWait())
     137                usleep(50 * 1000);
     138
     139            // Look at firewire device to see if it is having an error. The call GetAndResetIsNoInput
     140            // resets the indicator in thread safe way so that only one thread will handle repairing
     141            // the broken firewire connection.
     142            if (!must_save_listeners && channel->GetFirewireDevice()->GetAndResetIsNoInput())
     143                is_no_input = true;
     144        }
     145
     146        if (must_save_listeners)
     147        {
     148            // Remove And Save Listeners - may not be thread safe if others are concurrently
     149            // adding or removing listeners
     150            int count = 0;
     151            TSDataListener* listener;
     152            listener = channel->GetFirewireDevice()->GetTopListener();
     153            while (listener != NULL)
     154            {
     155                VERBOSE(VB_RECORD, LOC + QString("Saving listener %1").arg(++count));
     156                saved_listeners.push_back(listener);
     157                channel->GetFirewireDevice()->RemoveListener(listener);
     158                listener = channel->GetFirewireDevice()->GetTopListener();
     159            }
     160            must_save_listeners = false;
     161            number_opens = 0;
     162            while(Close())
     163                number_opens++;
     164            VERBOSE(VB_RECORD, LOC + QString("Number of closes: %1").arg(++number_opens));
     165        }
    75166    }
     167    while(!saved_listeners.empty());
    76168
    77169    StopStreaming();
     170    Close();
    78171    FinishRecording();
    79172
    80173    _recording = false;
  • mythtv/libs/libmythtv/firewirerecorder.h

    a b  
    3434
    3535    // Commands
    3636    bool Open(void);
    37     void Close(void);
     37    bool Close(void);
    3838
    3939    void StartStreaming(void);
    4040    void StopStreaming(void);
  • mythtv/libs/libmythtv/linuxfirewiredevice.cpp

    a b  
    269269        VERBOSE(VB_IMPORTANT, LOC_ERR + QString("Not an STB"));
    270270
    271271        m_lock.unlock();
     272        m_priv->start_stop_port_handler_lock.unlock();
    272273        ClosePort();
    273274        m_lock.lock();
    274275
     
    362363
    363364    if (!m_listeners.empty())
    364365    {
    365         OpenNode();
    366         OpenAVStream();
    367         StartStreaming();
     366        m_error = !OpenNode();
     367        if (!m_error)
     368            m_error = !OpenAVStream();
     369        if (!m_error)
     370            m_error = !StartStreaming();
    368371    }
    369372}
    370373
     
    608611{
    609612    VERBOSE(VB_RECORD, LOC + "RunPortHandler -- start");
    610613    m_lock.lock();
     614    m_is_no_input = false;
    611615    VERBOSE(VB_RECORD, LOC + "RunPortHandler -- got first lock");
    612616    m_priv->is_port_handler_running = true;
    613617
     
    659663
    660664            VERBOSE(VB_IMPORTANT, LOC_WARN + QString("No Input in %1 msec...")
    661665                    .arg(m_priv->no_data_cnt * kNoDataTimeout));
     666            // Set indicator for endless no data errors
     667            // so that FirewireRecorder can reset the device
     668            if (m_priv->no_data_cnt == 8)
     669            {
     670                m_is_no_input = true;
     671            }
    662672        }
    663673
    664674        // Confirm that we won't block, now that we have the lock...
     
    676686            }
    677687        }
    678688    }
    679 
    680689    m_priv->is_port_handler_running = false;
    681690    m_lock.unlock();
    682691    VERBOSE(VB_RECORD, LOC + "RunPortHandler -- end");
  • mythtv/libs/libmythtv/firewiresignalmonitor.cpp

    a b  
    150150
    151151    FirewireDevice *dev = lchan->GetFirewireDevice();
    152152
    153     dev->OpenPort();
    154     dev->AddListener(this);
     153    // This loop contains logic to handle a firewire no input error
     154    // When the firewire device reports no input received for 400 msec
     155    // this is indication of a problem that does not go away. To fix the error
     156    // we close the firewire port and open it again in this loop.
     157    bool is_no_input = false;
     158    bool must_save_listeners = false;
     159    vector<TSDataListener*>  saved_listeners;
     160    int number_opens = 1;
     161    do {
     162        while(number_opens > 0)
     163        {
     164            dev->OpenPort();
     165            number_opens--;
     166        }
     167        if (saved_listeners.empty())
     168            dev->AddListener(this);
     169        else
     170        {
     171            // Restore Listeners
     172            int count = 0;
     173            TSDataListener* listener;
     174            listener = saved_listeners.back();
     175            while (listener!=NULL)
     176            {
     177                VERBOSE(VB_CHANNEL, LOC + QString("Restoring listener %1").arg(++count));
     178                dev->AddListener(listener);
     179                saved_listeners.pop_back();
     180                if (saved_listeners.empty())
     181                    listener = NULL;
     182                else
     183                    listener = saved_listeners.back();
     184            }
     185        }
    155186
    156     while (dtvMonitorRunning && GetStreamData())
    157         usleep(100000);
     187        while (dtvMonitorRunning && GetStreamData() && !must_save_listeners)
     188        {
     189            if (is_no_input)
     190            {
     191                // proceed with reset
     192                VERBOSE(VB_IMPORTANT, LOC + QString("Firewire No Input Error - Resetting device."));
     193                is_no_input = false;
     194                must_save_listeners = true;
     195            }
     196            usleep(100000);
     197
     198            // Look at firewire device to see if it is having an error. The call GetAndResetIsNoInput
     199            // resets the indicator in thread safe way so that only one thread will handle repairing
     200            // the broken firewire connection.
     201            if (!must_save_listeners && dev->GetAndResetIsNoInput())
     202                is_no_input = true;
     203        }
     204        if (must_save_listeners)
     205        {
     206            // Remove And Save Listeners - may not be thread safe if others are concurrently
     207            // adding or removing listeners
     208            int count = 0;
     209            TSDataListener* listener;
     210            listener = dev->GetTopListener();
     211            while (listener != NULL)
     212            {
     213                VERBOSE(VB_CHANNEL, LOC + QString("Saving listener %1").arg(++count));
     214                saved_listeners.push_back(listener);
     215                dev->RemoveListener(listener);
     216                listener = dev->GetTopListener();
     217            }
     218            must_save_listeners = false;
     219            number_opens = 0;
     220            while(dev->ClosePort())
     221                number_opens++;
     222            VERBOSE(VB_CHANNEL, LOC + QString("Number of closes: %1").arg(++number_opens));
     223        }
     224    }
     225    while(!saved_listeners.empty());
    158226
    159227    VERBOSE(VB_CHANNEL, LOC + "RunTableMonitor(): -- shutdown ");
    160228