Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* |
Uwe Zeisberger | f30c226 | 2006-10-03 23:01:26 +0200 | [diff] [blame] | 2 | * sound/oss/audio.c |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3 | * |
| 4 | * Device file manager for /dev/audio |
| 5 | */ |
| 6 | |
| 7 | /* |
| 8 | * Copyright (C) by Hannu Savolainen 1993-1997 |
| 9 | * |
| 10 | * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) |
| 11 | * Version 2 (June 1991). See the "COPYING" file distributed with this software |
| 12 | * for more info. |
| 13 | */ |
| 14 | /* |
| 15 | * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) |
| 16 | * Thomas Sailer : moved several static variables into struct audio_operations |
| 17 | * (which is grossly misnamed btw.) because they have the same |
| 18 | * lifetime as the rest in there and dynamic allocation saves |
| 19 | * 12k or so |
| 20 | * Thomas Sailer : use more logical O_NONBLOCK semantics |
| 21 | * Daniel Rodriksson: reworked the use of the device specific copy_user |
| 22 | * still generic |
| 23 | * Horst von Brand: Add missing #include <linux/string.h> |
| 24 | * Chris Rankin : Update the module-usage counter for the coprocessor, |
| 25 | * and decrement the counters again if we cannot open |
| 26 | * the audio device. |
| 27 | */ |
| 28 | |
| 29 | #include <linux/stddef.h> |
| 30 | #include <linux/string.h> |
| 31 | #include <linux/kmod.h> |
| 32 | |
| 33 | #include "sound_config.h" |
| 34 | #include "ulaw.h" |
| 35 | #include "coproc.h" |
| 36 | |
| 37 | #define NEUTRAL8 0x80 |
| 38 | #define NEUTRAL16 0x00 |
| 39 | |
| 40 | |
| 41 | static int dma_ioctl(int dev, unsigned int cmd, void __user *arg); |
| 42 | |
| 43 | static int set_format(int dev, int fmt) |
| 44 | { |
| 45 | if (fmt != AFMT_QUERY) |
| 46 | { |
| 47 | audio_devs[dev]->local_conversion = 0; |
| 48 | |
| 49 | if (!(audio_devs[dev]->format_mask & fmt)) /* Not supported */ |
| 50 | { |
| 51 | if (fmt == AFMT_MU_LAW) |
| 52 | { |
| 53 | fmt = AFMT_U8; |
| 54 | audio_devs[dev]->local_conversion = CNV_MU_LAW; |
| 55 | } |
| 56 | else |
| 57 | fmt = AFMT_U8; /* This is always supported */ |
| 58 | } |
| 59 | audio_devs[dev]->audio_format = audio_devs[dev]->d->set_bits(dev, fmt); |
| 60 | audio_devs[dev]->local_format = fmt; |
| 61 | } |
| 62 | else |
| 63 | return audio_devs[dev]->local_format; |
| 64 | |
| 65 | if (audio_devs[dev]->local_conversion) |
| 66 | return audio_devs[dev]->local_conversion; |
| 67 | else |
| 68 | return audio_devs[dev]->local_format; |
| 69 | } |
| 70 | |
| 71 | int audio_open(int dev, struct file *file) |
| 72 | { |
| 73 | int ret; |
| 74 | int bits; |
| 75 | int dev_type = dev & 0x0f; |
| 76 | int mode = translate_mode(file); |
| 77 | const struct audio_driver *driver; |
| 78 | const struct coproc_operations *coprocessor; |
| 79 | |
| 80 | dev = dev >> 4; |
| 81 | |
| 82 | if (dev_type == SND_DEV_DSP16) |
| 83 | bits = 16; |
| 84 | else |
| 85 | bits = 8; |
| 86 | |
| 87 | if (dev < 0 || dev >= num_audiodevs) |
| 88 | return -ENXIO; |
| 89 | |
| 90 | driver = audio_devs[dev]->d; |
| 91 | |
| 92 | if (!try_module_get(driver->owner)) |
| 93 | return -ENODEV; |
| 94 | |
| 95 | if ((ret = DMAbuf_open(dev, mode)) < 0) |
| 96 | goto error_1; |
| 97 | |
| 98 | if ( (coprocessor = audio_devs[dev]->coproc) != NULL ) { |
| 99 | if (!try_module_get(coprocessor->owner)) |
| 100 | goto error_2; |
| 101 | |
| 102 | if ((ret = coprocessor->open(coprocessor->devc, COPR_PCM)) < 0) { |
| 103 | printk(KERN_WARNING "Sound: Can't access coprocessor device\n"); |
| 104 | goto error_3; |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | audio_devs[dev]->local_conversion = 0; |
| 109 | |
| 110 | if (dev_type == SND_DEV_AUDIO) |
| 111 | set_format(dev, AFMT_MU_LAW); |
| 112 | else |
| 113 | set_format(dev, bits); |
| 114 | |
| 115 | audio_devs[dev]->audio_mode = AM_NONE; |
| 116 | |
| 117 | return 0; |
| 118 | |
| 119 | /* |
| 120 | * Clean-up stack: this is what needs (un)doing if |
| 121 | * we can't open the audio device ... |
| 122 | */ |
| 123 | error_3: |
| 124 | module_put(coprocessor->owner); |
| 125 | |
| 126 | error_2: |
| 127 | DMAbuf_release(dev, mode); |
| 128 | |
| 129 | error_1: |
| 130 | module_put(driver->owner); |
| 131 | |
| 132 | return ret; |
| 133 | } |
| 134 | |
| 135 | static void sync_output(int dev) |
| 136 | { |
| 137 | int p, i; |
| 138 | int l; |
| 139 | struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; |
| 140 | |
| 141 | if (dmap->fragment_size <= 0) |
| 142 | return; |
| 143 | dmap->flags |= DMA_POST; |
| 144 | |
| 145 | /* Align the write pointer with fragment boundaries */ |
| 146 | |
| 147 | if ((l = dmap->user_counter % dmap->fragment_size) > 0) |
| 148 | { |
| 149 | int len; |
| 150 | unsigned long offs = dmap->user_counter % dmap->bytes_in_use; |
| 151 | |
| 152 | len = dmap->fragment_size - l; |
| 153 | memset(dmap->raw_buf + offs, dmap->neutral_byte, len); |
| 154 | DMAbuf_move_wrpointer(dev, len); |
| 155 | } |
| 156 | |
| 157 | /* |
| 158 | * Clean all unused buffer fragments. |
| 159 | */ |
| 160 | |
| 161 | p = dmap->qtail; |
| 162 | dmap->flags |= DMA_POST; |
| 163 | |
| 164 | for (i = dmap->qlen + 1; i < dmap->nbufs; i++) |
| 165 | { |
| 166 | p = (p + 1) % dmap->nbufs; |
| 167 | if (((dmap->raw_buf + p * dmap->fragment_size) + dmap->fragment_size) > |
| 168 | (dmap->raw_buf + dmap->buffsize)) |
| 169 | printk(KERN_ERR "audio: Buffer error 2\n"); |
| 170 | |
| 171 | memset(dmap->raw_buf + p * dmap->fragment_size, |
| 172 | dmap->neutral_byte, |
| 173 | dmap->fragment_size); |
| 174 | } |
| 175 | |
| 176 | dmap->flags |= DMA_DIRTY; |
| 177 | } |
| 178 | |
| 179 | void audio_release(int dev, struct file *file) |
| 180 | { |
| 181 | const struct coproc_operations *coprocessor; |
| 182 | int mode = translate_mode(file); |
| 183 | |
| 184 | dev = dev >> 4; |
| 185 | |
| 186 | /* |
| 187 | * We do this in DMAbuf_release(). Why are we doing it |
| 188 | * here? Why don't we test the file mode before setting |
| 189 | * both flags? DMAbuf_release() does. |
| 190 | * ...pester...pester...pester... |
| 191 | */ |
| 192 | audio_devs[dev]->dmap_out->closing = 1; |
| 193 | audio_devs[dev]->dmap_in->closing = 1; |
| 194 | |
| 195 | /* |
| 196 | * We need to make sure we allocated the dmap_out buffer |
| 197 | * before we go mucking around with it in sync_output(). |
| 198 | */ |
| 199 | if (mode & OPEN_WRITE) |
| 200 | sync_output(dev); |
| 201 | |
| 202 | if ( (coprocessor = audio_devs[dev]->coproc) != NULL ) { |
| 203 | coprocessor->close(coprocessor->devc, COPR_PCM); |
| 204 | module_put(coprocessor->owner); |
| 205 | } |
| 206 | DMAbuf_release(dev, mode); |
| 207 | |
| 208 | module_put(audio_devs[dev]->d->owner); |
| 209 | } |
| 210 | |
| 211 | static void translate_bytes(const unsigned char *table, unsigned char *buff, int n) |
| 212 | { |
| 213 | unsigned long i; |
| 214 | |
| 215 | if (n <= 0) |
| 216 | return; |
| 217 | |
| 218 | for (i = 0; i < n; ++i) |
| 219 | buff[i] = table[buff[i]]; |
| 220 | } |
| 221 | |
| 222 | int audio_write(int dev, struct file *file, const char __user *buf, int count) |
| 223 | { |
| 224 | int c, p, l, buf_size, used, returned; |
| 225 | int err; |
| 226 | char *dma_buf; |
| 227 | |
| 228 | dev = dev >> 4; |
| 229 | |
| 230 | p = 0; |
| 231 | c = count; |
| 232 | |
| 233 | if(count < 0) |
| 234 | return -EINVAL; |
| 235 | |
| 236 | if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) |
| 237 | return -EPERM; |
| 238 | |
| 239 | if (audio_devs[dev]->flags & DMA_DUPLEX) |
| 240 | audio_devs[dev]->audio_mode |= AM_WRITE; |
| 241 | else |
| 242 | audio_devs[dev]->audio_mode = AM_WRITE; |
| 243 | |
| 244 | if (!count) /* Flush output */ |
| 245 | { |
| 246 | sync_output(dev); |
| 247 | return 0; |
| 248 | } |
| 249 | |
| 250 | while (c) |
| 251 | { |
| 252 | if ((err = DMAbuf_getwrbuffer(dev, &dma_buf, &buf_size, !!(file->f_flags & O_NONBLOCK))) < 0) |
| 253 | { |
| 254 | /* Handle nonblocking mode */ |
| 255 | if ((file->f_flags & O_NONBLOCK) && err == -EAGAIN) |
| 256 | return p? p : -EAGAIN; /* No more space. Return # of accepted bytes */ |
| 257 | return err; |
| 258 | } |
| 259 | l = c; |
| 260 | |
| 261 | if (l > buf_size) |
| 262 | l = buf_size; |
| 263 | |
| 264 | returned = l; |
| 265 | used = l; |
| 266 | if (!audio_devs[dev]->d->copy_user) |
| 267 | { |
| 268 | if ((dma_buf + l) > |
| 269 | (audio_devs[dev]->dmap_out->raw_buf + audio_devs[dev]->dmap_out->buffsize)) |
| 270 | { |
| 271 | printk(KERN_ERR "audio: Buffer error 3 (%lx,%d), (%lx, %d)\n", (long) dma_buf, l, (long) audio_devs[dev]->dmap_out->raw_buf, (int) audio_devs[dev]->dmap_out->buffsize); |
| 272 | return -EDOM; |
| 273 | } |
| 274 | if (dma_buf < audio_devs[dev]->dmap_out->raw_buf) |
| 275 | { |
| 276 | printk(KERN_ERR "audio: Buffer error 13 (%lx<%lx)\n", (long) dma_buf, (long) audio_devs[dev]->dmap_out->raw_buf); |
| 277 | return -EDOM; |
| 278 | } |
| 279 | if(copy_from_user(dma_buf, &(buf)[p], l)) |
| 280 | return -EFAULT; |
| 281 | } |
| 282 | else audio_devs[dev]->d->copy_user (dev, |
| 283 | dma_buf, 0, |
| 284 | buf, p, |
| 285 | c, buf_size, |
| 286 | &used, &returned, |
| 287 | l); |
| 288 | l = returned; |
| 289 | |
| 290 | if (audio_devs[dev]->local_conversion & CNV_MU_LAW) |
| 291 | { |
| 292 | translate_bytes(ulaw_dsp, (unsigned char *) dma_buf, l); |
| 293 | } |
| 294 | c -= used; |
| 295 | p += used; |
| 296 | DMAbuf_move_wrpointer(dev, l); |
| 297 | |
| 298 | } |
| 299 | |
| 300 | return count; |
| 301 | } |
| 302 | |
| 303 | int audio_read(int dev, struct file *file, char __user *buf, int count) |
| 304 | { |
| 305 | int c, p, l; |
| 306 | char *dmabuf; |
| 307 | int buf_no; |
| 308 | |
| 309 | dev = dev >> 4; |
| 310 | p = 0; |
| 311 | c = count; |
| 312 | |
| 313 | if (!(audio_devs[dev]->open_mode & OPEN_READ)) |
| 314 | return -EPERM; |
| 315 | |
| 316 | if ((audio_devs[dev]->audio_mode & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX)) |
| 317 | sync_output(dev); |
| 318 | |
| 319 | if (audio_devs[dev]->flags & DMA_DUPLEX) |
| 320 | audio_devs[dev]->audio_mode |= AM_READ; |
| 321 | else |
| 322 | audio_devs[dev]->audio_mode = AM_READ; |
| 323 | |
| 324 | while(c) |
| 325 | { |
| 326 | if ((buf_no = DMAbuf_getrdbuffer(dev, &dmabuf, &l, !!(file->f_flags & O_NONBLOCK))) < 0) |
| 327 | { |
| 328 | /* |
| 329 | * Nonblocking mode handling. Return current # of bytes |
| 330 | */ |
| 331 | |
| 332 | if (p > 0) /* Avoid throwing away data */ |
| 333 | return p; /* Return it instead */ |
| 334 | |
| 335 | if ((file->f_flags & O_NONBLOCK) && buf_no == -EAGAIN) |
| 336 | return -EAGAIN; |
| 337 | |
| 338 | return buf_no; |
| 339 | } |
| 340 | if (l > c) |
| 341 | l = c; |
| 342 | |
| 343 | /* |
| 344 | * Insert any local processing here. |
| 345 | */ |
| 346 | |
| 347 | if (audio_devs[dev]->local_conversion & CNV_MU_LAW) |
| 348 | { |
| 349 | translate_bytes(dsp_ulaw, (unsigned char *) dmabuf, l); |
| 350 | } |
| 351 | |
| 352 | { |
| 353 | char *fixit = dmabuf; |
| 354 | |
| 355 | if(copy_to_user(&(buf)[p], fixit, l)) |
| 356 | return -EFAULT; |
Peter Senna Tschudin | 395d9dd | 2012-09-28 11:24:57 +0200 | [diff] [blame] | 357 | } |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 358 | |
| 359 | DMAbuf_rmchars(dev, buf_no, l); |
| 360 | |
| 361 | p += l; |
| 362 | c -= l; |
| 363 | } |
| 364 | |
| 365 | return count - c; |
| 366 | } |
| 367 | |
| 368 | int audio_ioctl(int dev, struct file *file, unsigned int cmd, void __user *arg) |
| 369 | { |
| 370 | int val, count; |
| 371 | unsigned long flags; |
| 372 | struct dma_buffparms *dmap; |
| 373 | int __user *p = arg; |
| 374 | |
| 375 | dev = dev >> 4; |
| 376 | |
| 377 | if (_IOC_TYPE(cmd) == 'C') { |
| 378 | if (audio_devs[dev]->coproc) /* Coprocessor ioctl */ |
| 379 | return audio_devs[dev]->coproc->ioctl(audio_devs[dev]->coproc->devc, cmd, arg, 0); |
| 380 | /* else |
| 381 | printk(KERN_DEBUG"/dev/dsp%d: No coprocessor for this device\n", dev); */ |
| 382 | return -ENXIO; |
| 383 | } |
| 384 | else switch (cmd) |
| 385 | { |
| 386 | case SNDCTL_DSP_SYNC: |
| 387 | if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) |
| 388 | return 0; |
| 389 | if (audio_devs[dev]->dmap_out->fragment_size == 0) |
| 390 | return 0; |
| 391 | sync_output(dev); |
| 392 | DMAbuf_sync(dev); |
| 393 | DMAbuf_reset(dev); |
| 394 | return 0; |
| 395 | |
| 396 | case SNDCTL_DSP_POST: |
| 397 | if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) |
| 398 | return 0; |
| 399 | if (audio_devs[dev]->dmap_out->fragment_size == 0) |
| 400 | return 0; |
| 401 | audio_devs[dev]->dmap_out->flags |= DMA_POST | DMA_DIRTY; |
| 402 | sync_output(dev); |
| 403 | dma_ioctl(dev, SNDCTL_DSP_POST, NULL); |
| 404 | return 0; |
| 405 | |
| 406 | case SNDCTL_DSP_RESET: |
| 407 | audio_devs[dev]->audio_mode = AM_NONE; |
| 408 | DMAbuf_reset(dev); |
| 409 | return 0; |
| 410 | |
| 411 | case SNDCTL_DSP_GETFMTS: |
| 412 | val = audio_devs[dev]->format_mask | AFMT_MU_LAW; |
| 413 | break; |
| 414 | |
| 415 | case SNDCTL_DSP_SETFMT: |
| 416 | if (get_user(val, p)) |
| 417 | return -EFAULT; |
| 418 | val = set_format(dev, val); |
| 419 | break; |
| 420 | |
| 421 | case SNDCTL_DSP_GETISPACE: |
| 422 | if (!(audio_devs[dev]->open_mode & OPEN_READ)) |
| 423 | return 0; |
| 424 | if ((audio_devs[dev]->audio_mode & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX)) |
| 425 | return -EBUSY; |
| 426 | return dma_ioctl(dev, cmd, arg); |
| 427 | |
| 428 | case SNDCTL_DSP_GETOSPACE: |
| 429 | if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) |
| 430 | return -EPERM; |
| 431 | if ((audio_devs[dev]->audio_mode & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX)) |
| 432 | return -EBUSY; |
| 433 | return dma_ioctl(dev, cmd, arg); |
| 434 | |
| 435 | case SNDCTL_DSP_NONBLOCK: |
Jonathan Corbet | db1dd4d | 2009-02-06 15:25:24 -0700 | [diff] [blame] | 436 | spin_lock(&file->f_lock); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 437 | file->f_flags |= O_NONBLOCK; |
Jonathan Corbet | db1dd4d | 2009-02-06 15:25:24 -0700 | [diff] [blame] | 438 | spin_unlock(&file->f_lock); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 439 | return 0; |
| 440 | |
| 441 | case SNDCTL_DSP_GETCAPS: |
| 442 | val = 1 | DSP_CAP_MMAP; /* Revision level of this ioctl() */ |
| 443 | if (audio_devs[dev]->flags & DMA_DUPLEX && |
| 444 | audio_devs[dev]->open_mode == OPEN_READWRITE) |
| 445 | val |= DSP_CAP_DUPLEX; |
| 446 | if (audio_devs[dev]->coproc) |
| 447 | val |= DSP_CAP_COPROC; |
| 448 | if (audio_devs[dev]->d->local_qlen) /* Device has hidden buffers */ |
| 449 | val |= DSP_CAP_BATCH; |
| 450 | if (audio_devs[dev]->d->trigger) /* Supports SETTRIGGER */ |
| 451 | val |= DSP_CAP_TRIGGER; |
| 452 | break; |
| 453 | |
| 454 | case SOUND_PCM_WRITE_RATE: |
| 455 | if (get_user(val, p)) |
| 456 | return -EFAULT; |
| 457 | val = audio_devs[dev]->d->set_speed(dev, val); |
| 458 | break; |
| 459 | |
| 460 | case SOUND_PCM_READ_RATE: |
| 461 | val = audio_devs[dev]->d->set_speed(dev, 0); |
| 462 | break; |
| 463 | |
| 464 | case SNDCTL_DSP_STEREO: |
| 465 | if (get_user(val, p)) |
| 466 | return -EFAULT; |
| 467 | if (val > 1 || val < 0) |
| 468 | return -EINVAL; |
| 469 | val = audio_devs[dev]->d->set_channels(dev, val + 1) - 1; |
| 470 | break; |
| 471 | |
| 472 | case SOUND_PCM_WRITE_CHANNELS: |
| 473 | if (get_user(val, p)) |
| 474 | return -EFAULT; |
| 475 | val = audio_devs[dev]->d->set_channels(dev, val); |
| 476 | break; |
| 477 | |
| 478 | case SOUND_PCM_READ_CHANNELS: |
| 479 | val = audio_devs[dev]->d->set_channels(dev, 0); |
| 480 | break; |
| 481 | |
| 482 | case SOUND_PCM_READ_BITS: |
| 483 | val = audio_devs[dev]->d->set_bits(dev, 0); |
| 484 | break; |
| 485 | |
| 486 | case SNDCTL_DSP_SETDUPLEX: |
| 487 | if (audio_devs[dev]->open_mode != OPEN_READWRITE) |
| 488 | return -EPERM; |
| 489 | return (audio_devs[dev]->flags & DMA_DUPLEX) ? 0 : -EIO; |
| 490 | |
| 491 | case SNDCTL_DSP_PROFILE: |
| 492 | if (get_user(val, p)) |
| 493 | return -EFAULT; |
| 494 | if (audio_devs[dev]->open_mode & OPEN_WRITE) |
| 495 | audio_devs[dev]->dmap_out->applic_profile = val; |
| 496 | if (audio_devs[dev]->open_mode & OPEN_READ) |
| 497 | audio_devs[dev]->dmap_in->applic_profile = val; |
| 498 | return 0; |
| 499 | |
| 500 | case SNDCTL_DSP_GETODELAY: |
| 501 | dmap = audio_devs[dev]->dmap_out; |
| 502 | if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) |
| 503 | return -EINVAL; |
| 504 | if (!(dmap->flags & DMA_ALLOC_DONE)) |
| 505 | { |
| 506 | val=0; |
| 507 | break; |
| 508 | } |
| 509 | |
| 510 | spin_lock_irqsave(&dmap->lock,flags); |
| 511 | /* Compute number of bytes that have been played */ |
| 512 | count = DMAbuf_get_buffer_pointer (dev, dmap, DMODE_OUTPUT); |
| 513 | if (count < dmap->fragment_size && dmap->qhead != 0) |
| 514 | count += dmap->bytes_in_use; /* Pointer wrap not handled yet */ |
| 515 | count += dmap->byte_counter; |
| 516 | |
Lucas De Marchi | 25985ed | 2011-03-30 22:57:33 -0300 | [diff] [blame] | 517 | /* Subtract current count from the number of bytes written by app */ |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 518 | count = dmap->user_counter - count; |
| 519 | if (count < 0) |
| 520 | count = 0; |
| 521 | spin_unlock_irqrestore(&dmap->lock,flags); |
| 522 | val = count; |
| 523 | break; |
| 524 | |
| 525 | default: |
| 526 | return dma_ioctl(dev, cmd, arg); |
| 527 | } |
| 528 | return put_user(val, p); |
| 529 | } |
| 530 | |
| 531 | void audio_init_devices(void) |
| 532 | { |
| 533 | /* |
| 534 | * NOTE! This routine could be called several times during boot. |
| 535 | */ |
| 536 | } |
| 537 | |
| 538 | void reorganize_buffers(int dev, struct dma_buffparms *dmap, int recording) |
| 539 | { |
| 540 | /* |
| 541 | * This routine breaks the physical device buffers to logical ones. |
| 542 | */ |
| 543 | |
| 544 | struct audio_operations *dsp_dev = audio_devs[dev]; |
| 545 | |
| 546 | unsigned i, n; |
| 547 | unsigned sr, nc, sz, bsz; |
| 548 | |
| 549 | sr = dsp_dev->d->set_speed(dev, 0); |
| 550 | nc = dsp_dev->d->set_channels(dev, 0); |
| 551 | sz = dsp_dev->d->set_bits(dev, 0); |
| 552 | |
| 553 | if (sz == 8) |
| 554 | dmap->neutral_byte = NEUTRAL8; |
| 555 | else |
| 556 | dmap->neutral_byte = NEUTRAL16; |
| 557 | |
| 558 | if (sr < 1 || nc < 1 || sz < 1) |
| 559 | { |
| 560 | /* printk(KERN_DEBUG "Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n", dev, sr, nc, sz);*/ |
| 561 | sr = DSP_DEFAULT_SPEED; |
| 562 | nc = 1; |
| 563 | sz = 8; |
| 564 | } |
| 565 | |
| 566 | sz = sr * nc * sz; |
| 567 | |
| 568 | sz /= 8; /* #bits -> #bytes */ |
| 569 | dmap->data_rate = sz; |
| 570 | |
| 571 | if (!dmap->needs_reorg) |
| 572 | return; |
| 573 | dmap->needs_reorg = 0; |
| 574 | |
| 575 | if (dmap->fragment_size == 0) |
| 576 | { |
| 577 | /* Compute the fragment size using the default algorithm */ |
| 578 | |
| 579 | /* |
| 580 | * Compute a buffer size for time not exceeding 1 second. |
| 581 | * Usually this algorithm gives a buffer size for 0.5 to 1.0 seconds |
| 582 | * of sound (using the current speed, sample size and #channels). |
| 583 | */ |
| 584 | |
| 585 | bsz = dmap->buffsize; |
| 586 | while (bsz > sz) |
| 587 | bsz /= 2; |
| 588 | |
| 589 | if (bsz == dmap->buffsize) |
| 590 | bsz /= 2; /* Needs at least 2 buffers */ |
| 591 | |
| 592 | /* |
| 593 | * Split the computed fragment to smaller parts. After 3.5a9 |
| 594 | * the default subdivision is 4 which should give better |
| 595 | * results when recording. |
| 596 | */ |
| 597 | |
| 598 | if (dmap->subdivision == 0) /* Not already set */ |
| 599 | { |
| 600 | dmap->subdivision = 4; /* Init to the default value */ |
| 601 | |
| 602 | if ((bsz / dmap->subdivision) > 4096) |
| 603 | dmap->subdivision *= 2; |
| 604 | if ((bsz / dmap->subdivision) < 4096) |
| 605 | dmap->subdivision = 1; |
| 606 | } |
| 607 | bsz /= dmap->subdivision; |
| 608 | |
| 609 | if (bsz < 16) |
| 610 | bsz = 16; /* Just a sanity check */ |
| 611 | |
| 612 | dmap->fragment_size = bsz; |
| 613 | } |
| 614 | else |
| 615 | { |
| 616 | /* |
| 617 | * The process has specified the buffer size with SNDCTL_DSP_SETFRAGMENT or |
| 618 | * the buffer size computation has already been done. |
| 619 | */ |
| 620 | if (dmap->fragment_size > (dmap->buffsize / 2)) |
| 621 | dmap->fragment_size = (dmap->buffsize / 2); |
| 622 | bsz = dmap->fragment_size; |
| 623 | } |
| 624 | |
| 625 | if (audio_devs[dev]->min_fragment) |
| 626 | if (bsz < (1 << audio_devs[dev]->min_fragment)) |
| 627 | bsz = 1 << audio_devs[dev]->min_fragment; |
| 628 | if (audio_devs[dev]->max_fragment) |
| 629 | if (bsz > (1 << audio_devs[dev]->max_fragment)) |
| 630 | bsz = 1 << audio_devs[dev]->max_fragment; |
| 631 | bsz &= ~0x07; /* Force size which is multiple of 8 bytes */ |
| 632 | #ifdef OS_DMA_ALIGN_CHECK |
| 633 | OS_DMA_ALIGN_CHECK(bsz); |
| 634 | #endif |
| 635 | |
| 636 | n = dmap->buffsize / bsz; |
| 637 | if (n > MAX_SUB_BUFFERS) |
| 638 | n = MAX_SUB_BUFFERS; |
| 639 | if (n > dmap->max_fragments) |
| 640 | n = dmap->max_fragments; |
| 641 | |
| 642 | if (n < 2) |
| 643 | { |
| 644 | n = 2; |
| 645 | bsz /= 2; |
| 646 | } |
| 647 | dmap->nbufs = n; |
| 648 | dmap->bytes_in_use = n * bsz; |
| 649 | dmap->fragment_size = bsz; |
| 650 | dmap->max_byte_counter = (dmap->data_rate * 60 * 60) + |
| 651 | dmap->bytes_in_use; /* Approximately one hour */ |
| 652 | |
| 653 | if (dmap->raw_buf) |
| 654 | { |
| 655 | memset(dmap->raw_buf, dmap->neutral_byte, dmap->bytes_in_use); |
| 656 | } |
| 657 | |
| 658 | for (i = 0; i < dmap->nbufs; i++) |
| 659 | { |
| 660 | dmap->counts[i] = 0; |
| 661 | } |
| 662 | |
| 663 | dmap->flags |= DMA_ALLOC_DONE | DMA_EMPTY; |
| 664 | } |
| 665 | |
| 666 | static int dma_subdivide(int dev, struct dma_buffparms *dmap, int fact) |
| 667 | { |
| 668 | if (fact == 0) |
| 669 | { |
| 670 | fact = dmap->subdivision; |
| 671 | if (fact == 0) |
| 672 | fact = 1; |
| 673 | return fact; |
| 674 | } |
| 675 | if (dmap->subdivision != 0 || dmap->fragment_size) /* Too late to change */ |
| 676 | return -EINVAL; |
| 677 | |
| 678 | if (fact > MAX_REALTIME_FACTOR) |
| 679 | return -EINVAL; |
| 680 | |
| 681 | if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16) |
| 682 | return -EINVAL; |
| 683 | |
| 684 | dmap->subdivision = fact; |
| 685 | return fact; |
| 686 | } |
| 687 | |
| 688 | static int dma_set_fragment(int dev, struct dma_buffparms *dmap, int fact) |
| 689 | { |
| 690 | int bytes, count; |
| 691 | |
| 692 | if (fact == 0) |
| 693 | return -EIO; |
| 694 | |
| 695 | if (dmap->subdivision != 0 || |
| 696 | dmap->fragment_size) /* Too late to change */ |
| 697 | return -EINVAL; |
| 698 | |
| 699 | bytes = fact & 0xffff; |
| 700 | count = (fact >> 16) & 0x7fff; |
| 701 | |
| 702 | if (count == 0) |
| 703 | count = MAX_SUB_BUFFERS; |
| 704 | else if (count < MAX_SUB_BUFFERS) |
| 705 | count++; |
| 706 | |
| 707 | if (bytes < 4 || bytes > 17) /* <16 || > 512k */ |
| 708 | return -EINVAL; |
| 709 | |
| 710 | if (count < 2) |
| 711 | return -EINVAL; |
| 712 | |
| 713 | if (audio_devs[dev]->min_fragment > 0) |
| 714 | if (bytes < audio_devs[dev]->min_fragment) |
| 715 | bytes = audio_devs[dev]->min_fragment; |
| 716 | |
| 717 | if (audio_devs[dev]->max_fragment > 0) |
| 718 | if (bytes > audio_devs[dev]->max_fragment) |
| 719 | bytes = audio_devs[dev]->max_fragment; |
| 720 | |
| 721 | #ifdef OS_DMA_MINBITS |
| 722 | if (bytes < OS_DMA_MINBITS) |
| 723 | bytes = OS_DMA_MINBITS; |
| 724 | #endif |
| 725 | |
| 726 | dmap->fragment_size = (1 << bytes); |
| 727 | dmap->max_fragments = count; |
| 728 | |
| 729 | if (dmap->fragment_size > dmap->buffsize) |
| 730 | dmap->fragment_size = dmap->buffsize; |
| 731 | |
| 732 | if (dmap->fragment_size == dmap->buffsize && |
| 733 | audio_devs[dev]->flags & DMA_AUTOMODE) |
| 734 | dmap->fragment_size /= 2; /* Needs at least 2 buffers */ |
| 735 | |
| 736 | dmap->subdivision = 1; /* Disable SNDCTL_DSP_SUBDIVIDE */ |
| 737 | return bytes | ((count - 1) << 16); |
| 738 | } |
| 739 | |
| 740 | static int dma_ioctl(int dev, unsigned int cmd, void __user *arg) |
| 741 | { |
| 742 | struct dma_buffparms *dmap_out = audio_devs[dev]->dmap_out; |
| 743 | struct dma_buffparms *dmap_in = audio_devs[dev]->dmap_in; |
| 744 | struct dma_buffparms *dmap; |
| 745 | audio_buf_info info; |
| 746 | count_info cinfo; |
| 747 | int fact, ret, changed, bits, count, err; |
| 748 | unsigned long flags; |
| 749 | |
| 750 | switch (cmd) |
| 751 | { |
| 752 | case SNDCTL_DSP_SUBDIVIDE: |
| 753 | ret = 0; |
| 754 | if (get_user(fact, (int __user *)arg)) |
| 755 | return -EFAULT; |
| 756 | if (audio_devs[dev]->open_mode & OPEN_WRITE) |
| 757 | ret = dma_subdivide(dev, dmap_out, fact); |
| 758 | if (ret < 0) |
| 759 | return ret; |
| 760 | if (audio_devs[dev]->open_mode != OPEN_WRITE || |
| 761 | (audio_devs[dev]->flags & DMA_DUPLEX && |
| 762 | audio_devs[dev]->open_mode & OPEN_READ)) |
| 763 | ret = dma_subdivide(dev, dmap_in, fact); |
| 764 | if (ret < 0) |
| 765 | return ret; |
| 766 | break; |
| 767 | |
| 768 | case SNDCTL_DSP_GETISPACE: |
| 769 | case SNDCTL_DSP_GETOSPACE: |
| 770 | dmap = dmap_out; |
| 771 | if (cmd == SNDCTL_DSP_GETISPACE && !(audio_devs[dev]->open_mode & OPEN_READ)) |
| 772 | return -EINVAL; |
| 773 | if (cmd == SNDCTL_DSP_GETOSPACE && !(audio_devs[dev]->open_mode & OPEN_WRITE)) |
| 774 | return -EINVAL; |
| 775 | if (cmd == SNDCTL_DSP_GETISPACE && audio_devs[dev]->flags & DMA_DUPLEX) |
| 776 | dmap = dmap_in; |
| 777 | if (dmap->mapping_flags & DMA_MAP_MAPPED) |
| 778 | return -EINVAL; |
| 779 | if (!(dmap->flags & DMA_ALLOC_DONE)) |
| 780 | reorganize_buffers(dev, dmap, (cmd == SNDCTL_DSP_GETISPACE)); |
| 781 | info.fragstotal = dmap->nbufs; |
| 782 | if (cmd == SNDCTL_DSP_GETISPACE) |
| 783 | info.fragments = dmap->qlen; |
| 784 | else |
| 785 | { |
| 786 | if (!DMAbuf_space_in_queue(dev)) |
| 787 | info.fragments = 0; |
| 788 | else |
| 789 | { |
| 790 | info.fragments = DMAbuf_space_in_queue(dev); |
| 791 | if (audio_devs[dev]->d->local_qlen) |
| 792 | { |
| 793 | int tmp = audio_devs[dev]->d->local_qlen(dev); |
| 794 | if (tmp && info.fragments) |
| 795 | tmp--; /* |
| 796 | * This buffer has been counted twice |
| 797 | */ |
| 798 | info.fragments -= tmp; |
| 799 | } |
| 800 | } |
| 801 | } |
| 802 | if (info.fragments < 0) |
| 803 | info.fragments = 0; |
| 804 | else if (info.fragments > dmap->nbufs) |
| 805 | info.fragments = dmap->nbufs; |
| 806 | |
| 807 | info.fragsize = dmap->fragment_size; |
| 808 | info.bytes = info.fragments * dmap->fragment_size; |
| 809 | |
| 810 | if (cmd == SNDCTL_DSP_GETISPACE && dmap->qlen) |
| 811 | info.bytes -= dmap->counts[dmap->qhead]; |
| 812 | else |
| 813 | { |
| 814 | info.fragments = info.bytes / dmap->fragment_size; |
| 815 | info.bytes -= dmap->user_counter % dmap->fragment_size; |
| 816 | } |
| 817 | if (copy_to_user(arg, &info, sizeof(info))) |
| 818 | return -EFAULT; |
| 819 | return 0; |
| 820 | |
| 821 | case SNDCTL_DSP_SETTRIGGER: |
| 822 | if (get_user(bits, (int __user *)arg)) |
| 823 | return -EFAULT; |
| 824 | bits &= audio_devs[dev]->open_mode; |
| 825 | if (audio_devs[dev]->d->trigger == NULL) |
| 826 | return -EINVAL; |
| 827 | if (!(audio_devs[dev]->flags & DMA_DUPLEX) && (bits & PCM_ENABLE_INPUT) && |
| 828 | (bits & PCM_ENABLE_OUTPUT)) |
| 829 | return -EINVAL; |
| 830 | |
| 831 | if (bits & PCM_ENABLE_INPUT) |
| 832 | { |
| 833 | spin_lock_irqsave(&dmap_in->lock,flags); |
| 834 | changed = (audio_devs[dev]->enable_bits ^ bits) & PCM_ENABLE_INPUT; |
| 835 | if (changed && audio_devs[dev]->go) |
| 836 | { |
| 837 | reorganize_buffers(dev, dmap_in, 1); |
| 838 | if ((err = audio_devs[dev]->d->prepare_for_input(dev, |
| 839 | dmap_in->fragment_size, dmap_in->nbufs)) < 0) { |
| 840 | spin_unlock_irqrestore(&dmap_in->lock,flags); |
Roel Kluin | 0d26ce3 | 2009-11-12 17:43:11 +0100 | [diff] [blame] | 841 | return err; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 842 | } |
| 843 | dmap_in->dma_mode = DMODE_INPUT; |
| 844 | audio_devs[dev]->enable_bits |= PCM_ENABLE_INPUT; |
| 845 | DMAbuf_activate_recording(dev, dmap_in); |
| 846 | } else |
| 847 | audio_devs[dev]->enable_bits &= ~PCM_ENABLE_INPUT; |
| 848 | spin_unlock_irqrestore(&dmap_in->lock,flags); |
| 849 | } |
| 850 | if (bits & PCM_ENABLE_OUTPUT) |
| 851 | { |
| 852 | spin_lock_irqsave(&dmap_out->lock,flags); |
| 853 | changed = (audio_devs[dev]->enable_bits ^ bits) & PCM_ENABLE_OUTPUT; |
| 854 | if (changed && |
| 855 | (dmap_out->mapping_flags & DMA_MAP_MAPPED || dmap_out->qlen > 0) && |
| 856 | audio_devs[dev]->go) |
| 857 | { |
| 858 | if (!(dmap_out->flags & DMA_ALLOC_DONE)) |
| 859 | reorganize_buffers(dev, dmap_out, 0); |
| 860 | dmap_out->dma_mode = DMODE_OUTPUT; |
| 861 | audio_devs[dev]->enable_bits |= PCM_ENABLE_OUTPUT; |
| 862 | dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size; |
| 863 | DMAbuf_launch_output(dev, dmap_out); |
| 864 | } else |
| 865 | audio_devs[dev]->enable_bits &= ~PCM_ENABLE_OUTPUT; |
| 866 | spin_unlock_irqrestore(&dmap_out->lock,flags); |
| 867 | } |
| 868 | #if 0 |
| 869 | if (changed && audio_devs[dev]->d->trigger) |
| 870 | audio_devs[dev]->d->trigger(dev, bits * audio_devs[dev]->go); |
| 871 | #endif |
| 872 | /* Falls through... */ |
| 873 | |
| 874 | case SNDCTL_DSP_GETTRIGGER: |
| 875 | ret = audio_devs[dev]->enable_bits; |
| 876 | break; |
| 877 | |
| 878 | case SNDCTL_DSP_SETSYNCRO: |
| 879 | if (!audio_devs[dev]->d->trigger) |
| 880 | return -EINVAL; |
| 881 | audio_devs[dev]->d->trigger(dev, 0); |
| 882 | audio_devs[dev]->go = 0; |
| 883 | return 0; |
| 884 | |
| 885 | case SNDCTL_DSP_GETIPTR: |
| 886 | if (!(audio_devs[dev]->open_mode & OPEN_READ)) |
| 887 | return -EINVAL; |
| 888 | spin_lock_irqsave(&dmap_in->lock,flags); |
| 889 | cinfo.bytes = dmap_in->byte_counter; |
| 890 | cinfo.ptr = DMAbuf_get_buffer_pointer(dev, dmap_in, DMODE_INPUT) & ~3; |
| 891 | if (cinfo.ptr < dmap_in->fragment_size && dmap_in->qtail != 0) |
| 892 | cinfo.bytes += dmap_in->bytes_in_use; /* Pointer wrap not handled yet */ |
| 893 | cinfo.blocks = dmap_in->qlen; |
| 894 | cinfo.bytes += cinfo.ptr; |
| 895 | if (dmap_in->mapping_flags & DMA_MAP_MAPPED) |
| 896 | dmap_in->qlen = 0; /* Reset interrupt counter */ |
| 897 | spin_unlock_irqrestore(&dmap_in->lock,flags); |
| 898 | if (copy_to_user(arg, &cinfo, sizeof(cinfo))) |
| 899 | return -EFAULT; |
| 900 | return 0; |
| 901 | |
| 902 | case SNDCTL_DSP_GETOPTR: |
| 903 | if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) |
| 904 | return -EINVAL; |
| 905 | |
| 906 | spin_lock_irqsave(&dmap_out->lock,flags); |
| 907 | cinfo.bytes = dmap_out->byte_counter; |
| 908 | cinfo.ptr = DMAbuf_get_buffer_pointer(dev, dmap_out, DMODE_OUTPUT) & ~3; |
| 909 | if (cinfo.ptr < dmap_out->fragment_size && dmap_out->qhead != 0) |
| 910 | cinfo.bytes += dmap_out->bytes_in_use; /* Pointer wrap not handled yet */ |
| 911 | cinfo.blocks = dmap_out->qlen; |
| 912 | cinfo.bytes += cinfo.ptr; |
| 913 | if (dmap_out->mapping_flags & DMA_MAP_MAPPED) |
| 914 | dmap_out->qlen = 0; /* Reset interrupt counter */ |
| 915 | spin_unlock_irqrestore(&dmap_out->lock,flags); |
| 916 | if (copy_to_user(arg, &cinfo, sizeof(cinfo))) |
| 917 | return -EFAULT; |
| 918 | return 0; |
| 919 | |
| 920 | case SNDCTL_DSP_GETODELAY: |
| 921 | if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) |
| 922 | return -EINVAL; |
| 923 | if (!(dmap_out->flags & DMA_ALLOC_DONE)) |
| 924 | { |
| 925 | ret=0; |
| 926 | break; |
| 927 | } |
| 928 | spin_lock_irqsave(&dmap_out->lock,flags); |
| 929 | /* Compute number of bytes that have been played */ |
| 930 | count = DMAbuf_get_buffer_pointer (dev, dmap_out, DMODE_OUTPUT); |
| 931 | if (count < dmap_out->fragment_size && dmap_out->qhead != 0) |
| 932 | count += dmap_out->bytes_in_use; /* Pointer wrap not handled yet */ |
| 933 | count += dmap_out->byte_counter; |
Lucas De Marchi | 25985ed | 2011-03-30 22:57:33 -0300 | [diff] [blame] | 934 | /* Subtract current count from the number of bytes written by app */ |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 935 | count = dmap_out->user_counter - count; |
| 936 | if (count < 0) |
| 937 | count = 0; |
| 938 | spin_unlock_irqrestore(&dmap_out->lock,flags); |
| 939 | ret = count; |
| 940 | break; |
| 941 | |
| 942 | case SNDCTL_DSP_POST: |
| 943 | if (audio_devs[dev]->dmap_out->qlen > 0) |
| 944 | if (!(audio_devs[dev]->dmap_out->flags & DMA_ACTIVE)) |
| 945 | DMAbuf_launch_output(dev, audio_devs[dev]->dmap_out); |
| 946 | return 0; |
| 947 | |
| 948 | case SNDCTL_DSP_GETBLKSIZE: |
| 949 | dmap = dmap_out; |
| 950 | if (audio_devs[dev]->open_mode & OPEN_WRITE) |
| 951 | reorganize_buffers(dev, dmap_out, (audio_devs[dev]->open_mode == OPEN_READ)); |
| 952 | if (audio_devs[dev]->open_mode == OPEN_READ || |
| 953 | (audio_devs[dev]->flags & DMA_DUPLEX && |
| 954 | audio_devs[dev]->open_mode & OPEN_READ)) |
| 955 | reorganize_buffers(dev, dmap_in, (audio_devs[dev]->open_mode == OPEN_READ)); |
| 956 | if (audio_devs[dev]->open_mode == OPEN_READ) |
| 957 | dmap = dmap_in; |
| 958 | ret = dmap->fragment_size; |
| 959 | break; |
| 960 | |
| 961 | case SNDCTL_DSP_SETFRAGMENT: |
| 962 | ret = 0; |
| 963 | if (get_user(fact, (int __user *)arg)) |
| 964 | return -EFAULT; |
| 965 | if (audio_devs[dev]->open_mode & OPEN_WRITE) |
| 966 | ret = dma_set_fragment(dev, dmap_out, fact); |
| 967 | if (ret < 0) |
| 968 | return ret; |
| 969 | if (audio_devs[dev]->open_mode == OPEN_READ || |
| 970 | (audio_devs[dev]->flags & DMA_DUPLEX && |
| 971 | audio_devs[dev]->open_mode & OPEN_READ)) |
| 972 | ret = dma_set_fragment(dev, dmap_in, fact); |
| 973 | if (ret < 0) |
| 974 | return ret; |
| 975 | if (!arg) /* don't know what this is good for, but preserve old semantics */ |
| 976 | return 0; |
| 977 | break; |
| 978 | |
| 979 | default: |
| 980 | if (!audio_devs[dev]->d->ioctl) |
| 981 | return -EINVAL; |
| 982 | return audio_devs[dev]->d->ioctl(dev, cmd, arg); |
| 983 | } |
| 984 | return put_user(ret, (int __user *)arg); |
| 985 | } |