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