2013-02-10 49 views
2

在Linux內核驅動程序,我想無限重複序列如下:Linux內核驅動程序:IRQ觸發或超時

  • 在時間T,硬件IRQ啓用
  • 時間T和T之間+「大約」15ms,如果IRQ被觸發,則可以達到IRQ回調。我說周圍因爲我沒有使用RT內核,如果它是14或16ms,那很好。在IRQ回調中,我需要寫下get cpu_clock(0)並調用wake_up_interruptible。超時需要被終止。整個過程需要在5ms內重新開始。
  • 如果通過T +「大約」15ms,IRQ沒有被觸發,我需要執行一些其他代碼。那麼應該禁用IRQ。整個過程需要在5ms內重新開始。

因此,通過T +「大約」20ms,在最壞的情況下,整個過程需要重新開始。

請注意,如果IRQ在18ms處被物理觸發,太糟糕了,「我錯過了火車」。我將在下一個序列中捕獲另一個硬件觸發器。

測試時,我是做沿着下面的僞代碼的東西:

INIT_DELAYED_WORK(&priv->work, driver_work); 
INIT_DELAYED_WORK(&priv->timeout, driver_timeout); 
request_irq(priv->irq, driver_interrupt, IRQF_TRIGGER_RISING, "my_irq", priv); 

則:

queue_delayed_work(priv->workq, &priv->work, 0ms); 

static void driver_work(struct work_struct *work) { 
    queue_delayed_work(priv->workq, &priv->timeout, 15ms); 
    priv->interruptCalled = 0; 
    enable_irq(priv->irq); 
} 

然後:

static irqreturn_t driver_interrupt(int irq, void *_priv) { 
    disable_irq_nosync(priv->irq); 
    priv->interruptCalled = 1; 
    cancel_delayed_work(&priv->timeout); 
    priv->stamp = cpu_clock(0); 
    wake_up_interruptible(&driver_wait); 
    queue_delayed_work(priv->workq, &priv->work, 5ms); 
    return IRQ_HANDLED; 

}

和:

static void driver_timeout(struct work_struct *work) { 
    if (priv->interruptCalled == 0) { 
     disable_irq_nosync(priv->irq); 
     //Do other small cleanup 
     queue_delayed_work(priv->workq, &priv->work, 5ms); 
    } 
} 

我想寫一個健壯但簡單的驅動程序。這是一個適當的實施?我怎樣才能改進這個實現?

+0

cancel_delayed_work不時產生「WARNING:at kernel/timer.c:1061 del_timer_sync + 0x44/0x64()」。我怎樣才能避免這種情況? – gregoiregentil 2013-02-10 18:27:41

+0

此警告對應於WARN_ON(in_irq());看起來從irq上下文刪除定時器並不是一個好主意。 – gregoiregentil 2013-02-10 18:34:58

+0

我已經把「cancel_delayed_work(&priv-> timeout);」作爲「driver_work」的第一行。沒有更多的警告,我認爲邏輯沒有被破壞,因爲即使在IRQ被觸發的情況下,即使超時被觸發,priv-> interruptCalled'也會保護'。 – gregoiregentil 2013-02-10 19:03:10

回答

0

回答我自己的問題:問題是queue_delayed_work是基於jiffies。或者5ms不可能,因爲HZ = 100(1 jiffy = 10ms)。 HR定時器帶來了很好的解決方案