diff options
Diffstat (limited to 'drivers/s390/cio/ccwgroup.c')
-rw-r--r-- | drivers/s390/cio/ccwgroup.c | 96 |
1 files changed, 75 insertions, 21 deletions
diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c index 85b2e51a42ae..26a930e832bd 100644 --- a/drivers/s390/cio/ccwgroup.c +++ b/drivers/s390/cio/ccwgroup.c | |||
@@ -152,44 +152,89 @@ __ccwgroup_create_symlinks(struct ccwgroup_device *gdev) | |||
152 | return 0; | 152 | return 0; |
153 | } | 153 | } |
154 | 154 | ||
155 | static int __get_next_bus_id(const char **buf, char *bus_id) | ||
156 | { | ||
157 | int rc, len; | ||
158 | char *start, *end; | ||
159 | |||
160 | start = (char *)*buf; | ||
161 | end = strchr(start, ','); | ||
162 | if (!end) { | ||
163 | /* Last entry. Strip trailing newline, if applicable. */ | ||
164 | end = strchr(start, '\n'); | ||
165 | if (end) | ||
166 | *end = '\0'; | ||
167 | len = strlen(start) + 1; | ||
168 | } else { | ||
169 | len = end - start + 1; | ||
170 | end++; | ||
171 | } | ||
172 | if (len < BUS_ID_SIZE) { | ||
173 | strlcpy(bus_id, start, len); | ||
174 | rc = 0; | ||
175 | } else | ||
176 | rc = -EINVAL; | ||
177 | *buf = end; | ||
178 | return rc; | ||
179 | } | ||
180 | |||
181 | static int __is_valid_bus_id(char bus_id[BUS_ID_SIZE]) | ||
182 | { | ||
183 | int cssid, ssid, devno; | ||
184 | |||
185 | /* Must be of form %x.%x.%04x */ | ||
186 | if (sscanf(bus_id, "%x.%1x.%04x", &cssid, &ssid, &devno) != 3) | ||
187 | return 0; | ||
188 | return 1; | ||
189 | } | ||
190 | |||
155 | /** | 191 | /** |
156 | * ccwgroup_create() - create and register a ccw group device | 192 | * ccwgroup_create_from_string() - create and register a ccw group device |
157 | * @root: parent device for the new device | 193 | * @root: parent device for the new device |
158 | * @creator_id: identifier of creating driver | 194 | * @creator_id: identifier of creating driver |
159 | * @cdrv: ccw driver of slave devices | 195 | * @cdrv: ccw driver of slave devices |
160 | * @argc: number of slave devices | 196 | * @num_devices: number of slave devices |
161 | * @argv: bus ids of slave devices | 197 | * @buf: buffer containing comma separated bus ids of slave devices |
162 | * | 198 | * |
163 | * Create and register a new ccw group device as a child of @root. Slave | 199 | * Create and register a new ccw group device as a child of @root. Slave |
164 | * devices are obtained from the list of bus ids given in @argv[] and must all | 200 | * devices are obtained from the list of bus ids given in @buf and must all |
165 | * belong to @cdrv. | 201 | * belong to @cdrv. |
166 | * Returns: | 202 | * Returns: |
167 | * %0 on success and an error code on failure. | 203 | * %0 on success and an error code on failure. |
168 | * Context: | 204 | * Context: |
169 | * non-atomic | 205 | * non-atomic |
170 | */ | 206 | */ |
171 | int ccwgroup_create(struct device *root, unsigned int creator_id, | 207 | int ccwgroup_create_from_string(struct device *root, unsigned int creator_id, |
172 | struct ccw_driver *cdrv, int argc, char *argv[]) | 208 | struct ccw_driver *cdrv, int num_devices, |
209 | const char *buf) | ||
173 | { | 210 | { |
174 | struct ccwgroup_device *gdev; | 211 | struct ccwgroup_device *gdev; |
175 | int i; | 212 | int rc, i; |
176 | int rc; | 213 | char tmp_bus_id[BUS_ID_SIZE]; |
214 | const char *curr_buf; | ||
177 | 215 | ||
178 | if (argc > 256) /* disallow dumb users */ | 216 | gdev = kzalloc(sizeof(*gdev) + num_devices * sizeof(gdev->cdev[0]), |
179 | return -EINVAL; | 217 | GFP_KERNEL); |
180 | |||
181 | gdev = kzalloc(sizeof(*gdev) + argc*sizeof(gdev->cdev[0]), GFP_KERNEL); | ||
182 | if (!gdev) | 218 | if (!gdev) |
183 | return -ENOMEM; | 219 | return -ENOMEM; |
184 | 220 | ||
185 | atomic_set(&gdev->onoff, 0); | 221 | atomic_set(&gdev->onoff, 0); |
186 | mutex_init(&gdev->reg_mutex); | 222 | mutex_init(&gdev->reg_mutex); |
187 | mutex_lock(&gdev->reg_mutex); | 223 | mutex_lock(&gdev->reg_mutex); |
188 | for (i = 0; i < argc; i++) { | 224 | curr_buf = buf; |
189 | gdev->cdev[i] = get_ccwdev_by_busid(cdrv, argv[i]); | 225 | for (i = 0; i < num_devices && curr_buf; i++) { |
190 | 226 | rc = __get_next_bus_id(&curr_buf, tmp_bus_id); | |
191 | /* all devices have to be of the same type in | 227 | if (rc != 0) |
192 | * order to be grouped */ | 228 | goto error; |
229 | if (!__is_valid_bus_id(tmp_bus_id)) { | ||
230 | rc = -EINVAL; | ||
231 | goto error; | ||
232 | } | ||
233 | gdev->cdev[i] = get_ccwdev_by_busid(cdrv, tmp_bus_id); | ||
234 | /* | ||
235 | * All devices have to be of the same type in | ||
236 | * order to be grouped. | ||
237 | */ | ||
193 | if (!gdev->cdev[i] | 238 | if (!gdev->cdev[i] |
194 | || gdev->cdev[i]->id.driver_info != | 239 | || gdev->cdev[i]->id.driver_info != |
195 | gdev->cdev[0]->id.driver_info) { | 240 | gdev->cdev[0]->id.driver_info) { |
@@ -203,9 +248,18 @@ int ccwgroup_create(struct device *root, unsigned int creator_id, | |||
203 | } | 248 | } |
204 | dev_set_drvdata(&gdev->cdev[i]->dev, gdev); | 249 | dev_set_drvdata(&gdev->cdev[i]->dev, gdev); |
205 | } | 250 | } |
206 | 251 | /* Check for sufficient number of bus ids. */ | |
252 | if (i < num_devices && !curr_buf) { | ||
253 | rc = -EINVAL; | ||
254 | goto error; | ||
255 | } | ||
256 | /* Check for trailing stuff. */ | ||
257 | if (i == num_devices && strlen(curr_buf) > 0) { | ||
258 | rc = -EINVAL; | ||
259 | goto error; | ||
260 | } | ||
207 | gdev->creator_id = creator_id; | 261 | gdev->creator_id = creator_id; |
208 | gdev->count = argc; | 262 | gdev->count = num_devices; |
209 | gdev->dev.bus = &ccwgroup_bus_type; | 263 | gdev->dev.bus = &ccwgroup_bus_type; |
210 | gdev->dev.parent = root; | 264 | gdev->dev.parent = root; |
211 | gdev->dev.release = ccwgroup_release; | 265 | gdev->dev.release = ccwgroup_release; |
@@ -233,7 +287,7 @@ int ccwgroup_create(struct device *root, unsigned int creator_id, | |||
233 | device_remove_file(&gdev->dev, &dev_attr_ungroup); | 287 | device_remove_file(&gdev->dev, &dev_attr_ungroup); |
234 | device_unregister(&gdev->dev); | 288 | device_unregister(&gdev->dev); |
235 | error: | 289 | error: |
236 | for (i = 0; i < argc; i++) | 290 | for (i = 0; i < num_devices; i++) |
237 | if (gdev->cdev[i]) { | 291 | if (gdev->cdev[i]) { |
238 | if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev) | 292 | if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev) |
239 | dev_set_drvdata(&gdev->cdev[i]->dev, NULL); | 293 | dev_set_drvdata(&gdev->cdev[i]->dev, NULL); |
@@ -243,6 +297,7 @@ error: | |||
243 | put_device(&gdev->dev); | 297 | put_device(&gdev->dev); |
244 | return rc; | 298 | return rc; |
245 | } | 299 | } |
300 | EXPORT_SYMBOL(ccwgroup_create_from_string); | ||
246 | 301 | ||
247 | static int __init | 302 | static int __init |
248 | init_ccwgroup (void) | 303 | init_ccwgroup (void) |
@@ -521,6 +576,5 @@ void ccwgroup_remove_ccwdev(struct ccw_device *cdev) | |||
521 | MODULE_LICENSE("GPL"); | 576 | MODULE_LICENSE("GPL"); |
522 | EXPORT_SYMBOL(ccwgroup_driver_register); | 577 | EXPORT_SYMBOL(ccwgroup_driver_register); |
523 | EXPORT_SYMBOL(ccwgroup_driver_unregister); | 578 | EXPORT_SYMBOL(ccwgroup_driver_unregister); |
524 | EXPORT_SYMBOL(ccwgroup_create); | ||
525 | EXPORT_SYMBOL(ccwgroup_probe_ccwdev); | 579 | EXPORT_SYMBOL(ccwgroup_probe_ccwdev); |
526 | EXPORT_SYMBOL(ccwgroup_remove_ccwdev); | 580 | EXPORT_SYMBOL(ccwgroup_remove_ccwdev); |