^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) * drivers/switch/switch_class.c
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) * Copyright (C) 2008 Google, Inc.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Author: Mike Lockwood <lockwood@android.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * This software is licensed under the terms of the GNU General Public
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * License version 2, as published by the Free Software Foundation, and
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * may be copied, distributed, and modified under those terms.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) * This program is distributed in the hope that it will be useful,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) * but WITHOUT ANY WARRANTY; without even the implied warranty of
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) * GNU General Public License for more details.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <linux/types.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #include <linux/device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #include <linux/fs.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #include <linux/err.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #include <linux/switch.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) struct class *switch_class;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) static atomic_t device_count;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) static ssize_t state_show(struct device *dev, struct device_attribute *attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) char *buf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) struct switch_dev *sdev = (struct switch_dev *)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) if (sdev->print_state) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) int ret = sdev->print_state(sdev, buf);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) if (ret >= 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) return sprintf(buf, "%d\n", sdev->state);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) static ssize_t name_show(struct device *dev, struct device_attribute *attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) char *buf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) struct switch_dev *sdev = (struct switch_dev *)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) if (sdev->print_name) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) int ret = sdev->print_name(sdev, buf);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) if (ret >= 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) return sprintf(buf, "%s\n", sdev->name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) static DEVICE_ATTR(state, S_IRUGO, state_show, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) static DEVICE_ATTR(name, S_IRUGO, name_show, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) void switch_set_state(struct switch_dev *sdev, int state)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) char name_buf[120];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) char state_buf[120];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) char *prop_buf;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) char *envp[3];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) int env_offset = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) int length;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) if (sdev->state != state) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) sdev->state = state;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) if (prop_buf) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) length = name_show(sdev->dev, NULL, prop_buf);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) if (length > 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) if (prop_buf[length - 1] == '\n')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) prop_buf[length - 1] = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) snprintf(name_buf, sizeof(name_buf),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) "SWITCH_NAME=%s", prop_buf);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) envp[env_offset++] = name_buf;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) length = state_show(sdev->dev, NULL, prop_buf);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) if (length > 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) if (prop_buf[length - 1] == '\n')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) prop_buf[length - 1] = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) snprintf(state_buf, sizeof(state_buf),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) "SWITCH_STATE=%s", prop_buf);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) envp[env_offset++] = state_buf;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) envp[env_offset] = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) kobject_uevent_env(&sdev->dev->kobj, KOBJ_CHANGE, envp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) free_page((unsigned long)prop_buf);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) printk(KERN_ERR "out of memory in switch_set_state\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) kobject_uevent(&sdev->dev->kobj, KOBJ_CHANGE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) EXPORT_SYMBOL_GPL(switch_set_state);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) static int create_switch_class(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) if (!switch_class) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) switch_class = class_create(THIS_MODULE, "switch");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) if (IS_ERR(switch_class))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) return PTR_ERR(switch_class);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) atomic_set(&device_count, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) int switch_dev_register(struct switch_dev *sdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) if (!switch_class) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) ret = create_switch_class();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) sdev->index = atomic_inc_return(&device_count);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) sdev->dev = device_create(switch_class, NULL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) MKDEV(0, sdev->index), NULL, sdev->name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) if (IS_ERR(sdev->dev))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) return PTR_ERR(sdev->dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) ret = device_create_file(sdev->dev, &dev_attr_state);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) goto err_create_file_1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) ret = device_create_file(sdev->dev, &dev_attr_name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) goto err_create_file_2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) dev_set_drvdata(sdev->dev, sdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) sdev->state = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) err_create_file_2:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) device_remove_file(sdev->dev, &dev_attr_state);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) err_create_file_1:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) device_destroy(switch_class, MKDEV(0, sdev->index));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) printk(KERN_ERR "switch: Failed to register driver %s\n", sdev->name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) EXPORT_SYMBOL_GPL(switch_dev_register);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) void switch_dev_unregister(struct switch_dev *sdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) device_remove_file(sdev->dev, &dev_attr_name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) device_remove_file(sdev->dev, &dev_attr_state);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) dev_set_drvdata(sdev->dev, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) device_destroy(switch_class, MKDEV(0, sdev->index));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) EXPORT_SYMBOL_GPL(switch_dev_unregister);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) static int __init switch_class_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) return create_switch_class();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) static void __exit switch_class_exit(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) class_destroy(switch_class);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) module_init(switch_class_init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) module_exit(switch_class_exit);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) MODULE_DESCRIPTION("Switch class driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) MODULE_LICENSE("GPL");