Ticket #10277: firewire_noinput_fix_V0_25_final.patch

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

Patch for mythtv version 0.25

  • 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
     
    6868        QString("RemoveListener() %1").arg(m_listeners.size()));
    6969}
    7070
     71TSDataListener * FirewireDevice::GetTopListener(void) const
     72{
     73    if (m_listeners.empty())
     74        return NULL;
     75    return m_listeners.back();
     76}
     77
     78bool  FirewireDevice::GetAndResetIsNoInput(void)
     79{
     80    if (!m_is_no_input)
     81        return false;
     82    QMutexLocker locker(&m_lock);
     83    bool ret = m_is_no_input;
     84    m_is_no_input = false;
     85    return ret;
     86}
     87
     88
    7189bool FirewireDevice::SetPowerState(bool on)
    7290{
    7391    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  
    2727
    2828bool FirewireRecorder::Open(void)
    2929{
    30     if (!isopen)
    31     {
    32         isopen = channel->GetFirewireDevice()->OpenPort();
    33         ResetForNewFile();
    34     }
     30    // Note that the firewire device can be opened multiple
     31    // times without being closed in between. The close
     32    // keeps track of this and actually only closes when
     33    // the number of opens gets down to 0
     34
     35    isopen = channel->GetFirewireDevice()->OpenPort();
     36
     37    // ResetForNewFile was called from here. Moved to run()
     38    // because Open() can be called more than once per file
     39    // and we should call ResetForNewFile only once per file.
     40    // -- Peter Bennett 2012-04-06
     41
    3542    return isopen;
    3643}
    3744
    38 void FirewireRecorder::Close(void)
     45bool FirewireRecorder::Close(void)
    3946{
    40     if (isopen)
    41     {
    42         channel->GetFirewireDevice()->ClosePort();
    43         isopen = false;
    44     }
     47    // Close decrements the open count and may leave
     48    // the device open if the open count is not zero.
     49    channel->GetFirewireDevice()->ClosePort();
     50    isopen = channel->GetFirewireDevice()->IsPortOpen();
     51    return isopen;
    4552}
    4653
    4754void FirewireRecorder::StartStreaming(void)
     
    5663
    5764void FirewireRecorder::run(void)
    5865{
    59     LOG(VB_RECORD, LOG_INFO, LOC + "run");
    60 
    61     if (!Open())
    62     {
    63         _error = "Failed to open firewire device";
    64         LOG(VB_GENERAL, LOG_ERR, LOC + _error);
    65         return;
    66     }
    67 
    68     {
    69         QMutexLocker locker(&pauseLock);
    70         request_recording = true;
    71         recording = true;
    72         recordingWait.wakeAll();
    73     }
     66    bool is_no_input = false;
     67    bool must_save_listeners = false;
     68    vector<TSDataListener*>  saved_listeners;
     69    int number_opens = 1;
     70    time_t time_last_reset = 0;
     71    bool done_wakeAll = false;
     72    bool done_ResetForNewFile = false;
     73
     74    LOG(VB_RECORD, LOG_INFO, LOC + "run " + curRecording->GetTitle());
     75
     76    // This loop contains logic to handle a firewire no input
     77    // error. When the firewire device reports no input
     78    // received for 400 msec, this is indication of a problem
     79    // that does not go away. To fix the error we close the
     80    // firewire port and open it again in this loop.
     81    // -- Peter Bennett 2012-04-05
     82
     83    do {
     84        while(number_opens > 0)
     85        {
     86            if (!Open())
     87            {
     88                _error = "Failed to open firewire device";
     89                LOG(VB_GENERAL, LOG_ERR, LOC + _error);
     90                return;
     91            }
     92            number_opens--;
     93        }
     94        // Moved here from Open() because Open() can be called more
     95        // than once per file and we should call ResetForNewFile only
     96        // once per file
     97        // -- Peter Bennett 2012-04-06
     98        if (!done_ResetForNewFile)
     99        {
     100            ResetForNewFile();
     101            done_ResetForNewFile = true;
     102        }
     103        if (!done_wakeAll)
     104        {
     105            QMutexLocker locker(&pauseLock);
     106            request_recording = true;
     107            recording = true;
     108            recordingWait.wakeAll();
     109            done_wakeAll = true;
     110        }
     111        else
     112        {
     113            request_recording = true;
     114            recording = true;
     115        }
    74116
    75     StartStreaming();
     117        if (saved_listeners.empty())
     118            StartStreaming();
     119        else
     120        {
     121            // Restore Listeners
     122            int count = 0;
     123            TSDataListener* listener;
     124            listener = saved_listeners.back();
     125            while (listener!=NULL)
     126            {
     127                LOG(VB_RECORD, LOG_INFO, LOC + QString("Restoring listener %1").arg(++count));
     128                channel->GetFirewireDevice()->AddListener(listener);
     129                saved_listeners.pop_back();
     130                if (saved_listeners.empty())
     131                    listener = NULL;
     132                else
     133                    listener = saved_listeners.back();
     134            }
     135        }
     136        // If adding listeners caused a failure, cancel recording
     137        if (channel->GetFirewireDevice()->IsError()) {
     138            _error = "Failed to add listeners to firewire device";
     139            LOG(VB_GENERAL, LOG_ERR, LOC + _error);
     140        }
     141        while (IsRecordingRequested() && !IsErrored())
     142        {
     143            if (is_no_input)
     144            {
     145                time_t now = time(NULL);
     146                time_t elapsed = now - time_last_reset;
     147                // if less than 30 seconds since last reset,
     148                // ignore the error and let bus reset maybe fix it
     149                if (elapsed <= 30) {
     150                    LOG(VB_RECORD, LOG_ERR, LOC + QString("Repeated Firewire No Input Error - Try Bus Reset."));
     151                    is_no_input = false;
     152                }
     153                // if between 30 seconds and 5 minutes since
     154                // last reset - we have a persistent error -
     155                // fail the recording
     156                if (elapsed > 30 && elapsed <= 300) {
     157                    LOG(VB_GENERAL, LOG_ERR, LOC + QString("Repeated Firewire No Input Error - Recording Fails."));
     158                    is_no_input = false;
     159                    _error = "Repeated Firewire No Input Error - Recording Fails.";
     160                    request_recording = false;
     161                }
     162                // if more than 5 minutes since last reset,
     163                // or this is the first reset, proceed with reset
     164                if (elapsed > 300) {
     165                    LOG(VB_GENERAL, LOG_ERR, LOC + QString("Firewire No Input Error - Resetting device."));
     166                    request_recording = false;
     167                    is_no_input = false;
     168                    must_save_listeners = true;
     169                    time_last_reset = now;
     170                }
     171            }
     172            if (PauseAndWait())
     173                continue;
    76174
    77     while (IsRecordingRequested() && !IsErrored())
    78     {
    79         if (PauseAndWait())
    80             continue;
     175            if (!IsRecordingRequested())
     176                break;
    81177
    82         if (!IsRecordingRequested())
    83             break;
     178            {   // sleep 0.25 seconds unless StopRecording() or Unpause() is called,
     179                // just to avoid running this too often.
     180                // Changed from 1 second to 250ms to reduce delay in recovering
     181                // from "No Input in 400 msec" errors  - Peter Bennett 5/3/2012
     182                QMutexLocker locker(&pauseLock);
     183                if (!request_recording || request_pause)
     184                    continue;
     185                unpauseWait.wait(&pauseLock, 250);
     186            }
     187
     188            // Look at firewire device to see if it is having
     189            // an error. The call GetAndResetIsNoInput
     190            // resets the indicator in thread safe way so that
     191            // only one thread will handle repairing
     192            // the broken firewire connection.
     193            if (!must_save_listeners && channel->GetFirewireDevice()->GetAndResetIsNoInput())
     194                is_no_input = true;
     195        }
    84196
    85         {   // sleep 1 seconds unless StopRecording() or Unpause() is called,
    86             // just to avoid running this too often.
    87             QMutexLocker locker(&pauseLock);
    88             if (!request_recording || request_pause)
    89                 continue;
    90             unpauseWait.wait(&pauseLock, 1000);
     197        if (must_save_listeners)
     198        {
     199            // Remove And Save Listeners. This may not be thread
     200            // safe if others are concurrently adding or removing
     201            // listeners. That should not happen.
     202            int count = 0;
     203            TSDataListener* listener;
     204            listener = channel->GetFirewireDevice()->GetTopListener();
     205            while (listener != NULL)
     206            {
     207                LOG(VB_RECORD, LOG_INFO, LOC + QString("Saving listener %1").arg(++count));
     208                saved_listeners.push_back(listener);
     209                channel->GetFirewireDevice()->RemoveListener(listener);
     210                listener = channel->GetFirewireDevice()->GetTopListener();
     211            }
     212            must_save_listeners = false;
     213            number_opens = 0;
     214            while(Close())
     215                number_opens++;
     216            LOG(VB_RECORD, LOG_INFO, LOC + QString("Number of closes: %1").arg(++number_opens));
    91217        }
    92218    }
     219    while(!saved_listeners.empty());
    93220
    94221    StopStreaming();
     222    Close();
    95223    FinishRecording();
    96224
    97225    QMutexLocker locker(&pauseLock);
  • 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/firewiresignalmonitor.cpp

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

    a b  
    274274        LOG(VB_GENERAL, LOG_ERR, LOC + QString("Not an STB"));
    275275
    276276        mlocker.unlock();
     277        locker.unlock();
    277278        ClosePort();
    278279
    279280        return false;
     
    352353
    353354    if (!m_listeners.empty())
    354355    {
    355         OpenNode();
    356         OpenAVStream();
    357         StartStreaming();
     356        m_error = !OpenNode();
     357        if (!m_error)
     358            m_error = !OpenAVStream();
     359        if (!m_error)
     360            m_error = !StartStreaming();
    358361    }
    359362}
    360363
     
    590593{
    591594    LOG(VB_RECORD, LOG_INFO, LOC + "RunPortHandler -- start");
    592595    m_lock.lock();
     596    m_is_no_input = false;
    593597    LOG(VB_RECORD, LOG_INFO, LOC + "RunPortHandler -- got first lock");
    594598    m_priv->is_port_handler_running = true;
    595599    m_priv->port_handler_wait.wakeAll();
     
    644648
    645649            LOG(VB_GENERAL, LOG_WARNING, LOC + QString("No Input in %1 msec...")
    646650                    .arg(m_priv->no_data_cnt * kNoDataTimeout));
     651            // Set indicator for endless no data errors
     652            // so that FirewireRecorder can reset the device
     653            if (m_priv->no_data_cnt == 8)
     654            {
     655                m_is_no_input = true;
     656            }
    647657        }
    648658
    649659        // Confirm that we won't block, now that we have the lock...