diff options
author | Faiz Authar <faizauthar@gmail.com> | 2017-04-27 18:14:55 +0700 |
---|---|---|
committer | Davide Garberi <dade.garberi@gmail.com> | 2022-07-27 18:59:05 +0200 |
commit | 5dafae2558993f60611f1f6bd69c11c6cf88225e (patch) | |
tree | c79bd304ad24e69de43616cd898d1b46ff372975 /drivers/misc/cclogic/cclogic-class.c | |
parent | a84facefcb7d4dbdfe1a4ad9655146540b62ed8f (diff) |
drivers: add cclogic usb type-c driver
Change-Id: I783aaf058c42e85712f861369cc85db2d6adab61
Signed-off-by: Faiz Authar <faizauthar@gmail.com>
Signed-off-by: Davide Garberi <dade.garberi@gmail.com>
Diffstat (limited to 'drivers/misc/cclogic/cclogic-class.c')
-rw-r--r-- | drivers/misc/cclogic/cclogic-class.c | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/drivers/misc/cclogic/cclogic-class.c b/drivers/misc/cclogic/cclogic-class.c new file mode 100644 index 000000000000..fffce515ed1a --- /dev/null +++ b/drivers/misc/cclogic/cclogic-class.c @@ -0,0 +1,284 @@ +#include <linux/module.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/fs.h> +#include <linux/err.h> + +#include "cclogic-class.h" +#include "cclogic-core.h" + +struct class *cclogic_class; +static atomic_t device_count; + + +// USB port's supported modes. (read-only) +// Contents: "", "ufp", "dfp", or "ufp dfp". +static ssize_t cclogic_supported_modes_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cclogic_class_dev *cdev = (struct cclogic_class_dev *) dev_get_drvdata(dev); + + pr_debug("[%s][%d]\n", __func__, __LINE__); + + if(cdev->support & (CCLOGIC_SUPPORT_MODE_DUAL)) + return sprintf(buf, "ufp dfp"); + else if (cdev->support & CCLOGIC_SUPPORT_MODE_UFP) + return sprintf(buf, "ufp"); + else if (cdev->support & CCLOGIC_SUPPORT_MODE_DFP) + return sprintf(buf, "dfp"); + else{ + *buf='\0'; + return 0; + } +} + +// USB port's current mode. (read-write if configurable) +// Contents: "", "ufp", or "dfp". +static ssize_t cclogic_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cclogic_class_dev *cdev = (struct cclogic_class_dev *) dev_get_drvdata(dev); + struct cclogic_dev *cclogic_dev = container_of(cdev, struct cclogic_dev, cdev); + struct cclogic_state *pstate = &cclogic_dev->state; + int mode; + + pr_debug("[%s][%d]\n", __func__, __LINE__); + + mode = cclogic_get_connected_mode(pstate); + if(mode == CCLOGIC_MODE_UFP) + return sprintf(buf,"ufp"); + else if (mode == CCLOGIC_MODE_DFP) + return sprintf(buf,"dfp"); + else{ + *buf = '\0'; + return 0; + } +} + +// USB port's current mode. (read-write if configurable) +// Contents: "", "ufp", or "dfp". +static ssize_t cclogic_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct cclogic_class_dev *cdev = (struct cclogic_class_dev *) dev_get_drvdata(dev); + struct cclogic_dev *cclogic_dev = container_of(cdev, struct cclogic_dev, cdev); + + pr_debug("[%s][%d]\n", __func__, __LINE__); + + if(!strcmp(buf,"ufp")){ + cclogic_set_mode(cclogic_dev, CCLOGIC_MODE_UFP); + }else if(!strcmp(buf,"dfp")){ + cclogic_set_mode(cclogic_dev, CCLOGIC_MODE_DFP); + } + + return count; +} + +// USB port's current power role. (read-write if configurable) +// Contents: "", "source", or "sink". +static ssize_t cclogic_power_role_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cclogic_class_dev *cdev = (struct cclogic_class_dev *) dev_get_drvdata(dev); + struct cclogic_dev *cclogic_dev = container_of(cdev, struct cclogic_dev, cdev); + struct cclogic_state *pstate = &cclogic_dev->state; + + pr_debug("[%s][%d]\n", __func__, __LINE__); + + if(cclogic_get_power_role(pstate)==CCLOGIC_POWER_SINK) + return sprintf(buf,"sink"); + else if(cclogic_get_power_role(pstate)==CCLOGIC_POWER_SOURCE) + return sprintf(buf,"source"); + else{ + *buf = '\0'; + return 0; + } +} + +// USB port's current power role. (read-write if configurable) +// Contents: "", "source", or "sink". +static ssize_t cclogic_power_role_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct cclogic_class_dev *cdev = (struct cclogic_class_dev *) dev_get_drvdata(dev); + struct cclogic_dev *cclogic_dev = container_of(cdev, struct cclogic_dev, cdev); + + pr_debug("[%s][%d]\n", __func__, __LINE__); + + if(!strcmp(buf,"sink")){ + cclogic_set_power_role(cclogic_dev, CCLOGIC_POWER_SINK); + }else if(!strcmp(buf,"source")){ + cclogic_set_power_role(cclogic_dev, CCLOGIC_POWER_SOURCE); + } + + return count; +} + +// USB port's current data role. (read-write if configurable) +// Contents: "", "host", or "device". +static ssize_t cclogic_data_role_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cclogic_class_dev *cdev = (struct cclogic_class_dev *) dev_get_drvdata(dev); + struct cclogic_dev *cclogic_dev = container_of(cdev, struct cclogic_dev, cdev); + struct cclogic_state *pstate = &cclogic_dev->state; + + pr_debug("[%s][%d]\n", __func__, __LINE__); + + if(cclogic_get_data_role(pstate)==CCLOGIC_DATA_HOST) + return sprintf(buf,"host"); + else if(cclogic_get_data_role(pstate)==CCLOGIC_DATA_DEVICE) + return sprintf(buf,"device"); + else{ + *buf = '\0'; + return 0; + } +} + +// USB port's current data role. (read-write if configurable) +// Contents: "", "host", or "device". +static ssize_t cclogic_data_role_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct cclogic_class_dev *cdev = (struct cclogic_class_dev *) dev_get_drvdata(dev); + struct cclogic_dev *cclogic_dev = container_of(cdev, struct cclogic_dev, cdev); + + pr_debug("[%s][%d]\n", __func__, __LINE__); + + if(!strcmp(buf,"host")){ + cclogic_set_data_role(cclogic_dev, CCLOGIC_DATA_HOST); + }else if(!strcmp(buf,"device")){ + cclogic_set_data_role(cclogic_dev, CCLOGIC_DATA_DEVICE); + } + + return count; +} + + + +void cclogic_class_update_state(struct cclogic_class_dev *cdev) +{ + kobject_uevent(&cdev->dev->kobj, KOBJ_CHANGE); +} +EXPORT_SYMBOL(cclogic_class_update_state); + +static int create_cclogic_class(void) +{ + if (!cclogic_class) { + cclogic_class = class_create(THIS_MODULE, "dual_role_usb"); + if (IS_ERR(cclogic_class)) + return PTR_ERR(cclogic_class); + } + + return 0; +} + +int cclogic_class_register(struct cclogic_class_dev *cdev) +{ + int ret; + struct cclogic_dev *cclogic_dev = container_of(cdev, struct cclogic_dev, cdev); + + if (!cclogic_class) { + ret = create_cclogic_class(); + if (ret < 0) + return ret; + } + + cdev->index = atomic_inc_return(&device_count); + cdev->dev = device_create(cclogic_class, NULL, + MKDEV(0, cdev->index), NULL, cdev->name); + if (IS_ERR(cdev->dev)) + return PTR_ERR(cdev->dev); + + cdev->support = cclogic_dev->ops->support; + + cdev->device_supported_modes_attr.attr.name = "supported_modes"; + cdev->device_supported_modes_attr.attr.mode = S_IRUGO; + cdev->device_supported_modes_attr.show = cclogic_supported_modes_show; + sysfs_attr_init(&cdev->device_supported_modes_attr.attr); + ret = device_create_file(cdev->dev, &cdev->device_supported_modes_attr); + if (ret < 0) + goto err_create_file_1; + + cdev->device_mode_attr.attr.name = "mode"; + cdev->device_mode_attr.attr.mode = S_IRUGO; + cdev->device_mode_attr.show = cclogic_mode_show; + if((cdev->support & CCLOGIC_SUPPORT_MODE_DUAL) == CCLOGIC_SUPPORT_MODE_DUAL){ + cdev->device_mode_attr.attr.mode |= S_IWUSR; + cdev->device_mode_attr.store = cclogic_mode_store; + } + sysfs_attr_init(&cdev->device_mode_attr.attr); + ret = device_create_file(cdev->dev, &cdev->device_mode_attr); + if (ret < 0) + goto err_create_file_2; + + cdev->device_power_role_attr.attr.name = "power_role"; + cdev->device_power_role_attr.attr.mode = S_IRUGO; + cdev->device_power_role_attr.show = cclogic_power_role_show; + if((cdev->support & CCLOGIC_SUPPORT_POWER_SWAP) == CCLOGIC_SUPPORT_POWER_SWAP){ + cdev->device_power_role_attr.attr.mode |= S_IWUSR; + cdev->device_power_role_attr.store = cclogic_power_role_store; + } + sysfs_attr_init(&cdev->device_power_role_attr.attr); + ret = device_create_file(cdev->dev, &cdev->device_power_role_attr); + if (ret < 0) + goto err_create_file_3; + + cdev->device_data_role_attr.attr.name = "data_role"; + cdev->device_data_role_attr.attr.mode = S_IRUGO; + cdev->device_data_role_attr.show = cclogic_data_role_show; + if((cdev->support & CCLOGIC_SUPPORT_DATA_SWAP) == CCLOGIC_SUPPORT_DATA_SWAP){ + cdev->device_data_role_attr.attr.mode |= S_IWUSR; + cdev->device_data_role_attr.store = cclogic_data_role_store; + } + sysfs_attr_init(&cdev->device_data_role_attr.attr); + ret = device_create_file(cdev->dev, &cdev->device_data_role_attr); + if (ret < 0) + goto err_create_file_4; + + dev_set_drvdata(cdev->dev, cdev); + + return 0; + +err_create_file_4: + device_remove_file(cdev->dev, &cdev->device_power_role_attr); +err_create_file_3: + device_remove_file(cdev->dev, &cdev->device_mode_attr); +err_create_file_2: + device_remove_file(cdev->dev, &cdev->device_supported_modes_attr); +err_create_file_1: + device_destroy(cclogic_class, MKDEV(0, cdev->index)); + printk(KERN_ERR "cclogic-class: Failed to register driver %s\n", cdev->name); + + return ret; +} +EXPORT_SYMBOL(cclogic_class_register); + +void cclogic_class_unregister(struct cclogic_class_dev *cdev) +{ + device_remove_file(cdev->dev, &cdev->device_supported_modes_attr); + device_remove_file(cdev->dev, &cdev->device_mode_attr); + device_remove_file(cdev->dev, &cdev->device_power_role_attr); + device_remove_file(cdev->dev, &cdev->device_data_role_attr); + dev_set_drvdata(cdev->dev, NULL); + device_destroy(cclogic_class, MKDEV(0, cdev->index)); +} +EXPORT_SYMBOL(cclogic_class_unregister); + +static int __init cclogic_class_init(void) +{ + return create_cclogic_class(); +} + +static void __exit cclogic_class_exit(void) +{ + class_destroy(cclogic_class); +} + +module_init(cclogic_class_init); +module_exit(cclogic_class_exit); + +MODULE_AUTHOR("yangshaoying <yangsy2@zuk.com>"); +MODULE_DESCRIPTION("cclogic class driver"); +MODULE_LICENSE("GPL"); |