1 /* 2 * soundbus 3 * 4 * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> 5 * 6 * GPL v2, can be found in COPYING. 7 */ 8 9 #include <linux/module.h> 10 #include "soundbus.h" 11 12 MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); 13 MODULE_LICENSE("GPL"); 14 MODULE_DESCRIPTION("Apple Soundbus"); 15 16 struct soundbus_dev *soundbus_dev_get(struct soundbus_dev *dev) 17 { 18 struct device *tmp; 19 20 if (!dev) 21 return NULL; 22 tmp = get_device(&dev->ofdev.dev); 23 if (tmp) 24 return to_soundbus_device(tmp); 25 else 26 return NULL; 27 } 28 EXPORT_SYMBOL_GPL(soundbus_dev_get); 29 30 void soundbus_dev_put(struct soundbus_dev *dev) 31 { 32 if (dev) 33 put_device(&dev->ofdev.dev); 34 } 35 EXPORT_SYMBOL_GPL(soundbus_dev_put); 36 37 static int soundbus_probe(struct device *dev) 38 { 39 int error = -ENODEV; 40 struct soundbus_driver *drv; 41 struct soundbus_dev *soundbus_dev; 42 43 drv = to_soundbus_driver(dev->driver); 44 soundbus_dev = to_soundbus_device(dev); 45 46 if (!drv->probe) 47 return error; 48 49 soundbus_dev_get(soundbus_dev); 50 51 error = drv->probe(soundbus_dev); 52 if (error) 53 soundbus_dev_put(soundbus_dev); 54 55 return error; 56 } 57 58 59 static int soundbus_uevent(struct device *dev, char **envp, int num_envp, 60 char *buffer, int buffer_size) 61 { 62 struct soundbus_dev * soundbus_dev; 63 struct of_device * of; 64 char *scratch, *compat, *compat2; 65 int i = 0; 66 int length, cplen, cplen2, seen = 0; 67 68 if (!dev) 69 return -ENODEV; 70 71 soundbus_dev = to_soundbus_device(dev); 72 if (!soundbus_dev) 73 return -ENODEV; 74 75 of = &soundbus_dev->ofdev; 76 77 /* stuff we want to pass to /sbin/hotplug */ 78 envp[i++] = scratch = buffer; 79 length = scnprintf (scratch, buffer_size, "OF_NAME=%s", of->node->name); 80 ++length; 81 buffer_size -= length; 82 if ((buffer_size <= 0) || (i >= num_envp)) 83 return -ENOMEM; 84 scratch += length; 85 86 envp[i++] = scratch; 87 length = scnprintf (scratch, buffer_size, "OF_TYPE=%s", of->node->type); 88 ++length; 89 buffer_size -= length; 90 if ((buffer_size <= 0) || (i >= num_envp)) 91 return -ENOMEM; 92 scratch += length; 93 94 /* Since the compatible field can contain pretty much anything 95 * it's not really legal to split it out with commas. We split it 96 * up using a number of environment variables instead. */ 97 98 compat = (char *) get_property(of->node, "compatible", &cplen); 99 compat2 = compat; 100 cplen2= cplen; 101 while (compat && cplen > 0) { 102 envp[i++] = scratch; 103 length = scnprintf (scratch, buffer_size, 104 "OF_COMPATIBLE_%d=%s", seen, compat); 105 ++length; 106 buffer_size -= length; 107 if ((buffer_size <= 0) || (i >= num_envp)) 108 return -ENOMEM; 109 scratch += length; 110 length = strlen (compat) + 1; 111 compat += length; 112 cplen -= length; 113 seen++; 114 } 115 116 envp[i++] = scratch; 117 length = scnprintf (scratch, buffer_size, "OF_COMPATIBLE_N=%d", seen); 118 ++length; 119 buffer_size -= length; 120 if ((buffer_size <= 0) || (i >= num_envp)) 121 return -ENOMEM; 122 scratch += length; 123 124 envp[i++] = scratch; 125 length = scnprintf (scratch, buffer_size, "MODALIAS=%s", 126 soundbus_dev->modalias); 127 128 buffer_size -= length; 129 if ((buffer_size <= 0) || (i >= num_envp)) 130 return -ENOMEM; 131 132 envp[i] = NULL; 133 134 return 0; 135 } 136 137 static int soundbus_device_remove(struct device *dev) 138 { 139 struct soundbus_dev * soundbus_dev = to_soundbus_device(dev); 140 struct soundbus_driver * drv = to_soundbus_driver(dev->driver); 141 142 if (dev->driver && drv->remove) 143 drv->remove(soundbus_dev); 144 soundbus_dev_put(soundbus_dev); 145 146 return 0; 147 } 148 149 static void soundbus_device_shutdown(struct device *dev) 150 { 151 struct soundbus_dev * soundbus_dev = to_soundbus_device(dev); 152 struct soundbus_driver * drv = to_soundbus_driver(dev->driver); 153 154 if (dev->driver && drv->shutdown) 155 drv->shutdown(soundbus_dev); 156 } 157 158 #ifdef CONFIG_PM 159 160 static int soundbus_device_suspend(struct device *dev, pm_message_t state) 161 { 162 struct soundbus_dev * soundbus_dev = to_soundbus_device(dev); 163 struct soundbus_driver * drv = to_soundbus_driver(dev->driver); 164 165 if (dev->driver && drv->suspend) 166 return drv->suspend(soundbus_dev, state); 167 return 0; 168 } 169 170 static int soundbus_device_resume(struct device * dev) 171 { 172 struct soundbus_dev * soundbus_dev = to_soundbus_device(dev); 173 struct soundbus_driver * drv = to_soundbus_driver(dev->driver); 174 175 if (dev->driver && drv->resume) 176 return drv->resume(soundbus_dev); 177 return 0; 178 } 179 180 #endif /* CONFIG_PM */ 181 182 extern struct device_attribute soundbus_dev_attrs[]; 183 184 static struct bus_type soundbus_bus_type = { 185 .name = "aoa-soundbus", 186 .probe = soundbus_probe, 187 .uevent = soundbus_uevent, 188 .remove = soundbus_device_remove, 189 .shutdown = soundbus_device_shutdown, 190 #ifdef CONFIG_PM 191 .suspend = soundbus_device_suspend, 192 .resume = soundbus_device_resume, 193 #endif 194 .dev_attrs = soundbus_dev_attrs, 195 }; 196 197 int soundbus_add_one(struct soundbus_dev *dev) 198 { 199 static int devcount; 200 201 /* sanity checks */ 202 if (!dev->attach_codec || 203 !dev->ofdev.node || 204 dev->pcmname || 205 dev->pcmid != -1) { 206 printk(KERN_ERR "soundbus: adding device failed sanity check!\n"); 207 return -EINVAL; 208 } 209 210 snprintf(dev->ofdev.dev.bus_id, BUS_ID_SIZE, "soundbus:%x", ++devcount); 211 dev->ofdev.dev.bus = &soundbus_bus_type; 212 return of_device_register(&dev->ofdev); 213 } 214 EXPORT_SYMBOL_GPL(soundbus_add_one); 215 216 void soundbus_remove_one(struct soundbus_dev *dev) 217 { 218 of_device_unregister(&dev->ofdev); 219 } 220 EXPORT_SYMBOL_GPL(soundbus_remove_one); 221 222 int soundbus_register_driver(struct soundbus_driver *drv) 223 { 224 /* initialize common driver fields */ 225 drv->driver.name = drv->name; 226 drv->driver.bus = &soundbus_bus_type; 227 228 /* register with core */ 229 return driver_register(&drv->driver); 230 } 231 EXPORT_SYMBOL_GPL(soundbus_register_driver); 232 233 void soundbus_unregister_driver(struct soundbus_driver *drv) 234 { 235 driver_unregister(&drv->driver); 236 } 237 EXPORT_SYMBOL_GPL(soundbus_unregister_driver); 238 239 static int __init soundbus_init(void) 240 { 241 return bus_register(&soundbus_bus_type); 242 } 243 244 static void __exit soundbus_exit(void) 245 { 246 bus_unregister(&soundbus_bus_type); 247 } 248 249 subsys_initcall(soundbus_init); 250 module_exit(soundbus_exit); 251