Merge remote-tracking branch 'extcon/extcon-next'
[deliverable/linux.git] / drivers / extcon / extcon.c
index 8682efc0f57b7557761feef72433ecfad08d237b..78298460d1686a79864c57d228ac6af5e114acf9 100644 (file)
 #define SUPPORTED_CABLE_MAX    32
 #define CABLE_NAME_MAX         30
 
-static const char *extcon_name[] =  {
-       [EXTCON_NONE]                   = "NONE",
+struct __extcon_info {
+       unsigned int type;
+       unsigned int id;
+       const char *name;
+
+} extcon_info[] = {
+       [EXTCON_NONE] = {
+               .type = EXTCON_TYPE_MISC,
+               .id = EXTCON_NONE,
+               .name = "NONE",
+       },
 
        /* USB external connector */
-       [EXTCON_USB]                    = "USB",
-       [EXTCON_USB_HOST]               = "USB-HOST",
+       [EXTCON_USB] = {
+               .type = EXTCON_TYPE_USB,
+               .id = EXTCON_USB,
+               .name = "USB",
+       },
+       [EXTCON_USB_HOST] = {
+               .type = EXTCON_TYPE_USB,
+               .id = EXTCON_USB_HOST,
+               .name = "USB_HOST",
+       },
 
        /* Charging external connector */
-       [EXTCON_CHG_USB_SDP]            = "SDP",
-       [EXTCON_CHG_USB_DCP]            = "DCP",
-       [EXTCON_CHG_USB_CDP]            = "CDP",
-       [EXTCON_CHG_USB_ACA]            = "ACA",
-       [EXTCON_CHG_USB_FAST]           = "FAST-CHARGER",
-       [EXTCON_CHG_USB_SLOW]           = "SLOW-CHARGER",
+       [EXTCON_CHG_USB_SDP] = {
+               .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
+               .id = EXTCON_CHG_USB_SDP,
+               .name = "SDP",
+       },
+       [EXTCON_CHG_USB_DCP] = {
+               .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
+               .id = EXTCON_CHG_USB_DCP,
+               .name = "DCP",
+       },
+       [EXTCON_CHG_USB_CDP] = {
+               .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
+               .id = EXTCON_CHG_USB_CDP,
+               .name = "CDP",
+       },
+       [EXTCON_CHG_USB_ACA] = {
+               .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
+               .id = EXTCON_CHG_USB_ACA,
+               .name = "ACA",
+       },
+       [EXTCON_CHG_USB_FAST] = {
+               .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
+               .id = EXTCON_CHG_USB_FAST,
+               .name = "FAST-CHARGER",
+       },
+       [EXTCON_CHG_USB_SLOW] = {
+               .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
+               .id = EXTCON_CHG_USB_SLOW,
+               .name = "SLOW-CHARGER",
+       },
+       [EXTCON_CHG_WPT] = {
+               .type = EXTCON_TYPE_CHG,
+               .id = EXTCON_CHG_WPT,
+               .name = "WPT",
+       },
 
        /* Jack external connector */
-       [EXTCON_JACK_MICROPHONE]        = "MICROPHONE",
-       [EXTCON_JACK_HEADPHONE]         = "HEADPHONE",
-       [EXTCON_JACK_LINE_IN]           = "LINE-IN",
-       [EXTCON_JACK_LINE_OUT]          = "LINE-OUT",
-       [EXTCON_JACK_VIDEO_IN]          = "VIDEO-IN",
-       [EXTCON_JACK_VIDEO_OUT]         = "VIDEO-OUT",
-       [EXTCON_JACK_SPDIF_IN]          = "SPDIF-IN",
-       [EXTCON_JACK_SPDIF_OUT]         = "SPDIF-OUT",
+       [EXTCON_JACK_MICROPHONE] = {
+               .type = EXTCON_TYPE_JACK,
+               .id = EXTCON_JACK_MICROPHONE,
+               .name = "MICROPHONE",
+       },
+       [EXTCON_JACK_HEADPHONE] = {
+               .type = EXTCON_TYPE_JACK,
+               .id = EXTCON_JACK_HEADPHONE,
+               .name = "HEADPHONE",
+       },
+       [EXTCON_JACK_LINE_IN] = {
+               .type = EXTCON_TYPE_JACK,
+               .id = EXTCON_JACK_LINE_IN,
+               .name = "LINE-IN",
+       },
+       [EXTCON_JACK_LINE_OUT] = {
+               .type = EXTCON_TYPE_JACK,
+               .id = EXTCON_JACK_LINE_OUT,
+               .name = "LINE-OUT",
+       },
+       [EXTCON_JACK_VIDEO_IN] = {
+               .type = EXTCON_TYPE_JACK,
+               .id = EXTCON_JACK_VIDEO_IN,
+               .name = "VIDEO-IN",
+       },
+       [EXTCON_JACK_VIDEO_OUT] = {
+               .type = EXTCON_TYPE_JACK,
+               .id = EXTCON_JACK_VIDEO_OUT,
+               .name = "VIDEO-OUT",
+       },
+       [EXTCON_JACK_SPDIF_IN] = {
+               .type = EXTCON_TYPE_JACK,
+               .id = EXTCON_JACK_SPDIF_IN,
+               .name = "SPDIF-IN",
+       },
+       [EXTCON_JACK_SPDIF_OUT] = {
+               .type = EXTCON_TYPE_JACK,
+               .id = EXTCON_JACK_SPDIF_OUT,
+               .name = "SPDIF-OUT",
+       },
 
        /* Display external connector */
-       [EXTCON_DISP_HDMI]              = "HDMI",
-       [EXTCON_DISP_MHL]               = "MHL",
-       [EXTCON_DISP_DVI]               = "DVI",
-       [EXTCON_DISP_VGA]               = "VGA",
+       [EXTCON_DISP_HDMI] = {
+               .type = EXTCON_TYPE_DISP,
+               .id = EXTCON_DISP_HDMI,
+               .name = "HDMI",
+       },
+       [EXTCON_DISP_MHL] = {
+               .type = EXTCON_TYPE_DISP,
+               .id = EXTCON_DISP_MHL,
+               .name = "MHL",
+       },
+       [EXTCON_DISP_DVI] = {
+               .type = EXTCON_TYPE_DISP,
+               .id = EXTCON_DISP_DVI,
+               .name = "DVI",
+       },
+       [EXTCON_DISP_VGA] = {
+               .type = EXTCON_TYPE_DISP,
+               .id = EXTCON_DISP_VGA,
+               .name = "VGA",
+       },
+       [EXTCON_DISP_DP] = {
+               .type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB,
+               .id = EXTCON_DISP_DP,
+               .name = "DP",
+       },
+       [EXTCON_DISP_HMD] = {
+               .type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB,
+               .id = EXTCON_DISP_HMD,
+               .name = "HMD",
+       },
 
        /* Miscellaneous external connector */
-       [EXTCON_DOCK]                   = "DOCK",
-       [EXTCON_JIG]                    = "JIG",
-       [EXTCON_MECHANICAL]             = "MECHANICAL",
-
-       NULL,
+       [EXTCON_DOCK] = {
+               .type = EXTCON_TYPE_MISC,
+               .id = EXTCON_DOCK,
+               .name = "DOCK",
+       },
+       [EXTCON_JIG] = {
+               .type = EXTCON_TYPE_MISC,
+               .id = EXTCON_JIG,
+               .name = "JIG",
+       },
+       [EXTCON_MECHANICAL] = {
+               .type = EXTCON_TYPE_MISC,
+               .id = EXTCON_MECHANICAL,
+               .name = "MECHANICAL",
+       },
+
+       { /* sentinel */ }
 };
 
 /**
@@ -95,6 +211,16 @@ struct extcon_cable {
        struct device_attribute attr_state;
 
        struct attribute *attrs[3]; /* to be fed to attr_g.attrs */
+
+       union extcon_property_value usb_propval[EXTCON_PROP_USB_CNT];
+       union extcon_property_value chg_propval[EXTCON_PROP_CHG_CNT];
+       union extcon_property_value jack_propval[EXTCON_PROP_JACK_CNT];
+       union extcon_property_value disp_propval[EXTCON_PROP_DISP_CNT];
+
+       unsigned long usb_bits[BITS_TO_LONGS(EXTCON_PROP_USB_CNT)];
+       unsigned long chg_bits[BITS_TO_LONGS(EXTCON_PROP_CHG_CNT)];
+       unsigned long jack_bits[BITS_TO_LONGS(EXTCON_PROP_JACK_CNT)];
+       unsigned long disp_bits[BITS_TO_LONGS(EXTCON_PROP_DISP_CNT)];
 };
 
 static struct class *extcon_class;
@@ -147,14 +273,93 @@ static int find_cable_index_by_id(struct extcon_dev *edev, const unsigned int id
        return -EINVAL;
 }
 
-static bool is_extcon_changed(u32 prev, u32 new, int idx, bool *attached)
+static int get_extcon_type(unsigned int prop)
 {
-       if (((prev >> idx) & 0x1) != ((new >> idx) & 0x1)) {
-               *attached = ((new >> idx) & 0x1) ? true : false;
-               return true;
+       switch (prop) {
+       case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX:
+               return EXTCON_TYPE_USB;
+       case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX:
+               return EXTCON_TYPE_CHG;
+       case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX:
+               return EXTCON_TYPE_JACK;
+       case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX:
+               return EXTCON_TYPE_DISP;
+       default:
+               return -EINVAL;
        }
+}
+
+static bool is_extcon_attached(struct extcon_dev *edev, unsigned int index)
+{
+       return !!(edev->state & BIT(index));
+}
+
+static bool is_extcon_changed(struct extcon_dev *edev, int index,
+                               bool new_state)
+{
+       int state = !!(edev->state & BIT(index));
+       return (state != new_state);
+}
+
+static bool is_extcon_property_supported(unsigned int id, unsigned int prop)
+{
+       int type;
+
+       /* Check whether the property is supported or not. */
+       type = get_extcon_type(prop);
+       if (type < 0)
+               return false;
 
-       return false;
+       /* Check whether a specific extcon id supports the property or not. */
+       return !!(extcon_info[id].type & type);
+}
+
+static int is_extcon_property_capability(struct extcon_dev *edev,
+                               unsigned int id, int index,unsigned int prop)
+{
+       struct extcon_cable *cable;
+       int type, ret;
+
+       /* Check whether the property is supported or not. */
+       type = get_extcon_type(prop);
+       if (type < 0)
+               return type;
+
+       cable = &edev->cables[index];
+
+       switch (type) {
+       case EXTCON_TYPE_USB:
+               ret = test_bit(prop - EXTCON_PROP_USB_MIN, cable->usb_bits);
+               break;
+       case EXTCON_TYPE_CHG:
+               ret = test_bit(prop - EXTCON_PROP_CHG_MIN, cable->chg_bits);
+               break;
+       case EXTCON_TYPE_JACK:
+               ret = test_bit(prop - EXTCON_PROP_JACK_MIN, cable->jack_bits);
+               break;
+       case EXTCON_TYPE_DISP:
+               ret = test_bit(prop - EXTCON_PROP_DISP_MIN, cable->disp_bits);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static void init_property(struct extcon_dev *edev, unsigned int id, int index)
+{
+       unsigned int type = extcon_info[id].type;
+       struct extcon_cable *cable = &edev->cables[index];
+
+       if (EXTCON_TYPE_USB & type)
+               memset(cable->usb_propval, 0, sizeof(cable->usb_propval));
+       if (EXTCON_TYPE_CHG & type)
+               memset(cable->chg_propval, 0, sizeof(cable->chg_propval));
+       if (EXTCON_TYPE_JACK & type)
+               memset(cable->jack_propval, 0, sizeof(cable->jack_propval));
+       if (EXTCON_TYPE_DISP & type)
+               memset(cable->disp_propval, 0, sizeof(cable->disp_propval));
 }
 
 static ssize_t state_show(struct device *dev, struct device_attribute *attr,
@@ -168,32 +373,13 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr,
 
        for (i = 0; i < edev->max_supported; i++) {
                count += sprintf(buf + count, "%s=%d\n",
-                               extcon_name[edev->supported_cable[i]],
+                               extcon_info[edev->supported_cable[i]].name,
                                 !!(edev->state & (1 << i)));
        }
 
        return count;
 }
-
-static ssize_t state_store(struct device *dev, struct device_attribute *attr,
-                          const char *buf, size_t count)
-{
-       u32 state;
-       ssize_t ret = 0;
-       struct extcon_dev *edev = dev_get_drvdata(dev);
-
-       ret = sscanf(buf, "0x%x", &state);
-       if (ret == 0)
-               ret = -EINVAL;
-       else
-               ret = extcon_set_state(edev, state);
-
-       if (ret < 0)
-               return ret;
-
-       return count;
-}
-static DEVICE_ATTR_RW(state);
+static DEVICE_ATTR_RO(state);
 
 static ssize_t name_show(struct device *dev, struct device_attribute *attr,
                char *buf)
@@ -212,7 +398,7 @@ static ssize_t cable_name_show(struct device *dev,
        int i = cable->cable_index;
 
        return sprintf(buf, "%s\n",
-                       extcon_name[cable->edev->supported_cable[i]]);
+                       extcon_info[cable->edev->supported_cable[i]].name);
 }
 
 static ssize_t cable_state_show(struct device *dev,
@@ -224,26 +410,17 @@ static ssize_t cable_state_show(struct device *dev,
        int i = cable->cable_index;
 
        return sprintf(buf, "%d\n",
-                      extcon_get_cable_state_(cable->edev,
-                                              cable->edev->supported_cable[i]));
+               extcon_get_state(cable->edev, cable->edev->supported_cable[i]));
 }
 
 /**
- * extcon_update_state() - Update the cable attach states of the extcon device
- *                        only for the masked bits.
- * @edev:      the extcon device
- * @mask:      the bit mask to designate updated bits.
- * @state:     new cable attach status for @edev
- *
- * Changing the state sends uevent with environment variable containing
- * the name of extcon device (envp[0]) and the state output (envp[1]).
- * Tizen uses this format for extcon device to get events from ports.
- * Android uses this format as well.
+ * extcon_sync()       - Synchronize the states for both the attached/detached
+ * @edev:              the extcon device that has the cable.
  *
- * Note that the notifier provides which bits are changed in the state
- * variable with the val parameter (second) to the callback.
+ * This function send a notification to synchronize the all states of a
+ * specific external connector
  */
-int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
+int extcon_sync(struct extcon_dev *edev, unsigned int id)
 {
        char name_buf[120];
        char state_buf[120];
@@ -252,100 +429,102 @@ int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
        int env_offset = 0;
        int length;
        int index;
+       int state;
        unsigned long flags;
-       bool attached;
 
        if (!edev)
                return -EINVAL;
 
-       spin_lock_irqsave(&edev->lock, flags);
+       index = find_cable_index_by_id(edev, id);
+       if (index < 0)
+               return index;
 
-       if (edev->state != ((edev->state & ~mask) | (state & mask))) {
-               u32 old_state;
+       spin_lock_irqsave(&edev->lock, flags);
 
-               if (check_mutually_exclusive(edev, (edev->state & ~mask) |
-                                                  (state & mask))) {
-                       spin_unlock_irqrestore(&edev->lock, flags);
-                       return -EPERM;
-               }
+       state = !!(edev->state & BIT(index));
+       raw_notifier_call_chain(&edev->nh[index], state, edev);
 
-               old_state = edev->state;
-               edev->state &= ~mask;
-               edev->state |= state & mask;
+       /* This could be in interrupt handler */
+       prop_buf = (char *)get_zeroed_page(GFP_ATOMIC);
+       if (!prop_buf) {
+               /* Unlock early before uevent */
+               spin_unlock_irqrestore(&edev->lock, flags);
 
-               for (index = 0; index < edev->max_supported; index++) {
-                       if (is_extcon_changed(old_state, edev->state, index,
-                                             &attached))
-                               raw_notifier_call_chain(&edev->nh[index],
-                                                       attached, edev);
-               }
+               dev_err(&edev->dev, "out of memory in extcon_set_state\n");
+               kobject_uevent(&edev->dev.kobj, KOBJ_CHANGE);
 
-               /* This could be in interrupt handler */
-               prop_buf = (char *)get_zeroed_page(GFP_ATOMIC);
-               if (prop_buf) {
-                       length = name_show(&edev->dev, NULL, prop_buf);
-                       if (length > 0) {
-                               if (prop_buf[length - 1] == '\n')
-                                       prop_buf[length - 1] = 0;
-                               snprintf(name_buf, sizeof(name_buf),
-                                       "NAME=%s", prop_buf);
-                               envp[env_offset++] = name_buf;
-                       }
-                       length = state_show(&edev->dev, NULL, prop_buf);
-                       if (length > 0) {
-                               if (prop_buf[length - 1] == '\n')
-                                       prop_buf[length - 1] = 0;
-                               snprintf(state_buf, sizeof(state_buf),
-                                       "STATE=%s", prop_buf);
-                               envp[env_offset++] = state_buf;
-                       }
-                       envp[env_offset] = NULL;
-                       /* Unlock early before uevent */
-                       spin_unlock_irqrestore(&edev->lock, flags);
+               return 0;
+       }
 
-                       kobject_uevent_env(&edev->dev.kobj, KOBJ_CHANGE, envp);
-                       free_page((unsigned long)prop_buf);
-               } else {
-                       /* Unlock early before uevent */
-                       spin_unlock_irqrestore(&edev->lock, flags);
+       length = name_show(&edev->dev, NULL, prop_buf);
+       if (length > 0) {
+               if (prop_buf[length - 1] == '\n')
+                       prop_buf[length - 1] = 0;
+               snprintf(name_buf, sizeof(name_buf), "NAME=%s", prop_buf);
+               envp[env_offset++] = name_buf;
+       }
 
-                       dev_err(&edev->dev, "out of memory in extcon_set_state\n");
-                       kobject_uevent(&edev->dev.kobj, KOBJ_CHANGE);
-               }
-       } else {
-               /* No changes */
-               spin_unlock_irqrestore(&edev->lock, flags);
+       length = state_show(&edev->dev, NULL, prop_buf);
+       if (length > 0) {
+               if (prop_buf[length - 1] == '\n')
+                       prop_buf[length - 1] = 0;
+               snprintf(state_buf, sizeof(state_buf), "STATE=%s", prop_buf);
+               envp[env_offset++] = state_buf;
        }
+       envp[env_offset] = NULL;
+
+       /* Unlock early before uevent */
+       spin_unlock_irqrestore(&edev->lock, flags);
+       kobject_uevent_env(&edev->dev.kobj, KOBJ_CHANGE, envp);
+       free_page((unsigned long)prop_buf);
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(extcon_update_state);
+EXPORT_SYMBOL_GPL(extcon_sync);
 
 /**
- * extcon_set_state() - Set the cable attach states of the extcon device.
- * @edev:      the extcon device
- * @state:     new cable attach status for @edev
- *
- * Note that notifier provides which bits are changed in the state
- * variable with the val parameter (second) to the callback.
+ * extcon_get_state() - Get the state of a external connector.
+ * @edev:      the extcon device that has the cable.
+ * @id:                the unique id of each external connector in extcon enumeration.
  */
-int extcon_set_state(struct extcon_dev *edev, u32 state)
+int extcon_get_state(struct extcon_dev *edev, const unsigned int id)
 {
+       int index, state;
+       unsigned long flags;
+
        if (!edev)
                return -EINVAL;
 
-       return extcon_update_state(edev, 0xffffffff, state);
+       index = find_cable_index_by_id(edev, id);
+       if (index < 0)
+               return index;
+
+       spin_lock_irqsave(&edev->lock, flags);
+       state = is_extcon_attached(edev, index);
+       spin_unlock_irqrestore(&edev->lock, flags);
+
+       return state;
 }
-EXPORT_SYMBOL_GPL(extcon_set_state);
+EXPORT_SYMBOL_GPL(extcon_get_state);
 
 /**
- * extcon_get_cable_state_() - Get the status of a specific cable.
- * @edev:      the extcon device that has the cable.
- * @id:                the unique id of each external connector in extcon enumeration.
+ * extcon_set_state() - Set the state of a external connector.
+ *                     without a notification.
+ * @edev:              the extcon device that has the cable.
+ * @id:                        the unique id of each external connector
+ *                     in extcon enumeration.
+ * @state:             the new cable status. The default semantics is
+ *                     true: attached / false: detached.
+ *
+ * This function only set the state of a external connector without
+ * a notification. To synchronize the data of a external connector,
+ * use extcon_set_state_sync() and extcon_sync().
  */
-int extcon_get_cable_state_(struct extcon_dev *edev, const unsigned int id)
+int extcon_set_state(struct extcon_dev *edev, unsigned int id,
+                               bool cable_state)
 {
-       int index;
+       unsigned long flags;
+       int index, ret = 0;
 
        if (!edev)
                return -EINVAL;
@@ -354,41 +533,338 @@ int extcon_get_cable_state_(struct extcon_dev *edev, const unsigned int id)
        if (index < 0)
                return index;
 
-       if (edev->max_supported && edev->max_supported <= index)
-               return -EINVAL;
+       spin_lock_irqsave(&edev->lock, flags);
+
+       /* Check whether the external connector's state is changed. */
+       if (!is_extcon_changed(edev, index, cable_state))
+               goto out;
+
+       if (check_mutually_exclusive(edev,
+               (edev->state & ~BIT(index)) | (cable_state & BIT(index)))) {
+               ret = -EPERM;
+               goto out;
+       }
+
+       /*
+        * Initialize the value of extcon property before setting
+        * the detached state for an external connector.
+        */
+       if (!cable_state)
+               init_property(edev, id, index);
+
+       /* Update the state for a external connector. */
+       if (cable_state)
+               edev->state |= BIT(index);
+       else
+               edev->state &= ~(BIT(index));
+out:
+       spin_unlock_irqrestore(&edev->lock, flags);
 
-       return !!(edev->state & (1 << index));
+       return ret;
 }
-EXPORT_SYMBOL_GPL(extcon_get_cable_state_);
+EXPORT_SYMBOL_GPL(extcon_set_state);
 
 /**
- * extcon_set_cable_state_() - Set the status of a specific cable.
+ * extcon_set_state_sync() - Set the state of a external connector
+ *                     with a notification.
  * @edev:              the extcon device that has the cable.
  * @id:                        the unique id of each external connector
  *                     in extcon enumeration.
  * @state:             the new cable status. The default semantics is
  *                     true: attached / false: detached.
+ *
+ * This function set the state of external connector and synchronize the data
+ * by usning a notification.
  */
-int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id,
+int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id,
                                bool cable_state)
 {
-       u32 state;
+       int ret, index;
+       unsigned long flags;
+
+       index = find_cable_index_by_id(edev, id);
+       if (index < 0)
+               return index;
+
+       /* Check whether the external connector's state is changed. */
+       spin_lock_irqsave(&edev->lock, flags);
+       ret = is_extcon_changed(edev, index, cable_state);
+       spin_unlock_irqrestore(&edev->lock, flags);
+       if (!ret)
+               return 0;
+
+       ret = extcon_set_state(edev, id, cable_state);
+       if (ret < 0)
+               return ret;
+
+       return extcon_sync(edev, id);
+}
+EXPORT_SYMBOL_GPL(extcon_set_state_sync);
+
+/**
+ * extcon_get_property() - Get the property value of a specific cable.
+ * @edev:              the extcon device that has the cable.
+ * @id:                        the unique id of each external connector
+ *                     in extcon enumeration.
+ * @prop:              the property id among enum extcon_property.
+ * @prop_val:          the pointer which store the value of property.
+ *
+ * When getting the property value of external connector, the external connector
+ * should be attached. If detached state, function just return 0 without
+ * property value. Also, the each property should be included in the list of
+ * supported properties according to the type of external connectors.
+ *
+ * Returns 0 if success or error number if fail
+ */
+int extcon_get_property(struct extcon_dev *edev, unsigned int id,
+                               unsigned int prop,
+                               union extcon_property_value *prop_val)
+{
+       struct extcon_cable *cable;
+       unsigned long flags;
+       int index, ret = 0;
+
+       *prop_val = (union extcon_property_value)(0);
+
+       if (!edev)
+               return -EINVAL;
+
+       /* Check whether the property is supported or not */
+       if (!is_extcon_property_supported(id, prop))
+               return -EINVAL;
+
+       /* Find the cable index of external connector by using id */
+       index = find_cable_index_by_id(edev, id);
+       if (index < 0)
+               return index;
+
+       spin_lock_irqsave(&edev->lock, flags);
+
+       /* Check whether the property is available or not. */
+       if (!is_extcon_property_capability(edev, id, index, prop)) {
+               spin_unlock_irqrestore(&edev->lock, flags);
+               return -EPERM;
+       }
+
+       /*
+        * Check whether the external connector is attached.
+        * If external connector is detached, the user can not
+        * get the property value.
+        */
+       if (!is_extcon_attached(edev, index)) {
+               spin_unlock_irqrestore(&edev->lock, flags);
+               return 0;
+       }
+
+       cable = &edev->cables[index];
+
+       /* Get the property value according to extcon type */
+       switch (prop) {
+       case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX:
+               *prop_val = cable->usb_propval[prop - EXTCON_PROP_USB_MIN];
+               break;
+       case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX:
+               *prop_val = cable->chg_propval[prop - EXTCON_PROP_CHG_MIN];
+               break;
+       case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX:
+               *prop_val = cable->jack_propval[prop - EXTCON_PROP_JACK_MIN];
+               break;
+       case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX:
+               *prop_val = cable->disp_propval[prop - EXTCON_PROP_DISP_MIN];
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       spin_unlock_irqrestore(&edev->lock, flags);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(extcon_get_property);
+
+/**
+ * extcon_set_property() - Set the property value of a specific cable.
+ * @edev:              the extcon device that has the cable.
+ * @id:                        the unique id of each external connector
+ *                     in extcon enumeration.
+ * @prop:              the property id among enum extcon_property.
+ * @prop_val:          the pointer including the new value of property.
+ *
+ * The each property should be included in the list of supported properties
+ * according to the type of external connectors.
+ *
+ * Returns 0 if success or error number if fail
+ */
+int extcon_set_property(struct extcon_dev *edev, unsigned int id,
+                               unsigned int prop,
+                               union extcon_property_value prop_val)
+{
+       struct extcon_cable *cable;
+       unsigned long flags;
+       int index, ret = 0;
+
+       if (!edev)
+               return -EINVAL;
+
+       /* Check whether the property is supported or not */
+       if (!is_extcon_property_supported(id, prop))
+               return -EINVAL;
+
+       /* Find the cable index of external connector by using id */
+       index = find_cable_index_by_id(edev, id);
+       if (index < 0)
+               return index;
+
+       spin_lock_irqsave(&edev->lock, flags);
+
+       /* Check whether the property is available or not. */
+       if (!is_extcon_property_capability(edev, id, index, prop)) {
+               spin_unlock_irqrestore(&edev->lock, flags);
+               return -EPERM;
+       }
+
+       cable = &edev->cables[index];
+
+       /* Set the property value according to extcon type */
+       switch (prop) {
+       case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX:
+               cable->usb_propval[prop - EXTCON_PROP_USB_MIN] = prop_val;
+               break;
+       case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX:
+               cable->chg_propval[prop - EXTCON_PROP_CHG_MIN] = prop_val;
+               break;
+       case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX:
+               cable->jack_propval[prop - EXTCON_PROP_JACK_MIN] = prop_val;
+               break;
+       case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX:
+               cable->disp_propval[prop - EXTCON_PROP_DISP_MIN] = prop_val;
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       spin_unlock_irqrestore(&edev->lock, flags);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(extcon_set_property);
+
+/**
+ * extcon_set_property_sync() - Set the property value of a specific cable
+                       with a notification.
+ * @prop_val:          the pointer including the new value of property.
+ *
+ * When setting the property value of external connector, the external connector
+ * should be attached. The each property should be included in the list of
+ * supported properties according to the type of external connectors.
+ *
+ * Returns 0 if success or error number if fail
+ */
+int extcon_set_property_sync(struct extcon_dev *edev, unsigned int id,
+                               unsigned int prop,
+                               union extcon_property_value prop_val)
+{
+       int ret;
+
+       ret = extcon_set_property(edev, id, prop, prop_val);
+       if (ret < 0)
+               return ret;
+
+       return extcon_sync(edev, id);
+}
+EXPORT_SYMBOL_GPL(extcon_set_property_sync);
+
+/**
+ * extcon_get_property_capability() - Get the capability of property
+ *                     of an external connector.
+ * @edev:              the extcon device that has the cable.
+ * @id:                        the unique id of each external connector
+ *                     in extcon enumeration.
+ * @prop:              the property id among enum extcon_property.
+ *
+ * Returns 1 if the property is available or 0 if not available.
+ */
+int extcon_get_property_capability(struct extcon_dev *edev, unsigned int id,
+                                       unsigned int prop)
+{
        int index;
 
        if (!edev)
                return -EINVAL;
 
+       /* Check whether the property is supported or not */
+       if (!is_extcon_property_supported(id, prop))
+               return -EINVAL;
+
+       /* Find the cable index of external connector by using id */
        index = find_cable_index_by_id(edev, id);
        if (index < 0)
                return index;
 
-       if (edev->max_supported && edev->max_supported <= index)
+       return is_extcon_property_capability(edev, id, index, prop);
+}
+EXPORT_SYMBOL_GPL(extcon_get_property_capability);
+
+/**
+ * extcon_set_property_capability() - Set the capability of a property
+ *                     of an external connector.
+ * @edev:              the extcon device that has the cable.
+ * @id:                        the unique id of each external connector
+ *                     in extcon enumeration.
+ * @prop:              the property id among enum extcon_property.
+ *
+ * This function set the capability of a property for an external connector
+ * to mark the bit in capability bitmap which mean the available state of
+ * a property.
+ *
+ * Returns 0 if success or error number if fail
+ */
+int extcon_set_property_capability(struct extcon_dev *edev, unsigned int id,
+                                       unsigned int prop)
+{
+       struct extcon_cable *cable;
+       int index, type, ret = 0;
+
+       if (!edev)
                return -EINVAL;
 
-       state = cable_state ? (1 << index) : 0;
-       return extcon_update_state(edev, 1 << index, state);
+       /* Check whether the property is supported or not. */
+       if (!is_extcon_property_supported(id, prop))
+               return -EINVAL;
+
+       /* Find the cable index of external connector by using id. */
+       index = find_cable_index_by_id(edev, id);
+       if (index < 0)
+               return index;
+
+       type = get_extcon_type(prop);
+       if (type < 0)
+               return type;
+
+       cable = &edev->cables[index];
+
+       switch (type) {
+       case EXTCON_TYPE_USB:
+               __set_bit(prop - EXTCON_PROP_USB_MIN, cable->usb_bits);
+               break;
+       case EXTCON_TYPE_CHG:
+               __set_bit(prop - EXTCON_PROP_CHG_MIN, cable->chg_bits);
+               break;
+       case EXTCON_TYPE_JACK:
+               __set_bit(prop - EXTCON_PROP_JACK_MIN, cable->jack_bits);
+               break;
+       case EXTCON_TYPE_DISP:
+               __set_bit(prop - EXTCON_PROP_DISP_MIN, cable->disp_bits);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
 }
-EXPORT_SYMBOL_GPL(extcon_set_cable_state_);
+EXPORT_SYMBOL_GPL(extcon_set_property_capability);
 
 /**
  * extcon_get_extcon_dev() - Get the extcon device instance from the name
@@ -428,7 +904,7 @@ int extcon_register_notifier(struct extcon_dev *edev, unsigned int id,
                             struct notifier_block *nb)
 {
        unsigned long flags;
-       int ret, idx;
+       int ret, idx = -EINVAL;
 
        if (!nb)
                return -EINVAL;
@@ -846,13 +1322,13 @@ struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index)
                return ERR_PTR(-EINVAL);
 
        if (!dev->of_node) {
-               dev_err(dev, "device does not have a device node entry\n");
+               dev_dbg(dev, "device does not have a device node entry\n");
                return ERR_PTR(-EINVAL);
        }
 
        node = of_parse_phandle(dev->of_node, "extcon", index);
        if (!node) {
-               dev_err(dev, "failed to get phandle in %s node\n",
+               dev_dbg(dev, "failed to get phandle in %s node\n",
                        dev->of_node->full_name);
                return ERR_PTR(-ENODEV);
        }
This page took 0.033259 seconds and 5 git commands to generate.