Messages in this thread Patch in this message | | | Date | Thu, 24 Nov 2011 11:06:47 +0900 | Subject | [RFC PATCH 3/3] Multistate Switch Class: support multiple states at a device | From | MyungJoo Ham <> |
| |
One switch device (e.g., MUIC(MAX8997, MAX77686, ...), and some 30-pin devices) may have multiple cables attached. For example, one 30-pin port may inhabit a USB cable, an HDMI cable, and a mic. Thus, one switch device requires multiple state bits each representing a type of cable.
For such purpose, we use the 32bit state variable; thus, up to 32 different type of cables may be defined for a switch device. The list of possible cables is defined by the array of cable names in the switch_dev struct given to the class.
Signed-off-by: Donggeun Kim <dg77.kim@samsung.com> Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> --- drivers/misc/switch_class.c | 112 +++++++++++++++++++++++++++++++++++++++++-- include/linux/switch.h | 63 ++++++++++++++++++++++++ 2 files changed, 170 insertions(+), 5 deletions(-)
diff --git a/drivers/misc/switch_class.c b/drivers/misc/switch_class.c index ab574a3..2b40356 100644 --- a/drivers/misc/switch_class.c +++ b/drivers/misc/switch_class.c @@ -39,6 +39,7 @@ static DEFINE_MUTEX(switch_dev_list_lock); static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf) { + int i, count = 0; struct switch_dev *sdev = (struct switch_dev *) dev_get_drvdata(dev);
@@ -47,7 +48,22 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr, if (ret >= 0) return ret; } - return sprintf(buf, "%u\n", sdev->state); + + if (sdev->max_supported == 0) + return sprintf(buf, "%u\n", sdev->state); + + for (i = 0; i < SUPPORTED_CABLE_MAX; i++) { + if (!sdev->supported_cable[i]) + break; + if (i > 0) + count += sprintf(buf + count, ", "); + count += sprintf(buf + count, "%s %d", + sdev->supported_cable[i], + !!(sdev->state & (1 << i))); + } + count += sprintf(buf + count, "\n"); + + return count; }
static ssize_t name_show(struct device *dev, struct device_attribute *attr, @@ -67,7 +83,7 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(state, S_IRUGO, state_show, NULL); static DEVICE_ATTR(name, S_IRUGO, name_show, NULL);
-void switch_set_state(struct switch_dev *sdev, u32 state) +void switch_update_state(struct switch_dev *sdev, u32 mask, u32 state) { char name_buf[120]; char state_buf[120]; @@ -75,9 +91,13 @@ void switch_set_state(struct switch_dev *sdev, u32 state) char *envp[3]; int env_offset = 0; int length; + unsigned long flags; + + spin_lock_irqsave(&sdev->lock, flags);
- if (sdev->state != state) { - sdev->state = state; + if (sdev->state != ((sdev->state & ~mask) | (state & mask))) { + sdev->state &= ~mask; + sdev->state |= state & mask;
raw_notifier_call_chain(&sdev->nh, sdev->state, NULL);
@@ -100,16 +120,83 @@ void switch_set_state(struct switch_dev *sdev, u32 state) envp[env_offset++] = state_buf; } envp[env_offset] = NULL; + /* Unlock early before uevent */ + spin_unlock_irqrestore(&sdev->lock, flags); + kobject_uevent_env(&sdev->dev->kobj, KOBJ_CHANGE, envp); free_page((unsigned long)prop_buf); } else { + /* Unlock early before uevent */ + spin_unlock_irqrestore(&sdev->lock, flags); + printk(KERN_ERR "out of memory in switch_set_state\n"); kobject_uevent(&sdev->dev->kobj, KOBJ_CHANGE); } + } else { + /* No changes */ + spin_unlock_irqrestore(&sdev->lock, flags); } } +EXPORT_SYMBOL_GPL(switch_update_state); + +void switch_set_state(struct switch_dev *sdev, u32 state) +{ + switch_update_state(sdev, 0xffffffff, state); +} EXPORT_SYMBOL_GPL(switch_set_state);
+int switch_find_cable_index(struct switch_dev *sdev, const char *cable_name) +{ + int i; + + if (sdev->supported_cable) { + for (i = 0; sdev->supported_cable[i]; i++) { + if (!strncmp(sdev->supported_cable[i], + cable_name, CABLE_NAME_MAX)) + return i; + } + } + + return -1; +} +EXPORT_SYMBOL_GPL(switch_find_cable_index); + +int switch_get_cable_state_(struct switch_dev *sdev, int index) +{ + if (index < 0 || (sdev->max_supported && sdev->max_supported <= index)) + return -EINVAL; + + return !!(sdev->state & (1 << index)); +} +EXPORT_SYMBOL_GPL(switch_get_cable_state_); + +int switch_get_cable_state(struct switch_dev *sdev, const char *cable_name) +{ + return switch_get_cable_state_(sdev, switch_find_cable_index + (sdev, cable_name)); +} +EXPORT_SYMBOL_GPL(switch_get_cable_state); + +int switch_set_cable_state_(struct switch_dev *sdev, + int index, bool cable_state) +{ + if (index < 0 || (sdev->max_supported && sdev->max_supported <= index)) + return -EINVAL; + + state = cable_state ? (1 << index) : 0; + switch_update_state(sdev, 1 << index, state); + return 0; +} +EXPORT_SYMBOL_GPL(switch_set_cable_state_); + +int switch_set_cable_state(struct switch_dev *sdev, + const char *cable_name, bool cable_state) +{ + return switch_set_cable_state_(sdev, switch_find_cable_index + (sdev, cable_name), cable_state); +} +EXPORT_SYMBOL_GPL(switch_set_cable_state); + struct switch_dev *switch_get_switch_dev(const char *switch_name) { struct switch_dev *sd; @@ -154,7 +241,7 @@ static int create_switch_class(void)
int switch_dev_register(struct switch_dev *sdev) { - int ret; + int ret, index = 0;
if (!switch_class) { ret = create_switch_class(); @@ -162,6 +249,21 @@ int switch_dev_register(struct switch_dev *sdev) return ret; }
+ if (sdev->supported_cable) { + /* Get size of array */ + for (index = 0; sdev->supported_cable[index]; index++) + ; + sdev->max_supported = index; + } else { + sdev->max_supported = 0; + } + + if (index > SUPPORTED_CABLE_MAX) { + dev_err(sdev->dev, "switch: Exceed maximum number of supported cable\n"); + return -EINVAL; + } + + spin_lock_init(&sdev->lock); sdev->index = atomic_inc_return(&device_count); sdev->dev = device_create(switch_class, NULL, MKDEV(0, sdev->index), NULL, sdev->name); diff --git a/include/linux/switch.h b/include/linux/switch.h index 0e43901..f5c3471 100644 --- a/include/linux/switch.h +++ b/include/linux/switch.h @@ -25,10 +25,18 @@
#include <linux/notifier.h>
+#define SUPPORTED_CABLE_MAX 32 +#define CABLE_NAME_MAX 30 + struct switch_dev { /* --- User initializing data --- */ const char *name; struct device *dev; + /* + * Array of cable names ending with NULL. + * If supported_cable is NULL, cable name related APIs are disabled + */ + const char **supported_cable;
/* --- Optional callbacks to override class functions --- */ ssize_t (*print_name)(struct switch_dev *sdev, char *buf); @@ -39,6 +47,8 @@ struct switch_dev { int index; struct raw_notifier_head nh; struct list_head entry; + spinlock_t lock; /* could be called by irq handler */ + int max_supported; };
struct gpio_switch_platform_data { @@ -57,12 +67,30 @@ struct gpio_switch_platform_data { extern int switch_dev_register(struct switch_dev *sdev); extern void switch_dev_unregister(struct switch_dev *sdev);
+/* + * get/set/update_state access the 32b encoded state value, which represents + * states of all possible cables of the multistate port. For example, if one + * calls switch_set_states(sdev, 0x7), it may mean that all the three cables + * are attached to the port. + */ static inline u32 switch_get_state(struct switch_dev *sdev) { return sdev->state; }
extern void switch_set_state(struct switch_dev *sdev, u32 state); +extern void switch_update_state(struct switch_dev *sdev, u32 mask, u32 state); + +extern int switch_find_cable_index(struct switch_dev *sdev, + const char *cable_name); +extern int switch_get_cable_state_(struct switch_dev *sdev, int cable_index); +extern int switch_set_cable_state_(struct switch_dev *sdev, int cable_index, + bool cable_state); + +extern int switch_get_cable_state(struct switch_dev *sdev, + const char *cable_name); +extern int switch_set_cable_state(struct switch_dev *sdev, + const char *cable_name, bool cable_state);
extern struct switch_dev *switch_get_switch_dev(const char *switch_name); extern int switch_register_notifier(struct switch_dev *sdev, @@ -83,6 +111,41 @@ static inline u32 switch_get_state(struct switch_dev *sdev) }
static inline void switch_set_state(struct switch_dev *sdev, u32 state) { } + +static inline void switch_update_state(struct switch_dev *sdev, u32 mask, + u32 state) +{ } + +static inline int switch_find_cable_index(struct switch_dev *sdev, + const char *cable_name) +{ + return 0; +} + +static inline int switch_get_cable_state_(struct switch_dev *sdev, + int cable_index) +{ + return 0; +} + +static inline int switch_set_cable_state_(struct switch_dev *sdev, + int cable_index, bool cable_state) +{ + return 0; +} + +static inline int switch_get_cable_state(struct switch_dev *sdev, + const char *cable_name) +{ + return 0; +} + +static inline int switch_set_cable_state(struct switch_dev *sdev, + const char *cable_name, int state) +{ + return 0; +} + static inline struct switch_dev *switch_get_switch_dev(const char *switch_name) { return NULL; -- 1.7.4.1
| |