summaryrefslogtreecommitdiff
path: root/drivers/misc/cclogic/cclogic-class.c
diff options
context:
space:
mode:
authorFaiz Authar <faizauthar@gmail.com>2017-04-27 18:14:55 +0700
committerDavide Garberi <dade.garberi@gmail.com>2022-07-27 18:59:05 +0200
commit5dafae2558993f60611f1f6bd69c11c6cf88225e (patch)
treec79bd304ad24e69de43616cd898d1b46ff372975 /drivers/misc/cclogic/cclogic-class.c
parenta84facefcb7d4dbdfe1a4ad9655146540b62ed8f (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.c284
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");