aboutsummaryrefslogtreecommitdiffstats
path: root/sound/core/info.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/core/info.c')
-rw-r--r--sound/core/info.c569
1 files changed, 248 insertions, 321 deletions
diff --git a/sound/core/info.c b/sound/core/info.c
index 9f404e965ea2..8c1275f0fcbd 100644
--- a/sound/core/info.c
+++ b/sound/core/info.c
@@ -81,66 +81,6 @@ static int snd_info_version_init(void);
81static int snd_info_version_done(void); 81static int snd_info_version_done(void);
82static void snd_info_disconnect(struct snd_info_entry *entry); 82static void snd_info_disconnect(struct snd_info_entry *entry);
83 83
84
85/* resize the proc r/w buffer */
86static int resize_info_buffer(struct snd_info_buffer *buffer,
87 unsigned int nsize)
88{
89 char *nbuf;
90
91 nsize = PAGE_ALIGN(nsize);
92 nbuf = krealloc(buffer->buffer, nsize, GFP_KERNEL | __GFP_ZERO);
93 if (! nbuf)
94 return -ENOMEM;
95
96 buffer->buffer = nbuf;
97 buffer->len = nsize;
98 return 0;
99}
100
101/**
102 * snd_iprintf - printf on the procfs buffer
103 * @buffer: the procfs buffer
104 * @fmt: the printf format
105 *
106 * Outputs the string on the procfs buffer just like printf().
107 *
108 * Return: The size of output string, or a negative error code.
109 */
110int snd_iprintf(struct snd_info_buffer *buffer, const char *fmt, ...)
111{
112 va_list args;
113 int len, res;
114 int err = 0;
115
116 might_sleep();
117 if (buffer->stop || buffer->error)
118 return 0;
119 len = buffer->len - buffer->size;
120 va_start(args, fmt);
121 for (;;) {
122 va_list ap;
123 va_copy(ap, args);
124 res = vsnprintf(buffer->buffer + buffer->curr, len, fmt, ap);
125 va_end(ap);
126 if (res < len)
127 break;
128 err = resize_info_buffer(buffer, buffer->len + PAGE_SIZE);
129 if (err < 0)
130 break;
131 len = buffer->len - buffer->size;
132 }
133 va_end(args);
134
135 if (err < 0)
136 return err;
137 buffer->curr += res;
138 buffer->size += res;
139 return res;
140}
141
142EXPORT_SYMBOL(snd_iprintf);
143
144/* 84/*
145 85
146 */ 86 */
@@ -153,6 +93,37 @@ EXPORT_SYMBOL(snd_seq_root);
153struct snd_info_entry *snd_oss_root; 93struct snd_info_entry *snd_oss_root;
154#endif 94#endif
155 95
96static int alloc_info_private(struct snd_info_entry *entry,
97 struct snd_info_private_data **ret)
98{
99 struct snd_info_private_data *data;
100
101 if (!entry || !entry->p)
102 return -ENODEV;
103 if (!try_module_get(entry->module))
104 return -EFAULT;
105 data = kzalloc(sizeof(*data), GFP_KERNEL);
106 if (!data) {
107 module_put(entry->module);
108 return -ENOMEM;
109 }
110 data->entry = entry;
111 *ret = data;
112 return 0;
113}
114
115static bool valid_pos(loff_t pos, size_t count)
116{
117 if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
118 return false;
119 if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
120 return false;
121 return true;
122}
123
124/*
125 * file ops for binary proc files
126 */
156static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig) 127static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
157{ 128{
158 struct snd_info_private_data *data; 129 struct snd_info_private_data *data;
@@ -162,17 +133,14 @@ static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
162 data = file->private_data; 133 data = file->private_data;
163 entry = data->entry; 134 entry = data->entry;
164 mutex_lock(&entry->access); 135 mutex_lock(&entry->access);
165 if (entry->content == SNDRV_INFO_CONTENT_DATA && 136 if (entry->c.ops->llseek) {
166 entry->c.ops->llseek) {
167 offset = entry->c.ops->llseek(entry, 137 offset = entry->c.ops->llseek(entry,
168 data->file_private_data, 138 data->file_private_data,
169 file, offset, orig); 139 file, offset, orig);
170 goto out; 140 goto out;
171 } 141 }
172 if (entry->content == SNDRV_INFO_CONTENT_DATA) 142
173 size = entry->size; 143 size = entry->size;
174 else
175 size = 0;
176 switch (orig) { 144 switch (orig) {
177 case SEEK_SET: 145 case SEEK_SET:
178 break; 146 break;
@@ -201,45 +169,20 @@ static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
201static ssize_t snd_info_entry_read(struct file *file, char __user *buffer, 169static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
202 size_t count, loff_t * offset) 170 size_t count, loff_t * offset)
203{ 171{
204 struct snd_info_private_data *data; 172 struct snd_info_private_data *data = file->private_data;
205 struct snd_info_entry *entry; 173 struct snd_info_entry *entry = data->entry;
206 struct snd_info_buffer *buf; 174 size_t size;
207 size_t size = 0;
208 loff_t pos; 175 loff_t pos;
209 176
210 data = file->private_data;
211 if (snd_BUG_ON(!data))
212 return -ENXIO;
213 pos = *offset; 177 pos = *offset;
214 if (pos < 0 || (long) pos != pos || (ssize_t) count < 0) 178 if (!valid_pos(pos, count))
215 return -EIO;
216 if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
217 return -EIO; 179 return -EIO;
218 entry = data->entry; 180 if (pos >= entry->size)
219 switch (entry->content) { 181 return 0;
220 case SNDRV_INFO_CONTENT_TEXT: 182 size = entry->size - pos;
221 buf = data->rbuffer; 183 size = min(count, size);
222 if (buf == NULL) 184 size = entry->c.ops->read(entry, data->file_private_data,
223 return -EIO; 185 file, buffer, size, pos);
224 if (pos >= buf->size)
225 return 0;
226 size = buf->size - pos;
227 size = min(count, size);
228 if (copy_to_user(buffer, buf->buffer + pos, size))
229 return -EFAULT;
230 break;
231 case SNDRV_INFO_CONTENT_DATA:
232 if (pos >= entry->size)
233 return 0;
234 if (entry->c.ops->read) {
235 size = entry->size - pos;
236 size = min(count, size);
237 size = entry->c.ops->read(entry,
238 data->file_private_data,
239 file, buffer, size, pos);
240 }
241 break;
242 }
243 if ((ssize_t) size > 0) 186 if ((ssize_t) size > 0)
244 *offset = pos + size; 187 *offset = pos + size;
245 return size; 188 return size;
@@ -248,280 +191,259 @@ static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
248static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer, 191static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer,
249 size_t count, loff_t * offset) 192 size_t count, loff_t * offset)
250{ 193{
251 struct snd_info_private_data *data; 194 struct snd_info_private_data *data = file->private_data;
252 struct snd_info_entry *entry; 195 struct snd_info_entry *entry = data->entry;
253 struct snd_info_buffer *buf;
254 ssize_t size = 0; 196 ssize_t size = 0;
255 loff_t pos; 197 loff_t pos;
256 198
257 data = file->private_data;
258 if (snd_BUG_ON(!data))
259 return -ENXIO;
260 entry = data->entry;
261 pos = *offset; 199 pos = *offset;
262 if (pos < 0 || (long) pos != pos || (ssize_t) count < 0) 200 if (!valid_pos(pos, count))
263 return -EIO; 201 return -EIO;
264 if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos) 202 if (count > 0) {
265 return -EIO; 203 size_t maxsize = entry->size - pos;
266 switch (entry->content) { 204 count = min(count, maxsize);
267 case SNDRV_INFO_CONTENT_TEXT: 205 size = entry->c.ops->write(entry, data->file_private_data,
268 buf = data->wbuffer; 206 file, buffer, count, pos);
269 if (buf == NULL)
270 return -EIO;
271 mutex_lock(&entry->access);
272 if (pos + count >= buf->len) {
273 if (resize_info_buffer(buf, pos + count)) {
274 mutex_unlock(&entry->access);
275 return -ENOMEM;
276 }
277 }
278 if (copy_from_user(buf->buffer + pos, buffer, count)) {
279 mutex_unlock(&entry->access);
280 return -EFAULT;
281 }
282 buf->size = pos + count;
283 mutex_unlock(&entry->access);
284 size = count;
285 break;
286 case SNDRV_INFO_CONTENT_DATA:
287 if (entry->c.ops->write && count > 0) {
288 size_t maxsize = entry->size - pos;
289 count = min(count, maxsize);
290 size = entry->c.ops->write(entry,
291 data->file_private_data,
292 file, buffer, count, pos);
293 }
294 break;
295 } 207 }
296 if ((ssize_t) size > 0) 208 if (size > 0)
297 *offset = pos + size; 209 *offset = pos + size;
298 return size; 210 return size;
299} 211}
300 212
301static int snd_info_entry_open(struct inode *inode, struct file *file) 213static unsigned int snd_info_entry_poll(struct file *file, poll_table *wait)
214{
215 struct snd_info_private_data *data = file->private_data;
216 struct snd_info_entry *entry = data->entry;
217 unsigned int mask = 0;
218
219 if (entry->c.ops->poll)
220 return entry->c.ops->poll(entry,
221 data->file_private_data,
222 file, wait);
223 if (entry->c.ops->read)
224 mask |= POLLIN | POLLRDNORM;
225 if (entry->c.ops->write)
226 mask |= POLLOUT | POLLWRNORM;
227 return mask;
228}
229
230static long snd_info_entry_ioctl(struct file *file, unsigned int cmd,
231 unsigned long arg)
232{
233 struct snd_info_private_data *data = file->private_data;
234 struct snd_info_entry *entry = data->entry;
235
236 if (!entry->c.ops->ioctl)
237 return -ENOTTY;
238 return entry->c.ops->ioctl(entry, data->file_private_data,
239 file, cmd, arg);
240}
241
242static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma)
302{ 243{
244 struct inode *inode = file_inode(file);
245 struct snd_info_private_data *data;
303 struct snd_info_entry *entry; 246 struct snd_info_entry *entry;
247
248 data = file->private_data;
249 if (data == NULL)
250 return 0;
251 entry = data->entry;
252 if (!entry->c.ops->mmap)
253 return -ENXIO;
254 return entry->c.ops->mmap(entry, data->file_private_data,
255 inode, file, vma);
256}
257
258static int snd_info_entry_open(struct inode *inode, struct file *file)
259{
260 struct snd_info_entry *entry = PDE_DATA(inode);
304 struct snd_info_private_data *data; 261 struct snd_info_private_data *data;
305 struct snd_info_buffer *buffer;
306 int mode, err; 262 int mode, err;
307 263
308 mutex_lock(&info_mutex); 264 mutex_lock(&info_mutex);
309 entry = PDE_DATA(inode); 265 err = alloc_info_private(entry, &data);
310 if (entry == NULL || ! entry->p) { 266 if (err < 0)
311 mutex_unlock(&info_mutex); 267 goto unlock;
312 return -ENODEV; 268
313 }
314 if (!try_module_get(entry->module)) {
315 err = -EFAULT;
316 goto __error1;
317 }
318 mode = file->f_flags & O_ACCMODE; 269 mode = file->f_flags & O_ACCMODE;
319 if (mode == O_RDONLY || mode == O_RDWR) { 270 if (((mode == O_RDONLY || mode == O_RDWR) && !entry->c.ops->read) ||
320 if ((entry->content == SNDRV_INFO_CONTENT_DATA && 271 ((mode == O_WRONLY || mode == O_RDWR) && !entry->c.ops->write)) {
321 entry->c.ops->read == NULL)) { 272 err = -ENODEV;
322 err = -ENODEV; 273 goto error;
323 goto __error;
324 }
325 }
326 if (mode == O_WRONLY || mode == O_RDWR) {
327 if ((entry->content == SNDRV_INFO_CONTENT_DATA &&
328 entry->c.ops->write == NULL)) {
329 err = -ENODEV;
330 goto __error;
331 }
332 } 274 }
333 data = kzalloc(sizeof(*data), GFP_KERNEL); 275
334 if (data == NULL) { 276 if (entry->c.ops->open) {
335 err = -ENOMEM; 277 err = entry->c.ops->open(entry, mode, &data->file_private_data);
336 goto __error; 278 if (err < 0)
337 } 279 goto error;
338 data->entry = entry;
339 switch (entry->content) {
340 case SNDRV_INFO_CONTENT_TEXT:
341 if (mode == O_RDONLY || mode == O_RDWR) {
342 buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
343 if (buffer == NULL)
344 goto __nomem;
345 data->rbuffer = buffer;
346 buffer->len = PAGE_SIZE;
347 buffer->buffer = kzalloc(buffer->len, GFP_KERNEL);
348 if (buffer->buffer == NULL)
349 goto __nomem;
350 }
351 if (mode == O_WRONLY || mode == O_RDWR) {
352 buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
353 if (buffer == NULL)
354 goto __nomem;
355 data->wbuffer = buffer;
356 buffer->len = PAGE_SIZE;
357 buffer->buffer = kmalloc(buffer->len, GFP_KERNEL);
358 if (buffer->buffer == NULL)
359 goto __nomem;
360 }
361 break;
362 case SNDRV_INFO_CONTENT_DATA: /* data */
363 if (entry->c.ops->open) {
364 if ((err = entry->c.ops->open(entry, mode,
365 &data->file_private_data)) < 0) {
366 kfree(data);
367 goto __error;
368 }
369 }
370 break;
371 } 280 }
281
372 file->private_data = data; 282 file->private_data = data;
373 mutex_unlock(&info_mutex); 283 mutex_unlock(&info_mutex);
374 if (entry->content == SNDRV_INFO_CONTENT_TEXT &&
375 (mode == O_RDONLY || mode == O_RDWR)) {
376 if (entry->c.text.read) {
377 mutex_lock(&entry->access);
378 entry->c.text.read(entry, data->rbuffer);
379 mutex_unlock(&entry->access);
380 }
381 }
382 return 0; 284 return 0;
383 285
384 __nomem: 286 error:
385 if (data->rbuffer) {
386 kfree(data->rbuffer->buffer);
387 kfree(data->rbuffer);
388 }
389 if (data->wbuffer) {
390 kfree(data->wbuffer->buffer);
391 kfree(data->wbuffer);
392 }
393 kfree(data); 287 kfree(data);
394 err = -ENOMEM;
395 __error:
396 module_put(entry->module); 288 module_put(entry->module);
397 __error1: 289 unlock:
398 mutex_unlock(&info_mutex); 290 mutex_unlock(&info_mutex);
399 return err; 291 return err;
400} 292}
401 293
402static int snd_info_entry_release(struct inode *inode, struct file *file) 294static int snd_info_entry_release(struct inode *inode, struct file *file)
403{ 295{
404 struct snd_info_entry *entry; 296 struct snd_info_private_data *data = file->private_data;
405 struct snd_info_private_data *data; 297 struct snd_info_entry *entry = data->entry;
406 int mode;
407 298
408 mode = file->f_flags & O_ACCMODE; 299 if (entry->c.ops->release)
409 data = file->private_data; 300 entry->c.ops->release(entry, file->f_flags & O_ACCMODE,
410 entry = data->entry; 301 data->file_private_data);
411 switch (entry->content) {
412 case SNDRV_INFO_CONTENT_TEXT:
413 if (data->rbuffer) {
414 kfree(data->rbuffer->buffer);
415 kfree(data->rbuffer);
416 }
417 if (data->wbuffer) {
418 if (entry->c.text.write) {
419 entry->c.text.write(entry, data->wbuffer);
420 if (data->wbuffer->error) {
421 if (entry->card)
422 dev_warn(entry->card->dev, "info: data write error to %s (%i)\n",
423 entry->name,
424 data->wbuffer->error);
425 else
426 pr_warn("ALSA: info: data write error to %s (%i)\n",
427 entry->name,
428 data->wbuffer->error);
429 }
430 }
431 kfree(data->wbuffer->buffer);
432 kfree(data->wbuffer);
433 }
434 break;
435 case SNDRV_INFO_CONTENT_DATA:
436 if (entry->c.ops->release)
437 entry->c.ops->release(entry, mode,
438 data->file_private_data);
439 break;
440 }
441 module_put(entry->module); 302 module_put(entry->module);
442 kfree(data); 303 kfree(data);
443 return 0; 304 return 0;
444} 305}
445 306
446static unsigned int snd_info_entry_poll(struct file *file, poll_table * wait) 307static const struct file_operations snd_info_entry_operations =
447{ 308{
448 struct snd_info_private_data *data; 309 .owner = THIS_MODULE,
449 struct snd_info_entry *entry; 310 .llseek = snd_info_entry_llseek,
450 unsigned int mask; 311 .read = snd_info_entry_read,
312 .write = snd_info_entry_write,
313 .poll = snd_info_entry_poll,
314 .unlocked_ioctl = snd_info_entry_ioctl,
315 .mmap = snd_info_entry_mmap,
316 .open = snd_info_entry_open,
317 .release = snd_info_entry_release,
318};
451 319
452 data = file->private_data; 320/*
453 if (data == NULL) 321 * file ops for text proc files
454 return 0; 322 */
455 entry = data->entry; 323static ssize_t snd_info_text_entry_write(struct file *file,
456 mask = 0; 324 const char __user *buffer,
457 switch (entry->content) { 325 size_t count, loff_t *offset)
458 case SNDRV_INFO_CONTENT_DATA: 326{
459 if (entry->c.ops->poll) 327 struct seq_file *m = file->private_data;
460 return entry->c.ops->poll(entry, 328 struct snd_info_private_data *data = m->private;
461 data->file_private_data, 329 struct snd_info_entry *entry = data->entry;
462 file, wait); 330 struct snd_info_buffer *buf;
463 if (entry->c.ops->read) 331 loff_t pos;
464 mask |= POLLIN | POLLRDNORM; 332 size_t next;
465 if (entry->c.ops->write) 333 int err = 0;
466 mask |= POLLOUT | POLLWRNORM; 334
467 break; 335 pos = *offset;
336 if (!valid_pos(pos, count))
337 return -EIO;
338 next = pos + count;
339 mutex_lock(&entry->access);
340 buf = data->wbuffer;
341 if (!buf) {
342 data->wbuffer = buf = kzalloc(sizeof(*buf), GFP_KERNEL);
343 if (!buf) {
344 err = -ENOMEM;
345 goto error;
346 }
468 } 347 }
469 return mask; 348 if (next > buf->len) {
349 char *nbuf = krealloc(buf->buffer, PAGE_ALIGN(next),
350 GFP_KERNEL | __GFP_ZERO);
351 if (!nbuf) {
352 err = -ENOMEM;
353 goto error;
354 }
355 buf->buffer = nbuf;
356 buf->len = PAGE_ALIGN(next);
357 }
358 if (copy_from_user(buf->buffer + pos, buffer, count)) {
359 err = -EFAULT;
360 goto error;
361 }
362 buf->size = next;
363 error:
364 mutex_unlock(&entry->access);
365 if (err < 0)
366 return err;
367 *offset = next;
368 return count;
470} 369}
471 370
472static long snd_info_entry_ioctl(struct file *file, unsigned int cmd, 371static int snd_info_seq_show(struct seq_file *seq, void *p)
473 unsigned long arg)
474{ 372{
475 struct snd_info_private_data *data; 373 struct snd_info_private_data *data = seq->private;
476 struct snd_info_entry *entry; 374 struct snd_info_entry *entry = data->entry;
477 375
478 data = file->private_data; 376 if (entry->c.text.read) {
479 if (data == NULL) 377 data->rbuffer->buffer = (char *)seq; /* XXX hack! */
480 return 0; 378 entry->c.text.read(entry, data->rbuffer);
481 entry = data->entry;
482 switch (entry->content) {
483 case SNDRV_INFO_CONTENT_DATA:
484 if (entry->c.ops->ioctl)
485 return entry->c.ops->ioctl(entry,
486 data->file_private_data,
487 file, cmd, arg);
488 break;
489 } 379 }
490 return -ENOTTY; 380 return 0;
491} 381}
492 382
493static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma) 383static int snd_info_text_entry_open(struct inode *inode, struct file *file)
494{ 384{
495 struct inode *inode = file_inode(file); 385 struct snd_info_entry *entry = PDE_DATA(inode);
496 struct snd_info_private_data *data; 386 struct snd_info_private_data *data;
497 struct snd_info_entry *entry; 387 int err;
498 388
499 data = file->private_data; 389 mutex_lock(&info_mutex);
500 if (data == NULL) 390 err = alloc_info_private(entry, &data);
501 return 0; 391 if (err < 0)
502 entry = data->entry; 392 goto unlock;
503 switch (entry->content) { 393
504 case SNDRV_INFO_CONTENT_DATA: 394 data->rbuffer = kzalloc(sizeof(*data->rbuffer), GFP_KERNEL);
505 if (entry->c.ops->mmap) 395 if (!data->rbuffer) {
506 return entry->c.ops->mmap(entry, 396 err = -ENOMEM;
507 data->file_private_data, 397 goto error;
508 inode, file, vma); 398 }
509 break; 399 if (entry->size)
400 err = single_open_size(file, snd_info_seq_show, data,
401 entry->size);
402 else
403 err = single_open(file, snd_info_seq_show, data);
404 if (err < 0)
405 goto error;
406 mutex_unlock(&info_mutex);
407 return 0;
408
409 error:
410 kfree(data->rbuffer);
411 kfree(data);
412 module_put(entry->module);
413 unlock:
414 mutex_unlock(&info_mutex);
415 return err;
416}
417
418static int snd_info_text_entry_release(struct inode *inode, struct file *file)
419{
420 struct seq_file *m = file->private_data;
421 struct snd_info_private_data *data = m->private;
422 struct snd_info_entry *entry = data->entry;
423
424 if (data->wbuffer && entry->c.text.write)
425 entry->c.text.write(entry, data->wbuffer);
426
427 single_release(inode, file);
428 kfree(data->rbuffer);
429 if (data->wbuffer) {
430 kfree(data->wbuffer->buffer);
431 kfree(data->wbuffer);
510 } 432 }
511 return -ENXIO; 433
434 module_put(entry->module);
435 kfree(data);
436 return 0;
512} 437}
513 438
514static const struct file_operations snd_info_entry_operations = 439static const struct file_operations snd_info_text_entry_ops =
515{ 440{
516 .owner = THIS_MODULE, 441 .owner = THIS_MODULE,
517 .llseek = snd_info_entry_llseek, 442 .open = snd_info_text_entry_open,
518 .read = snd_info_entry_read, 443 .release = snd_info_text_entry_release,
519 .write = snd_info_entry_write, 444 .write = snd_info_text_entry_write,
520 .poll = snd_info_entry_poll, 445 .llseek = seq_lseek,
521 .unlocked_ioctl = snd_info_entry_ioctl, 446 .read = seq_read,
522 .mmap = snd_info_entry_mmap,
523 .open = snd_info_entry_open,
524 .release = snd_info_entry_release,
525}; 447};
526 448
527int __init snd_info_init(void) 449int __init snd_info_init(void)
@@ -955,8 +877,13 @@ int snd_info_register(struct snd_info_entry * entry)
955 return -ENOMEM; 877 return -ENOMEM;
956 } 878 }
957 } else { 879 } else {
880 const struct file_operations *ops;
881 if (entry->content == SNDRV_INFO_CONTENT_DATA)
882 ops = &snd_info_entry_operations;
883 else
884 ops = &snd_info_text_entry_ops;
958 p = proc_create_data(entry->name, entry->mode, root, 885 p = proc_create_data(entry->name, entry->mode, root,
959 &snd_info_entry_operations, entry); 886 ops, entry);
960 if (!p) { 887 if (!p) {
961 mutex_unlock(&info_mutex); 888 mutex_unlock(&info_mutex);
962 return -ENOMEM; 889 return -ENOMEM;