123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- *
- * Copyright (C) 2011-2023 Samsung, Inc.
- * Author: Dongrak Shin <[email protected]>
- *
- */
- /* usb notify layer v4.0 */
- #include <linux/module.h>
- #include <linux/types.h>
- #include <linux/init.h>
- #include <linux/device.h>
- #include <linux/slab.h>
- #include <linux/fs.h>
- #include <linux/err.h>
- #include <linux/host_notify.h>
- #if defined(CONFIG_USB_HW_PARAM)
- #include <linux/usb_notify.h>
- #endif
- struct notify_data {
- struct class *host_notify_class;
- atomic_t device_count;
- struct mutex host_notify_lock;
- };
- static struct notify_data host_notify;
- static ssize_t mode_show(
- struct device *dev, struct device_attribute *attr, char *buf)
- {
- struct host_notify_dev *ndev = (struct host_notify_dev *)
- dev_get_drvdata(dev);
- char *mode;
- switch (ndev->mode) {
- case NOTIFY_HOST_MODE:
- mode = "HOST";
- break;
- case NOTIFY_PERIPHERAL_MODE:
- mode = "PERIPHERAL";
- break;
- case NOTIFY_TEST_MODE:
- mode = "TEST";
- break;
- case NOTIFY_NONE_MODE:
- default:
- mode = "NONE";
- break;
- }
- return sprintf(buf, "%s\n", mode);
- }
- static ssize_t mode_store(
- struct device *dev, struct device_attribute *attr,
- const char *buf, size_t size)
- {
- #ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
- struct host_notify_dev *ndev = (struct host_notify_dev *)
- dev_get_drvdata(dev);
- #endif
- char *mode;
- size_t ret = -ENOMEM;
- int sret = 0;
- if (size < strlen(buf))
- goto error;
- mode = kzalloc(size+1, GFP_KERNEL);
- if (!mode)
- goto error;
- sret = sscanf(buf, "%s", mode);
- if (sret != 1)
- goto error1;
- #ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
- if (ndev->set_mode) {
- unl_info("host_notify: set mode %s\n", mode);
- if (!strncmp(mode, "HOST", 4))
- ndev->set_mode(NOTIFY_SET_ON);
- else if (!strncmp(mode, "NONE", 4))
- ndev->set_mode(NOTIFY_SET_OFF);
- }
- #endif
- ret = size;
- error1:
- kfree(mode);
- error:
- return ret;
- }
- static ssize_t booster_show(struct device *dev, struct device_attribute *attr,
- char *buf)
- {
- struct host_notify_dev *ndev = (struct host_notify_dev *)
- dev_get_drvdata(dev);
- char *booster;
- switch (ndev->booster) {
- case NOTIFY_POWER_ON:
- booster = "ON";
- break;
- case NOTIFY_POWER_OFF:
- default:
- booster = "OFF";
- break;
- }
- unl_info("host_notify: read booster %s\n", booster);
- return sprintf(buf, "%s\n", booster);
- }
- static ssize_t booster_store(
- struct device *dev, struct device_attribute *attr,
- const char *buf, size_t size)
- {
- struct host_notify_dev *ndev = (struct host_notify_dev *)
- dev_get_drvdata(dev);
- char *booster;
- size_t ret = -ENOMEM;
- int sret = 0;
- if (size < strlen(buf))
- goto error;
- booster = kzalloc(size+1, GFP_KERNEL);
- if (!booster)
- goto error;
- sret = sscanf(buf, "%s", booster);
- if (sret != 1)
- goto error1;
- if (ndev->set_booster) {
- unl_info("host_notify: set booster %s\n", booster);
- if (!strncmp(booster, "ON", 2)) {
- ndev->set_booster(NOTIFY_SET_ON);
- ndev->mode = NOTIFY_TEST_MODE;
- } else if (!strncmp(booster, "OFF", 3)) {
- ndev->set_booster(NOTIFY_SET_OFF);
- ndev->mode = NOTIFY_NONE_MODE;
- }
- }
- ret = size;
- error1:
- kfree(booster);
- error:
- return ret;
- }
- static DEVICE_ATTR_RW(mode);
- static DEVICE_ATTR_RW(booster);
- static struct attribute *host_notify_attrs[] = {
- &dev_attr_mode.attr,
- &dev_attr_booster.attr,
- NULL,
- };
- static struct attribute_group host_notify_attr_grp = {
- .attrs = host_notify_attrs,
- };
- char *host_state_string(int type)
- {
- switch (type) {
- case NOTIFY_HOST_NONE: return "none";
- case NOTIFY_HOST_ADD: return "add";
- case NOTIFY_HOST_REMOVE: return "remove";
- case NOTIFY_HOST_OVERCURRENT: return "overcurrent";
- case NOTIFY_HOST_LOWBATT: return "lowbatt";
- case NOTIFY_HOST_BLOCK: return "block";
- case NOTIFY_HOST_SOURCE: return "source";
- case NOTIFY_HOST_SINK: return "sink";
- case NOTIFY_HOST_UNKNOWN:
- default: return "unknown";
- }
- }
- static int check_state_type(int state)
- {
- int ret = 0;
- switch (state) {
- case NOTIFY_HOST_ADD:
- case NOTIFY_HOST_REMOVE:
- case NOTIFY_HOST_BLOCK:
- ret = NOTIFY_HOST_STATE;
- break;
- case NOTIFY_HOST_OVERCURRENT:
- case NOTIFY_HOST_LOWBATT:
- case NOTIFY_HOST_SOURCE:
- case NOTIFY_HOST_SINK:
- ret = NOTIFY_POWER_STATE;
- break;
- case NOTIFY_HOST_NONE:
- case NOTIFY_HOST_UNKNOWN:
- default:
- ret = NOTIFY_UNKNOWN_STATE;
- break;
- }
- return ret;
- }
- int host_state_notify(struct host_notify_dev *ndev, int state)
- {
- int type = 0;
- if (!ndev->dev) {
- unl_err("host_notify: %s ndev->dev is NULL\n", __func__);
- return -ENXIO;
- }
- mutex_lock(&host_notify.host_notify_lock);
- unl_info("host_notify: ndev name=%s: state=%s\n",
- ndev->name, host_state_string(state));
- type = check_state_type(state);
- if (type == NOTIFY_HOST_STATE) {
- if (ndev->host_state != state) {
- unl_info("host_notify: host_state (%s->%s)\n",
- host_state_string(ndev->host_state),
- host_state_string(state));
- ndev->host_state = state;
- ndev->host_change = 1;
- kobject_uevent(&ndev->dev->kobj, KOBJ_CHANGE);
- ndev->host_change = 0;
- #if defined(CONFIG_USB_HW_PARAM)
- if (state == NOTIFY_HOST_ADD)
- inc_hw_param_host(ndev, USB_CCIC_OTG_USE_COUNT);
- #endif
- }
- } else if (type == NOTIFY_POWER_STATE) {
- if (ndev->power_state != state) {
- unl_info("host_notify: power_state (%s->%s)\n",
- host_state_string(ndev->power_state),
- host_state_string(state));
- ndev->power_state = state;
- ndev->power_change = 1;
- kobject_uevent(&ndev->dev->kobj, KOBJ_CHANGE);
- ndev->power_change = 0;
- #if defined(CONFIG_USB_HW_PARAM)
- if (state == NOTIFY_HOST_OVERCURRENT)
- inc_hw_param_host(ndev, USB_CCIC_OVC_COUNT);
- #endif
- }
- } else {
- ndev->host_state = state;
- ndev->power_state = state;
- }
- mutex_unlock(&host_notify.host_notify_lock);
- return 0;
- }
- EXPORT_SYMBOL_GPL(host_state_notify);
- static int
- host_notify_uevent(struct device *dev, struct kobj_uevent_env *env)
- {
- struct host_notify_dev *ndev = (struct host_notify_dev *)
- dev_get_drvdata(dev);
- char *state;
- int state_type;
- if (!ndev) {
- /* this happens when the device is first created */
- return 0;
- }
- if (ndev->host_change)
- state_type = ndev->host_state;
- else if (ndev->power_change)
- state_type = ndev->power_state;
- else
- state_type = NOTIFY_HOST_NONE;
- switch (state_type) {
- case NOTIFY_HOST_ADD:
- state = "ADD";
- break;
- case NOTIFY_HOST_REMOVE:
- state = "REMOVE";
- break;
- case NOTIFY_HOST_OVERCURRENT:
- state = "OVERCURRENT";
- break;
- case NOTIFY_HOST_LOWBATT:
- state = "LOWBATT";
- break;
- case NOTIFY_HOST_BLOCK:
- state = "BLOCK";
- break;
- case NOTIFY_HOST_SOURCE:
- state = "SOURCE";
- break;
- case NOTIFY_HOST_SINK:
- state = "SINK";
- break;
- case NOTIFY_HOST_UNKNOWN:
- state = "UNKNOWN";
- break;
- case NOTIFY_HOST_NONE:
- default:
- return 0;
- }
- if (add_uevent_var(env, "DEVNAME=%s", ndev->dev->kobj.name))
- return -ENOMEM;
- if (add_uevent_var(env, "STATE=%s", state))
- return -ENOMEM;
- return 0;
- }
- static int create_notify_class(void)
- {
- if (!host_notify.host_notify_class) {
- host_notify.host_notify_class
- = class_create(THIS_MODULE, "host_notify");
- if (IS_ERR(host_notify.host_notify_class))
- return PTR_ERR(host_notify.host_notify_class);
- atomic_set(&host_notify.device_count, 0);
- mutex_init(&host_notify.host_notify_lock);
- host_notify.host_notify_class->dev_uevent = host_notify_uevent;
- }
- return 0;
- }
- int host_notify_dev_register(struct host_notify_dev *ndev)
- {
- int ret;
- if (!host_notify.host_notify_class) {
- ret = create_notify_class();
- if (ret < 0)
- return ret;
- }
- ndev->index = atomic_inc_return(&host_notify.device_count);
- ndev->dev = device_create(host_notify.host_notify_class, NULL,
- MKDEV(0, ndev->index), NULL, "%s", ndev->name);
- if (IS_ERR(ndev->dev))
- return PTR_ERR(ndev->dev);
- ret = sysfs_create_group(&ndev->dev->kobj, &host_notify_attr_grp);
- if (ret < 0) {
- device_destroy(host_notify.host_notify_class,
- MKDEV(0, ndev->index));
- return ret;
- }
- dev_set_drvdata(ndev->dev, ndev);
- ndev->host_state = NOTIFY_HOST_NONE;
- ndev->power_state = NOTIFY_HOST_SINK;
- return 0;
- }
- EXPORT_SYMBOL_GPL(host_notify_dev_register);
- void host_notify_dev_unregister(struct host_notify_dev *ndev)
- {
- ndev->host_state = NOTIFY_HOST_NONE;
- ndev->power_state = NOTIFY_HOST_SINK;
- sysfs_remove_group(&ndev->dev->kobj, &host_notify_attr_grp);
- dev_set_drvdata(ndev->dev, NULL);
- device_destroy(host_notify.host_notify_class, MKDEV(0, ndev->index));
- ndev->dev = NULL;
- }
- EXPORT_SYMBOL_GPL(host_notify_dev_unregister);
- int notify_class_init(void)
- {
- return create_notify_class();
- }
- void notify_class_exit(void)
- {
- class_destroy(host_notify.host_notify_class);
- }
|