i2c: dev: use after free in detach
[cascardo/linux.git] / drivers / i2c / i2c-core.c
index e584d88..af11b65 100644 (file)
@@ -954,48 +954,40 @@ static int i2c_check_addr_busy(struct i2c_adapter *adapter, int addr)
 }
 
 /**
- * i2c_lock_adapter - Get exclusive access to an I2C bus segment
+ * i2c_adapter_lock_bus - Get exclusive access to an I2C bus segment
  * @adapter: Target I2C bus segment
+ * @flags: I2C_LOCK_ROOT_ADAPTER locks the root i2c adapter, I2C_LOCK_SEGMENT
+ *     locks only this branch in the adapter tree
  */
-void i2c_lock_adapter(struct i2c_adapter *adapter)
+static void i2c_adapter_lock_bus(struct i2c_adapter *adapter,
+                                unsigned int flags)
 {
-       struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter);
-
-       if (parent)
-               i2c_lock_adapter(parent);
-       else
-               rt_mutex_lock(&adapter->bus_lock);
+       rt_mutex_lock(&adapter->bus_lock);
 }
-EXPORT_SYMBOL_GPL(i2c_lock_adapter);
 
 /**
- * i2c_trylock_adapter - Try to get exclusive access to an I2C bus segment
+ * i2c_adapter_trylock_bus - Try to get exclusive access to an I2C bus segment
  * @adapter: Target I2C bus segment
+ * @flags: I2C_LOCK_ROOT_ADAPTER trylocks the root i2c adapter, I2C_LOCK_SEGMENT
+ *     trylocks only this branch in the adapter tree
  */
-static int i2c_trylock_adapter(struct i2c_adapter *adapter)
+static int i2c_adapter_trylock_bus(struct i2c_adapter *adapter,
+                                  unsigned int flags)
 {
-       struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter);
-
-       if (parent)
-               return i2c_trylock_adapter(parent);
-       else
-               return rt_mutex_trylock(&adapter->bus_lock);
+       return rt_mutex_trylock(&adapter->bus_lock);
 }
 
 /**
- * i2c_unlock_adapter - Release exclusive access to an I2C bus segment
+ * i2c_adapter_unlock_bus - Release exclusive access to an I2C bus segment
  * @adapter: Target I2C bus segment
+ * @flags: I2C_LOCK_ROOT_ADAPTER unlocks the root i2c adapter, I2C_LOCK_SEGMENT
+ *     unlocks only this branch in the adapter tree
  */
-void i2c_unlock_adapter(struct i2c_adapter *adapter)
+static void i2c_adapter_unlock_bus(struct i2c_adapter *adapter,
+                                  unsigned int flags)
 {
-       struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter);
-
-       if (parent)
-               i2c_unlock_adapter(parent);
-       else
-               rt_mutex_unlock(&adapter->bus_lock);
+       rt_mutex_unlock(&adapter->bus_lock);
 }
-EXPORT_SYMBOL_GPL(i2c_unlock_adapter);
 
 static void i2c_dev_set_name(struct i2c_adapter *adap,
                             struct i2c_client *client)
@@ -1541,7 +1533,14 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
                return -EINVAL;
        }
 
+       if (!adap->lock_bus) {
+               adap->lock_bus = i2c_adapter_lock_bus;
+               adap->trylock_bus = i2c_adapter_trylock_bus;
+               adap->unlock_bus = i2c_adapter_unlock_bus;
+       }
+
        rt_mutex_init(&adap->bus_lock);
+       rt_mutex_init(&adap->mux_lock);
        mutex_init(&adap->userspace_clients_lock);
        INIT_LIST_HEAD(&adap->userspace_clients);
 
@@ -1559,6 +1558,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
        dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
 
        pm_runtime_no_callbacks(&adap->dev);
+       pm_suspend_ignore_children(&adap->dev, true);
        pm_runtime_enable(&adap->dev);
 
 #ifdef CONFIG_I2C_COMPAT
@@ -1594,10 +1594,12 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
 
                        bri->get_scl = get_scl_gpio_value;
                        bri->set_scl = set_scl_gpio_value;
-               } else if (!bri->set_scl || !bri->get_scl) {
+               } else if (bri->recover_bus == i2c_generic_scl_recovery) {
                        /* Generic SCL recovery */
-                       dev_err(&adap->dev, "No {get|set}_gpio() found, not using recovery\n");
-                       adap->bus_recovery_info = NULL;
+                       if (!bri->set_scl || !bri->get_scl) {
+                               dev_err(&adap->dev, "No {get|set}_scl() found, not using recovery\n");
+                               adap->bus_recovery_info = NULL;
+                       }
                }
        }
 
@@ -2309,16 +2311,16 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
 #endif
 
                if (in_atomic() || irqs_disabled()) {
-                       ret = i2c_trylock_adapter(adap);
+                       ret = adap->trylock_bus(adap, I2C_LOCK_SEGMENT);
                        if (!ret)
                                /* I2C activity is ongoing. */
                                return -EAGAIN;
                } else {
-                       i2c_lock_adapter(adap);
+                       i2c_lock_bus(adap, I2C_LOCK_SEGMENT);
                }
 
                ret = __i2c_transfer(adap, msgs, num);
-               i2c_unlock_adapter(adap);
+               i2c_unlock_bus(adap, I2C_LOCK_SEGMENT);
 
                return ret;
        } else {
@@ -2646,7 +2648,7 @@ static u8 i2c_smbus_pec(u8 crc, u8 *p, size_t count)
 static u8 i2c_smbus_msg_pec(u8 pec, struct i2c_msg *msg)
 {
        /* The address will be sent first */
-       u8 addr = (msg->addr << 1) | !!(msg->flags & I2C_M_RD);
+       u8 addr = i2c_8bit_addr_from_msg(msg);
        pec = i2c_smbus_pec(pec, &addr, 1);
 
        /* The data buffer follows */
@@ -3093,7 +3095,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
        flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB;
 
        if (adapter->algo->smbus_xfer) {
-               i2c_lock_adapter(adapter);
+               i2c_lock_bus(adapter, I2C_LOCK_SEGMENT);
 
                /* Retry automatically on arbitration loss */
                orig_jiffies = jiffies;
@@ -3107,7 +3109,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
                                       orig_jiffies + adapter->timeout))
                                break;
                }
-               i2c_unlock_adapter(adapter);
+               i2c_unlock_bus(adapter, I2C_LOCK_SEGMENT);
 
                if (res != -EOPNOTSUPP || !adapter->algo->master_xfer)
                        goto trace;