This is one in a series of patches for adding a non-blocking interface to the I2C driver for supporting the IPMI SMBus driver.
Add a timer to the I2C layer.  This doesn't do much until the
non-blocking code shows up.

Signed-off-by: Corey Minyard <[EMAIL PROTECTED]>

Index: linux-2.6.11-rc4/drivers/i2c/i2c-core.c
===================================================================
--- linux-2.6.11-rc4.orig/drivers/i2c/i2c-core.c
+++ linux-2.6.11-rc4/drivers/i2c/i2c-core.c
@@ -33,6 +33,12 @@
 #include <asm/uaccess.h>
 
 
+static int i2c_stop_timer(struct i2c_adapter * adap);
+static void i2c_start_timer(struct i2c_adapter * adap,
+                           struct i2c_op_q_entry * entry);
+
+#define USEC_PER_JIFFIE (1000000 / HZ)
+
 static LIST_HEAD(adapters);
 static LIST_HEAD(drivers);
 static DECLARE_MUTEX(core_lists);
@@ -143,6 +149,19 @@
        list_add_tail(&adap->list,&adapters);
        INIT_LIST_HEAD(&adap->clients);
 
+       adap->timer = kmalloc(sizeof(*adap->timer), GFP_KERNEL);
+       if (!adap->timer) {
+               res = -ENOMEM;
+               goto out_unlock;
+       }
+               
+       init_timer(&adap->timer->timer);
+       spin_lock_init(&adap->timer->lock);
+       adap->timer->deleted = 0;
+       adap->timer->running = 0;
+       adap->timer->next_call_time = 0;
+       adap->timer->adapter = adap;
+
        /* Add the adapter to the driver core.
         * If the parent pointer is not set up,
         * we add this adapter to the host bus.
@@ -185,6 +204,7 @@
        struct i2c_driver *driver;
        struct i2c_client *client;
        int res = 0;
+       unsigned long flags;
 
        down(&core_lists);
 
@@ -237,6 +257,17 @@
        device_unregister(&adap->dev);
        list_del(&adap->list);
 
+       /* Stop the timer and free its memory */
+       spin_lock_irqsave(&adap->timer->lock, flags);
+       if (i2c_stop_timer(adap)) {
+               spin_unlock_irqrestore(&adap->timer->lock, flags);
+               kfree(adap->timer);
+       } else {
+               adap->timer->deleted = 1;
+               spin_unlock_irqrestore(&adap->timer->lock, flags);
+       }
+       adap->timer = NULL;
+
        /* wait for sysfs to drop all references */
        wait_for_completion(&adap->dev_released);
        wait_for_completion(&adap->class_dev_released);
@@ -583,6 +614,83 @@
 module_exit(i2c_exit);
 
 /* ----------------------------------------------------
+ * Timer operations
+ * ----------------------------------------------------
+ */
+static void i2c_handle_timer(unsigned long data);
+
+static void i2c_start_timer(struct i2c_adapter * adap,
+                           struct i2c_op_q_entry * entry)
+{
+       unsigned int wait_jiffies;
+       struct i2c_timer *t = adap->timer;
+       unsigned long flags;
+
+       wait_jiffies = ((entry->call_again_us + USEC_PER_JIFFIE - 1)
+                       / USEC_PER_JIFFIE);
+       if (wait_jiffies == 0)
+               wait_jiffies = 1;
+       /* This won't be polled from the user code, so
+          start a timer to poll it. */
+       spin_lock_irqsave(&t->lock, flags);
+       if (! t->running) {
+               t->timer.expires = jiffies + wait_jiffies;
+               t->timer.data = (unsigned long) t;
+               t->timer.function = i2c_handle_timer;
+               t->running = 1;
+               t->next_call_time = wait_jiffies * USEC_PER_JIFFIE;
+               add_timer(&t->timer);
+               t->sequence = adap->timer_sequence;
+       }
+       spin_unlock_irqrestore(&t->lock, flags);
+}
+
+/* Returns true if the timer is stopped (or was not running), false if
+   not.  Must be called with the timer lock held. */
+static int i2c_stop_timer(struct i2c_adapter * adap)
+{
+       return (!adap->timer->running || del_timer(&adap->timer->timer));
+}
+
+static void i2c_handle_timer(unsigned long data)
+{
+       struct i2c_timer      * t = (void *) data;
+       struct i2c_adapter    * adap;
+       unsigned long         flags;
+       struct i2c_op_q_entry * entry;
+       unsigned int          sequence_match;
+
+       spin_lock_irqsave(&t->lock, flags);
+       if (t->deleted) {
+               spin_unlock_irqrestore(&t->lock, flags);
+               kfree(t);
+               return;
+       }
+
+       adap = t->adapter;
+       t->running = 0;
+       sequence_match = adap->timer_sequence == t->sequence;
+       spin_unlock_irqrestore(&t->lock, flags);
+
+       entry = i2c_entry_get(adap);
+       pr_debug("i2c_handle_timer: %p %p\n", adap, entry);
+       if (!entry)
+               return;
+
+       if (sequence_match) {
+               /* Poll will go here. */
+
+               if (!entry_completed(entry))
+                       i2c_start_timer(adap, entry);
+       } else if (entry->use_timer)
+               /* We raced in timer deletion, just restart the
+                  timer if necessary. */
+               i2c_start_timer(adap, entry);
+
+       i2c_entry_put(adap, entry);
+}
+
+/* ----------------------------------------------------
  * the functional interface to the i2c busses.
  * ----------------------------------------------------
  */
@@ -1425,6 +1533,21 @@
        if (atomic_dec_and_test(&e->completed)) {
                /* We are the lucky winner!  We get to clean up the
                   entry. */
+               if (e->use_timer) {
+                       unsigned long    flags;
+                       struct i2c_timer *t = adap->timer;
+                       spin_lock_irqsave(&t->lock, flags);
+                       if (!i2c_stop_timer(adap))
+                               /* If we are unable to stop the timer, that
+                                  means the timer has gone off but has not
+                                  yet run the first part of the handler call.
+                                  Increment the sequence so the timer handler
+                                  can detect this. */
+                               adap->timer_sequence++;
+                       else
+                               t->running = 0;
+                       spin_unlock_irqrestore(&t->lock, flags);
+               }
                if (e->complete)
                        e->complete(adap, e);
        }
Index: linux-2.6.11-rc4/include/linux/i2c.h
===================================================================
--- linux-2.6.11-rc4.orig/include/linux/i2c.h
+++ linux-2.6.11-rc4/include/linux/i2c.h
@@ -35,6 +35,7 @@
 #include <linux/completion.h>
 #include <linux/list.h>
 #include <linux/spinlock.h>
+#include <linux/timer.h>
 #include <asm/semaphore.h>
 #include <asm/atomic.h>
 
@@ -244,6 +245,21 @@
 };
 
 /*
+ * The timer has it's own separately allocated data structure because
+ * it needs to be able to exist even if the adapter is deleted (due to
+ * timer cancellation races).
+ */
+struct i2c_timer {
+       spinlock_t lock;
+       int deleted;
+       struct timer_list timer;
+       int running;
+       unsigned int next_call_time;
+       struct i2c_adapter *adapter;
+       unsigned int sequence;
+};
+
+/*
  * i2c_adapter is the structure used to identify a physical i2c bus along
  * with the access algorithms necessary to access it.
  */
@@ -265,6 +281,11 @@
 
        struct semaphore bus_lock;
 
+       /* Used to time non-blocking operations.  The sequence is used
+          to handle race conditions in the timer handler. */
+       struct i2c_timer *timer;
+       unsigned int timer_sequence;
+
        int timeout;
        int retries;
        struct device dev;              /* the adapter device */

Reply via email to