aboutsummaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /sound
downloadlinux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.gz
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'sound')
-rw-r--r--sound/Kconfig89
-rw-r--r--sound/Makefile13
-rw-r--r--sound/arm/Kconfig18
-rw-r--r--sound/arm/Makefile8
-rw-r--r--sound/arm/sa11xx-uda1341.c973
-rw-r--r--sound/core/Kconfig133
-rw-r--r--sound/core/Makefile33
-rw-r--r--sound/core/control.c1375
-rw-r--r--sound/core/control_compat.c412
-rw-r--r--sound/core/device.c240
-rw-r--r--sound/core/hwdep.c524
-rw-r--r--sound/core/hwdep_compat.c77
-rw-r--r--sound/core/info.c989
-rw-r--r--sound/core/info_oss.c137
-rw-r--r--sound/core/init.c887
-rw-r--r--sound/core/isadma.c103
-rw-r--r--sound/core/memalloc.c663
-rw-r--r--sound/core/memory.c306
-rw-r--r--sound/core/misc.c76
-rw-r--r--sound/core/oss/Makefile12
-rw-r--r--sound/core/oss/copy.c87
-rw-r--r--sound/core/oss/io.c134
-rw-r--r--sound/core/oss/linear.c158
-rw-r--r--sound/core/oss/mixer_oss.c1340
-rw-r--r--sound/core/oss/mulaw.c308
-rw-r--r--sound/core/oss/pcm_oss.c2530
-rw-r--r--sound/core/oss/pcm_plugin.c921
-rw-r--r--sound/core/oss/pcm_plugin.h250
-rw-r--r--sound/core/oss/plugin_ops.h536
-rw-r--r--sound/core/oss/rate.c378
-rw-r--r--sound/core/oss/route.c519
-rw-r--r--sound/core/pcm.c1074
-rw-r--r--sound/core/pcm_compat.c513
-rw-r--r--sound/core/pcm_lib.c2612
-rw-r--r--sound/core/pcm_memory.c363
-rw-r--r--sound/core/pcm_misc.c481
-rw-r--r--sound/core/pcm_native.c3364
-rw-r--r--sound/core/pcm_timer.c161
-rw-r--r--sound/core/rawmidi.c1680
-rw-r--r--sound/core/rawmidi_compat.c120
-rw-r--r--sound/core/rtctimer.c188
-rw-r--r--sound/core/seq/Makefile44
-rw-r--r--sound/core/seq/instr/Makefile23
-rw-r--r--sound/core/seq/instr/ainstr_fm.c156
-rw-r--r--sound/core/seq/instr/ainstr_gf1.c358
-rw-r--r--sound/core/seq/instr/ainstr_iw.c622
-rw-r--r--sound/core/seq/instr/ainstr_simple.c215
-rw-r--r--sound/core/seq/oss/Makefile10
-rw-r--r--sound/core/seq/oss/seq_oss.c317
-rw-r--r--sound/core/seq/oss/seq_oss_device.h198
-rw-r--r--sound/core/seq/oss/seq_oss_event.c447
-rw-r--r--sound/core/seq/oss/seq_oss_event.h112
-rw-r--r--sound/core/seq/oss/seq_oss_init.c555
-rw-r--r--sound/core/seq/oss/seq_oss_ioctl.c209
-rw-r--r--sound/core/seq/oss/seq_oss_midi.c710
-rw-r--r--sound/core/seq/oss/seq_oss_midi.h49
-rw-r--r--sound/core/seq/oss/seq_oss_readq.c234
-rw-r--r--sound/core/seq/oss/seq_oss_readq.h56
-rw-r--r--sound/core/seq/oss/seq_oss_rw.c216
-rw-r--r--sound/core/seq/oss/seq_oss_synth.c659
-rw-r--r--sound/core/seq/oss/seq_oss_synth.h49
-rw-r--r--sound/core/seq/oss/seq_oss_timer.c283
-rw-r--r--sound/core/seq/oss/seq_oss_timer.h70
-rw-r--r--sound/core/seq/oss/seq_oss_writeq.c170
-rw-r--r--sound/core/seq/oss/seq_oss_writeq.h50
-rw-r--r--sound/core/seq/seq.c147
-rw-r--r--sound/core/seq/seq_clientmgr.c2503
-rw-r--r--sound/core/seq/seq_clientmgr.h104
-rw-r--r--sound/core/seq/seq_compat.c137
-rw-r--r--sound/core/seq/seq_device.c575
-rw-r--r--sound/core/seq/seq_dummy.c273
-rw-r--r--sound/core/seq/seq_fifo.c264
-rw-r--r--sound/core/seq/seq_fifo.h72
-rw-r--r--sound/core/seq/seq_info.c75
-rw-r--r--sound/core/seq/seq_info.h36
-rw-r--r--sound/core/seq/seq_instr.c653
-rw-r--r--sound/core/seq/seq_lock.c48
-rw-r--r--sound/core/seq/seq_lock.h33
-rw-r--r--sound/core/seq/seq_memory.c510
-rw-r--r--sound/core/seq/seq_memory.h104
-rw-r--r--sound/core/seq/seq_midi.c489
-rw-r--r--sound/core/seq/seq_midi_emul.c735
-rw-r--r--sound/core/seq/seq_midi_event.c539
-rw-r--r--sound/core/seq/seq_ports.c674
-rw-r--r--sound/core/seq/seq_ports.h128
-rw-r--r--sound/core/seq/seq_prioq.c449
-rw-r--r--sound/core/seq/seq_prioq.h62
-rw-r--r--sound/core/seq/seq_queue.c783
-rw-r--r--sound/core/seq/seq_queue.h140
-rw-r--r--sound/core/seq/seq_system.c190
-rw-r--r--sound/core/seq/seq_system.h46
-rw-r--r--sound/core/seq/seq_timer.c435
-rw-r--r--sound/core/seq/seq_timer.h141
-rw-r--r--sound/core/seq/seq_virmidi.c551
-rw-r--r--sound/core/sgbuf.c111
-rw-r--r--sound/core/sound.c491
-rw-r--r--sound/core/sound_oss.c250
-rw-r--r--sound/core/timer.c1901
-rw-r--r--sound/core/timer_compat.c119
-rw-r--r--sound/core/wrappers.c50
-rw-r--r--sound/drivers/Kconfig98
-rw-r--r--sound/drivers/Makefile17
-rw-r--r--sound/drivers/dummy.c643
-rw-r--r--sound/drivers/mpu401/Makefile12
-rw-r--r--sound/drivers/mpu401/mpu401.c229
-rw-r--r--sound/drivers/mpu401/mpu401_uart.c541
-rw-r--r--sound/drivers/mtpav.c795
-rw-r--r--sound/drivers/opl3/Makefile22
-rw-r--r--sound/drivers/opl3/opl3_drums.c223
-rw-r--r--sound/drivers/opl3/opl3_lib.c558
-rw-r--r--sound/drivers/opl3/opl3_midi.c873
-rw-r--r--sound/drivers/opl3/opl3_oss.c356
-rw-r--r--sound/drivers/opl3/opl3_seq.c314
-rw-r--r--sound/drivers/opl3/opl3_synth.c447
-rw-r--r--sound/drivers/opl3/opl3_voice.h52
-rw-r--r--sound/drivers/opl4/Makefile18
-rw-r--r--sound/drivers/opl4/opl4_lib.c281
-rw-r--r--sound/drivers/opl4/opl4_local.h232
-rw-r--r--sound/drivers/opl4/opl4_mixer.c95
-rw-r--r--sound/drivers/opl4/opl4_proc.c166
-rw-r--r--sound/drivers/opl4/opl4_seq.c223
-rw-r--r--sound/drivers/opl4/opl4_synth.c630
-rw-r--r--sound/drivers/opl4/yrw801.c961
-rw-r--r--sound/drivers/serial-u16550.c990
-rw-r--r--sound/drivers/virmidi.c159
-rw-r--r--sound/drivers/vx/Makefile8
-rw-r--r--sound/drivers/vx/vx_cmd.c109
-rw-r--r--sound/drivers/vx/vx_cmd.h246
-rw-r--r--sound/drivers/vx/vx_core.c837
-rw-r--r--sound/drivers/vx/vx_hwdep.c249
-rw-r--r--sound/drivers/vx/vx_mixer.c1000
-rw-r--r--sound/drivers/vx/vx_pcm.c1312
-rw-r--r--sound/drivers/vx/vx_uer.c321
-rw-r--r--sound/i2c/Makefile18
-rw-r--r--sound/i2c/cs8427.c572
-rw-r--r--sound/i2c/i2c.c333
-rw-r--r--sound/i2c/l3/Makefile8
-rw-r--r--sound/i2c/l3/uda1341.c830
-rw-r--r--sound/i2c/other/Makefile16
-rw-r--r--sound/i2c/other/ak4114.c580
-rw-r--r--sound/i2c/other/ak4117.c559
-rw-r--r--sound/i2c/other/ak4xxx-adda.c501
-rw-r--r--sound/i2c/other/tea575x-tuner.c233
-rw-r--r--sound/i2c/tea6330t.c369
-rw-r--r--sound/isa/Kconfig377
-rw-r--r--sound/isa/Makefile26
-rw-r--r--sound/isa/ad1816a/Makefile10
-rw-r--r--sound/isa/ad1816a/ad1816a.c312
-rw-r--r--sound/isa/ad1816a/ad1816a_lib.c974
-rw-r--r--sound/isa/ad1848/Makefile15
-rw-r--r--sound/isa/ad1848/ad1848.c151
-rw-r--r--sound/isa/ad1848/ad1848_lib.c1279
-rw-r--r--sound/isa/als100.c336
-rw-r--r--sound/isa/azt2320.c366
-rw-r--r--sound/isa/cmi8330.c633
-rw-r--r--sound/isa/cs423x/Makefile25
-rw-r--r--sound/isa/cs423x/cs4231.c169
-rw-r--r--sound/isa/cs423x/cs4231_lib.c1964
-rw-r--r--sound/isa/cs423x/cs4232.c2
-rw-r--r--sound/isa/cs423x/cs4236.c608
-rw-r--r--sound/isa/cs423x/cs4236_lib.c970
-rw-r--r--sound/isa/dt019x.c327
-rw-r--r--sound/isa/es1688/Makefile11
-rw-r--r--sound/isa/es1688/es1688.c204
-rw-r--r--sound/isa/es1688/es1688_lib.c1062
-rw-r--r--sound/isa/es18xx.c2224
-rw-r--r--sound/isa/gus/Makefile36
-rw-r--r--sound/isa/gus/gus_dma.c244
-rw-r--r--sound/isa/gus/gus_dram.c103
-rw-r--r--sound/isa/gus/gus_instr.c173
-rw-r--r--sound/isa/gus/gus_io.c531
-rw-r--r--sound/isa/gus/gus_irq.c142
-rw-r--r--sound/isa/gus/gus_main.c514
-rw-r--r--sound/isa/gus/gus_mem.c353
-rw-r--r--sound/isa/gus/gus_mem_proc.c135
-rw-r--r--sound/isa/gus/gus_mixer.c199
-rw-r--r--sound/isa/gus/gus_pcm.c903
-rw-r--r--sound/isa/gus/gus_reset.c413
-rw-r--r--sound/isa/gus/gus_sample.c155
-rw-r--r--sound/isa/gus/gus_simple.c634
-rw-r--r--sound/isa/gus/gus_synth.c329
-rw-r--r--sound/isa/gus/gus_tables.h86
-rw-r--r--sound/isa/gus/gus_timer.c204
-rw-r--r--sound/isa/gus/gus_uart.c257
-rw-r--r--sound/isa/gus/gus_volume.c210
-rw-r--r--sound/isa/gus/gusclassic.c260
-rw-r--r--sound/isa/gus/gusextreme.c374
-rw-r--r--sound/isa/gus/gusmax.c400
-rw-r--r--sound/isa/gus/interwave-stb.c2
-rw-r--r--sound/isa/gus/interwave.c969
-rw-r--r--sound/isa/opl3sa2.c860
-rw-r--r--sound/isa/opti9xx/Makefile13
-rw-r--r--sound/isa/opti9xx/opti92x-ad1848.c2226
-rw-r--r--sound/isa/opti9xx/opti92x-cs4231.c2
-rw-r--r--sound/isa/opti9xx/opti93x.c3
-rw-r--r--sound/isa/sb/Makefile39
-rw-r--r--sound/isa/sb/emu8000.c1170
-rw-r--r--sound/isa/sb/emu8000_callback.c543
-rw-r--r--sound/isa/sb/emu8000_local.h43
-rw-r--r--sound/isa/sb/emu8000_patch.c303
-rw-r--r--sound/isa/sb/emu8000_pcm.c704
-rw-r--r--sound/isa/sb/emu8000_synth.c134
-rw-r--r--sound/isa/sb/es968.c235
-rw-r--r--sound/isa/sb/sb16.c678
-rw-r--r--sound/isa/sb/sb16_csp.c1175
-rw-r--r--sound/isa/sb/sb16_csp_codecs.h949
-rw-r--r--sound/isa/sb/sb16_main.c916
-rw-r--r--sound/isa/sb/sb8.c223
-rw-r--r--sound/isa/sb/sb8_main.c565
-rw-r--r--sound/isa/sb/sb8_midi.c293
-rw-r--r--sound/isa/sb/sb_common.c313
-rw-r--r--sound/isa/sb/sb_mixer.c844
-rw-r--r--sound/isa/sb/sbawe.c2
-rw-r--r--sound/isa/sgalaxy.c322
-rw-r--r--sound/isa/sscape.c1517
-rw-r--r--sound/isa/wavefront/Makefile9
-rw-r--r--sound/isa/wavefront/wavefront.c716
-rw-r--r--sound/isa/wavefront/wavefront_fx.c1019
-rw-r--r--sound/isa/wavefront/wavefront_midi.c570
-rw-r--r--sound/isa/wavefront/wavefront_synth.c2243
-rw-r--r--sound/last.c42
-rw-r--r--sound/mips/Kconfig15
-rw-r--r--sound/mips/Makefile8
-rw-r--r--sound/mips/au1x00.c686
-rw-r--r--sound/oss/CHANGELOG369
-rw-r--r--sound/oss/COPYING339
-rw-r--r--sound/oss/Kconfig1120
-rw-r--r--sound/oss/Makefile187
-rw-r--r--sound/oss/README.FIRST6
-rw-r--r--sound/oss/ac97.c452
-rw-r--r--sound/oss/ac97.h204
-rw-r--r--sound/oss/ac97_codec.c1576
-rw-r--r--sound/oss/ac97_plugin_ad1980.c126
-rw-r--r--sound/oss/aci.c711
-rw-r--r--sound/oss/aci.h57
-rw-r--r--sound/oss/ad1816.c1369
-rw-r--r--sound/oss/ad1848.c3159
-rw-r--r--sound/oss/ad1848.h25
-rw-r--r--sound/oss/ad1848_mixer.h253
-rw-r--r--sound/oss/ad1889.c1103
-rw-r--r--sound/oss/ad1889.h134
-rw-r--r--sound/oss/adlib_card.c73
-rw-r--r--sound/oss/aedsp16.c1381
-rw-r--r--sound/oss/ali5455.c3733
-rw-r--r--sound/oss/au1000.c2214
-rw-r--r--sound/oss/au1550_ac97.c2119
-rw-r--r--sound/oss/audio.c983
-rw-r--r--sound/oss/audio_syms.c16
-rw-r--r--sound/oss/awe_hw.h99
-rw-r--r--sound/oss/awe_wave.c6147
-rw-r--r--sound/oss/awe_wave.h77
-rw-r--r--sound/oss/bin2hex.c39
-rw-r--r--sound/oss/btaudio.c1136
-rw-r--r--sound/oss/cmpci.c3378
-rw-r--r--sound/oss/coproc.h12
-rw-r--r--sound/oss/cs4232.c520
-rw-r--r--sound/oss/cs4281/Makefile6
-rw-r--r--sound/oss/cs4281/cs4281_hwdefs.h1234
-rw-r--r--sound/oss/cs4281/cs4281_wrapper-24.c41
-rw-r--r--sound/oss/cs4281/cs4281m.c4505
-rw-r--r--sound/oss/cs4281/cs4281pm-24.c84
-rw-r--r--sound/oss/cs4281/cs4281pm.h74
-rw-r--r--sound/oss/cs461x.h1691
-rw-r--r--sound/oss/cs461x_image.h322
-rw-r--r--sound/oss/cs46xx.c5794
-rw-r--r--sound/oss/cs46xx_wrapper-24.h56
-rw-r--r--sound/oss/cs46xxpm-24.h52
-rw-r--r--sound/oss/cs46xxpm.h70
-rw-r--r--sound/oss/dev_table.c214
-rw-r--r--sound/oss/dev_table.h405
-rw-r--r--sound/oss/dm.h79
-rw-r--r--sound/oss/dmabuf.c1298
-rw-r--r--sound/oss/dmasound/Kconfig58
-rw-r--r--sound/oss/dmasound/Makefile13
-rw-r--r--sound/oss/dmasound/awacs_defs.h251
-rw-r--r--sound/oss/dmasound/dac3550a.c210
-rw-r--r--sound/oss/dmasound/dmasound.h277
-rw-r--r--sound/oss/dmasound/dmasound_atari.c1600
-rw-r--r--sound/oss/dmasound/dmasound_awacs.c3176
-rw-r--r--sound/oss/dmasound/dmasound_core.c1829
-rw-r--r--sound/oss/dmasound/dmasound_paula.c743
-rw-r--r--sound/oss/dmasound/dmasound_q40.c634
-rw-r--r--sound/oss/dmasound/tas3001c.c850
-rw-r--r--sound/oss/dmasound/tas3001c.h64
-rw-r--r--sound/oss/dmasound/tas3001c_tables.c375
-rw-r--r--sound/oss/dmasound/tas3004.c1140
-rw-r--r--sound/oss/dmasound/tas3004.h77
-rw-r--r--sound/oss/dmasound/tas3004_tables.c301
-rw-r--r--sound/oss/dmasound/tas_common.c214
-rw-r--r--sound/oss/dmasound/tas_common.h284
-rw-r--r--sound/oss/dmasound/tas_eq_prefs.h24
-rw-r--r--sound/oss/dmasound/tas_ioctl.h24
-rw-r--r--sound/oss/dmasound/trans_16.c897
-rw-r--r--sound/oss/emu10k1/8010.h737
-rw-r--r--sound/oss/emu10k1/Makefile17
-rw-r--r--sound/oss/emu10k1/audio.c1588
-rw-r--r--sound/oss/emu10k1/audio.h44
-rw-r--r--sound/oss/emu10k1/cardmi.c832
-rw-r--r--sound/oss/emu10k1/cardmi.h97
-rw-r--r--sound/oss/emu10k1/cardmo.c229
-rw-r--r--sound/oss/emu10k1/cardmo.h62
-rw-r--r--sound/oss/emu10k1/cardwi.c373
-rw-r--r--sound/oss/emu10k1/cardwi.h91
-rw-r--r--sound/oss/emu10k1/cardwo.c643
-rw-r--r--sound/oss/emu10k1/cardwo.h90
-rw-r--r--sound/oss/emu10k1/ecard.c157
-rw-r--r--sound/oss/emu10k1/ecard.h113
-rw-r--r--sound/oss/emu10k1/efxmgr.c220
-rw-r--r--sound/oss/emu10k1/efxmgr.h270
-rw-r--r--sound/oss/emu10k1/emuadxmg.c104
-rw-r--r--sound/oss/emu10k1/hwaccess.c507
-rw-r--r--sound/oss/emu10k1/hwaccess.h247
-rw-r--r--sound/oss/emu10k1/icardmid.h163
-rw-r--r--sound/oss/emu10k1/icardwav.h53
-rw-r--r--sound/oss/emu10k1/irqmgr.c113
-rw-r--r--sound/oss/emu10k1/irqmgr.h52
-rw-r--r--sound/oss/emu10k1/main.c1475
-rw-r--r--sound/oss/emu10k1/midi.c613
-rw-r--r--sound/oss/emu10k1/midi.h78
-rw-r--r--sound/oss/emu10k1/mixer.c690
-rw-r--r--sound/oss/emu10k1/passthrough.c236
-rw-r--r--sound/oss/emu10k1/passthrough.h99
-rw-r--r--sound/oss/emu10k1/recmgr.c147
-rw-r--r--sound/oss/emu10k1/recmgr.h48
-rw-r--r--sound/oss/emu10k1/timer.c176
-rw-r--r--sound/oss/emu10k1/timer.h54
-rw-r--r--sound/oss/emu10k1/voicemgr.c398
-rw-r--r--sound/oss/emu10k1/voicemgr.h103
-rw-r--r--sound/oss/es1370.c2789
-rw-r--r--sound/oss/es1371.c3097
-rw-r--r--sound/oss/esssolo1.c2497
-rw-r--r--sound/oss/forte.c2137
-rw-r--r--sound/oss/gus.h24
-rw-r--r--sound/oss/gus_card.c293
-rw-r--r--sound/oss/gus_hw.h50
-rw-r--r--sound/oss/gus_linearvol.h18
-rw-r--r--sound/oss/gus_midi.c256
-rw-r--r--sound/oss/gus_vol.c153
-rw-r--r--sound/oss/gus_wave.c3464
-rw-r--r--sound/oss/hal2.c1557
-rw-r--r--sound/oss/hal2.h248
-rw-r--r--sound/oss/harmony.c1330
-rw-r--r--sound/oss/hex2hex.c101
-rw-r--r--sound/oss/i810_audio.c3658
-rw-r--r--sound/oss/ics2101.c247
-rw-r--r--sound/oss/ite8172.c2259
-rw-r--r--sound/oss/iwmem.h36
-rw-r--r--sound/oss/kahlua.c232
-rw-r--r--sound/oss/mad16.c1097
-rw-r--r--sound/oss/maestro.c3832
-rw-r--r--sound/oss/maestro.h60
-rw-r--r--sound/oss/maestro3.c2973
-rw-r--r--sound/oss/maestro3.h821
-rw-r--r--sound/oss/maui.c478
-rw-r--r--sound/oss/midi_ctrl.h22
-rw-r--r--sound/oss/midi_syms.c29
-rw-r--r--sound/oss/midi_synth.c697
-rw-r--r--sound/oss/midi_synth.h47
-rw-r--r--sound/oss/midibuf.c431
-rw-r--r--sound/oss/mpu401.c1826
-rw-r--r--sound/oss/mpu401.h14
-rw-r--r--sound/oss/msnd.c419
-rw-r--r--sound/oss/msnd.h280
-rw-r--r--sound/oss/msnd_classic.c3
-rw-r--r--sound/oss/msnd_classic.h188
-rw-r--r--sound/oss/msnd_pinnacle.c1922
-rw-r--r--sound/oss/msnd_pinnacle.h249
-rw-r--r--sound/oss/nec_vrc5477.c2059
-rw-r--r--sound/oss/nm256.h295
-rw-r--r--sound/oss/nm256_audio.c1707
-rw-r--r--sound/oss/nm256_coeff.h4697
-rw-r--r--sound/oss/opl3.c1257
-rw-r--r--sound/oss/opl3.h5
-rw-r--r--sound/oss/opl3_hw.h246
-rw-r--r--sound/oss/opl3sa.c329
-rw-r--r--sound/oss/opl3sa2.c1129
-rw-r--r--sound/oss/os.h51
-rw-r--r--sound/oss/pas2.h17
-rw-r--r--sound/oss/pas2_card.c458
-rw-r--r--sound/oss/pas2_midi.c262
-rw-r--r--sound/oss/pas2_mixer.c336
-rw-r--r--sound/oss/pas2_pcm.c437
-rw-r--r--sound/oss/pss.c1283
-rw-r--r--sound/oss/rme96xx.c1861
-rw-r--r--sound/oss/rme96xx.h78
-rw-r--r--sound/oss/sb.h185
-rw-r--r--sound/oss/sb_audio.c1098
-rw-r--r--sound/oss/sb_card.c347
-rw-r--r--sound/oss/sb_card.h149
-rw-r--r--sound/oss/sb_common.c1291
-rw-r--r--sound/oss/sb_ess.c1832
-rw-r--r--sound/oss/sb_ess.h34
-rw-r--r--sound/oss/sb_midi.c205
-rw-r--r--sound/oss/sb_mixer.c768
-rw-r--r--sound/oss/sb_mixer.h105
-rw-r--r--sound/oss/sequencer.c1684
-rw-r--r--sound/oss/sequencer_syms.c30
-rw-r--r--sound/oss/sgalaxy.c207
-rw-r--r--sound/oss/sh_dac_audio.c325
-rw-r--r--sound/oss/skeleton.c219
-rw-r--r--sound/oss/sonicvibes.c2792
-rw-r--r--sound/oss/sound_calls.h90
-rw-r--r--sound/oss/sound_config.h154
-rw-r--r--sound/oss/sound_firmware.h2
-rw-r--r--sound/oss/sound_syms.c50
-rw-r--r--sound/oss/sound_timer.c323
-rw-r--r--sound/oss/soundcard.c751
-rw-r--r--sound/oss/soundvers.h2
-rw-r--r--sound/oss/sscape.c1485
-rw-r--r--sound/oss/swarm_cs4297a.c2742
-rw-r--r--sound/oss/sys_timer.c289
-rw-r--r--sound/oss/trident.c4628
-rw-r--r--sound/oss/trident.h358
-rw-r--r--sound/oss/trix.c525
-rw-r--r--sound/oss/tuning.h29
-rw-r--r--sound/oss/uart401.c481
-rw-r--r--sound/oss/uart6850.c362
-rw-r--r--sound/oss/ulaw.h69
-rw-r--r--sound/oss/v_midi.c291
-rw-r--r--sound/oss/v_midi.h15
-rw-r--r--sound/oss/via82cxxx_audio.c3615
-rw-r--r--sound/oss/vidc.c561
-rw-r--r--sound/oss/vidc.h67
-rw-r--r--sound/oss/vidc_fill.S218
-rw-r--r--sound/oss/vwsnd.c3486
-rw-r--r--sound/oss/waveartist.c2035
-rw-r--r--sound/oss/waveartist.h92
-rw-r--r--sound/oss/wavfront.c3538
-rw-r--r--sound/oss/wf_midi.c880
-rw-r--r--sound/oss/ymfpci.c2691
-rw-r--r--sound/oss/ymfpci.h360
-rw-r--r--sound/oss/ymfpci_image.h1565
-rw-r--r--sound/oss/yss225.c319
-rw-r--r--sound/oss/yss225.h24
-rw-r--r--sound/parisc/Kconfig15
-rw-r--r--sound/parisc/Makefile8
-rw-r--r--sound/parisc/harmony.c993
-rw-r--r--sound/parisc/harmony.h151
-rw-r--r--sound/pci/Kconfig528
-rw-r--r--sound/pci/Makefile64
-rw-r--r--sound/pci/ac97/Makefile18
-rw-r--r--sound/pci/ac97/ac97_codec.c2579
-rw-r--r--sound/pci/ac97/ac97_id.h62
-rw-r--r--sound/pci/ac97/ac97_local.h83
-rw-r--r--sound/pci/ac97/ac97_patch.c2309
-rw-r--r--sound/pci/ac97/ac97_patch.h59
-rw-r--r--sound/pci/ac97/ac97_pcm.c700
-rw-r--r--sound/pci/ac97/ac97_proc.c449
-rw-r--r--sound/pci/ac97/ak4531_codec.c437
-rw-r--r--sound/pci/ali5451/Makefile9
-rw-r--r--sound/pci/ali5451/ali5451.c2282
-rw-r--r--sound/pci/als4000.c789
-rw-r--r--sound/pci/atiixp.c1657
-rw-r--r--sound/pci/atiixp_modem.c1344
-rw-r--r--sound/pci/au88x0/Makefile7
-rw-r--r--sound/pci/au88x0/au8810.c17
-rw-r--r--sound/pci/au88x0/au8810.h229
-rw-r--r--sound/pci/au88x0/au8820.c15
-rw-r--r--sound/pci/au88x0/au8820.h209
-rw-r--r--sound/pci/au88x0/au8830.c18
-rw-r--r--sound/pci/au88x0/au8830.h256
-rw-r--r--sound/pci/au88x0/au88x0.c388
-rw-r--r--sound/pci/au88x0/au88x0.h284
-rw-r--r--sound/pci/au88x0/au88x0_a3d.c914
-rw-r--r--sound/pci/au88x0/au88x0_a3d.h123
-rw-r--r--sound/pci/au88x0/au88x0_a3ddata.c91
-rw-r--r--sound/pci/au88x0/au88x0_core.c2837
-rw-r--r--sound/pci/au88x0/au88x0_eq.c937
-rw-r--r--sound/pci/au88x0/au88x0_eq.h46
-rw-r--r--sound/pci/au88x0/au88x0_eqdata.c112
-rw-r--r--sound/pci/au88x0/au88x0_game.c135
-rw-r--r--sound/pci/au88x0/au88x0_mixer.c33
-rw-r--r--sound/pci/au88x0/au88x0_mpu401.c112
-rw-r--r--sound/pci/au88x0/au88x0_pcm.c548
-rw-r--r--sound/pci/au88x0/au88x0_sb.h40
-rw-r--r--sound/pci/au88x0/au88x0_synth.c395
-rw-r--r--sound/pci/au88x0/au88x0_wt.h65
-rw-r--r--sound/pci/au88x0/au88x0_xtalk.c787
-rw-r--r--sound/pci/au88x0/au88x0_xtalk.h61
-rw-r--r--sound/pci/azt3328.c1536
-rw-r--r--sound/pci/azt3328.h165
-rw-r--r--sound/pci/bt87x.c930
-rw-r--r--sound/pci/ca0106/Makefile3
-rw-r--r--sound/pci/ca0106/ca0106.h549
-rw-r--r--sound/pci/ca0106/ca0106_main.c1283
-rw-r--r--sound/pci/ca0106/ca0106_mixer.c634
-rw-r--r--sound/pci/ca0106/ca0106_proc.c436
-rw-r--r--sound/pci/cmipci.c2956
-rw-r--r--sound/pci/cs4281.c2136
-rw-r--r--sound/pci/cs46xx/Makefile12
-rw-r--r--sound/pci/cs46xx/cs46xx.c183
-rw-r--r--sound/pci/cs46xx/cs46xx_image.h3468
-rw-r--r--sound/pci/cs46xx/cs46xx_lib.c3922
-rw-r--r--sound/pci/cs46xx/cs46xx_lib.h182
-rw-r--r--sound/pci/cs46xx/dsp_spos.c1892
-rw-r--r--sound/pci/cs46xx/dsp_spos.h225
-rw-r--r--sound/pci/cs46xx/dsp_spos_scb_lib.c1750
-rw-r--r--sound/pci/cs46xx/imgs/cwc4630.h320
-rw-r--r--sound/pci/cs46xx/imgs/cwcasync.h176
-rw-r--r--sound/pci/cs46xx/imgs/cwcbinhack.h48
-rw-r--r--sound/pci/cs46xx/imgs/cwcdma.asp169
-rw-r--r--sound/pci/cs46xx/imgs/cwcdma.h68
-rw-r--r--sound/pci/cs46xx/imgs/cwcemb80.h1607
-rw-r--r--sound/pci/cs46xx/imgs/cwcsnoop.h46
-rw-r--r--sound/pci/emu10k1/Makefile23
-rw-r--r--sound/pci/emu10k1/emu10k1.c240
-rw-r--r--sound/pci/emu10k1/emu10k1_callback.c540
-rw-r--r--sound/pci/emu10k1/emu10k1_main.c875
-rw-r--r--sound/pci/emu10k1/emu10k1_patch.c223
-rw-r--r--sound/pci/emu10k1/emu10k1_synth.c120
-rw-r--r--sound/pci/emu10k1/emu10k1_synth_local.h38
-rw-r--r--sound/pci/emu10k1/emu10k1x.c1643
-rw-r--r--sound/pci/emu10k1/emufx.c2320
-rw-r--r--sound/pci/emu10k1/emumixer.c955
-rw-r--r--sound/pci/emu10k1/emumpu401.c374
-rw-r--r--sound/pci/emu10k1/emupcm.c1724
-rw-r--r--sound/pci/emu10k1/emuproc.c568
-rw-r--r--sound/pci/emu10k1/io.c404
-rw-r--r--sound/pci/emu10k1/irq.c189
-rw-r--r--sound/pci/emu10k1/memory.c564
-rw-r--r--sound/pci/emu10k1/p16v.c736
-rw-r--r--sound/pci/emu10k1/p16v.h299
-rw-r--r--sound/pci/emu10k1/timer.c97
-rw-r--r--sound/pci/emu10k1/voice.c152
-rw-r--r--sound/pci/ens1370.c2413
-rw-r--r--sound/pci/ens1371.c2
-rw-r--r--sound/pci/es1938.c1773
-rw-r--r--sound/pci/es1968.c2807
-rw-r--r--sound/pci/fm801.c1480
-rw-r--r--sound/pci/hda/Makefile7
-rw-r--r--sound/pci/hda/hda_codec.c1856
-rw-r--r--sound/pci/hda/hda_codec.h604
-rw-r--r--sound/pci/hda/hda_generic.c906
-rw-r--r--sound/pci/hda/hda_intel.c1449
-rw-r--r--sound/pci/hda/hda_local.h161
-rw-r--r--sound/pci/hda/hda_patch.h17
-rw-r--r--sound/pci/hda/hda_proc.c298
-rw-r--r--sound/pci/hda/patch_analog.c445
-rw-r--r--sound/pci/hda/patch_cmedia.c621
-rw-r--r--sound/pci/hda/patch_realtek.c1503
-rw-r--r--sound/pci/ice1712/Makefile12
-rw-r--r--sound/pci/ice1712/ak4xxx.c194
-rw-r--r--sound/pci/ice1712/amp.c65
-rw-r--r--sound/pci/ice1712/amp.h34
-rw-r--r--sound/pci/ice1712/aureon.c1948
-rw-r--r--sound/pci/ice1712/aureon.h56
-rw-r--r--sound/pci/ice1712/delta.c771
-rw-r--r--sound/pci/ice1712/delta.h150
-rw-r--r--sound/pci/ice1712/envy24ht.h215
-rw-r--r--sound/pci/ice1712/ews.c1036
-rw-r--r--sound/pci/ice1712/ews.h84
-rw-r--r--sound/pci/ice1712/hoontech.c326
-rw-r--r--sound/pci/ice1712/hoontech.h77
-rw-r--r--sound/pci/ice1712/ice1712.c2760
-rw-r--r--sound/pci/ice1712/ice1712.h494
-rw-r--r--sound/pci/ice1712/ice1724.c2340
-rw-r--r--sound/pci/ice1712/juli.c230
-rw-r--r--sound/pci/ice1712/juli.h10
-rw-r--r--sound/pci/ice1712/phase.c138
-rw-r--r--sound/pci/ice1712/phase.h34
-rw-r--r--sound/pci/ice1712/pontis.c849
-rw-r--r--sound/pci/ice1712/pontis.h33
-rw-r--r--sound/pci/ice1712/prodigy192.c524
-rw-r--r--sound/pci/ice1712/prodigy192.h11
-rw-r--r--sound/pci/ice1712/revo.c205
-rw-r--r--sound/pci/ice1712/revo.h48
-rw-r--r--sound/pci/ice1712/stac946x.h25
-rw-r--r--sound/pci/ice1712/vt1720_mobo.c115
-rw-r--r--sound/pci/ice1712/vt1720_mobo.h39
-rw-r--r--sound/pci/intel8x0.c2855
-rw-r--r--sound/pci/intel8x0m.c1462
-rw-r--r--sound/pci/korg1212/Makefile9
-rw-r--r--sound/pci/korg1212/korg1212-firmware.h987
-rw-r--r--sound/pci/korg1212/korg1212.c2553
-rw-r--r--sound/pci/maestro3.c2714
-rw-r--r--sound/pci/mixart/Makefile8
-rw-r--r--sound/pci/mixart/mixart.c1443
-rw-r--r--sound/pci/mixart/mixart.h242
-rw-r--r--sound/pci/mixart/mixart_core.c588
-rw-r--r--sound/pci/mixart/mixart_core.h607
-rw-r--r--sound/pci/mixart/mixart_hwdep.c647
-rw-r--r--sound/pci/mixart/mixart_hwdep.h145
-rw-r--r--sound/pci/mixart/mixart_mixer.c1136
-rw-r--r--sound/pci/mixart/mixart_mixer.h31
-rw-r--r--sound/pci/nm256/Makefile9
-rw-r--r--sound/pci/nm256/nm256.c1657
-rw-r--r--sound/pci/nm256/nm256_coef.c4607
-rw-r--r--sound/pci/rme32.c2043
-rw-r--r--sound/pci/rme96.c2449
-rw-r--r--sound/pci/rme9652/Makefile11
-rw-r--r--sound/pci/rme9652/hdsp.c5206
-rw-r--r--sound/pci/rme9652/rme9652.c2676
-rw-r--r--sound/pci/sonicvibes.c1534
-rw-r--r--sound/pci/trident/Makefile19
-rw-r--r--sound/pci/trident/trident.c196
-rw-r--r--sound/pci/trident/trident_main.c3991
-rw-r--r--sound/pci/trident/trident_memory.c476
-rw-r--r--sound/pci/trident/trident_synth.c1031
-rw-r--r--sound/pci/via82xx.c2346
-rw-r--r--sound/pci/via82xx_modem.c1245
-rw-r--r--sound/pci/vx222/Makefile8
-rw-r--r--sound/pci/vx222/vx222.c272
-rw-r--r--sound/pci/vx222/vx222.h114
-rw-r--r--sound/pci/vx222/vx222_ops.c1004
-rw-r--r--sound/pci/ymfpci/Makefile9
-rw-r--r--sound/pci/ymfpci/ymfpci.c372
-rw-r--r--sound/pci/ymfpci/ymfpci_image.h1565
-rw-r--r--sound/pci/ymfpci/ymfpci_main.c2273
-rw-r--r--sound/pcmcia/Kconfig39
-rw-r--r--sound/pcmcia/Makefile6
-rw-r--r--sound/pcmcia/pdaudiocf/Makefile8
-rw-r--r--sound/pcmcia/pdaudiocf/pdaudiocf.c404
-rw-r--r--sound/pcmcia/pdaudiocf/pdaudiocf.h145
-rw-r--r--sound/pcmcia/pdaudiocf/pdaudiocf_core.c291
-rw-r--r--sound/pcmcia/pdaudiocf/pdaudiocf_irq.c325
-rw-r--r--sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c361
-rw-r--r--sound/pcmcia/vx/Makefile11
-rw-r--r--sound/pcmcia/vx/vx_entry.c384
-rw-r--r--sound/pcmcia/vx/vxp440.c14
-rw-r--r--sound/pcmcia/vx/vxp_mixer.c148
-rw-r--r--sound/pcmcia/vx/vxp_ops.c614
-rw-r--r--sound/pcmcia/vx/vxpocket.c164
-rw-r--r--sound/pcmcia/vx/vxpocket.h118
-rw-r--r--sound/ppc/Kconfig23
-rw-r--r--sound/ppc/Makefile9
-rw-r--r--sound/ppc/awacs.c903
-rw-r--r--sound/ppc/awacs.h192
-rw-r--r--sound/ppc/beep.c262
-rw-r--r--sound/ppc/burgundy.c439
-rw-r--r--sound/ppc/burgundy.h95
-rw-r--r--sound/ppc/daca.c283
-rw-r--r--sound/ppc/keywest.c143
-rw-r--r--sound/ppc/pmac.c1328
-rw-r--r--sound/ppc/pmac.h214
-rw-r--r--sound/ppc/powermac.c159
-rw-r--r--sound/ppc/tumbler.c1175
-rw-r--r--sound/ppc/tumbler_volume.h250
-rw-r--r--sound/sound_core.c583
-rw-r--r--sound/sound_firmware.c76
-rw-r--r--sound/sparc/Kconfig18
-rw-r--r--sound/sparc/Makefile12
-rw-r--r--sound/sparc/amd7930.c1146
-rw-r--r--sound/sparc/cs4231.c2245
-rw-r--r--sound/synth/Makefile20
-rw-r--r--sound/synth/emux/Makefile20
-rw-r--r--sound/synth/emux/emux.c173
-rw-r--r--sound/synth/emux/emux_effect.c308
-rw-r--r--sound/synth/emux/emux_hwdep.c171
-rw-r--r--sound/synth/emux/emux_nrpn.c392
-rw-r--r--sound/synth/emux/emux_oss.c497
-rw-r--r--sound/synth/emux/emux_proc.c138
-rw-r--r--sound/synth/emux/emux_seq.c428
-rw-r--r--sound/synth/emux/emux_synth.c963
-rw-r--r--sound/synth/emux/emux_voice.h88
-rw-r--r--sound/synth/emux/soundfont.c1462
-rw-r--r--sound/synth/util_mem.c207
-rw-r--r--sound/usb/Kconfig32
-rw-r--r--sound/usb/Makefile12
-rw-r--r--sound/usb/usbaudio.c3337
-rw-r--r--sound/usb/usbaudio.h251
-rw-r--r--sound/usb/usbmidi.c1564
-rw-r--r--sound/usb/usbmixer.c1545
-rw-r--r--sound/usb/usbmixer_maps.c135
-rw-r--r--sound/usb/usbquirks.h1202
-rw-r--r--sound/usb/usx2y/Makefile3
-rw-r--r--sound/usb/usx2y/usX2Yhwdep.c280
-rw-r--r--sound/usb/usx2y/usX2Yhwdep.h6
-rw-r--r--sound/usb/usx2y/usbus428ctldefs.h108
-rw-r--r--sound/usb/usx2y/usbusx2y.c461
-rw-r--r--sound/usb/usx2y/usbusx2y.h84
-rw-r--r--sound/usb/usx2y/usbusx2yaudio.c1026
-rw-r--r--sound/usb/usx2y/usx2y.h51
-rw-r--r--sound/usb/usx2y/usx2yhwdeppcm.c807
-rw-r--r--sound/usb/usx2y/usx2yhwdeppcm.h21
674 files changed, 441272 insertions, 0 deletions
diff --git a/sound/Kconfig b/sound/Kconfig
new file mode 100644
index 00000000000..047d59ea057
--- /dev/null
+++ b/sound/Kconfig
@@ -0,0 +1,89 @@
+# sound/Config.in
+#
+
+menu "Sound"
+
+config SOUND
+ tristate "Sound card support"
+ help
+ If you have a sound card in your computer, i.e. if it can say more
+ than an occasional beep, say Y. Be sure to have all the information
+ about your sound card and its configuration down (I/O port,
+ interrupt and DMA channel), because you will be asked for it.
+
+ You want to read the Sound-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>. General information about
+ the modular sound system is contained in the files
+ <file:Documentation/sound/oss/Introduction>. The file
+ <file:Documentation/sound/oss/README.OSS> contains some slightly
+ outdated but still useful information as well. Newer sound
+ driver documentation is found in <file:Documentation/sound/alsa/*>.
+
+ If you have a PnP sound card and you want to configure it at boot
+ time using the ISA PnP tools (read
+ <http://www.roestock.demon.co.uk/isapnptools/>), then you need to
+ compile the sound card support as a module and load that module
+ after the PnP configuration is finished. To do this, choose M here
+ and read <file:Documentation/sound/oss/README.modules>; the module
+ will be called soundcore.
+
+ I'm told that even without a sound card, you can make your computer
+ say more than an occasional beep, by programming the PC speaker.
+ Kernel patches and supporting utilities to do that are in the pcsp
+ package, available at <ftp://ftp.infradead.org/pub/pcsp/>.
+
+source "sound/oss/dmasound/Kconfig"
+
+if !M68K
+
+menu "Advanced Linux Sound Architecture"
+ depends on SOUND!=n
+
+config SND
+ tristate "Advanced Linux Sound Architecture"
+ depends on SOUND
+
+source "sound/core/Kconfig"
+
+source "sound/drivers/Kconfig"
+
+source "sound/isa/Kconfig"
+
+source "sound/pci/Kconfig"
+
+source "sound/ppc/Kconfig"
+
+source "sound/arm/Kconfig"
+
+source "sound/mips/Kconfig"
+
+# the following will depenend on the order of config.
+# here assuming USB is defined before ALSA
+source "sound/usb/Kconfig"
+
+# the following will depenend on the order of config.
+# here assuming PCMCIA is defined before ALSA
+source "sound/pcmcia/Kconfig"
+
+source "sound/sparc/Kconfig"
+
+source "sound/parisc/Kconfig"
+
+endmenu
+
+menu "Open Sound System"
+ depends on SOUND!=n && (BROKEN || (!SPARC32 && !SPARC64))
+
+config SOUND_PRIME
+ tristate "Open Sound System (DEPRECATED)"
+ depends on SOUND
+ help
+ Say 'Y' or 'M' to enable Open Sound System drivers.
+
+source "sound/oss/Kconfig"
+
+endmenu
+
+endif
+
+endmenu
diff --git a/sound/Makefile b/sound/Makefile
new file mode 100644
index 00000000000..f352bb23596
--- /dev/null
+++ b/sound/Makefile
@@ -0,0 +1,13 @@
+# Makefile for the Linux sound card driver
+#
+
+obj-$(CONFIG_SOUND) += soundcore.o
+obj-$(CONFIG_SOUND_PRIME) += oss/
+obj-$(CONFIG_DMASOUND) += oss/
+obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/
+
+ifeq ($(CONFIG_SND),y)
+ obj-y += last.o
+endif
+
+soundcore-objs := sound_core.o sound_firmware.o
diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig
new file mode 100644
index 00000000000..cdacf4d3a38
--- /dev/null
+++ b/sound/arm/Kconfig
@@ -0,0 +1,18 @@
+# ALSA ARM drivers
+
+menu "ALSA ARM devices"
+ depends on SND!=n && ARM
+
+config SND_SA11XX_UDA1341
+ tristate "SA11xx UDA1341TS driver (iPaq H3600)"
+ depends on ARCH_SA1100 && SND && L3
+ select SND_PCM
+ help
+ Say Y here if you have a Compaq iPaq H3x00 handheld computer
+ and want to use its Philips UDA 1341 audio chip.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-sa11xx-uda1341.
+
+endmenu
+
diff --git a/sound/arm/Makefile b/sound/arm/Makefile
new file mode 100644
index 00000000000..d7e7dc0c3cd
--- /dev/null
+++ b/sound/arm/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for ALSA
+#
+
+snd-sa11xx-uda1341-objs := sa11xx-uda1341.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_SA11XX_UDA1341) += snd-sa11xx-uda1341.o
diff --git a/sound/arm/sa11xx-uda1341.c b/sound/arm/sa11xx-uda1341.c
new file mode 100644
index 00000000000..174bc032d1a
--- /dev/null
+++ b/sound/arm/sa11xx-uda1341.c
@@ -0,0 +1,973 @@
+/*
+ * Driver for Philips UDA1341TS on Compaq iPAQ H3600 soundcard
+ * Copyright (C) 2002 Tomas Kasparek <tomas.kasparek@seznam.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License.
+ *
+ * History:
+ *
+ * 2002-03-13 Tomas Kasparek initial release - based on h3600-uda1341.c from OSS
+ * 2002-03-20 Tomas Kasparek playback over ALSA is working
+ * 2002-03-28 Tomas Kasparek playback over OSS emulation is working
+ * 2002-03-29 Tomas Kasparek basic capture is working (native ALSA)
+ * 2002-03-29 Tomas Kasparek capture is working (OSS emulation)
+ * 2002-04-04 Tomas Kasparek better rates handling (allow non-standard rates)
+ * 2003-02-14 Brian Avery fixed full duplex mode, other updates
+ * 2003-02-20 Tomas Kasparek merged updates by Brian (except HAL)
+ * 2003-04-19 Jaroslav Kysela recoded DMA stuff to follow 2.4.18rmk3-hh24 kernel
+ * working suspend and resume
+ * 2003-04-28 Tomas Kasparek updated work by Jaroslav to compile it under 2.5.x again
+ * merged HAL layer (patches from Brian)
+ */
+
+/* $Id: sa11xx-uda1341.c,v 1.21 2005/01/28 19:34:04 tiwai Exp $ */
+
+/***************************************************************************************************
+*
+* To understand what Alsa Drivers should be doing look at "Writing an Alsa Driver" by Takashi Iwai
+* available in the Alsa doc section on the website
+*
+* A few notes to make things clearer. The UDA1341 is hooked up to Serial port 4 on the SA1100.
+* We are using SSP mode to talk to the UDA1341. The UDA1341 bit & wordselect clocks are generated
+* by this UART. Unfortunately, the clock only runs if the transmit buffer has something in it.
+* So, if we are just recording, we feed the transmit DMA stream a bunch of 0x0000 so that the
+* transmit buffer is full and the clock keeps going. The zeroes come from FLUSH_BASE_PHYS which
+* is a mem loc that always decodes to 0's w/ no off chip access.
+*
+* Some alsa terminology:
+* frame => num_channels * sample_size e.g stereo 16 bit is 2 * 16 = 32 bytes
+* period => the least number of bytes that will generate an interrupt e.g. we have a 1024 byte
+* buffer and 4 periods in the runtime structure this means we'll get an int every 256
+* bytes or 4 times per buffer.
+* A number of the sizes are in frames rather than bytes, use frames_to_bytes and
+* bytes_to_frames to convert. The easiest way to tell the units is to look at the
+* type i.e. runtime-> buffer_size is in frames and its type is snd_pcm_uframes_t
+*
+* Notes about the pointer fxn:
+* The pointer fxn needs to return the offset into the dma buffer in frames.
+* Interrupts must be blocked before calling the dma_get_pos fxn to avoid race with interrupts.
+*
+* Notes about pause/resume
+* Implementing this would be complicated so it's skipped. The problem case is:
+* A full duplex connection is going, then play is paused. At this point you need to start xmitting
+* 0's to keep the record active which means you cant just freeze the dma and resume it later you'd
+* need to save off the dma info, and restore it properly on a resume. Yeach!
+*
+* Notes about transfer methods:
+* The async write calls fail. I probably need to implement something else to support them?
+*
+***************************************************************************************************/
+
+#include <linux/config.h>
+#include <sound/driver.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/ioctl.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#ifdef CONFIG_PM
+#include <linux/pm.h>
+#endif
+
+#include <asm/hardware.h>
+#include <asm/arch/h3600.h>
+#include <asm/mach-types.h>
+#include <asm/dma.h>
+
+#ifdef CONFIG_H3600_HAL
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+#include <asm/arch/h3600_hal.h>
+#endif
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+
+#include <linux/l3/l3.h>
+
+#undef DEBUG_MODE
+#undef DEBUG_FUNCTION_NAMES
+#include <sound/uda1341.h>
+
+/*
+ * FIXME: Is this enough as autodetection of 2.4.X-rmkY-hhZ kernels?
+ * We use DMA stuff from 2.4.18-rmk3-hh24 here to be able to compile this
+ * module for Familiar 0.6.1
+ */
+#ifdef CONFIG_H3600_HAL
+#define HH_VERSION 1
+#endif
+
+/* {{{ Type definitions */
+
+MODULE_AUTHOR("Tomas Kasparek <tomas.kasparek@seznam.cz>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SA1100/SA1111 + UDA1341TS driver for ALSA");
+MODULE_SUPPORTED_DEVICE("{{UDA1341,iPAQ H3600 UDA1341TS}}");
+
+static char *id = NULL; /* ID for this card */
+
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for SA1100/SA1111 + UDA1341TS soundcard.");
+
+typedef struct audio_stream {
+ char *id; /* identification string */
+ int stream_id; /* numeric identification */
+ dma_device_t dma_dev; /* device identifier for DMA */
+#ifdef HH_VERSION
+ dmach_t dmach; /* dma channel identification */
+#else
+ dma_regs_t *dma_regs; /* points to our DMA registers */
+#endif
+ int active:1; /* we are using this stream for transfer now */
+ int period; /* current transfer period */
+ int periods; /* current count of periods registerd in the DMA engine */
+ int tx_spin; /* are we recoding - flag used to do DMA trans. for sync */
+ unsigned int old_offset;
+ spinlock_t dma_lock; /* for locking in DMA operations (see dma-sa1100.c in the kernel) */
+ snd_pcm_substream_t *stream;
+}audio_stream_t;
+
+typedef struct snd_card_sa11xx_uda1341 {
+ snd_card_t *card;
+ struct l3_client *uda1341;
+ snd_pcm_t *pcm;
+ long samplerate;
+ audio_stream_t s[2]; /* playback & capture */
+} sa11xx_uda1341_t;
+
+static struct snd_card_sa11xx_uda1341 *sa11xx_uda1341 = NULL;
+
+static unsigned int rates[] = {
+ 8000, 10666, 10985, 14647,
+ 16000, 21970, 22050, 24000,
+ 29400, 32000, 44100, 48000,
+};
+
+static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+};
+
+/* }}} */
+
+/* {{{ Clock and sample rate stuff */
+
+/*
+ * Stop-gap solution until rest of hh.org HAL stuff is merged.
+ */
+#define GPIO_H3600_CLK_SET0 GPIO_GPIO (12)
+#define GPIO_H3600_CLK_SET1 GPIO_GPIO (13)
+
+#ifdef CONFIG_SA1100_H3XXX
+#define clr_sa11xx_uda1341_egpio(x) clr_h3600_egpio(x)
+#define set_sa11xx_uda1341_egpio(x) set_h3600_egpio(x)
+#else
+#error This driver could serve H3x00 handhelds only!
+#endif
+
+static void sa11xx_uda1341_set_audio_clock(long val)
+{
+ switch (val) {
+ case 24000: case 32000: case 48000: /* 00: 12.288 MHz */
+ GPCR = GPIO_H3600_CLK_SET0 | GPIO_H3600_CLK_SET1;
+ break;
+
+ case 22050: case 29400: case 44100: /* 01: 11.2896 MHz */
+ GPSR = GPIO_H3600_CLK_SET0;
+ GPCR = GPIO_H3600_CLK_SET1;
+ break;
+
+ case 8000: case 10666: case 16000: /* 10: 4.096 MHz */
+ GPCR = GPIO_H3600_CLK_SET0;
+ GPSR = GPIO_H3600_CLK_SET1;
+ break;
+
+ case 10985: case 14647: case 21970: /* 11: 5.6245 MHz */
+ GPSR = GPIO_H3600_CLK_SET0 | GPIO_H3600_CLK_SET1;
+ break;
+ }
+}
+
+static void sa11xx_uda1341_set_samplerate(sa11xx_uda1341_t *sa11xx_uda1341, long rate)
+{
+ int clk_div = 0;
+ int clk=0;
+
+ /* We don't want to mess with clocks when frames are in flight */
+ Ser4SSCR0 &= ~SSCR0_SSE;
+ /* wait for any frame to complete */
+ udelay(125);
+
+ /*
+ * We have the following clock sources:
+ * 4.096 MHz, 5.6245 MHz, 11.2896 MHz, 12.288 MHz
+ * Those can be divided either by 256, 384 or 512.
+ * This makes up 12 combinations for the following samplerates...
+ */
+ if (rate >= 48000)
+ rate = 48000;
+ else if (rate >= 44100)
+ rate = 44100;
+ else if (rate >= 32000)
+ rate = 32000;
+ else if (rate >= 29400)
+ rate = 29400;
+ else if (rate >= 24000)
+ rate = 24000;
+ else if (rate >= 22050)
+ rate = 22050;
+ else if (rate >= 21970)
+ rate = 21970;
+ else if (rate >= 16000)
+ rate = 16000;
+ else if (rate >= 14647)
+ rate = 14647;
+ else if (rate >= 10985)
+ rate = 10985;
+ else if (rate >= 10666)
+ rate = 10666;
+ else
+ rate = 8000;
+
+ /* Set the external clock generator */
+#ifdef CONFIG_H3600_HAL
+ h3600_audio_clock(rate);
+#else
+ sa11xx_uda1341_set_audio_clock(rate);
+#endif
+
+ /* Select the clock divisor */
+ switch (rate) {
+ case 8000:
+ case 10985:
+ case 22050:
+ case 24000:
+ clk = F512;
+ clk_div = SSCR0_SerClkDiv(16);
+ break;
+ case 16000:
+ case 21970:
+ case 44100:
+ case 48000:
+ clk = F256;
+ clk_div = SSCR0_SerClkDiv(8);
+ break;
+ case 10666:
+ case 14647:
+ case 29400:
+ case 32000:
+ clk = F384;
+ clk_div = SSCR0_SerClkDiv(12);
+ break;
+ }
+
+ /* FMT setting should be moved away when other FMTs are added (FIXME) */
+ l3_command(sa11xx_uda1341->uda1341, CMD_FORMAT, (void *)LSB16);
+
+ l3_command(sa11xx_uda1341->uda1341, CMD_FS, (void *)clk);
+ Ser4SSCR0 = (Ser4SSCR0 & ~0xff00) + clk_div + SSCR0_SSE;
+ sa11xx_uda1341->samplerate = rate;
+}
+
+/* }}} */
+
+/* {{{ HW init and shutdown */
+
+static void sa11xx_uda1341_audio_init(sa11xx_uda1341_t *sa11xx_uda1341)
+{
+ unsigned long flags;
+
+ /* Setup DMA stuff */
+ sa11xx_uda1341->s[SNDRV_PCM_STREAM_PLAYBACK].id = "UDA1341 out";
+ sa11xx_uda1341->s[SNDRV_PCM_STREAM_PLAYBACK].stream_id = SNDRV_PCM_STREAM_PLAYBACK;
+ sa11xx_uda1341->s[SNDRV_PCM_STREAM_PLAYBACK].dma_dev = DMA_Ser4SSPWr;
+
+ sa11xx_uda1341->s[SNDRV_PCM_STREAM_CAPTURE].id = "UDA1341 in";
+ sa11xx_uda1341->s[SNDRV_PCM_STREAM_CAPTURE].stream_id = SNDRV_PCM_STREAM_CAPTURE;
+ sa11xx_uda1341->s[SNDRV_PCM_STREAM_CAPTURE].dma_dev = DMA_Ser4SSPRd;
+
+ /* Initialize the UDA1341 internal state */
+
+ /* Setup the uarts */
+ local_irq_save(flags);
+ GAFR |= (GPIO_SSP_CLK);
+ GPDR &= ~(GPIO_SSP_CLK);
+ Ser4SSCR0 = 0;
+ Ser4SSCR0 = SSCR0_DataSize(16) + SSCR0_TI + SSCR0_SerClkDiv(8);
+ Ser4SSCR1 = SSCR1_SClkIactL + SSCR1_SClk1P + SSCR1_ExtClk;
+ Ser4SSCR0 |= SSCR0_SSE;
+ local_irq_restore(flags);
+
+ /* Enable the audio power */
+#ifdef CONFIG_H3600_HAL
+ h3600_audio_power(AUDIO_RATE_DEFAULT);
+#else
+ clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_CODEC_NRESET);
+ set_sa11xx_uda1341_egpio(IPAQ_EGPIO_AUDIO_ON);
+ set_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);
+#endif
+
+ /* Wait for the UDA1341 to wake up */
+ mdelay(1); //FIXME - was removed by Perex - Why?
+
+ /* Initialize the UDA1341 internal state */
+ l3_open(sa11xx_uda1341->uda1341);
+
+ /* external clock configuration (after l3_open - regs must be initialized */
+ sa11xx_uda1341_set_samplerate(sa11xx_uda1341, sa11xx_uda1341->samplerate);
+
+ /* Wait for the UDA1341 to wake up */
+ set_sa11xx_uda1341_egpio(IPAQ_EGPIO_CODEC_NRESET);
+ mdelay(1);
+
+ /* make the left and right channels unswapped (flip the WS latch) */
+ Ser4SSDR = 0;
+
+#ifdef CONFIG_H3600_HAL
+ h3600_audio_mute(0);
+#else
+ clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);
+#endif
+}
+
+static void sa11xx_uda1341_audio_shutdown(sa11xx_uda1341_t *sa11xx_uda1341)
+{
+ /* mute on */
+#ifdef CONFIG_H3600_HAL
+ h3600_audio_mute(1);
+#else
+ set_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);
+#endif
+
+ /* disable the audio power and all signals leading to the audio chip */
+ l3_close(sa11xx_uda1341->uda1341);
+ Ser4SSCR0 = 0;
+ clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_CODEC_NRESET);
+
+ /* power off and mute off */
+ /* FIXME - is muting off necesary??? */
+#ifdef CONFIG_H3600_HAL
+ h3600_audio_power(0);
+ h3600_audio_mute(0);
+#else
+ clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_AUDIO_ON);
+ clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);
+#endif
+}
+
+/* }}} */
+
+/* {{{ DMA staff */
+
+/*
+ * these are the address and sizes used to fill the xmit buffer
+ * so we can get a clock in record only mode
+ */
+#define FORCE_CLOCK_ADDR (dma_addr_t)FLUSH_BASE_PHYS
+#define FORCE_CLOCK_SIZE 4096 // was 2048
+
+// FIXME Why this value exactly - wrote comment
+#define DMA_BUF_SIZE 8176 /* <= MAX_DMA_SIZE from asm/arch-sa1100/dma.h */
+
+#ifdef HH_VERSION
+
+static int audio_dma_request(audio_stream_t *s, void (*callback)(void *, int))
+{
+ int ret;
+
+ ret = sa1100_request_dma(&s->dmach, s->id, s->dma_dev);
+ if (ret < 0) {
+ printk(KERN_ERR "unable to grab audio dma 0x%x\n", s->dma_dev);
+ return ret;
+ }
+ sa1100_dma_set_callback(s->dmach, callback);
+ return 0;
+}
+
+static inline void audio_dma_free(audio_stream_t *s)
+{
+ sa1100_free_dma(s->dmach);
+ s->dmach = -1;
+}
+
+#else
+
+static int audio_dma_request(audio_stream_t *s, void (*callback)(void *))
+{
+ int ret;
+
+ ret = sa1100_request_dma(s->dma_dev, s->id, callback, s, &s->dma_regs);
+ if (ret < 0)
+ printk(KERN_ERR "unable to grab audio dma 0x%x\n", s->dma_dev);
+ return ret;
+}
+
+static void audio_dma_free(audio_stream_t *s)
+{
+ sa1100_free_dma((s)->dma_regs);
+ (s)->dma_regs = 0;
+}
+
+#endif
+
+static u_int audio_get_dma_pos(audio_stream_t *s)
+{
+ snd_pcm_substream_t * substream = s->stream;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ unsigned int offset;
+ unsigned long flags;
+ dma_addr_t addr;
+
+ // this must be called w/ interrupts locked out see dma-sa1100.c in the kernel
+ spin_lock_irqsave(&s->dma_lock, flags);
+#ifdef HH_VERSION
+ sa1100_dma_get_current(s->dmach, NULL, &addr);
+#else
+ addr = sa1100_get_dma_pos((s)->dma_regs);
+#endif
+ offset = addr - runtime->dma_addr;
+ spin_unlock_irqrestore(&s->dma_lock, flags);
+
+ offset = bytes_to_frames(runtime,offset);
+ if (offset >= runtime->buffer_size)
+ offset = 0;
+
+ return offset;
+}
+
+/*
+ * this stops the dma and clears the dma ptrs
+ */
+static void audio_stop_dma(audio_stream_t *s)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&s->dma_lock, flags);
+ s->active = 0;
+ s->period = 0;
+ /* this stops the dma channel and clears the buffer ptrs */
+#ifdef HH_VERSION
+ sa1100_dma_flush_all(s->dmach);
+#else
+ sa1100_clear_dma(s->dma_regs);
+#endif
+ spin_unlock_irqrestore(&s->dma_lock, flags);
+}
+
+static void audio_process_dma(audio_stream_t *s)
+{
+ snd_pcm_substream_t *substream = s->stream;
+ snd_pcm_runtime_t *runtime;
+ unsigned int dma_size;
+ unsigned int offset;
+ int ret;
+
+ /* we are requested to process synchronization DMA transfer */
+ if (s->tx_spin) {
+ snd_assert(s->stream_id == SNDRV_PCM_STREAM_PLAYBACK, return);
+ /* fill the xmit dma buffers and return */
+#ifdef HH_VERSION
+ sa1100_dma_set_spin(s->dmach, FORCE_CLOCK_ADDR, FORCE_CLOCK_SIZE);
+#else
+ while (1) {
+ ret = sa1100_start_dma(s->dma_regs, FORCE_CLOCK_ADDR, FORCE_CLOCK_SIZE);
+ if (ret)
+ return;
+ }
+#endif
+ return;
+ }
+
+ /* must be set here - only valid for running streams, not for forced_clock dma fills */
+ runtime = substream->runtime;
+ while (s->active && s->periods < runtime->periods) {
+ dma_size = frames_to_bytes(runtime, runtime->period_size);
+ if (s->old_offset) {
+ /* a little trick, we need resume from old position */
+ offset = frames_to_bytes(runtime, s->old_offset - 1);
+ s->old_offset = 0;
+ s->periods = 0;
+ s->period = offset / dma_size;
+ offset %= dma_size;
+ dma_size = dma_size - offset;
+ if (!dma_size)
+ continue; /* special case */
+ } else {
+ offset = dma_size * s->period;
+ snd_assert(dma_size <= DMA_BUF_SIZE, );
+ }
+#ifdef HH_VERSION
+ ret = sa1100_dma_queue_buffer(s->dmach, s, runtime->dma_addr + offset, dma_size);
+ if (ret)
+ return; //FIXME
+#else
+ ret = sa1100_start_dma((s)->dma_regs, runtime->dma_addr + offset, dma_size);
+ if (ret) {
+ printk(KERN_ERR "audio_process_dma: cannot queue DMA buffer (%i)\n", ret);
+ return;
+ }
+#endif
+
+ s->period++;
+ s->period %= runtime->periods;
+ s->periods++;
+ }
+}
+
+#ifdef HH_VERSION
+static void audio_dma_callback(void *data, int size)
+#else
+static void audio_dma_callback(void *data)
+#endif
+{
+ audio_stream_t *s = data;
+
+ /*
+ * If we are getting a callback for an active stream then we inform
+ * the PCM middle layer we've finished a period
+ */
+ if (s->active)
+ snd_pcm_period_elapsed(s->stream);
+
+ spin_lock(&s->dma_lock);
+ if (!s->tx_spin && s->periods > 0)
+ s->periods--;
+ audio_process_dma(s);
+ spin_unlock(&s->dma_lock);
+}
+
+/* }}} */
+
+/* {{{ PCM setting */
+
+/* {{{ trigger & timer */
+
+static int snd_sa11xx_uda1341_trigger(snd_pcm_substream_t * substream, int cmd)
+{
+ sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream);
+ int stream_id = substream->pstr->stream;
+ audio_stream_t *s = &chip->s[stream_id];
+ audio_stream_t *s1 = &chip->s[stream_id ^ 1];
+ int err = 0;
+
+ /* note local interrupts are already disabled in the midlevel code */
+ spin_lock(&s->dma_lock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ /* now we need to make sure a record only stream has a clock */
+ if (stream_id == SNDRV_PCM_STREAM_CAPTURE && !s1->active) {
+ /* we need to force fill the xmit DMA with zeros */
+ s1->tx_spin = 1;
+ audio_process_dma(s1);
+ }
+ /* this case is when you were recording then you turn on a
+ * playback stream so we stop (also clears it) the dma first,
+ * clear the sync flag and then we let it turned on
+ */
+ else {
+ s->tx_spin = 0;
+ }
+
+ /* requested stream startup */
+ s->active = 1;
+ audio_process_dma(s);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ /* requested stream shutdown */
+ audio_stop_dma(s);
+
+ /*
+ * now we need to make sure a record only stream has a clock
+ * so if we're stopping a playback with an active capture
+ * we need to turn the 0 fill dma on for the xmit side
+ */
+ if (stream_id == SNDRV_PCM_STREAM_PLAYBACK && s1->active) {
+ /* we need to force fill the xmit DMA with zeros */
+ s->tx_spin = 1;
+ audio_process_dma(s);
+ }
+ /*
+ * we killed a capture only stream, so we should also kill
+ * the zero fill transmit
+ */
+ else {
+ if (s1->tx_spin) {
+ s1->tx_spin = 0;
+ audio_stop_dma(s1);
+ }
+ }
+
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ s->active = 0;
+#ifdef HH_VERSION
+ sa1100_dma_stop(s->dmach);
+#else
+ //FIXME - DMA API
+#endif
+ s->old_offset = audio_get_dma_pos(s) + 1;
+#ifdef HH_VERSION
+ sa1100_dma_flush_all(s->dmach);
+#else
+ //FIXME - DMA API
+#endif
+ s->periods = 0;
+ break;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ s->active = 1;
+ s->tx_spin = 0;
+ audio_process_dma(s);
+ if (stream_id == SNDRV_PCM_STREAM_CAPTURE && !s1->active) {
+ s1->tx_spin = 1;
+ audio_process_dma(s1);
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+#ifdef HH_VERSION
+ sa1100_dma_stop(s->dmach);
+#else
+ //FIXME - DMA API
+#endif
+ s->active = 0;
+ if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (s1->active) {
+ s->tx_spin = 1;
+ s->old_offset = audio_get_dma_pos(s) + 1;
+#ifdef HH_VERSION
+ sa1100_dma_flush_all(s->dmach);
+#else
+ //FIXME - DMA API
+#endif
+ audio_process_dma(s);
+ }
+ } else {
+ if (s1->tx_spin) {
+ s1->tx_spin = 0;
+#ifdef HH_VERSION
+ sa1100_dma_flush_all(s1->dmach);
+#else
+ //FIXME - DMA API
+#endif
+ }
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ s->active = 1;
+ if (s->old_offset) {
+ s->tx_spin = 0;
+ audio_process_dma(s);
+ break;
+ }
+ if (stream_id == SNDRV_PCM_STREAM_CAPTURE && !s1->active) {
+ s1->tx_spin = 1;
+ audio_process_dma(s1);
+ }
+#ifdef HH_VERSION
+ sa1100_dma_resume(s->dmach);
+#else
+ //FIXME - DMA API
+#endif
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+ spin_unlock(&s->dma_lock);
+ return err;
+}
+
+static int snd_sa11xx_uda1341_prepare(snd_pcm_substream_t * substream)
+{
+ sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ audio_stream_t *s = &chip->s[substream->pstr->stream];
+
+ /* set requested samplerate */
+ sa11xx_uda1341_set_samplerate(chip, runtime->rate);
+
+ /* set requestd format when available */
+ /* set FMT here !!! FIXME */
+
+ s->period = 0;
+ s->periods = 0;
+
+ return 0;
+}
+
+static snd_pcm_uframes_t snd_sa11xx_uda1341_pointer(snd_pcm_substream_t * substream)
+{
+ sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream);
+ return audio_get_dma_pos(&chip->s[substream->pstr->stream]);
+}
+
+/* }}} */
+
+static snd_pcm_hardware_t snd_sa11xx_uda1341_capture =
+{
+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |\
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_KNOT),
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 64*1024,
+ .period_bytes_min = 64,
+ .period_bytes_max = DMA_BUF_SIZE,
+ .periods_min = 2,
+ .periods_max = 255,
+ .fifo_size = 0,
+};
+
+static snd_pcm_hardware_t snd_sa11xx_uda1341_playback =
+{
+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |\
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_KNOT),
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 64*1024,
+ .period_bytes_min = 64,
+ .period_bytes_max = DMA_BUF_SIZE,
+ .periods_min = 2,
+ .periods_max = 255,
+ .fifo_size = 0,
+};
+
+static int snd_card_sa11xx_uda1341_open(snd_pcm_substream_t * substream)
+{
+ sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int stream_id = substream->pstr->stream;
+ int err;
+
+ chip->s[stream_id].stream = substream;
+
+ if (stream_id == SNDRV_PCM_STREAM_PLAYBACK)
+ runtime->hw = snd_sa11xx_uda1341_playback;
+ else
+ runtime->hw = snd_sa11xx_uda1341_capture;
+ if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+ return err;
+ if ((err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates)) < 0)
+ return err;
+
+ return 0;
+}
+
+static int snd_card_sa11xx_uda1341_close(snd_pcm_substream_t * substream)
+{
+ sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream);
+
+ chip->s[substream->pstr->stream].stream = NULL;
+ return 0;
+}
+
+/* {{{ HW params & free */
+
+static int snd_sa11xx_uda1341_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+
+ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_sa11xx_uda1341_hw_free(snd_pcm_substream_t * substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+/* }}} */
+
+static snd_pcm_ops_t snd_card_sa11xx_uda1341_playback_ops = {
+ .open = snd_card_sa11xx_uda1341_open,
+ .close = snd_card_sa11xx_uda1341_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_sa11xx_uda1341_hw_params,
+ .hw_free = snd_sa11xx_uda1341_hw_free,
+ .prepare = snd_sa11xx_uda1341_prepare,
+ .trigger = snd_sa11xx_uda1341_trigger,
+ .pointer = snd_sa11xx_uda1341_pointer,
+};
+
+static snd_pcm_ops_t snd_card_sa11xx_uda1341_capture_ops = {
+ .open = snd_card_sa11xx_uda1341_open,
+ .close = snd_card_sa11xx_uda1341_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_sa11xx_uda1341_hw_params,
+ .hw_free = snd_sa11xx_uda1341_hw_free,
+ .prepare = snd_sa11xx_uda1341_prepare,
+ .trigger = snd_sa11xx_uda1341_trigger,
+ .pointer = snd_sa11xx_uda1341_pointer,
+};
+
+static int __init snd_card_sa11xx_uda1341_pcm(sa11xx_uda1341_t *sa11xx_uda1341, int device)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ if ((err = snd_pcm_new(sa11xx_uda1341->card, "UDA1341 PCM", device, 1, 1, &pcm)) < 0)
+ return err;
+
+ /*
+ * this sets up our initial buffers and sets the dma_type to isa.
+ * isa works but I'm not sure why (or if) it's the right choice
+ * this may be too large, trying it for now
+ */
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_ISA,
+ snd_pcm_dma_flags(0),
+ 64*1024, 64*1024);
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_sa11xx_uda1341_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_sa11xx_uda1341_capture_ops);
+ pcm->private_data = sa11xx_uda1341;
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "UDA1341 PCM");
+
+ sa11xx_uda1341_audio_init(sa11xx_uda1341);
+
+ /* setup DMA controller */
+ audio_dma_request(&sa11xx_uda1341->s[SNDRV_PCM_STREAM_PLAYBACK], audio_dma_callback);
+ audio_dma_request(&sa11xx_uda1341->s[SNDRV_PCM_STREAM_CAPTURE], audio_dma_callback);
+
+ sa11xx_uda1341->pcm = pcm;
+
+ return 0;
+}
+
+/* }}} */
+
+/* {{{ module init & exit */
+
+#ifdef CONFIG_PM
+
+static int snd_sa11xx_uda1341_suspend(snd_card_t *card, pm_message_t state)
+{
+ sa11xx_uda1341_t *chip = card->pm_private_data;
+
+ snd_pcm_suspend_all(chip->pcm);
+#ifdef HH_VERSION
+ sa1100_dma_sleep(chip->s[SNDRV_PCM_STREAM_PLAYBACK].dmach);
+ sa1100_dma_sleep(chip->s[SNDRV_PCM_STREAM_CAPTURE].dmach);
+#else
+ //FIXME
+#endif
+ l3_command(chip->uda1341, CMD_SUSPEND, NULL);
+ sa11xx_uda1341_audio_shutdown(chip);
+ return 0;
+}
+
+static int snd_sa11xx_uda1341_resume(snd_card_t *card)
+{
+ sa11xx_uda1341_t *chip = card->pm_private_data;
+
+ sa11xx_uda1341_audio_init(chip);
+ l3_command(chip->uda1341, CMD_RESUME, NULL);
+#ifdef HH_VERSION
+ sa1100_dma_wakeup(chip->s[SNDRV_PCM_STREAM_PLAYBACK].dmach);
+ sa1100_dma_wakeup(chip->s[SNDRV_PCM_STREAM_CAPTURE].dmach);
+#else
+ //FIXME
+#endif
+ return 0;
+}
+#endif /* COMFIG_PM */
+
+void snd_sa11xx_uda1341_free(snd_card_t *card)
+{
+ sa11xx_uda1341_t *chip = card->private_data;
+
+ audio_dma_free(&chip->s[SNDRV_PCM_STREAM_PLAYBACK]);
+ audio_dma_free(&chip->s[SNDRV_PCM_STREAM_CAPTURE]);
+ sa11xx_uda1341 = NULL;
+ card->private_data = NULL;
+ kfree(chip);
+}
+
+static int __init sa11xx_uda1341_init(void)
+{
+ int err;
+ snd_card_t *card;
+
+ if (!machine_is_h3xxx())
+ return -ENODEV;
+
+ /* register the soundcard */
+ card = snd_card_new(-1, id, THIS_MODULE, sizeof(sa11xx_uda1341_t));
+ if (card == NULL)
+ return -ENOMEM;
+
+ sa11xx_uda1341 = kcalloc(1, sizeof(*sa11xx_uda1341), GFP_KERNEL);
+ if (sa11xx_uda1341 == NULL)
+ return -ENOMEM;
+ spin_lock_init(&chip->s[0].dma_lock);
+ spin_lock_init(&chip->s[1].dma_lock);
+
+ card->private_data = (void *)sa11xx_uda1341;
+ card->private_free = snd_sa11xx_uda1341_free;
+
+ sa11xx_uda1341->card = card;
+ sa11xx_uda1341->samplerate = AUDIO_RATE_DEFAULT;
+
+ // mixer
+ if ((err = snd_chip_uda1341_mixer_new(sa11xx_uda1341->card, &sa11xx_uda1341->uda1341)))
+ goto nodev;
+
+ // PCM
+ if ((err = snd_card_sa11xx_uda1341_pcm(sa11xx_uda1341, 0)) < 0)
+ goto nodev;
+
+ snd_card_set_generic_pm_callback(card,
+ snd_sa11xx_uda1341_suspend, snd_sa11_uda1341_resume,
+ sa11xx_uda1341);
+
+ strcpy(card->driver, "UDA1341");
+ strcpy(card->shortname, "H3600 UDA1341TS");
+ sprintf(card->longname, "Compaq iPAQ H3600 with Philips UDA1341TS");
+
+ if ((err = snd_card_register(card)) == 0) {
+ printk( KERN_INFO "iPAQ audio support initialized\n" );
+ return 0;
+ }
+
+ nodev:
+ snd_card_free(card);
+ return err;
+}
+
+static void __exit sa11xx_uda1341_exit(void)
+{
+ snd_card_free(sa11xx_uda1341->card);
+}
+
+module_init(sa11xx_uda1341_init);
+module_exit(sa11xx_uda1341_exit);
+
+/* }}} */
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/sound/core/Kconfig b/sound/core/Kconfig
new file mode 100644
index 00000000000..d1e800b9866
--- /dev/null
+++ b/sound/core/Kconfig
@@ -0,0 +1,133 @@
+# ALSA soundcard-configuration
+config SND_TIMER
+ tristate
+ depends on SND
+
+config SND_PCM
+ tristate
+ select SND_TIMER
+ depends on SND
+
+config SND_HWDEP
+ tristate
+ depends on SND
+
+config SND_RAWMIDI
+ tristate
+ depends on SND
+
+config SND_SEQUENCER
+ tristate "Sequencer support"
+ depends on SND
+ select SND_TIMER
+ help
+ Say Y or M to enable MIDI sequencer and router support. This
+ feature allows routing and enqueueing of MIDI events. Events
+ can be processed at a given time.
+
+ Many programs require this feature, so you should enable it
+ unless you know what you're doing.
+
+config SND_SEQ_DUMMY
+ tristate "Sequencer dummy client"
+ depends on SND_SEQUENCER
+ help
+ Say Y here to enable the dummy sequencer client. This client
+ is a simple MIDI-through client: all normal input events are
+ redirected to the output port immediately.
+
+ You don't need this unless you want to connect many MIDI
+ devices or applications together.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-seq-dummy.
+
+config SND_OSSEMUL
+ bool
+ depends on SND
+
+config SND_MIXER_OSS
+ tristate "OSS Mixer API"
+ depends on SND
+ select SND_OSSEMUL
+ help
+ To enable OSS mixer API emulation (/dev/mixer*), say Y here
+ and read <file:Documentation/sound/alsa/OSS-Emulation.txt>.
+
+ Many programs still use the OSS API, so say Y.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-mixer-oss.
+
+config SND_PCM_OSS
+ tristate "OSS PCM (digital audio) API"
+ depends on SND
+ select SND_OSSEMUL
+ select SND_PCM
+ help
+ To enable OSS digital audio (PCM) emulation (/dev/dsp*), say Y
+ here and read <file:Documentation/sound/alsa/OSS-Emulation.txt>.
+
+ Many programs still use the OSS API, so say Y.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-pcm-oss.
+
+config SND_SEQUENCER_OSS
+ bool "OSS Sequencer API"
+ depends on SND && SND_SEQUENCER
+ select SND_OSSEMUL
+ help
+ Say Y here to enable OSS sequencer emulation (both
+ /dev/sequencer and /dev/music interfaces).
+
+ Many programs still use the OSS API, so say Y.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-seq-oss.
+
+config SND_RTCTIMER
+ tristate "RTC Timer support"
+ depends on SND && RTC
+ select SND_TIMER
+ help
+ Say Y here to enable RTC timer support for ALSA. ALSA uses
+ the RTC timer as a precise timing source and maps the RTC
+ timer to ALSA's timer interface. The ALSA sequencer code also
+ can use this timing source.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-rtctimer.
+
+config SND_VERBOSE_PRINTK
+ bool "Verbose printk"
+ depends on SND
+ help
+ Say Y here to enable verbose log messages. These messages
+ will help to identify source file and position containing
+ printed messages.
+
+ You don't need this unless you're debugging ALSA.
+
+config SND_DEBUG
+ bool "Debug"
+ depends on SND
+ help
+ Say Y here to enable ALSA debug code.
+
+config SND_DEBUG_MEMORY
+ bool "Debug memory"
+ depends on SND_DEBUG
+ help
+ Say Y here to enable debugging of memory allocations.
+
+config SND_DEBUG_DETECT
+ bool "Debug detection"
+ depends on SND_DEBUG
+ help
+ Say Y here to enable extra-verbose log messages printed when
+ detecting devices.
+
+config SND_GENERIC_PM
+ bool
+ depends on SND
diff --git a/sound/core/Makefile b/sound/core/Makefile
new file mode 100644
index 00000000000..764ac184b22
--- /dev/null
+++ b/sound/core/Makefile
@@ -0,0 +1,33 @@
+#
+# Makefile for ALSA
+# Copyright (c) 1999,2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-objs := sound.o init.o memory.o info.o control.o misc.o \
+ device.o wrappers.o
+ifeq ($(CONFIG_ISA),y)
+snd-objs += isadma.o
+endif
+ifeq ($(CONFIG_SND_OSSEMUL),y)
+snd-objs += sound_oss.o info_oss.o
+endif
+
+snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \
+ pcm_memory.o
+
+snd-page-alloc-objs := memalloc.o sgbuf.o
+
+snd-rawmidi-objs := rawmidi.o
+snd-timer-objs := timer.o
+snd-rtctimer-objs := rtctimer.o
+snd-hwdep-objs := hwdep.o
+
+obj-$(CONFIG_SND) += snd.o
+obj-$(CONFIG_SND_HWDEP) += snd-hwdep.o
+obj-$(CONFIG_SND_TIMER) += snd-timer.o
+obj-$(CONFIG_SND_RTCTIMER) += snd-rtctimer.o
+obj-$(CONFIG_SND_PCM) += snd-pcm.o snd-page-alloc.o
+obj-$(CONFIG_SND_RAWMIDI) += snd-rawmidi.o
+
+obj-$(CONFIG_SND_OSSEMUL) += oss/
+obj-$(CONFIG_SND_SEQUENCER) += seq/
diff --git a/sound/core/control.c b/sound/core/control.c
new file mode 100644
index 00000000000..f4ea6bff1dd
--- /dev/null
+++ b/sound/core/control.c
@@ -0,0 +1,1375 @@
+/*
+ * Routines for driver control interface
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/threads.h>
+#include <linux/interrupt.h>
+#include <linux/smp_lock.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/minors.h>
+#include <sound/info.h>
+#include <sound/control.h>
+
+/* max number of user-defined controls */
+#define MAX_USER_CONTROLS 32
+
+typedef struct _snd_kctl_ioctl {
+ struct list_head list; /* list of all ioctls */
+ snd_kctl_ioctl_func_t fioctl;
+} snd_kctl_ioctl_t;
+
+#define snd_kctl_ioctl(n) list_entry(n, snd_kctl_ioctl_t, list)
+
+static DECLARE_RWSEM(snd_ioctl_rwsem);
+static LIST_HEAD(snd_control_ioctls);
+#ifdef CONFIG_COMPAT
+static LIST_HEAD(snd_control_compat_ioctls);
+#endif
+
+static int snd_ctl_open(struct inode *inode, struct file *file)
+{
+ int cardnum = SNDRV_MINOR_CARD(iminor(inode));
+ unsigned long flags;
+ snd_card_t *card;
+ snd_ctl_file_t *ctl;
+ int err;
+
+ card = snd_cards[cardnum];
+ if (!card) {
+ err = -ENODEV;
+ goto __error1;
+ }
+ err = snd_card_file_add(card, file);
+ if (err < 0) {
+ err = -ENODEV;
+ goto __error1;
+ }
+ if (!try_module_get(card->module)) {
+ err = -EFAULT;
+ goto __error2;
+ }
+ ctl = kcalloc(1, sizeof(*ctl), GFP_KERNEL);
+ if (ctl == NULL) {
+ err = -ENOMEM;
+ goto __error;
+ }
+ INIT_LIST_HEAD(&ctl->events);
+ init_waitqueue_head(&ctl->change_sleep);
+ spin_lock_init(&ctl->read_lock);
+ ctl->card = card;
+ ctl->pid = current->pid;
+ file->private_data = ctl;
+ write_lock_irqsave(&card->ctl_files_rwlock, flags);
+ list_add_tail(&ctl->list, &card->ctl_files);
+ write_unlock_irqrestore(&card->ctl_files_rwlock, flags);
+ return 0;
+
+ __error:
+ module_put(card->module);
+ __error2:
+ snd_card_file_remove(card, file);
+ __error1:
+ return err;
+}
+
+static void snd_ctl_empty_read_queue(snd_ctl_file_t * ctl)
+{
+ snd_kctl_event_t *cread;
+
+ spin_lock(&ctl->read_lock);
+ while (!list_empty(&ctl->events)) {
+ cread = snd_kctl_event(ctl->events.next);
+ list_del(&cread->list);
+ kfree(cread);
+ }
+ spin_unlock(&ctl->read_lock);
+}
+
+static int snd_ctl_release(struct inode *inode, struct file *file)
+{
+ unsigned long flags;
+ struct list_head *list;
+ snd_card_t *card;
+ snd_ctl_file_t *ctl;
+ snd_kcontrol_t *control;
+ unsigned int idx;
+
+ ctl = file->private_data;
+ fasync_helper(-1, file, 0, &ctl->fasync);
+ file->private_data = NULL;
+ card = ctl->card;
+ write_lock_irqsave(&card->ctl_files_rwlock, flags);
+ list_del(&ctl->list);
+ write_unlock_irqrestore(&card->ctl_files_rwlock, flags);
+ down_write(&card->controls_rwsem);
+ list_for_each(list, &card->controls) {
+ control = snd_kcontrol(list);
+ for (idx = 0; idx < control->count; idx++)
+ if (control->vd[idx].owner == ctl)
+ control->vd[idx].owner = NULL;
+ }
+ up_write(&card->controls_rwsem);
+ snd_ctl_empty_read_queue(ctl);
+ kfree(ctl);
+ module_put(card->module);
+ snd_card_file_remove(card, file);
+ return 0;
+}
+
+void snd_ctl_notify(snd_card_t *card, unsigned int mask, snd_ctl_elem_id_t *id)
+{
+ unsigned long flags;
+ struct list_head *flist;
+ snd_ctl_file_t *ctl;
+ snd_kctl_event_t *ev;
+
+ snd_runtime_check(card != NULL && id != NULL, return);
+ read_lock(&card->ctl_files_rwlock);
+#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
+ card->mixer_oss_change_count++;
+#endif
+ list_for_each(flist, &card->ctl_files) {
+ struct list_head *elist;
+ ctl = snd_ctl_file(flist);
+ if (!ctl->subscribed)
+ continue;
+ spin_lock_irqsave(&ctl->read_lock, flags);
+ list_for_each(elist, &ctl->events) {
+ ev = snd_kctl_event(elist);
+ if (ev->id.numid == id->numid) {
+ ev->mask |= mask;
+ goto _found;
+ }
+ }
+ ev = kcalloc(1, sizeof(*ev), GFP_ATOMIC);
+ if (ev) {
+ ev->id = *id;
+ ev->mask = mask;
+ list_add_tail(&ev->list, &ctl->events);
+ } else {
+ snd_printk(KERN_ERR "No memory available to allocate event\n");
+ }
+ _found:
+ wake_up(&ctl->change_sleep);
+ spin_unlock_irqrestore(&ctl->read_lock, flags);
+ kill_fasync(&ctl->fasync, SIGIO, POLL_IN);
+ }
+ read_unlock(&card->ctl_files_rwlock);
+}
+
+/**
+ * snd_ctl_new - create a control instance from the template
+ * @control: the control template
+ * @access: the default control access
+ *
+ * Allocates a new snd_kcontrol_t instance and copies the given template
+ * to the new instance. It does not copy volatile data (access).
+ *
+ * Returns the pointer of the new instance, or NULL on failure.
+ */
+snd_kcontrol_t *snd_ctl_new(snd_kcontrol_t * control, unsigned int access)
+{
+ snd_kcontrol_t *kctl;
+ unsigned int idx;
+
+ snd_runtime_check(control != NULL, return NULL);
+ snd_runtime_check(control->count > 0, return NULL);
+ kctl = kcalloc(1, sizeof(*kctl) + sizeof(snd_kcontrol_volatile_t) * control->count, GFP_KERNEL);
+ if (kctl == NULL)
+ return NULL;
+ *kctl = *control;
+ for (idx = 0; idx < kctl->count; idx++)
+ kctl->vd[idx].access = access;
+ return kctl;
+}
+
+/**
+ * snd_ctl_new1 - create a control instance from the template
+ * @ncontrol: the initialization record
+ * @private_data: the private data to set
+ *
+ * Allocates a new snd_kcontrol_t instance and initialize from the given
+ * template. When the access field of ncontrol is 0, it's assumed as
+ * READWRITE access. When the count field is 0, it's assumes as one.
+ *
+ * Returns the pointer of the newly generated instance, or NULL on failure.
+ */
+snd_kcontrol_t *snd_ctl_new1(snd_kcontrol_new_t * ncontrol, void *private_data)
+{
+ snd_kcontrol_t kctl;
+ unsigned int access;
+
+ snd_runtime_check(ncontrol != NULL, return NULL);
+ snd_assert(ncontrol->info != NULL, return NULL);
+ memset(&kctl, 0, sizeof(kctl));
+ kctl.id.iface = ncontrol->iface;
+ kctl.id.device = ncontrol->device;
+ kctl.id.subdevice = ncontrol->subdevice;
+ if (ncontrol->name)
+ strlcpy(kctl.id.name, ncontrol->name, sizeof(kctl.id.name));
+ kctl.id.index = ncontrol->index;
+ kctl.count = ncontrol->count ? ncontrol->count : 1;
+ access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
+ (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|SNDRV_CTL_ELEM_ACCESS_INACTIVE|
+ SNDRV_CTL_ELEM_ACCESS_DINDIRECT|SNDRV_CTL_ELEM_ACCESS_INDIRECT));
+ kctl.info = ncontrol->info;
+ kctl.get = ncontrol->get;
+ kctl.put = ncontrol->put;
+ kctl.private_value = ncontrol->private_value;
+ kctl.private_data = private_data;
+ return snd_ctl_new(&kctl, access);
+}
+
+/**
+ * snd_ctl_free_one - release the control instance
+ * @kcontrol: the control instance
+ *
+ * Releases the control instance created via snd_ctl_new()
+ * or snd_ctl_new1().
+ * Don't call this after the control was added to the card.
+ */
+void snd_ctl_free_one(snd_kcontrol_t * kcontrol)
+{
+ if (kcontrol) {
+ if (kcontrol->private_free)
+ kcontrol->private_free(kcontrol);
+ kfree(kcontrol);
+ }
+}
+
+static unsigned int snd_ctl_hole_check(snd_card_t * card,
+ unsigned int count)
+{
+ struct list_head *list;
+ snd_kcontrol_t *kctl;
+
+ list_for_each(list, &card->controls) {
+ kctl = snd_kcontrol(list);
+ if ((kctl->id.numid <= card->last_numid &&
+ kctl->id.numid + kctl->count > card->last_numid) ||
+ (kctl->id.numid <= card->last_numid + count - 1 &&
+ kctl->id.numid + kctl->count > card->last_numid + count - 1))
+ return card->last_numid = kctl->id.numid + kctl->count - 1;
+ }
+ return card->last_numid;
+}
+
+static int snd_ctl_find_hole(snd_card_t * card, unsigned int count)
+{
+ unsigned int last_numid, iter = 100000;
+
+ last_numid = card->last_numid;
+ while (last_numid != snd_ctl_hole_check(card, count)) {
+ if (--iter == 0) {
+ /* this situation is very unlikely */
+ snd_printk(KERN_ERR "unable to allocate new control numid\n");
+ return -ENOMEM;
+ }
+ last_numid = card->last_numid;
+ }
+ return 0;
+}
+
+/**
+ * snd_ctl_add - add the control instance to the card
+ * @card: the card instance
+ * @kcontrol: the control instance to add
+ *
+ * Adds the control instance created via snd_ctl_new() or
+ * snd_ctl_new1() to the given card. Assigns also an unique
+ * numid used for fast search.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ *
+ * It frees automatically the control which cannot be added.
+ */
+int snd_ctl_add(snd_card_t * card, snd_kcontrol_t * kcontrol)
+{
+ snd_ctl_elem_id_t id;
+ unsigned int idx;
+
+ snd_runtime_check(card != NULL && kcontrol != NULL, return -EINVAL);
+ snd_assert(kcontrol->info != NULL, return -EINVAL);
+ id = kcontrol->id;
+ down_write(&card->controls_rwsem);
+ if (snd_ctl_find_id(card, &id)) {
+ up_write(&card->controls_rwsem);
+ snd_ctl_free_one(kcontrol);
+ snd_printd(KERN_ERR "control %i:%i:%i:%s:%i is already present\n",
+ id.iface,
+ id.device,
+ id.subdevice,
+ id.name,
+ id.index);
+ return -EBUSY;
+ }
+ if (snd_ctl_find_hole(card, kcontrol->count) < 0) {
+ up_write(&card->controls_rwsem);
+ snd_ctl_free_one(kcontrol);
+ return -ENOMEM;
+ }
+ list_add_tail(&kcontrol->list, &card->controls);
+ card->controls_count += kcontrol->count;
+ kcontrol->id.numid = card->last_numid + 1;
+ card->last_numid += kcontrol->count;
+ up_write(&card->controls_rwsem);
+ for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id);
+ return 0;
+}
+
+/**
+ * snd_ctl_remove - remove the control from the card and release it
+ * @card: the card instance
+ * @kcontrol: the control instance to remove
+ *
+ * Removes the control from the card and then releases the instance.
+ * You don't need to call snd_ctl_free_one(). You must be in
+ * the write lock - down_write(&card->controls_rwsem).
+ *
+ * Returns 0 if successful, or a negative error code on failure.
+ */
+int snd_ctl_remove(snd_card_t * card, snd_kcontrol_t * kcontrol)
+{
+ snd_ctl_elem_id_t id;
+ unsigned int idx;
+
+ snd_runtime_check(card != NULL && kcontrol != NULL, return -EINVAL);
+ list_del(&kcontrol->list);
+ card->controls_count -= kcontrol->count;
+ id = kcontrol->id;
+ for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_REMOVE, &id);
+ snd_ctl_free_one(kcontrol);
+ return 0;
+}
+
+/**
+ * snd_ctl_remove_id - remove the control of the given id and release it
+ * @card: the card instance
+ * @id: the control id to remove
+ *
+ * Finds the control instance with the given id, removes it from the
+ * card list and releases it.
+ *
+ * Returns 0 if successful, or a negative error code on failure.
+ */
+int snd_ctl_remove_id(snd_card_t * card, snd_ctl_elem_id_t *id)
+{
+ snd_kcontrol_t *kctl;
+ int ret;
+
+ down_write(&card->controls_rwsem);
+ kctl = snd_ctl_find_id(card, id);
+ if (kctl == NULL) {
+ up_write(&card->controls_rwsem);
+ return -ENOENT;
+ }
+ ret = snd_ctl_remove(card, kctl);
+ up_write(&card->controls_rwsem);
+ return ret;
+}
+
+/**
+ * snd_ctl_remove_unlocked_id - remove the unlocked control of the given id and release it
+ * @file: active control handle
+ * @id: the control id to remove
+ *
+ * Finds the control instance with the given id, removes it from the
+ * card list and releases it.
+ *
+ * Returns 0 if successful, or a negative error code on failure.
+ */
+static int snd_ctl_remove_unlocked_id(snd_ctl_file_t * file, snd_ctl_elem_id_t *id)
+{
+ snd_card_t *card = file->card;
+ snd_kcontrol_t *kctl;
+ int idx, ret;
+
+ down_write(&card->controls_rwsem);
+ kctl = snd_ctl_find_id(card, id);
+ if (kctl == NULL) {
+ up_write(&card->controls_rwsem);
+ return -ENOENT;
+ }
+ for (idx = 0; idx < kctl->count; idx++)
+ if (kctl->vd[idx].owner != NULL && kctl->vd[idx].owner != file) {
+ up_write(&card->controls_rwsem);
+ return -EBUSY;
+ }
+ ret = snd_ctl_remove(card, kctl);
+ up_write(&card->controls_rwsem);
+ return ret;
+}
+
+/**
+ * snd_ctl_rename_id - replace the id of a control on the card
+ * @card: the card instance
+ * @src_id: the old id
+ * @dst_id: the new id
+ *
+ * Finds the control with the old id from the card, and replaces the
+ * id with the new one.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_ctl_rename_id(snd_card_t * card, snd_ctl_elem_id_t *src_id, snd_ctl_elem_id_t *dst_id)
+{
+ snd_kcontrol_t *kctl;
+
+ down_write(&card->controls_rwsem);
+ kctl = snd_ctl_find_id(card, src_id);
+ if (kctl == NULL) {
+ up_write(&card->controls_rwsem);
+ return -ENOENT;
+ }
+ kctl->id = *dst_id;
+ kctl->id.numid = card->last_numid + 1;
+ card->last_numid += kctl->count;
+ up_write(&card->controls_rwsem);
+ return 0;
+}
+
+/**
+ * snd_ctl_find_numid - find the control instance with the given number-id
+ * @card: the card instance
+ * @numid: the number-id to search
+ *
+ * Finds the control instance with the given number-id from the card.
+ *
+ * Returns the pointer of the instance if found, or NULL if not.
+ *
+ * The caller must down card->controls_rwsem before calling this function
+ * (if the race condition can happen).
+ */
+snd_kcontrol_t *snd_ctl_find_numid(snd_card_t * card, unsigned int numid)
+{
+ struct list_head *list;
+ snd_kcontrol_t *kctl;
+
+ snd_runtime_check(card != NULL && numid != 0, return NULL);
+ list_for_each(list, &card->controls) {
+ kctl = snd_kcontrol(list);
+ if (kctl->id.numid <= numid && kctl->id.numid + kctl->count > numid)
+ return kctl;
+ }
+ return NULL;
+}
+
+/**
+ * snd_ctl_find_id - find the control instance with the given id
+ * @card: the card instance
+ * @id: the id to search
+ *
+ * Finds the control instance with the given id from the card.
+ *
+ * Returns the pointer of the instance if found, or NULL if not.
+ *
+ * The caller must down card->controls_rwsem before calling this function
+ * (if the race condition can happen).
+ */
+snd_kcontrol_t *snd_ctl_find_id(snd_card_t * card, snd_ctl_elem_id_t *id)
+{
+ struct list_head *list;
+ snd_kcontrol_t *kctl;
+
+ snd_runtime_check(card != NULL && id != NULL, return NULL);
+ if (id->numid != 0)
+ return snd_ctl_find_numid(card, id->numid);
+ list_for_each(list, &card->controls) {
+ kctl = snd_kcontrol(list);
+ if (kctl->id.iface != id->iface)
+ continue;
+ if (kctl->id.device != id->device)
+ continue;
+ if (kctl->id.subdevice != id->subdevice)
+ continue;
+ if (strncmp(kctl->id.name, id->name, sizeof(kctl->id.name)))
+ continue;
+ if (kctl->id.index > id->index)
+ continue;
+ if (kctl->id.index + kctl->count <= id->index)
+ continue;
+ return kctl;
+ }
+ return NULL;
+}
+
+static int snd_ctl_card_info(snd_card_t * card, snd_ctl_file_t * ctl,
+ unsigned int cmd, void __user *arg)
+{
+ snd_ctl_card_info_t *info;
+
+ info = kcalloc(1, sizeof(*info), GFP_KERNEL);
+ if (! info)
+ return -ENOMEM;
+ down_read(&snd_ioctl_rwsem);
+ info->card = card->number;
+ strlcpy(info->id, card->id, sizeof(info->id));
+ strlcpy(info->driver, card->driver, sizeof(info->driver));
+ strlcpy(info->name, card->shortname, sizeof(info->name));
+ strlcpy(info->longname, card->longname, sizeof(info->longname));
+ strlcpy(info->mixername, card->mixername, sizeof(info->mixername));
+ strlcpy(info->components, card->components, sizeof(info->components));
+ up_read(&snd_ioctl_rwsem);
+ if (copy_to_user(arg, info, sizeof(snd_ctl_card_info_t))) {
+ kfree(info);
+ return -EFAULT;
+ }
+ kfree(info);
+ return 0;
+}
+
+static int snd_ctl_elem_list(snd_card_t *card, snd_ctl_elem_list_t __user *_list)
+{
+ struct list_head *plist;
+ snd_ctl_elem_list_t list;
+ snd_kcontrol_t *kctl;
+ snd_ctl_elem_id_t *dst, *id;
+ unsigned int offset, space, first, jidx;
+
+ if (copy_from_user(&list, _list, sizeof(list)))
+ return -EFAULT;
+ offset = list.offset;
+ space = list.space;
+ first = 0;
+ /* try limit maximum space */
+ if (space > 16384)
+ return -ENOMEM;
+ if (space > 0) {
+ /* allocate temporary buffer for atomic operation */
+ dst = vmalloc(space * sizeof(snd_ctl_elem_id_t));
+ if (dst == NULL)
+ return -ENOMEM;
+ down_read(&card->controls_rwsem);
+ list.count = card->controls_count;
+ plist = card->controls.next;
+ while (plist != &card->controls) {
+ if (offset == 0)
+ break;
+ kctl = snd_kcontrol(plist);
+ if (offset < kctl->count)
+ break;
+ offset -= kctl->count;
+ plist = plist->next;
+ }
+ list.used = 0;
+ id = dst;
+ while (space > 0 && plist != &card->controls) {
+ kctl = snd_kcontrol(plist);
+ for (jidx = offset; space > 0 && jidx < kctl->count; jidx++) {
+ snd_ctl_build_ioff(id, kctl, jidx);
+ id++;
+ space--;
+ list.used++;
+ }
+ plist = plist->next;
+ offset = 0;
+ }
+ up_read(&card->controls_rwsem);
+ if (list.used > 0 && copy_to_user(list.pids, dst, list.used * sizeof(snd_ctl_elem_id_t))) {
+ vfree(dst);
+ return -EFAULT;
+ }
+ vfree(dst);
+ } else {
+ down_read(&card->controls_rwsem);
+ list.count = card->controls_count;
+ up_read(&card->controls_rwsem);
+ }
+ if (copy_to_user(_list, &list, sizeof(list)))
+ return -EFAULT;
+ return 0;
+}
+
+static int snd_ctl_elem_info(snd_ctl_file_t *ctl, snd_ctl_elem_info_t *info)
+{
+ snd_card_t *card = ctl->card;
+ snd_kcontrol_t *kctl;
+ snd_kcontrol_volatile_t *vd;
+ unsigned int index_offset;
+ int result;
+
+ down_read(&card->controls_rwsem);
+ kctl = snd_ctl_find_id(card, &info->id);
+ if (kctl == NULL) {
+ up_read(&card->controls_rwsem);
+ return -ENOENT;
+ }
+#ifdef CONFIG_SND_DEBUG
+ info->access = 0;
+#endif
+ result = kctl->info(kctl, info);
+ if (result >= 0) {
+ snd_assert(info->access == 0, );
+ index_offset = snd_ctl_get_ioff(kctl, &info->id);
+ vd = &kctl->vd[index_offset];
+ snd_ctl_build_ioff(&info->id, kctl, index_offset);
+ info->access = vd->access;
+ if (vd->owner) {
+ info->access |= SNDRV_CTL_ELEM_ACCESS_LOCK;
+ if (vd->owner == ctl)
+ info->access |= SNDRV_CTL_ELEM_ACCESS_OWNER;
+ info->owner = vd->owner_pid;
+ } else {
+ info->owner = -1;
+ }
+ }
+ up_read(&card->controls_rwsem);
+ return result;
+}
+
+static int snd_ctl_elem_info_user(snd_ctl_file_t *ctl, snd_ctl_elem_info_t __user *_info)
+{
+ snd_ctl_elem_info_t info;
+ int result;
+
+ if (copy_from_user(&info, _info, sizeof(info)))
+ return -EFAULT;
+ result = snd_ctl_elem_info(ctl, &info);
+ if (result >= 0)
+ if (copy_to_user(_info, &info, sizeof(info)))
+ return -EFAULT;
+ return result;
+}
+
+int snd_ctl_elem_read(snd_card_t *card, snd_ctl_elem_value_t *control)
+{
+ snd_kcontrol_t *kctl;
+ snd_kcontrol_volatile_t *vd;
+ unsigned int index_offset;
+ int result, indirect;
+
+ down_read(&card->controls_rwsem);
+ kctl = snd_ctl_find_id(card, &control->id);
+ if (kctl == NULL) {
+ result = -ENOENT;
+ } else {
+ index_offset = snd_ctl_get_ioff(kctl, &control->id);
+ vd = &kctl->vd[index_offset];
+ indirect = vd->access & SNDRV_CTL_ELEM_ACCESS_INDIRECT ? 1 : 0;
+ if (control->indirect != indirect) {
+ result = -EACCES;
+ } else {
+ if ((vd->access & SNDRV_CTL_ELEM_ACCESS_READ) && kctl->get != NULL) {
+ snd_ctl_build_ioff(&control->id, kctl, index_offset);
+ result = kctl->get(kctl, control);
+ } else {
+ result = -EPERM;
+ }
+ }
+ }
+ up_read(&card->controls_rwsem);
+ return result;
+}
+
+static int snd_ctl_elem_read_user(snd_card_t *card, snd_ctl_elem_value_t __user *_control)
+{
+ snd_ctl_elem_value_t *control;
+ int result;
+
+ control = kmalloc(sizeof(*control), GFP_KERNEL);
+ if (control == NULL)
+ return -ENOMEM;
+ if (copy_from_user(control, _control, sizeof(*control))) {
+ kfree(control);
+ return -EFAULT;
+ }
+ result = snd_ctl_elem_read(card, control);
+ if (result >= 0)
+ if (copy_to_user(_control, control, sizeof(*control)))
+ result = -EFAULT;
+ kfree(control);
+ return result;
+}
+
+int snd_ctl_elem_write(snd_card_t *card, snd_ctl_file_t *file, snd_ctl_elem_value_t *control)
+{
+ snd_kcontrol_t *kctl;
+ snd_kcontrol_volatile_t *vd;
+ unsigned int index_offset;
+ int result, indirect;
+
+ down_read(&card->controls_rwsem);
+ kctl = snd_ctl_find_id(card, &control->id);
+ if (kctl == NULL) {
+ result = -ENOENT;
+ } else {
+ index_offset = snd_ctl_get_ioff(kctl, &control->id);
+ vd = &kctl->vd[index_offset];
+ indirect = vd->access & SNDRV_CTL_ELEM_ACCESS_INDIRECT ? 1 : 0;
+ if (control->indirect != indirect) {
+ result = -EACCES;
+ } else {
+ if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_WRITE) ||
+ kctl->put == NULL ||
+ (file && vd->owner != NULL && vd->owner != file)) {
+ result = -EPERM;
+ } else {
+ snd_ctl_build_ioff(&control->id, kctl, index_offset);
+ result = kctl->put(kctl, control);
+ }
+ if (result > 0) {
+ up_read(&card->controls_rwsem);
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &control->id);
+ return 0;
+ }
+ }
+ }
+ up_read(&card->controls_rwsem);
+ return result;
+}
+
+static int snd_ctl_elem_write_user(snd_ctl_file_t *file, snd_ctl_elem_value_t __user *_control)
+{
+ snd_ctl_elem_value_t *control;
+ int result;
+
+ control = kmalloc(sizeof(*control), GFP_KERNEL);
+ if (control == NULL)
+ return -ENOMEM;
+ if (copy_from_user(control, _control, sizeof(*control))) {
+ kfree(control);
+ return -EFAULT;
+ }
+ result = snd_ctl_elem_write(file->card, file, control);
+ if (result >= 0)
+ if (copy_to_user(_control, control, sizeof(*control)))
+ result = -EFAULT;
+ kfree(control);
+ return result;
+}
+
+static int snd_ctl_elem_lock(snd_ctl_file_t *file, snd_ctl_elem_id_t __user *_id)
+{
+ snd_card_t *card = file->card;
+ snd_ctl_elem_id_t id;
+ snd_kcontrol_t *kctl;
+ snd_kcontrol_volatile_t *vd;
+ int result;
+
+ if (copy_from_user(&id, _id, sizeof(id)))
+ return -EFAULT;
+ down_write(&card->controls_rwsem);
+ kctl = snd_ctl_find_id(card, &id);
+ if (kctl == NULL) {
+ result = -ENOENT;
+ } else {
+ vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
+ if (vd->owner != NULL)
+ result = -EBUSY;
+ else {
+ vd->owner = file;
+ vd->owner_pid = current->pid;
+ result = 0;
+ }
+ }
+ up_write(&card->controls_rwsem);
+ return result;
+}
+
+static int snd_ctl_elem_unlock(snd_ctl_file_t *file, snd_ctl_elem_id_t __user *_id)
+{
+ snd_card_t *card = file->card;
+ snd_ctl_elem_id_t id;
+ snd_kcontrol_t *kctl;
+ snd_kcontrol_volatile_t *vd;
+ int result;
+
+ if (copy_from_user(&id, _id, sizeof(id)))
+ return -EFAULT;
+ down_write(&card->controls_rwsem);
+ kctl = snd_ctl_find_id(card, &id);
+ if (kctl == NULL) {
+ result = -ENOENT;
+ } else {
+ vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
+ if (vd->owner == NULL)
+ result = -EINVAL;
+ else if (vd->owner != file)
+ result = -EPERM;
+ else {
+ vd->owner = NULL;
+ vd->owner_pid = 0;
+ result = 0;
+ }
+ }
+ up_write(&card->controls_rwsem);
+ return result;
+}
+
+struct user_element {
+ snd_ctl_elem_info_t info;
+ void *elem_data; /* element data */
+ unsigned long elem_data_size; /* size of element data in bytes */
+ void *priv_data; /* private data (like strings for enumerated type) */
+ unsigned long priv_data_size; /* size of private data in bytes */
+};
+
+static int snd_ctl_elem_user_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ struct user_element *ue = kcontrol->private_data;
+
+ *uinfo = ue->info;
+ return 0;
+}
+
+static int snd_ctl_elem_user_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ struct user_element *ue = kcontrol->private_data;
+
+ memcpy(&ucontrol->value, ue->elem_data, ue->elem_data_size);
+ return 0;
+}
+
+static int snd_ctl_elem_user_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ int change;
+ struct user_element *ue = kcontrol->private_data;
+
+ change = memcmp(&ucontrol->value, ue->elem_data, ue->elem_data_size) != 0;
+ if (change)
+ memcpy(ue->elem_data, &ucontrol->value, ue->elem_data_size);
+ return change;
+}
+
+static void snd_ctl_elem_user_free(snd_kcontrol_t * kcontrol)
+{
+ kfree(kcontrol->private_data);
+}
+
+static int snd_ctl_elem_add(snd_ctl_file_t *file, snd_ctl_elem_info_t *info, int replace)
+{
+ snd_card_t *card = file->card;
+ snd_kcontrol_t kctl, *_kctl;
+ unsigned int access;
+ long private_size;
+ struct user_element *ue;
+ int idx, err;
+
+ if (card->user_ctl_count >= MAX_USER_CONTROLS)
+ return -ENOMEM;
+ if (info->count > 1024)
+ return -EINVAL;
+ access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
+ (info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|SNDRV_CTL_ELEM_ACCESS_INACTIVE));
+ info->id.numid = 0;
+ memset(&kctl, 0, sizeof(kctl));
+ down_write(&card->controls_rwsem);
+ _kctl = snd_ctl_find_id(card, &info->id);
+ err = 0;
+ if (_kctl) {
+ if (replace)
+ err = snd_ctl_remove(card, _kctl);
+ else
+ err = -EBUSY;
+ } else {
+ if (replace)
+ err = -ENOENT;
+ }
+ up_write(&card->controls_rwsem);
+ if (err < 0)
+ return err;
+ memcpy(&kctl.id, &info->id, sizeof(info->id));
+ kctl.count = info->owner ? info->owner : 1;
+ access |= SNDRV_CTL_ELEM_ACCESS_USER;
+ kctl.info = snd_ctl_elem_user_info;
+ if (access & SNDRV_CTL_ELEM_ACCESS_READ)
+ kctl.get = snd_ctl_elem_user_get;
+ if (access & SNDRV_CTL_ELEM_ACCESS_WRITE)
+ kctl.put = snd_ctl_elem_user_put;
+ switch (info->type) {
+ case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
+ private_size = sizeof(char);
+ if (info->count > 128)
+ return -EINVAL;
+ break;
+ case SNDRV_CTL_ELEM_TYPE_INTEGER:
+ private_size = sizeof(long);
+ if (info->count > 128)
+ return -EINVAL;
+ break;
+ case SNDRV_CTL_ELEM_TYPE_INTEGER64:
+ private_size = sizeof(long long);
+ if (info->count > 64)
+ return -EINVAL;
+ break;
+ case SNDRV_CTL_ELEM_TYPE_BYTES:
+ private_size = sizeof(unsigned char);
+ if (info->count > 512)
+ return -EINVAL;
+ break;
+ case SNDRV_CTL_ELEM_TYPE_IEC958:
+ private_size = sizeof(struct sndrv_aes_iec958);
+ if (info->count != 1)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ private_size *= info->count;
+ ue = kcalloc(1, sizeof(struct user_element) + private_size, GFP_KERNEL);
+ if (ue == NULL)
+ return -ENOMEM;
+ ue->info = *info;
+ ue->elem_data = (char *)ue + sizeof(*ue);
+ ue->elem_data_size = private_size;
+ kctl.private_free = snd_ctl_elem_user_free;
+ _kctl = snd_ctl_new(&kctl, access);
+ if (_kctl == NULL) {
+ kfree(_kctl->private_data);
+ return -ENOMEM;
+ }
+ _kctl->private_data = ue;
+ for (idx = 0; idx < _kctl->count; idx++)
+ _kctl->vd[idx].owner = file;
+ err = snd_ctl_add(card, _kctl);
+ if (err < 0) {
+ snd_ctl_free_one(_kctl);
+ return err;
+ }
+
+ down_write(&card->controls_rwsem);
+ card->user_ctl_count++;
+ up_write(&card->controls_rwsem);
+
+ return 0;
+}
+
+static int snd_ctl_elem_add_user(snd_ctl_file_t *file, snd_ctl_elem_info_t __user *_info, int replace)
+{
+ snd_ctl_elem_info_t info;
+ if (copy_from_user(&info, _info, sizeof(info)))
+ return -EFAULT;
+ return snd_ctl_elem_add(file, &info, replace);
+}
+
+static int snd_ctl_elem_remove(snd_ctl_file_t *file, snd_ctl_elem_id_t __user *_id)
+{
+ snd_ctl_elem_id_t id;
+ int err;
+
+ if (copy_from_user(&id, _id, sizeof(id)))
+ return -EFAULT;
+ err = snd_ctl_remove_unlocked_id(file, &id);
+ if (! err) {
+ snd_card_t *card = file->card;
+ down_write(&card->controls_rwsem);
+ card->user_ctl_count--;
+ up_write(&card->controls_rwsem);
+ }
+ return err;
+}
+
+static int snd_ctl_subscribe_events(snd_ctl_file_t *file, int __user *ptr)
+{
+ int subscribe;
+ if (get_user(subscribe, ptr))
+ return -EFAULT;
+ if (subscribe < 0) {
+ subscribe = file->subscribed;
+ if (put_user(subscribe, ptr))
+ return -EFAULT;
+ return 0;
+ }
+ if (subscribe) {
+ file->subscribed = 1;
+ return 0;
+ } else if (file->subscribed) {
+ snd_ctl_empty_read_queue(file);
+ file->subscribed = 0;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * change the power state
+ */
+static int snd_ctl_set_power_state(snd_card_t *card, unsigned int power_state)
+{
+ switch (power_state) {
+ case SNDRV_CTL_POWER_D0:
+ if (card->power_state != power_state) {
+ card->pm_resume(card);
+ snd_power_change_state(card, power_state);
+ }
+ break;
+ case SNDRV_CTL_POWER_D3hot:
+ if (card->power_state != power_state) {
+ card->pm_suspend(card, PMSG_SUSPEND);
+ snd_power_change_state(card, power_state);
+ }
+ break;
+ case SNDRV_CTL_POWER_D1:
+ case SNDRV_CTL_POWER_D2:
+ case SNDRV_CTL_POWER_D3cold:
+ /* not supported yet */
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+#endif
+
+static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ snd_ctl_file_t *ctl;
+ snd_card_t *card;
+ struct list_head *list;
+ snd_kctl_ioctl_t *p;
+ void __user *argp = (void __user *)arg;
+ int __user *ip = argp;
+ int err;
+
+ ctl = file->private_data;
+ card = ctl->card;
+ snd_assert(card != NULL, return -ENXIO);
+ switch (cmd) {
+ case SNDRV_CTL_IOCTL_PVERSION:
+ return put_user(SNDRV_CTL_VERSION, ip) ? -EFAULT : 0;
+ case SNDRV_CTL_IOCTL_CARD_INFO:
+ return snd_ctl_card_info(card, ctl, cmd, argp);
+ case SNDRV_CTL_IOCTL_ELEM_LIST:
+ return snd_ctl_elem_list(ctl->card, argp);
+ case SNDRV_CTL_IOCTL_ELEM_INFO:
+ return snd_ctl_elem_info_user(ctl, argp);
+ case SNDRV_CTL_IOCTL_ELEM_READ:
+ return snd_ctl_elem_read_user(ctl->card, argp);
+ case SNDRV_CTL_IOCTL_ELEM_WRITE:
+ return snd_ctl_elem_write_user(ctl, argp);
+ case SNDRV_CTL_IOCTL_ELEM_LOCK:
+ return snd_ctl_elem_lock(ctl, argp);
+ case SNDRV_CTL_IOCTL_ELEM_UNLOCK:
+ return snd_ctl_elem_unlock(ctl, argp);
+ case SNDRV_CTL_IOCTL_ELEM_ADD:
+ return snd_ctl_elem_add_user(ctl, argp, 0);
+ case SNDRV_CTL_IOCTL_ELEM_REPLACE:
+ return snd_ctl_elem_add_user(ctl, argp, 1);
+ case SNDRV_CTL_IOCTL_ELEM_REMOVE:
+ return snd_ctl_elem_remove(ctl, argp);
+ case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
+ return snd_ctl_subscribe_events(ctl, ip);
+ case SNDRV_CTL_IOCTL_POWER:
+ if (get_user(err, ip))
+ return -EFAULT;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+#ifdef CONFIG_PM
+ if (card->pm_suspend && card->pm_resume) {
+ snd_power_lock(card);
+ err = snd_ctl_set_power_state(card, err);
+ snd_power_unlock(card);
+ } else
+#endif
+ err = -ENOPROTOOPT;
+ return err;
+ case SNDRV_CTL_IOCTL_POWER_STATE:
+#ifdef CONFIG_PM
+ return put_user(card->power_state, ip) ? -EFAULT : 0;
+#else
+ return put_user(SNDRV_CTL_POWER_D0, ip) ? -EFAULT : 0;
+#endif
+ }
+ down_read(&snd_ioctl_rwsem);
+ list_for_each(list, &snd_control_ioctls) {
+ p = list_entry(list, snd_kctl_ioctl_t, list);
+ err = p->fioctl(card, ctl, cmd, arg);
+ if (err != -ENOIOCTLCMD) {
+ up_read(&snd_ioctl_rwsem);
+ return err;
+ }
+ }
+ up_read(&snd_ioctl_rwsem);
+ snd_printd("unknown ioctl = 0x%x\n", cmd);
+ return -ENOTTY;
+}
+
+static ssize_t snd_ctl_read(struct file *file, char __user *buffer, size_t count, loff_t * offset)
+{
+ snd_ctl_file_t *ctl;
+ int err = 0;
+ ssize_t result = 0;
+
+ ctl = file->private_data;
+ snd_assert(ctl != NULL && ctl->card != NULL, return -ENXIO);
+ if (!ctl->subscribed)
+ return -EBADFD;
+ if (count < sizeof(snd_ctl_event_t))
+ return -EINVAL;
+ spin_lock_irq(&ctl->read_lock);
+ while (count >= sizeof(snd_ctl_event_t)) {
+ snd_ctl_event_t ev;
+ snd_kctl_event_t *kev;
+ while (list_empty(&ctl->events)) {
+ wait_queue_t wait;
+ if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) {
+ err = -EAGAIN;
+ goto __end_lock;
+ }
+ init_waitqueue_entry(&wait, current);
+ add_wait_queue(&ctl->change_sleep, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&ctl->read_lock);
+ schedule();
+ remove_wait_queue(&ctl->change_sleep, &wait);
+ if (signal_pending(current))
+ return result > 0 ? result : -ERESTARTSYS;
+ spin_lock_irq(&ctl->read_lock);
+ }
+ kev = snd_kctl_event(ctl->events.next);
+ ev.type = SNDRV_CTL_EVENT_ELEM;
+ ev.data.elem.mask = kev->mask;
+ ev.data.elem.id = kev->id;
+ list_del(&kev->list);
+ spin_unlock_irq(&ctl->read_lock);
+ kfree(kev);
+ if (copy_to_user(buffer, &ev, sizeof(snd_ctl_event_t))) {
+ err = -EFAULT;
+ goto __end;
+ }
+ spin_lock_irq(&ctl->read_lock);
+ buffer += sizeof(snd_ctl_event_t);
+ count -= sizeof(snd_ctl_event_t);
+ result += sizeof(snd_ctl_event_t);
+ }
+ __end_lock:
+ spin_unlock_irq(&ctl->read_lock);
+ __end:
+ return result > 0 ? result : err;
+}
+
+static unsigned int snd_ctl_poll(struct file *file, poll_table * wait)
+{
+ unsigned int mask;
+ snd_ctl_file_t *ctl;
+
+ ctl = file->private_data;
+ if (!ctl->subscribed)
+ return 0;
+ poll_wait(file, &ctl->change_sleep, wait);
+
+ mask = 0;
+ if (!list_empty(&ctl->events))
+ mask |= POLLIN | POLLRDNORM;
+
+ return mask;
+}
+
+/*
+ * register the device-specific control-ioctls.
+ * called from each device manager like pcm.c, hwdep.c, etc.
+ */
+static int _snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn, struct list_head *lists)
+{
+ snd_kctl_ioctl_t *pn;
+
+ pn = kcalloc(1, sizeof(snd_kctl_ioctl_t), GFP_KERNEL);
+ if (pn == NULL)
+ return -ENOMEM;
+ pn->fioctl = fcn;
+ down_write(&snd_ioctl_rwsem);
+ list_add_tail(&pn->list, lists);
+ up_write(&snd_ioctl_rwsem);
+ return 0;
+}
+
+int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn)
+{
+ return _snd_ctl_register_ioctl(fcn, &snd_control_ioctls);
+}
+
+#ifdef CONFIG_COMPAT
+int snd_ctl_register_ioctl_compat(snd_kctl_ioctl_func_t fcn)
+{
+ return _snd_ctl_register_ioctl(fcn, &snd_control_compat_ioctls);
+}
+#endif
+
+/*
+ * de-register the device-specific control-ioctls.
+ */
+static int _snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn, struct list_head *lists)
+{
+ struct list_head *list;
+ snd_kctl_ioctl_t *p;
+
+ snd_runtime_check(fcn != NULL, return -EINVAL);
+ down_write(&snd_ioctl_rwsem);
+ list_for_each(list, lists) {
+ p = list_entry(list, snd_kctl_ioctl_t, list);
+ if (p->fioctl == fcn) {
+ list_del(&p->list);
+ up_write(&snd_ioctl_rwsem);
+ kfree(p);
+ return 0;
+ }
+ }
+ up_write(&snd_ioctl_rwsem);
+ snd_BUG();
+ return -EINVAL;
+}
+
+int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn)
+{
+ return _snd_ctl_unregister_ioctl(fcn, &snd_control_ioctls);
+}
+
+#ifdef CONFIG_COMPAT
+int snd_ctl_unregister_ioctl_compat(snd_kctl_ioctl_func_t fcn)
+{
+ return _snd_ctl_unregister_ioctl(fcn, &snd_control_compat_ioctls);
+}
+
+#endif
+
+static int snd_ctl_fasync(int fd, struct file * file, int on)
+{
+ snd_ctl_file_t *ctl;
+ int err;
+ ctl = file->private_data;
+ err = fasync_helper(fd, file, on, &ctl->fasync);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+/*
+ * ioctl32 compat
+ */
+#ifdef CONFIG_COMPAT
+#include "control_compat.c"
+#else
+#define snd_ctl_ioctl_compat NULL
+#endif
+
+/*
+ * INIT PART
+ */
+
+static struct file_operations snd_ctl_f_ops =
+{
+ .owner = THIS_MODULE,
+ .read = snd_ctl_read,
+ .open = snd_ctl_open,
+ .release = snd_ctl_release,
+ .poll = snd_ctl_poll,
+ .unlocked_ioctl = snd_ctl_ioctl,
+ .compat_ioctl = snd_ctl_ioctl_compat,
+ .fasync = snd_ctl_fasync,
+};
+
+static snd_minor_t snd_ctl_reg =
+{
+ .comment = "ctl",
+ .f_ops = &snd_ctl_f_ops,
+};
+
+/*
+ * registration of the control device
+ */
+static int snd_ctl_dev_register(snd_device_t *device)
+{
+ snd_card_t *card = device->device_data;
+ int err, cardnum;
+ char name[16];
+
+ snd_assert(card != NULL, return -ENXIO);
+ cardnum = card->number;
+ snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO);
+ sprintf(name, "controlC%i", cardnum);
+ if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL,
+ card, 0, &snd_ctl_reg, name)) < 0)
+ return err;
+ return 0;
+}
+
+/*
+ * disconnection of the control device
+ */
+static int snd_ctl_dev_disconnect(snd_device_t *device)
+{
+ snd_card_t *card = device->device_data;
+ struct list_head *flist;
+ snd_ctl_file_t *ctl;
+
+ down_read(&card->controls_rwsem);
+ list_for_each(flist, &card->ctl_files) {
+ ctl = snd_ctl_file(flist);
+ wake_up(&ctl->change_sleep);
+ kill_fasync(&ctl->fasync, SIGIO, POLL_ERR);
+ }
+ up_read(&card->controls_rwsem);
+ return 0;
+}
+
+/*
+ * free all controls
+ */
+static int snd_ctl_dev_free(snd_device_t *device)
+{
+ snd_card_t *card = device->device_data;
+ snd_kcontrol_t *control;
+
+ down_write(&card->controls_rwsem);
+ while (!list_empty(&card->controls)) {
+ control = snd_kcontrol(card->controls.next);
+ snd_ctl_remove(card, control);
+ }
+ up_write(&card->controls_rwsem);
+ return 0;
+}
+
+/*
+ * de-registration of the control device
+ */
+static int snd_ctl_dev_unregister(snd_device_t *device)
+{
+ snd_card_t *card = device->device_data;
+ int err, cardnum;
+
+ snd_assert(card != NULL, return -ENXIO);
+ cardnum = card->number;
+ snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO);
+ if ((err = snd_unregister_device(SNDRV_DEVICE_TYPE_CONTROL, card, 0)) < 0)
+ return err;
+ return snd_ctl_dev_free(device);
+}
+
+/*
+ * create control core:
+ * called from init.c
+ */
+int snd_ctl_create(snd_card_t *card)
+{
+ static snd_device_ops_t ops = {
+ .dev_free = snd_ctl_dev_free,
+ .dev_register = snd_ctl_dev_register,
+ .dev_disconnect = snd_ctl_dev_disconnect,
+ .dev_unregister = snd_ctl_dev_unregister
+ };
+
+ snd_assert(card != NULL, return -ENXIO);
+ return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
+}
diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c
new file mode 100644
index 00000000000..7fdabea4bfc
--- /dev/null
+++ b/sound/core/control_compat.c
@@ -0,0 +1,412 @@
+/*
+ * compat ioctls for control API
+ *
+ * Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* this file included from control.c */
+
+#include <linux/compat.h>
+
+struct sndrv_ctl_elem_list32 {
+ u32 offset;
+ u32 space;
+ u32 used;
+ u32 count;
+ u32 pids;
+ unsigned char reserved[50];
+} /* don't set packed attribute here */;
+
+static int snd_ctl_elem_list_compat(snd_card_t *card, struct sndrv_ctl_elem_list32 __user *data32)
+{
+ struct sndrv_ctl_elem_list __user *data;
+ compat_caddr_t ptr;
+ int err;
+
+ data = compat_alloc_user_space(sizeof(*data));
+
+ /* offset, space, used, count */
+ if (copy_in_user(data, data32, 4 * sizeof(u32)))
+ return -EFAULT;
+ /* pids */
+ if (get_user(ptr, &data32->pids) ||
+ put_user(compat_ptr(ptr), &data->pids))
+ return -EFAULT;
+ err = snd_ctl_elem_list(card, data);
+ if (err < 0)
+ return err;
+ /* copy the result */
+ if (copy_in_user(data32, data, 4 * sizeof(u32)))
+ return -EFAULT;
+ return 0;
+}
+
+/*
+ * control element info
+ * it uses union, so the things are not easy..
+ */
+
+struct sndrv_ctl_elem_info32 {
+ struct sndrv_ctl_elem_id id; // the size of struct is same
+ s32 type;
+ u32 access;
+ u32 count;
+ s32 owner;
+ union {
+ struct {
+ s32 min;
+ s32 max;
+ s32 step;
+ } integer;
+ struct {
+ u64 min;
+ u64 max;
+ u64 step;
+ } integer64;
+ struct {
+ u32 items;
+ u32 item;
+ char name[64];
+ } enumerated;
+ unsigned char reserved[128];
+ } value;
+ unsigned char reserved[64];
+} __attribute__((packed));
+
+static int snd_ctl_elem_info_compat(snd_ctl_file_t *ctl, struct sndrv_ctl_elem_info32 __user *data32)
+{
+ struct sndrv_ctl_elem_info *data;
+ int err;
+
+ data = kcalloc(1, sizeof(*data), GFP_KERNEL);
+ if (! data)
+ return -ENOMEM;
+
+ err = -EFAULT;
+ /* copy id */
+ if (copy_from_user(&data->id, &data32->id, sizeof(data->id)))
+ goto error;
+ /* we need to copy the item index.
+ * hope this doesn't break anything..
+ */
+ if (get_user(data->value.enumerated.item, &data32->value.enumerated.item))
+ goto error;
+ err = snd_ctl_elem_info(ctl, data);
+ if (err < 0)
+ goto error;
+ /* restore info to 32bit */
+ err = -EFAULT;
+ /* id, type, access, count */
+ if (copy_to_user(&data32->id, &data->id, sizeof(data->id)) ||
+ copy_to_user(&data32->type, &data->type, 3 * sizeof(u32)))
+ goto error;
+ if (put_user(data->owner, &data32->owner))
+ goto error;
+ switch (data->type) {
+ case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
+ case SNDRV_CTL_ELEM_TYPE_INTEGER:
+ if (put_user(data->value.integer.min, &data32->value.integer.min) ||
+ put_user(data->value.integer.max, &data32->value.integer.max) ||
+ put_user(data->value.integer.step, &data32->value.integer.step))
+ goto error;
+ break;
+ case SNDRV_CTL_ELEM_TYPE_INTEGER64:
+ if (copy_to_user(&data32->value.integer64,
+ &data->value.integer64,
+ sizeof(data->value.integer64)))
+ goto error;
+ break;
+ case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
+ if (copy_to_user(&data32->value.enumerated,
+ &data->value.enumerated,
+ sizeof(data->value.enumerated)))
+ goto error;
+ break;
+ default:
+ break;
+ }
+ err = 0;
+ error:
+ kfree(data);
+ return err;
+}
+
+/* read / write */
+struct sndrv_ctl_elem_value32 {
+ struct sndrv_ctl_elem_id id;
+ unsigned int indirect; /* bit-field causes misalignment */
+ union {
+ s32 integer[128];
+ unsigned char data[512];
+#ifndef CONFIG_X86_64
+ s64 integer64[64];
+#endif
+ } value;
+ unsigned char reserved[128];
+};
+
+
+/* get the value type and count of the control */
+static int get_ctl_type(snd_card_t *card, snd_ctl_elem_id_t *id, int *countp)
+{
+ snd_kcontrol_t *kctl;
+ snd_ctl_elem_info_t info;
+ int err;
+
+ down_read(&card->controls_rwsem);
+ kctl = snd_ctl_find_id(card, id);
+ if (! kctl) {
+ up_read(&card->controls_rwsem);
+ return -ENXIO;
+ }
+ info.id = *id;
+ err = kctl->info(kctl, &info);
+ up_read(&card->controls_rwsem);
+ if (err >= 0) {
+ err = info.type;
+ *countp = info.count;
+ }
+ return err;
+}
+
+static int get_elem_size(int type, int count)
+{
+ switch (type) {
+ case SNDRV_CTL_ELEM_TYPE_INTEGER64:
+ return sizeof(s64) * count;
+ case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
+ return sizeof(int) * count;
+ case SNDRV_CTL_ELEM_TYPE_BYTES:
+ return 512;
+ case SNDRV_CTL_ELEM_TYPE_IEC958:
+ return sizeof(struct sndrv_aes_iec958);
+ default:
+ return -1;
+ }
+}
+
+static int copy_ctl_value_from_user(snd_card_t *card,
+ struct sndrv_ctl_elem_value *data,
+ struct sndrv_ctl_elem_value32 __user *data32,
+ int *typep, int *countp)
+{
+ int i, type, count, size;
+ unsigned int indirect;
+
+ if (copy_from_user(&data->id, &data32->id, sizeof(data->id)))
+ return -EFAULT;
+ if (get_user(indirect, &data32->indirect))
+ return -EFAULT;
+ if (indirect)
+ return -EINVAL;
+ type = get_ctl_type(card, &data->id, &count);
+ if (type < 0)
+ return type;
+
+ if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
+ type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
+ for (i = 0; i < count; i++) {
+ int val;
+ if (get_user(val, &data32->value.integer[i]))
+ return -EFAULT;
+ data->value.integer.value[i] = val;
+ }
+ } else {
+ size = get_elem_size(type, count);
+ if (size < 0) {
+ printk(KERN_ERR "snd_ioctl32_ctl_elem_value: unknown type %d\n", type);
+ return -EINVAL;
+ }
+ if (copy_from_user(data->value.bytes.data,
+ data32->value.data, size))
+ return -EFAULT;
+ }
+
+ *typep = type;
+ *countp = count;
+ return 0;
+}
+
+/* restore the value to 32bit */
+static int copy_ctl_value_to_user(struct sndrv_ctl_elem_value32 __user *data32,
+ struct sndrv_ctl_elem_value *data,
+ int type, int count)
+{
+ int i, size;
+
+ if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
+ type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
+ for (i = 0; i < count; i++) {
+ int val;
+ val = data->value.integer.value[i];
+ if (put_user(val, &data32->value.integer[i]))
+ return -EFAULT;
+ }
+ } else {
+ size = get_elem_size(type, count);
+ if (copy_to_user(data32->value.data,
+ data->value.bytes.data, size))
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static int snd_ctl_elem_read_user_compat(snd_card_t *card,
+ struct sndrv_ctl_elem_value32 __user *data32)
+{
+ struct sndrv_ctl_elem_value *data;
+ int err, type, count;
+
+ data = kcalloc(1, sizeof(*data), GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ if ((err = copy_ctl_value_from_user(card, data, data32, &type, &count)) < 0)
+ goto error;
+ if ((err = snd_ctl_elem_read(card, data)) < 0)
+ goto error;
+ err = copy_ctl_value_to_user(data32, data, type, count);
+ error:
+ kfree(data);
+ return err;
+}
+
+static int snd_ctl_elem_write_user_compat(snd_ctl_file_t *file,
+ struct sndrv_ctl_elem_value32 __user *data32)
+{
+ struct sndrv_ctl_elem_value *data;
+ int err, type, count;
+
+ data = kcalloc(1, sizeof(*data), GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ if ((err = copy_ctl_value_from_user(file->card, data, data32, &type, &count)) < 0)
+ goto error;
+ if ((err = snd_ctl_elem_write(file->card, file, data)) < 0)
+ goto error;
+ err = copy_ctl_value_to_user(data32, data, type, count);
+ error:
+ kfree(data);
+ return err;
+}
+
+/* add or replace a user control */
+static int snd_ctl_elem_add_compat(snd_ctl_file_t *file,
+ struct sndrv_ctl_elem_info32 __user *data32,
+ int replace)
+{
+ struct sndrv_ctl_elem_info *data;
+ int err;
+
+ data = kcalloc(1, sizeof(*data), GFP_KERNEL);
+ if (! data)
+ return -ENOMEM;
+
+ err = -EFAULT;
+ /* id, type, access, count */ \
+ if (copy_from_user(&data->id, &data32->id, sizeof(data->id)) ||
+ copy_from_user(&data->type, &data32->type, 3 * sizeof(u32)))
+ goto error;
+ if (get_user(data->owner, &data32->owner) ||
+ get_user(data->type, &data32->type))
+ goto error;
+ switch (data->type) {
+ case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
+ case SNDRV_CTL_ELEM_TYPE_INTEGER:
+ if (get_user(data->value.integer.min, &data32->value.integer.min) ||
+ get_user(data->value.integer.max, &data32->value.integer.max) ||
+ get_user(data->value.integer.step, &data32->value.integer.step))
+ goto error;
+ break;
+ case SNDRV_CTL_ELEM_TYPE_INTEGER64:
+ if (copy_from_user(&data->value.integer64,
+ &data32->value.integer64,
+ sizeof(data->value.integer64)))
+ goto error;
+ break;
+ case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
+ if (copy_from_user(&data->value.enumerated,
+ &data32->value.enumerated,
+ sizeof(data->value.enumerated)))
+ goto error;
+ break;
+ default:
+ break;
+ }
+ err = snd_ctl_elem_add(file, data, replace);
+ error:
+ kfree(data);
+ return err;
+}
+
+enum {
+ SNDRV_CTL_IOCTL_ELEM_LIST32 = _IOWR('U', 0x10, struct sndrv_ctl_elem_list32),
+ SNDRV_CTL_IOCTL_ELEM_INFO32 = _IOWR('U', 0x11, struct sndrv_ctl_elem_info32),
+ SNDRV_CTL_IOCTL_ELEM_READ32 = _IOWR('U', 0x12, struct sndrv_ctl_elem_value32),
+ SNDRV_CTL_IOCTL_ELEM_WRITE32 = _IOWR('U', 0x13, struct sndrv_ctl_elem_value32),
+ SNDRV_CTL_IOCTL_ELEM_ADD32 = _IOWR('U', 0x17, struct sndrv_ctl_elem_info32),
+ SNDRV_CTL_IOCTL_ELEM_REPLACE32 = _IOWR('U', 0x18, struct sndrv_ctl_elem_info32),
+};
+
+static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ snd_ctl_file_t *ctl;
+ struct list_head *list;
+ void __user *argp = compat_ptr(arg);
+ int err;
+
+ ctl = file->private_data;
+ snd_assert(ctl && ctl->card, return -ENXIO);
+
+ switch (cmd) {
+ case SNDRV_CTL_IOCTL_PVERSION:
+ case SNDRV_CTL_IOCTL_CARD_INFO:
+ case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
+ case SNDRV_CTL_IOCTL_POWER:
+ case SNDRV_CTL_IOCTL_POWER_STATE:
+ case SNDRV_CTL_IOCTL_ELEM_LOCK:
+ case SNDRV_CTL_IOCTL_ELEM_UNLOCK:
+ return snd_ctl_ioctl(file, cmd, (unsigned long)argp);
+ case SNDRV_CTL_IOCTL_ELEM_LIST32:
+ return snd_ctl_elem_list_compat(ctl->card, argp);
+ case SNDRV_CTL_IOCTL_ELEM_INFO32:
+ return snd_ctl_elem_info_compat(ctl, argp);
+ case SNDRV_CTL_IOCTL_ELEM_READ32:
+ return snd_ctl_elem_read_user_compat(ctl->card, argp);
+ case SNDRV_CTL_IOCTL_ELEM_WRITE32:
+ return snd_ctl_elem_write_user_compat(ctl, argp);
+ case SNDRV_CTL_IOCTL_ELEM_ADD32:
+ return snd_ctl_elem_add_compat(ctl, argp, 0);
+ case SNDRV_CTL_IOCTL_ELEM_REPLACE32:
+ return snd_ctl_elem_add_compat(ctl, argp, 1);
+ }
+
+ down_read(&snd_ioctl_rwsem);
+ list_for_each(list, &snd_control_compat_ioctls) {
+ snd_kctl_ioctl_t *p = list_entry(list, snd_kctl_ioctl_t, list);
+ if (p->fioctl) {
+ err = p->fioctl(ctl->card, ctl, cmd, arg);
+ if (err != -ENOIOCTLCMD) {
+ up_read(&snd_ioctl_rwsem);
+ return err;
+ }
+ }
+ }
+ up_read(&snd_ioctl_rwsem);
+ return -ENOIOCTLCMD;
+}
diff --git a/sound/core/device.c b/sound/core/device.c
new file mode 100644
index 00000000000..18c71f913d2
--- /dev/null
+++ b/sound/core/device.c
@@ -0,0 +1,240 @@
+/*
+ * Device management routines
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/errno.h>
+#include <sound/core.h>
+
+/**
+ * snd_device_new - create an ALSA device component
+ * @card: the card instance
+ * @type: the device type, SNDRV_DEV_TYPE_XXX
+ * @device_data: the data pointer of this device
+ * @ops: the operator table
+ *
+ * Creates a new device component for the given data pointer.
+ * The device will be assigned to the card and managed together
+ * by the card.
+ *
+ * The data pointer plays a role as the identifier, too, so the
+ * pointer address must be unique and unchanged.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_device_new(snd_card_t *card, snd_device_type_t type,
+ void *device_data, snd_device_ops_t *ops)
+{
+ snd_device_t *dev;
+
+ snd_assert(card != NULL && device_data != NULL && ops != NULL, return -ENXIO);
+ dev = kcalloc(1, sizeof(*dev), GFP_KERNEL);
+ if (dev == NULL)
+ return -ENOMEM;
+ dev->card = card;
+ dev->type = type;
+ dev->state = SNDRV_DEV_BUILD;
+ dev->device_data = device_data;
+ dev->ops = ops;
+ list_add(&dev->list, &card->devices); /* add to the head of list */
+ return 0;
+}
+
+/**
+ * snd_device_free - release the device from the card
+ * @card: the card instance
+ * @device_data: the data pointer to release
+ *
+ * Removes the device from the list on the card and invokes the
+ * callback, dev_unregister or dev_free, corresponding to the state.
+ * Then release the device.
+ *
+ * Returns zero if successful, or a negative error code on failure or if the
+ * device not found.
+ */
+int snd_device_free(snd_card_t *card, void *device_data)
+{
+ struct list_head *list;
+ snd_device_t *dev;
+
+ snd_assert(card != NULL, return -ENXIO);
+ snd_assert(device_data != NULL, return -ENXIO);
+ list_for_each(list, &card->devices) {
+ dev = snd_device(list);
+ if (dev->device_data != device_data)
+ continue;
+ /* unlink */
+ list_del(&dev->list);
+ if ((dev->state == SNDRV_DEV_REGISTERED || dev->state == SNDRV_DEV_DISCONNECTED) &&
+ dev->ops->dev_unregister) {
+ if (dev->ops->dev_unregister(dev))
+ snd_printk(KERN_ERR "device unregister failure\n");
+ } else {
+ if (dev->ops->dev_free) {
+ if (dev->ops->dev_free(dev))
+ snd_printk(KERN_ERR "device free failure\n");
+ }
+ }
+ kfree(dev);
+ return 0;
+ }
+ snd_printd("device free %p (from %p), not found\n", device_data, __builtin_return_address(0));
+ return -ENXIO;
+}
+
+/**
+ * snd_device_free - disconnect the device
+ * @card: the card instance
+ * @device_data: the data pointer to disconnect
+ *
+ * Turns the device into the disconnection state, invoking
+ * dev_disconnect callback, if the device was already registered.
+ *
+ * Usually called from snd_card_disconnect().
+ *
+ * Returns zero if successful, or a negative error code on failure or if the
+ * device not found.
+ */
+int snd_device_disconnect(snd_card_t *card, void *device_data)
+{
+ struct list_head *list;
+ snd_device_t *dev;
+
+ snd_assert(card != NULL, return -ENXIO);
+ snd_assert(device_data != NULL, return -ENXIO);
+ list_for_each(list, &card->devices) {
+ dev = snd_device(list);
+ if (dev->device_data != device_data)
+ continue;
+ if (dev->state == SNDRV_DEV_REGISTERED && dev->ops->dev_disconnect) {
+ if (dev->ops->dev_disconnect(dev))
+ snd_printk(KERN_ERR "device disconnect failure\n");
+ dev->state = SNDRV_DEV_DISCONNECTED;
+ }
+ return 0;
+ }
+ snd_printd("device disconnect %p (from %p), not found\n", device_data, __builtin_return_address(0));
+ return -ENXIO;
+}
+
+/**
+ * snd_device_register - register the device
+ * @card: the card instance
+ * @device_data: the data pointer to register
+ *
+ * Registers the device which was already created via
+ * snd_device_new(). Usually this is called from snd_card_register(),
+ * but it can be called later if any new devices are created after
+ * invocation of snd_card_register().
+ *
+ * Returns zero if successful, or a negative error code on failure or if the
+ * device not found.
+ */
+int snd_device_register(snd_card_t *card, void *device_data)
+{
+ struct list_head *list;
+ snd_device_t *dev;
+ int err;
+
+ snd_assert(card != NULL && device_data != NULL, return -ENXIO);
+ list_for_each(list, &card->devices) {
+ dev = snd_device(list);
+ if (dev->device_data != device_data)
+ continue;
+ if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) {
+ if ((err = dev->ops->dev_register(dev)) < 0)
+ return err;
+ dev->state = SNDRV_DEV_REGISTERED;
+ return 0;
+ }
+ return -EBUSY;
+ }
+ snd_BUG();
+ return -ENXIO;
+}
+
+/*
+ * register all the devices on the card.
+ * called from init.c
+ */
+int snd_device_register_all(snd_card_t *card)
+{
+ struct list_head *list;
+ snd_device_t *dev;
+ int err;
+
+ snd_assert(card != NULL, return -ENXIO);
+ list_for_each(list, &card->devices) {
+ dev = snd_device(list);
+ if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) {
+ if ((err = dev->ops->dev_register(dev)) < 0)
+ return err;
+ dev->state = SNDRV_DEV_REGISTERED;
+ }
+ }
+ return 0;
+}
+
+/*
+ * disconnect all the devices on the card.
+ * called from init.c
+ */
+int snd_device_disconnect_all(snd_card_t *card)
+{
+ snd_device_t *dev;
+ struct list_head *list;
+ int err = 0;
+
+ snd_assert(card != NULL, return -ENXIO);
+ list_for_each(list, &card->devices) {
+ dev = snd_device(list);
+ if (snd_device_disconnect(card, dev->device_data) < 0)
+ err = -ENXIO;
+ }
+ return err;
+}
+
+/*
+ * release all the devices on the card.
+ * called from init.c
+ */
+int snd_device_free_all(snd_card_t *card, snd_device_cmd_t cmd)
+{
+ snd_device_t *dev;
+ struct list_head *list;
+ int err;
+ unsigned int range_low, range_high;
+
+ snd_assert(card != NULL, return -ENXIO);
+ range_low = cmd * SNDRV_DEV_TYPE_RANGE_SIZE;
+ range_high = range_low + SNDRV_DEV_TYPE_RANGE_SIZE - 1;
+ __again:
+ list_for_each(list, &card->devices) {
+ dev = snd_device(list);
+ if (dev->type >= range_low && dev->type <= range_high) {
+ if ((err = snd_device_free(card, dev->device_data)) < 0)
+ return err;
+ goto __again;
+ }
+ }
+ return 0;
+}
diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c
new file mode 100644
index 00000000000..997dd41c584
--- /dev/null
+++ b/sound/core/hwdep.c
@@ -0,0 +1,524 @@
+/*
+ * Hardware dependent layer
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/major.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/minors.h>
+#include <sound/hwdep.h>
+#include <sound/info.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Hardware dependent layer");
+MODULE_LICENSE("GPL");
+
+static snd_hwdep_t *snd_hwdep_devices[SNDRV_CARDS * SNDRV_MINOR_HWDEPS];
+
+static DECLARE_MUTEX(register_mutex);
+
+static int snd_hwdep_free(snd_hwdep_t *hwdep);
+static int snd_hwdep_dev_free(snd_device_t *device);
+static int snd_hwdep_dev_register(snd_device_t *device);
+static int snd_hwdep_dev_unregister(snd_device_t *device);
+
+/*
+
+ */
+
+static loff_t snd_hwdep_llseek(struct file * file, loff_t offset, int orig)
+{
+ snd_hwdep_t *hw = file->private_data;
+ if (hw->ops.llseek)
+ return hw->ops.llseek(hw, file, offset, orig);
+ return -ENXIO;
+}
+
+static ssize_t snd_hwdep_read(struct file * file, char __user *buf, size_t count, loff_t *offset)
+{
+ snd_hwdep_t *hw = file->private_data;
+ if (hw->ops.read)
+ return hw->ops.read(hw, buf, count, offset);
+ return -ENXIO;
+}
+
+static ssize_t snd_hwdep_write(struct file * file, const char __user *buf, size_t count, loff_t *offset)
+{
+ snd_hwdep_t *hw = file->private_data;
+ if (hw->ops.write)
+ return hw->ops.write(hw, buf, count, offset);
+ return -ENXIO;
+}
+
+static int snd_hwdep_open(struct inode *inode, struct file * file)
+{
+ int major = imajor(inode);
+ int cardnum;
+ int device;
+ snd_hwdep_t *hw;
+ int err;
+ wait_queue_t wait;
+
+ switch (major) {
+ case CONFIG_SND_MAJOR:
+ cardnum = SNDRV_MINOR_CARD(iminor(inode));
+ device = SNDRV_MINOR_DEVICE(iminor(inode)) - SNDRV_MINOR_HWDEP;
+ break;
+#ifdef CONFIG_SND_OSSEMUL
+ case SOUND_MAJOR:
+ cardnum = SNDRV_MINOR_OSS_CARD(iminor(inode));
+ device = 0;
+ break;
+#endif
+ default:
+ return -ENXIO;
+ }
+ cardnum %= SNDRV_CARDS;
+ device %= SNDRV_MINOR_HWDEPS;
+ hw = snd_hwdep_devices[(cardnum * SNDRV_MINOR_HWDEPS) + device];
+ if (hw == NULL)
+ return -ENODEV;
+
+ if (!hw->ops.open)
+ return -ENXIO;
+#ifdef CONFIG_SND_OSSEMUL
+ if (major == SOUND_MAJOR && hw->oss_type < 0)
+ return -ENXIO;
+#endif
+
+ if (!try_module_get(hw->card->module))
+ return -EFAULT;
+
+ init_waitqueue_entry(&wait, current);
+ add_wait_queue(&hw->open_wait, &wait);
+ down(&hw->open_mutex);
+ while (1) {
+ if (hw->exclusive && hw->used > 0) {
+ err = -EBUSY;
+ break;
+ }
+ err = hw->ops.open(hw, file);
+ if (err >= 0)
+ break;
+ if (err == -EAGAIN) {
+ if (file->f_flags & O_NONBLOCK) {
+ err = -EBUSY;
+ break;
+ }
+ } else
+ break;
+ set_current_state(TASK_INTERRUPTIBLE);
+ up(&hw->open_mutex);
+ schedule();
+ down(&hw->open_mutex);
+ if (signal_pending(current)) {
+ err = -ERESTARTSYS;
+ break;
+ }
+ }
+ remove_wait_queue(&hw->open_wait, &wait);
+ if (err >= 0) {
+ err = snd_card_file_add(hw->card, file);
+ if (err >= 0) {
+ file->private_data = hw;
+ hw->used++;
+ } else {
+ if (hw->ops.release)
+ hw->ops.release(hw, file);
+ }
+ }
+ up(&hw->open_mutex);
+ if (err < 0)
+ module_put(hw->card->module);
+ return err;
+}
+
+static int snd_hwdep_release(struct inode *inode, struct file * file)
+{
+ int err = -ENXIO;
+ snd_hwdep_t *hw = file->private_data;
+ down(&hw->open_mutex);
+ if (hw->ops.release) {
+ err = hw->ops.release(hw, file);
+ wake_up(&hw->open_wait);
+ }
+ if (hw->used > 0)
+ hw->used--;
+ snd_card_file_remove(hw->card, file);
+ up(&hw->open_mutex);
+ module_put(hw->card->module);
+ return err;
+}
+
+static unsigned int snd_hwdep_poll(struct file * file, poll_table * wait)
+{
+ snd_hwdep_t *hw = file->private_data;
+ if (hw->ops.poll)
+ return hw->ops.poll(hw, file, wait);
+ return 0;
+}
+
+static int snd_hwdep_info(snd_hwdep_t *hw, snd_hwdep_info_t __user *_info)
+{
+ snd_hwdep_info_t info;
+
+ memset(&info, 0, sizeof(info));
+ info.card = hw->card->number;
+ strlcpy(info.id, hw->id, sizeof(info.id));
+ strlcpy(info.name, hw->name, sizeof(info.name));
+ info.iface = hw->iface;
+ if (copy_to_user(_info, &info, sizeof(info)))
+ return -EFAULT;
+ return 0;
+}
+
+static int snd_hwdep_dsp_status(snd_hwdep_t *hw, snd_hwdep_dsp_status_t __user *_info)
+{
+ snd_hwdep_dsp_status_t info;
+ int err;
+
+ if (! hw->ops.dsp_status)
+ return -ENXIO;
+ memset(&info, 0, sizeof(info));
+ info.dsp_loaded = hw->dsp_loaded;
+ if ((err = hw->ops.dsp_status(hw, &info)) < 0)
+ return err;
+ if (copy_to_user(_info, &info, sizeof(info)))
+ return -EFAULT;
+ return 0;
+}
+
+static int snd_hwdep_dsp_load(snd_hwdep_t *hw, snd_hwdep_dsp_image_t __user *_info)
+{
+ snd_hwdep_dsp_image_t info;
+ int err;
+
+ if (! hw->ops.dsp_load)
+ return -ENXIO;
+ memset(&info, 0, sizeof(info));
+ if (copy_from_user(&info, _info, sizeof(info)))
+ return -EFAULT;
+ /* check whether the dsp was already loaded */
+ if (hw->dsp_loaded & (1 << info.index))
+ return -EBUSY;
+ if (!access_ok(VERIFY_READ, info.image, info.length))
+ return -EFAULT;
+ err = hw->ops.dsp_load(hw, &info);
+ if (err < 0)
+ return err;
+ hw->dsp_loaded |= (1 << info.index);
+ return 0;
+}
+
+static long snd_hwdep_ioctl(struct file * file, unsigned int cmd, unsigned long arg)
+{
+ snd_hwdep_t *hw = file->private_data;
+ void __user *argp = (void __user *)arg;
+ switch (cmd) {
+ case SNDRV_HWDEP_IOCTL_PVERSION:
+ return put_user(SNDRV_HWDEP_VERSION, (int __user *)argp);
+ case SNDRV_HWDEP_IOCTL_INFO:
+ return snd_hwdep_info(hw, argp);
+ case SNDRV_HWDEP_IOCTL_DSP_STATUS:
+ return snd_hwdep_dsp_status(hw, argp);
+ case SNDRV_HWDEP_IOCTL_DSP_LOAD:
+ return snd_hwdep_dsp_load(hw, argp);
+ }
+ if (hw->ops.ioctl)
+ return hw->ops.ioctl(hw, file, cmd, arg);
+ return -ENOTTY;
+}
+
+static int snd_hwdep_mmap(struct file * file, struct vm_area_struct * vma)
+{
+ snd_hwdep_t *hw = file->private_data;
+ if (hw->ops.mmap)
+ return hw->ops.mmap(hw, file, vma);
+ return -ENXIO;
+}
+
+static int snd_hwdep_control_ioctl(snd_card_t * card, snd_ctl_file_t * control,
+ unsigned int cmd, unsigned long arg)
+{
+ unsigned int tmp;
+
+ tmp = card->number * SNDRV_MINOR_HWDEPS;
+ switch (cmd) {
+ case SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE:
+ {
+ int device;
+
+ if (get_user(device, (int __user *)arg))
+ return -EFAULT;
+ device = device < 0 ? 0 : device + 1;
+ while (device < SNDRV_MINOR_HWDEPS) {
+ if (snd_hwdep_devices[tmp + device])
+ break;
+ device++;
+ }
+ if (device >= SNDRV_MINOR_HWDEPS)
+ device = -1;
+ if (put_user(device, (int __user *)arg))
+ return -EFAULT;
+ return 0;
+ }
+ case SNDRV_CTL_IOCTL_HWDEP_INFO:
+ {
+ snd_hwdep_info_t __user *info = (snd_hwdep_info_t __user *)arg;
+ int device;
+ snd_hwdep_t *hwdep;
+
+ if (get_user(device, &info->device))
+ return -EFAULT;
+ if (device < 0 || device >= SNDRV_MINOR_HWDEPS)
+ return -ENXIO;
+ hwdep = snd_hwdep_devices[tmp + device];
+ if (hwdep == NULL)
+ return -ENXIO;
+ return snd_hwdep_info(hwdep, info);
+ }
+ }
+ return -ENOIOCTLCMD;
+}
+
+#ifdef CONFIG_COMPAT
+#include "hwdep_compat.c"
+#else
+#define snd_hwdep_ioctl_compat NULL
+#endif
+
+/*
+
+ */
+
+static struct file_operations snd_hwdep_f_ops =
+{
+ .owner = THIS_MODULE,
+ .llseek = snd_hwdep_llseek,
+ .read = snd_hwdep_read,
+ .write = snd_hwdep_write,
+ .open = snd_hwdep_open,
+ .release = snd_hwdep_release,
+ .poll = snd_hwdep_poll,
+ .unlocked_ioctl = snd_hwdep_ioctl,
+ .compat_ioctl = snd_hwdep_ioctl_compat,
+ .mmap = snd_hwdep_mmap,
+};
+
+static snd_minor_t snd_hwdep_reg =
+{
+ .comment = "hardware dependent",
+ .f_ops = &snd_hwdep_f_ops,
+};
+
+/**
+ * snd_hwdep_new - create a new hwdep instance
+ * @card: the card instance
+ * @id: the id string
+ * @device: the device index (zero-based)
+ * @rhwdep: the pointer to store the new hwdep instance
+ *
+ * Creates a new hwdep instance with the given index on the card.
+ * The callbacks (hwdep->ops) must be set on the returned instance
+ * after this call manually by the caller.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_hwdep_new(snd_card_t * card, char *id, int device, snd_hwdep_t ** rhwdep)
+{
+ snd_hwdep_t *hwdep;
+ int err;
+ static snd_device_ops_t ops = {
+ .dev_free = snd_hwdep_dev_free,
+ .dev_register = snd_hwdep_dev_register,
+ .dev_unregister = snd_hwdep_dev_unregister
+ };
+
+ snd_assert(rhwdep != NULL, return -EINVAL);
+ *rhwdep = NULL;
+ snd_assert(card != NULL, return -ENXIO);
+ hwdep = kcalloc(1, sizeof(*hwdep), GFP_KERNEL);
+ if (hwdep == NULL)
+ return -ENOMEM;
+ hwdep->card = card;
+ hwdep->device = device;
+ if (id) {
+ strlcpy(hwdep->id, id, sizeof(hwdep->id));
+ }
+#ifdef CONFIG_SND_OSSEMUL
+ hwdep->oss_type = -1;
+#endif
+ if ((err = snd_device_new(card, SNDRV_DEV_HWDEP, hwdep, &ops)) < 0) {
+ snd_hwdep_free(hwdep);
+ return err;
+ }
+ init_waitqueue_head(&hwdep->open_wait);
+ init_MUTEX(&hwdep->open_mutex);
+ *rhwdep = hwdep;
+ return 0;
+}
+
+static int snd_hwdep_free(snd_hwdep_t *hwdep)
+{
+ snd_assert(hwdep != NULL, return -ENXIO);
+ if (hwdep->private_free)
+ hwdep->private_free(hwdep);
+ kfree(hwdep);
+ return 0;
+}
+
+static int snd_hwdep_dev_free(snd_device_t *device)
+{
+ snd_hwdep_t *hwdep = device->device_data;
+ return snd_hwdep_free(hwdep);
+}
+
+static int snd_hwdep_dev_register(snd_device_t *device)
+{
+ snd_hwdep_t *hwdep = device->device_data;
+ int idx, err;
+ char name[32];
+
+ down(&register_mutex);
+ idx = (hwdep->card->number * SNDRV_MINOR_HWDEPS) + hwdep->device;
+ if (snd_hwdep_devices[idx]) {
+ up(&register_mutex);
+ return -EBUSY;
+ }
+ snd_hwdep_devices[idx] = hwdep;
+ sprintf(name, "hwC%iD%i", hwdep->card->number, hwdep->device);
+ if ((err = snd_register_device(SNDRV_DEVICE_TYPE_HWDEP,
+ hwdep->card, hwdep->device,
+ &snd_hwdep_reg, name)) < 0) {
+ snd_printk(KERN_ERR "unable to register hardware dependent device %i:%i\n",
+ hwdep->card->number, hwdep->device);
+ snd_hwdep_devices[idx] = NULL;
+ up(&register_mutex);
+ return err;
+ }
+#ifdef CONFIG_SND_OSSEMUL
+ hwdep->ossreg = 0;
+ if (hwdep->oss_type >= 0) {
+ if ((hwdep->oss_type == SNDRV_OSS_DEVICE_TYPE_DMFM) && (hwdep->device != 0)) {
+ snd_printk (KERN_WARNING "only hwdep device 0 can be registered as OSS direct FM device!\n");
+ } else {
+ if (snd_register_oss_device(hwdep->oss_type,
+ hwdep->card, hwdep->device,
+ &snd_hwdep_reg, hwdep->oss_dev) < 0) {
+ snd_printk(KERN_ERR "unable to register OSS compatibility device %i:%i\n",
+ hwdep->card->number, hwdep->device);
+ } else
+ hwdep->ossreg = 1;
+ }
+ }
+#endif
+ up(&register_mutex);
+ return 0;
+}
+
+static int snd_hwdep_dev_unregister(snd_device_t *device)
+{
+ snd_hwdep_t *hwdep = device->device_data;
+ int idx;
+
+ snd_assert(hwdep != NULL, return -ENXIO);
+ down(&register_mutex);
+ idx = (hwdep->card->number * SNDRV_MINOR_HWDEPS) + hwdep->device;
+ if (snd_hwdep_devices[idx] != hwdep) {
+ up(&register_mutex);
+ return -EINVAL;
+ }
+#ifdef CONFIG_SND_OSSEMUL
+ if (hwdep->ossreg)
+ snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device);
+#endif
+ snd_unregister_device(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, hwdep->device);
+ snd_hwdep_devices[idx] = NULL;
+ up(&register_mutex);
+ return snd_hwdep_free(hwdep);
+}
+
+/*
+ * Info interface
+ */
+
+static void snd_hwdep_proc_read(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ int idx;
+ snd_hwdep_t *hwdep;
+
+ down(&register_mutex);
+ for (idx = 0; idx < SNDRV_CARDS * SNDRV_MINOR_HWDEPS; idx++) {
+ hwdep = snd_hwdep_devices[idx];
+ if (hwdep == NULL)
+ continue;
+ snd_iprintf(buffer, "%02i-%02i: %s\n",
+ idx / SNDRV_MINOR_HWDEPS,
+ idx % SNDRV_MINOR_HWDEPS,
+ hwdep->name);
+ }
+ up(&register_mutex);
+}
+
+/*
+ * ENTRY functions
+ */
+
+static snd_info_entry_t *snd_hwdep_proc_entry = NULL;
+
+static int __init alsa_hwdep_init(void)
+{
+ snd_info_entry_t *entry;
+
+ memset(snd_hwdep_devices, 0, sizeof(snd_hwdep_devices));
+ if ((entry = snd_info_create_module_entry(THIS_MODULE, "hwdep", NULL)) != NULL) {
+ entry->c.text.read_size = 512;
+ entry->c.text.read = snd_hwdep_proc_read;
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ entry = NULL;
+ }
+ }
+ snd_hwdep_proc_entry = entry;
+ snd_ctl_register_ioctl(snd_hwdep_control_ioctl);
+ snd_ctl_register_ioctl_compat(snd_hwdep_control_ioctl);
+ return 0;
+}
+
+static void __exit alsa_hwdep_exit(void)
+{
+ snd_ctl_unregister_ioctl(snd_hwdep_control_ioctl);
+ snd_ctl_unregister_ioctl_compat(snd_hwdep_control_ioctl);
+ if (snd_hwdep_proc_entry) {
+ snd_info_unregister(snd_hwdep_proc_entry);
+ snd_hwdep_proc_entry = NULL;
+ }
+}
+
+module_init(alsa_hwdep_init)
+module_exit(alsa_hwdep_exit)
+
+EXPORT_SYMBOL(snd_hwdep_new);
diff --git a/sound/core/hwdep_compat.c b/sound/core/hwdep_compat.c
new file mode 100644
index 00000000000..6866f423d4b
--- /dev/null
+++ b/sound/core/hwdep_compat.c
@@ -0,0 +1,77 @@
+/*
+ * 32bit -> 64bit ioctl wrapper for hwdep API
+ * Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* This file is included from hwdep.c */
+
+#include <linux/compat.h>
+
+struct sndrv_hwdep_dsp_image32 {
+ u32 index;
+ unsigned char name[64];
+ u32 image; /* pointer */
+ u32 length;
+ u32 driver_data;
+} /* don't set packed attribute here */;
+
+static int snd_hwdep_dsp_load_compat(snd_hwdep_t *hw,
+ struct sndrv_hwdep_dsp_image32 __user *src)
+{
+ struct sndrv_hwdep_dsp_image *dst;
+ compat_caddr_t ptr;
+ u32 val;
+
+ dst = compat_alloc_user_space(sizeof(*dst));
+
+ /* index and name */
+ if (copy_in_user(dst, src, 4 + 64))
+ return -EFAULT;
+ if (get_user(ptr, &src->image) ||
+ put_user(compat_ptr(ptr), &dst->image))
+ return -EFAULT;
+ if (get_user(val, &src->length) ||
+ put_user(val, &dst->length))
+ return -EFAULT;
+ if (get_user(val, &src->driver_data) ||
+ put_user(val, &dst->driver_data))
+ return -EFAULT;
+
+ return snd_hwdep_dsp_load(hw, dst);
+}
+
+enum {
+ SNDRV_HWDEP_IOCTL_DSP_LOAD32 = _IOW('H', 0x03, struct sndrv_hwdep_dsp_image32)
+};
+
+static long snd_hwdep_ioctl_compat(struct file * file, unsigned int cmd, unsigned long arg)
+{
+ snd_hwdep_t *hw = file->private_data;
+ void __user *argp = compat_ptr(arg);
+ switch (cmd) {
+ case SNDRV_HWDEP_IOCTL_PVERSION:
+ case SNDRV_HWDEP_IOCTL_INFO:
+ case SNDRV_HWDEP_IOCTL_DSP_STATUS:
+ return snd_hwdep_ioctl(file, cmd, (unsigned long)argp);
+ case SNDRV_HWDEP_IOCTL_DSP_LOAD32:
+ return snd_hwdep_dsp_load_compat(hw, argp);
+ }
+ if (hw->ops.ioctl_compat)
+ return hw->ops.ioctl_compat(hw, file, cmd, arg);
+ return -ENOIOCTLCMD;
+}
diff --git a/sound/core/info.c b/sound/core/info.c
new file mode 100644
index 00000000000..31faffe01cb
--- /dev/null
+++ b/sound/core/info.c
@@ -0,0 +1,989 @@
+/*
+ * Information interface for ALSA driver
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/time.h>
+#include <linux/smp_lock.h>
+#include <sound/core.h>
+#include <sound/minors.h>
+#include <sound/info.h>
+#include <sound/version.h>
+#include <linux/proc_fs.h>
+#include <linux/devfs_fs_kernel.h>
+#include <stdarg.h>
+
+/*
+ *
+ */
+
+int snd_info_check_reserved_words(const char *str)
+{
+ static char *reserved[] =
+ {
+ "version",
+ "meminfo",
+ "memdebug",
+ "detect",
+ "devices",
+ "oss",
+ "cards",
+ "timers",
+ "synth",
+ "pcm",
+ "seq",
+ NULL
+ };
+ char **xstr = reserved;
+
+ while (*xstr) {
+ if (!strcmp(*xstr, str))
+ return 0;
+ xstr++;
+ }
+ if (!strncmp(str, "card", 4))
+ return 0;
+ return 1;
+}
+
+#ifdef CONFIG_PROC_FS
+
+static DECLARE_MUTEX(info_mutex);
+
+typedef struct _snd_info_private_data {
+ snd_info_buffer_t *rbuffer;
+ snd_info_buffer_t *wbuffer;
+ snd_info_entry_t *entry;
+ void *file_private_data;
+} snd_info_private_data_t;
+
+static int snd_info_version_init(void);
+static int snd_info_version_done(void);
+
+
+/**
+ * snd_iprintf - printf on the procfs buffer
+ * @buffer: the procfs buffer
+ * @fmt: the printf format
+ *
+ * Outputs the string on the procfs buffer just like printf().
+ *
+ * Returns the size of output string.
+ */
+int snd_iprintf(snd_info_buffer_t * buffer, char *fmt,...)
+{
+ va_list args;
+ int len, res;
+
+ if (buffer->stop || buffer->error)
+ return 0;
+ len = buffer->len - buffer->size;
+ va_start(args, fmt);
+ res = vsnprintf(buffer->curr, len, fmt, args);
+ va_end(args);
+ if (res >= len) {
+ buffer->stop = 1;
+ return 0;
+ }
+ buffer->curr += res;
+ buffer->size += res;
+ return res;
+}
+
+/*
+
+ */
+
+static struct proc_dir_entry *snd_proc_root = NULL;
+snd_info_entry_t *snd_seq_root = NULL;
+#ifdef CONFIG_SND_OSSEMUL
+snd_info_entry_t *snd_oss_root = NULL;
+#endif
+
+static inline void snd_info_entry_prepare(struct proc_dir_entry *de)
+{
+ de->owner = THIS_MODULE;
+}
+
+static void snd_remove_proc_entry(struct proc_dir_entry *parent,
+ struct proc_dir_entry *de)
+{
+ if (de)
+ remove_proc_entry(de->name, parent);
+}
+
+static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
+{
+ snd_info_private_data_t *data;
+ struct snd_info_entry *entry;
+ loff_t ret;
+
+ data = file->private_data;
+ entry = data->entry;
+ lock_kernel();
+ switch (entry->content) {
+ case SNDRV_INFO_CONTENT_TEXT:
+ switch (orig) {
+ case 0: /* SEEK_SET */
+ file->f_pos = offset;
+ ret = file->f_pos;
+ goto out;
+ case 1: /* SEEK_CUR */
+ file->f_pos += offset;
+ ret = file->f_pos;
+ goto out;
+ case 2: /* SEEK_END */
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+ break;
+ case SNDRV_INFO_CONTENT_DATA:
+ if (entry->c.ops->llseek) {
+ ret = entry->c.ops->llseek(entry,
+ data->file_private_data,
+ file, offset, orig);
+ goto out;
+ }
+ break;
+ }
+ ret = -ENXIO;
+out:
+ unlock_kernel();
+ return ret;
+}
+
+static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
+ size_t count, loff_t * offset)
+{
+ snd_info_private_data_t *data;
+ struct snd_info_entry *entry;
+ snd_info_buffer_t *buf;
+ size_t size = 0;
+ loff_t pos;
+
+ data = file->private_data;
+ snd_assert(data != NULL, return -ENXIO);
+ pos = *offset;
+ if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
+ return -EIO;
+ if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
+ return -EIO;
+ entry = data->entry;
+ switch (entry->content) {
+ case SNDRV_INFO_CONTENT_TEXT:
+ buf = data->rbuffer;
+ if (buf == NULL)
+ return -EIO;
+ if (pos >= buf->size)
+ return 0;
+ size = buf->size - pos;
+ size = min(count, size);
+ if (copy_to_user(buffer, buf->buffer + pos, size))
+ return -EFAULT;
+ break;
+ case SNDRV_INFO_CONTENT_DATA:
+ if (entry->c.ops->read)
+ size = entry->c.ops->read(entry,
+ data->file_private_data,
+ file, buffer, count, pos);
+ break;
+ }
+ if ((ssize_t) size > 0)
+ *offset = pos + size;
+ return size;
+}
+
+static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t * offset)
+{
+ snd_info_private_data_t *data;
+ struct snd_info_entry *entry;
+ snd_info_buffer_t *buf;
+ size_t size = 0;
+ loff_t pos;
+
+ data = file->private_data;
+ snd_assert(data != NULL, return -ENXIO);
+ entry = data->entry;
+ pos = *offset;
+ if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
+ return -EIO;
+ if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
+ return -EIO;
+ switch (entry->content) {
+ case SNDRV_INFO_CONTENT_TEXT:
+ buf = data->wbuffer;
+ if (buf == NULL)
+ return -EIO;
+ if (pos >= buf->len)
+ return -ENOMEM;
+ size = buf->len - pos;
+ size = min(count, size);
+ if (copy_from_user(buf->buffer + pos, buffer, size))
+ return -EFAULT;
+ if ((long)buf->size < pos + size)
+ buf->size = pos + size;
+ break;
+ case SNDRV_INFO_CONTENT_DATA:
+ if (entry->c.ops->write)
+ size = entry->c.ops->write(entry,
+ data->file_private_data,
+ file, buffer, count, pos);
+ break;
+ }
+ if ((ssize_t) size > 0)
+ *offset = pos + size;
+ return size;
+}
+
+static int snd_info_entry_open(struct inode *inode, struct file *file)
+{
+ snd_info_entry_t *entry;
+ snd_info_private_data_t *data;
+ snd_info_buffer_t *buffer;
+ struct proc_dir_entry *p;
+ int mode, err;
+
+ down(&info_mutex);
+ p = PDE(inode);
+ entry = p == NULL ? NULL : (snd_info_entry_t *)p->data;
+ if (entry == NULL || entry->disconnected) {
+ up(&info_mutex);
+ return -ENODEV;
+ }
+ if (!try_module_get(entry->module)) {
+ err = -EFAULT;
+ goto __error1;
+ }
+ mode = file->f_flags & O_ACCMODE;
+ if (mode == O_RDONLY || mode == O_RDWR) {
+ if ((entry->content == SNDRV_INFO_CONTENT_TEXT &&
+ !entry->c.text.read_size) ||
+ (entry->content == SNDRV_INFO_CONTENT_DATA &&
+ entry->c.ops->read == NULL)) {
+ err = -ENODEV;
+ goto __error;
+ }
+ }
+ if (mode == O_WRONLY || mode == O_RDWR) {
+ if ((entry->content == SNDRV_INFO_CONTENT_TEXT &&
+ !entry->c.text.write_size) ||
+ (entry->content == SNDRV_INFO_CONTENT_DATA &&
+ entry->c.ops->write == NULL)) {
+ err = -ENODEV;
+ goto __error;
+ }
+ }
+ data = kcalloc(1, sizeof(*data), GFP_KERNEL);
+ if (data == NULL) {
+ err = -ENOMEM;
+ goto __error;
+ }
+ data->entry = entry;
+ switch (entry->content) {
+ case SNDRV_INFO_CONTENT_TEXT:
+ if (mode == O_RDONLY || mode == O_RDWR) {
+ buffer = kcalloc(1, sizeof(*buffer), GFP_KERNEL);
+ if (buffer == NULL) {
+ kfree(data);
+ err = -ENOMEM;
+ goto __error;
+ }
+ buffer->len = (entry->c.text.read_size +
+ (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
+ buffer->buffer = vmalloc(buffer->len);
+ if (buffer->buffer == NULL) {
+ kfree(buffer);
+ kfree(data);
+ err = -ENOMEM;
+ goto __error;
+ }
+ buffer->curr = buffer->buffer;
+ data->rbuffer = buffer;
+ }
+ if (mode == O_WRONLY || mode == O_RDWR) {
+ buffer = kcalloc(1, sizeof(*buffer), GFP_KERNEL);
+ if (buffer == NULL) {
+ if (mode == O_RDWR) {
+ vfree(data->rbuffer->buffer);
+ kfree(data->rbuffer);
+ }
+ kfree(data);
+ err = -ENOMEM;
+ goto __error;
+ }
+ buffer->len = (entry->c.text.write_size +
+ (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
+ buffer->buffer = vmalloc(buffer->len);
+ if (buffer->buffer == NULL) {
+ if (mode == O_RDWR) {
+ vfree(data->rbuffer->buffer);
+ kfree(data->rbuffer);
+ }
+ kfree(buffer);
+ kfree(data);
+ err = -ENOMEM;
+ goto __error;
+ }
+ buffer->curr = buffer->buffer;
+ data->wbuffer = buffer;
+ }
+ break;
+ case SNDRV_INFO_CONTENT_DATA: /* data */
+ if (entry->c.ops->open) {
+ if ((err = entry->c.ops->open(entry, mode,
+ &data->file_private_data)) < 0) {
+ kfree(data);
+ goto __error;
+ }
+ }
+ break;
+ }
+ file->private_data = data;
+ up(&info_mutex);
+ if (entry->content == SNDRV_INFO_CONTENT_TEXT &&
+ (mode == O_RDONLY || mode == O_RDWR)) {
+ if (entry->c.text.read) {
+ down(&entry->access);
+ entry->c.text.read(entry, data->rbuffer);
+ up(&entry->access);
+ }
+ }
+ return 0;
+
+ __error:
+ module_put(entry->module);
+ __error1:
+ up(&info_mutex);
+ return err;
+}
+
+static int snd_info_entry_release(struct inode *inode, struct file *file)
+{
+ snd_info_entry_t *entry;
+ snd_info_private_data_t *data;
+ int mode;
+
+ mode = file->f_flags & O_ACCMODE;
+ data = file->private_data;
+ entry = data->entry;
+ switch (entry->content) {
+ case SNDRV_INFO_CONTENT_TEXT:
+ if (mode == O_RDONLY || mode == O_RDWR) {
+ vfree(data->rbuffer->buffer);
+ kfree(data->rbuffer);
+ }
+ if (mode == O_WRONLY || mode == O_RDWR) {
+ if (entry->c.text.write) {
+ entry->c.text.write(entry, data->wbuffer);
+ if (data->wbuffer->error) {
+ snd_printk(KERN_WARNING "data write error to %s (%i)\n",
+ entry->name,
+ data->wbuffer->error);
+ }
+ }
+ vfree(data->wbuffer->buffer);
+ kfree(data->wbuffer);
+ }
+ break;
+ case SNDRV_INFO_CONTENT_DATA:
+ if (entry->c.ops->release)
+ entry->c.ops->release(entry, mode,
+ data->file_private_data);
+ break;
+ }
+ module_put(entry->module);
+ kfree(data);
+ return 0;
+}
+
+static unsigned int snd_info_entry_poll(struct file *file, poll_table * wait)
+{
+ snd_info_private_data_t *data;
+ struct snd_info_entry *entry;
+ unsigned int mask;
+
+ data = file->private_data;
+ if (data == NULL)
+ return 0;
+ entry = data->entry;
+ mask = 0;
+ switch (entry->content) {
+ case SNDRV_INFO_CONTENT_DATA:
+ if (entry->c.ops->poll)
+ return entry->c.ops->poll(entry,
+ data->file_private_data,
+ file, wait);
+ if (entry->c.ops->read)
+ mask |= POLLIN | POLLRDNORM;
+ if (entry->c.ops->write)
+ mask |= POLLOUT | POLLWRNORM;
+ break;
+ }
+ return mask;
+}
+
+static inline int _snd_info_entry_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ snd_info_private_data_t *data;
+ struct snd_info_entry *entry;
+
+ data = file->private_data;
+ if (data == NULL)
+ return 0;
+ entry = data->entry;
+ switch (entry->content) {
+ case SNDRV_INFO_CONTENT_DATA:
+ if (entry->c.ops->ioctl)
+ return entry->c.ops->ioctl(entry,
+ data->file_private_data,
+ file, cmd, arg);
+ break;
+ }
+ return -ENOTTY;
+}
+
+/* FIXME: need to unlock BKL to allow preemption */
+static int snd_info_entry_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int err;
+ unlock_kernel();
+ err = _snd_info_entry_ioctl(inode, file, cmd, arg);
+ lock_kernel();
+ return err;
+}
+
+static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct inode *inode = file->f_dentry->d_inode;
+ snd_info_private_data_t *data;
+ struct snd_info_entry *entry;
+
+ data = file->private_data;
+ if (data == NULL)
+ return 0;
+ entry = data->entry;
+ switch (entry->content) {
+ case SNDRV_INFO_CONTENT_DATA:
+ if (entry->c.ops->mmap)
+ return entry->c.ops->mmap(entry,
+ data->file_private_data,
+ inode, file, vma);
+ break;
+ }
+ return -ENXIO;
+}
+
+static struct file_operations snd_info_entry_operations =
+{
+ .owner = THIS_MODULE,
+ .llseek = snd_info_entry_llseek,
+ .read = snd_info_entry_read,
+ .write = snd_info_entry_write,
+ .poll = snd_info_entry_poll,
+ .ioctl = snd_info_entry_ioctl,
+ .mmap = snd_info_entry_mmap,
+ .open = snd_info_entry_open,
+ .release = snd_info_entry_release,
+};
+
+/**
+ * snd_create_proc_entry - create a procfs entry
+ * @name: the name of the proc file
+ * @mode: the file permission bits, S_Ixxx
+ * @parent: the parent proc-directory entry
+ *
+ * Creates a new proc file entry with the given name and permission
+ * on the given directory.
+ *
+ * Returns the pointer of new instance or NULL on failure.
+ */
+static struct proc_dir_entry *snd_create_proc_entry(const char *name, mode_t mode,
+ struct proc_dir_entry *parent)
+{
+ struct proc_dir_entry *p;
+ p = create_proc_entry(name, mode, parent);
+ if (p)
+ snd_info_entry_prepare(p);
+ return p;
+}
+
+int __init snd_info_init(void)
+{
+ struct proc_dir_entry *p;
+
+ p = snd_create_proc_entry("asound", S_IFDIR | S_IRUGO | S_IXUGO, &proc_root);
+ if (p == NULL)
+ return -ENOMEM;
+ snd_proc_root = p;
+#ifdef CONFIG_SND_OSSEMUL
+ {
+ snd_info_entry_t *entry;
+ if ((entry = snd_info_create_module_entry(THIS_MODULE, "oss", NULL)) == NULL)
+ return -ENOMEM;
+ entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ return -ENOMEM;
+ }
+ snd_oss_root = entry;
+ }
+#endif
+#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+ {
+ snd_info_entry_t *entry;
+ if ((entry = snd_info_create_module_entry(THIS_MODULE, "seq", NULL)) == NULL)
+ return -ENOMEM;
+ entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ return -ENOMEM;
+ }
+ snd_seq_root = entry;
+ }
+#endif
+ snd_info_version_init();
+ snd_memory_info_init();
+ snd_minor_info_init();
+ snd_minor_info_oss_init();
+ snd_card_info_init();
+ return 0;
+}
+
+int __exit snd_info_done(void)
+{
+ snd_card_info_done();
+ snd_minor_info_oss_done();
+ snd_minor_info_done();
+ snd_memory_info_done();
+ snd_info_version_done();
+ if (snd_proc_root) {
+#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+ if (snd_seq_root)
+ snd_info_unregister(snd_seq_root);
+#endif
+#ifdef CONFIG_SND_OSSEMUL
+ if (snd_oss_root)
+ snd_info_unregister(snd_oss_root);
+#endif
+ snd_remove_proc_entry(&proc_root, snd_proc_root);
+ }
+ return 0;
+}
+
+/*
+
+ */
+
+
+/*
+ * create a card proc file
+ * called from init.c
+ */
+int snd_info_card_create(snd_card_t * card)
+{
+ char str[8];
+ snd_info_entry_t *entry;
+
+ snd_assert(card != NULL, return -ENXIO);
+
+ sprintf(str, "card%i", card->number);
+ if ((entry = snd_info_create_module_entry(card->module, str, NULL)) == NULL)
+ return -ENOMEM;
+ entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ return -ENOMEM;
+ }
+ card->proc_root = entry;
+ return 0;
+}
+
+/*
+ * register the card proc file
+ * called from init.c
+ */
+int snd_info_card_register(snd_card_t * card)
+{
+ struct proc_dir_entry *p;
+
+ snd_assert(card != NULL, return -ENXIO);
+
+ if (!strcmp(card->id, card->proc_root->name))
+ return 0;
+
+ p = proc_symlink(card->id, snd_proc_root, card->proc_root->name);
+ if (p == NULL)
+ return -ENOMEM;
+ card->proc_root_link = p;
+ return 0;
+}
+
+/*
+ * de-register the card proc file
+ * called from init.c
+ */
+int snd_info_card_free(snd_card_t * card)
+{
+ snd_assert(card != NULL, return -ENXIO);
+ if (card->proc_root_link) {
+ snd_remove_proc_entry(snd_proc_root, card->proc_root_link);
+ card->proc_root_link = NULL;
+ }
+ if (card->proc_root) {
+ snd_info_unregister(card->proc_root);
+ card->proc_root = NULL;
+ }
+ return 0;
+}
+
+
+/**
+ * snd_info_get_line - read one line from the procfs buffer
+ * @buffer: the procfs buffer
+ * @line: the buffer to store
+ * @len: the max. buffer size - 1
+ *
+ * Reads one line from the buffer and stores the string.
+ *
+ * Returns zero if successful, or 1 if error or EOF.
+ */
+int snd_info_get_line(snd_info_buffer_t * buffer, char *line, int len)
+{
+ int c = -1;
+
+ if (len <= 0 || buffer->stop || buffer->error)
+ return 1;
+ while (--len > 0) {
+ c = *buffer->curr++;
+ if (c == '\n') {
+ if ((buffer->curr - buffer->buffer) >= (long)buffer->size) {
+ buffer->stop = 1;
+ }
+ break;
+ }
+ *line++ = c;
+ if ((buffer->curr - buffer->buffer) >= (long)buffer->size) {
+ buffer->stop = 1;
+ break;
+ }
+ }
+ while (c != '\n' && !buffer->stop) {
+ c = *buffer->curr++;
+ if ((buffer->curr - buffer->buffer) >= (long)buffer->size) {
+ buffer->stop = 1;
+ }
+ }
+ *line = '\0';
+ return 0;
+}
+
+/**
+ * snd_info_get_line - parse a string token
+ * @dest: the buffer to store the string token
+ * @src: the original string
+ * @len: the max. length of token - 1
+ *
+ * Parses the original string and copy a token to the given
+ * string buffer.
+ *
+ * Returns the updated pointer of the original string so that
+ * it can be used for the next call.
+ */
+char *snd_info_get_str(char *dest, char *src, int len)
+{
+ int c;
+
+ while (*src == ' ' || *src == '\t')
+ src++;
+ if (*src == '"' || *src == '\'') {
+ c = *src++;
+ while (--len > 0 && *src && *src != c) {
+ *dest++ = *src++;
+ }
+ if (*src == c)
+ src++;
+ } else {
+ while (--len > 0 && *src && *src != ' ' && *src != '\t') {
+ *dest++ = *src++;
+ }
+ }
+ *dest = 0;
+ while (*src == ' ' || *src == '\t')
+ src++;
+ return src;
+}
+
+/**
+ * snd_info_create_entry - create an info entry
+ * @name: the proc file name
+ *
+ * Creates an info entry with the given file name and initializes as
+ * the default state.
+ *
+ * Usually called from other functions such as
+ * snd_info_create_card_entry().
+ *
+ * Returns the pointer of the new instance, or NULL on failure.
+ */
+static snd_info_entry_t *snd_info_create_entry(const char *name)
+{
+ snd_info_entry_t *entry;
+ entry = kcalloc(1, sizeof(*entry), GFP_KERNEL);
+ if (entry == NULL)
+ return NULL;
+ entry->name = snd_kmalloc_strdup(name, GFP_KERNEL);
+ if (entry->name == NULL) {
+ kfree(entry);
+ return NULL;
+ }
+ entry->mode = S_IFREG | S_IRUGO;
+ entry->content = SNDRV_INFO_CONTENT_TEXT;
+ init_MUTEX(&entry->access);
+ return entry;
+}
+
+/**
+ * snd_info_create_module_entry - create an info entry for the given module
+ * @module: the module pointer
+ * @name: the file name
+ * @parent: the parent directory
+ *
+ * Creates a new info entry and assigns it to the given module.
+ *
+ * Returns the pointer of the new instance, or NULL on failure.
+ */
+snd_info_entry_t *snd_info_create_module_entry(struct module * module,
+ const char *name,
+ snd_info_entry_t *parent)
+{
+ snd_info_entry_t *entry = snd_info_create_entry(name);
+ if (entry) {
+ entry->module = module;
+ entry->parent = parent;
+ }
+ return entry;
+}
+
+/**
+ * snd_info_create_card_entry - create an info entry for the given card
+ * @card: the card instance
+ * @name: the file name
+ * @parent: the parent directory
+ *
+ * Creates a new info entry and assigns it to the given card.
+ *
+ * Returns the pointer of the new instance, or NULL on failure.
+ */
+snd_info_entry_t *snd_info_create_card_entry(snd_card_t * card,
+ const char *name,
+ snd_info_entry_t * parent)
+{
+ snd_info_entry_t *entry = snd_info_create_entry(name);
+ if (entry) {
+ entry->module = card->module;
+ entry->card = card;
+ entry->parent = parent;
+ }
+ return entry;
+}
+
+static int snd_info_dev_free_entry(snd_device_t *device)
+{
+ snd_info_entry_t *entry = device->device_data;
+ snd_info_free_entry(entry);
+ return 0;
+}
+
+static int snd_info_dev_register_entry(snd_device_t *device)
+{
+ snd_info_entry_t *entry = device->device_data;
+ return snd_info_register(entry);
+}
+
+static int snd_info_dev_disconnect_entry(snd_device_t *device)
+{
+ snd_info_entry_t *entry = device->device_data;
+ entry->disconnected = 1;
+ return 0;
+}
+
+static int snd_info_dev_unregister_entry(snd_device_t *device)
+{
+ snd_info_entry_t *entry = device->device_data;
+ return snd_info_unregister(entry);
+}
+
+/**
+ * snd_card_proc_new - create an info entry for the given card
+ * @card: the card instance
+ * @name: the file name
+ * @entryp: the pointer to store the new info entry
+ *
+ * Creates a new info entry and assigns it to the given card.
+ * Unlike snd_info_create_card_entry(), this function registers the
+ * info entry as an ALSA device component, so that it can be
+ * unregistered/released without explicit call.
+ * Also, you don't have to register this entry via snd_info_register(),
+ * since this will be registered by snd_card_register() automatically.
+ *
+ * The parent is assumed as card->proc_root.
+ *
+ * For releasing this entry, use snd_device_free() instead of
+ * snd_info_free_entry().
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_card_proc_new(snd_card_t *card, const char *name,
+ snd_info_entry_t **entryp)
+{
+ static snd_device_ops_t ops = {
+ .dev_free = snd_info_dev_free_entry,
+ .dev_register = snd_info_dev_register_entry,
+ .dev_disconnect = snd_info_dev_disconnect_entry,
+ .dev_unregister = snd_info_dev_unregister_entry
+ };
+ snd_info_entry_t *entry;
+ int err;
+
+ entry = snd_info_create_card_entry(card, name, card->proc_root);
+ if (! entry)
+ return -ENOMEM;
+ if ((err = snd_device_new(card, SNDRV_DEV_INFO, entry, &ops)) < 0) {
+ snd_info_free_entry(entry);
+ return err;
+ }
+ if (entryp)
+ *entryp = entry;
+ return 0;
+}
+
+/**
+ * snd_info_free_entry - release the info entry
+ * @entry: the info entry
+ *
+ * Releases the info entry. Don't call this after registered.
+ */
+void snd_info_free_entry(snd_info_entry_t * entry)
+{
+ if (entry == NULL)
+ return;
+ kfree(entry->name);
+ if (entry->private_free)
+ entry->private_free(entry);
+ kfree(entry);
+}
+
+/**
+ * snd_info_register - register the info entry
+ * @entry: the info entry
+ *
+ * Registers the proc info entry.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_info_register(snd_info_entry_t * entry)
+{
+ struct proc_dir_entry *root, *p = NULL;
+
+ snd_assert(entry != NULL, return -ENXIO);
+ root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
+ down(&info_mutex);
+ p = snd_create_proc_entry(entry->name, entry->mode, root);
+ if (!p) {
+ up(&info_mutex);
+ return -ENOMEM;
+ }
+ p->owner = entry->module;
+ if (!S_ISDIR(entry->mode))
+ p->proc_fops = &snd_info_entry_operations;
+ p->size = entry->size;
+ p->data = entry;
+ entry->p = p;
+ up(&info_mutex);
+ return 0;
+}
+
+/**
+ * snd_info_unregister - de-register the info entry
+ * @entry: the info entry
+ *
+ * De-registers the info entry and releases the instance.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_info_unregister(snd_info_entry_t * entry)
+{
+ struct proc_dir_entry *root;
+
+ snd_assert(entry != NULL && entry->p != NULL, return -ENXIO);
+ root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
+ snd_assert(root, return -ENXIO);
+ down(&info_mutex);
+ snd_remove_proc_entry(root, entry->p);
+ up(&info_mutex);
+ snd_info_free_entry(entry);
+ return 0;
+}
+
+/*
+
+ */
+
+static snd_info_entry_t *snd_info_version_entry = NULL;
+
+static void snd_info_version_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+ snd_iprintf(buffer,
+ "Advanced Linux Sound Architecture Driver Version "
+ CONFIG_SND_VERSION CONFIG_SND_DATE ".\n"
+ );
+}
+
+static int __init snd_info_version_init(void)
+{
+ snd_info_entry_t *entry;
+
+ entry = snd_info_create_module_entry(THIS_MODULE, "version", NULL);
+ if (entry == NULL)
+ return -ENOMEM;
+ entry->c.text.read_size = 256;
+ entry->c.text.read = snd_info_version_read;
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ return -ENOMEM;
+ }
+ snd_info_version_entry = entry;
+ return 0;
+}
+
+static int __exit snd_info_version_done(void)
+{
+ if (snd_info_version_entry)
+ snd_info_unregister(snd_info_version_entry);
+ return 0;
+}
+
+#endif /* CONFIG_PROC_FS */
diff --git a/sound/core/info_oss.c b/sound/core/info_oss.c
new file mode 100644
index 00000000000..f9e4ce44345
--- /dev/null
+++ b/sound/core/info_oss.c
@@ -0,0 +1,137 @@
+/*
+ * Information interface for ALSA driver
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/minors.h>
+#include <sound/info.h>
+#include <sound/version.h>
+#include <linux/utsname.h>
+
+#if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS)
+
+/*
+ * OSS compatible part
+ */
+
+static DECLARE_MUTEX(strings);
+static char *snd_sndstat_strings[SNDRV_CARDS][SNDRV_OSS_INFO_DEV_COUNT];
+static snd_info_entry_t *snd_sndstat_proc_entry;
+
+int snd_oss_info_register(int dev, int num, char *string)
+{
+ char *x;
+
+ snd_assert(dev >= 0 && dev < SNDRV_OSS_INFO_DEV_COUNT, return -ENXIO);
+ snd_assert(num >= 0 && num < SNDRV_CARDS, return -ENXIO);
+ down(&strings);
+ if (string == NULL) {
+ if ((x = snd_sndstat_strings[num][dev]) != NULL) {
+ kfree(x);
+ x = NULL;
+ }
+ } else {
+ x = snd_kmalloc_strdup(string, GFP_KERNEL);
+ if (x == NULL) {
+ up(&strings);
+ return -ENOMEM;
+ }
+ }
+ snd_sndstat_strings[num][dev] = x;
+ up(&strings);
+ return 0;
+}
+
+extern void snd_card_info_read_oss(snd_info_buffer_t * buffer);
+
+static int snd_sndstat_show_strings(snd_info_buffer_t * buf, char *id, int dev)
+{
+ int idx, ok = -1;
+ char *str;
+
+ snd_iprintf(buf, "\n%s:", id);
+ down(&strings);
+ for (idx = 0; idx < SNDRV_CARDS; idx++) {
+ str = snd_sndstat_strings[idx][dev];
+ if (str) {
+ if (ok < 0) {
+ snd_iprintf(buf, "\n");
+ ok++;
+ }
+ snd_iprintf(buf, "%i: %s\n", idx, str);
+ }
+ }
+ up(&strings);
+ if (ok < 0)
+ snd_iprintf(buf, " NOT ENABLED IN CONFIG\n");
+ return ok;
+}
+
+static void snd_sndstat_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+ snd_iprintf(buffer, "Sound Driver:3.8.1a-980706 (ALSA v" CONFIG_SND_VERSION " emulation code)\n");
+ snd_iprintf(buffer, "Kernel: %s %s %s %s %s\n",
+ system_utsname.sysname,
+ system_utsname.nodename,
+ system_utsname.release,
+ system_utsname.version,
+ system_utsname.machine);
+ snd_iprintf(buffer, "Config options: 0\n");
+ snd_iprintf(buffer, "\nInstalled drivers: \n");
+ snd_iprintf(buffer, "Type 10: ALSA emulation\n");
+ snd_iprintf(buffer, "\nCard config: \n");
+ snd_card_info_read_oss(buffer);
+ snd_sndstat_show_strings(buffer, "Audio devices", SNDRV_OSS_INFO_DEV_AUDIO);
+ snd_sndstat_show_strings(buffer, "Synth devices", SNDRV_OSS_INFO_DEV_SYNTH);
+ snd_sndstat_show_strings(buffer, "Midi devices", SNDRV_OSS_INFO_DEV_MIDI);
+ snd_sndstat_show_strings(buffer, "Timers", SNDRV_OSS_INFO_DEV_TIMERS);
+ snd_sndstat_show_strings(buffer, "Mixers", SNDRV_OSS_INFO_DEV_MIXERS);
+}
+
+int snd_info_minor_register(void)
+{
+ snd_info_entry_t *entry;
+
+ memset(snd_sndstat_strings, 0, sizeof(snd_sndstat_strings));
+ if ((entry = snd_info_create_module_entry(THIS_MODULE, "sndstat", snd_oss_root)) != NULL) {
+ entry->c.text.read_size = 2048;
+ entry->c.text.read = snd_sndstat_proc_read;
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ entry = NULL;
+ }
+ }
+ snd_sndstat_proc_entry = entry;
+ return 0;
+}
+
+int snd_info_minor_unregister(void)
+{
+ if (snd_sndstat_proc_entry) {
+ snd_info_unregister(snd_sndstat_proc_entry);
+ snd_sndstat_proc_entry = NULL;
+ }
+ return 0;
+}
+
+#endif /* CONFIG_SND_OSSEMUL */
diff --git a/sound/core/init.c b/sound/core/init.c
new file mode 100644
index 00000000000..3f1fa8eabb7
--- /dev/null
+++ b/sound/core/init.c
@@ -0,0 +1,887 @@
+/*
+ * Initialization routines
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/file.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/ctype.h>
+#include <linux/pci.h>
+#include <linux/pm.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/info.h>
+
+struct snd_shutdown_f_ops {
+ struct file_operations f_ops;
+ struct snd_shutdown_f_ops *next;
+};
+
+unsigned int snd_cards_lock = 0; /* locked for registering/using */
+snd_card_t *snd_cards[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = NULL};
+DEFINE_RWLOCK(snd_card_rwlock);
+
+#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
+int (*snd_mixer_oss_notify_callback)(snd_card_t *card, int free_flag);
+#endif
+
+static void snd_card_id_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+ snd_iprintf(buffer, "%s\n", entry->card->id);
+}
+
+static void snd_card_free_thread(void * __card);
+
+/**
+ * snd_card_new - create and initialize a soundcard structure
+ * @idx: card index (address) [0 ... (SNDRV_CARDS-1)]
+ * @xid: card identification (ASCII string)
+ * @module: top level module for locking
+ * @extra_size: allocate this extra size after the main soundcard structure
+ *
+ * Creates and initializes a soundcard structure.
+ *
+ * Returns kmallocated snd_card_t structure. Creates the ALSA control interface
+ * (which is blocked until snd_card_register function is called).
+ */
+snd_card_t *snd_card_new(int idx, const char *xid,
+ struct module *module, int extra_size)
+{
+ snd_card_t *card;
+ int err;
+
+ if (extra_size < 0)
+ extra_size = 0;
+ card = kcalloc(1, sizeof(*card) + extra_size, GFP_KERNEL);
+ if (card == NULL)
+ return NULL;
+ if (xid) {
+ if (!snd_info_check_reserved_words(xid))
+ goto __error;
+ strlcpy(card->id, xid, sizeof(card->id));
+ }
+ err = 0;
+ write_lock(&snd_card_rwlock);
+ if (idx < 0) {
+ int idx2;
+ for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++)
+ if (~snd_cards_lock & idx & 1<<idx2) {
+ idx = idx2;
+ if (idx >= snd_ecards_limit)
+ snd_ecards_limit = idx + 1;
+ break;
+ }
+ } else if (idx < snd_ecards_limit) {
+ if (snd_cards_lock & (1 << idx))
+ err = -ENODEV; /* invalid */
+ } else if (idx < SNDRV_CARDS)
+ snd_ecards_limit = idx + 1; /* increase the limit */
+ else
+ err = -ENODEV;
+ if (idx < 0 || err < 0) {
+ write_unlock(&snd_card_rwlock);
+ snd_printk(KERN_ERR "cannot find the slot for index %d (range 0-%i)\n", idx, snd_ecards_limit - 1);
+ goto __error;
+ }
+ snd_cards_lock |= 1 << idx; /* lock it */
+ write_unlock(&snd_card_rwlock);
+ card->number = idx;
+ card->module = module;
+ INIT_LIST_HEAD(&card->devices);
+ init_rwsem(&card->controls_rwsem);
+ rwlock_init(&card->ctl_files_rwlock);
+ INIT_LIST_HEAD(&card->controls);
+ INIT_LIST_HEAD(&card->ctl_files);
+ spin_lock_init(&card->files_lock);
+ init_waitqueue_head(&card->shutdown_sleep);
+ INIT_WORK(&card->free_workq, snd_card_free_thread, card);
+#ifdef CONFIG_PM
+ init_MUTEX(&card->power_lock);
+ init_waitqueue_head(&card->power_sleep);
+#endif
+ /* the control interface cannot be accessed from the user space until */
+ /* snd_cards_bitmask and snd_cards are set with snd_card_register */
+ if ((err = snd_ctl_create(card)) < 0) {
+ snd_printd("unable to register control minors\n");
+ goto __error;
+ }
+ if ((err = snd_info_card_create(card)) < 0) {
+ snd_printd("unable to create card info\n");
+ goto __error_ctl;
+ }
+ if (extra_size > 0)
+ card->private_data = (char *)card + sizeof(snd_card_t);
+ return card;
+
+ __error_ctl:
+ snd_device_free_all(card, SNDRV_DEV_CMD_PRE);
+ __error:
+ kfree(card);
+ return NULL;
+}
+
+static unsigned int snd_disconnect_poll(struct file * file, poll_table * wait)
+{
+ return POLLERR | POLLNVAL;
+}
+
+/**
+ * snd_card_disconnect - disconnect all APIs from the file-operations (user space)
+ * @card: soundcard structure
+ *
+ * Disconnects all APIs from the file-operations (user space).
+ *
+ * Returns zero, otherwise a negative error code.
+ *
+ * Note: The current implementation replaces all active file->f_op with special
+ * dummy file operations (they do nothing except release).
+ */
+int snd_card_disconnect(snd_card_t * card)
+{
+ struct snd_monitor_file *mfile;
+ struct file *file;
+ struct snd_shutdown_f_ops *s_f_ops;
+ struct file_operations *f_ops, *old_f_ops;
+ int err;
+
+ spin_lock(&card->files_lock);
+ if (card->shutdown) {
+ spin_unlock(&card->files_lock);
+ return 0;
+ }
+ card->shutdown = 1;
+ spin_unlock(&card->files_lock);
+
+ /* phase 1: disable fops (user space) operations for ALSA API */
+ write_lock(&snd_card_rwlock);
+ snd_cards[card->number] = NULL;
+ write_unlock(&snd_card_rwlock);
+
+ /* phase 2: replace file->f_op with special dummy operations */
+
+ spin_lock(&card->files_lock);
+ mfile = card->files;
+ while (mfile) {
+ file = mfile->file;
+
+ /* it's critical part, use endless loop */
+ /* we have no room to fail */
+ s_f_ops = kmalloc(sizeof(struct snd_shutdown_f_ops), GFP_ATOMIC);
+ if (s_f_ops == NULL)
+ panic("Atomic allocation failed for snd_shutdown_f_ops!");
+
+ f_ops = &s_f_ops->f_ops;
+
+ memset(f_ops, 0, sizeof(*f_ops));
+ f_ops->owner = file->f_op->owner;
+ f_ops->release = file->f_op->release;
+ f_ops->poll = snd_disconnect_poll;
+
+ s_f_ops->next = card->s_f_ops;
+ card->s_f_ops = s_f_ops;
+
+ f_ops = fops_get(f_ops);
+
+ old_f_ops = file->f_op;
+ file->f_op = f_ops; /* must be atomic */
+ fops_put(old_f_ops);
+
+ mfile = mfile->next;
+ }
+ spin_unlock(&card->files_lock);
+
+ /* phase 3: notify all connected devices about disconnection */
+ /* at this point, they cannot respond to any calls except release() */
+
+#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
+ if (snd_mixer_oss_notify_callback)
+ snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_DISCONNECT);
+#endif
+
+ /* notify all devices that we are disconnected */
+ err = snd_device_disconnect_all(card);
+ if (err < 0)
+ snd_printk(KERN_ERR "not all devices for card %i can be disconnected\n", card->number);
+
+ return 0;
+}
+
+#if defined(CONFIG_PM) && defined(CONFIG_SND_GENERIC_PM)
+static void snd_generic_device_unregister(struct snd_generic_device *dev);
+#endif
+
+/**
+ * snd_card_free - frees given soundcard structure
+ * @card: soundcard structure
+ *
+ * This function releases the soundcard structure and the all assigned
+ * devices automatically. That is, you don't have to release the devices
+ * by yourself.
+ *
+ * Returns zero. Frees all associated devices and frees the control
+ * interface associated to given soundcard.
+ */
+int snd_card_free(snd_card_t * card)
+{
+ struct snd_shutdown_f_ops *s_f_ops;
+
+ if (card == NULL)
+ return -EINVAL;
+ write_lock(&snd_card_rwlock);
+ snd_cards[card->number] = NULL;
+ write_unlock(&snd_card_rwlock);
+
+#ifdef CONFIG_PM
+ wake_up(&card->power_sleep);
+#ifdef CONFIG_SND_GENERIC_PM
+ if (card->pm_dev) {
+ snd_generic_device_unregister(card->pm_dev);
+ card->pm_dev = NULL;
+ }
+#endif
+#endif
+
+ /* wait, until all devices are ready for the free operation */
+ wait_event(card->shutdown_sleep, card->files == NULL);
+
+#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
+ if (snd_mixer_oss_notify_callback)
+ snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE);
+#endif
+ if (snd_device_free_all(card, SNDRV_DEV_CMD_PRE) < 0) {
+ snd_printk(KERN_ERR "unable to free all devices (pre)\n");
+ /* Fatal, but this situation should never occur */
+ }
+ if (snd_device_free_all(card, SNDRV_DEV_CMD_NORMAL) < 0) {
+ snd_printk(KERN_ERR "unable to free all devices (normal)\n");
+ /* Fatal, but this situation should never occur */
+ }
+ if (snd_device_free_all(card, SNDRV_DEV_CMD_POST) < 0) {
+ snd_printk(KERN_ERR "unable to free all devices (post)\n");
+ /* Fatal, but this situation should never occur */
+ }
+ if (card->private_free)
+ card->private_free(card);
+ if (card->proc_id)
+ snd_info_unregister(card->proc_id);
+ if (snd_info_card_free(card) < 0) {
+ snd_printk(KERN_WARNING "unable to free card info\n");
+ /* Not fatal error */
+ }
+ while (card->s_f_ops) {
+ s_f_ops = card->s_f_ops;
+ card->s_f_ops = s_f_ops->next;
+ kfree(s_f_ops);
+ }
+ write_lock(&snd_card_rwlock);
+ snd_cards_lock &= ~(1 << card->number);
+ write_unlock(&snd_card_rwlock);
+ kfree(card);
+ return 0;
+}
+
+static void snd_card_free_thread(void * __card)
+{
+ snd_card_t *card = __card;
+ struct module * module = card->module;
+
+ if (!try_module_get(module)) {
+ snd_printk(KERN_ERR "unable to lock toplevel module for card %i in free thread\n", card->number);
+ module = NULL;
+ }
+
+ snd_card_free(card);
+
+ module_put(module);
+}
+
+/**
+ * snd_card_free_in_thread - call snd_card_free() in thread
+ * @card: soundcard structure
+ *
+ * This function schedules the call of snd_card_free() function in a
+ * work queue. When all devices are released (non-busy), the work
+ * is woken up and calls snd_card_free().
+ *
+ * When a card can be disconnected at any time by hotplug service,
+ * this function should be used in disconnect (or detach) callback
+ * instead of calling snd_card_free() directly.
+ *
+ * Returns - zero otherwise a negative error code if the start of thread failed.
+ */
+int snd_card_free_in_thread(snd_card_t * card)
+{
+ if (card->files == NULL) {
+ snd_card_free(card);
+ return 0;
+ }
+
+ if (schedule_work(&card->free_workq))
+ return 0;
+
+ snd_printk(KERN_ERR "schedule_work() failed in snd_card_free_in_thread for card %i\n", card->number);
+ /* try to free the structure immediately */
+ snd_card_free(card);
+ return -EFAULT;
+}
+
+static void choose_default_id(snd_card_t * card)
+{
+ int i, len, idx_flag = 0, loops = 8;
+ char *id, *spos;
+
+ id = spos = card->shortname;
+ while (*id != '\0') {
+ if (*id == ' ')
+ spos = id + 1;
+ id++;
+ }
+ id = card->id;
+ while (*spos != '\0' && !isalnum(*spos))
+ spos++;
+ if (isdigit(*spos))
+ *id++ = isalpha(card->shortname[0]) ? card->shortname[0] : 'D';
+ while (*spos != '\0' && (size_t)(id - card->id) < sizeof(card->id) - 1) {
+ if (isalnum(*spos))
+ *id++ = *spos;
+ spos++;
+ }
+ *id = '\0';
+
+ id = card->id;
+
+ if (*id == '\0')
+ strcpy(id, "default");
+
+ while (1) {
+ if (loops-- == 0) {
+ snd_printk(KERN_ERR "unable to choose default card id (%s)\n", id);
+ strcpy(card->id, card->proc_root->name);
+ return;
+ }
+ if (!snd_info_check_reserved_words(id))
+ goto __change;
+ for (i = 0; i < snd_ecards_limit; i++) {
+ if (snd_cards[i] && !strcmp(snd_cards[i]->id, id))
+ goto __change;
+ }
+ break;
+
+ __change:
+ len = strlen(id);
+ if (idx_flag)
+ id[len-1]++;
+ else if ((size_t)len <= sizeof(card->id) - 3) {
+ strcat(id, "_1");
+ idx_flag++;
+ } else {
+ spos = id + len - 2;
+ if ((size_t)len <= sizeof(card->id) - 2)
+ spos++;
+ *spos++ = '_';
+ *spos++ = '1';
+ *spos++ = '\0';
+ idx_flag++;
+ }
+ }
+}
+
+/**
+ * snd_card_register - register the soundcard
+ * @card: soundcard structure
+ *
+ * This function registers all the devices assigned to the soundcard.
+ * Until calling this, the ALSA control interface is blocked from the
+ * external accesses. Thus, you should call this function at the end
+ * of the initialization of the card.
+ *
+ * Returns zero otherwise a negative error code if the registrain failed.
+ */
+int snd_card_register(snd_card_t * card)
+{
+ int err;
+ snd_info_entry_t *entry;
+
+ snd_runtime_check(card != NULL, return -EINVAL);
+ if ((err = snd_device_register_all(card)) < 0)
+ return err;
+ write_lock(&snd_card_rwlock);
+ if (snd_cards[card->number]) {
+ /* already registered */
+ write_unlock(&snd_card_rwlock);
+ return 0;
+ }
+ if (card->id[0] == '\0')
+ choose_default_id(card);
+ snd_cards[card->number] = card;
+ write_unlock(&snd_card_rwlock);
+ if ((err = snd_info_card_register(card)) < 0) {
+ snd_printd("unable to create card info\n");
+ goto __skip_info;
+ }
+ if ((entry = snd_info_create_card_entry(card, "id", card->proc_root)) == NULL) {
+ snd_printd("unable to create card entry\n");
+ goto __skip_info;
+ }
+ entry->c.text.read_size = PAGE_SIZE;
+ entry->c.text.read = snd_card_id_read;
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ entry = NULL;
+ }
+ card->proc_id = entry;
+ __skip_info:
+#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
+ if (snd_mixer_oss_notify_callback)
+ snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);
+#endif
+ return 0;
+}
+
+static snd_info_entry_t *snd_card_info_entry = NULL;
+
+static void snd_card_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+ int idx, count;
+ snd_card_t *card;
+
+ for (idx = count = 0; idx < SNDRV_CARDS; idx++) {
+ read_lock(&snd_card_rwlock);
+ if ((card = snd_cards[idx]) != NULL) {
+ count++;
+ snd_iprintf(buffer, "%i [%-15s]: %s - %s\n",
+ idx,
+ card->id,
+ card->driver,
+ card->shortname);
+ snd_iprintf(buffer, " %s\n",
+ card->longname);
+ }
+ read_unlock(&snd_card_rwlock);
+ }
+ if (!count)
+ snd_iprintf(buffer, "--- no soundcards ---\n");
+}
+
+#if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS)
+
+void snd_card_info_read_oss(snd_info_buffer_t * buffer)
+{
+ int idx, count;
+ snd_card_t *card;
+
+ for (idx = count = 0; idx < SNDRV_CARDS; idx++) {
+ read_lock(&snd_card_rwlock);
+ if ((card = snd_cards[idx]) != NULL) {
+ count++;
+ snd_iprintf(buffer, "%s\n", card->longname);
+ }
+ read_unlock(&snd_card_rwlock);
+ }
+ if (!count) {
+ snd_iprintf(buffer, "--- no soundcards ---\n");
+ }
+}
+
+#endif
+
+#ifdef MODULE
+static snd_info_entry_t *snd_card_module_info_entry;
+static void snd_card_module_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+ int idx;
+ snd_card_t *card;
+
+ for (idx = 0; idx < SNDRV_CARDS; idx++) {
+ read_lock(&snd_card_rwlock);
+ if ((card = snd_cards[idx]) != NULL)
+ snd_iprintf(buffer, "%i %s\n", idx, card->module->name);
+ read_unlock(&snd_card_rwlock);
+ }
+}
+#endif
+
+int __init snd_card_info_init(void)
+{
+ snd_info_entry_t *entry;
+
+ entry = snd_info_create_module_entry(THIS_MODULE, "cards", NULL);
+ snd_runtime_check(entry != NULL, return -ENOMEM);
+ entry->c.text.read_size = PAGE_SIZE;
+ entry->c.text.read = snd_card_info_read;
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ return -ENOMEM;
+ }
+ snd_card_info_entry = entry;
+
+#ifdef MODULE
+ entry = snd_info_create_module_entry(THIS_MODULE, "modules", NULL);
+ if (entry) {
+ entry->c.text.read_size = PAGE_SIZE;
+ entry->c.text.read = snd_card_module_info_read;
+ if (snd_info_register(entry) < 0)
+ snd_info_free_entry(entry);
+ else
+ snd_card_module_info_entry = entry;
+ }
+#endif
+
+ return 0;
+}
+
+int __exit snd_card_info_done(void)
+{
+ if (snd_card_info_entry)
+ snd_info_unregister(snd_card_info_entry);
+#ifdef MODULE
+ if (snd_card_module_info_entry)
+ snd_info_unregister(snd_card_module_info_entry);
+#endif
+ return 0;
+}
+
+/**
+ * snd_component_add - add a component string
+ * @card: soundcard structure
+ * @component: the component id string
+ *
+ * This function adds the component id string to the supported list.
+ * The component can be referred from the alsa-lib.
+ *
+ * Returns zero otherwise a negative error code.
+ */
+
+int snd_component_add(snd_card_t *card, const char *component)
+{
+ char *ptr;
+ int len = strlen(component);
+
+ ptr = strstr(card->components, component);
+ if (ptr != NULL) {
+ if (ptr[len] == '\0' || ptr[len] == ' ') /* already there */
+ return 1;
+ }
+ if (strlen(card->components) + 1 + len + 1 > sizeof(card->components)) {
+ snd_BUG();
+ return -ENOMEM;
+ }
+ if (card->components[0] != '\0')
+ strcat(card->components, " ");
+ strcat(card->components, component);
+ return 0;
+}
+
+/**
+ * snd_card_file_add - add the file to the file list of the card
+ * @card: soundcard structure
+ * @file: file pointer
+ *
+ * This function adds the file to the file linked-list of the card.
+ * This linked-list is used to keep tracking the connection state,
+ * and to avoid the release of busy resources by hotplug.
+ *
+ * Returns zero or a negative error code.
+ */
+int snd_card_file_add(snd_card_t *card, struct file *file)
+{
+ struct snd_monitor_file *mfile;
+
+ mfile = kmalloc(sizeof(*mfile), GFP_KERNEL);
+ if (mfile == NULL)
+ return -ENOMEM;
+ mfile->file = file;
+ mfile->next = NULL;
+ spin_lock(&card->files_lock);
+ if (card->shutdown) {
+ spin_unlock(&card->files_lock);
+ kfree(mfile);
+ return -ENODEV;
+ }
+ mfile->next = card->files;
+ card->files = mfile;
+ spin_unlock(&card->files_lock);
+ return 0;
+}
+
+/**
+ * snd_card_file_remove - remove the file from the file list
+ * @card: soundcard structure
+ * @file: file pointer
+ *
+ * This function removes the file formerly added to the card via
+ * snd_card_file_add() function.
+ * If all files are removed and the release of the card is
+ * scheduled, it will wake up the the thread to call snd_card_free()
+ * (see snd_card_free_in_thread() function).
+ *
+ * Returns zero or a negative error code.
+ */
+int snd_card_file_remove(snd_card_t *card, struct file *file)
+{
+ struct snd_monitor_file *mfile, *pfile = NULL;
+
+ spin_lock(&card->files_lock);
+ mfile = card->files;
+ while (mfile) {
+ if (mfile->file == file) {
+ if (pfile)
+ pfile->next = mfile->next;
+ else
+ card->files = mfile->next;
+ break;
+ }
+ pfile = mfile;
+ mfile = mfile->next;
+ }
+ spin_unlock(&card->files_lock);
+ if (card->files == NULL)
+ wake_up(&card->shutdown_sleep);
+ if (!mfile) {
+ snd_printk(KERN_ERR "ALSA card file remove problem (%p)\n", file);
+ return -ENOENT;
+ }
+ kfree(mfile);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/**
+ * snd_power_wait - wait until the power-state is changed.
+ * @card: soundcard structure
+ * @power_state: expected power state
+ * @file: file structure for the O_NONBLOCK check (optional)
+ *
+ * Waits until the power-state is changed.
+ *
+ * Note: the power lock must be active before call.
+ */
+int snd_power_wait(snd_card_t *card, unsigned int power_state, struct file *file)
+{
+ wait_queue_t wait;
+ int result = 0;
+
+ /* fastpath */
+ if (snd_power_get_state(card) == power_state)
+ return 0;
+ init_waitqueue_entry(&wait, current);
+ add_wait_queue(&card->power_sleep, &wait);
+ while (1) {
+ if (card->shutdown) {
+ result = -ENODEV;
+ break;
+ }
+ if (snd_power_get_state(card) == power_state)
+ break;
+#if 0 /* block all devices */
+ if (file && (file->f_flags & O_NONBLOCK)) {
+ result = -EAGAIN;
+ break;
+ }
+#endif
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ snd_power_unlock(card);
+ schedule_timeout(30 * HZ);
+ snd_power_lock(card);
+ }
+ remove_wait_queue(&card->power_sleep, &wait);
+ return result;
+}
+
+/**
+ * snd_card_set_pm_callback - set the PCI power-management callbacks
+ * @card: soundcard structure
+ * @suspend: suspend callback function
+ * @resume: resume callback function
+ * @private_data: private data to pass to the callback functions
+ *
+ * Sets the power-management callback functions of the card.
+ * These callbacks are called from ALSA's common PCI suspend/resume
+ * handler and from the control API.
+ */
+int snd_card_set_pm_callback(snd_card_t *card,
+ int (*suspend)(snd_card_t *, pm_message_t),
+ int (*resume)(snd_card_t *),
+ void *private_data)
+{
+ card->pm_suspend = suspend;
+ card->pm_resume = resume;
+ card->pm_private_data = private_data;
+ return 0;
+}
+
+#ifdef CONFIG_SND_GENERIC_PM
+/*
+ * use platform_device for generic power-management without a proper bus
+ * (e.g. ISA)
+ */
+struct snd_generic_device {
+ struct platform_device pdev;
+ snd_card_t *card;
+};
+
+#define get_snd_generic_card(dev) container_of(to_platform_device(dev), struct snd_generic_device, pdev)->card
+
+#define SND_GENERIC_NAME "snd_generic_pm"
+
+static int snd_generic_suspend(struct device *dev, u32 state, u32 level);
+static int snd_generic_resume(struct device *dev, u32 level);
+
+static struct device_driver snd_generic_driver = {
+ .name = SND_GENERIC_NAME,
+ .bus = &platform_bus_type,
+ .suspend = snd_generic_suspend,
+ .resume = snd_generic_resume,
+};
+
+static int generic_driver_registered;
+
+static void generic_driver_unregister(void)
+{
+ if (generic_driver_registered) {
+ generic_driver_registered--;
+ if (! generic_driver_registered)
+ driver_unregister(&snd_generic_driver);
+ }
+}
+
+static struct snd_generic_device *snd_generic_device_register(snd_card_t *card)
+{
+ struct snd_generic_device *dev;
+
+ if (! generic_driver_registered) {
+ if (driver_register(&snd_generic_driver) < 0)
+ return NULL;
+ }
+ generic_driver_registered++;
+
+ dev = kcalloc(1, sizeof(*dev), GFP_KERNEL);
+ if (! dev) {
+ generic_driver_unregister();
+ return NULL;
+ }
+
+ dev->pdev.name = SND_GENERIC_NAME;
+ dev->pdev.id = card->number;
+ dev->card = card;
+ if (platform_device_register(&dev->pdev) < 0) {
+ kfree(dev);
+ generic_driver_unregister();
+ return NULL;
+ }
+ return dev;
+}
+
+static void snd_generic_device_unregister(struct snd_generic_device *dev)
+{
+ platform_device_unregister(&dev->pdev);
+ kfree(dev);
+ generic_driver_unregister();
+}
+
+/* suspend/resume callbacks for snd_generic platform device */
+static int snd_generic_suspend(struct device *dev, u32 state, u32 level)
+{
+ snd_card_t *card;
+
+ if (level != SUSPEND_DISABLE)
+ return 0;
+
+ card = get_snd_generic_card(dev);
+ if (card->power_state == SNDRV_CTL_POWER_D3hot)
+ return 0;
+ card->pm_suspend(card, PMSG_SUSPEND);
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+ return 0;
+}
+
+static int snd_generic_resume(struct device *dev, u32 level)
+{
+ snd_card_t *card;
+
+ if (level != RESUME_ENABLE)
+ return 0;
+
+ card = get_snd_generic_card(dev);
+ if (card->power_state == SNDRV_CTL_POWER_D0)
+ return 0;
+ card->pm_resume(card);
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+ return 0;
+}
+
+/**
+ * snd_card_set_generic_pm_callback - set the generic power-management callbacks
+ * @card: soundcard structure
+ * @suspend: suspend callback function
+ * @resume: resume callback function
+ * @private_data: private data to pass to the callback functions
+ *
+ * Registers the power-management and sets the lowlevel callbacks for
+ * the given card. These callbacks are called from the ALSA's common
+ * PM handler and from the control API.
+ */
+int snd_card_set_generic_pm_callback(snd_card_t *card,
+ int (*suspend)(snd_card_t *, pm_message_t),
+ int (*resume)(snd_card_t *),
+ void *private_data)
+{
+ card->pm_dev = snd_generic_device_register(card);
+ if (! card->pm_dev)
+ return -ENOMEM;
+ snd_card_set_pm_callback(card, suspend, resume, private_data);
+ return 0;
+}
+#endif /* CONFIG_SND_GENERIC_PM */
+
+#ifdef CONFIG_PCI
+int snd_card_pci_suspend(struct pci_dev *dev, pm_message_t state)
+{
+ snd_card_t *card = pci_get_drvdata(dev);
+ int err;
+ if (! card || ! card->pm_suspend)
+ return 0;
+ if (card->power_state == SNDRV_CTL_POWER_D3hot)
+ return 0;
+ err = card->pm_suspend(card, PMSG_SUSPEND);
+ pci_save_state(dev);
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+ return err;
+}
+
+int snd_card_pci_resume(struct pci_dev *dev)
+{
+ snd_card_t *card = pci_get_drvdata(dev);
+ if (! card || ! card->pm_resume)
+ return 0;
+ if (card->power_state == SNDRV_CTL_POWER_D0)
+ return 0;
+ /* restore the PCI config space */
+ pci_restore_state(dev);
+ card->pm_resume(card);
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+ return 0;
+}
+#endif
+
+#endif /* CONFIG_PM */
diff --git a/sound/core/isadma.c b/sound/core/isadma.c
new file mode 100644
index 00000000000..1a378951da5
--- /dev/null
+++ b/sound/core/isadma.c
@@ -0,0 +1,103 @@
+/*
+ * ISA DMA support functions
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ * Defining following add some delay. Maybe this helps for some broken
+ * ISA DMA controllers.
+ */
+
+#undef HAVE_REALLY_SLOW_DMA_CONTROLLER
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <asm/dma.h>
+
+/**
+ * snd_dma_program - program an ISA DMA transfer
+ * @dma: the dma number
+ * @addr: the physical address of the buffer
+ * @size: the DMA transfer size
+ * @mode: the DMA transfer mode, DMA_MODE_XXX
+ *
+ * Programs an ISA DMA transfer for the given buffer.
+ */
+void snd_dma_program(unsigned long dma,
+ unsigned long addr, unsigned int size,
+ unsigned short mode)
+{
+ unsigned long flags;
+
+ flags = claim_dma_lock();
+ disable_dma(dma);
+ clear_dma_ff(dma);
+ set_dma_mode(dma, mode);
+ set_dma_addr(dma, addr);
+ set_dma_count(dma, size);
+ if (!(mode & DMA_MODE_NO_ENABLE))
+ enable_dma(dma);
+ release_dma_lock(flags);
+}
+
+/**
+ * snd_dma_disable - stop the ISA DMA transfer
+ * @dma: the dma number
+ *
+ * Stops the ISA DMA transfer.
+ */
+void snd_dma_disable(unsigned long dma)
+{
+ unsigned long flags;
+
+ flags = claim_dma_lock();
+ clear_dma_ff(dma);
+ disable_dma(dma);
+ release_dma_lock(flags);
+}
+
+/**
+ * snd_dma_pointer - return the current pointer to DMA transfer buffer in bytes
+ * @dma: the dma number
+ * @size: the dma transfer size
+ *
+ * Returns the current pointer in DMA tranfer buffer in bytes
+ */
+unsigned int snd_dma_pointer(unsigned long dma, unsigned int size)
+{
+ unsigned long flags;
+ unsigned int result;
+
+ flags = claim_dma_lock();
+ clear_dma_ff(dma);
+ if (!isa_dma_bridge_buggy)
+ disable_dma(dma);
+ result = get_dma_residue(dma);
+ if (!isa_dma_bridge_buggy)
+ enable_dma(dma);
+ release_dma_lock(flags);
+#ifdef CONFIG_SND_DEBUG
+ if (result > size)
+ snd_printk(KERN_ERR "pointer (0x%x) for DMA #%ld is greater than transfer size (0x%x)\n", result, dma, size);
+#endif
+ if (result >= size || result == 0)
+ return 0;
+ else
+ return size - result;
+}
diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c
new file mode 100644
index 00000000000..344a83fd7c2
--- /dev/null
+++ b/sound/core/memalloc.c
@@ -0,0 +1,663 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Takashi Iwai <tiwai@suse.de>
+ *
+ * Generic memory allocators
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/dma-mapping.h>
+#include <linux/moduleparam.h>
+#include <asm/semaphore.h>
+#include <sound/memalloc.h>
+#ifdef CONFIG_SBUS
+#include <asm/sbus.h>
+#endif
+
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>, Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Memory allocator for ALSA system.");
+MODULE_LICENSE("GPL");
+
+
+#ifndef SNDRV_CARDS
+#define SNDRV_CARDS 8
+#endif
+
+/* FIXME: so far only some PCI devices have the preallocation table */
+#ifdef CONFIG_PCI
+static int enable[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1};
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable cards to allocate buffers.");
+#endif
+
+/*
+ */
+
+void *snd_malloc_sgbuf_pages(struct device *device,
+ size_t size, struct snd_dma_buffer *dmab,
+ size_t *res_size);
+int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab);
+
+/*
+ */
+
+static DECLARE_MUTEX(list_mutex);
+static LIST_HEAD(mem_list_head);
+
+/* buffer preservation list */
+struct snd_mem_list {
+ struct snd_dma_buffer buffer;
+ unsigned int id;
+ struct list_head list;
+};
+
+/* id for pre-allocated buffers */
+#define SNDRV_DMA_DEVICE_UNUSED (unsigned int)-1
+
+#ifdef CONFIG_SND_DEBUG
+#define __ASTRING__(x) #x
+#define snd_assert(expr, args...) do {\
+ if (!(expr)) {\
+ printk(KERN_ERR "snd-malloc: BUG? (%s) (called from %p)\n", __ASTRING__(expr), __builtin_return_address(0));\
+ args;\
+ }\
+} while (0)
+#else
+#define snd_assert(expr, args...) /**/
+#endif
+
+/*
+ * Hacks
+ */
+
+#if defined(__i386__) || defined(__ppc__) || defined(__x86_64__)
+/*
+ * A hack to allocate large buffers via dma_alloc_coherent()
+ *
+ * since dma_alloc_coherent always tries GFP_DMA when the requested
+ * pci memory region is below 32bit, it happens quite often that even
+ * 2 order of pages cannot be allocated.
+ *
+ * so in the following, we allocate at first without dma_mask, so that
+ * allocation will be done without GFP_DMA. if the area doesn't match
+ * with the requested region, then realloate with the original dma_mask
+ * again.
+ *
+ * Really, we want to move this type of thing into dma_alloc_coherent()
+ * so dma_mask doesn't have to be messed with.
+ */
+
+static void *snd_dma_hack_alloc_coherent(struct device *dev, size_t size,
+ dma_addr_t *dma_handle, int flags)
+{
+ void *ret;
+ u64 dma_mask, coherent_dma_mask;
+
+ if (dev == NULL || !dev->dma_mask)
+ return dma_alloc_coherent(dev, size, dma_handle, flags);
+ dma_mask = *dev->dma_mask;
+ coherent_dma_mask = dev->coherent_dma_mask;
+ *dev->dma_mask = 0xffffffff; /* do without masking */
+ dev->coherent_dma_mask = 0xffffffff; /* do without masking */
+ ret = dma_alloc_coherent(dev, size, dma_handle, flags);
+ *dev->dma_mask = dma_mask; /* restore */
+ dev->coherent_dma_mask = coherent_dma_mask; /* restore */
+ if (ret) {
+ /* obtained address is out of range? */
+ if (((unsigned long)*dma_handle + size - 1) & ~dma_mask) {
+ /* reallocate with the proper mask */
+ dma_free_coherent(dev, size, ret, *dma_handle);
+ ret = dma_alloc_coherent(dev, size, dma_handle, flags);
+ }
+ } else {
+ /* wish to success now with the proper mask... */
+ if (dma_mask != 0xffffffffUL) {
+ /* allocation with GFP_ATOMIC to avoid the long stall */
+ flags &= ~GFP_KERNEL;
+ flags |= GFP_ATOMIC;
+ ret = dma_alloc_coherent(dev, size, dma_handle, flags);
+ }
+ }
+ return ret;
+}
+
+/* redefine dma_alloc_coherent for some architectures */
+#undef dma_alloc_coherent
+#define dma_alloc_coherent snd_dma_hack_alloc_coherent
+
+#endif /* arch */
+
+#if ! defined(__arm__)
+#define NEED_RESERVE_PAGES
+#endif
+
+/*
+ *
+ * Generic memory allocators
+ *
+ */
+
+static long snd_allocated_pages; /* holding the number of allocated pages */
+
+static inline void inc_snd_pages(int order)
+{
+ snd_allocated_pages += 1 << order;
+}
+
+static inline void dec_snd_pages(int order)
+{
+ snd_allocated_pages -= 1 << order;
+}
+
+static void mark_pages(struct page *page, int order)
+{
+ struct page *last_page = page + (1 << order);
+ while (page < last_page)
+ SetPageReserved(page++);
+}
+
+static void unmark_pages(struct page *page, int order)
+{
+ struct page *last_page = page + (1 << order);
+ while (page < last_page)
+ ClearPageReserved(page++);
+}
+
+/**
+ * snd_malloc_pages - allocate pages with the given size
+ * @size: the size to allocate in bytes
+ * @gfp_flags: the allocation conditions, GFP_XXX
+ *
+ * Allocates the physically contiguous pages with the given size.
+ *
+ * Returns the pointer of the buffer, or NULL if no enoguh memory.
+ */
+void *snd_malloc_pages(size_t size, unsigned int gfp_flags)
+{
+ int pg;
+ void *res;
+
+ snd_assert(size > 0, return NULL);
+ snd_assert(gfp_flags != 0, return NULL);
+ pg = get_order(size);
+ if ((res = (void *) __get_free_pages(gfp_flags, pg)) != NULL) {
+ mark_pages(virt_to_page(res), pg);
+ inc_snd_pages(pg);
+ }
+ return res;
+}
+
+/**
+ * snd_free_pages - release the pages
+ * @ptr: the buffer pointer to release
+ * @size: the allocated buffer size
+ *
+ * Releases the buffer allocated via snd_malloc_pages().
+ */
+void snd_free_pages(void *ptr, size_t size)
+{
+ int pg;
+
+ if (ptr == NULL)
+ return;
+ pg = get_order(size);
+ dec_snd_pages(pg);
+ unmark_pages(virt_to_page(ptr), pg);
+ free_pages((unsigned long) ptr, pg);
+}
+
+/*
+ *
+ * Bus-specific memory allocators
+ *
+ */
+
+/* allocate the coherent DMA pages */
+static void *snd_malloc_dev_pages(struct device *dev, size_t size, dma_addr_t *dma)
+{
+ int pg;
+ void *res;
+ unsigned int gfp_flags;
+
+ snd_assert(size > 0, return NULL);
+ snd_assert(dma != NULL, return NULL);
+ pg = get_order(size);
+ gfp_flags = GFP_KERNEL
+ | __GFP_NORETRY /* don't trigger OOM-killer */
+ | __GFP_NOWARN; /* no stack trace print - this call is non-critical */
+ res = dma_alloc_coherent(dev, PAGE_SIZE << pg, dma, gfp_flags);
+ if (res != NULL) {
+#ifdef NEED_RESERVE_PAGES
+ mark_pages(virt_to_page(res), pg); /* should be dma_to_page() */
+#endif
+ inc_snd_pages(pg);
+ }
+
+ return res;
+}
+
+/* free the coherent DMA pages */
+static void snd_free_dev_pages(struct device *dev, size_t size, void *ptr,
+ dma_addr_t dma)
+{
+ int pg;
+
+ if (ptr == NULL)
+ return;
+ pg = get_order(size);
+ dec_snd_pages(pg);
+#ifdef NEED_RESERVE_PAGES
+ unmark_pages(virt_to_page(ptr), pg); /* should be dma_to_page() */
+#endif
+ dma_free_coherent(dev, PAGE_SIZE << pg, ptr, dma);
+}
+
+#ifdef CONFIG_SBUS
+
+static void *snd_malloc_sbus_pages(struct device *dev, size_t size,
+ dma_addr_t *dma_addr)
+{
+ struct sbus_dev *sdev = (struct sbus_dev *)dev;
+ int pg;
+ void *res;
+
+ snd_assert(size > 0, return NULL);
+ snd_assert(dma_addr != NULL, return NULL);
+ pg = get_order(size);
+ res = sbus_alloc_consistent(sdev, PAGE_SIZE * (1 << pg), dma_addr);
+ if (res != NULL)
+ inc_snd_pages(pg);
+ return res;
+}
+
+static void snd_free_sbus_pages(struct device *dev, size_t size,
+ void *ptr, dma_addr_t dma_addr)
+{
+ struct sbus_dev *sdev = (struct sbus_dev *)dev;
+ int pg;
+
+ if (ptr == NULL)
+ return;
+ pg = get_order(size);
+ dec_snd_pages(pg);
+ sbus_free_consistent(sdev, PAGE_SIZE * (1 << pg), ptr, dma_addr);
+}
+
+#endif /* CONFIG_SBUS */
+
+/*
+ *
+ * ALSA generic memory management
+ *
+ */
+
+
+/**
+ * snd_dma_alloc_pages - allocate the buffer area according to the given type
+ * @type: the DMA buffer type
+ * @device: the device pointer
+ * @size: the buffer size to allocate
+ * @dmab: buffer allocation record to store the allocated data
+ *
+ * Calls the memory-allocator function for the corresponding
+ * buffer type.
+ *
+ * Returns zero if the buffer with the given size is allocated successfuly,
+ * other a negative value at error.
+ */
+int snd_dma_alloc_pages(int type, struct device *device, size_t size,
+ struct snd_dma_buffer *dmab)
+{
+ snd_assert(size > 0, return -ENXIO);
+ snd_assert(dmab != NULL, return -ENXIO);
+
+ dmab->dev.type = type;
+ dmab->dev.dev = device;
+ dmab->bytes = 0;
+ switch (type) {
+ case SNDRV_DMA_TYPE_CONTINUOUS:
+ dmab->area = snd_malloc_pages(size, (unsigned long)device);
+ dmab->addr = 0;
+ break;
+#ifdef CONFIG_SBUS
+ case SNDRV_DMA_TYPE_SBUS:
+ dmab->area = snd_malloc_sbus_pages(device, size, &dmab->addr);
+ break;
+#endif
+ case SNDRV_DMA_TYPE_DEV:
+ dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr);
+ break;
+ case SNDRV_DMA_TYPE_DEV_SG:
+ snd_malloc_sgbuf_pages(device, size, dmab, NULL);
+ break;
+ default:
+ printk(KERN_ERR "snd-malloc: invalid device type %d\n", type);
+ dmab->area = NULL;
+ dmab->addr = 0;
+ return -ENXIO;
+ }
+ if (! dmab->area)
+ return -ENOMEM;
+ dmab->bytes = size;
+ return 0;
+}
+
+/**
+ * snd_dma_alloc_pages_fallback - allocate the buffer area according to the given type with fallback
+ * @type: the DMA buffer type
+ * @device: the device pointer
+ * @size: the buffer size to allocate
+ * @dmab: buffer allocation record to store the allocated data
+ *
+ * Calls the memory-allocator function for the corresponding
+ * buffer type. When no space is left, this function reduces the size and
+ * tries to allocate again. The size actually allocated is stored in
+ * res_size argument.
+ *
+ * Returns zero if the buffer with the given size is allocated successfuly,
+ * other a negative value at error.
+ */
+int snd_dma_alloc_pages_fallback(int type, struct device *device, size_t size,
+ struct snd_dma_buffer *dmab)
+{
+ int err;
+
+ snd_assert(size > 0, return -ENXIO);
+ snd_assert(dmab != NULL, return -ENXIO);
+
+ while ((err = snd_dma_alloc_pages(type, device, size, dmab)) < 0) {
+ if (err != -ENOMEM)
+ return err;
+ size >>= 1;
+ if (size <= PAGE_SIZE)
+ return -ENOMEM;
+ }
+ if (! dmab->area)
+ return -ENOMEM;
+ return 0;
+}
+
+
+/**
+ * snd_dma_free_pages - release the allocated buffer
+ * @dmab: the buffer allocation record to release
+ *
+ * Releases the allocated buffer via snd_dma_alloc_pages().
+ */
+void snd_dma_free_pages(struct snd_dma_buffer *dmab)
+{
+ switch (dmab->dev.type) {
+ case SNDRV_DMA_TYPE_CONTINUOUS:
+ snd_free_pages(dmab->area, dmab->bytes);
+ break;
+#ifdef CONFIG_SBUS
+ case SNDRV_DMA_TYPE_SBUS:
+ snd_free_sbus_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
+ break;
+#endif
+ case SNDRV_DMA_TYPE_DEV:
+ snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
+ break;
+ case SNDRV_DMA_TYPE_DEV_SG:
+ snd_free_sgbuf_pages(dmab);
+ break;
+ default:
+ printk(KERN_ERR "snd-malloc: invalid device type %d\n", dmab->dev.type);
+ }
+}
+
+
+/**
+ * snd_dma_get_reserved - get the reserved buffer for the given device
+ * @dmab: the buffer allocation record to store
+ * @id: the buffer id
+ *
+ * Looks for the reserved-buffer list and re-uses if the same buffer
+ * is found in the list. When the buffer is found, it's removed from the free list.
+ *
+ * Returns the size of buffer if the buffer is found, or zero if not found.
+ */
+size_t snd_dma_get_reserved_buf(struct snd_dma_buffer *dmab, unsigned int id)
+{
+ struct list_head *p;
+ struct snd_mem_list *mem;
+
+ snd_assert(dmab, return 0);
+
+ down(&list_mutex);
+ list_for_each(p, &mem_list_head) {
+ mem = list_entry(p, struct snd_mem_list, list);
+ if (mem->id == id &&
+ ! memcmp(&mem->buffer.dev, &dmab->dev, sizeof(dmab->dev))) {
+ list_del(p);
+ *dmab = mem->buffer;
+ kfree(mem);
+ up(&list_mutex);
+ return dmab->bytes;
+ }
+ }
+ up(&list_mutex);
+ return 0;
+}
+
+/**
+ * snd_dma_reserve_buf - reserve the buffer
+ * @dmab: the buffer to reserve
+ * @id: the buffer id
+ *
+ * Reserves the given buffer as a reserved buffer.
+ *
+ * Returns zero if successful, or a negative code at error.
+ */
+int snd_dma_reserve_buf(struct snd_dma_buffer *dmab, unsigned int id)
+{
+ struct snd_mem_list *mem;
+
+ snd_assert(dmab, return -EINVAL);
+ mem = kmalloc(sizeof(*mem), GFP_KERNEL);
+ if (! mem)
+ return -ENOMEM;
+ down(&list_mutex);
+ mem->buffer = *dmab;
+ mem->id = id;
+ list_add_tail(&mem->list, &mem_list_head);
+ up(&list_mutex);
+ return 0;
+}
+
+/*
+ * purge all reserved buffers
+ */
+static void free_all_reserved_pages(void)
+{
+ struct list_head *p;
+ struct snd_mem_list *mem;
+
+ down(&list_mutex);
+ while (! list_empty(&mem_list_head)) {
+ p = mem_list_head.next;
+ mem = list_entry(p, struct snd_mem_list, list);
+ list_del(p);
+ snd_dma_free_pages(&mem->buffer);
+ kfree(mem);
+ }
+ up(&list_mutex);
+}
+
+
+
+/*
+ * allocation of buffers for pre-defined devices
+ */
+
+#ifdef CONFIG_PCI
+/* FIXME: for pci only - other bus? */
+struct prealloc_dev {
+ unsigned short vendor;
+ unsigned short device;
+ unsigned long dma_mask;
+ unsigned int size;
+ unsigned int buffers;
+};
+
+#define HAMMERFALL_BUFFER_SIZE (16*1024*4*(26+1)+0x10000)
+
+static struct prealloc_dev prealloc_devices[] __initdata = {
+ {
+ /* hammerfall */
+ .vendor = 0x10ee,
+ .device = 0x3fc4,
+ .dma_mask = 0xffffffff,
+ .size = HAMMERFALL_BUFFER_SIZE,
+ .buffers = 2
+ },
+ {
+ /* HDSP */
+ .vendor = 0x10ee,
+ .device = 0x3fc5,
+ .dma_mask = 0xffffffff,
+ .size = HAMMERFALL_BUFFER_SIZE,
+ .buffers = 2
+ },
+ { }, /* terminator */
+};
+
+static void __init preallocate_cards(void)
+{
+ struct pci_dev *pci = NULL;
+ int card;
+
+ card = 0;
+
+ while ((pci = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pci)) != NULL) {
+ struct prealloc_dev *dev;
+ unsigned int i;
+ if (card >= SNDRV_CARDS)
+ break;
+ for (dev = prealloc_devices; dev->vendor; dev++) {
+ if (dev->vendor == pci->vendor && dev->device == pci->device)
+ break;
+ }
+ if (! dev->vendor)
+ continue;
+ if (! enable[card++]) {
+ printk(KERN_DEBUG "snd-page-alloc: skipping card %d, device %04x:%04x\n", card, pci->vendor, pci->device);
+ continue;
+ }
+
+ if (pci_set_dma_mask(pci, dev->dma_mask) < 0 ||
+ pci_set_consistent_dma_mask(pci, dev->dma_mask) < 0) {
+ printk(KERN_ERR "snd-page-alloc: cannot set DMA mask %lx for pci %04x:%04x\n", dev->dma_mask, dev->vendor, dev->device);
+ continue;
+ }
+ for (i = 0; i < dev->buffers; i++) {
+ struct snd_dma_buffer dmab;
+ memset(&dmab, 0, sizeof(dmab));
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+ dev->size, &dmab) < 0)
+ printk(KERN_WARNING "snd-page-alloc: cannot allocate buffer pages (size = %d)\n", dev->size);
+ else
+ snd_dma_reserve_buf(&dmab, snd_dma_pci_buf_id(pci));
+ }
+ }
+}
+#else
+#define preallocate_cards() /* NOP */
+#endif
+
+
+#ifdef CONFIG_PROC_FS
+/*
+ * proc file interface
+ */
+static int snd_mem_proc_read(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ long pages = snd_allocated_pages >> (PAGE_SHIFT-12);
+ struct list_head *p;
+ struct snd_mem_list *mem;
+ int devno;
+ static char *types[] = { "UNKNOWN", "CONT", "DEV", "DEV-SG", "SBUS" };
+
+ down(&list_mutex);
+ len += snprintf(page + len, count - len,
+ "pages : %li bytes (%li pages per %likB)\n",
+ pages * PAGE_SIZE, pages, PAGE_SIZE / 1024);
+ devno = 0;
+ list_for_each(p, &mem_list_head) {
+ mem = list_entry(p, struct snd_mem_list, list);
+ devno++;
+ len += snprintf(page + len, count - len,
+ "buffer %d : ID %08x : type %s\n",
+ devno, mem->id, types[mem->buffer.dev.type]);
+ len += snprintf(page + len, count - len,
+ " addr = 0x%lx, size = %d bytes\n",
+ (unsigned long)mem->buffer.addr, (int)mem->buffer.bytes);
+ }
+ up(&list_mutex);
+ return len;
+}
+#endif /* CONFIG_PROC_FS */
+
+/*
+ * module entry
+ */
+
+static int __init snd_mem_init(void)
+{
+#ifdef CONFIG_PROC_FS
+ create_proc_read_entry("driver/snd-page-alloc", 0, NULL, snd_mem_proc_read, NULL);
+#endif
+ preallocate_cards();
+ return 0;
+}
+
+static void __exit snd_mem_exit(void)
+{
+ remove_proc_entry("driver/snd-page-alloc", NULL);
+ free_all_reserved_pages();
+ if (snd_allocated_pages > 0)
+ printk(KERN_ERR "snd-malloc: Memory leak? pages not freed = %li\n", snd_allocated_pages);
+}
+
+
+module_init(snd_mem_init)
+module_exit(snd_mem_exit)
+
+
+/*
+ * exports
+ */
+EXPORT_SYMBOL(snd_dma_alloc_pages);
+EXPORT_SYMBOL(snd_dma_alloc_pages_fallback);
+EXPORT_SYMBOL(snd_dma_free_pages);
+
+EXPORT_SYMBOL(snd_dma_get_reserved_buf);
+EXPORT_SYMBOL(snd_dma_reserve_buf);
+
+EXPORT_SYMBOL(snd_malloc_pages);
+EXPORT_SYMBOL(snd_free_pages);
diff --git a/sound/core/memory.c b/sound/core/memory.c
new file mode 100644
index 00000000000..20860fec936
--- /dev/null
+++ b/sound/core/memory.c
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ * Memory allocation helpers.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include <sound/info.h>
+
+/*
+ * memory allocation helpers and debug routines
+ */
+
+#ifdef CONFIG_SND_DEBUG_MEMORY
+
+struct snd_alloc_track {
+ unsigned long magic;
+ void *caller;
+ size_t size;
+ struct list_head list;
+ long data[0];
+};
+
+#define snd_alloc_track_entry(obj) (struct snd_alloc_track *)((char*)obj - (unsigned long)((struct snd_alloc_track *)0)->data)
+
+static long snd_alloc_kmalloc;
+static long snd_alloc_vmalloc;
+static LIST_HEAD(snd_alloc_kmalloc_list);
+static LIST_HEAD(snd_alloc_vmalloc_list);
+static DEFINE_SPINLOCK(snd_alloc_kmalloc_lock);
+static DEFINE_SPINLOCK(snd_alloc_vmalloc_lock);
+#define KMALLOC_MAGIC 0x87654321
+#define VMALLOC_MAGIC 0x87654320
+static snd_info_entry_t *snd_memory_info_entry;
+
+void snd_memory_init(void)
+{
+ snd_alloc_kmalloc = 0;
+ snd_alloc_vmalloc = 0;
+}
+
+void snd_memory_done(void)
+{
+ struct list_head *head;
+ struct snd_alloc_track *t;
+
+ if (snd_alloc_kmalloc > 0)
+ snd_printk(KERN_ERR "Not freed snd_alloc_kmalloc = %li\n", snd_alloc_kmalloc);
+ if (snd_alloc_vmalloc > 0)
+ snd_printk(KERN_ERR "Not freed snd_alloc_vmalloc = %li\n", snd_alloc_vmalloc);
+ list_for_each_prev(head, &snd_alloc_kmalloc_list) {
+ t = list_entry(head, struct snd_alloc_track, list);
+ if (t->magic != KMALLOC_MAGIC) {
+ snd_printk(KERN_ERR "Corrupted kmalloc\n");
+ break;
+ }
+ snd_printk(KERN_ERR "kmalloc(%ld) from %p not freed\n", (long) t->size, t->caller);
+ }
+ list_for_each_prev(head, &snd_alloc_vmalloc_list) {
+ t = list_entry(head, struct snd_alloc_track, list);
+ if (t->magic != VMALLOC_MAGIC) {
+ snd_printk(KERN_ERR "Corrupted vmalloc\n");
+ break;
+ }
+ snd_printk(KERN_ERR "vmalloc(%ld) from %p not freed\n", (long) t->size, t->caller);
+ }
+}
+
+static void *__snd_kmalloc(size_t size, int flags, void *caller)
+{
+ unsigned long cpu_flags;
+ struct snd_alloc_track *t;
+ void *ptr;
+
+ ptr = snd_wrapper_kmalloc(size + sizeof(struct snd_alloc_track), flags);
+ if (ptr != NULL) {
+ t = (struct snd_alloc_track *)ptr;
+ t->magic = KMALLOC_MAGIC;
+ t->caller = caller;
+ spin_lock_irqsave(&snd_alloc_kmalloc_lock, cpu_flags);
+ list_add_tail(&t->list, &snd_alloc_kmalloc_list);
+ spin_unlock_irqrestore(&snd_alloc_kmalloc_lock, cpu_flags);
+ t->size = size;
+ snd_alloc_kmalloc += size;
+ ptr = t->data;
+ }
+ return ptr;
+}
+
+#define _snd_kmalloc(size, flags) __snd_kmalloc((size), (flags), __builtin_return_address(0));
+void *snd_hidden_kmalloc(size_t size, int flags)
+{
+ return _snd_kmalloc(size, flags);
+}
+
+void *snd_hidden_kcalloc(size_t n, size_t size, int flags)
+{
+ void *ret = NULL;
+ if (n != 0 && size > INT_MAX / n)
+ return ret;
+ ret = _snd_kmalloc(n * size, flags);
+ if (ret)
+ memset(ret, 0, n * size);
+ return ret;
+}
+
+void snd_hidden_kfree(const void *obj)
+{
+ unsigned long flags;
+ struct snd_alloc_track *t;
+ if (obj == NULL)
+ return;
+ t = snd_alloc_track_entry(obj);
+ if (t->magic != KMALLOC_MAGIC) {
+ snd_printk(KERN_WARNING "bad kfree (called from %p)\n", __builtin_return_address(0));
+ return;
+ }
+ spin_lock_irqsave(&snd_alloc_kmalloc_lock, flags);
+ list_del(&t->list);
+ spin_unlock_irqrestore(&snd_alloc_kmalloc_lock, flags);
+ t->magic = 0;
+ snd_alloc_kmalloc -= t->size;
+ obj = t;
+ snd_wrapper_kfree(obj);
+}
+
+void *snd_hidden_vmalloc(unsigned long size)
+{
+ void *ptr;
+ ptr = snd_wrapper_vmalloc(size + sizeof(struct snd_alloc_track));
+ if (ptr) {
+ struct snd_alloc_track *t = (struct snd_alloc_track *)ptr;
+ t->magic = VMALLOC_MAGIC;
+ t->caller = __builtin_return_address(0);
+ spin_lock(&snd_alloc_vmalloc_lock);
+ list_add_tail(&t->list, &snd_alloc_vmalloc_list);
+ spin_unlock(&snd_alloc_vmalloc_lock);
+ t->size = size;
+ snd_alloc_vmalloc += size;
+ ptr = t->data;
+ }
+ return ptr;
+}
+
+void snd_hidden_vfree(void *obj)
+{
+ struct snd_alloc_track *t;
+ if (obj == NULL)
+ return;
+ t = snd_alloc_track_entry(obj);
+ if (t->magic != VMALLOC_MAGIC) {
+ snd_printk(KERN_ERR "bad vfree (called from %p)\n", __builtin_return_address(0));
+ return;
+ }
+ spin_lock(&snd_alloc_vmalloc_lock);
+ list_del(&t->list);
+ spin_unlock(&snd_alloc_vmalloc_lock);
+ t->magic = 0;
+ snd_alloc_vmalloc -= t->size;
+ obj = t;
+ snd_wrapper_vfree(obj);
+}
+
+static void snd_memory_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+ snd_iprintf(buffer, "kmalloc: %li bytes\n", snd_alloc_kmalloc);
+ snd_iprintf(buffer, "vmalloc: %li bytes\n", snd_alloc_vmalloc);
+}
+
+int __init snd_memory_info_init(void)
+{
+ snd_info_entry_t *entry;
+
+ entry = snd_info_create_module_entry(THIS_MODULE, "meminfo", NULL);
+ if (entry) {
+ entry->c.text.read_size = 256;
+ entry->c.text.read = snd_memory_info_read;
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ entry = NULL;
+ }
+ }
+ snd_memory_info_entry = entry;
+ return 0;
+}
+
+int __exit snd_memory_info_done(void)
+{
+ if (snd_memory_info_entry)
+ snd_info_unregister(snd_memory_info_entry);
+ return 0;
+}
+
+#else
+
+#define _snd_kmalloc kmalloc
+
+#endif /* CONFIG_SND_DEBUG_MEMORY */
+
+/**
+ * snd_kmalloc_strdup - copy the string
+ * @string: the original string
+ * @flags: allocation conditions, GFP_XXX
+ *
+ * Allocates a memory chunk via kmalloc() and copies the string to it.
+ *
+ * Returns the pointer, or NULL if no enoguh memory.
+ */
+char *snd_kmalloc_strdup(const char *string, int flags)
+{
+ size_t len;
+ char *ptr;
+
+ if (!string)
+ return NULL;
+ len = strlen(string) + 1;
+ ptr = _snd_kmalloc(len, flags);
+ if (ptr)
+ memcpy(ptr, string, len);
+ return ptr;
+}
+
+/**
+ * copy_to_user_fromio - copy data from mmio-space to user-space
+ * @dst: the destination pointer on user-space
+ * @src: the source pointer on mmio
+ * @count: the data size to copy in bytes
+ *
+ * Copies the data from mmio-space to user-space.
+ *
+ * Returns zero if successful, or non-zero on failure.
+ */
+int copy_to_user_fromio(void __user *dst, const volatile void __iomem *src, size_t count)
+{
+#if defined(__i386__) || defined(CONFIG_SPARC32)
+ return copy_to_user(dst, (const void*)src, count) ? -EFAULT : 0;
+#else
+ char buf[256];
+ while (count) {
+ size_t c = count;
+ if (c > sizeof(buf))
+ c = sizeof(buf);
+ memcpy_fromio(buf, (void __iomem *)src, c);
+ if (copy_to_user(dst, buf, c))
+ return -EFAULT;
+ count -= c;
+ dst += c;
+ src += c;
+ }
+ return 0;
+#endif
+}
+
+/**
+ * copy_from_user_toio - copy data from user-space to mmio-space
+ * @dst: the destination pointer on mmio-space
+ * @src: the source pointer on user-space
+ * @count: the data size to copy in bytes
+ *
+ * Copies the data from user-space to mmio-space.
+ *
+ * Returns zero if successful, or non-zero on failure.
+ */
+int copy_from_user_toio(volatile void __iomem *dst, const void __user *src, size_t count)
+{
+#if defined(__i386__) || defined(CONFIG_SPARC32)
+ return copy_from_user((void*)dst, src, count) ? -EFAULT : 0;
+#else
+ char buf[256];
+ while (count) {
+ size_t c = count;
+ if (c > sizeof(buf))
+ c = sizeof(buf);
+ if (copy_from_user(buf, src, c))
+ return -EFAULT;
+ memcpy_toio(dst, buf, c);
+ count -= c;
+ dst += c;
+ src += c;
+ }
+ return 0;
+#endif
+}
diff --git a/sound/core/misc.c b/sound/core/misc.c
new file mode 100644
index 00000000000..1a81fe4df21
--- /dev/null
+++ b/sound/core/misc.c
@@ -0,0 +1,76 @@
+/*
+ * Misc and compatibility things
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <sound/core.h>
+
+int snd_task_name(struct task_struct *task, char *name, size_t size)
+{
+ unsigned int idx;
+
+ snd_assert(task != NULL && name != NULL && size >= 2, return -EINVAL);
+ for (idx = 0; idx < sizeof(task->comm) && idx + 1 < size; idx++)
+ name[idx] = task->comm[idx];
+ name[idx] = '\0';
+ return 0;
+}
+
+#ifdef CONFIG_SND_VERBOSE_PRINTK
+void snd_verbose_printk(const char *file, int line, const char *format, ...)
+{
+ va_list args;
+
+ if (format[0] == '<' && format[1] >= '0' && format[1] <= '9' && format[2] == '>') {
+ char tmp[] = "<0>";
+ tmp[1] = format[1];
+ printk("%sALSA %s:%d: ", tmp, file, line);
+ format += 3;
+ } else {
+ printk("ALSA %s:%d: ", file, line);
+ }
+ va_start(args, format);
+ vprintk(format, args);
+ va_end(args);
+}
+#endif
+
+#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_VERBOSE_PRINTK)
+void snd_verbose_printd(const char *file, int line, const char *format, ...)
+{
+ va_list args;
+
+ if (format[0] == '<' && format[1] >= '0' && format[1] <= '9' && format[2] == '>') {
+ char tmp[] = "<0>";
+ tmp[1] = format[1];
+ printk("%sALSA %s:%d: ", tmp, file, line);
+ format += 3;
+ } else {
+ printk(KERN_DEBUG "ALSA %s:%d: ", file, line);
+ }
+ va_start(args, format);
+ vprintk(format, args);
+ va_end(args);
+
+}
+#endif
diff --git a/sound/core/oss/Makefile b/sound/core/oss/Makefile
new file mode 100644
index 00000000000..e6d5a045ba2
--- /dev/null
+++ b/sound/core/oss/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for ALSA
+# Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-mixer-oss-objs := mixer_oss.o
+
+snd-pcm-oss-objs := pcm_oss.o pcm_plugin.o \
+ io.o copy.o linear.o mulaw.o route.o rate.o
+
+obj-$(CONFIG_SND_MIXER_OSS) += snd-mixer-oss.o
+obj-$(CONFIG_SND_PCM_OSS) += snd-pcm-oss.o
diff --git a/sound/core/oss/copy.c b/sound/core/oss/copy.c
new file mode 100644
index 00000000000..edecbe7417b
--- /dev/null
+++ b/sound/core/oss/copy.c
@@ -0,0 +1,87 @@
+/*
+ * Linear conversion Plug-In
+ * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
+ *
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include "pcm_plugin.h"
+
+static snd_pcm_sframes_t copy_transfer(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels,
+ snd_pcm_plugin_channel_t *dst_channels,
+ snd_pcm_uframes_t frames)
+{
+ unsigned int channel;
+ unsigned int nchannels;
+
+ snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
+ if (frames == 0)
+ return 0;
+ nchannels = plugin->src_format.channels;
+ for (channel = 0; channel < nchannels; channel++) {
+ snd_assert(src_channels->area.first % 8 == 0 &&
+ src_channels->area.step % 8 == 0,
+ return -ENXIO);
+ snd_assert(dst_channels->area.first % 8 == 0 &&
+ dst_channels->area.step % 8 == 0,
+ return -ENXIO);
+ if (!src_channels->enabled) {
+ if (dst_channels->wanted)
+ snd_pcm_area_silence(&dst_channels->area, 0, frames, plugin->dst_format.format);
+ dst_channels->enabled = 0;
+ continue;
+ }
+ dst_channels->enabled = 1;
+ snd_pcm_area_copy(&src_channels->area, 0, &dst_channels->area, 0, frames, plugin->src_format.format);
+ src_channels++;
+ dst_channels++;
+ }
+ return frames;
+}
+
+int snd_pcm_plugin_build_copy(snd_pcm_plug_t *plug,
+ snd_pcm_plugin_format_t *src_format,
+ snd_pcm_plugin_format_t *dst_format,
+ snd_pcm_plugin_t **r_plugin)
+{
+ int err;
+ snd_pcm_plugin_t *plugin;
+ int width;
+
+ snd_assert(r_plugin != NULL, return -ENXIO);
+ *r_plugin = NULL;
+
+ snd_assert(src_format->format == dst_format->format, return -ENXIO);
+ snd_assert(src_format->rate == dst_format->rate, return -ENXIO);
+ snd_assert(src_format->channels == dst_format->channels, return -ENXIO);
+
+ width = snd_pcm_format_physical_width(src_format->format);
+ snd_assert(width > 0, return -ENXIO);
+
+ err = snd_pcm_plugin_build(plug, "copy", src_format, dst_format,
+ 0, &plugin);
+ if (err < 0)
+ return err;
+ plugin->transfer = copy_transfer;
+ *r_plugin = plugin;
+ return 0;
+}
diff --git a/sound/core/oss/io.c b/sound/core/oss/io.c
new file mode 100644
index 00000000000..bb1c99a5b73
--- /dev/null
+++ b/sound/core/oss/io.c
@@ -0,0 +1,134 @@
+/*
+ * PCM I/O Plug-In Interface
+ * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "pcm_plugin.h"
+
+#define pcm_write(plug,buf,count) snd_pcm_oss_write3(plug,buf,count,1)
+#define pcm_writev(plug,vec,count) snd_pcm_oss_writev3(plug,vec,count,1)
+#define pcm_read(plug,buf,count) snd_pcm_oss_read3(plug,buf,count,1)
+#define pcm_readv(plug,vec,count) snd_pcm_oss_readv3(plug,vec,count,1)
+
+/*
+ * Basic io plugin
+ */
+
+static snd_pcm_sframes_t io_playback_transfer(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels,
+ snd_pcm_plugin_channel_t *dst_channels ATTRIBUTE_UNUSED,
+ snd_pcm_uframes_t frames)
+{
+ snd_assert(plugin != NULL, return -ENXIO);
+ snd_assert(src_channels != NULL, return -ENXIO);
+ if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
+ return pcm_write(plugin->plug, src_channels->area.addr, frames);
+ } else {
+ int channel, channels = plugin->dst_format.channels;
+ void **bufs = (void**)plugin->extra_data;
+ snd_assert(bufs != NULL, return -ENXIO);
+ for (channel = 0; channel < channels; channel++) {
+ if (src_channels[channel].enabled)
+ bufs[channel] = src_channels[channel].area.addr;
+ else
+ bufs[channel] = NULL;
+ }
+ return pcm_writev(plugin->plug, bufs, frames);
+ }
+}
+
+static snd_pcm_sframes_t io_capture_transfer(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels ATTRIBUTE_UNUSED,
+ snd_pcm_plugin_channel_t *dst_channels,
+ snd_pcm_uframes_t frames)
+{
+ snd_assert(plugin != NULL, return -ENXIO);
+ snd_assert(dst_channels != NULL, return -ENXIO);
+ if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
+ return pcm_read(plugin->plug, dst_channels->area.addr, frames);
+ } else {
+ int channel, channels = plugin->dst_format.channels;
+ void **bufs = (void**)plugin->extra_data;
+ snd_assert(bufs != NULL, return -ENXIO);
+ for (channel = 0; channel < channels; channel++) {
+ if (dst_channels[channel].enabled)
+ bufs[channel] = dst_channels[channel].area.addr;
+ else
+ bufs[channel] = NULL;
+ }
+ return pcm_readv(plugin->plug, bufs, frames);
+ }
+ return 0;
+}
+
+static snd_pcm_sframes_t io_src_channels(snd_pcm_plugin_t *plugin,
+ snd_pcm_uframes_t frames,
+ snd_pcm_plugin_channel_t **channels)
+{
+ int err;
+ unsigned int channel;
+ snd_pcm_plugin_channel_t *v;
+ err = snd_pcm_plugin_client_channels(plugin, frames, &v);
+ if (err < 0)
+ return err;
+ *channels = v;
+ if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
+ for (channel = 0; channel < plugin->src_format.channels; ++channel, ++v)
+ v->wanted = 1;
+ }
+ return frames;
+}
+
+int snd_pcm_plugin_build_io(snd_pcm_plug_t *plug,
+ snd_pcm_hw_params_t *params,
+ snd_pcm_plugin_t **r_plugin)
+{
+ int err;
+ snd_pcm_plugin_format_t format;
+ snd_pcm_plugin_t *plugin;
+
+ snd_assert(r_plugin != NULL, return -ENXIO);
+ *r_plugin = NULL;
+ snd_assert(plug != NULL && params != NULL, return -ENXIO);
+ format.format = params_format(params);
+ format.rate = params_rate(params);
+ format.channels = params_channels(params);
+ err = snd_pcm_plugin_build(plug, "I/O io",
+ &format, &format,
+ sizeof(void *) * format.channels,
+ &plugin);
+ if (err < 0)
+ return err;
+ plugin->access = params_access(params);
+ if (snd_pcm_plug_stream(plug) == SNDRV_PCM_STREAM_PLAYBACK) {
+ plugin->transfer = io_playback_transfer;
+ if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED)
+ plugin->client_channels = io_src_channels;
+ } else {
+ plugin->transfer = io_capture_transfer;
+ }
+
+ *r_plugin = plugin;
+ return 0;
+}
diff --git a/sound/core/oss/linear.c b/sound/core/oss/linear.c
new file mode 100644
index 00000000000..12ed27a57b2
--- /dev/null
+++ b/sound/core/oss/linear.c
@@ -0,0 +1,158 @@
+/*
+ * Linear conversion Plug-In
+ * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>,
+ * Abramo Bagnara <abramo@alsa-project.org>
+ *
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include "pcm_plugin.h"
+
+/*
+ * Basic linear conversion plugin
+ */
+
+typedef struct linear_private_data {
+ int conv;
+} linear_t;
+
+static void convert(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels,
+ snd_pcm_plugin_channel_t *dst_channels,
+ snd_pcm_uframes_t frames)
+{
+#define CONV_LABELS
+#include "plugin_ops.h"
+#undef CONV_LABELS
+ linear_t *data = (linear_t *)plugin->extra_data;
+ void *conv = conv_labels[data->conv];
+ int channel;
+ int nchannels = plugin->src_format.channels;
+ for (channel = 0; channel < nchannels; ++channel) {
+ char *src;
+ char *dst;
+ int src_step, dst_step;
+ snd_pcm_uframes_t frames1;
+ if (!src_channels[channel].enabled) {
+ if (dst_channels[channel].wanted)
+ snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format);
+ dst_channels[channel].enabled = 0;
+ continue;
+ }
+ dst_channels[channel].enabled = 1;
+ src = src_channels[channel].area.addr + src_channels[channel].area.first / 8;
+ dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
+ src_step = src_channels[channel].area.step / 8;
+ dst_step = dst_channels[channel].area.step / 8;
+ frames1 = frames;
+ while (frames1-- > 0) {
+ goto *conv;
+#define CONV_END after
+#include "plugin_ops.h"
+#undef CONV_END
+ after:
+ src += src_step;
+ dst += dst_step;
+ }
+ }
+}
+
+static snd_pcm_sframes_t linear_transfer(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels,
+ snd_pcm_plugin_channel_t *dst_channels,
+ snd_pcm_uframes_t frames)
+{
+ linear_t *data;
+
+ snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
+ data = (linear_t *)plugin->extra_data;
+ if (frames == 0)
+ return 0;
+#ifdef CONFIG_SND_DEBUG
+ {
+ unsigned int channel;
+ for (channel = 0; channel < plugin->src_format.channels; channel++) {
+ snd_assert(src_channels[channel].area.first % 8 == 0 &&
+ src_channels[channel].area.step % 8 == 0,
+ return -ENXIO);
+ snd_assert(dst_channels[channel].area.first % 8 == 0 &&
+ dst_channels[channel].area.step % 8 == 0,
+ return -ENXIO);
+ }
+ }
+#endif
+ convert(plugin, src_channels, dst_channels, frames);
+ return frames;
+}
+
+int conv_index(int src_format, int dst_format)
+{
+ int src_endian, dst_endian, sign, src_width, dst_width;
+
+ sign = (snd_pcm_format_signed(src_format) !=
+ snd_pcm_format_signed(dst_format));
+#ifdef SNDRV_LITTLE_ENDIAN
+ src_endian = snd_pcm_format_big_endian(src_format);
+ dst_endian = snd_pcm_format_big_endian(dst_format);
+#else
+ src_endian = snd_pcm_format_little_endian(src_format);
+ dst_endian = snd_pcm_format_little_endian(dst_format);
+#endif
+
+ if (src_endian < 0)
+ src_endian = 0;
+ if (dst_endian < 0)
+ dst_endian = 0;
+
+ src_width = snd_pcm_format_width(src_format) / 8 - 1;
+ dst_width = snd_pcm_format_width(dst_format) / 8 - 1;
+
+ return src_width * 32 + src_endian * 16 + sign * 8 + dst_width * 2 + dst_endian;
+}
+
+int snd_pcm_plugin_build_linear(snd_pcm_plug_t *plug,
+ snd_pcm_plugin_format_t *src_format,
+ snd_pcm_plugin_format_t *dst_format,
+ snd_pcm_plugin_t **r_plugin)
+{
+ int err;
+ struct linear_private_data *data;
+ snd_pcm_plugin_t *plugin;
+
+ snd_assert(r_plugin != NULL, return -ENXIO);
+ *r_plugin = NULL;
+
+ snd_assert(src_format->rate == dst_format->rate, return -ENXIO);
+ snd_assert(src_format->channels == dst_format->channels, return -ENXIO);
+ snd_assert(snd_pcm_format_linear(src_format->format) &&
+ snd_pcm_format_linear(dst_format->format), return -ENXIO);
+
+ err = snd_pcm_plugin_build(plug, "linear format conversion",
+ src_format, dst_format,
+ sizeof(linear_t), &plugin);
+ if (err < 0)
+ return err;
+ data = (linear_t *)plugin->extra_data;
+ data->conv = conv_index(src_format->format, dst_format->format);
+ plugin->transfer = linear_transfer;
+ *r_plugin = plugin;
+ return 0;
+}
diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c
new file mode 100644
index 00000000000..98ed9a9f0da
--- /dev/null
+++ b/sound/core/oss/mixer_oss.c
@@ -0,0 +1,1340 @@
+/*
+ * OSS emulation layer for the mixer interface
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/minors.h>
+#include <sound/control.h>
+#include <sound/info.h>
+#include <sound/mixer_oss.h>
+#include <linux/soundcard.h>
+
+#define OSS_ALSAEMULVER _SIOR ('M', 249, int)
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Mixer OSS emulation for ALSA.");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_MIXER);
+
+static int snd_mixer_oss_open(struct inode *inode, struct file *file)
+{
+ int cardnum = SNDRV_MINOR_OSS_CARD(iminor(inode));
+ snd_card_t *card;
+ snd_mixer_oss_file_t *fmixer;
+ int err;
+
+ if ((card = snd_cards[cardnum]) == NULL)
+ return -ENODEV;
+ if (card->mixer_oss == NULL)
+ return -ENODEV;
+ err = snd_card_file_add(card, file);
+ if (err < 0)
+ return err;
+ fmixer = kcalloc(1, sizeof(*fmixer), GFP_KERNEL);
+ if (fmixer == NULL) {
+ snd_card_file_remove(card, file);
+ return -ENOMEM;
+ }
+ fmixer->card = card;
+ fmixer->mixer = card->mixer_oss;
+ file->private_data = fmixer;
+ if (!try_module_get(card->module)) {
+ kfree(fmixer);
+ snd_card_file_remove(card, file);
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static int snd_mixer_oss_release(struct inode *inode, struct file *file)
+{
+ snd_mixer_oss_file_t *fmixer;
+
+ if (file->private_data) {
+ fmixer = (snd_mixer_oss_file_t *) file->private_data;
+ module_put(fmixer->card->module);
+ snd_card_file_remove(fmixer->card, file);
+ kfree(fmixer);
+ }
+ return 0;
+}
+
+static int snd_mixer_oss_info(snd_mixer_oss_file_t *fmixer,
+ mixer_info __user *_info)
+{
+ snd_card_t *card = fmixer->card;
+ snd_mixer_oss_t *mixer = fmixer->mixer;
+ struct mixer_info info;
+
+ memset(&info, 0, sizeof(info));
+ strlcpy(info.id, mixer && mixer->id[0] ? mixer->id : card->driver, sizeof(info.id));
+ strlcpy(info.name, mixer && mixer->name[0] ? mixer->name : card->mixername, sizeof(info.name));
+ info.modify_counter = card->mixer_oss_change_count;
+ if (copy_to_user(_info, &info, sizeof(info)))
+ return -EFAULT;
+ return 0;
+}
+
+static int snd_mixer_oss_info_obsolete(snd_mixer_oss_file_t *fmixer,
+ _old_mixer_info __user *_info)
+{
+ snd_card_t *card = fmixer->card;
+ snd_mixer_oss_t *mixer = fmixer->mixer;
+ _old_mixer_info info;
+
+ memset(&info, 0, sizeof(info));
+ strlcpy(info.id, mixer && mixer->id[0] ? mixer->id : card->driver, sizeof(info.id));
+ strlcpy(info.name, mixer && mixer->name[0] ? mixer->name : card->mixername, sizeof(info.name));
+ if (copy_to_user(_info, &info, sizeof(info)))
+ return -EFAULT;
+ return 0;
+}
+
+static int snd_mixer_oss_caps(snd_mixer_oss_file_t *fmixer)
+{
+ snd_mixer_oss_t *mixer = fmixer->mixer;
+ int result = 0;
+
+ if (mixer == NULL)
+ return -EIO;
+ if (mixer->get_recsrc && mixer->put_recsrc)
+ result |= SOUND_CAP_EXCL_INPUT;
+ return result;
+}
+
+static int snd_mixer_oss_devmask(snd_mixer_oss_file_t *fmixer)
+{
+ snd_mixer_oss_t *mixer = fmixer->mixer;
+ snd_mixer_oss_slot_t *pslot;
+ int result = 0, chn;
+
+ if (mixer == NULL)
+ return -EIO;
+ for (chn = 0; chn < 31; chn++) {
+ pslot = &mixer->slots[chn];
+ if (pslot->put_volume || pslot->put_recsrc)
+ result |= 1 << chn;
+ }
+ return result;
+}
+
+static int snd_mixer_oss_stereodevs(snd_mixer_oss_file_t *fmixer)
+{
+ snd_mixer_oss_t *mixer = fmixer->mixer;
+ snd_mixer_oss_slot_t *pslot;
+ int result = 0, chn;
+
+ if (mixer == NULL)
+ return -EIO;
+ for (chn = 0; chn < 31; chn++) {
+ pslot = &mixer->slots[chn];
+ if (pslot->put_volume && pslot->stereo)
+ result |= 1 << chn;
+ }
+ return result;
+}
+
+static int snd_mixer_oss_recmask(snd_mixer_oss_file_t *fmixer)
+{
+ snd_mixer_oss_t *mixer = fmixer->mixer;
+ int result = 0;
+
+ if (mixer == NULL)
+ return -EIO;
+ if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */
+ result = mixer->mask_recsrc;
+ } else {
+ snd_mixer_oss_slot_t *pslot;
+ int chn;
+ for (chn = 0; chn < 31; chn++) {
+ pslot = &mixer->slots[chn];
+ if (pslot->put_recsrc)
+ result |= 1 << chn;
+ }
+ }
+ return result;
+}
+
+static int snd_mixer_oss_get_recsrc(snd_mixer_oss_file_t *fmixer)
+{
+ snd_mixer_oss_t *mixer = fmixer->mixer;
+ int result = 0;
+
+ if (mixer == NULL)
+ return -EIO;
+ if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */
+ int err;
+ if ((err = mixer->get_recsrc(fmixer, &result)) < 0)
+ return err;
+ result = 1 << result;
+ } else {
+ snd_mixer_oss_slot_t *pslot;
+ int chn;
+ for (chn = 0; chn < 31; chn++) {
+ pslot = &mixer->slots[chn];
+ if (pslot->get_recsrc) {
+ int active = 0;
+ pslot->get_recsrc(fmixer, pslot, &active);
+ if (active)
+ result |= 1 << chn;
+ }
+ }
+ }
+ return mixer->oss_recsrc = result;
+}
+
+static int snd_mixer_oss_set_recsrc(snd_mixer_oss_file_t *fmixer, int recsrc)
+{
+ snd_mixer_oss_t *mixer = fmixer->mixer;
+ snd_mixer_oss_slot_t *pslot;
+ int chn, active;
+ int result = 0;
+
+ if (mixer == NULL)
+ return -EIO;
+ if (mixer->get_recsrc && mixer->put_recsrc) { /* exclusive input */
+ if (recsrc & ~mixer->oss_recsrc)
+ recsrc &= ~mixer->oss_recsrc;
+ mixer->put_recsrc(fmixer, ffz(~recsrc));
+ mixer->get_recsrc(fmixer, &result);
+ result = 1 << result;
+ }
+ for (chn = 0; chn < 31; chn++) {
+ pslot = &mixer->slots[chn];
+ if (pslot->put_recsrc) {
+ active = (recsrc & (1 << chn)) ? 1 : 0;
+ pslot->put_recsrc(fmixer, pslot, active);
+ }
+ }
+ if (! result) {
+ for (chn = 0; chn < 31; chn++) {
+ pslot = &mixer->slots[chn];
+ if (pslot->get_recsrc) {
+ active = 0;
+ pslot->get_recsrc(fmixer, pslot, &active);
+ if (active)
+ result |= 1 << chn;
+ }
+ }
+ }
+ return result;
+}
+
+static int snd_mixer_oss_get_volume(snd_mixer_oss_file_t *fmixer, int slot)
+{
+ snd_mixer_oss_t *mixer = fmixer->mixer;
+ snd_mixer_oss_slot_t *pslot;
+ int result = 0, left, right;
+
+ if (mixer == NULL || slot > 30)
+ return -EIO;
+ pslot = &mixer->slots[slot];
+ left = pslot->volume[0];
+ right = pslot->volume[1];
+ if (pslot->get_volume)
+ result = pslot->get_volume(fmixer, pslot, &left, &right);
+ if (!pslot->stereo)
+ right = left;
+ snd_assert(left >= 0 && left <= 100, return -EIO);
+ snd_assert(right >= 0 && right <= 100, return -EIO);
+ if (result >= 0) {
+ pslot->volume[0] = left;
+ pslot->volume[1] = right;
+ result = (left & 0xff) | ((right & 0xff) << 8);
+ }
+ return result;
+}
+
+static int snd_mixer_oss_set_volume(snd_mixer_oss_file_t *fmixer,
+ int slot, int volume)
+{
+ snd_mixer_oss_t *mixer = fmixer->mixer;
+ snd_mixer_oss_slot_t *pslot;
+ int result = 0, left = volume & 0xff, right = (volume >> 8) & 0xff;
+
+ if (mixer == NULL || slot > 30)
+ return -EIO;
+ pslot = &mixer->slots[slot];
+ if (left > 100)
+ left = 100;
+ if (right > 100)
+ right = 100;
+ if (!pslot->stereo)
+ right = left;
+ if (pslot->put_volume)
+ result = pslot->put_volume(fmixer, pslot, left, right);
+ if (result < 0)
+ return result;
+ pslot->volume[0] = left;
+ pslot->volume[1] = right;
+ return (left & 0xff) | ((right & 0xff) << 8);
+}
+
+static int snd_mixer_oss_ioctl1(snd_mixer_oss_file_t *fmixer, unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int tmp;
+
+ snd_assert(fmixer != NULL, return -ENXIO);
+ if (((cmd >> 8) & 0xff) == 'M') {
+ switch (cmd) {
+ case SOUND_MIXER_INFO:
+ return snd_mixer_oss_info(fmixer, argp);
+ case SOUND_OLD_MIXER_INFO:
+ return snd_mixer_oss_info_obsolete(fmixer, argp);
+ case SOUND_MIXER_WRITE_RECSRC:
+ if (get_user(tmp, p))
+ return -EFAULT;
+ tmp = snd_mixer_oss_set_recsrc(fmixer, tmp);
+ if (tmp < 0)
+ return tmp;
+ return put_user(tmp, p);
+ case OSS_GETVERSION:
+ return put_user(SNDRV_OSS_VERSION, p);
+ case OSS_ALSAEMULVER:
+ return put_user(1, p);
+ case SOUND_MIXER_READ_DEVMASK:
+ tmp = snd_mixer_oss_devmask(fmixer);
+ if (tmp < 0)
+ return tmp;
+ return put_user(tmp, p);
+ case SOUND_MIXER_READ_STEREODEVS:
+ tmp = snd_mixer_oss_stereodevs(fmixer);
+ if (tmp < 0)
+ return tmp;
+ return put_user(tmp, p);
+ case SOUND_MIXER_READ_RECMASK:
+ tmp = snd_mixer_oss_recmask(fmixer);
+ if (tmp < 0)
+ return tmp;
+ return put_user(tmp, p);
+ case SOUND_MIXER_READ_CAPS:
+ tmp = snd_mixer_oss_caps(fmixer);
+ if (tmp < 0)
+ return tmp;
+ return put_user(tmp, p);
+ case SOUND_MIXER_READ_RECSRC:
+ tmp = snd_mixer_oss_get_recsrc(fmixer);
+ if (tmp < 0)
+ return tmp;
+ return put_user(tmp, p);
+ }
+ }
+ if (cmd & SIOC_IN) {
+ if (get_user(tmp, p))
+ return -EFAULT;
+ tmp = snd_mixer_oss_set_volume(fmixer, cmd & 0xff, tmp);
+ if (tmp < 0)
+ return tmp;
+ return put_user(tmp, p);
+ } else if (cmd & SIOC_OUT) {
+ tmp = snd_mixer_oss_get_volume(fmixer, cmd & 0xff);
+ if (tmp < 0)
+ return tmp;
+ return put_user(tmp, p);
+ }
+ return -ENXIO;
+}
+
+static long snd_mixer_oss_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ return snd_mixer_oss_ioctl1((snd_mixer_oss_file_t *) file->private_data, cmd, arg);
+}
+
+int snd_mixer_oss_ioctl_card(snd_card_t *card, unsigned int cmd, unsigned long arg)
+{
+ snd_mixer_oss_file_t fmixer;
+
+ snd_assert(card != NULL, return -ENXIO);
+ if (card->mixer_oss == NULL)
+ return -ENXIO;
+ memset(&fmixer, 0, sizeof(fmixer));
+ fmixer.card = card;
+ fmixer.mixer = card->mixer_oss;
+ return snd_mixer_oss_ioctl1(&fmixer, cmd, arg);
+}
+
+#ifdef CONFIG_COMPAT
+/* all compatible */
+#define snd_mixer_oss_ioctl_compat snd_mixer_oss_ioctl
+#else
+#define snd_mixer_oss_ioctl_compat NULL
+#endif
+
+/*
+ * REGISTRATION PART
+ */
+
+static struct file_operations snd_mixer_oss_f_ops =
+{
+ .owner = THIS_MODULE,
+ .open = snd_mixer_oss_open,
+ .release = snd_mixer_oss_release,
+ .unlocked_ioctl = snd_mixer_oss_ioctl,
+ .compat_ioctl = snd_mixer_oss_ioctl_compat,
+};
+
+static snd_minor_t snd_mixer_oss_reg =
+{
+ .comment = "mixer",
+ .f_ops = &snd_mixer_oss_f_ops,
+};
+
+/*
+ * utilities
+ */
+
+static long snd_mixer_oss_conv(long val, long omin, long omax, long nmin, long nmax)
+{
+ long orange = omax - omin, nrange = nmax - nmin;
+
+ if (orange == 0)
+ return 0;
+ return ((nrange * (val - omin)) + (orange / 2)) / orange + nmin;
+}
+
+/* convert from alsa native to oss values (0-100) */
+static long snd_mixer_oss_conv1(long val, long min, long max, int *old)
+{
+ if (val == snd_mixer_oss_conv(*old, 0, 100, min, max))
+ return *old;
+ return snd_mixer_oss_conv(val, min, max, 0, 100);
+}
+
+/* convert from oss to alsa native values */
+static long snd_mixer_oss_conv2(long val, long min, long max)
+{
+ return snd_mixer_oss_conv(val, 0, 100, min, max);
+}
+
+#if 0
+static void snd_mixer_oss_recsrce_set(snd_card_t *card, int slot)
+{
+ snd_mixer_oss_t *mixer = card->mixer_oss;
+ if (mixer)
+ mixer->mask_recsrc |= 1 << slot;
+}
+
+static int snd_mixer_oss_recsrce_get(snd_card_t *card, int slot)
+{
+ snd_mixer_oss_t *mixer = card->mixer_oss;
+ if (mixer && (mixer->mask_recsrc & (1 << slot)))
+ return 1;
+ return 0;
+}
+#endif
+
+#define SNDRV_MIXER_OSS_SIGNATURE 0x65999250
+
+#define SNDRV_MIXER_OSS_ITEM_GLOBAL 0
+#define SNDRV_MIXER_OSS_ITEM_GSWITCH 1
+#define SNDRV_MIXER_OSS_ITEM_GROUTE 2
+#define SNDRV_MIXER_OSS_ITEM_GVOLUME 3
+#define SNDRV_MIXER_OSS_ITEM_PSWITCH 4
+#define SNDRV_MIXER_OSS_ITEM_PROUTE 5
+#define SNDRV_MIXER_OSS_ITEM_PVOLUME 6
+#define SNDRV_MIXER_OSS_ITEM_CSWITCH 7
+#define SNDRV_MIXER_OSS_ITEM_CROUTE 8
+#define SNDRV_MIXER_OSS_ITEM_CVOLUME 9
+#define SNDRV_MIXER_OSS_ITEM_CAPTURE 10
+
+#define SNDRV_MIXER_OSS_ITEM_COUNT 11
+
+#define SNDRV_MIXER_OSS_PRESENT_GLOBAL (1<<0)
+#define SNDRV_MIXER_OSS_PRESENT_GSWITCH (1<<1)
+#define SNDRV_MIXER_OSS_PRESENT_GROUTE (1<<2)
+#define SNDRV_MIXER_OSS_PRESENT_GVOLUME (1<<3)
+#define SNDRV_MIXER_OSS_PRESENT_PSWITCH (1<<4)
+#define SNDRV_MIXER_OSS_PRESENT_PROUTE (1<<5)
+#define SNDRV_MIXER_OSS_PRESENT_PVOLUME (1<<6)
+#define SNDRV_MIXER_OSS_PRESENT_CSWITCH (1<<7)
+#define SNDRV_MIXER_OSS_PRESENT_CROUTE (1<<8)
+#define SNDRV_MIXER_OSS_PRESENT_CVOLUME (1<<9)
+#define SNDRV_MIXER_OSS_PRESENT_CAPTURE (1<<10)
+
+struct slot {
+ unsigned int signature;
+ unsigned int present;
+ unsigned int channels;
+ unsigned int numid[SNDRV_MIXER_OSS_ITEM_COUNT];
+ unsigned int capture_item;
+ struct snd_mixer_oss_assign_table *assigned;
+ unsigned int allocated: 1;
+};
+
+#define ID_UNKNOWN ((unsigned int)-1)
+
+static snd_kcontrol_t *snd_mixer_oss_test_id(snd_mixer_oss_t *mixer, const char *name, int index)
+{
+ snd_card_t * card = mixer->card;
+ snd_ctl_elem_id_t id;
+
+ memset(&id, 0, sizeof(id));
+ id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ strcpy(id.name, name);
+ id.index = index;
+ return snd_ctl_find_id(card, &id);
+}
+
+static void snd_mixer_oss_get_volume1_vol(snd_mixer_oss_file_t *fmixer,
+ snd_mixer_oss_slot_t *pslot,
+ unsigned int numid,
+ int *left, int *right)
+{
+ snd_ctl_elem_info_t *uinfo;
+ snd_ctl_elem_value_t *uctl;
+ snd_kcontrol_t *kctl;
+ snd_card_t *card = fmixer->card;
+
+ if (numid == ID_UNKNOWN)
+ return;
+ down_read(&card->controls_rwsem);
+ if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) {
+ up_read(&card->controls_rwsem);
+ return;
+ }
+ uinfo = kcalloc(1, sizeof(*uinfo), GFP_KERNEL);
+ uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL);
+ if (uinfo == NULL || uctl == NULL)
+ goto __unalloc;
+ snd_runtime_check(!kctl->info(kctl, uinfo), goto __unalloc);
+ snd_runtime_check(!kctl->get(kctl, uctl), goto __unalloc);
+ snd_runtime_check(uinfo->type != SNDRV_CTL_ELEM_TYPE_BOOLEAN || uinfo->value.integer.min != 0 || uinfo->value.integer.max != 1, goto __unalloc);
+ *left = snd_mixer_oss_conv1(uctl->value.integer.value[0], uinfo->value.integer.min, uinfo->value.integer.max, &pslot->volume[0]);
+ if (uinfo->count > 1)
+ *right = snd_mixer_oss_conv1(uctl->value.integer.value[1], uinfo->value.integer.min, uinfo->value.integer.max, &pslot->volume[1]);
+ __unalloc:
+ up_read(&card->controls_rwsem);
+ kfree(uctl);
+ kfree(uinfo);
+}
+
+static void snd_mixer_oss_get_volume1_sw(snd_mixer_oss_file_t *fmixer,
+ snd_mixer_oss_slot_t *pslot,
+ unsigned int numid,
+ int *left, int *right,
+ int route)
+{
+ snd_ctl_elem_info_t *uinfo;
+ snd_ctl_elem_value_t *uctl;
+ snd_kcontrol_t *kctl;
+ snd_card_t *card = fmixer->card;
+
+ if (numid == ID_UNKNOWN)
+ return;
+ down_read(&card->controls_rwsem);
+ if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) {
+ up_read(&card->controls_rwsem);
+ return;
+ }
+ uinfo = kcalloc(1, sizeof(*uinfo), GFP_KERNEL);
+ uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL);
+ if (uinfo == NULL || uctl == NULL)
+ goto __unalloc;
+ snd_runtime_check(!kctl->info(kctl, uinfo), goto __unalloc);
+ snd_runtime_check(!kctl->get(kctl, uctl), goto __unalloc);
+ if (!uctl->value.integer.value[0]) {
+ *left = 0;
+ if (uinfo->count == 1)
+ *right = 0;
+ }
+ if (uinfo->count > 1 && !uctl->value.integer.value[route ? 3 : 1])
+ *right = 0;
+ __unalloc:
+ up_read(&card->controls_rwsem);
+ kfree(uctl);
+ kfree(uinfo);
+}
+
+static int snd_mixer_oss_get_volume1(snd_mixer_oss_file_t *fmixer,
+ snd_mixer_oss_slot_t *pslot,
+ int *left, int *right)
+{
+ struct slot *slot = (struct slot *)pslot->private_data;
+
+ *left = *right = 100;
+ if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) {
+ snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PVOLUME], left, right);
+ } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GVOLUME) {
+ snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GVOLUME], left, right);
+ } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GLOBAL) {
+ snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GLOBAL], left, right);
+ }
+ if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) {
+ snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0);
+ } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) {
+ snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0);
+ } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) {
+ snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1);
+ } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) {
+ snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1);
+ }
+ return 0;
+}
+
+static void snd_mixer_oss_put_volume1_vol(snd_mixer_oss_file_t *fmixer,
+ snd_mixer_oss_slot_t *pslot,
+ unsigned int numid,
+ int left, int right)
+{
+ snd_ctl_elem_info_t *uinfo;
+ snd_ctl_elem_value_t *uctl;
+ snd_kcontrol_t *kctl;
+ snd_card_t *card = fmixer->card;
+ int res;
+
+ if (numid == ID_UNKNOWN)
+ return;
+ down_read(&card->controls_rwsem);
+ if ((kctl = snd_ctl_find_numid(card, numid)) == NULL)
+ return;
+ uinfo = kcalloc(1, sizeof(*uinfo), GFP_KERNEL);
+ uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL);
+ if (uinfo == NULL || uctl == NULL)
+ goto __unalloc;
+ snd_runtime_check(!kctl->info(kctl, uinfo), goto __unalloc);
+ snd_runtime_check(uinfo->type != SNDRV_CTL_ELEM_TYPE_BOOLEAN || uinfo->value.integer.min != 0 || uinfo->value.integer.max != 1, goto __unalloc);
+ uctl->value.integer.value[0] = snd_mixer_oss_conv2(left, uinfo->value.integer.min, uinfo->value.integer.max);
+ if (uinfo->count > 1)
+ uctl->value.integer.value[1] = snd_mixer_oss_conv2(right, uinfo->value.integer.min, uinfo->value.integer.max);
+ snd_runtime_check((res = kctl->put(kctl, uctl)) >= 0, goto __unalloc);
+ if (res > 0)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
+ __unalloc:
+ up_read(&card->controls_rwsem);
+ kfree(uctl);
+ kfree(uinfo);
+}
+
+static void snd_mixer_oss_put_volume1_sw(snd_mixer_oss_file_t *fmixer,
+ snd_mixer_oss_slot_t *pslot,
+ unsigned int numid,
+ int left, int right,
+ int route)
+{
+ snd_ctl_elem_info_t *uinfo;
+ snd_ctl_elem_value_t *uctl;
+ snd_kcontrol_t *kctl;
+ snd_card_t *card = fmixer->card;
+ int res;
+
+ if (numid == ID_UNKNOWN)
+ return;
+ down_read(&card->controls_rwsem);
+ if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) {
+ up_read(&fmixer->card->controls_rwsem);
+ return;
+ }
+ uinfo = kcalloc(1, sizeof(*uinfo), GFP_KERNEL);
+ uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL);
+ if (uinfo == NULL || uctl == NULL)
+ goto __unalloc;
+ snd_runtime_check(!kctl->info(kctl, uinfo), goto __unalloc);
+ if (uinfo->count > 1) {
+ uctl->value.integer.value[0] = left > 0 ? 1 : 0;
+ uctl->value.integer.value[route ? 3 : 1] = right > 0 ? 1 : 0;
+ if (route) {
+ uctl->value.integer.value[1] =
+ uctl->value.integer.value[2] = 0;
+ }
+ } else {
+ uctl->value.integer.value[0] = (left > 0 || right > 0) ? 1 : 0;
+ }
+ snd_runtime_check((res = kctl->put(kctl, uctl)) >= 0, goto __unalloc);
+ if (res > 0)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
+ __unalloc:
+ up_read(&card->controls_rwsem);
+ kfree(uctl);
+ kfree(uinfo);
+}
+
+static int snd_mixer_oss_put_volume1(snd_mixer_oss_file_t *fmixer,
+ snd_mixer_oss_slot_t *pslot,
+ int left, int right)
+{
+ struct slot *slot = (struct slot *)pslot->private_data;
+
+ if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) {
+ snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PVOLUME], left, right);
+ if (slot->present & SNDRV_MIXER_OSS_PRESENT_CVOLUME)
+ snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CVOLUME], left, right);
+ } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GVOLUME) {
+ snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GVOLUME], left, right);
+ } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GLOBAL) {
+ snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GLOBAL], left, right);
+ }
+ if (left || right) {
+ if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH)
+ snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0);
+ if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH)
+ snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0);
+ if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE)
+ snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1);
+ if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE)
+ snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1);
+ } else {
+ if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) {
+ snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0);
+ } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) {
+ snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0);
+ } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) {
+ snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1);
+ } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) {
+ snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1);
+ }
+ }
+ return 0;
+}
+
+static int snd_mixer_oss_get_recsrc1_sw(snd_mixer_oss_file_t *fmixer,
+ snd_mixer_oss_slot_t *pslot,
+ int *active)
+{
+ struct slot *slot = (struct slot *)pslot->private_data;
+ int left, right;
+
+ left = right = 1;
+ snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], &left, &right, 0);
+ *active = (left || right) ? 1 : 0;
+ return 0;
+}
+
+static int snd_mixer_oss_get_recsrc1_route(snd_mixer_oss_file_t *fmixer,
+ snd_mixer_oss_slot_t *pslot,
+ int *active)
+{
+ struct slot *slot = (struct slot *)pslot->private_data;
+ int left, right;
+
+ left = right = 1;
+ snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], &left, &right, 1);
+ *active = (left || right) ? 1 : 0;
+ return 0;
+}
+
+static int snd_mixer_oss_put_recsrc1_sw(snd_mixer_oss_file_t *fmixer,
+ snd_mixer_oss_slot_t *pslot,
+ int active)
+{
+ struct slot *slot = (struct slot *)pslot->private_data;
+
+ snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], active, active, 0);
+ return 0;
+}
+
+static int snd_mixer_oss_put_recsrc1_route(snd_mixer_oss_file_t *fmixer,
+ snd_mixer_oss_slot_t *pslot,
+ int active)
+{
+ struct slot *slot = (struct slot *)pslot->private_data;
+
+ snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], active, active, 1);
+ return 0;
+}
+
+static int snd_mixer_oss_get_recsrc2(snd_mixer_oss_file_t *fmixer, unsigned int *active_index)
+{
+ snd_card_t *card = fmixer->card;
+ snd_mixer_oss_t *mixer = fmixer->mixer;
+ snd_kcontrol_t *kctl;
+ snd_mixer_oss_slot_t *pslot;
+ struct slot *slot;
+ snd_ctl_elem_info_t *uinfo;
+ snd_ctl_elem_value_t *uctl;
+ int err, idx;
+
+ uinfo = kcalloc(1, sizeof(*uinfo), GFP_KERNEL);
+ uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL);
+ if (uinfo == NULL || uctl == NULL) {
+ err = -ENOMEM;
+ goto __unlock;
+ }
+ down_read(&card->controls_rwsem);
+ kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);
+ snd_runtime_check(kctl != NULL, err = -ENOENT; goto __unlock);
+ snd_runtime_check(!(err = kctl->info(kctl, uinfo)), goto __unlock);
+ snd_runtime_check(!(err = kctl->get(kctl, uctl)), goto __unlock);
+ for (idx = 0; idx < 32; idx++) {
+ if (!(mixer->mask_recsrc & (1 << idx)))
+ continue;
+ pslot = &mixer->slots[idx];
+ slot = (struct slot *)pslot->private_data;
+ if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE)
+ continue;
+ if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE))
+ continue;
+ if (slot->capture_item == uctl->value.enumerated.item[0]) {
+ *active_index = idx;
+ break;
+ }
+ }
+ err = 0;
+ __unlock:
+ up_read(&card->controls_rwsem);
+ kfree(uctl);
+ kfree(uinfo);
+ return err;
+}
+
+static int snd_mixer_oss_put_recsrc2(snd_mixer_oss_file_t *fmixer, unsigned int active_index)
+{
+ snd_card_t *card = fmixer->card;
+ snd_mixer_oss_t *mixer = fmixer->mixer;
+ snd_kcontrol_t *kctl;
+ snd_mixer_oss_slot_t *pslot;
+ struct slot *slot = NULL;
+ snd_ctl_elem_info_t *uinfo;
+ snd_ctl_elem_value_t *uctl;
+ int err;
+ unsigned int idx;
+
+ uinfo = kcalloc(1, sizeof(*uinfo), GFP_KERNEL);
+ uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL);
+ if (uinfo == NULL || uctl == NULL) {
+ err = -ENOMEM;
+ goto __unlock;
+ }
+ down_read(&card->controls_rwsem);
+ kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);
+ snd_runtime_check(kctl != NULL, err = -ENOENT; goto __unlock);
+ snd_runtime_check(!(err = kctl->info(kctl, uinfo)), goto __unlock);
+ for (idx = 0; idx < 32; idx++) {
+ if (!(mixer->mask_recsrc & (1 << idx)))
+ continue;
+ pslot = &mixer->slots[idx];
+ slot = (struct slot *)pslot->private_data;
+ if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE)
+ continue;
+ if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE))
+ continue;
+ if (idx == active_index)
+ break;
+ slot = NULL;
+ }
+ snd_runtime_check(slot != NULL, goto __unlock);
+ for (idx = 0; idx < uinfo->count; idx++)
+ uctl->value.enumerated.item[idx] = slot->capture_item;
+ snd_runtime_check((err = kctl->put(kctl, uctl)) >= 0, );
+ if (err > 0)
+ snd_ctl_notify(fmixer->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
+ err = 0;
+ __unlock:
+ up_read(&card->controls_rwsem);
+ kfree(uctl);
+ kfree(uinfo);
+ return err;
+}
+
+struct snd_mixer_oss_assign_table {
+ int oss_id;
+ const char *name;
+ int index;
+};
+
+static int snd_mixer_oss_build_test(snd_mixer_oss_t *mixer, struct slot *slot, const char *name, int index, int item)
+{
+ snd_ctl_elem_info_t *info;
+ snd_kcontrol_t *kcontrol;
+ snd_card_t *card = mixer->card;
+ int err;
+
+ down_read(&card->controls_rwsem);
+ kcontrol = snd_mixer_oss_test_id(mixer, name, index);
+ if (kcontrol == NULL) {
+ up_read(&card->controls_rwsem);
+ return 0;
+ }
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (! info) {
+ up_read(&card->controls_rwsem);
+ return -ENOMEM;
+ }
+ if ((err = kcontrol->info(kcontrol, info)) < 0) {
+ up_read(&card->controls_rwsem);
+ kfree(info);
+ return err;
+ }
+ slot->numid[item] = kcontrol->id.numid;
+ up_read(&card->controls_rwsem);
+ if (info->count > slot->channels)
+ slot->channels = info->count;
+ slot->present |= 1 << item;
+ kfree(info);
+ return 0;
+}
+
+static void snd_mixer_oss_slot_free(snd_mixer_oss_slot_t *chn)
+{
+ struct slot *p = (struct slot *)chn->private_data;
+ if (p) {
+ if (p->allocated && p->assigned) {
+ kfree(p->assigned->name);
+ kfree(p->assigned);
+ }
+ kfree(p);
+ }
+}
+
+static void mixer_slot_clear(snd_mixer_oss_slot_t *rslot)
+{
+ int idx = rslot->number; /* remember this */
+ if (rslot->private_free)
+ rslot->private_free(rslot);
+ memset(rslot, 0, sizeof(*rslot));
+ rslot->number = idx;
+}
+
+/*
+ * build an OSS mixer element.
+ * ptr_allocated means the entry is dynamically allocated (change via proc file).
+ * when replace_old = 1, the old entry is replaced with the new one.
+ */
+static int snd_mixer_oss_build_input(snd_mixer_oss_t *mixer, struct snd_mixer_oss_assign_table *ptr, int ptr_allocated, int replace_old)
+{
+ struct slot slot;
+ struct slot *pslot;
+ snd_kcontrol_t *kctl;
+ snd_mixer_oss_slot_t *rslot;
+ char str[64];
+
+ /* check if already assigned */
+ if (mixer->slots[ptr->oss_id].get_volume && ! replace_old)
+ return 0;
+
+ memset(&slot, 0, sizeof(slot));
+ memset(slot.numid, 0xff, sizeof(slot.numid)); /* ID_UNKNOWN */
+ if (snd_mixer_oss_build_test(mixer, &slot, ptr->name, ptr->index,
+ SNDRV_MIXER_OSS_ITEM_GLOBAL))
+ return 0;
+ sprintf(str, "%s Switch", ptr->name);
+ if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
+ SNDRV_MIXER_OSS_ITEM_GSWITCH))
+ return 0;
+ sprintf(str, "%s Route", ptr->name);
+ if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
+ SNDRV_MIXER_OSS_ITEM_GROUTE))
+ return 0;
+ sprintf(str, "%s Volume", ptr->name);
+ if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
+ SNDRV_MIXER_OSS_ITEM_GVOLUME))
+ return 0;
+ sprintf(str, "%s Playback Switch", ptr->name);
+ if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
+ SNDRV_MIXER_OSS_ITEM_PSWITCH))
+ return 0;
+ sprintf(str, "%s Playback Route", ptr->name);
+ if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
+ SNDRV_MIXER_OSS_ITEM_PROUTE))
+ return 0;
+ sprintf(str, "%s Playback Volume", ptr->name);
+ if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
+ SNDRV_MIXER_OSS_ITEM_PVOLUME))
+ return 0;
+ sprintf(str, "%s Capture Switch", ptr->name);
+ if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
+ SNDRV_MIXER_OSS_ITEM_CSWITCH))
+ return 0;
+ sprintf(str, "%s Capture Route", ptr->name);
+ if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
+ SNDRV_MIXER_OSS_ITEM_CROUTE))
+ return 0;
+ sprintf(str, "%s Capture Volume", ptr->name);
+ if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
+ SNDRV_MIXER_OSS_ITEM_CVOLUME))
+ return 0;
+ down_read(&mixer->card->controls_rwsem);
+ if (ptr->index == 0 && (kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0)) != NULL) {
+ snd_ctl_elem_info_t *uinfo;
+
+ uinfo = kmalloc(sizeof(*uinfo), GFP_KERNEL);
+ if (! uinfo) {
+ up_read(&mixer->card->controls_rwsem);
+ return -ENOMEM;
+ }
+
+ memset(uinfo, 0, sizeof(*uinfo));
+ if (kctl->info(kctl, uinfo)) {
+ up_read(&mixer->card->controls_rwsem);
+ return 0;
+ }
+ strcpy(str, ptr->name);
+ if (!strcmp(str, "Master"))
+ strcpy(str, "Mix");
+ if (!strcmp(str, "Master Mono"))
+ strcpy(str, "Mix Mono");
+ slot.capture_item = 0;
+ if (!strcmp(uinfo->value.enumerated.name, str)) {
+ slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE;
+ } else {
+ for (slot.capture_item = 1; slot.capture_item < uinfo->value.enumerated.items; slot.capture_item++) {
+ uinfo->value.enumerated.item = slot.capture_item;
+ if (kctl->info(kctl, uinfo)) {
+ up_read(&mixer->card->controls_rwsem);
+ return 0;
+ }
+ if (!strcmp(uinfo->value.enumerated.name, str)) {
+ slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE;
+ break;
+ }
+ }
+ }
+ kfree(uinfo);
+ }
+ up_read(&mixer->card->controls_rwsem);
+ if (slot.present != 0) {
+ pslot = (struct slot *)kmalloc(sizeof(slot), GFP_KERNEL);
+ snd_runtime_check(pslot != NULL, return -ENOMEM);
+ *pslot = slot;
+ pslot->signature = SNDRV_MIXER_OSS_SIGNATURE;
+ pslot->assigned = ptr;
+ pslot->allocated = ptr_allocated;
+ rslot = &mixer->slots[ptr->oss_id];
+ mixer_slot_clear(rslot);
+ rslot->stereo = slot.channels > 1 ? 1 : 0;
+ rslot->get_volume = snd_mixer_oss_get_volume1;
+ rslot->put_volume = snd_mixer_oss_put_volume1;
+ /* note: ES18xx have both Capture Source and XX Capture Volume !!! */
+ if (slot.present & SNDRV_MIXER_OSS_PRESENT_CSWITCH) {
+ rslot->get_recsrc = snd_mixer_oss_get_recsrc1_sw;
+ rslot->put_recsrc = snd_mixer_oss_put_recsrc1_sw;
+ } else if (slot.present & SNDRV_MIXER_OSS_PRESENT_CROUTE) {
+ rslot->get_recsrc = snd_mixer_oss_get_recsrc1_route;
+ rslot->put_recsrc = snd_mixer_oss_put_recsrc1_route;
+ } else if (slot.present & SNDRV_MIXER_OSS_PRESENT_CAPTURE) {
+ mixer->mask_recsrc |= 1 << ptr->oss_id;
+ }
+ rslot->private_data = pslot;
+ rslot->private_free = snd_mixer_oss_slot_free;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ */
+#define MIXER_VOL(name) [SOUND_MIXER_##name] = #name
+static char *oss_mixer_names[SNDRV_OSS_MAX_MIXERS] = {
+ MIXER_VOL(VOLUME),
+ MIXER_VOL(BASS),
+ MIXER_VOL(TREBLE),
+ MIXER_VOL(SYNTH),
+ MIXER_VOL(PCM),
+ MIXER_VOL(SPEAKER),
+ MIXER_VOL(LINE),
+ MIXER_VOL(MIC),
+ MIXER_VOL(CD),
+ MIXER_VOL(IMIX),
+ MIXER_VOL(ALTPCM),
+ MIXER_VOL(RECLEV),
+ MIXER_VOL(IGAIN),
+ MIXER_VOL(OGAIN),
+ MIXER_VOL(LINE1),
+ MIXER_VOL(LINE2),
+ MIXER_VOL(LINE3),
+ MIXER_VOL(DIGITAL1),
+ MIXER_VOL(DIGITAL2),
+ MIXER_VOL(DIGITAL3),
+ MIXER_VOL(PHONEIN),
+ MIXER_VOL(PHONEOUT),
+ MIXER_VOL(VIDEO),
+ MIXER_VOL(RADIO),
+ MIXER_VOL(MONITOR),
+};
+
+/*
+ * /proc interface
+ */
+
+static void snd_mixer_oss_proc_read(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ snd_mixer_oss_t *mixer = entry->private_data;
+ int i;
+
+ down(&mixer->reg_mutex);
+ for (i = 0; i < SNDRV_OSS_MAX_MIXERS; i++) {
+ struct slot *p;
+
+ if (! oss_mixer_names[i])
+ continue;
+ p = (struct slot *)mixer->slots[i].private_data;
+ snd_iprintf(buffer, "%s ", oss_mixer_names[i]);
+ if (p && p->assigned)
+ snd_iprintf(buffer, "\"%s\" %d\n",
+ p->assigned->name,
+ p->assigned->index);
+ else
+ snd_iprintf(buffer, "\"\" 0\n");
+ }
+ up(&mixer->reg_mutex);
+}
+
+static void snd_mixer_oss_proc_write(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ snd_mixer_oss_t *mixer = entry->private_data;
+ char line[128], str[32], idxstr[16], *cptr;
+ int ch, idx;
+ struct snd_mixer_oss_assign_table *tbl;
+ struct slot *slot;
+
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ cptr = snd_info_get_str(str, line, sizeof(str));
+ for (ch = 0; ch < SNDRV_OSS_MAX_MIXERS; ch++)
+ if (oss_mixer_names[ch] && strcmp(oss_mixer_names[ch], str) == 0)
+ break;
+ if (ch >= SNDRV_OSS_MAX_MIXERS) {
+ snd_printk(KERN_ERR "mixer_oss: invalid OSS volume '%s'\n", str);
+ continue;
+ }
+ cptr = snd_info_get_str(str, cptr, sizeof(str));
+ if (! *str) {
+ /* remove the entry */
+ down(&mixer->reg_mutex);
+ mixer_slot_clear(&mixer->slots[ch]);
+ up(&mixer->reg_mutex);
+ continue;
+ }
+ snd_info_get_str(idxstr, cptr, sizeof(idxstr));
+ idx = simple_strtoul(idxstr, NULL, 10);
+ if (idx >= 0x4000) { /* too big */
+ snd_printk(KERN_ERR "mixer_oss: invalid index %d\n", idx);
+ continue;
+ }
+ down(&mixer->reg_mutex);
+ slot = (struct slot *)mixer->slots[ch].private_data;
+ if (slot && slot->assigned &&
+ slot->assigned->index == idx && ! strcmp(slot->assigned->name, str))
+ /* not changed */
+ goto __unlock;
+ tbl = kmalloc(sizeof(*tbl), GFP_KERNEL);
+ if (! tbl) {
+ snd_printk(KERN_ERR "mixer_oss: no memory\n");
+ goto __unlock;
+ }
+ tbl->oss_id = ch;
+ tbl->name = snd_kmalloc_strdup(str, GFP_KERNEL);
+ if (! tbl->name) {
+ kfree(tbl);
+ goto __unlock;
+ }
+ tbl->index = idx;
+ if (snd_mixer_oss_build_input(mixer, tbl, 1, 1) <= 0) {
+ kfree(tbl->name);
+ kfree(tbl);
+ }
+ __unlock:
+ up(&mixer->reg_mutex);
+ }
+}
+
+static void snd_mixer_oss_proc_init(snd_mixer_oss_t *mixer)
+{
+ snd_info_entry_t *entry;
+
+ entry = snd_info_create_card_entry(mixer->card, "oss_mixer",
+ mixer->card->proc_root);
+ if (! entry)
+ return;
+ entry->content = SNDRV_INFO_CONTENT_TEXT;
+ entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+ entry->c.text.read_size = 8192;
+ entry->c.text.read = snd_mixer_oss_proc_read;
+ entry->c.text.write_size = 8192;
+ entry->c.text.write = snd_mixer_oss_proc_write;
+ entry->private_data = mixer;
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ entry = NULL;
+ }
+ mixer->proc_entry = entry;
+}
+
+static void snd_mixer_oss_proc_done(snd_mixer_oss_t *mixer)
+{
+ if (mixer->proc_entry) {
+ snd_info_unregister(mixer->proc_entry);
+ mixer->proc_entry = NULL;
+ }
+}
+
+static void snd_mixer_oss_build(snd_mixer_oss_t *mixer)
+{
+ static struct snd_mixer_oss_assign_table table[] = {
+ { SOUND_MIXER_VOLUME, "Master", 0 },
+ { SOUND_MIXER_VOLUME, "Front", 0 }, /* fallback */
+ { SOUND_MIXER_BASS, "Tone Control - Bass", 0 },
+ { SOUND_MIXER_TREBLE, "Tone Control - Treble", 0 },
+ { SOUND_MIXER_SYNTH, "Synth", 0 },
+ { SOUND_MIXER_SYNTH, "FM", 0 }, /* fallback */
+ { SOUND_MIXER_SYNTH, "Music", 0 }, /* fallback */
+ { SOUND_MIXER_PCM, "PCM", 0 },
+ { SOUND_MIXER_SPEAKER, "PC Speaker", 0 },
+ { SOUND_MIXER_LINE, "Line", 0 },
+ { SOUND_MIXER_MIC, "Mic", 0 },
+ { SOUND_MIXER_CD, "CD", 0 },
+ { SOUND_MIXER_IMIX, "Monitor Mix", 0 },
+ { SOUND_MIXER_ALTPCM, "PCM", 1 },
+ { SOUND_MIXER_ALTPCM, "Headphone", 0 }, /* fallback */
+ { SOUND_MIXER_ALTPCM, "Wave", 0 }, /* fallback */
+ { SOUND_MIXER_RECLEV, "-- nothing --", 0 },
+ { SOUND_MIXER_IGAIN, "Capture", 0 },
+ { SOUND_MIXER_OGAIN, "Playback", 0 },
+ { SOUND_MIXER_LINE1, "Aux", 0 },
+ { SOUND_MIXER_LINE2, "Aux", 1 },
+ { SOUND_MIXER_LINE3, "Aux", 2 },
+ { SOUND_MIXER_DIGITAL1, "Digital", 0 },
+ { SOUND_MIXER_DIGITAL1, "IEC958", 0 }, /* fallback */
+ { SOUND_MIXER_DIGITAL1, "IEC958 Optical", 0 }, /* fallback */
+ { SOUND_MIXER_DIGITAL1, "IEC958 Coaxial", 0 }, /* fallback */
+ { SOUND_MIXER_DIGITAL2, "Digital", 1 },
+ { SOUND_MIXER_DIGITAL3, "Digital", 2 },
+ { SOUND_MIXER_PHONEIN, "Phone", 0 },
+ { SOUND_MIXER_PHONEOUT, "Master Mono", 0 },
+ { SOUND_MIXER_PHONEOUT, "Phone", 0 }, /* fallback */
+ { SOUND_MIXER_VIDEO, "Video", 0 },
+ { SOUND_MIXER_RADIO, "Radio", 0 },
+ { SOUND_MIXER_MONITOR, "Monitor", 0 }
+ };
+ unsigned int idx;
+
+ for (idx = 0; idx < ARRAY_SIZE(table); idx++)
+ snd_mixer_oss_build_input(mixer, &table[idx], 0, 0);
+ if (mixer->mask_recsrc) {
+ mixer->get_recsrc = snd_mixer_oss_get_recsrc2;
+ mixer->put_recsrc = snd_mixer_oss_put_recsrc2;
+ }
+}
+
+/*
+ *
+ */
+
+static int snd_mixer_oss_free1(void *private)
+{
+ snd_mixer_oss_t *mixer = private;
+ snd_card_t * card;
+ int idx;
+
+ snd_assert(mixer != NULL, return -ENXIO);
+ card = mixer->card;
+ snd_assert(mixer == card->mixer_oss, return -ENXIO);
+ card->mixer_oss = NULL;
+ for (idx = 0; idx < SNDRV_OSS_MAX_MIXERS; idx++) {
+ snd_mixer_oss_slot_t *chn = &mixer->slots[idx];
+ if (chn->private_free)
+ chn->private_free(chn);
+ }
+ kfree(mixer);
+ return 0;
+}
+
+static int snd_mixer_oss_notify_handler(snd_card_t * card, int cmd)
+{
+ snd_mixer_oss_t *mixer;
+
+ if (cmd == SND_MIXER_OSS_NOTIFY_REGISTER) {
+ char name[128];
+ int idx, err;
+
+ mixer = kcalloc(2, sizeof(*mixer), GFP_KERNEL);
+ if (mixer == NULL)
+ return -ENOMEM;
+ init_MUTEX(&mixer->reg_mutex);
+ sprintf(name, "mixer%i%i", card->number, 0);
+ if ((err = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER,
+ card, 0,
+ &snd_mixer_oss_reg,
+ name)) < 0) {
+ snd_printk("unable to register OSS mixer device %i:%i\n", card->number, 0);
+ kfree(mixer);
+ return err;
+ }
+ mixer->oss_dev_alloc = 1;
+ mixer->card = card;
+ if (*card->mixername)
+ strlcpy(mixer->name, card->mixername, sizeof(mixer->name));
+ else
+ strlcpy(mixer->name, name, sizeof(mixer->name));
+#ifdef SNDRV_OSS_INFO_DEV_MIXERS
+ snd_oss_info_register(SNDRV_OSS_INFO_DEV_MIXERS,
+ card->number,
+ mixer->name);
+#endif
+ for (idx = 0; idx < SNDRV_OSS_MAX_MIXERS; idx++)
+ mixer->slots[idx].number = idx;
+ card->mixer_oss = mixer;
+ snd_mixer_oss_build(mixer);
+ snd_mixer_oss_proc_init(mixer);
+ } else if (cmd == SND_MIXER_OSS_NOTIFY_DISCONNECT) {
+ mixer = card->mixer_oss;
+ if (mixer == NULL || !mixer->oss_dev_alloc)
+ return 0;
+ snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, mixer->card, 0);
+ mixer->oss_dev_alloc = 0;
+ } else { /* free */
+ mixer = card->mixer_oss;
+ if (mixer == NULL)
+ return 0;
+#ifdef SNDRV_OSS_INFO_DEV_MIXERS
+ snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIXERS, mixer->card->number);
+#endif
+ if (mixer->oss_dev_alloc)
+ snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, mixer->card, 0);
+ snd_mixer_oss_proc_done(mixer);
+ return snd_mixer_oss_free1(mixer);
+ }
+ return 0;
+}
+
+static int __init alsa_mixer_oss_init(void)
+{
+ int idx;
+
+ snd_mixer_oss_notify_callback = snd_mixer_oss_notify_handler;
+ for (idx = 0; idx < SNDRV_CARDS; idx++) {
+ if (snd_cards[idx])
+ snd_mixer_oss_notify_handler(snd_cards[idx], SND_MIXER_OSS_NOTIFY_REGISTER);
+ }
+ return 0;
+}
+
+static void __exit alsa_mixer_oss_exit(void)
+{
+ int idx;
+
+ snd_mixer_oss_notify_callback = NULL;
+ for (idx = 0; idx < SNDRV_CARDS; idx++) {
+ if (snd_cards[idx])
+ snd_mixer_oss_notify_handler(snd_cards[idx], SND_MIXER_OSS_NOTIFY_FREE);
+ }
+}
+
+module_init(alsa_mixer_oss_init)
+module_exit(alsa_mixer_oss_exit)
+
+EXPORT_SYMBOL(snd_mixer_oss_ioctl_card);
diff --git a/sound/core/oss/mulaw.c b/sound/core/oss/mulaw.c
new file mode 100644
index 00000000000..44ec4c66eb1
--- /dev/null
+++ b/sound/core/oss/mulaw.c
@@ -0,0 +1,308 @@
+/*
+ * Mu-Law conversion Plug-In Interface
+ * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+ * Uros Bizjak <uros@kss-loka.si>
+ *
+ * Based on reference implementation by Sun Microsystems, Inc.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include "pcm_plugin.h"
+
+#define SIGN_BIT (0x80) /* Sign bit for a u-law byte. */
+#define QUANT_MASK (0xf) /* Quantization field mask. */
+#define NSEGS (8) /* Number of u-law segments. */
+#define SEG_SHIFT (4) /* Left shift for segment number. */
+#define SEG_MASK (0x70) /* Segment field mask. */
+
+static inline int val_seg(int val)
+{
+ int r = 0;
+ val >>= 7;
+ if (val & 0xf0) {
+ val >>= 4;
+ r += 4;
+ }
+ if (val & 0x0c) {
+ val >>= 2;
+ r += 2;
+ }
+ if (val & 0x02)
+ r += 1;
+ return r;
+}
+
+#define BIAS (0x84) /* Bias for linear code. */
+
+/*
+ * linear2ulaw() - Convert a linear PCM value to u-law
+ *
+ * In order to simplify the encoding process, the original linear magnitude
+ * is biased by adding 33 which shifts the encoding range from (0 - 8158) to
+ * (33 - 8191). The result can be seen in the following encoding table:
+ *
+ * Biased Linear Input Code Compressed Code
+ * ------------------------ ---------------
+ * 00000001wxyza 000wxyz
+ * 0000001wxyzab 001wxyz
+ * 000001wxyzabc 010wxyz
+ * 00001wxyzabcd 011wxyz
+ * 0001wxyzabcde 100wxyz
+ * 001wxyzabcdef 101wxyz
+ * 01wxyzabcdefg 110wxyz
+ * 1wxyzabcdefgh 111wxyz
+ *
+ * Each biased linear code has a leading 1 which identifies the segment
+ * number. The value of the segment number is equal to 7 minus the number
+ * of leading 0's. The quantization interval is directly available as the
+ * four bits wxyz. * The trailing bits (a - h) are ignored.
+ *
+ * Ordinarily the complement of the resulting code word is used for
+ * transmission, and so the code word is complemented before it is returned.
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+static unsigned char linear2ulaw(int pcm_val) /* 2's complement (16-bit range) */
+{
+ int mask;
+ int seg;
+ unsigned char uval;
+
+ /* Get the sign and the magnitude of the value. */
+ if (pcm_val < 0) {
+ pcm_val = BIAS - pcm_val;
+ mask = 0x7F;
+ } else {
+ pcm_val += BIAS;
+ mask = 0xFF;
+ }
+ if (pcm_val > 0x7FFF)
+ pcm_val = 0x7FFF;
+
+ /* Convert the scaled magnitude to segment number. */
+ seg = val_seg(pcm_val);
+
+ /*
+ * Combine the sign, segment, quantization bits;
+ * and complement the code word.
+ */
+ uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF);
+ return uval ^ mask;
+}
+
+/*
+ * ulaw2linear() - Convert a u-law value to 16-bit linear PCM
+ *
+ * First, a biased linear code is derived from the code word. An unbiased
+ * output can then be obtained by subtracting 33 from the biased code.
+ *
+ * Note that this function expects to be passed the complement of the
+ * original code word. This is in keeping with ISDN conventions.
+ */
+static int ulaw2linear(unsigned char u_val)
+{
+ int t;
+
+ /* Complement to obtain normal u-law value. */
+ u_val = ~u_val;
+
+ /*
+ * Extract and bias the quantization bits. Then
+ * shift up by the segment number and subtract out the bias.
+ */
+ t = ((u_val & QUANT_MASK) << 3) + BIAS;
+ t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
+
+ return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
+}
+
+/*
+ * Basic Mu-Law plugin
+ */
+
+typedef void (*mulaw_f)(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels,
+ snd_pcm_plugin_channel_t *dst_channels,
+ snd_pcm_uframes_t frames);
+
+typedef struct mulaw_private_data {
+ mulaw_f func;
+ int conv;
+} mulaw_t;
+
+static void mulaw_decode(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels,
+ snd_pcm_plugin_channel_t *dst_channels,
+ snd_pcm_uframes_t frames)
+{
+#define PUT_S16_LABELS
+#include "plugin_ops.h"
+#undef PUT_S16_LABELS
+ mulaw_t *data = (mulaw_t *)plugin->extra_data;
+ void *put = put_s16_labels[data->conv];
+ int channel;
+ int nchannels = plugin->src_format.channels;
+ for (channel = 0; channel < nchannels; ++channel) {
+ char *src;
+ char *dst;
+ int src_step, dst_step;
+ snd_pcm_uframes_t frames1;
+ if (!src_channels[channel].enabled) {
+ if (dst_channels[channel].wanted)
+ snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format);
+ dst_channels[channel].enabled = 0;
+ continue;
+ }
+ dst_channels[channel].enabled = 1;
+ src = src_channels[channel].area.addr + src_channels[channel].area.first / 8;
+ dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
+ src_step = src_channels[channel].area.step / 8;
+ dst_step = dst_channels[channel].area.step / 8;
+ frames1 = frames;
+ while (frames1-- > 0) {
+ signed short sample = ulaw2linear(*src);
+ goto *put;
+#define PUT_S16_END after
+#include "plugin_ops.h"
+#undef PUT_S16_END
+ after:
+ src += src_step;
+ dst += dst_step;
+ }
+ }
+}
+
+static void mulaw_encode(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels,
+ snd_pcm_plugin_channel_t *dst_channels,
+ snd_pcm_uframes_t frames)
+{
+#define GET_S16_LABELS
+#include "plugin_ops.h"
+#undef GET_S16_LABELS
+ mulaw_t *data = (mulaw_t *)plugin->extra_data;
+ void *get = get_s16_labels[data->conv];
+ int channel;
+ int nchannels = plugin->src_format.channels;
+ signed short sample = 0;
+ for (channel = 0; channel < nchannels; ++channel) {
+ char *src;
+ char *dst;
+ int src_step, dst_step;
+ snd_pcm_uframes_t frames1;
+ if (!src_channels[channel].enabled) {
+ if (dst_channels[channel].wanted)
+ snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format);
+ dst_channels[channel].enabled = 0;
+ continue;
+ }
+ dst_channels[channel].enabled = 1;
+ src = src_channels[channel].area.addr + src_channels[channel].area.first / 8;
+ dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
+ src_step = src_channels[channel].area.step / 8;
+ dst_step = dst_channels[channel].area.step / 8;
+ frames1 = frames;
+ while (frames1-- > 0) {
+ goto *get;
+#define GET_S16_END after
+#include "plugin_ops.h"
+#undef GET_S16_END
+ after:
+ *dst = linear2ulaw(sample);
+ src += src_step;
+ dst += dst_step;
+ }
+ }
+}
+
+static snd_pcm_sframes_t mulaw_transfer(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels,
+ snd_pcm_plugin_channel_t *dst_channels,
+ snd_pcm_uframes_t frames)
+{
+ mulaw_t *data;
+
+ snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
+ if (frames == 0)
+ return 0;
+#ifdef CONFIG_SND_DEBUG
+ {
+ unsigned int channel;
+ for (channel = 0; channel < plugin->src_format.channels; channel++) {
+ snd_assert(src_channels[channel].area.first % 8 == 0 &&
+ src_channels[channel].area.step % 8 == 0,
+ return -ENXIO);
+ snd_assert(dst_channels[channel].area.first % 8 == 0 &&
+ dst_channels[channel].area.step % 8 == 0,
+ return -ENXIO);
+ }
+ }
+#endif
+ data = (mulaw_t *)plugin->extra_data;
+ data->func(plugin, src_channels, dst_channels, frames);
+ return frames;
+}
+
+int snd_pcm_plugin_build_mulaw(snd_pcm_plug_t *plug,
+ snd_pcm_plugin_format_t *src_format,
+ snd_pcm_plugin_format_t *dst_format,
+ snd_pcm_plugin_t **r_plugin)
+{
+ int err;
+ mulaw_t *data;
+ snd_pcm_plugin_t *plugin;
+ snd_pcm_plugin_format_t *format;
+ mulaw_f func;
+
+ snd_assert(r_plugin != NULL, return -ENXIO);
+ *r_plugin = NULL;
+
+ snd_assert(src_format->rate == dst_format->rate, return -ENXIO);
+ snd_assert(src_format->channels == dst_format->channels, return -ENXIO);
+
+ if (dst_format->format == SNDRV_PCM_FORMAT_MU_LAW) {
+ format = src_format;
+ func = mulaw_encode;
+ }
+ else if (src_format->format == SNDRV_PCM_FORMAT_MU_LAW) {
+ format = dst_format;
+ func = mulaw_decode;
+ }
+ else {
+ snd_BUG();
+ return -EINVAL;
+ }
+ snd_assert(snd_pcm_format_linear(format->format) != 0, return -ENXIO);
+
+ err = snd_pcm_plugin_build(plug, "Mu-Law<->linear conversion",
+ src_format, dst_format,
+ sizeof(mulaw_t), &plugin);
+ if (err < 0)
+ return err;
+ data = (mulaw_t*)plugin->extra_data;
+ data->func = func;
+ data->conv = getput_index(format->format);
+ snd_assert(data->conv >= 0 && data->conv < 4*2*2, return -EINVAL);
+ plugin->transfer = mulaw_transfer;
+ *r_plugin = plugin;
+ return 0;
+}
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c
new file mode 100644
index 00000000000..1a805020f57
--- /dev/null
+++ b/sound/core/oss/pcm_oss.c
@@ -0,0 +1,2530 @@
+/*
+ * Digital Audio (PCM) abstract layer / OSS compatible
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#if 0
+#define PLUGIN_DEBUG
+#endif
+#if 0
+#define OSS_DEBUG
+#endif
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/vmalloc.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/minors.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "pcm_plugin.h"
+#include <sound/info.h>
+#include <linux/soundcard.h>
+#include <sound/initval.h>
+
+#define OSS_ALSAEMULVER _SIOR ('M', 249, int)
+
+static int dsp_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 0};
+static int adsp_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1};
+static int nonblock_open = 1;
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Abramo Bagnara <abramo@alsa-project.org>");
+MODULE_DESCRIPTION("PCM OSS emulation for ALSA.");
+MODULE_LICENSE("GPL");
+module_param_array(dsp_map, int, NULL, 0444);
+MODULE_PARM_DESC(dsp_map, "PCM device number assigned to 1st OSS device.");
+module_param_array(adsp_map, int, NULL, 0444);
+MODULE_PARM_DESC(adsp_map, "PCM device number assigned to 2nd OSS device.");
+module_param(nonblock_open, bool, 0644);
+MODULE_PARM_DESC(nonblock_open, "Don't block opening busy PCM devices.");
+MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_PCM);
+MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_PCM1);
+
+extern int snd_mixer_oss_ioctl_card(snd_card_t *card, unsigned int cmd, unsigned long arg);
+static int snd_pcm_oss_get_rate(snd_pcm_oss_file_t *pcm_oss_file);
+static int snd_pcm_oss_get_channels(snd_pcm_oss_file_t *pcm_oss_file);
+static int snd_pcm_oss_get_format(snd_pcm_oss_file_t *pcm_oss_file);
+
+static inline mm_segment_t snd_enter_user(void)
+{
+ mm_segment_t fs = get_fs();
+ set_fs(get_ds());
+ return fs;
+}
+
+static inline void snd_leave_user(mm_segment_t fs)
+{
+ set_fs(fs);
+}
+
+static int snd_pcm_oss_plugin_clear(snd_pcm_substream_t *substream)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_pcm_plugin_t *plugin, *next;
+
+ plugin = runtime->oss.plugin_first;
+ while (plugin) {
+ next = plugin->next;
+ snd_pcm_plugin_free(plugin);
+ plugin = next;
+ }
+ runtime->oss.plugin_first = runtime->oss.plugin_last = NULL;
+ return 0;
+}
+
+static int snd_pcm_plugin_insert(snd_pcm_plugin_t *plugin)
+{
+ snd_pcm_runtime_t *runtime = plugin->plug->runtime;
+ plugin->next = runtime->oss.plugin_first;
+ plugin->prev = NULL;
+ if (runtime->oss.plugin_first) {
+ runtime->oss.plugin_first->prev = plugin;
+ runtime->oss.plugin_first = plugin;
+ } else {
+ runtime->oss.plugin_last =
+ runtime->oss.plugin_first = plugin;
+ }
+ return 0;
+}
+
+int snd_pcm_plugin_append(snd_pcm_plugin_t *plugin)
+{
+ snd_pcm_runtime_t *runtime = plugin->plug->runtime;
+ plugin->next = NULL;
+ plugin->prev = runtime->oss.plugin_last;
+ if (runtime->oss.plugin_last) {
+ runtime->oss.plugin_last->next = plugin;
+ runtime->oss.plugin_last = plugin;
+ } else {
+ runtime->oss.plugin_last =
+ runtime->oss.plugin_first = plugin;
+ }
+ return 0;
+}
+
+static long snd_pcm_oss_bytes(snd_pcm_substream_t *substream, long frames)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_pcm_uframes_t buffer_size = snd_pcm_lib_buffer_bytes(substream);
+ frames = frames_to_bytes(runtime, frames);
+ if (buffer_size == runtime->oss.buffer_bytes)
+ return frames;
+ return (runtime->oss.buffer_bytes * frames) / buffer_size;
+}
+
+static long snd_pcm_alsa_frames(snd_pcm_substream_t *substream, long bytes)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_pcm_uframes_t buffer_size = snd_pcm_lib_buffer_bytes(substream);
+ if (buffer_size == runtime->oss.buffer_bytes)
+ return bytes_to_frames(runtime, bytes);
+ return bytes_to_frames(runtime, (buffer_size * bytes) / runtime->oss.buffer_bytes);
+}
+
+static int snd_pcm_oss_format_from(int format)
+{
+ switch (format) {
+ case AFMT_MU_LAW: return SNDRV_PCM_FORMAT_MU_LAW;
+ case AFMT_A_LAW: return SNDRV_PCM_FORMAT_A_LAW;
+ case AFMT_IMA_ADPCM: return SNDRV_PCM_FORMAT_IMA_ADPCM;
+ case AFMT_U8: return SNDRV_PCM_FORMAT_U8;
+ case AFMT_S16_LE: return SNDRV_PCM_FORMAT_S16_LE;
+ case AFMT_S16_BE: return SNDRV_PCM_FORMAT_S16_BE;
+ case AFMT_S8: return SNDRV_PCM_FORMAT_S8;
+ case AFMT_U16_LE: return SNDRV_PCM_FORMAT_U16_LE;
+ case AFMT_U16_BE: return SNDRV_PCM_FORMAT_U16_BE;
+ case AFMT_MPEG: return SNDRV_PCM_FORMAT_MPEG;
+ default: return SNDRV_PCM_FORMAT_U8;
+ }
+}
+
+static int snd_pcm_oss_format_to(int format)
+{
+ switch (format) {
+ case SNDRV_PCM_FORMAT_MU_LAW: return AFMT_MU_LAW;
+ case SNDRV_PCM_FORMAT_A_LAW: return AFMT_A_LAW;
+ case SNDRV_PCM_FORMAT_IMA_ADPCM: return AFMT_IMA_ADPCM;
+ case SNDRV_PCM_FORMAT_U8: return AFMT_U8;
+ case SNDRV_PCM_FORMAT_S16_LE: return AFMT_S16_LE;
+ case SNDRV_PCM_FORMAT_S16_BE: return AFMT_S16_BE;
+ case SNDRV_PCM_FORMAT_S8: return AFMT_S8;
+ case SNDRV_PCM_FORMAT_U16_LE: return AFMT_U16_LE;
+ case SNDRV_PCM_FORMAT_U16_BE: return AFMT_U16_BE;
+ case SNDRV_PCM_FORMAT_MPEG: return AFMT_MPEG;
+ default: return -EINVAL;
+ }
+}
+
+static int snd_pcm_oss_period_size(snd_pcm_substream_t *substream,
+ snd_pcm_hw_params_t *oss_params,
+ snd_pcm_hw_params_t *slave_params)
+{
+ size_t s;
+ size_t oss_buffer_size, oss_period_size, oss_periods;
+ size_t min_period_size, max_period_size;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ size_t oss_frame_size;
+
+ oss_frame_size = snd_pcm_format_physical_width(params_format(oss_params)) *
+ params_channels(oss_params) / 8;
+
+ oss_buffer_size = snd_pcm_plug_client_size(substream,
+ snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, NULL)) * oss_frame_size;
+ oss_buffer_size = 1 << ld2(oss_buffer_size);
+ if (atomic_read(&runtime->mmap_count)) {
+ if (oss_buffer_size > runtime->oss.mmap_bytes)
+ oss_buffer_size = runtime->oss.mmap_bytes;
+ }
+
+ if (substream->oss.setup &&
+ substream->oss.setup->period_size > 16)
+ oss_period_size = substream->oss.setup->period_size;
+ else if (runtime->oss.fragshift) {
+ oss_period_size = 1 << runtime->oss.fragshift;
+ if (oss_period_size > oss_buffer_size / 2)
+ oss_period_size = oss_buffer_size / 2;
+ } else {
+ int sd;
+ size_t bytes_per_sec = params_rate(oss_params) * snd_pcm_format_physical_width(params_format(oss_params)) * params_channels(oss_params) / 8;
+
+ oss_period_size = oss_buffer_size;
+ do {
+ oss_period_size /= 2;
+ } while (oss_period_size > bytes_per_sec);
+ if (runtime->oss.subdivision == 0) {
+ sd = 4;
+ if (oss_period_size / sd > 4096)
+ sd *= 2;
+ if (oss_period_size / sd < 4096)
+ sd = 1;
+ } else
+ sd = runtime->oss.subdivision;
+ oss_period_size /= sd;
+ if (oss_period_size < 16)
+ oss_period_size = 16;
+ }
+
+ min_period_size = snd_pcm_plug_client_size(substream,
+ snd_pcm_hw_param_value_min(slave_params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, NULL));
+ min_period_size *= oss_frame_size;
+ min_period_size = 1 << (ld2(min_period_size - 1) + 1);
+ if (oss_period_size < min_period_size)
+ oss_period_size = min_period_size;
+
+ max_period_size = snd_pcm_plug_client_size(substream,
+ snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, NULL));
+ max_period_size *= oss_frame_size;
+ max_period_size = 1 << ld2(max_period_size);
+ if (oss_period_size > max_period_size)
+ oss_period_size = max_period_size;
+
+ oss_periods = oss_buffer_size / oss_period_size;
+
+ if (substream->oss.setup) {
+ if (substream->oss.setup->periods > 1)
+ oss_periods = substream->oss.setup->periods;
+ }
+
+ s = snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIODS, NULL);
+ if (runtime->oss.maxfrags && s > runtime->oss.maxfrags)
+ s = runtime->oss.maxfrags;
+ if (oss_periods > s)
+ oss_periods = s;
+
+ s = snd_pcm_hw_param_value_min(slave_params, SNDRV_PCM_HW_PARAM_PERIODS, NULL);
+ if (s < 2)
+ s = 2;
+ if (oss_periods < s)
+ oss_periods = s;
+
+ while (oss_period_size * oss_periods > oss_buffer_size)
+ oss_period_size /= 2;
+
+ snd_assert(oss_period_size >= 16, return -EINVAL);
+ runtime->oss.period_bytes = oss_period_size;
+ runtime->oss.period_frames = 1;
+ runtime->oss.periods = oss_periods;
+ return 0;
+}
+
+static int choose_rate(snd_pcm_substream_t *substream,
+ snd_pcm_hw_params_t *params, unsigned int best_rate)
+{
+ snd_interval_t *it;
+ snd_pcm_hw_params_t *save;
+ unsigned int rate, prev;
+
+ save = kmalloc(sizeof(*save), GFP_KERNEL);
+ if (save == NULL)
+ return -ENOMEM;
+ *save = *params;
+ it = hw_param_interval(save, SNDRV_PCM_HW_PARAM_RATE);
+
+ /* try multiples of the best rate */
+ rate = best_rate;
+ for (;;) {
+ if (it->max < rate || (it->max == rate && it->openmax))
+ break;
+ if (it->min < rate || (it->min == rate && !it->openmin)) {
+ int ret;
+ ret = snd_pcm_hw_param_set(substream, params,
+ SNDRV_PCM_HW_PARAM_RATE,
+ rate, 0);
+ if (ret == (int)rate) {
+ kfree(save);
+ return rate;
+ }
+ *params = *save;
+ }
+ prev = rate;
+ rate += best_rate;
+ if (rate <= prev)
+ break;
+ }
+
+ /* not found, use the nearest rate */
+ kfree(save);
+ return snd_pcm_hw_param_near(substream, params, SNDRV_PCM_HW_PARAM_RATE, best_rate, NULL);
+}
+
+static int snd_pcm_oss_change_params(snd_pcm_substream_t *substream)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_pcm_hw_params_t *params, *sparams;
+ snd_pcm_sw_params_t *sw_params;
+ ssize_t oss_buffer_size, oss_period_size;
+ size_t oss_frame_size;
+ int err;
+ int direct;
+ int format, sformat, n;
+ snd_mask_t sformat_mask;
+ snd_mask_t mask;
+
+ sw_params = kmalloc(sizeof(*sw_params), GFP_KERNEL);
+ params = kmalloc(sizeof(*params), GFP_KERNEL);
+ sparams = kmalloc(sizeof(*sparams), GFP_KERNEL);
+ if (!sw_params || !params || !sparams) {
+ snd_printd("No memory\n");
+ err = -ENOMEM;
+ goto failure;
+ }
+
+ if (atomic_read(&runtime->mmap_count)) {
+ direct = 1;
+ } else {
+ snd_pcm_oss_setup_t *setup = substream->oss.setup;
+ direct = (setup != NULL && setup->direct);
+ }
+
+ _snd_pcm_hw_params_any(sparams);
+ _snd_pcm_hw_param_setinteger(sparams, SNDRV_PCM_HW_PARAM_PERIODS);
+ _snd_pcm_hw_param_min(sparams, SNDRV_PCM_HW_PARAM_PERIODS, 2, 0);
+ snd_mask_none(&mask);
+ if (atomic_read(&runtime->mmap_count))
+ snd_mask_set(&mask, SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);
+ else {
+ snd_mask_set(&mask, SNDRV_PCM_ACCESS_RW_INTERLEAVED);
+ if (!direct)
+ snd_mask_set(&mask, SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);
+ }
+ err = snd_pcm_hw_param_mask(substream, sparams, SNDRV_PCM_HW_PARAM_ACCESS, &mask);
+ if (err < 0) {
+ snd_printd("No usable accesses\n");
+ err = -EINVAL;
+ goto failure;
+ }
+ choose_rate(substream, sparams, runtime->oss.rate);
+ snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_CHANNELS, runtime->oss.channels, NULL);
+
+ format = snd_pcm_oss_format_from(runtime->oss.format);
+
+ sformat_mask = *hw_param_mask(sparams, SNDRV_PCM_HW_PARAM_FORMAT);
+ if (direct)
+ sformat = format;
+ else
+ sformat = snd_pcm_plug_slave_format(format, &sformat_mask);
+
+ if (sformat < 0 || !snd_mask_test(&sformat_mask, sformat)) {
+ for (sformat = 0; sformat <= SNDRV_PCM_FORMAT_LAST; sformat++) {
+ if (snd_mask_test(&sformat_mask, sformat) &&
+ snd_pcm_oss_format_to(sformat) >= 0)
+ break;
+ }
+ if (sformat > SNDRV_PCM_FORMAT_LAST) {
+ snd_printd("Cannot find a format!!!\n");
+ err = -EINVAL;
+ goto failure;
+ }
+ }
+ err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, sformat, 0);
+ snd_assert(err >= 0, goto failure);
+
+ if (direct) {
+ memcpy(params, sparams, sizeof(*params));
+ } else {
+ _snd_pcm_hw_params_any(params);
+ _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS,
+ SNDRV_PCM_ACCESS_RW_INTERLEAVED, 0);
+ _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ snd_pcm_oss_format_from(runtime->oss.format), 0);
+ _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS,
+ runtime->oss.channels, 0);
+ _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE,
+ runtime->oss.rate, 0);
+ pdprintf("client: access = %i, format = %i, channels = %i, rate = %i\n",
+ params_access(params), params_format(params),
+ params_channels(params), params_rate(params));
+ }
+ pdprintf("slave: access = %i, format = %i, channels = %i, rate = %i\n",
+ params_access(sparams), params_format(sparams),
+ params_channels(sparams), params_rate(sparams));
+
+ oss_frame_size = snd_pcm_format_physical_width(params_format(params)) *
+ params_channels(params) / 8;
+
+ snd_pcm_oss_plugin_clear(substream);
+ if (!direct) {
+ /* add necessary plugins */
+ snd_pcm_oss_plugin_clear(substream);
+ if ((err = snd_pcm_plug_format_plugins(substream,
+ params,
+ sparams)) < 0) {
+ snd_printd("snd_pcm_plug_format_plugins failed: %i\n", err);
+ snd_pcm_oss_plugin_clear(substream);
+ goto failure;
+ }
+ if (runtime->oss.plugin_first) {
+ snd_pcm_plugin_t *plugin;
+ if ((err = snd_pcm_plugin_build_io(substream, sparams, &plugin)) < 0) {
+ snd_printd("snd_pcm_plugin_build_io failed: %i\n", err);
+ snd_pcm_oss_plugin_clear(substream);
+ goto failure;
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ err = snd_pcm_plugin_append(plugin);
+ } else {
+ err = snd_pcm_plugin_insert(plugin);
+ }
+ if (err < 0) {
+ snd_pcm_oss_plugin_clear(substream);
+ goto failure;
+ }
+ }
+ }
+
+ err = snd_pcm_oss_period_size(substream, params, sparams);
+ if (err < 0)
+ goto failure;
+
+ n = snd_pcm_plug_slave_size(substream, runtime->oss.period_bytes / oss_frame_size);
+ err = snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, n, NULL);
+ snd_assert(err >= 0, goto failure);
+
+ err = snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_PERIODS,
+ runtime->oss.periods, NULL);
+ snd_assert(err >= 0, goto failure);
+
+ snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
+
+ if ((err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, sparams)) < 0) {
+ snd_printd("HW_PARAMS failed: %i\n", err);
+ goto failure;
+ }
+
+ memset(sw_params, 0, sizeof(*sw_params));
+ if (runtime->oss.trigger) {
+ sw_params->start_threshold = 1;
+ } else {
+ sw_params->start_threshold = runtime->boundary;
+ }
+ if (atomic_read(&runtime->mmap_count) || substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ sw_params->stop_threshold = runtime->boundary;
+ else
+ sw_params->stop_threshold = runtime->buffer_size;
+ sw_params->tstamp_mode = SNDRV_PCM_TSTAMP_NONE;
+ sw_params->period_step = 1;
+ sw_params->sleep_min = 0;
+ sw_params->avail_min = 1;
+ sw_params->xfer_align = 1;
+ if (atomic_read(&runtime->mmap_count) ||
+ (substream->oss.setup && substream->oss.setup->nosilence)) {
+ sw_params->silence_threshold = 0;
+ sw_params->silence_size = 0;
+ } else {
+ snd_pcm_uframes_t frames;
+ frames = runtime->period_size + 16;
+ if (frames > runtime->buffer_size)
+ frames = runtime->buffer_size;
+ sw_params->silence_threshold = frames;
+ sw_params->silence_size = frames;
+ }
+
+ if ((err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_SW_PARAMS, sw_params)) < 0) {
+ snd_printd("SW_PARAMS failed: %i\n", err);
+ goto failure;
+ }
+
+ runtime->oss.periods = params_periods(sparams);
+ oss_period_size = snd_pcm_plug_client_size(substream, params_period_size(sparams));
+ snd_assert(oss_period_size >= 0, err = -EINVAL; goto failure);
+ if (runtime->oss.plugin_first) {
+ err = snd_pcm_plug_alloc(substream, oss_period_size);
+ if (err < 0)
+ goto failure;
+ }
+ oss_period_size *= oss_frame_size;
+
+ oss_buffer_size = oss_period_size * runtime->oss.periods;
+ snd_assert(oss_buffer_size >= 0, err = -EINVAL; goto failure);
+
+ runtime->oss.period_bytes = oss_period_size;
+ runtime->oss.buffer_bytes = oss_buffer_size;
+
+ pdprintf("oss: period bytes = %i, buffer bytes = %i\n",
+ runtime->oss.period_bytes,
+ runtime->oss.buffer_bytes);
+ pdprintf("slave: period_size = %i, buffer_size = %i\n",
+ params_period_size(sparams),
+ params_buffer_size(sparams));
+
+ runtime->oss.format = snd_pcm_oss_format_to(params_format(params));
+ runtime->oss.channels = params_channels(params);
+ runtime->oss.rate = params_rate(params);
+
+ runtime->oss.params = 0;
+ runtime->oss.prepare = 1;
+ vfree(runtime->oss.buffer);
+ runtime->oss.buffer = vmalloc(runtime->oss.period_bytes);
+ runtime->oss.buffer_used = 0;
+ if (runtime->dma_area)
+ snd_pcm_format_set_silence(runtime->format, runtime->dma_area, bytes_to_samples(runtime, runtime->dma_bytes));
+
+ runtime->oss.period_frames = snd_pcm_alsa_frames(substream, oss_period_size);
+
+ err = 0;
+failure:
+ kfree(sw_params);
+ kfree(params);
+ kfree(sparams);
+ return err;
+}
+
+static int snd_pcm_oss_get_active_substream(snd_pcm_oss_file_t *pcm_oss_file, snd_pcm_substream_t **r_substream)
+{
+ int idx, err;
+ snd_pcm_substream_t *asubstream = NULL, *substream;
+
+ for (idx = 0; idx < 2; idx++) {
+ substream = pcm_oss_file->streams[idx];
+ if (substream == NULL)
+ continue;
+ if (asubstream == NULL)
+ asubstream = substream;
+ if (substream->runtime->oss.params) {
+ err = snd_pcm_oss_change_params(substream);
+ if (err < 0)
+ return err;
+ }
+ }
+ snd_assert(asubstream != NULL, return -EIO);
+ if (r_substream)
+ *r_substream = asubstream;
+ return 0;
+}
+
+static int snd_pcm_oss_prepare(snd_pcm_substream_t *substream)
+{
+ int err;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, NULL);
+ if (err < 0) {
+ snd_printd("snd_pcm_oss_prepare: SNDRV_PCM_IOCTL_PREPARE failed\n");
+ return err;
+ }
+ runtime->oss.prepare = 0;
+ runtime->oss.prev_hw_ptr_interrupt = 0;
+ runtime->oss.period_ptr = 0;
+ runtime->oss.buffer_used = 0;
+
+ return 0;
+}
+
+static int snd_pcm_oss_make_ready(snd_pcm_substream_t *substream)
+{
+ snd_pcm_runtime_t *runtime;
+ int err;
+
+ if (substream == NULL)
+ return 0;
+ runtime = substream->runtime;
+ if (runtime->oss.params) {
+ err = snd_pcm_oss_change_params(substream);
+ if (err < 0)
+ return err;
+ }
+ if (runtime->oss.prepare) {
+ err = snd_pcm_oss_prepare(substream);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static int snd_pcm_oss_capture_position_fixup(snd_pcm_substream_t *substream, snd_pcm_sframes_t *delay)
+{
+ snd_pcm_runtime_t *runtime;
+ snd_pcm_uframes_t frames;
+ int err = 0;
+
+ while (1) {
+ err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, delay);
+ if (err < 0)
+ break;
+ runtime = substream->runtime;
+ if (*delay <= (snd_pcm_sframes_t)runtime->buffer_size)
+ break;
+ /* in case of overrun, skip whole periods like OSS/Linux driver does */
+ /* until avail(delay) <= buffer_size */
+ frames = (*delay - runtime->buffer_size) + runtime->period_size - 1;
+ frames /= runtime->period_size;
+ frames *= runtime->period_size;
+ err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_FORWARD, &frames);
+ if (err < 0)
+ break;
+ }
+ return err;
+}
+
+snd_pcm_sframes_t snd_pcm_oss_write3(snd_pcm_substream_t *substream, const char *ptr, snd_pcm_uframes_t frames, int in_kernel)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int ret;
+ while (1) {
+ if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
+ runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+#ifdef OSS_DEBUG
+ if (runtime->status->state == SNDRV_PCM_STATE_XRUN)
+ printk("pcm_oss: write: recovering from XRUN\n");
+ else
+ printk("pcm_oss: write: recovering from SUSPEND\n");
+#endif
+ ret = snd_pcm_oss_prepare(substream);
+ if (ret < 0)
+ break;
+ }
+ if (in_kernel) {
+ mm_segment_t fs;
+ fs = snd_enter_user();
+ ret = snd_pcm_lib_write(substream, (void __user *)ptr, frames);
+ snd_leave_user(fs);
+ } else {
+ ret = snd_pcm_lib_write(substream, (void __user *)ptr, frames);
+ }
+ if (ret != -EPIPE && ret != -ESTRPIPE)
+ break;
+ /* test, if we can't store new data, because the stream */
+ /* has not been started */
+ if (runtime->status->state == SNDRV_PCM_STATE_PREPARED)
+ return -EAGAIN;
+ }
+ return ret;
+}
+
+snd_pcm_sframes_t snd_pcm_oss_read3(snd_pcm_substream_t *substream, char *ptr, snd_pcm_uframes_t frames, int in_kernel)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_pcm_sframes_t delay;
+ int ret;
+ while (1) {
+ if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
+ runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+#ifdef OSS_DEBUG
+ if (runtime->status->state == SNDRV_PCM_STATE_XRUN)
+ printk("pcm_oss: read: recovering from XRUN\n");
+ else
+ printk("pcm_oss: read: recovering from SUSPEND\n");
+#endif
+ ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL);
+ if (ret < 0)
+ break;
+ } else if (runtime->status->state == SNDRV_PCM_STATE_SETUP) {
+ ret = snd_pcm_oss_prepare(substream);
+ if (ret < 0)
+ break;
+ }
+ ret = snd_pcm_oss_capture_position_fixup(substream, &delay);
+ if (ret < 0)
+ break;
+ if (in_kernel) {
+ mm_segment_t fs;
+ fs = snd_enter_user();
+ ret = snd_pcm_lib_read(substream, (void __user *)ptr, frames);
+ snd_leave_user(fs);
+ } else {
+ ret = snd_pcm_lib_read(substream, (void __user *)ptr, frames);
+ }
+ if (ret == -EPIPE) {
+ if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
+ ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
+ if (ret < 0)
+ break;
+ }
+ continue;
+ }
+ if (ret != -ESTRPIPE)
+ break;
+ }
+ return ret;
+}
+
+snd_pcm_sframes_t snd_pcm_oss_writev3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int ret;
+ while (1) {
+ if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
+ runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+#ifdef OSS_DEBUG
+ if (runtime->status->state == SNDRV_PCM_STATE_XRUN)
+ printk("pcm_oss: writev: recovering from XRUN\n");
+ else
+ printk("pcm_oss: writev: recovering from SUSPEND\n");
+#endif
+ ret = snd_pcm_oss_prepare(substream);
+ if (ret < 0)
+ break;
+ }
+ if (in_kernel) {
+ mm_segment_t fs;
+ fs = snd_enter_user();
+ ret = snd_pcm_lib_writev(substream, (void __user **)bufs, frames);
+ snd_leave_user(fs);
+ } else {
+ ret = snd_pcm_lib_writev(substream, (void __user **)bufs, frames);
+ }
+ if (ret != -EPIPE && ret != -ESTRPIPE)
+ break;
+
+ /* test, if we can't store new data, because the stream */
+ /* has not been started */
+ if (runtime->status->state == SNDRV_PCM_STATE_PREPARED)
+ return -EAGAIN;
+ }
+ return ret;
+}
+
+snd_pcm_sframes_t snd_pcm_oss_readv3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ int ret;
+ while (1) {
+ if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
+ runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+#ifdef OSS_DEBUG
+ if (runtime->status->state == SNDRV_PCM_STATE_XRUN)
+ printk("pcm_oss: readv: recovering from XRUN\n");
+ else
+ printk("pcm_oss: readv: recovering from SUSPEND\n");
+#endif
+ ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL);
+ if (ret < 0)
+ break;
+ } else if (runtime->status->state == SNDRV_PCM_STATE_SETUP) {
+ ret = snd_pcm_oss_prepare(substream);
+ if (ret < 0)
+ break;
+ }
+ if (in_kernel) {
+ mm_segment_t fs;
+ fs = snd_enter_user();
+ ret = snd_pcm_lib_readv(substream, (void __user **)bufs, frames);
+ snd_leave_user(fs);
+ } else {
+ ret = snd_pcm_lib_readv(substream, (void __user **)bufs, frames);
+ }
+ if (ret != -EPIPE && ret != -ESTRPIPE)
+ break;
+ }
+ return ret;
+}
+
+static ssize_t snd_pcm_oss_write2(snd_pcm_substream_t *substream, const char *buf, size_t bytes, int in_kernel)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_pcm_sframes_t frames, frames1;
+ if (runtime->oss.plugin_first) {
+ snd_pcm_plugin_channel_t *channels;
+ size_t oss_frame_bytes = (runtime->oss.plugin_first->src_width * runtime->oss.plugin_first->src_format.channels) / 8;
+ if (!in_kernel) {
+ if (copy_from_user(runtime->oss.buffer, (const char __user *)buf, bytes))
+ return -EFAULT;
+ buf = runtime->oss.buffer;
+ }
+ frames = bytes / oss_frame_bytes;
+ frames1 = snd_pcm_plug_client_channels_buf(substream, (char *)buf, frames, &channels);
+ if (frames1 < 0)
+ return frames1;
+ frames1 = snd_pcm_plug_write_transfer(substream, channels, frames1);
+ if (frames1 <= 0)
+ return frames1;
+ bytes = frames1 * oss_frame_bytes;
+ } else {
+ frames = bytes_to_frames(runtime, bytes);
+ frames1 = snd_pcm_oss_write3(substream, buf, frames, in_kernel);
+ if (frames1 <= 0)
+ return frames1;
+ bytes = frames_to_bytes(runtime, frames1);
+ }
+ return bytes;
+}
+
+static ssize_t snd_pcm_oss_write1(snd_pcm_substream_t *substream, const char __user *buf, size_t bytes)
+{
+ size_t xfer = 0;
+ ssize_t tmp;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ if (atomic_read(&runtime->mmap_count))
+ return -ENXIO;
+
+ if ((tmp = snd_pcm_oss_make_ready(substream)) < 0)
+ return tmp;
+ while (bytes > 0) {
+ if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {
+ tmp = bytes;
+ if (tmp + runtime->oss.buffer_used > runtime->oss.period_bytes)
+ tmp = runtime->oss.period_bytes - runtime->oss.buffer_used;
+ if (tmp > 0) {
+ if (copy_from_user(runtime->oss.buffer + runtime->oss.buffer_used, buf, tmp))
+ return xfer > 0 ? (snd_pcm_sframes_t)xfer : -EFAULT;
+ }
+ runtime->oss.buffer_used += tmp;
+ buf += tmp;
+ bytes -= tmp;
+ xfer += tmp;
+ if ((substream->oss.setup != NULL && substream->oss.setup->partialfrag) ||
+ runtime->oss.buffer_used == runtime->oss.period_bytes) {
+ tmp = snd_pcm_oss_write2(substream, runtime->oss.buffer + runtime->oss.period_ptr,
+ runtime->oss.buffer_used - runtime->oss.period_ptr, 1);
+ if (tmp <= 0)
+ return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
+ runtime->oss.bytes += tmp;
+ runtime->oss.period_ptr += tmp;
+ runtime->oss.period_ptr %= runtime->oss.period_bytes;
+ if (runtime->oss.period_ptr == 0 ||
+ runtime->oss.period_ptr == runtime->oss.buffer_used)
+ runtime->oss.buffer_used = 0;
+ else if ((substream->ffile->f_flags & O_NONBLOCK) != 0)
+ return xfer > 0 ? xfer : -EAGAIN;
+ }
+ } else {
+ tmp = snd_pcm_oss_write2(substream, (const char *)buf, runtime->oss.period_bytes, 0);
+ if (tmp <= 0)
+ return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
+ runtime->oss.bytes += tmp;
+ buf += tmp;
+ bytes -= tmp;
+ xfer += tmp;
+ if ((substream->ffile->f_flags & O_NONBLOCK) != 0 &&
+ tmp != runtime->oss.period_bytes)
+ break;
+ }
+ }
+ return xfer;
+}
+
+static ssize_t snd_pcm_oss_read2(snd_pcm_substream_t *substream, char *buf, size_t bytes, int in_kernel)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_pcm_sframes_t frames, frames1;
+ char __user *final_dst = (char __user *)buf;
+ if (runtime->oss.plugin_first) {
+ snd_pcm_plugin_channel_t *channels;
+ size_t oss_frame_bytes = (runtime->oss.plugin_last->dst_width * runtime->oss.plugin_last->dst_format.channels) / 8;
+ if (!in_kernel)
+ buf = runtime->oss.buffer;
+ frames = bytes / oss_frame_bytes;
+ frames1 = snd_pcm_plug_client_channels_buf(substream, buf, frames, &channels);
+ if (frames1 < 0)
+ return frames1;
+ frames1 = snd_pcm_plug_read_transfer(substream, channels, frames1);
+ if (frames1 <= 0)
+ return frames1;
+ bytes = frames1 * oss_frame_bytes;
+ if (!in_kernel && copy_to_user(final_dst, buf, bytes))
+ return -EFAULT;
+ } else {
+ frames = bytes_to_frames(runtime, bytes);
+ frames1 = snd_pcm_oss_read3(substream, buf, frames, in_kernel);
+ if (frames1 <= 0)
+ return frames1;
+ bytes = frames_to_bytes(runtime, frames1);
+ }
+ return bytes;
+}
+
+static ssize_t snd_pcm_oss_read1(snd_pcm_substream_t *substream, char __user *buf, size_t bytes)
+{
+ size_t xfer = 0;
+ ssize_t tmp;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ if (atomic_read(&runtime->mmap_count))
+ return -ENXIO;
+
+ if ((tmp = snd_pcm_oss_make_ready(substream)) < 0)
+ return tmp;
+ while (bytes > 0) {
+ if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {
+ if (runtime->oss.buffer_used == 0) {
+ tmp = snd_pcm_oss_read2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1);
+ if (tmp <= 0)
+ return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
+ runtime->oss.bytes += tmp;
+ runtime->oss.period_ptr = tmp;
+ runtime->oss.buffer_used = tmp;
+ }
+ tmp = bytes;
+ if ((size_t) tmp > runtime->oss.buffer_used)
+ tmp = runtime->oss.buffer_used;
+ if (copy_to_user(buf, runtime->oss.buffer + (runtime->oss.period_ptr - runtime->oss.buffer_used), tmp))
+ return xfer > 0 ? (snd_pcm_sframes_t)xfer : -EFAULT;
+ buf += tmp;
+ bytes -= tmp;
+ xfer += tmp;
+ runtime->oss.buffer_used -= tmp;
+ } else {
+ tmp = snd_pcm_oss_read2(substream, (char *)buf, runtime->oss.period_bytes, 0);
+ if (tmp <= 0)
+ return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
+ runtime->oss.bytes += tmp;
+ buf += tmp;
+ bytes -= tmp;
+ xfer += tmp;
+ }
+ }
+ return xfer;
+}
+
+static int snd_pcm_oss_reset(snd_pcm_oss_file_t *pcm_oss_file)
+{
+ snd_pcm_substream_t *substream;
+
+ substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
+ if (substream != NULL) {
+ snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
+ substream->runtime->oss.prepare = 1;
+ }
+ substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
+ if (substream != NULL) {
+ snd_pcm_kernel_capture_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
+ substream->runtime->oss.prepare = 1;
+ }
+ return 0;
+}
+
+static int snd_pcm_oss_post(snd_pcm_oss_file_t *pcm_oss_file)
+{
+ snd_pcm_substream_t *substream;
+ int err;
+
+ substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
+ if (substream != NULL) {
+ if ((err = snd_pcm_oss_make_ready(substream)) < 0)
+ return err;
+ snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_START, NULL);
+ }
+ /* note: all errors from the start action are ignored */
+ /* OSS apps do not know, how to handle them */
+ return 0;
+}
+
+static int snd_pcm_oss_sync1(snd_pcm_substream_t *substream, size_t size)
+{
+ snd_pcm_runtime_t *runtime;
+ ssize_t result = 0;
+ long res;
+ wait_queue_t wait;
+
+ runtime = substream->runtime;
+ init_waitqueue_entry(&wait, current);
+ add_wait_queue(&runtime->sleep, &wait);
+#ifdef OSS_DEBUG
+ printk("sync1: size = %li\n", size);
+#endif
+ while (1) {
+ result = snd_pcm_oss_write2(substream, runtime->oss.buffer, size, 1);
+ if (result > 0) {
+ runtime->oss.buffer_used = 0;
+ result = 0;
+ break;
+ }
+ if (result != 0 && result != -EAGAIN)
+ break;
+ result = 0;
+ set_current_state(TASK_INTERRUPTIBLE);
+ snd_pcm_stream_lock_irq(substream);
+ res = runtime->status->state;
+ snd_pcm_stream_unlock_irq(substream);
+ if (res != SNDRV_PCM_STATE_RUNNING) {
+ set_current_state(TASK_RUNNING);
+ break;
+ }
+ res = schedule_timeout(10 * HZ);
+ if (signal_pending(current)) {
+ result = -ERESTARTSYS;
+ break;
+ }
+ if (res == 0) {
+ snd_printk(KERN_ERR "OSS sync error - DMA timeout\n");
+ result = -EIO;
+ break;
+ }
+ }
+ remove_wait_queue(&runtime->sleep, &wait);
+ return result;
+}
+
+static int snd_pcm_oss_sync(snd_pcm_oss_file_t *pcm_oss_file)
+{
+ int err = 0;
+ unsigned int saved_f_flags;
+ snd_pcm_substream_t *substream;
+ snd_pcm_runtime_t *runtime;
+ snd_pcm_format_t format;
+ unsigned long width;
+ size_t size;
+
+ substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
+ if (substream != NULL) {
+ runtime = substream->runtime;
+ if (atomic_read(&runtime->mmap_count))
+ goto __direct;
+ if ((err = snd_pcm_oss_make_ready(substream)) < 0)
+ return err;
+ format = snd_pcm_oss_format_from(runtime->oss.format);
+ width = snd_pcm_format_physical_width(format);
+ if (runtime->oss.buffer_used > 0) {
+#ifdef OSS_DEBUG
+ printk("sync: buffer_used\n");
+#endif
+ size = (8 * (runtime->oss.period_bytes - runtime->oss.buffer_used) + 7) / width;
+ snd_pcm_format_set_silence(format,
+ runtime->oss.buffer + runtime->oss.buffer_used,
+ size);
+ err = snd_pcm_oss_sync1(substream, runtime->oss.period_bytes);
+ if (err < 0)
+ return err;
+ } else if (runtime->oss.period_ptr > 0) {
+#ifdef OSS_DEBUG
+ printk("sync: period_ptr\n");
+#endif
+ size = runtime->oss.period_bytes - runtime->oss.period_ptr;
+ snd_pcm_format_set_silence(format,
+ runtime->oss.buffer,
+ size * 8 / width);
+ err = snd_pcm_oss_sync1(substream, size);
+ if (err < 0)
+ return err;
+ }
+ /*
+ * The ALSA's period might be a bit large than OSS one.
+ * Fill the remain portion of ALSA period with zeros.
+ */
+ size = runtime->control->appl_ptr % runtime->period_size;
+ if (size > 0) {
+ size = runtime->period_size - size;
+ if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
+ size = (runtime->frame_bits * size) / 8;
+ while (size > 0) {
+ mm_segment_t fs;
+ size_t size1 = size < runtime->oss.period_bytes ? size : runtime->oss.period_bytes;
+ size -= size1;
+ size1 *= 8;
+ size1 /= runtime->sample_bits;
+ snd_pcm_format_set_silence(runtime->format,
+ runtime->oss.buffer,
+ size1);
+ fs = snd_enter_user();
+ snd_pcm_lib_write(substream, (void __user *)runtime->oss.buffer, size1);
+ snd_leave_user(fs);
+ }
+ } else if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) {
+ void __user *buffers[runtime->channels];
+ memset(buffers, 0, runtime->channels * sizeof(void *));
+ snd_pcm_lib_writev(substream, buffers, size);
+ }
+ }
+ /*
+ * finish sync: drain the buffer
+ */
+ __direct:
+ saved_f_flags = substream->ffile->f_flags;
+ substream->ffile->f_flags &= ~O_NONBLOCK;
+ err = snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL);
+ substream->ffile->f_flags = saved_f_flags;
+ if (err < 0)
+ return err;
+ runtime->oss.prepare = 1;
+ }
+
+ substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
+ if (substream != NULL) {
+ if ((err = snd_pcm_oss_make_ready(substream)) < 0)
+ return err;
+ runtime = substream->runtime;
+ err = snd_pcm_kernel_capture_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
+ if (err < 0)
+ return err;
+ runtime->oss.buffer_used = 0;
+ runtime->oss.prepare = 1;
+ }
+ return 0;
+}
+
+static int snd_pcm_oss_set_rate(snd_pcm_oss_file_t *pcm_oss_file, int rate)
+{
+ int idx;
+
+ for (idx = 1; idx >= 0; --idx) {
+ snd_pcm_substream_t *substream = pcm_oss_file->streams[idx];
+ snd_pcm_runtime_t *runtime;
+ if (substream == NULL)
+ continue;
+ runtime = substream->runtime;
+ if (rate < 1000)
+ rate = 1000;
+ else if (rate > 192000)
+ rate = 192000;
+ if (runtime->oss.rate != rate) {
+ runtime->oss.params = 1;
+ runtime->oss.rate = rate;
+ }
+ }
+ return snd_pcm_oss_get_rate(pcm_oss_file);
+}
+
+static int snd_pcm_oss_get_rate(snd_pcm_oss_file_t *pcm_oss_file)
+{
+ snd_pcm_substream_t *substream;
+ int err;
+
+ if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
+ return err;
+ return substream->runtime->oss.rate;
+}
+
+static int snd_pcm_oss_set_channels(snd_pcm_oss_file_t *pcm_oss_file, unsigned int channels)
+{
+ int idx;
+ if (channels < 1)
+ channels = 1;
+ if (channels > 128)
+ return -EINVAL;
+ for (idx = 1; idx >= 0; --idx) {
+ snd_pcm_substream_t *substream = pcm_oss_file->streams[idx];
+ snd_pcm_runtime_t *runtime;
+ if (substream == NULL)
+ continue;
+ runtime = substream->runtime;
+ if (runtime->oss.channels != channels) {
+ runtime->oss.params = 1;
+ runtime->oss.channels = channels;
+ }
+ }
+ return snd_pcm_oss_get_channels(pcm_oss_file);
+}
+
+static int snd_pcm_oss_get_channels(snd_pcm_oss_file_t *pcm_oss_file)
+{
+ snd_pcm_substream_t *substream;
+ int err;
+
+ if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
+ return err;
+ return substream->runtime->oss.channels;
+}
+
+static int snd_pcm_oss_get_block_size(snd_pcm_oss_file_t *pcm_oss_file)
+{
+ snd_pcm_substream_t *substream;
+ int err;
+
+ if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
+ return err;
+ return substream->runtime->oss.period_bytes;
+}
+
+static int snd_pcm_oss_get_formats(snd_pcm_oss_file_t *pcm_oss_file)
+{
+ snd_pcm_substream_t *substream;
+ int err;
+ int direct;
+ snd_pcm_hw_params_t *params;
+ unsigned int formats = 0;
+ snd_mask_t format_mask;
+ int fmt;
+
+ if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
+ return err;
+ if (atomic_read(&substream->runtime->mmap_count)) {
+ direct = 1;
+ } else {
+ snd_pcm_oss_setup_t *setup = substream->oss.setup;
+ direct = (setup != NULL && setup->direct);
+ }
+ if (!direct)
+ return AFMT_MU_LAW | AFMT_U8 |
+ AFMT_S16_LE | AFMT_S16_BE |
+ AFMT_S8 | AFMT_U16_LE |
+ AFMT_U16_BE;
+ params = kmalloc(sizeof(*params), GFP_KERNEL);
+ if (!params)
+ return -ENOMEM;
+ _snd_pcm_hw_params_any(params);
+ err = snd_pcm_hw_refine(substream, params);
+ format_mask = *hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ kfree(params);
+ snd_assert(err >= 0, return err);
+ for (fmt = 0; fmt < 32; ++fmt) {
+ if (snd_mask_test(&format_mask, fmt)) {
+ int f = snd_pcm_oss_format_to(fmt);
+ if (f >= 0)
+ formats |= f;
+ }
+ }
+ return formats;
+}
+
+static int snd_pcm_oss_set_format(snd_pcm_oss_file_t *pcm_oss_file, int format)
+{
+ int formats, idx;
+
+ if (format != AFMT_QUERY) {
+ formats = snd_pcm_oss_get_formats(pcm_oss_file);
+ if (!(formats & format))
+ format = AFMT_U8;
+ for (idx = 1; idx >= 0; --idx) {
+ snd_pcm_substream_t *substream = pcm_oss_file->streams[idx];
+ snd_pcm_runtime_t *runtime;
+ if (substream == NULL)
+ continue;
+ runtime = substream->runtime;
+ if (runtime->oss.format != format) {
+ runtime->oss.params = 1;
+ runtime->oss.format = format;
+ }
+ }
+ }
+ return snd_pcm_oss_get_format(pcm_oss_file);
+}
+
+static int snd_pcm_oss_get_format(snd_pcm_oss_file_t *pcm_oss_file)
+{
+ snd_pcm_substream_t *substream;
+ int err;
+
+ if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
+ return err;
+ return substream->runtime->oss.format;
+}
+
+static int snd_pcm_oss_set_subdivide1(snd_pcm_substream_t *substream, int subdivide)
+{
+ snd_pcm_runtime_t *runtime;
+
+ if (substream == NULL)
+ return 0;
+ runtime = substream->runtime;
+ if (subdivide == 0) {
+ subdivide = runtime->oss.subdivision;
+ if (subdivide == 0)
+ subdivide = 1;
+ return subdivide;
+ }
+ if (runtime->oss.subdivision || runtime->oss.fragshift)
+ return -EINVAL;
+ if (subdivide != 1 && subdivide != 2 && subdivide != 4 &&
+ subdivide != 8 && subdivide != 16)
+ return -EINVAL;
+ runtime->oss.subdivision = subdivide;
+ runtime->oss.params = 1;
+ return subdivide;
+}
+
+static int snd_pcm_oss_set_subdivide(snd_pcm_oss_file_t *pcm_oss_file, int subdivide)
+{
+ int err = -EINVAL, idx;
+
+ for (idx = 1; idx >= 0; --idx) {
+ snd_pcm_substream_t *substream = pcm_oss_file->streams[idx];
+ if (substream == NULL)
+ continue;
+ if ((err = snd_pcm_oss_set_subdivide1(substream, subdivide)) < 0)
+ return err;
+ }
+ return err;
+}
+
+static int snd_pcm_oss_set_fragment1(snd_pcm_substream_t *substream, unsigned int val)
+{
+ snd_pcm_runtime_t *runtime;
+
+ if (substream == NULL)
+ return 0;
+ runtime = substream->runtime;
+ if (runtime->oss.subdivision || runtime->oss.fragshift)
+ return -EINVAL;
+ runtime->oss.fragshift = val & 0xffff;
+ runtime->oss.maxfrags = (val >> 16) & 0xffff;
+ if (runtime->oss.fragshift < 4) /* < 16 */
+ runtime->oss.fragshift = 4;
+ if (runtime->oss.maxfrags < 2)
+ runtime->oss.maxfrags = 2;
+ runtime->oss.params = 1;
+ return 0;
+}
+
+static int snd_pcm_oss_set_fragment(snd_pcm_oss_file_t *pcm_oss_file, unsigned int val)
+{
+ int err = -EINVAL, idx;
+
+ for (idx = 1; idx >= 0; --idx) {
+ snd_pcm_substream_t *substream = pcm_oss_file->streams[idx];
+ if (substream == NULL)
+ continue;
+ if ((err = snd_pcm_oss_set_fragment1(substream, val)) < 0)
+ return err;
+ }
+ return err;
+}
+
+static int snd_pcm_oss_nonblock(struct file * file)
+{
+ file->f_flags |= O_NONBLOCK;
+ return 0;
+}
+
+static int snd_pcm_oss_get_caps1(snd_pcm_substream_t *substream, int res)
+{
+
+ if (substream == NULL) {
+ res &= ~DSP_CAP_DUPLEX;
+ return res;
+ }
+#ifdef DSP_CAP_MULTI
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ if (substream->pstr->substream_count > 1)
+ res |= DSP_CAP_MULTI;
+#endif
+ /* DSP_CAP_REALTIME is set all times: */
+ /* all ALSA drivers can return actual pointer in ring buffer */
+#if defined(DSP_CAP_REALTIME) && 0
+ {
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ if (runtime->info & (SNDRV_PCM_INFO_BLOCK_TRANSFER|SNDRV_PCM_INFO_BATCH))
+ res &= ~DSP_CAP_REALTIME;
+ }
+#endif
+ return res;
+}
+
+static int snd_pcm_oss_get_caps(snd_pcm_oss_file_t *pcm_oss_file)
+{
+ int result, idx;
+
+ result = DSP_CAP_TRIGGER | DSP_CAP_MMAP | DSP_CAP_DUPLEX | DSP_CAP_REALTIME;
+ for (idx = 0; idx < 2; idx++) {
+ snd_pcm_substream_t *substream = pcm_oss_file->streams[idx];
+ result = snd_pcm_oss_get_caps1(substream, result);
+ }
+ result |= 0x0001; /* revision - same as SB AWE 64 */
+ return result;
+}
+
+static void snd_pcm_oss_simulate_fill(snd_pcm_substream_t *substream, snd_pcm_uframes_t hw_ptr)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_pcm_uframes_t appl_ptr;
+ appl_ptr = hw_ptr + runtime->buffer_size;
+ appl_ptr %= runtime->boundary;
+ runtime->control->appl_ptr = appl_ptr;
+}
+
+static int snd_pcm_oss_set_trigger(snd_pcm_oss_file_t *pcm_oss_file, int trigger)
+{
+ snd_pcm_runtime_t *runtime;
+ snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL;
+ int err, cmd;
+
+#ifdef OSS_DEBUG
+ printk("pcm_oss: trigger = 0x%x\n", trigger);
+#endif
+
+ psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
+ csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
+
+ if (psubstream) {
+ if ((err = snd_pcm_oss_make_ready(psubstream)) < 0)
+ return err;
+ }
+ if (csubstream) {
+ if ((err = snd_pcm_oss_make_ready(csubstream)) < 0)
+ return err;
+ }
+ if (psubstream) {
+ runtime = psubstream->runtime;
+ if (trigger & PCM_ENABLE_OUTPUT) {
+ if (runtime->oss.trigger)
+ goto _skip1;
+ if (atomic_read(&psubstream->runtime->mmap_count))
+ snd_pcm_oss_simulate_fill(psubstream, runtime->hw_ptr_interrupt);
+ runtime->oss.trigger = 1;
+ runtime->start_threshold = 1;
+ cmd = SNDRV_PCM_IOCTL_START;
+ } else {
+ if (!runtime->oss.trigger)
+ goto _skip1;
+ runtime->oss.trigger = 0;
+ runtime->start_threshold = runtime->boundary;
+ cmd = SNDRV_PCM_IOCTL_DROP;
+ runtime->oss.prepare = 1;
+ }
+ err = snd_pcm_kernel_playback_ioctl(psubstream, cmd, NULL);
+ if (err < 0)
+ return err;
+ }
+ _skip1:
+ if (csubstream) {
+ runtime = csubstream->runtime;
+ if (trigger & PCM_ENABLE_INPUT) {
+ if (runtime->oss.trigger)
+ goto _skip2;
+ runtime->oss.trigger = 1;
+ runtime->start_threshold = 1;
+ cmd = SNDRV_PCM_IOCTL_START;
+ } else {
+ if (!runtime->oss.trigger)
+ goto _skip2;
+ runtime->oss.trigger = 0;
+ runtime->start_threshold = runtime->boundary;
+ cmd = SNDRV_PCM_IOCTL_DROP;
+ runtime->oss.prepare = 1;
+ }
+ err = snd_pcm_kernel_capture_ioctl(csubstream, cmd, NULL);
+ if (err < 0)
+ return err;
+ }
+ _skip2:
+ return 0;
+}
+
+static int snd_pcm_oss_get_trigger(snd_pcm_oss_file_t *pcm_oss_file)
+{
+ snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL;
+ int result = 0;
+
+ psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
+ csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
+ if (psubstream && psubstream->runtime && psubstream->runtime->oss.trigger)
+ result |= PCM_ENABLE_OUTPUT;
+ if (csubstream && csubstream->runtime && csubstream->runtime->oss.trigger)
+ result |= PCM_ENABLE_INPUT;
+ return result;
+}
+
+static int snd_pcm_oss_get_odelay(snd_pcm_oss_file_t *pcm_oss_file)
+{
+ snd_pcm_substream_t *substream;
+ snd_pcm_runtime_t *runtime;
+ snd_pcm_sframes_t delay;
+ int err;
+
+ substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
+ if (substream == NULL)
+ return -EINVAL;
+ if ((err = snd_pcm_oss_make_ready(substream)) < 0)
+ return err;
+ runtime = substream->runtime;
+ if (runtime->oss.params || runtime->oss.prepare)
+ return 0;
+ err = snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, &delay);
+ if (err == -EPIPE)
+ delay = 0; /* hack for broken OSS applications */
+ else if (err < 0)
+ return err;
+ return snd_pcm_oss_bytes(substream, delay);
+}
+
+static int snd_pcm_oss_get_ptr(snd_pcm_oss_file_t *pcm_oss_file, int stream, struct count_info __user * _info)
+{
+ snd_pcm_substream_t *substream;
+ snd_pcm_runtime_t *runtime;
+ snd_pcm_sframes_t delay;
+ int fixup;
+ struct count_info info;
+ int err;
+
+ if (_info == NULL)
+ return -EFAULT;
+ substream = pcm_oss_file->streams[stream];
+ if (substream == NULL)
+ return -EINVAL;
+ if ((err = snd_pcm_oss_make_ready(substream)) < 0)
+ return err;
+ runtime = substream->runtime;
+ if (runtime->oss.params || runtime->oss.prepare) {
+ memset(&info, 0, sizeof(info));
+ if (copy_to_user(_info, &info, sizeof(info)))
+ return -EFAULT;
+ return 0;
+ }
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, &delay);
+ if (err == -EPIPE || err == -ESTRPIPE || (! err && delay < 0)) {
+ err = 0;
+ delay = 0;
+ fixup = 0;
+ } else {
+ fixup = runtime->oss.buffer_used;
+ }
+ } else {
+ err = snd_pcm_oss_capture_position_fixup(substream, &delay);
+ fixup = -runtime->oss.buffer_used;
+ }
+ if (err < 0)
+ return err;
+ info.ptr = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr % runtime->buffer_size);
+ if (atomic_read(&runtime->mmap_count)) {
+ snd_pcm_sframes_t n;
+ n = (delay = runtime->hw_ptr_interrupt) - runtime->oss.prev_hw_ptr_interrupt;
+ if (n < 0)
+ n += runtime->boundary;
+ info.blocks = n / runtime->period_size;
+ runtime->oss.prev_hw_ptr_interrupt = delay;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_pcm_oss_simulate_fill(substream, delay);
+ info.bytes = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr) & INT_MAX;
+ } else {
+ delay = snd_pcm_oss_bytes(substream, delay) + fixup;
+ info.blocks = delay / runtime->oss.period_bytes;
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ info.bytes = (runtime->oss.bytes - delay) & INT_MAX;
+ else
+ info.bytes = (runtime->oss.bytes + delay) & INT_MAX;
+ }
+ if (copy_to_user(_info, &info, sizeof(info)))
+ return -EFAULT;
+ return 0;
+}
+
+static int snd_pcm_oss_get_space(snd_pcm_oss_file_t *pcm_oss_file, int stream, struct audio_buf_info __user *_info)
+{
+ snd_pcm_substream_t *substream;
+ snd_pcm_runtime_t *runtime;
+ snd_pcm_sframes_t avail;
+ int fixup;
+ struct audio_buf_info info;
+ int err;
+
+ if (_info == NULL)
+ return -EFAULT;
+ substream = pcm_oss_file->streams[stream];
+ if (substream == NULL)
+ return -EINVAL;
+ runtime = substream->runtime;
+
+ if (runtime->oss.params &&
+ (err = snd_pcm_oss_change_params(substream)) < 0)
+ return err;
+
+ info.fragsize = runtime->oss.period_bytes;
+ info.fragstotal = runtime->periods;
+ if (runtime->oss.prepare) {
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ info.bytes = runtime->oss.period_bytes * runtime->oss.periods;
+ info.fragments = runtime->oss.periods;
+ } else {
+ info.bytes = 0;
+ info.fragments = 0;
+ }
+ } else {
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, &avail);
+ if (err == -EPIPE || err == -ESTRPIPE || (! err && avail < 0)) {
+ avail = runtime->buffer_size;
+ err = 0;
+ fixup = 0;
+ } else {
+ avail = runtime->buffer_size - avail;
+ fixup = -runtime->oss.buffer_used;
+ }
+ } else {
+ err = snd_pcm_oss_capture_position_fixup(substream, &avail);
+ fixup = runtime->oss.buffer_used;
+ }
+ if (err < 0)
+ return err;
+ info.bytes = snd_pcm_oss_bytes(substream, avail) + fixup;
+ info.fragments = info.bytes / runtime->oss.period_bytes;
+ }
+
+#ifdef OSS_DEBUG
+ printk("pcm_oss: space: bytes = %i, fragments = %i, fragstotal = %i, fragsize = %i\n", info.bytes, info.fragments, info.fragstotal, info.fragsize);
+#endif
+ if (copy_to_user(_info, &info, sizeof(info)))
+ return -EFAULT;
+ return 0;
+}
+
+static int snd_pcm_oss_get_mapbuf(snd_pcm_oss_file_t *pcm_oss_file, int stream, struct buffmem_desc __user * _info)
+{
+ // it won't be probably implemented
+ // snd_printd("TODO: snd_pcm_oss_get_mapbuf\n");
+ return -EINVAL;
+}
+
+static snd_pcm_oss_setup_t *snd_pcm_oss_look_for_setup(snd_pcm_t *pcm, int stream, const char *task_name)
+{
+ const char *ptr, *ptrl;
+ snd_pcm_oss_setup_t *setup;
+
+ down(&pcm->streams[stream].oss.setup_mutex);
+ for (setup = pcm->streams[stream].oss.setup_list; setup; setup = setup->next) {
+ if (!strcmp(setup->task_name, task_name)) {
+ up(&pcm->streams[stream].oss.setup_mutex);
+ return setup;
+ }
+ }
+ ptr = ptrl = task_name;
+ while (*ptr) {
+ if (*ptr == '/')
+ ptrl = ptr + 1;
+ ptr++;
+ }
+ if (ptrl == task_name) {
+ goto __not_found;
+ return NULL;
+ }
+ for (setup = pcm->streams[stream].oss.setup_list; setup; setup = setup->next) {
+ if (!strcmp(setup->task_name, ptrl)) {
+ up(&pcm->streams[stream].oss.setup_mutex);
+ return setup;
+ }
+ }
+ __not_found:
+ up(&pcm->streams[stream].oss.setup_mutex);
+ return NULL;
+}
+
+static void snd_pcm_oss_init_substream(snd_pcm_substream_t *substream,
+ snd_pcm_oss_setup_t *setup,
+ int minor)
+{
+ snd_pcm_runtime_t *runtime;
+
+ substream->oss.oss = 1;
+ substream->oss.setup = setup;
+ runtime = substream->runtime;
+ runtime->oss.params = 1;
+ runtime->oss.trigger = 1;
+ runtime->oss.rate = 8000;
+ switch (SNDRV_MINOR_OSS_DEVICE(minor)) {
+ case SNDRV_MINOR_OSS_PCM_8:
+ runtime->oss.format = AFMT_U8;
+ break;
+ case SNDRV_MINOR_OSS_PCM_16:
+ runtime->oss.format = AFMT_S16_LE;
+ break;
+ default:
+ runtime->oss.format = AFMT_MU_LAW;
+ }
+ runtime->oss.channels = 1;
+ runtime->oss.fragshift = 0;
+ runtime->oss.maxfrags = 0;
+ runtime->oss.subdivision = 0;
+}
+
+static void snd_pcm_oss_release_substream(snd_pcm_substream_t *substream)
+{
+ snd_pcm_runtime_t *runtime;
+ runtime = substream->runtime;
+ vfree(runtime->oss.buffer);
+ snd_pcm_oss_plugin_clear(substream);
+ substream->oss.file = NULL;
+ substream->oss.oss = 0;
+}
+
+static int snd_pcm_oss_release_file(snd_pcm_oss_file_t *pcm_oss_file)
+{
+ int cidx;
+ snd_assert(pcm_oss_file != NULL, return -ENXIO);
+ for (cidx = 0; cidx < 2; ++cidx) {
+ snd_pcm_substream_t *substream = pcm_oss_file->streams[cidx];
+ snd_pcm_runtime_t *runtime;
+ if (substream == NULL)
+ continue;
+ runtime = substream->runtime;
+
+ snd_pcm_stream_lock_irq(substream);
+ if (snd_pcm_running(substream))
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
+ snd_pcm_stream_unlock_irq(substream);
+ if (substream->open_flag) {
+ if (substream->ops->hw_free != NULL)
+ substream->ops->hw_free(substream);
+ substream->ops->close(substream);
+ substream->open_flag = 0;
+ }
+ substream->ffile = NULL;
+ snd_pcm_oss_release_substream(substream);
+ snd_pcm_release_substream(substream);
+ }
+ kfree(pcm_oss_file);
+ return 0;
+}
+
+static int snd_pcm_oss_open_file(struct file *file,
+ snd_pcm_t *pcm,
+ snd_pcm_oss_file_t **rpcm_oss_file,
+ int minor,
+ snd_pcm_oss_setup_t *psetup,
+ snd_pcm_oss_setup_t *csetup)
+{
+ int err = 0;
+ snd_pcm_oss_file_t *pcm_oss_file;
+ snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL;
+ unsigned int f_mode = file->f_mode;
+
+ snd_assert(rpcm_oss_file != NULL, return -EINVAL);
+ *rpcm_oss_file = NULL;
+
+ pcm_oss_file = kcalloc(1, sizeof(*pcm_oss_file), GFP_KERNEL);
+ if (pcm_oss_file == NULL)
+ return -ENOMEM;
+
+ if ((f_mode & (FMODE_WRITE|FMODE_READ)) == (FMODE_WRITE|FMODE_READ) &&
+ (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX))
+ f_mode = FMODE_WRITE;
+ if ((f_mode & FMODE_WRITE) && !(psetup && psetup->disable)) {
+ if ((err = snd_pcm_open_substream(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &psubstream)) < 0) {
+ snd_pcm_oss_release_file(pcm_oss_file);
+ return err;
+ }
+ pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK] = psubstream;
+ }
+ if ((f_mode & FMODE_READ) && !(csetup && csetup->disable)) {
+ if ((err = snd_pcm_open_substream(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &csubstream)) < 0) {
+ if (!(f_mode & FMODE_WRITE) || err != -ENODEV) {
+ snd_pcm_oss_release_file(pcm_oss_file);
+ return err;
+ } else {
+ csubstream = NULL;
+ }
+ }
+ pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE] = csubstream;
+ }
+
+ if (psubstream == NULL && csubstream == NULL) {
+ snd_pcm_oss_release_file(pcm_oss_file);
+ return -EINVAL;
+ }
+ if (psubstream != NULL) {
+ psubstream->oss.file = pcm_oss_file;
+ err = snd_pcm_hw_constraints_init(psubstream);
+ if (err < 0) {
+ snd_printd("snd_pcm_hw_constraint_init failed\n");
+ snd_pcm_oss_release_file(pcm_oss_file);
+ return err;
+ }
+ if ((err = psubstream->ops->open(psubstream)) < 0) {
+ snd_pcm_oss_release_file(pcm_oss_file);
+ return err;
+ }
+ psubstream->open_flag = 1;
+ err = snd_pcm_hw_constraints_complete(psubstream);
+ if (err < 0) {
+ snd_printd("snd_pcm_hw_constraint_complete failed\n");
+ snd_pcm_oss_release_file(pcm_oss_file);
+ return err;
+ }
+ psubstream->ffile = file;
+ snd_pcm_oss_init_substream(psubstream, psetup, minor);
+ }
+ if (csubstream != NULL) {
+ csubstream->oss.file = pcm_oss_file;
+ err = snd_pcm_hw_constraints_init(csubstream);
+ if (err < 0) {
+ snd_printd("snd_pcm_hw_constraint_init failed\n");
+ snd_pcm_oss_release_file(pcm_oss_file);
+ return err;
+ }
+ if ((err = csubstream->ops->open(csubstream)) < 0) {
+ snd_pcm_oss_release_file(pcm_oss_file);
+ return err;
+ }
+ csubstream->open_flag = 1;
+ err = snd_pcm_hw_constraints_complete(csubstream);
+ if (err < 0) {
+ snd_printd("snd_pcm_hw_constraint_complete failed\n");
+ snd_pcm_oss_release_file(pcm_oss_file);
+ return err;
+ }
+ csubstream->ffile = file;
+ snd_pcm_oss_init_substream(csubstream, csetup, minor);
+ }
+
+ file->private_data = pcm_oss_file;
+ *rpcm_oss_file = pcm_oss_file;
+ return 0;
+}
+
+
+static int snd_pcm_oss_open(struct inode *inode, struct file *file)
+{
+ int minor = iminor(inode);
+ int cardnum = SNDRV_MINOR_OSS_CARD(minor);
+ int device;
+ int err;
+ char task_name[32];
+ snd_pcm_t *pcm;
+ snd_pcm_oss_file_t *pcm_oss_file;
+ snd_pcm_oss_setup_t *psetup = NULL, *csetup = NULL;
+ int nonblock;
+ wait_queue_t wait;
+
+ snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO);
+ device = SNDRV_MINOR_OSS_DEVICE(minor) == SNDRV_MINOR_OSS_PCM1 ?
+ adsp_map[cardnum] : dsp_map[cardnum];
+
+ pcm = snd_pcm_devices[(cardnum * SNDRV_PCM_DEVICES) + device];
+ if (pcm == NULL) {
+ err = -ENODEV;
+ goto __error1;
+ }
+ err = snd_card_file_add(pcm->card, file);
+ if (err < 0)
+ goto __error1;
+ if (!try_module_get(pcm->card->module)) {
+ err = -EFAULT;
+ goto __error2;
+ }
+ if (snd_task_name(current, task_name, sizeof(task_name)) < 0) {
+ err = -EFAULT;
+ goto __error;
+ }
+ if (file->f_mode & FMODE_WRITE)
+ psetup = snd_pcm_oss_look_for_setup(pcm, SNDRV_PCM_STREAM_PLAYBACK, task_name);
+ if (file->f_mode & FMODE_READ)
+ csetup = snd_pcm_oss_look_for_setup(pcm, SNDRV_PCM_STREAM_CAPTURE, task_name);
+
+ nonblock = !!(file->f_flags & O_NONBLOCK);
+ if (psetup && !psetup->disable) {
+ if (psetup->nonblock)
+ nonblock = 1;
+ else if (psetup->block)
+ nonblock = 0;
+ } else if (csetup && !csetup->disable) {
+ if (csetup->nonblock)
+ nonblock = 1;
+ else if (csetup->block)
+ nonblock = 0;
+ }
+ if (!nonblock)
+ nonblock = nonblock_open;
+
+ init_waitqueue_entry(&wait, current);
+ add_wait_queue(&pcm->open_wait, &wait);
+ down(&pcm->open_mutex);
+ while (1) {
+ err = snd_pcm_oss_open_file(file, pcm, &pcm_oss_file,
+ minor, psetup, csetup);
+ if (err >= 0)
+ break;
+ if (err == -EAGAIN) {
+ if (nonblock) {
+ err = -EBUSY;
+ break;
+ }
+ } else
+ break;
+ set_current_state(TASK_INTERRUPTIBLE);
+ up(&pcm->open_mutex);
+ schedule();
+ down(&pcm->open_mutex);
+ if (signal_pending(current)) {
+ err = -ERESTARTSYS;
+ break;
+ }
+ }
+ remove_wait_queue(&pcm->open_wait, &wait);
+ up(&pcm->open_mutex);
+ if (err < 0)
+ goto __error;
+ return err;
+
+ __error:
+ module_put(pcm->card->module);
+ __error2:
+ snd_card_file_remove(pcm->card, file);
+ __error1:
+ return err;
+}
+
+static int snd_pcm_oss_release(struct inode *inode, struct file *file)
+{
+ snd_pcm_t *pcm;
+ snd_pcm_substream_t *substream;
+ snd_pcm_oss_file_t *pcm_oss_file;
+
+ pcm_oss_file = file->private_data;
+ substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
+ if (substream == NULL)
+ substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
+ snd_assert(substream != NULL, return -ENXIO);
+ pcm = substream->pcm;
+ snd_pcm_oss_sync(pcm_oss_file);
+ down(&pcm->open_mutex);
+ snd_pcm_oss_release_file(pcm_oss_file);
+ up(&pcm->open_mutex);
+ wake_up(&pcm->open_wait);
+ module_put(pcm->card->module);
+ snd_card_file_remove(pcm->card, file);
+ return 0;
+}
+
+static long snd_pcm_oss_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ snd_pcm_oss_file_t *pcm_oss_file;
+ int __user *p = (int __user *)arg;
+ int res;
+
+ pcm_oss_file = file->private_data;
+ if (cmd == OSS_GETVERSION)
+ return put_user(SNDRV_OSS_VERSION, p);
+ if (cmd == OSS_ALSAEMULVER)
+ return put_user(1, p);
+#if defined(CONFIG_SND_MIXER_OSS) || (defined(MODULE) && defined(CONFIG_SND_MIXER_OSS_MODULE))
+ if (((cmd >> 8) & 0xff) == 'M') { /* mixer ioctl - for OSS compatibility */
+ snd_pcm_substream_t *substream;
+ int idx;
+ for (idx = 0; idx < 2; ++idx) {
+ substream = pcm_oss_file->streams[idx];
+ if (substream != NULL)
+ break;
+ }
+ snd_assert(substream != NULL, return -ENXIO);
+ return snd_mixer_oss_ioctl_card(substream->pcm->card, cmd, arg);
+ }
+#endif
+ if (((cmd >> 8) & 0xff) != 'P')
+ return -EINVAL;
+#ifdef OSS_DEBUG
+ printk("pcm_oss: ioctl = 0x%x\n", cmd);
+#endif
+ switch (cmd) {
+ case SNDCTL_DSP_RESET:
+ return snd_pcm_oss_reset(pcm_oss_file);
+ case SNDCTL_DSP_SYNC:
+ return snd_pcm_oss_sync(pcm_oss_file);
+ case SNDCTL_DSP_SPEED:
+ if (get_user(res, p))
+ return -EFAULT;
+ if ((res = snd_pcm_oss_set_rate(pcm_oss_file, res))<0)
+ return res;
+ return put_user(res, p);
+ case SOUND_PCM_READ_RATE:
+ res = snd_pcm_oss_get_rate(pcm_oss_file);
+ if (res < 0)
+ return res;
+ return put_user(res, p);
+ case SNDCTL_DSP_STEREO:
+ if (get_user(res, p))
+ return -EFAULT;
+ res = res > 0 ? 2 : 1;
+ if ((res = snd_pcm_oss_set_channels(pcm_oss_file, res)) < 0)
+ return res;
+ return put_user(--res, p);
+ case SNDCTL_DSP_GETBLKSIZE:
+ res = snd_pcm_oss_get_block_size(pcm_oss_file);
+ if (res < 0)
+ return res;
+ return put_user(res, p);
+ case SNDCTL_DSP_SETFMT:
+ if (get_user(res, p))
+ return -EFAULT;
+ res = snd_pcm_oss_set_format(pcm_oss_file, res);
+ if (res < 0)
+ return res;
+ return put_user(res, p);
+ case SOUND_PCM_READ_BITS:
+ res = snd_pcm_oss_get_format(pcm_oss_file);
+ if (res < 0)
+ return res;
+ return put_user(res, p);
+ case SNDCTL_DSP_CHANNELS:
+ if (get_user(res, p))
+ return -EFAULT;
+ res = snd_pcm_oss_set_channels(pcm_oss_file, res);
+ if (res < 0)
+ return res;
+ return put_user(res, p);
+ case SOUND_PCM_READ_CHANNELS:
+ res = snd_pcm_oss_get_channels(pcm_oss_file);
+ if (res < 0)
+ return res;
+ return put_user(res, p);
+ case SOUND_PCM_WRITE_FILTER:
+ case SOUND_PCM_READ_FILTER:
+ return -EIO;
+ case SNDCTL_DSP_POST:
+ return snd_pcm_oss_post(pcm_oss_file);
+ case SNDCTL_DSP_SUBDIVIDE:
+ if (get_user(res, p))
+ return -EFAULT;
+ res = snd_pcm_oss_set_subdivide(pcm_oss_file, res);
+ if (res < 0)
+ return res;
+ return put_user(res, p);
+ case SNDCTL_DSP_SETFRAGMENT:
+ if (get_user(res, p))
+ return -EFAULT;
+ return snd_pcm_oss_set_fragment(pcm_oss_file, res);
+ case SNDCTL_DSP_GETFMTS:
+ res = snd_pcm_oss_get_formats(pcm_oss_file);
+ if (res < 0)
+ return res;
+ return put_user(res, p);
+ case SNDCTL_DSP_GETOSPACE:
+ case SNDCTL_DSP_GETISPACE:
+ return snd_pcm_oss_get_space(pcm_oss_file,
+ cmd == SNDCTL_DSP_GETISPACE ?
+ SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK,
+ (struct audio_buf_info __user *) arg);
+ case SNDCTL_DSP_NONBLOCK:
+ return snd_pcm_oss_nonblock(file);
+ case SNDCTL_DSP_GETCAPS:
+ res = snd_pcm_oss_get_caps(pcm_oss_file);
+ if (res < 0)
+ return res;
+ return put_user(res, p);
+ case SNDCTL_DSP_GETTRIGGER:
+ res = snd_pcm_oss_get_trigger(pcm_oss_file);
+ if (res < 0)
+ return res;
+ return put_user(res, p);
+ case SNDCTL_DSP_SETTRIGGER:
+ if (get_user(res, p))
+ return -EFAULT;
+ return snd_pcm_oss_set_trigger(pcm_oss_file, res);
+ case SNDCTL_DSP_GETIPTR:
+ case SNDCTL_DSP_GETOPTR:
+ return snd_pcm_oss_get_ptr(pcm_oss_file,
+ cmd == SNDCTL_DSP_GETIPTR ?
+ SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK,
+ (struct count_info __user *) arg);
+ case SNDCTL_DSP_MAPINBUF:
+ case SNDCTL_DSP_MAPOUTBUF:
+ return snd_pcm_oss_get_mapbuf(pcm_oss_file,
+ cmd == SNDCTL_DSP_MAPINBUF ?
+ SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK,
+ (struct buffmem_desc __user *) arg);
+ case SNDCTL_DSP_SETSYNCRO:
+ /* stop DMA now.. */
+ return 0;
+ case SNDCTL_DSP_SETDUPLEX:
+ if (snd_pcm_oss_get_caps(pcm_oss_file) & DSP_CAP_DUPLEX)
+ return 0;
+ return -EIO;
+ case SNDCTL_DSP_GETODELAY:
+ res = snd_pcm_oss_get_odelay(pcm_oss_file);
+ if (res < 0) {
+ /* it's for sure, some broken apps don't check for error codes */
+ put_user(0, p);
+ return res;
+ }
+ return put_user(res, p);
+ case SNDCTL_DSP_PROFILE:
+ return 0; /* silently ignore */
+ default:
+ snd_printd("pcm_oss: unknown command = 0x%x\n", cmd);
+ }
+ return -EINVAL;
+}
+
+#ifdef CONFIG_COMPAT
+/* all compatible */
+#define snd_pcm_oss_ioctl_compat snd_pcm_oss_ioctl
+#else
+#define snd_pcm_oss_ioctl_compat NULL
+#endif
+
+static ssize_t snd_pcm_oss_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
+{
+ snd_pcm_oss_file_t *pcm_oss_file;
+ snd_pcm_substream_t *substream;
+
+ pcm_oss_file = file->private_data;
+ substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
+ if (substream == NULL)
+ return -ENXIO;
+#ifndef OSS_DEBUG
+ return snd_pcm_oss_read1(substream, buf, count);
+#else
+ {
+ ssize_t res = snd_pcm_oss_read1(substream, buf, count);
+ printk("pcm_oss: read %li bytes (returned %li bytes)\n", (long)count, (long)res);
+ return res;
+ }
+#endif
+}
+
+static ssize_t snd_pcm_oss_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
+{
+ snd_pcm_oss_file_t *pcm_oss_file;
+ snd_pcm_substream_t *substream;
+ long result;
+
+ pcm_oss_file = file->private_data;
+ substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
+ if (substream == NULL)
+ return -ENXIO;
+ up(&file->f_dentry->d_inode->i_sem);
+ result = snd_pcm_oss_write1(substream, buf, count);
+ down(&file->f_dentry->d_inode->i_sem);
+#ifdef OSS_DEBUG
+ printk("pcm_oss: write %li bytes (wrote %li bytes)\n", (long)count, (long)result);
+#endif
+ return result;
+}
+
+static int snd_pcm_oss_playback_ready(snd_pcm_substream_t *substream)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ if (atomic_read(&runtime->mmap_count))
+ return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt;
+ else
+ return snd_pcm_playback_avail(runtime) >= runtime->oss.period_frames;
+}
+
+static int snd_pcm_oss_capture_ready(snd_pcm_substream_t *substream)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ if (atomic_read(&runtime->mmap_count))
+ return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt;
+ else
+ return snd_pcm_capture_avail(runtime) >= runtime->oss.period_frames;
+}
+
+static unsigned int snd_pcm_oss_poll(struct file *file, poll_table * wait)
+{
+ snd_pcm_oss_file_t *pcm_oss_file;
+ unsigned int mask;
+ snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL;
+
+ pcm_oss_file = file->private_data;
+
+ psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
+ csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
+
+ mask = 0;
+ if (psubstream != NULL) {
+ snd_pcm_runtime_t *runtime = psubstream->runtime;
+ poll_wait(file, &runtime->sleep, wait);
+ snd_pcm_stream_lock_irq(psubstream);
+ if (runtime->status->state != SNDRV_PCM_STATE_DRAINING &&
+ (runtime->status->state != SNDRV_PCM_STATE_RUNNING ||
+ snd_pcm_oss_playback_ready(psubstream)))
+ mask |= POLLOUT | POLLWRNORM;
+ snd_pcm_stream_unlock_irq(psubstream);
+ }
+ if (csubstream != NULL) {
+ snd_pcm_runtime_t *runtime = csubstream->runtime;
+ enum sndrv_pcm_state ostate;
+ poll_wait(file, &runtime->sleep, wait);
+ snd_pcm_stream_lock_irq(csubstream);
+ if ((ostate = runtime->status->state) != SNDRV_PCM_STATE_RUNNING ||
+ snd_pcm_oss_capture_ready(csubstream))
+ mask |= POLLIN | POLLRDNORM;
+ snd_pcm_stream_unlock_irq(csubstream);
+ if (ostate != SNDRV_PCM_STATE_RUNNING && runtime->oss.trigger) {
+ snd_pcm_oss_file_t ofile;
+ memset(&ofile, 0, sizeof(ofile));
+ ofile.streams[SNDRV_PCM_STREAM_CAPTURE] = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
+ runtime->oss.trigger = 0;
+ snd_pcm_oss_set_trigger(&ofile, PCM_ENABLE_INPUT);
+ }
+ }
+
+ return mask;
+}
+
+static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area)
+{
+ snd_pcm_oss_file_t *pcm_oss_file;
+ snd_pcm_substream_t *substream = NULL;
+ snd_pcm_runtime_t *runtime;
+ int err;
+
+#ifdef OSS_DEBUG
+ printk("pcm_oss: mmap begin\n");
+#endif
+ pcm_oss_file = file->private_data;
+ switch ((area->vm_flags & (VM_READ | VM_WRITE))) {
+ case VM_READ | VM_WRITE:
+ substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
+ if (substream)
+ break;
+ /* Fall through */
+ case VM_READ:
+ substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
+ break;
+ case VM_WRITE:
+ substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* set VM_READ access as well to fix memset() routines that do
+ reads before writes (to improve performance) */
+ area->vm_flags |= VM_READ;
+ if (substream == NULL)
+ return -ENXIO;
+ runtime = substream->runtime;
+ if (!(runtime->info & SNDRV_PCM_INFO_MMAP_VALID))
+ return -EIO;
+ if (runtime->info & SNDRV_PCM_INFO_INTERLEAVED)
+ runtime->access = SNDRV_PCM_ACCESS_MMAP_INTERLEAVED;
+ else
+ return -EIO;
+
+ if (runtime->oss.params) {
+ if ((err = snd_pcm_oss_change_params(substream)) < 0)
+ return err;
+ }
+ if (runtime->oss.plugin_first != NULL)
+ return -EIO;
+
+ if (area->vm_pgoff != 0)
+ return -EINVAL;
+
+ err = snd_pcm_mmap_data(substream, file, area);
+ if (err < 0)
+ return err;
+ runtime->oss.mmap_bytes = area->vm_end - area->vm_start;
+ runtime->silence_threshold = 0;
+ runtime->silence_size = 0;
+#ifdef OSS_DEBUG
+ printk("pcm_oss: mmap ok, bytes = 0x%x\n", runtime->oss.mmap_bytes);
+#endif
+ /* In mmap mode we never stop */
+ runtime->stop_threshold = runtime->boundary;
+
+ return 0;
+}
+
+/*
+ * /proc interface
+ */
+
+static void snd_pcm_oss_proc_read(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ snd_pcm_str_t *pstr = (snd_pcm_str_t *)entry->private_data;
+ snd_pcm_oss_setup_t *setup = pstr->oss.setup_list;
+ down(&pstr->oss.setup_mutex);
+ while (setup) {
+ snd_iprintf(buffer, "%s %u %u%s%s%s%s%s%s\n",
+ setup->task_name,
+ setup->periods,
+ setup->period_size,
+ setup->disable ? " disable" : "",
+ setup->direct ? " direct" : "",
+ setup->block ? " block" : "",
+ setup->nonblock ? " non-block" : "",
+ setup->partialfrag ? " partial-frag" : "",
+ setup->nosilence ? " no-silence" : "");
+ setup = setup->next;
+ }
+ up(&pstr->oss.setup_mutex);
+}
+
+static void snd_pcm_oss_proc_free_setup_list(snd_pcm_str_t * pstr)
+{
+ unsigned int idx;
+ snd_pcm_substream_t *substream;
+ snd_pcm_oss_setup_t *setup, *setupn;
+
+ for (idx = 0, substream = pstr->substream;
+ idx < pstr->substream_count; idx++, substream = substream->next)
+ substream->oss.setup = NULL;
+ for (setup = pstr->oss.setup_list, pstr->oss.setup_list = NULL;
+ setup; setup = setupn) {
+ setupn = setup->next;
+ kfree(setup->task_name);
+ kfree(setup);
+ }
+ pstr->oss.setup_list = NULL;
+}
+
+static void snd_pcm_oss_proc_write(snd_info_entry_t *entry,
+ snd_info_buffer_t * buffer)
+{
+ snd_pcm_str_t *pstr = (snd_pcm_str_t *)entry->private_data;
+ char line[128], str[32], task_name[32], *ptr;
+ int idx1;
+ snd_pcm_oss_setup_t *setup, *setup1, template;
+
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ down(&pstr->oss.setup_mutex);
+ memset(&template, 0, sizeof(template));
+ ptr = snd_info_get_str(task_name, line, sizeof(task_name));
+ if (!strcmp(task_name, "clear") || !strcmp(task_name, "erase")) {
+ snd_pcm_oss_proc_free_setup_list(pstr);
+ up(&pstr->oss.setup_mutex);
+ continue;
+ }
+ for (setup = pstr->oss.setup_list; setup; setup = setup->next) {
+ if (!strcmp(setup->task_name, task_name)) {
+ template = *setup;
+ break;
+ }
+ }
+ ptr = snd_info_get_str(str, ptr, sizeof(str));
+ template.periods = simple_strtoul(str, NULL, 10);
+ ptr = snd_info_get_str(str, ptr, sizeof(str));
+ template.period_size = simple_strtoul(str, NULL, 10);
+ for (idx1 = 31; idx1 >= 0; idx1--)
+ if (template.period_size & (1 << idx1))
+ break;
+ for (idx1--; idx1 >= 0; idx1--)
+ template.period_size &= ~(1 << idx1);
+ do {
+ ptr = snd_info_get_str(str, ptr, sizeof(str));
+ if (!strcmp(str, "disable")) {
+ template.disable = 1;
+ } else if (!strcmp(str, "direct")) {
+ template.direct = 1;
+ } else if (!strcmp(str, "block")) {
+ template.block = 1;
+ } else if (!strcmp(str, "non-block")) {
+ template.nonblock = 1;
+ } else if (!strcmp(str, "partial-frag")) {
+ template.partialfrag = 1;
+ } else if (!strcmp(str, "no-silence")) {
+ template.nosilence = 1;
+ }
+ } while (*str);
+ if (setup == NULL) {
+ setup = (snd_pcm_oss_setup_t *) kmalloc(sizeof(snd_pcm_oss_setup_t), GFP_KERNEL);
+ if (setup) {
+ if (pstr->oss.setup_list == NULL) {
+ pstr->oss.setup_list = setup;
+ } else {
+ for (setup1 = pstr->oss.setup_list; setup1->next; setup1 = setup1->next);
+ setup1->next = setup;
+ }
+ template.task_name = snd_kmalloc_strdup(task_name, GFP_KERNEL);
+ } else {
+ buffer->error = -ENOMEM;
+ }
+ }
+ if (setup)
+ *setup = template;
+ up(&pstr->oss.setup_mutex);
+ }
+}
+
+static void snd_pcm_oss_proc_init(snd_pcm_t *pcm)
+{
+ int stream;
+ for (stream = 0; stream < 2; ++stream) {
+ snd_info_entry_t *entry;
+ snd_pcm_str_t *pstr = &pcm->streams[stream];
+ if (pstr->substream_count == 0)
+ continue;
+ if ((entry = snd_info_create_card_entry(pcm->card, "oss", pstr->proc_root)) != NULL) {
+ entry->content = SNDRV_INFO_CONTENT_TEXT;
+ entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+ entry->c.text.read_size = 8192;
+ entry->c.text.read = snd_pcm_oss_proc_read;
+ entry->c.text.write_size = 8192;
+ entry->c.text.write = snd_pcm_oss_proc_write;
+ entry->private_data = pstr;
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ entry = NULL;
+ }
+ }
+ pstr->oss.proc_entry = entry;
+ }
+}
+
+static void snd_pcm_oss_proc_done(snd_pcm_t *pcm)
+{
+ int stream;
+ for (stream = 0; stream < 2; ++stream) {
+ snd_pcm_str_t *pstr = &pcm->streams[stream];
+ if (pstr->oss.proc_entry) {
+ snd_info_unregister(pstr->oss.proc_entry);
+ pstr->oss.proc_entry = NULL;
+ snd_pcm_oss_proc_free_setup_list(pstr);
+ }
+ }
+}
+
+/*
+ * ENTRY functions
+ */
+
+static struct file_operations snd_pcm_oss_f_reg =
+{
+ .owner = THIS_MODULE,
+ .read = snd_pcm_oss_read,
+ .write = snd_pcm_oss_write,
+ .open = snd_pcm_oss_open,
+ .release = snd_pcm_oss_release,
+ .poll = snd_pcm_oss_poll,
+ .unlocked_ioctl = snd_pcm_oss_ioctl,
+ .compat_ioctl = snd_pcm_oss_ioctl_compat,
+ .mmap = snd_pcm_oss_mmap,
+};
+
+static snd_minor_t snd_pcm_oss_reg =
+{
+ .comment = "digital audio",
+ .f_ops = &snd_pcm_oss_f_reg,
+};
+
+static void register_oss_dsp(snd_pcm_t *pcm, int index)
+{
+ char name[128];
+ sprintf(name, "dsp%i%i", pcm->card->number, pcm->device);
+ if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM,
+ pcm->card, index, &snd_pcm_oss_reg,
+ name) < 0) {
+ snd_printk("unable to register OSS PCM device %i:%i\n", pcm->card->number, pcm->device);
+ }
+}
+
+static int snd_pcm_oss_register_minor(snd_pcm_t * pcm)
+{
+ pcm->oss.reg = 0;
+ if (dsp_map[pcm->card->number] == (int)pcm->device) {
+ char name[128];
+ int duplex;
+ register_oss_dsp(pcm, 0);
+ duplex = (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count > 0 &&
+ pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count &&
+ !(pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX));
+ sprintf(name, "%s%s", pcm->name, duplex ? " (DUPLEX)" : "");
+#ifdef SNDRV_OSS_INFO_DEV_AUDIO
+ snd_oss_info_register(SNDRV_OSS_INFO_DEV_AUDIO,
+ pcm->card->number,
+ name);
+#endif
+ pcm->oss.reg++;
+ pcm->oss.reg_mask |= 1;
+ }
+ if (adsp_map[pcm->card->number] == (int)pcm->device) {
+ register_oss_dsp(pcm, 1);
+ pcm->oss.reg++;
+ pcm->oss.reg_mask |= 2;
+ }
+
+ if (pcm->oss.reg)
+ snd_pcm_oss_proc_init(pcm);
+
+ return 0;
+}
+
+static int snd_pcm_oss_disconnect_minor(snd_pcm_t * pcm)
+{
+ if (pcm->oss.reg) {
+ if (pcm->oss.reg_mask & 1) {
+ pcm->oss.reg_mask &= ~1;
+ snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM,
+ pcm->card, 0);
+ }
+ if (pcm->oss.reg_mask & 2) {
+ pcm->oss.reg_mask &= ~2;
+ snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM,
+ pcm->card, 1);
+ }
+ }
+ return 0;
+}
+
+static int snd_pcm_oss_unregister_minor(snd_pcm_t * pcm)
+{
+ snd_pcm_oss_disconnect_minor(pcm);
+ if (pcm->oss.reg) {
+ if (dsp_map[pcm->card->number] == (int)pcm->device) {
+#ifdef SNDRV_OSS_INFO_DEV_AUDIO
+ snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_AUDIO, pcm->card->number);
+#endif
+ }
+ pcm->oss.reg = 0;
+ snd_pcm_oss_proc_done(pcm);
+ }
+ return 0;
+}
+
+static snd_pcm_notify_t snd_pcm_oss_notify =
+{
+ .n_register = snd_pcm_oss_register_minor,
+ .n_disconnect = snd_pcm_oss_disconnect_minor,
+ .n_unregister = snd_pcm_oss_unregister_minor,
+};
+
+static int __init alsa_pcm_oss_init(void)
+{
+ int i;
+ int err;
+
+ /* check device map table */
+ for (i = 0; i < SNDRV_CARDS; i++) {
+ if (dsp_map[i] < 0 || dsp_map[i] >= SNDRV_PCM_DEVICES) {
+ snd_printk("invalid dsp_map[%d] = %d\n", i, dsp_map[i]);
+ dsp_map[i] = 0;
+ }
+ if (adsp_map[i] < 0 || adsp_map[i] >= SNDRV_PCM_DEVICES) {
+ snd_printk("invalid adsp_map[%d] = %d\n", i, adsp_map[i]);
+ adsp_map[i] = 1;
+ }
+ }
+ if ((err = snd_pcm_notify(&snd_pcm_oss_notify, 0)) < 0)
+ return err;
+ return 0;
+}
+
+static void __exit alsa_pcm_oss_exit(void)
+{
+ snd_pcm_notify(&snd_pcm_oss_notify, 1);
+}
+
+module_init(alsa_pcm_oss_init)
+module_exit(alsa_pcm_oss_exit)
diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c
new file mode 100644
index 00000000000..6bb31009f0b
--- /dev/null
+++ b/sound/core/oss/pcm_plugin.c
@@ -0,0 +1,921 @@
+/*
+ * PCM Plug-In shared (kernel/library) code
+ * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
+ *
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#if 0
+#define PLUGIN_DEBUG
+#endif
+
+#include <sound/driver.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/vmalloc.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "pcm_plugin.h"
+
+#define snd_pcm_plug_first(plug) ((plug)->runtime->oss.plugin_first)
+#define snd_pcm_plug_last(plug) ((plug)->runtime->oss.plugin_last)
+
+static int snd_pcm_plugin_src_channels_mask(snd_pcm_plugin_t *plugin,
+ bitset_t *dst_vmask,
+ bitset_t **src_vmask)
+{
+ bitset_t *vmask = plugin->src_vmask;
+ bitset_copy(vmask, dst_vmask, plugin->src_format.channels);
+ *src_vmask = vmask;
+ return 0;
+}
+
+static int snd_pcm_plugin_dst_channels_mask(snd_pcm_plugin_t *plugin,
+ bitset_t *src_vmask,
+ bitset_t **dst_vmask)
+{
+ bitset_t *vmask = plugin->dst_vmask;
+ bitset_copy(vmask, src_vmask, plugin->dst_format.channels);
+ *dst_vmask = vmask;
+ return 0;
+}
+
+/*
+ * because some cards might have rates "very close", we ignore
+ * all "resampling" requests within +-5%
+ */
+static int rate_match(unsigned int src_rate, unsigned int dst_rate)
+{
+ unsigned int low = (src_rate * 95) / 100;
+ unsigned int high = (src_rate * 105) / 100;
+ return dst_rate >= low && dst_rate <= high;
+}
+
+static int snd_pcm_plugin_alloc(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t frames)
+{
+ snd_pcm_plugin_format_t *format;
+ ssize_t width;
+ size_t size;
+ unsigned int channel;
+ snd_pcm_plugin_channel_t *c;
+
+ if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ format = &plugin->src_format;
+ } else {
+ format = &plugin->dst_format;
+ }
+ if ((width = snd_pcm_format_physical_width(format->format)) < 0)
+ return width;
+ size = frames * format->channels * width;
+ snd_assert((size % 8) == 0, return -ENXIO);
+ size /= 8;
+ if (plugin->buf_frames < frames) {
+ vfree(plugin->buf);
+ plugin->buf = vmalloc(size);
+ plugin->buf_frames = frames;
+ }
+ if (!plugin->buf) {
+ plugin->buf_frames = 0;
+ return -ENOMEM;
+ }
+ c = plugin->buf_channels;
+ if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
+ for (channel = 0; channel < format->channels; channel++, c++) {
+ c->frames = frames;
+ c->enabled = 1;
+ c->wanted = 0;
+ c->area.addr = plugin->buf;
+ c->area.first = channel * width;
+ c->area.step = format->channels * width;
+ }
+ } else if (plugin->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) {
+ snd_assert((size % format->channels) == 0,);
+ size /= format->channels;
+ for (channel = 0; channel < format->channels; channel++, c++) {
+ c->frames = frames;
+ c->enabled = 1;
+ c->wanted = 0;
+ c->area.addr = plugin->buf + (channel * size);
+ c->area.first = 0;
+ c->area.step = width;
+ }
+ } else
+ return -EINVAL;
+ return 0;
+}
+
+int snd_pcm_plug_alloc(snd_pcm_plug_t *plug, snd_pcm_uframes_t frames)
+{
+ int err;
+ snd_assert(snd_pcm_plug_first(plug) != NULL, return -ENXIO);
+ if (snd_pcm_plug_stream(plug) == SNDRV_PCM_STREAM_PLAYBACK) {
+ snd_pcm_plugin_t *plugin = snd_pcm_plug_first(plug);
+ while (plugin->next) {
+ if (plugin->dst_frames)
+ frames = plugin->dst_frames(plugin, frames);
+ snd_assert(frames > 0, return -ENXIO);
+ plugin = plugin->next;
+ err = snd_pcm_plugin_alloc(plugin, frames);
+ if (err < 0)
+ return err;
+ }
+ } else {
+ snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug);
+ while (plugin->prev) {
+ if (plugin->src_frames)
+ frames = plugin->src_frames(plugin, frames);
+ snd_assert(frames > 0, return -ENXIO);
+ plugin = plugin->prev;
+ err = snd_pcm_plugin_alloc(plugin, frames);
+ if (err < 0)
+ return err;
+ }
+ }
+ return 0;
+}
+
+
+snd_pcm_sframes_t snd_pcm_plugin_client_channels(snd_pcm_plugin_t *plugin,
+ snd_pcm_uframes_t frames,
+ snd_pcm_plugin_channel_t **channels)
+{
+ *channels = plugin->buf_channels;
+ return frames;
+}
+
+int snd_pcm_plugin_build(snd_pcm_plug_t *plug,
+ const char *name,
+ snd_pcm_plugin_format_t *src_format,
+ snd_pcm_plugin_format_t *dst_format,
+ size_t extra,
+ snd_pcm_plugin_t **ret)
+{
+ snd_pcm_plugin_t *plugin;
+ unsigned int channels;
+
+ snd_assert(plug != NULL, return -ENXIO);
+ snd_assert(src_format != NULL && dst_format != NULL, return -ENXIO);
+ plugin = kcalloc(1, sizeof(*plugin) + extra, GFP_KERNEL);
+ if (plugin == NULL)
+ return -ENOMEM;
+ plugin->name = name;
+ plugin->plug = plug;
+ plugin->stream = snd_pcm_plug_stream(plug);
+ plugin->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
+ plugin->src_format = *src_format;
+ plugin->src_width = snd_pcm_format_physical_width(src_format->format);
+ snd_assert(plugin->src_width > 0, );
+ plugin->dst_format = *dst_format;
+ plugin->dst_width = snd_pcm_format_physical_width(dst_format->format);
+ snd_assert(plugin->dst_width > 0, );
+ if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ channels = src_format->channels;
+ else
+ channels = dst_format->channels;
+ plugin->buf_channels = kcalloc(channels, sizeof(*plugin->buf_channels), GFP_KERNEL);
+ if (plugin->buf_channels == NULL) {
+ snd_pcm_plugin_free(plugin);
+ return -ENOMEM;
+ }
+ plugin->src_vmask = bitset_alloc(src_format->channels);
+ if (plugin->src_vmask == NULL) {
+ snd_pcm_plugin_free(plugin);
+ return -ENOMEM;
+ }
+ plugin->dst_vmask = bitset_alloc(dst_format->channels);
+ if (plugin->dst_vmask == NULL) {
+ snd_pcm_plugin_free(plugin);
+ return -ENOMEM;
+ }
+ plugin->client_channels = snd_pcm_plugin_client_channels;
+ plugin->src_channels_mask = snd_pcm_plugin_src_channels_mask;
+ plugin->dst_channels_mask = snd_pcm_plugin_dst_channels_mask;
+ *ret = plugin;
+ return 0;
+}
+
+int snd_pcm_plugin_free(snd_pcm_plugin_t *plugin)
+{
+ if (! plugin)
+ return 0;
+ if (plugin->private_free)
+ plugin->private_free(plugin);
+ kfree(plugin->buf_channels);
+ vfree(plugin->buf);
+ kfree(plugin->src_vmask);
+ kfree(plugin->dst_vmask);
+ kfree(plugin);
+ return 0;
+}
+
+snd_pcm_sframes_t snd_pcm_plug_client_size(snd_pcm_plug_t *plug, snd_pcm_uframes_t drv_frames)
+{
+ snd_pcm_plugin_t *plugin, *plugin_prev, *plugin_next;
+ int stream = snd_pcm_plug_stream(plug);
+
+ snd_assert(plug != NULL, return -ENXIO);
+ if (drv_frames == 0)
+ return 0;
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ plugin = snd_pcm_plug_last(plug);
+ while (plugin && drv_frames > 0) {
+ plugin_prev = plugin->prev;
+ if (plugin->src_frames)
+ drv_frames = plugin->src_frames(plugin, drv_frames);
+ plugin = plugin_prev;
+ }
+ } else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
+ plugin = snd_pcm_plug_first(plug);
+ while (plugin && drv_frames > 0) {
+ plugin_next = plugin->next;
+ if (plugin->dst_frames)
+ drv_frames = plugin->dst_frames(plugin, drv_frames);
+ plugin = plugin_next;
+ }
+ } else
+ snd_BUG();
+ return drv_frames;
+}
+
+snd_pcm_sframes_t snd_pcm_plug_slave_size(snd_pcm_plug_t *plug, snd_pcm_uframes_t clt_frames)
+{
+ snd_pcm_plugin_t *plugin, *plugin_prev, *plugin_next;
+ snd_pcm_sframes_t frames;
+ int stream = snd_pcm_plug_stream(plug);
+
+ snd_assert(plug != NULL, return -ENXIO);
+ if (clt_frames == 0)
+ return 0;
+ frames = clt_frames;
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ plugin = snd_pcm_plug_first(plug);
+ while (plugin && frames > 0) {
+ plugin_next = plugin->next;
+ if (plugin->dst_frames) {
+ frames = plugin->dst_frames(plugin, frames);
+ if (frames < 0)
+ return frames;
+ }
+ plugin = plugin_next;
+ }
+ } else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
+ plugin = snd_pcm_plug_last(plug);
+ while (plugin) {
+ plugin_prev = plugin->prev;
+ if (plugin->src_frames) {
+ frames = plugin->src_frames(plugin, frames);
+ if (frames < 0)
+ return frames;
+ }
+ plugin = plugin_prev;
+ }
+ } else
+ snd_BUG();
+ return frames;
+}
+
+static int snd_pcm_plug_formats(snd_mask_t *mask, int format)
+{
+ snd_mask_t formats = *mask;
+ u64 linfmts = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_U24_BE | SNDRV_PCM_FMTBIT_S24_BE |
+ SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE);
+ snd_mask_set(&formats, SNDRV_PCM_FORMAT_MU_LAW);
+
+ if (formats.bits[0] & (u32)linfmts)
+ formats.bits[0] |= (u32)linfmts;
+ if (formats.bits[1] & (u32)(linfmts >> 32))
+ formats.bits[1] |= (u32)(linfmts >> 32);
+ return snd_mask_test(&formats, format);
+}
+
+static int preferred_formats[] = {
+ SNDRV_PCM_FORMAT_S16_LE,
+ SNDRV_PCM_FORMAT_S16_BE,
+ SNDRV_PCM_FORMAT_U16_LE,
+ SNDRV_PCM_FORMAT_U16_BE,
+ SNDRV_PCM_FORMAT_S24_LE,
+ SNDRV_PCM_FORMAT_S24_BE,
+ SNDRV_PCM_FORMAT_U24_LE,
+ SNDRV_PCM_FORMAT_U24_BE,
+ SNDRV_PCM_FORMAT_S32_LE,
+ SNDRV_PCM_FORMAT_S32_BE,
+ SNDRV_PCM_FORMAT_U32_LE,
+ SNDRV_PCM_FORMAT_U32_BE,
+ SNDRV_PCM_FORMAT_S8,
+ SNDRV_PCM_FORMAT_U8
+};
+
+int snd_pcm_plug_slave_format(int format, snd_mask_t *format_mask)
+{
+ if (snd_mask_test(format_mask, format))
+ return format;
+ if (! snd_pcm_plug_formats(format_mask, format))
+ return -EINVAL;
+ if (snd_pcm_format_linear(format)) {
+ int width = snd_pcm_format_width(format);
+ int unsignd = snd_pcm_format_unsigned(format);
+ int big = snd_pcm_format_big_endian(format);
+ int format1;
+ int wid, width1=width;
+ int dwidth1 = 8;
+ for (wid = 0; wid < 4; ++wid) {
+ int end, big1 = big;
+ for (end = 0; end < 2; ++end) {
+ int sgn, unsignd1 = unsignd;
+ for (sgn = 0; sgn < 2; ++sgn) {
+ format1 = snd_pcm_build_linear_format(width1, unsignd1, big1);
+ if (format1 >= 0 &&
+ snd_mask_test(format_mask, format1))
+ goto _found;
+ unsignd1 = !unsignd1;
+ }
+ big1 = !big1;
+ }
+ if (width1 == 32) {
+ dwidth1 = -dwidth1;
+ width1 = width;
+ }
+ width1 += dwidth1;
+ }
+ return -EINVAL;
+ _found:
+ return format1;
+ } else {
+ unsigned int i;
+ switch (format) {
+ case SNDRV_PCM_FORMAT_MU_LAW:
+ for (i = 0; i < ARRAY_SIZE(preferred_formats); ++i) {
+ int format1 = preferred_formats[i];
+ if (snd_mask_test(format_mask, format1))
+ return format1;
+ }
+ default:
+ return -EINVAL;
+ }
+ }
+}
+
+int snd_pcm_plug_format_plugins(snd_pcm_plug_t *plug,
+ snd_pcm_hw_params_t *params,
+ snd_pcm_hw_params_t *slave_params)
+{
+ snd_pcm_plugin_format_t tmpformat;
+ snd_pcm_plugin_format_t dstformat;
+ snd_pcm_plugin_format_t srcformat;
+ int src_access, dst_access;
+ snd_pcm_plugin_t *plugin = NULL;
+ int err;
+ int stream = snd_pcm_plug_stream(plug);
+ int slave_interleaved = (params_channels(slave_params) == 1 ||
+ params_access(slave_params) == SNDRV_PCM_ACCESS_RW_INTERLEAVED);
+
+ switch (stream) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ dstformat.format = params_format(slave_params);
+ dstformat.rate = params_rate(slave_params);
+ dstformat.channels = params_channels(slave_params);
+ srcformat.format = params_format(params);
+ srcformat.rate = params_rate(params);
+ srcformat.channels = params_channels(params);
+ src_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
+ dst_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED :
+ SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);
+ break;
+ case SNDRV_PCM_STREAM_CAPTURE:
+ dstformat.format = params_format(params);
+ dstformat.rate = params_rate(params);
+ dstformat.channels = params_channels(params);
+ srcformat.format = params_format(slave_params);
+ srcformat.rate = params_rate(slave_params);
+ srcformat.channels = params_channels(slave_params);
+ src_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED :
+ SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);
+ dst_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
+ break;
+ default:
+ snd_BUG();
+ return -EINVAL;
+ }
+ tmpformat = srcformat;
+
+ pdprintf("srcformat: format=%i, rate=%i, channels=%i\n",
+ srcformat.format,
+ srcformat.rate,
+ srcformat.channels);
+ pdprintf("dstformat: format=%i, rate=%i, channels=%i\n",
+ dstformat.format,
+ dstformat.rate,
+ dstformat.channels);
+
+ /* Format change (linearization) */
+ if ((srcformat.format != dstformat.format ||
+ !rate_match(srcformat.rate, dstformat.rate) ||
+ srcformat.channels != dstformat.channels) &&
+ !snd_pcm_format_linear(srcformat.format)) {
+ if (snd_pcm_format_linear(dstformat.format))
+ tmpformat.format = dstformat.format;
+ else
+ tmpformat.format = SNDRV_PCM_FORMAT_S16;
+ switch (srcformat.format) {
+ case SNDRV_PCM_FORMAT_MU_LAW:
+ err = snd_pcm_plugin_build_mulaw(plug,
+ &srcformat, &tmpformat,
+ &plugin);
+ break;
+ default:
+ return -EINVAL;
+ }
+ pdprintf("format change: src=%i, dst=%i returns %i\n", srcformat.format, tmpformat.format, err);
+ if (err < 0)
+ return err;
+ err = snd_pcm_plugin_append(plugin);
+ if (err < 0) {
+ snd_pcm_plugin_free(plugin);
+ return err;
+ }
+ srcformat = tmpformat;
+ src_access = dst_access;
+ }
+
+ /* channels reduction */
+ if (srcformat.channels > dstformat.channels) {
+ int sv = srcformat.channels;
+ int dv = dstformat.channels;
+ route_ttable_entry_t *ttable = kcalloc(dv * sv, sizeof(*ttable), GFP_KERNEL);
+ if (ttable == NULL)
+ return -ENOMEM;
+#if 1
+ if (sv == 2 && dv == 1) {
+ ttable[0] = HALF;
+ ttable[1] = HALF;
+ } else
+#endif
+ {
+ int v;
+ for (v = 0; v < dv; ++v)
+ ttable[v * sv + v] = FULL;
+ }
+ tmpformat.channels = dstformat.channels;
+ if (rate_match(srcformat.rate, dstformat.rate) &&
+ snd_pcm_format_linear(dstformat.format))
+ tmpformat.format = dstformat.format;
+ err = snd_pcm_plugin_build_route(plug,
+ &srcformat, &tmpformat,
+ ttable, &plugin);
+ kfree(ttable);
+ pdprintf("channels reduction: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err);
+ if (err < 0) {
+ snd_pcm_plugin_free(plugin);
+ return err;
+ }
+ err = snd_pcm_plugin_append(plugin);
+ if (err < 0) {
+ snd_pcm_plugin_free(plugin);
+ return err;
+ }
+ srcformat = tmpformat;
+ src_access = dst_access;
+ }
+
+ /* rate resampling */
+ if (!rate_match(srcformat.rate, dstformat.rate)) {
+ tmpformat.rate = dstformat.rate;
+ if (srcformat.channels == dstformat.channels &&
+ snd_pcm_format_linear(dstformat.format))
+ tmpformat.format = dstformat.format;
+ err = snd_pcm_plugin_build_rate(plug,
+ &srcformat, &tmpformat,
+ &plugin);
+ pdprintf("rate down resampling: src=%i, dst=%i returns %i\n", srcformat.rate, tmpformat.rate, err);
+ if (err < 0) {
+ snd_pcm_plugin_free(plugin);
+ return err;
+ }
+ err = snd_pcm_plugin_append(plugin);
+ if (err < 0) {
+ snd_pcm_plugin_free(plugin);
+ return err;
+ }
+ srcformat = tmpformat;
+ src_access = dst_access;
+ }
+
+ /* channels extension */
+ if (srcformat.channels < dstformat.channels) {
+ int sv = srcformat.channels;
+ int dv = dstformat.channels;
+ route_ttable_entry_t *ttable = kcalloc(dv * sv, sizeof(*ttable), GFP_KERNEL);
+ if (ttable == NULL)
+ return -ENOMEM;
+#if 0
+ {
+ int v;
+ for (v = 0; v < sv; ++v)
+ ttable[v * sv + v] = FULL;
+ }
+#else
+ {
+ /* Playback is spreaded on all channels */
+ int vd, vs;
+ for (vd = 0, vs = 0; vd < dv; ++vd) {
+ ttable[vd * sv + vs] = FULL;
+ vs++;
+ if (vs == sv)
+ vs = 0;
+ }
+ }
+#endif
+ tmpformat.channels = dstformat.channels;
+ if (snd_pcm_format_linear(dstformat.format))
+ tmpformat.format = dstformat.format;
+ err = snd_pcm_plugin_build_route(plug,
+ &srcformat, &tmpformat,
+ ttable, &plugin);
+ kfree(ttable);
+ pdprintf("channels extension: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err);
+ if (err < 0) {
+ snd_pcm_plugin_free(plugin);
+ return err;
+ }
+ err = snd_pcm_plugin_append(plugin);
+ if (err < 0) {
+ snd_pcm_plugin_free(plugin);
+ return err;
+ }
+ srcformat = tmpformat;
+ src_access = dst_access;
+ }
+
+ /* format change */
+ if (srcformat.format != dstformat.format) {
+ tmpformat.format = dstformat.format;
+ if (tmpformat.format == SNDRV_PCM_FORMAT_MU_LAW) {
+ err = snd_pcm_plugin_build_mulaw(plug,
+ &srcformat, &tmpformat,
+ &plugin);
+ }
+ else if (snd_pcm_format_linear(srcformat.format) &&
+ snd_pcm_format_linear(tmpformat.format)) {
+ err = snd_pcm_plugin_build_linear(plug,
+ &srcformat, &tmpformat,
+ &plugin);
+ }
+ else
+ return -EINVAL;
+ pdprintf("format change: src=%i, dst=%i returns %i\n", srcformat.format, tmpformat.format, err);
+ if (err < 0)
+ return err;
+ err = snd_pcm_plugin_append(plugin);
+ if (err < 0) {
+ snd_pcm_plugin_free(plugin);
+ return err;
+ }
+ srcformat = tmpformat;
+ src_access = dst_access;
+ }
+
+ /* de-interleave */
+ if (src_access != dst_access) {
+ err = snd_pcm_plugin_build_copy(plug,
+ &srcformat,
+ &tmpformat,
+ &plugin);
+ pdprintf("interleave change (copy: returns %i)\n", err);
+ if (err < 0)
+ return err;
+ err = snd_pcm_plugin_append(plugin);
+ if (err < 0) {
+ snd_pcm_plugin_free(plugin);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(snd_pcm_plug_t *plug,
+ char *buf,
+ snd_pcm_uframes_t count,
+ snd_pcm_plugin_channel_t **channels)
+{
+ snd_pcm_plugin_t *plugin;
+ snd_pcm_plugin_channel_t *v;
+ snd_pcm_plugin_format_t *format;
+ int width, nchannels, channel;
+ int stream = snd_pcm_plug_stream(plug);
+
+ snd_assert(buf != NULL, return -ENXIO);
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ plugin = snd_pcm_plug_first(plug);
+ format = &plugin->src_format;
+ } else {
+ plugin = snd_pcm_plug_last(plug);
+ format = &plugin->dst_format;
+ }
+ v = plugin->buf_channels;
+ *channels = v;
+ if ((width = snd_pcm_format_physical_width(format->format)) < 0)
+ return width;
+ nchannels = format->channels;
+ snd_assert(plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || format->channels <= 1, return -ENXIO);
+ for (channel = 0; channel < nchannels; channel++, v++) {
+ v->frames = count;
+ v->enabled = 1;
+ v->wanted = (stream == SNDRV_PCM_STREAM_CAPTURE);
+ v->area.addr = buf;
+ v->area.first = channel * width;
+ v->area.step = nchannels * width;
+ }
+ return count;
+}
+
+static int snd_pcm_plug_playback_channels_mask(snd_pcm_plug_t *plug,
+ bitset_t *client_vmask)
+{
+ snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug);
+ if (plugin == NULL) {
+ return 0;
+ } else {
+ int schannels = plugin->dst_format.channels;
+ bitset_t bs[bitset_size(schannels)];
+ bitset_t *srcmask;
+ bitset_t *dstmask = bs;
+ int err;
+ bitset_one(dstmask, schannels);
+ if (plugin == NULL) {
+ bitset_and(client_vmask, dstmask, schannels);
+ return 0;
+ }
+ while (1) {
+ err = plugin->src_channels_mask(plugin, dstmask, &srcmask);
+ if (err < 0)
+ return err;
+ dstmask = srcmask;
+ if (plugin->prev == NULL)
+ break;
+ plugin = plugin->prev;
+ }
+ bitset_and(client_vmask, dstmask, plugin->src_format.channels);
+ return 0;
+ }
+}
+
+static int snd_pcm_plug_playback_disable_useless_channels(snd_pcm_plug_t *plug,
+ snd_pcm_plugin_channel_t *src_channels)
+{
+ snd_pcm_plugin_t *plugin = snd_pcm_plug_first(plug);
+ unsigned int nchannels = plugin->src_format.channels;
+ bitset_t bs[bitset_size(nchannels)];
+ bitset_t *srcmask = bs;
+ int err;
+ unsigned int channel;
+ for (channel = 0; channel < nchannels; channel++) {
+ if (src_channels[channel].enabled)
+ bitset_set(srcmask, channel);
+ else
+ bitset_reset(srcmask, channel);
+ }
+ err = snd_pcm_plug_playback_channels_mask(plug, srcmask);
+ if (err < 0)
+ return err;
+ for (channel = 0; channel < nchannels; channel++) {
+ if (!bitset_get(srcmask, channel))
+ src_channels[channel].enabled = 0;
+ }
+ return 0;
+}
+
+static int snd_pcm_plug_capture_disable_useless_channels(snd_pcm_plug_t *plug,
+ snd_pcm_plugin_channel_t *src_channels,
+ snd_pcm_plugin_channel_t *client_channels)
+{
+ snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug);
+ unsigned int nchannels = plugin->dst_format.channels;
+ bitset_t bs[bitset_size(nchannels)];
+ bitset_t *dstmask = bs;
+ bitset_t *srcmask;
+ int err;
+ unsigned int channel;
+ for (channel = 0; channel < nchannels; channel++) {
+ if (client_channels[channel].enabled)
+ bitset_set(dstmask, channel);
+ else
+ bitset_reset(dstmask, channel);
+ }
+ while (plugin) {
+ err = plugin->src_channels_mask(plugin, dstmask, &srcmask);
+ if (err < 0)
+ return err;
+ dstmask = srcmask;
+ plugin = plugin->prev;
+ }
+ plugin = snd_pcm_plug_first(plug);
+ nchannels = plugin->src_format.channels;
+ for (channel = 0; channel < nchannels; channel++) {
+ if (!bitset_get(dstmask, channel))
+ src_channels[channel].enabled = 0;
+ }
+ return 0;
+}
+
+snd_pcm_sframes_t snd_pcm_plug_write_transfer(snd_pcm_plug_t *plug, snd_pcm_plugin_channel_t *src_channels, snd_pcm_uframes_t size)
+{
+ snd_pcm_plugin_t *plugin, *next;
+ snd_pcm_plugin_channel_t *dst_channels;
+ int err;
+ snd_pcm_sframes_t frames = size;
+
+ if ((err = snd_pcm_plug_playback_disable_useless_channels(plug, src_channels)) < 0)
+ return err;
+
+ plugin = snd_pcm_plug_first(plug);
+ while (plugin && frames > 0) {
+ if ((next = plugin->next) != NULL) {
+ snd_pcm_sframes_t frames1 = frames;
+ if (plugin->dst_frames)
+ frames1 = plugin->dst_frames(plugin, frames);
+ if ((err = next->client_channels(next, frames1, &dst_channels)) < 0) {
+ return err;
+ }
+ if (err != frames1) {
+ frames = err;
+ if (plugin->src_frames)
+ frames = plugin->src_frames(plugin, frames1);
+ }
+ } else
+ dst_channels = NULL;
+ pdprintf("write plugin: %s, %li\n", plugin->name, frames);
+ if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0)
+ return frames;
+ src_channels = dst_channels;
+ plugin = next;
+ }
+ return snd_pcm_plug_client_size(plug, frames);
+}
+
+snd_pcm_sframes_t snd_pcm_plug_read_transfer(snd_pcm_plug_t *plug, snd_pcm_plugin_channel_t *dst_channels_final, snd_pcm_uframes_t size)
+{
+ snd_pcm_plugin_t *plugin, *next;
+ snd_pcm_plugin_channel_t *src_channels, *dst_channels;
+ snd_pcm_sframes_t frames = size;
+ int err;
+
+ frames = snd_pcm_plug_slave_size(plug, frames);
+ if (frames < 0)
+ return frames;
+
+ src_channels = NULL;
+ plugin = snd_pcm_plug_first(plug);
+ while (plugin && frames > 0) {
+ if ((next = plugin->next) != NULL) {
+ if ((err = plugin->client_channels(plugin, frames, &dst_channels)) < 0) {
+ return err;
+ }
+ frames = err;
+ if (!plugin->prev) {
+ if ((err = snd_pcm_plug_capture_disable_useless_channels(plug, dst_channels, dst_channels_final)) < 0)
+ return err;
+ }
+ } else {
+ dst_channels = dst_channels_final;
+ }
+ pdprintf("read plugin: %s, %li\n", plugin->name, frames);
+ if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0)
+ return frames;
+ plugin = next;
+ src_channels = dst_channels;
+ }
+ return frames;
+}
+
+int snd_pcm_area_silence(const snd_pcm_channel_area_t *dst_area, size_t dst_offset,
+ size_t samples, int format)
+{
+ /* FIXME: sub byte resolution and odd dst_offset */
+ unsigned char *dst;
+ unsigned int dst_step;
+ int width;
+ const unsigned char *silence;
+ if (!dst_area->addr)
+ return 0;
+ dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8;
+ width = snd_pcm_format_physical_width(format);
+ if (width <= 0)
+ return -EINVAL;
+ if (dst_area->step == (unsigned int) width && width >= 8)
+ return snd_pcm_format_set_silence(format, dst, samples);
+ silence = snd_pcm_format_silence_64(format);
+ if (! silence)
+ return -EINVAL;
+ dst_step = dst_area->step / 8;
+ if (width == 4) {
+ /* Ima ADPCM */
+ int dstbit = dst_area->first % 8;
+ int dstbit_step = dst_area->step % 8;
+ while (samples-- > 0) {
+ if (dstbit)
+ *dst &= 0xf0;
+ else
+ *dst &= 0x0f;
+ dst += dst_step;
+ dstbit += dstbit_step;
+ if (dstbit == 8) {
+ dst++;
+ dstbit = 0;
+ }
+ }
+ } else {
+ width /= 8;
+ while (samples-- > 0) {
+ memcpy(dst, silence, width);
+ dst += dst_step;
+ }
+ }
+ return 0;
+}
+
+int snd_pcm_area_copy(const snd_pcm_channel_area_t *src_area, size_t src_offset,
+ const snd_pcm_channel_area_t *dst_area, size_t dst_offset,
+ size_t samples, int format)
+{
+ /* FIXME: sub byte resolution and odd dst_offset */
+ char *src, *dst;
+ int width;
+ int src_step, dst_step;
+ src = src_area->addr + (src_area->first + src_area->step * src_offset) / 8;
+ if (!src_area->addr)
+ return snd_pcm_area_silence(dst_area, dst_offset, samples, format);
+ dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8;
+ if (!dst_area->addr)
+ return 0;
+ width = snd_pcm_format_physical_width(format);
+ if (width <= 0)
+ return -EINVAL;
+ if (src_area->step == (unsigned int) width &&
+ dst_area->step == (unsigned int) width && width >= 8) {
+ size_t bytes = samples * width / 8;
+ memcpy(dst, src, bytes);
+ return 0;
+ }
+ src_step = src_area->step / 8;
+ dst_step = dst_area->step / 8;
+ if (width == 4) {
+ /* Ima ADPCM */
+ int srcbit = src_area->first % 8;
+ int srcbit_step = src_area->step % 8;
+ int dstbit = dst_area->first % 8;
+ int dstbit_step = dst_area->step % 8;
+ while (samples-- > 0) {
+ unsigned char srcval;
+ if (srcbit)
+ srcval = *src & 0x0f;
+ else
+ srcval = (*src & 0xf0) >> 4;
+ if (dstbit)
+ *dst = (*dst & 0xf0) | srcval;
+ else
+ *dst = (*dst & 0x0f) | (srcval << 4);
+ src += src_step;
+ srcbit += srcbit_step;
+ if (srcbit == 8) {
+ src++;
+ srcbit = 0;
+ }
+ dst += dst_step;
+ dstbit += dstbit_step;
+ if (dstbit == 8) {
+ dst++;
+ dstbit = 0;
+ }
+ }
+ } else {
+ width /= 8;
+ while (samples-- > 0) {
+ memcpy(dst, src, width);
+ src += src_step;
+ dst += dst_step;
+ }
+ }
+ return 0;
+}
diff --git a/sound/core/oss/pcm_plugin.h b/sound/core/oss/pcm_plugin.h
new file mode 100644
index 00000000000..0f86ce47749
--- /dev/null
+++ b/sound/core/oss/pcm_plugin.h
@@ -0,0 +1,250 @@
+#ifndef __PCM_PLUGIN_H
+#define __PCM_PLUGIN_H
+
+/*
+ * Digital Audio (Plugin interface) abstract layer
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef ATTRIBUTE_UNUSED
+#define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
+#endif
+
+typedef unsigned int bitset_t;
+
+static inline size_t bitset_size(int nbits)
+{
+ return (nbits + sizeof(bitset_t) * 8 - 1) / (sizeof(bitset_t) * 8);
+}
+
+static inline bitset_t *bitset_alloc(int nbits)
+{
+ return kcalloc(bitset_size(nbits), sizeof(bitset_t), GFP_KERNEL);
+}
+
+static inline void bitset_set(bitset_t *bitmap, unsigned int pos)
+{
+ size_t bits = sizeof(*bitmap) * 8;
+ bitmap[pos / bits] |= 1 << (pos % bits);
+}
+
+static inline void bitset_reset(bitset_t *bitmap, unsigned int pos)
+{
+ size_t bits = sizeof(*bitmap) * 8;
+ bitmap[pos / bits] &= ~(1 << (pos % bits));
+}
+
+static inline int bitset_get(bitset_t *bitmap, unsigned int pos)
+{
+ size_t bits = sizeof(*bitmap) * 8;
+ return !!(bitmap[pos / bits] & (1 << (pos % bits)));
+}
+
+static inline void bitset_copy(bitset_t *dst, bitset_t *src, unsigned int nbits)
+{
+ memcpy(dst, src, bitset_size(nbits) * sizeof(bitset_t));
+}
+
+static inline void bitset_and(bitset_t *dst, bitset_t *bs, unsigned int nbits)
+{
+ bitset_t *end = dst + bitset_size(nbits);
+ while (dst < end)
+ *dst++ &= *bs++;
+}
+
+static inline void bitset_or(bitset_t *dst, bitset_t *bs, unsigned int nbits)
+{
+ bitset_t *end = dst + bitset_size(nbits);
+ while (dst < end)
+ *dst++ |= *bs++;
+}
+
+static inline void bitset_zero(bitset_t *dst, unsigned int nbits)
+{
+ bitset_t *end = dst + bitset_size(nbits);
+ while (dst < end)
+ *dst++ = 0;
+}
+
+static inline void bitset_one(bitset_t *dst, unsigned int nbits)
+{
+ bitset_t *end = dst + bitset_size(nbits);
+ while (dst < end)
+ *dst++ = ~(bitset_t)0;
+}
+
+#define snd_pcm_plug_t snd_pcm_substream_t
+#define snd_pcm_plug_stream(plug) ((plug)->stream)
+
+typedef enum {
+ INIT = 0,
+ PREPARE = 1,
+} snd_pcm_plugin_action_t;
+
+typedef struct _snd_pcm_channel_area {
+ void *addr; /* base address of channel samples */
+ unsigned int first; /* offset to first sample in bits */
+ unsigned int step; /* samples distance in bits */
+} snd_pcm_channel_area_t;
+
+typedef struct _snd_pcm_plugin_channel {
+ void *aptr; /* pointer to the allocated area */
+ snd_pcm_channel_area_t area;
+ snd_pcm_uframes_t frames; /* allocated frames */
+ unsigned int enabled:1; /* channel need to be processed */
+ unsigned int wanted:1; /* channel is wanted */
+} snd_pcm_plugin_channel_t;
+
+typedef struct _snd_pcm_plugin_format {
+ int format;
+ unsigned int rate;
+ unsigned int channels;
+} snd_pcm_plugin_format_t;
+
+struct _snd_pcm_plugin {
+ const char *name; /* plug-in name */
+ int stream;
+ snd_pcm_plugin_format_t src_format; /* source format */
+ snd_pcm_plugin_format_t dst_format; /* destination format */
+ int src_width; /* sample width in bits */
+ int dst_width; /* sample width in bits */
+ int access;
+ snd_pcm_sframes_t (*src_frames)(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t dst_frames);
+ snd_pcm_sframes_t (*dst_frames)(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t src_frames);
+ snd_pcm_sframes_t (*client_channels)(snd_pcm_plugin_t *plugin,
+ snd_pcm_uframes_t frames,
+ snd_pcm_plugin_channel_t **channels);
+ int (*src_channels_mask)(snd_pcm_plugin_t *plugin,
+ bitset_t *dst_vmask,
+ bitset_t **src_vmask);
+ int (*dst_channels_mask)(snd_pcm_plugin_t *plugin,
+ bitset_t *src_vmask,
+ bitset_t **dst_vmask);
+ snd_pcm_sframes_t (*transfer)(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels,
+ snd_pcm_plugin_channel_t *dst_channels,
+ snd_pcm_uframes_t frames);
+ int (*action)(snd_pcm_plugin_t *plugin,
+ snd_pcm_plugin_action_t action,
+ unsigned long data);
+ snd_pcm_plugin_t *prev;
+ snd_pcm_plugin_t *next;
+ snd_pcm_plug_t *plug;
+ void *private_data;
+ void (*private_free)(snd_pcm_plugin_t *plugin);
+ char *buf;
+ snd_pcm_uframes_t buf_frames;
+ snd_pcm_plugin_channel_t *buf_channels;
+ bitset_t *src_vmask;
+ bitset_t *dst_vmask;
+ char extra_data[0];
+};
+
+int snd_pcm_plugin_build(snd_pcm_plug_t *handle,
+ const char *name,
+ snd_pcm_plugin_format_t *src_format,
+ snd_pcm_plugin_format_t *dst_format,
+ size_t extra,
+ snd_pcm_plugin_t **ret);
+int snd_pcm_plugin_free(snd_pcm_plugin_t *plugin);
+int snd_pcm_plugin_clear(snd_pcm_plugin_t **first);
+int snd_pcm_plug_alloc(snd_pcm_plug_t *plug, snd_pcm_uframes_t frames);
+snd_pcm_sframes_t snd_pcm_plug_client_size(snd_pcm_plug_t *handle, snd_pcm_uframes_t drv_size);
+snd_pcm_sframes_t snd_pcm_plug_slave_size(snd_pcm_plug_t *handle, snd_pcm_uframes_t clt_size);
+
+#define FULL ROUTE_PLUGIN_RESOLUTION
+#define HALF ROUTE_PLUGIN_RESOLUTION / 2
+typedef int route_ttable_entry_t;
+
+int snd_pcm_plugin_build_io(snd_pcm_plug_t *handle,
+ snd_pcm_hw_params_t *params,
+ snd_pcm_plugin_t **r_plugin);
+int snd_pcm_plugin_build_linear(snd_pcm_plug_t *handle,
+ snd_pcm_plugin_format_t *src_format,
+ snd_pcm_plugin_format_t *dst_format,
+ snd_pcm_plugin_t **r_plugin);
+int snd_pcm_plugin_build_mulaw(snd_pcm_plug_t *handle,
+ snd_pcm_plugin_format_t *src_format,
+ snd_pcm_plugin_format_t *dst_format,
+ snd_pcm_plugin_t **r_plugin);
+int snd_pcm_plugin_build_rate(snd_pcm_plug_t *handle,
+ snd_pcm_plugin_format_t *src_format,
+ snd_pcm_plugin_format_t *dst_format,
+ snd_pcm_plugin_t **r_plugin);
+int snd_pcm_plugin_build_route(snd_pcm_plug_t *handle,
+ snd_pcm_plugin_format_t *src_format,
+ snd_pcm_plugin_format_t *dst_format,
+ route_ttable_entry_t *ttable,
+ snd_pcm_plugin_t **r_plugin);
+int snd_pcm_plugin_build_copy(snd_pcm_plug_t *handle,
+ snd_pcm_plugin_format_t *src_format,
+ snd_pcm_plugin_format_t *dst_format,
+ snd_pcm_plugin_t **r_plugin);
+
+int snd_pcm_plug_format_plugins(snd_pcm_plug_t *substream,
+ snd_pcm_hw_params_t *params,
+ snd_pcm_hw_params_t *slave_params);
+
+int snd_pcm_plug_slave_format(int format, snd_mask_t *format_mask);
+
+int snd_pcm_plugin_append(snd_pcm_plugin_t *plugin);
+
+snd_pcm_sframes_t snd_pcm_plug_write_transfer(snd_pcm_plug_t *handle, snd_pcm_plugin_channel_t *src_channels, snd_pcm_uframes_t size);
+snd_pcm_sframes_t snd_pcm_plug_read_transfer(snd_pcm_plug_t *handle, snd_pcm_plugin_channel_t *dst_channels_final, snd_pcm_uframes_t size);
+
+snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(snd_pcm_plug_t *handle,
+ char *buf, snd_pcm_uframes_t count,
+ snd_pcm_plugin_channel_t **channels);
+
+snd_pcm_sframes_t snd_pcm_plugin_client_channels(snd_pcm_plugin_t *plugin,
+ snd_pcm_uframes_t frames,
+ snd_pcm_plugin_channel_t **channels);
+
+int snd_pcm_area_silence(const snd_pcm_channel_area_t *dst_channel, size_t dst_offset,
+ size_t samples, int format);
+int snd_pcm_area_copy(const snd_pcm_channel_area_t *src_channel, size_t src_offset,
+ const snd_pcm_channel_area_t *dst_channel, size_t dst_offset,
+ size_t samples, int format);
+
+void *snd_pcm_plug_buf_alloc(snd_pcm_plug_t *plug, snd_pcm_uframes_t size);
+void snd_pcm_plug_buf_unlock(snd_pcm_plug_t *plug, void *ptr);
+snd_pcm_sframes_t snd_pcm_oss_write3(snd_pcm_substream_t *substream, const char *ptr, snd_pcm_uframes_t size, int in_kernel);
+snd_pcm_sframes_t snd_pcm_oss_read3(snd_pcm_substream_t *substream, char *ptr, snd_pcm_uframes_t size, int in_kernel);
+snd_pcm_sframes_t snd_pcm_oss_writev3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel);
+snd_pcm_sframes_t snd_pcm_oss_readv3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel);
+
+
+
+#define ROUTE_PLUGIN_RESOLUTION 16
+
+int getput_index(int format);
+int copy_index(int format);
+int conv_index(int src_format, int dst_format);
+
+void zero_channel(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *dst_channel,
+ size_t samples);
+
+#ifdef PLUGIN_DEBUG
+#define pdprintf( fmt, args... ) printk( "plugin: " fmt, ##args)
+#else
+#define pdprintf( fmt, args... )
+#endif
+
+#endif /* __PCM_PLUGIN_H */
diff --git a/sound/core/oss/plugin_ops.h b/sound/core/oss/plugin_ops.h
new file mode 100644
index 00000000000..0607e956608
--- /dev/null
+++ b/sound/core/oss/plugin_ops.h
@@ -0,0 +1,536 @@
+/*
+ * Plugin sample operators with fast switch
+ * Copyright (c) 2000 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+
+#define as_u8(ptr) (*(u_int8_t*)(ptr))
+#define as_u16(ptr) (*(u_int16_t*)(ptr))
+#define as_u32(ptr) (*(u_int32_t*)(ptr))
+#define as_u64(ptr) (*(u_int64_t*)(ptr))
+#define as_s8(ptr) (*(int8_t*)(ptr))
+#define as_s16(ptr) (*(int16_t*)(ptr))
+#define as_s32(ptr) (*(int32_t*)(ptr))
+#define as_s64(ptr) (*(int64_t*)(ptr))
+
+#ifdef COPY_LABELS
+static void *copy_labels[4] = {
+ &&copy_8,
+ &&copy_16,
+ &&copy_32,
+ &&copy_64
+};
+#endif
+
+#ifdef COPY_END
+while(0) {
+copy_8: as_s8(dst) = as_s8(src); goto COPY_END;
+copy_16: as_s16(dst) = as_s16(src); goto COPY_END;
+copy_32: as_s32(dst) = as_s32(src); goto COPY_END;
+copy_64: as_s64(dst) = as_s64(src); goto COPY_END;
+}
+#endif
+
+#ifdef CONV_LABELS
+/* src_wid src_endswap sign_toggle dst_wid dst_endswap */
+static void *conv_labels[4 * 2 * 2 * 4 * 2] = {
+ &&conv_xxx1_xxx1, /* 8h -> 8h */
+ &&conv_xxx1_xxx1, /* 8h -> 8s */
+ &&conv_xxx1_xx10, /* 8h -> 16h */
+ &&conv_xxx1_xx01, /* 8h -> 16s */
+ &&conv_xxx1_x100, /* 8h -> 24h */
+ &&conv_xxx1_001x, /* 8h -> 24s */
+ &&conv_xxx1_1000, /* 8h -> 32h */
+ &&conv_xxx1_0001, /* 8h -> 32s */
+ &&conv_xxx1_xxx9, /* 8h ^> 8h */
+ &&conv_xxx1_xxx9, /* 8h ^> 8s */
+ &&conv_xxx1_xx90, /* 8h ^> 16h */
+ &&conv_xxx1_xx09, /* 8h ^> 16s */
+ &&conv_xxx1_x900, /* 8h ^> 24h */
+ &&conv_xxx1_009x, /* 8h ^> 24s */
+ &&conv_xxx1_9000, /* 8h ^> 32h */
+ &&conv_xxx1_0009, /* 8h ^> 32s */
+ &&conv_xxx1_xxx1, /* 8s -> 8h */
+ &&conv_xxx1_xxx1, /* 8s -> 8s */
+ &&conv_xxx1_xx10, /* 8s -> 16h */
+ &&conv_xxx1_xx01, /* 8s -> 16s */
+ &&conv_xxx1_x100, /* 8s -> 24h */
+ &&conv_xxx1_001x, /* 8s -> 24s */
+ &&conv_xxx1_1000, /* 8s -> 32h */
+ &&conv_xxx1_0001, /* 8s -> 32s */
+ &&conv_xxx1_xxx9, /* 8s ^> 8h */
+ &&conv_xxx1_xxx9, /* 8s ^> 8s */
+ &&conv_xxx1_xx90, /* 8s ^> 16h */
+ &&conv_xxx1_xx09, /* 8s ^> 16s */
+ &&conv_xxx1_x900, /* 8s ^> 24h */
+ &&conv_xxx1_009x, /* 8s ^> 24s */
+ &&conv_xxx1_9000, /* 8s ^> 32h */
+ &&conv_xxx1_0009, /* 8s ^> 32s */
+ &&conv_xx12_xxx1, /* 16h -> 8h */
+ &&conv_xx12_xxx1, /* 16h -> 8s */
+ &&conv_xx12_xx12, /* 16h -> 16h */
+ &&conv_xx12_xx21, /* 16h -> 16s */
+ &&conv_xx12_x120, /* 16h -> 24h */
+ &&conv_xx12_021x, /* 16h -> 24s */
+ &&conv_xx12_1200, /* 16h -> 32h */
+ &&conv_xx12_0021, /* 16h -> 32s */
+ &&conv_xx12_xxx9, /* 16h ^> 8h */
+ &&conv_xx12_xxx9, /* 16h ^> 8s */
+ &&conv_xx12_xx92, /* 16h ^> 16h */
+ &&conv_xx12_xx29, /* 16h ^> 16s */
+ &&conv_xx12_x920, /* 16h ^> 24h */
+ &&conv_xx12_029x, /* 16h ^> 24s */
+ &&conv_xx12_9200, /* 16h ^> 32h */
+ &&conv_xx12_0029, /* 16h ^> 32s */
+ &&conv_xx12_xxx2, /* 16s -> 8h */
+ &&conv_xx12_xxx2, /* 16s -> 8s */
+ &&conv_xx12_xx21, /* 16s -> 16h */
+ &&conv_xx12_xx12, /* 16s -> 16s */
+ &&conv_xx12_x210, /* 16s -> 24h */
+ &&conv_xx12_012x, /* 16s -> 24s */
+ &&conv_xx12_2100, /* 16s -> 32h */
+ &&conv_xx12_0012, /* 16s -> 32s */
+ &&conv_xx12_xxxA, /* 16s ^> 8h */
+ &&conv_xx12_xxxA, /* 16s ^> 8s */
+ &&conv_xx12_xxA1, /* 16s ^> 16h */
+ &&conv_xx12_xx1A, /* 16s ^> 16s */
+ &&conv_xx12_xA10, /* 16s ^> 24h */
+ &&conv_xx12_01Ax, /* 16s ^> 24s */
+ &&conv_xx12_A100, /* 16s ^> 32h */
+ &&conv_xx12_001A, /* 16s ^> 32s */
+ &&conv_x123_xxx1, /* 24h -> 8h */
+ &&conv_x123_xxx1, /* 24h -> 8s */
+ &&conv_x123_xx12, /* 24h -> 16h */
+ &&conv_x123_xx21, /* 24h -> 16s */
+ &&conv_x123_x123, /* 24h -> 24h */
+ &&conv_x123_321x, /* 24h -> 24s */
+ &&conv_x123_1230, /* 24h -> 32h */
+ &&conv_x123_0321, /* 24h -> 32s */
+ &&conv_x123_xxx9, /* 24h ^> 8h */
+ &&conv_x123_xxx9, /* 24h ^> 8s */
+ &&conv_x123_xx92, /* 24h ^> 16h */
+ &&conv_x123_xx29, /* 24h ^> 16s */
+ &&conv_x123_x923, /* 24h ^> 24h */
+ &&conv_x123_329x, /* 24h ^> 24s */
+ &&conv_x123_9230, /* 24h ^> 32h */
+ &&conv_x123_0329, /* 24h ^> 32s */
+ &&conv_123x_xxx3, /* 24s -> 8h */
+ &&conv_123x_xxx3, /* 24s -> 8s */
+ &&conv_123x_xx32, /* 24s -> 16h */
+ &&conv_123x_xx23, /* 24s -> 16s */
+ &&conv_123x_x321, /* 24s -> 24h */
+ &&conv_123x_123x, /* 24s -> 24s */
+ &&conv_123x_3210, /* 24s -> 32h */
+ &&conv_123x_0123, /* 24s -> 32s */
+ &&conv_123x_xxxB, /* 24s ^> 8h */
+ &&conv_123x_xxxB, /* 24s ^> 8s */
+ &&conv_123x_xxB2, /* 24s ^> 16h */
+ &&conv_123x_xx2B, /* 24s ^> 16s */
+ &&conv_123x_xB21, /* 24s ^> 24h */
+ &&conv_123x_12Bx, /* 24s ^> 24s */
+ &&conv_123x_B210, /* 24s ^> 32h */
+ &&conv_123x_012B, /* 24s ^> 32s */
+ &&conv_1234_xxx1, /* 32h -> 8h */
+ &&conv_1234_xxx1, /* 32h -> 8s */
+ &&conv_1234_xx12, /* 32h -> 16h */
+ &&conv_1234_xx21, /* 32h -> 16s */
+ &&conv_1234_x123, /* 32h -> 24h */
+ &&conv_1234_321x, /* 32h -> 24s */
+ &&conv_1234_1234, /* 32h -> 32h */
+ &&conv_1234_4321, /* 32h -> 32s */
+ &&conv_1234_xxx9, /* 32h ^> 8h */
+ &&conv_1234_xxx9, /* 32h ^> 8s */
+ &&conv_1234_xx92, /* 32h ^> 16h */
+ &&conv_1234_xx29, /* 32h ^> 16s */
+ &&conv_1234_x923, /* 32h ^> 24h */
+ &&conv_1234_329x, /* 32h ^> 24s */
+ &&conv_1234_9234, /* 32h ^> 32h */
+ &&conv_1234_4329, /* 32h ^> 32s */
+ &&conv_1234_xxx4, /* 32s -> 8h */
+ &&conv_1234_xxx4, /* 32s -> 8s */
+ &&conv_1234_xx43, /* 32s -> 16h */
+ &&conv_1234_xx34, /* 32s -> 16s */
+ &&conv_1234_x432, /* 32s -> 24h */
+ &&conv_1234_234x, /* 32s -> 24s */
+ &&conv_1234_4321, /* 32s -> 32h */
+ &&conv_1234_1234, /* 32s -> 32s */
+ &&conv_1234_xxxC, /* 32s ^> 8h */
+ &&conv_1234_xxxC, /* 32s ^> 8s */
+ &&conv_1234_xxC3, /* 32s ^> 16h */
+ &&conv_1234_xx3C, /* 32s ^> 16s */
+ &&conv_1234_xC32, /* 32s ^> 24h */
+ &&conv_1234_23Cx, /* 32s ^> 24s */
+ &&conv_1234_C321, /* 32s ^> 32h */
+ &&conv_1234_123C, /* 32s ^> 32s */
+};
+#endif
+
+#ifdef CONV_END
+while(0) {
+conv_xxx1_xxx1: as_u8(dst) = as_u8(src); goto CONV_END;
+conv_xxx1_xx10: as_u16(dst) = (u_int16_t)as_u8(src) << 8; goto CONV_END;
+conv_xxx1_xx01: as_u16(dst) = (u_int16_t)as_u8(src); goto CONV_END;
+conv_xxx1_x100: as_u32(dst) = (u_int32_t)as_u8(src) << 16; goto CONV_END;
+conv_xxx1_001x: as_u32(dst) = (u_int32_t)as_u8(src) << 8; goto CONV_END;
+conv_xxx1_1000: as_u32(dst) = (u_int32_t)as_u8(src) << 24; goto CONV_END;
+conv_xxx1_0001: as_u32(dst) = (u_int32_t)as_u8(src); goto CONV_END;
+conv_xxx1_xxx9: as_u8(dst) = as_u8(src) ^ 0x80; goto CONV_END;
+conv_xxx1_xx90: as_u16(dst) = (u_int16_t)(as_u8(src) ^ 0x80) << 8; goto CONV_END;
+conv_xxx1_xx09: as_u16(dst) = (u_int16_t)(as_u8(src) ^ 0x80); goto CONV_END;
+conv_xxx1_x900: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 16; goto CONV_END;
+conv_xxx1_009x: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 8; goto CONV_END;
+conv_xxx1_9000: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 24; goto CONV_END;
+conv_xxx1_0009: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80); goto CONV_END;
+conv_xx12_xxx1: as_u8(dst) = as_u16(src) >> 8; goto CONV_END;
+conv_xx12_xx12: as_u16(dst) = as_u16(src); goto CONV_END;
+conv_xx12_xx21: as_u16(dst) = swab16(as_u16(src)); goto CONV_END;
+conv_xx12_x120: as_u32(dst) = (u_int32_t)as_u16(src) << 8; goto CONV_END;
+conv_xx12_021x: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 8; goto CONV_END;
+conv_xx12_1200: as_u32(dst) = (u_int32_t)as_u16(src) << 16; goto CONV_END;
+conv_xx12_0021: as_u32(dst) = (u_int32_t)swab16(as_u16(src)); goto CONV_END;
+conv_xx12_xxx9: as_u8(dst) = (as_u16(src) >> 8) ^ 0x80; goto CONV_END;
+conv_xx12_xx92: as_u16(dst) = as_u16(src) ^ 0x8000; goto CONV_END;
+conv_xx12_xx29: as_u16(dst) = swab16(as_u16(src)) ^ 0x80; goto CONV_END;
+conv_xx12_x920: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x8000) << 8; goto CONV_END;
+conv_xx12_029x: as_u32(dst) = (u_int32_t)(swab16(as_u16(src)) ^ 0x80) << 8; goto CONV_END;
+conv_xx12_9200: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x8000) << 16; goto CONV_END;
+conv_xx12_0029: as_u32(dst) = (u_int32_t)(swab16(as_u16(src)) ^ 0x80); goto CONV_END;
+conv_xx12_xxx2: as_u8(dst) = as_u16(src) & 0xff; goto CONV_END;
+conv_xx12_x210: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 8; goto CONV_END;
+conv_xx12_012x: as_u32(dst) = (u_int32_t)as_u16(src) << 8; goto CONV_END;
+conv_xx12_2100: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 16; goto CONV_END;
+conv_xx12_0012: as_u32(dst) = (u_int32_t)as_u16(src); goto CONV_END;
+conv_xx12_xxxA: as_u8(dst) = (as_u16(src) ^ 0x80) & 0xff; goto CONV_END;
+conv_xx12_xxA1: as_u16(dst) = swab16(as_u16(src) ^ 0x80); goto CONV_END;
+conv_xx12_xx1A: as_u16(dst) = as_u16(src) ^ 0x80; goto CONV_END;
+conv_xx12_xA10: as_u32(dst) = (u_int32_t)swab16(as_u16(src) ^ 0x80) << 8; goto CONV_END;
+conv_xx12_01Ax: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x80) << 8; goto CONV_END;
+conv_xx12_A100: as_u32(dst) = (u_int32_t)swab16(as_u16(src) ^ 0x80) << 16; goto CONV_END;
+conv_xx12_001A: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x80); goto CONV_END;
+conv_x123_xxx1: as_u8(dst) = as_u32(src) >> 16; goto CONV_END;
+conv_x123_xx12: as_u16(dst) = as_u32(src) >> 8; goto CONV_END;
+conv_x123_xx21: as_u16(dst) = swab16(as_u32(src) >> 8); goto CONV_END;
+conv_x123_x123: as_u32(dst) = as_u32(src); goto CONV_END;
+conv_x123_321x: as_u32(dst) = swab32(as_u32(src)); goto CONV_END;
+conv_x123_1230: as_u32(dst) = as_u32(src) << 8; goto CONV_END;
+conv_x123_0321: as_u32(dst) = swab32(as_u32(src)) >> 8; goto CONV_END;
+conv_x123_xxx9: as_u8(dst) = (as_u32(src) >> 16) ^ 0x80; goto CONV_END;
+conv_x123_xx92: as_u16(dst) = (as_u32(src) >> 8) ^ 0x8000; goto CONV_END;
+conv_x123_xx29: as_u16(dst) = swab16(as_u32(src) >> 8) ^ 0x80; goto CONV_END;
+conv_x123_x923: as_u32(dst) = as_u32(src) ^ 0x800000; goto CONV_END;
+conv_x123_329x: as_u32(dst) = swab32(as_u32(src)) ^ 0x8000; goto CONV_END;
+conv_x123_9230: as_u32(dst) = (as_u32(src) ^ 0x800000) << 8; goto CONV_END;
+conv_x123_0329: as_u32(dst) = (swab32(as_u32(src)) >> 8) ^ 0x80; goto CONV_END;
+conv_123x_xxx3: as_u8(dst) = (as_u32(src) >> 8) & 0xff; goto CONV_END;
+conv_123x_xx32: as_u16(dst) = swab16(as_u32(src) >> 8); goto CONV_END;
+conv_123x_xx23: as_u16(dst) = (as_u32(src) >> 8) & 0xffff; goto CONV_END;
+conv_123x_x321: as_u32(dst) = swab32(as_u32(src)); goto CONV_END;
+conv_123x_123x: as_u32(dst) = as_u32(src); goto CONV_END;
+conv_123x_3210: as_u32(dst) = swab32(as_u32(src)) << 8; goto CONV_END;
+conv_123x_0123: as_u32(dst) = as_u32(src) >> 8; goto CONV_END;
+conv_123x_xxxB: as_u8(dst) = ((as_u32(src) >> 8) & 0xff) ^ 0x80; goto CONV_END;
+conv_123x_xxB2: as_u16(dst) = swab16((as_u32(src) >> 8) ^ 0x80); goto CONV_END;
+conv_123x_xx2B: as_u16(dst) = ((as_u32(src) >> 8) & 0xffff) ^ 0x80; goto CONV_END;
+conv_123x_xB21: as_u32(dst) = swab32(as_u32(src)) ^ 0x800000; goto CONV_END;
+conv_123x_12Bx: as_u32(dst) = as_u32(src) ^ 0x8000; goto CONV_END;
+conv_123x_B210: as_u32(dst) = swab32(as_u32(src) ^ 0x8000) << 8; goto CONV_END;
+conv_123x_012B: as_u32(dst) = (as_u32(src) >> 8) ^ 0x80; goto CONV_END;
+conv_1234_xxx1: as_u8(dst) = as_u32(src) >> 24; goto CONV_END;
+conv_1234_xx12: as_u16(dst) = as_u32(src) >> 16; goto CONV_END;
+conv_1234_xx21: as_u16(dst) = swab16(as_u32(src) >> 16); goto CONV_END;
+conv_1234_x123: as_u32(dst) = as_u32(src) >> 8; goto CONV_END;
+conv_1234_321x: as_u32(dst) = swab32(as_u32(src)) << 8; goto CONV_END;
+conv_1234_1234: as_u32(dst) = as_u32(src); goto CONV_END;
+conv_1234_4321: as_u32(dst) = swab32(as_u32(src)); goto CONV_END;
+conv_1234_xxx9: as_u8(dst) = (as_u32(src) >> 24) ^ 0x80; goto CONV_END;
+conv_1234_xx92: as_u16(dst) = (as_u32(src) >> 16) ^ 0x8000; goto CONV_END;
+conv_1234_xx29: as_u16(dst) = swab16(as_u32(src) >> 16) ^ 0x80; goto CONV_END;
+conv_1234_x923: as_u32(dst) = (as_u32(src) >> 8) ^ 0x800000; goto CONV_END;
+conv_1234_329x: as_u32(dst) = (swab32(as_u32(src)) ^ 0x80) << 8; goto CONV_END;
+conv_1234_9234: as_u32(dst) = as_u32(src) ^ 0x80000000; goto CONV_END;
+conv_1234_4329: as_u32(dst) = swab32(as_u32(src)) ^ 0x80; goto CONV_END;
+conv_1234_xxx4: as_u8(dst) = as_u32(src) & 0xff; goto CONV_END;
+conv_1234_xx43: as_u16(dst) = swab16(as_u32(src)); goto CONV_END;
+conv_1234_xx34: as_u16(dst) = as_u32(src) & 0xffff; goto CONV_END;
+conv_1234_x432: as_u32(dst) = swab32(as_u32(src)) >> 8; goto CONV_END;
+conv_1234_234x: as_u32(dst) = as_u32(src) << 8; goto CONV_END;
+conv_1234_xxxC: as_u8(dst) = (as_u32(src) & 0xff) ^ 0x80; goto CONV_END;
+conv_1234_xxC3: as_u16(dst) = swab16(as_u32(src) ^ 0x80); goto CONV_END;
+conv_1234_xx3C: as_u16(dst) = (as_u32(src) & 0xffff) ^ 0x80; goto CONV_END;
+conv_1234_xC32: as_u32(dst) = (swab32(as_u32(src)) >> 8) ^ 0x800000; goto CONV_END;
+conv_1234_23Cx: as_u32(dst) = (as_u32(src) ^ 0x80) << 8; goto CONV_END;
+conv_1234_C321: as_u32(dst) = swab32(as_u32(src) ^ 0x80); goto CONV_END;
+conv_1234_123C: as_u32(dst) = as_u32(src) ^ 0x80; goto CONV_END;
+}
+#endif
+
+#ifdef GET_S16_LABELS
+/* src_wid src_endswap unsigned */
+static void *get_s16_labels[4 * 2 * 2] = {
+ &&get_s16_xxx1_xx10, /* 8h -> 16h */
+ &&get_s16_xxx1_xx90, /* 8h ^> 16h */
+ &&get_s16_xxx1_xx10, /* 8s -> 16h */
+ &&get_s16_xxx1_xx90, /* 8s ^> 16h */
+ &&get_s16_xx12_xx12, /* 16h -> 16h */
+ &&get_s16_xx12_xx92, /* 16h ^> 16h */
+ &&get_s16_xx12_xx21, /* 16s -> 16h */
+ &&get_s16_xx12_xxA1, /* 16s ^> 16h */
+ &&get_s16_x123_xx12, /* 24h -> 16h */
+ &&get_s16_x123_xx92, /* 24h ^> 16h */
+ &&get_s16_123x_xx32, /* 24s -> 16h */
+ &&get_s16_123x_xxB2, /* 24s ^> 16h */
+ &&get_s16_1234_xx12, /* 32h -> 16h */
+ &&get_s16_1234_xx92, /* 32h ^> 16h */
+ &&get_s16_1234_xx43, /* 32s -> 16h */
+ &&get_s16_1234_xxC3, /* 32s ^> 16h */
+};
+#endif
+
+#ifdef GET_S16_END
+while(0) {
+get_s16_xxx1_xx10: sample = (u_int16_t)as_u8(src) << 8; goto GET_S16_END;
+get_s16_xxx1_xx90: sample = (u_int16_t)(as_u8(src) ^ 0x80) << 8; goto GET_S16_END;
+get_s16_xx12_xx12: sample = as_u16(src); goto GET_S16_END;
+get_s16_xx12_xx92: sample = as_u16(src) ^ 0x8000; goto GET_S16_END;
+get_s16_xx12_xx21: sample = swab16(as_u16(src)); goto GET_S16_END;
+get_s16_xx12_xxA1: sample = swab16(as_u16(src) ^ 0x80); goto GET_S16_END;
+get_s16_x123_xx12: sample = as_u32(src) >> 8; goto GET_S16_END;
+get_s16_x123_xx92: sample = (as_u32(src) >> 8) ^ 0x8000; goto GET_S16_END;
+get_s16_123x_xx32: sample = swab16(as_u32(src) >> 8); goto GET_S16_END;
+get_s16_123x_xxB2: sample = swab16((as_u32(src) >> 8) ^ 0x8000); goto GET_S16_END;
+get_s16_1234_xx12: sample = as_u32(src) >> 16; goto GET_S16_END;
+get_s16_1234_xx92: sample = (as_u32(src) >> 16) ^ 0x8000; goto GET_S16_END;
+get_s16_1234_xx43: sample = swab16(as_u32(src)); goto GET_S16_END;
+get_s16_1234_xxC3: sample = swab16(as_u32(src) ^ 0x80); goto GET_S16_END;
+}
+#endif
+
+#ifdef PUT_S16_LABELS
+/* dst_wid dst_endswap unsigned */
+static void *put_s16_labels[4 * 2 * 2] = {
+ &&put_s16_xx12_xxx1, /* 16h -> 8h */
+ &&put_s16_xx12_xxx9, /* 16h ^> 8h */
+ &&put_s16_xx12_xxx1, /* 16h -> 8s */
+ &&put_s16_xx12_xxx9, /* 16h ^> 8s */
+ &&put_s16_xx12_xx12, /* 16h -> 16h */
+ &&put_s16_xx12_xx92, /* 16h ^> 16h */
+ &&put_s16_xx12_xx21, /* 16h -> 16s */
+ &&put_s16_xx12_xx29, /* 16h ^> 16s */
+ &&put_s16_xx12_x120, /* 16h -> 24h */
+ &&put_s16_xx12_x920, /* 16h ^> 24h */
+ &&put_s16_xx12_021x, /* 16h -> 24s */
+ &&put_s16_xx12_029x, /* 16h ^> 24s */
+ &&put_s16_xx12_1200, /* 16h -> 32h */
+ &&put_s16_xx12_9200, /* 16h ^> 32h */
+ &&put_s16_xx12_0021, /* 16h -> 32s */
+ &&put_s16_xx12_0029, /* 16h ^> 32s */
+};
+#endif
+
+#ifdef PUT_S16_END
+while (0) {
+put_s16_xx12_xxx1: as_u8(dst) = sample >> 8; goto PUT_S16_END;
+put_s16_xx12_xxx9: as_u8(dst) = (sample >> 8) ^ 0x80; goto PUT_S16_END;
+put_s16_xx12_xx12: as_u16(dst) = sample; goto PUT_S16_END;
+put_s16_xx12_xx92: as_u16(dst) = sample ^ 0x8000; goto PUT_S16_END;
+put_s16_xx12_xx21: as_u16(dst) = swab16(sample); goto PUT_S16_END;
+put_s16_xx12_xx29: as_u16(dst) = swab16(sample) ^ 0x80; goto PUT_S16_END;
+put_s16_xx12_x120: as_u32(dst) = (u_int32_t)sample << 8; goto PUT_S16_END;
+put_s16_xx12_x920: as_u32(dst) = (u_int32_t)(sample ^ 0x8000) << 8; goto PUT_S16_END;
+put_s16_xx12_021x: as_u32(dst) = (u_int32_t)swab16(sample) << 8; goto PUT_S16_END;
+put_s16_xx12_029x: as_u32(dst) = (u_int32_t)(swab16(sample) ^ 0x80) << 8; goto PUT_S16_END;
+put_s16_xx12_1200: as_u32(dst) = (u_int32_t)sample << 16; goto PUT_S16_END;
+put_s16_xx12_9200: as_u32(dst) = (u_int32_t)(sample ^ 0x8000) << 16; goto PUT_S16_END;
+put_s16_xx12_0021: as_u32(dst) = (u_int32_t)swab16(sample); goto PUT_S16_END;
+put_s16_xx12_0029: as_u32(dst) = (u_int32_t)swab16(sample) ^ 0x80; goto PUT_S16_END;
+}
+#endif
+
+#if 0
+#ifdef GET32_LABELS
+/* src_wid src_endswap unsigned */
+static void *get32_labels[4 * 2 * 2] = {
+ &&get32_xxx1_1000, /* 8h -> 32h */
+ &&get32_xxx1_9000, /* 8h ^> 32h */
+ &&get32_xxx1_1000, /* 8s -> 32h */
+ &&get32_xxx1_9000, /* 8s ^> 32h */
+ &&get32_xx12_1200, /* 16h -> 32h */
+ &&get32_xx12_9200, /* 16h ^> 32h */
+ &&get32_xx12_2100, /* 16s -> 32h */
+ &&get32_xx12_A100, /* 16s ^> 32h */
+ &&get32_x123_1230, /* 24h -> 32h */
+ &&get32_x123_9230, /* 24h ^> 32h */
+ &&get32_123x_3210, /* 24s -> 32h */
+ &&get32_123x_B210, /* 24s ^> 32h */
+ &&get32_1234_1234, /* 32h -> 32h */
+ &&get32_1234_9234, /* 32h ^> 32h */
+ &&get32_1234_4321, /* 32s -> 32h */
+ &&get32_1234_C321, /* 32s ^> 32h */
+};
+#endif
+
+#ifdef GET32_END
+while (0) {
+get32_xxx1_1000: sample = (u_int32_t)as_u8(src) << 24; goto GET32_END;
+get32_xxx1_9000: sample = (u_int32_t)(as_u8(src) ^ 0x80) << 24; goto GET32_END;
+get32_xx12_1200: sample = (u_int32_t)as_u16(src) << 16; goto GET32_END;
+get32_xx12_9200: sample = (u_int32_t)(as_u16(src) ^ 0x8000) << 16; goto GET32_END;
+get32_xx12_2100: sample = (u_int32_t)swab16(as_u16(src)) << 16; goto GET32_END;
+get32_xx12_A100: sample = (u_int32_t)swab16(as_u16(src) ^ 0x80) << 16; goto GET32_END;
+get32_x123_1230: sample = as_u32(src) << 8; goto GET32_END;
+get32_x123_9230: sample = (as_u32(src) << 8) ^ 0x80000000; goto GET32_END;
+get32_123x_3210: sample = swab32(as_u32(src) >> 8); goto GET32_END;
+get32_123x_B210: sample = swab32((as_u32(src) >> 8) ^ 0x80); goto GET32_END;
+get32_1234_1234: sample = as_u32(src); goto GET32_END;
+get32_1234_9234: sample = as_u32(src) ^ 0x80000000; goto GET32_END;
+get32_1234_4321: sample = swab32(as_u32(src)); goto GET32_END;
+get32_1234_C321: sample = swab32(as_u32(src) ^ 0x80); goto GET32_END;
+}
+#endif
+#endif
+
+#ifdef PUT_U32_LABELS
+/* dst_wid dst_endswap unsigned */
+static void *put_u32_labels[4 * 2 * 2] = {
+ &&put_u32_1234_xxx9, /* u32h -> s8h */
+ &&put_u32_1234_xxx1, /* u32h -> u8h */
+ &&put_u32_1234_xxx9, /* u32h -> s8s */
+ &&put_u32_1234_xxx1, /* u32h -> u8s */
+ &&put_u32_1234_xx92, /* u32h -> s16h */
+ &&put_u32_1234_xx12, /* u32h -> u16h */
+ &&put_u32_1234_xx29, /* u32h -> s16s */
+ &&put_u32_1234_xx21, /* u32h -> u16s */
+ &&put_u32_1234_x923, /* u32h -> s24h */
+ &&put_u32_1234_x123, /* u32h -> u24h */
+ &&put_u32_1234_329x, /* u32h -> s24s */
+ &&put_u32_1234_321x, /* u32h -> u24s */
+ &&put_u32_1234_9234, /* u32h -> s32h */
+ &&put_u32_1234_1234, /* u32h -> u32h */
+ &&put_u32_1234_4329, /* u32h -> s32s */
+ &&put_u32_1234_4321, /* u32h -> u32s */
+};
+#endif
+
+#ifdef PUT_U32_END
+while (0) {
+put_u32_1234_xxx1: as_u8(dst) = sample >> 24; goto PUT_U32_END;
+put_u32_1234_xxx9: as_u8(dst) = (sample >> 24) ^ 0x80; goto PUT_U32_END;
+put_u32_1234_xx12: as_u16(dst) = sample >> 16; goto PUT_U32_END;
+put_u32_1234_xx92: as_u16(dst) = (sample >> 16) ^ 0x8000; goto PUT_U32_END;
+put_u32_1234_xx21: as_u16(dst) = swab16(sample >> 16); goto PUT_U32_END;
+put_u32_1234_xx29: as_u16(dst) = swab16(sample >> 16) ^ 0x80; goto PUT_U32_END;
+put_u32_1234_x123: as_u32(dst) = sample >> 8; goto PUT_U32_END;
+put_u32_1234_x923: as_u32(dst) = (sample >> 8) ^ 0x800000; goto PUT_U32_END;
+put_u32_1234_321x: as_u32(dst) = swab32(sample) << 8; goto PUT_U32_END;
+put_u32_1234_329x: as_u32(dst) = (swab32(sample) ^ 0x80) << 8; goto PUT_U32_END;
+put_u32_1234_1234: as_u32(dst) = sample; goto PUT_U32_END;
+put_u32_1234_9234: as_u32(dst) = sample ^ 0x80000000; goto PUT_U32_END;
+put_u32_1234_4321: as_u32(dst) = swab32(sample); goto PUT_U32_END;
+put_u32_1234_4329: as_u32(dst) = swab32(sample) ^ 0x80; goto PUT_U32_END;
+}
+#endif
+
+#ifdef GET_U_LABELS
+/* width endswap unsigned*/
+static void *get_u_labels[4 * 2 * 2] = {
+ &&get_u_s8, /* s8 -> u8 */
+ &&get_u_u8, /* u8 -> u8 */
+ &&get_u_s8, /* s8 -> u8 */
+ &&get_u_u8, /* u8 -> u8 */
+ &&get_u_s16h, /* s16h -> u16h */
+ &&get_u_u16h, /* u16h -> u16h */
+ &&get_u_s16s, /* s16s -> u16h */
+ &&get_u_u16s, /* u16s -> u16h */
+ &&get_u_s24h, /* s24h -> u32h */
+ &&get_u_u24h, /* u24h -> u32h */
+ &&get_u_s24s, /* s24s -> u32h */
+ &&get_u_u24s, /* u24s -> u32h */
+ &&get_u_s32h, /* s32h -> u32h */
+ &&get_u_u32h, /* u32h -> u32h */
+ &&get_u_s32s, /* s32s -> u32h */
+ &&get_u_u32s, /* u32s -> u32h */
+};
+#endif
+
+#ifdef GET_U_END
+while (0) {
+get_u_s8: sample = as_u8(src) ^ 0x80; goto GET_U_END;
+get_u_u8: sample = as_u8(src); goto GET_U_END;
+get_u_s16h: sample = as_u16(src) ^ 0x8000; goto GET_U_END;
+get_u_u16h: sample = as_u16(src); goto GET_U_END;
+get_u_s16s: sample = swab16(as_u16(src) ^ 0x80); goto GET_U_END;
+get_u_u16s: sample = swab16(as_u16(src)); goto GET_U_END;
+get_u_s24h: sample = (as_u32(src) ^ 0x800000); goto GET_U_END;
+get_u_u24h: sample = as_u32(src); goto GET_U_END;
+get_u_s24s: sample = swab32(as_u32(src) ^ 0x800000); goto GET_U_END;
+get_u_u24s: sample = swab32(as_u32(src)); goto GET_U_END;
+get_u_s32h: sample = as_u32(src) ^ 0x80000000; goto GET_U_END;
+get_u_u32h: sample = as_u32(src); goto GET_U_END;
+get_u_s32s: sample = swab32(as_u32(src) ^ 0x80); goto GET_U_END;
+get_u_u32s: sample = swab32(as_u32(src)); goto GET_U_END;
+}
+#endif
+
+#if 0
+#ifdef PUT_LABELS
+/* width endswap unsigned */
+static void *put_labels[4 * 2 * 2] = {
+ &&put_s8, /* s8 -> s8 */
+ &&put_u8, /* u8 -> s8 */
+ &&put_s8, /* s8 -> s8 */
+ &&put_u8, /* u8 -> s8 */
+ &&put_s16h, /* s16h -> s16h */
+ &&put_u16h, /* u16h -> s16h */
+ &&put_s16s, /* s16s -> s16h */
+ &&put_u16s, /* u16s -> s16h */
+ &&put_s24h, /* s24h -> s32h */
+ &&put_u24h, /* u24h -> s32h */
+ &&put_s24s, /* s24s -> s32h */
+ &&put_u24s, /* u24s -> s32h */
+ &&put_s32h, /* s32h -> s32h */
+ &&put_u32h, /* u32h -> s32h */
+ &&put_s32s, /* s32s -> s32h */
+ &&put_u32s, /* u32s -> s32h */
+};
+#endif
+
+#ifdef PUT_END
+put_s8: as_s8(dst) = sample; goto PUT_END;
+put_u8: as_u8(dst) = sample ^ 0x80; goto PUT_END;
+put_s16h: as_s16(dst) = sample; goto PUT_END;
+put_u16h: as_u16(dst) = sample ^ 0x8000; goto PUT_END;
+put_s16s: as_s16(dst) = swab16(sample); goto PUT_END;
+put_u16s: as_u16(dst) = swab16(sample ^ 0x80); goto PUT_END;
+put_s24h: as_s24(dst) = sample & 0xffffff; goto PUT_END;
+put_u24h: as_u24(dst) = sample ^ 0x80000000; goto PUT_END;
+put_s24s: as_s24(dst) = swab32(sample & 0xffffff); goto PUT_END;
+put_u24s: as_u24(dst) = swab32(sample ^ 0x80); goto PUT_END;
+put_s32h: as_s32(dst) = sample; goto PUT_END;
+put_u32h: as_u32(dst) = sample ^ 0x80000000; goto PUT_END;
+put_s32s: as_s32(dst) = swab32(sample); goto PUT_END;
+put_u32s: as_u32(dst) = swab32(sample ^ 0x80); goto PUT_END;
+#endif
+#endif
+
+#undef as_u8
+#undef as_u16
+#undef as_u32
+#undef as_s8
+#undef as_s16
+#undef as_s32
diff --git a/sound/core/oss/rate.c b/sound/core/oss/rate.c
new file mode 100644
index 00000000000..1096ec18671
--- /dev/null
+++ b/sound/core/oss/rate.c
@@ -0,0 +1,378 @@
+/*
+ * Rate conversion Plug-In
+ * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include "pcm_plugin.h"
+
+#define SHIFT 11
+#define BITS (1<<SHIFT)
+#define R_MASK (BITS-1)
+
+/*
+ * Basic rate conversion plugin
+ */
+
+typedef struct {
+ signed short last_S1;
+ signed short last_S2;
+} rate_channel_t;
+
+typedef void (*rate_f)(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels,
+ snd_pcm_plugin_channel_t *dst_channels,
+ int src_frames, int dst_frames);
+
+typedef struct rate_private_data {
+ unsigned int pitch;
+ unsigned int pos;
+ rate_f func;
+ int get, put;
+ snd_pcm_sframes_t old_src_frames, old_dst_frames;
+ rate_channel_t channels[0];
+} rate_t;
+
+static void rate_init(snd_pcm_plugin_t *plugin)
+{
+ unsigned int channel;
+ rate_t *data = (rate_t *)plugin->extra_data;
+ data->pos = 0;
+ for (channel = 0; channel < plugin->src_format.channels; channel++) {
+ data->channels[channel].last_S1 = 0;
+ data->channels[channel].last_S2 = 0;
+ }
+}
+
+static void resample_expand(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels,
+ snd_pcm_plugin_channel_t *dst_channels,
+ int src_frames, int dst_frames)
+{
+ unsigned int pos = 0;
+ signed int val;
+ signed short S1, S2;
+ char *src, *dst;
+ unsigned int channel;
+ int src_step, dst_step;
+ int src_frames1, dst_frames1;
+ rate_t *data = (rate_t *)plugin->extra_data;
+ rate_channel_t *rchannels = data->channels;
+
+#define GET_S16_LABELS
+#define PUT_S16_LABELS
+#include "plugin_ops.h"
+#undef GET_S16_LABELS
+#undef PUT_S16_LABELS
+ void *get = get_s16_labels[data->get];
+ void *put = put_s16_labels[data->put];
+ signed short sample = 0;
+
+ for (channel = 0; channel < plugin->src_format.channels; channel++) {
+ pos = data->pos;
+ S1 = rchannels->last_S1;
+ S2 = rchannels->last_S2;
+ if (!src_channels[channel].enabled) {
+ if (dst_channels[channel].wanted)
+ snd_pcm_area_silence(&dst_channels[channel].area, 0, dst_frames, plugin->dst_format.format);
+ dst_channels[channel].enabled = 0;
+ continue;
+ }
+ dst_channels[channel].enabled = 1;
+ src = (char *)src_channels[channel].area.addr + src_channels[channel].area.first / 8;
+ dst = (char *)dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
+ src_step = src_channels[channel].area.step / 8;
+ dst_step = dst_channels[channel].area.step / 8;
+ src_frames1 = src_frames;
+ dst_frames1 = dst_frames;
+ while (dst_frames1-- > 0) {
+ if (pos & ~R_MASK) {
+ pos &= R_MASK;
+ S1 = S2;
+ if (src_frames1-- > 0) {
+ goto *get;
+#define GET_S16_END after_get
+#include "plugin_ops.h"
+#undef GET_S16_END
+ after_get:
+ S2 = sample;
+ src += src_step;
+ }
+ }
+ val = S1 + ((S2 - S1) * (signed int)pos) / BITS;
+ if (val < -32768)
+ val = -32768;
+ else if (val > 32767)
+ val = 32767;
+ sample = val;
+ goto *put;
+#define PUT_S16_END after_put
+#include "plugin_ops.h"
+#undef PUT_S16_END
+ after_put:
+ dst += dst_step;
+ pos += data->pitch;
+ }
+ rchannels->last_S1 = S1;
+ rchannels->last_S2 = S2;
+ rchannels++;
+ }
+ data->pos = pos;
+}
+
+static void resample_shrink(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels,
+ snd_pcm_plugin_channel_t *dst_channels,
+ int src_frames, int dst_frames)
+{
+ unsigned int pos = 0;
+ signed int val;
+ signed short S1, S2;
+ char *src, *dst;
+ unsigned int channel;
+ int src_step, dst_step;
+ int src_frames1, dst_frames1;
+ rate_t *data = (rate_t *)plugin->extra_data;
+ rate_channel_t *rchannels = data->channels;
+
+#define GET_S16_LABELS
+#define PUT_S16_LABELS
+#include "plugin_ops.h"
+#undef GET_S16_LABELS
+#undef PUT_S16_LABELS
+ void *get = get_s16_labels[data->get];
+ void *put = put_s16_labels[data->put];
+ signed short sample = 0;
+
+ for (channel = 0; channel < plugin->src_format.channels; ++channel) {
+ pos = data->pos;
+ S1 = rchannels->last_S1;
+ S2 = rchannels->last_S2;
+ if (!src_channels[channel].enabled) {
+ if (dst_channels[channel].wanted)
+ snd_pcm_area_silence(&dst_channels[channel].area, 0, dst_frames, plugin->dst_format.format);
+ dst_channels[channel].enabled = 0;
+ continue;
+ }
+ dst_channels[channel].enabled = 1;
+ src = (char *)src_channels[channel].area.addr + src_channels[channel].area.first / 8;
+ dst = (char *)dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
+ src_step = src_channels[channel].area.step / 8;
+ dst_step = dst_channels[channel].area.step / 8;
+ src_frames1 = src_frames;
+ dst_frames1 = dst_frames;
+ while (dst_frames1 > 0) {
+ S1 = S2;
+ if (src_frames1-- > 0) {
+ goto *get;
+#define GET_S16_END after_get
+#include "plugin_ops.h"
+#undef GET_S16_END
+ after_get:
+ S2 = sample;
+ src += src_step;
+ }
+ if (pos & ~R_MASK) {
+ pos &= R_MASK;
+ val = S1 + ((S2 - S1) * (signed int)pos) / BITS;
+ if (val < -32768)
+ val = -32768;
+ else if (val > 32767)
+ val = 32767;
+ sample = val;
+ goto *put;
+#define PUT_S16_END after_put
+#include "plugin_ops.h"
+#undef PUT_S16_END
+ after_put:
+ dst += dst_step;
+ dst_frames1--;
+ }
+ pos += data->pitch;
+ }
+ rchannels->last_S1 = S1;
+ rchannels->last_S2 = S2;
+ rchannels++;
+ }
+ data->pos = pos;
+}
+
+static snd_pcm_sframes_t rate_src_frames(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t frames)
+{
+ rate_t *data;
+ snd_pcm_sframes_t res;
+
+ snd_assert(plugin != NULL, return -ENXIO);
+ if (frames == 0)
+ return 0;
+ data = (rate_t *)plugin->extra_data;
+ if (plugin->src_format.rate < plugin->dst_format.rate) {
+ res = (((frames * data->pitch) + (BITS/2)) >> SHIFT);
+ } else {
+ res = (((frames << SHIFT) + (data->pitch / 2)) / data->pitch);
+ }
+ if (data->old_src_frames > 0) {
+ snd_pcm_sframes_t frames1 = frames, res1 = data->old_dst_frames;
+ while (data->old_src_frames < frames1) {
+ frames1 >>= 1;
+ res1 <<= 1;
+ }
+ while (data->old_src_frames > frames1) {
+ frames1 <<= 1;
+ res1 >>= 1;
+ }
+ if (data->old_src_frames == frames1)
+ return res1;
+ }
+ data->old_src_frames = frames;
+ data->old_dst_frames = res;
+ return res;
+}
+
+static snd_pcm_sframes_t rate_dst_frames(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t frames)
+{
+ rate_t *data;
+ snd_pcm_sframes_t res;
+
+ snd_assert(plugin != NULL, return -ENXIO);
+ if (frames == 0)
+ return 0;
+ data = (rate_t *)plugin->extra_data;
+ if (plugin->src_format.rate < plugin->dst_format.rate) {
+ res = (((frames << SHIFT) + (data->pitch / 2)) / data->pitch);
+ } else {
+ res = (((frames * data->pitch) + (BITS/2)) >> SHIFT);
+ }
+ if (data->old_dst_frames > 0) {
+ snd_pcm_sframes_t frames1 = frames, res1 = data->old_src_frames;
+ while (data->old_dst_frames < frames1) {
+ frames1 >>= 1;
+ res1 <<= 1;
+ }
+ while (data->old_dst_frames > frames1) {
+ frames1 <<= 1;
+ res1 >>= 1;
+ }
+ if (data->old_dst_frames == frames1)
+ return res1;
+ }
+ data->old_dst_frames = frames;
+ data->old_src_frames = res;
+ return res;
+}
+
+static snd_pcm_sframes_t rate_transfer(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_channel_t *src_channels,
+ snd_pcm_plugin_channel_t *dst_channels,
+ snd_pcm_uframes_t frames)
+{
+ snd_pcm_uframes_t dst_frames;
+ rate_t *data;
+
+ snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
+ if (frames == 0)
+ return 0;
+#ifdef CONFIG_SND_DEBUG
+ {
+ unsigned int channel;
+ for (channel = 0; channel < plugin->src_format.channels; channel++) {
+ snd_assert(src_channels[channel].area.first % 8 == 0 &&
+ src_channels[channel].area.step % 8 == 0,
+ return -ENXIO);
+ snd_assert(dst_channels[channel].area.first % 8 == 0 &&
+ dst_channels[channel].area.step % 8 == 0,
+ return -ENXIO);
+ }
+ }
+#endif
+
+ dst_frames = rate_dst_frames(plugin, frames);
+ if (dst_frames > dst_channels[0].frames)
+ dst_frames = dst_channels[0].frames;
+ data = (rate_t *)plugin->extra_data;
+ data->func(plugin, src_channels, dst_channels, frames, dst_frames);
+ return dst_frames;
+}
+
+static int rate_action(snd_pcm_plugin_t *plugin,
+ snd_pcm_plugin_action_t action,
+ unsigned long udata ATTRIBUTE_UNUSED)
+{
+ snd_assert(plugin != NULL, return -ENXIO);
+ switch (action) {
+ case INIT:
+ case PREPARE:
+ rate_init(plugin);
+ break;
+ default:
+ break;
+ }
+ return 0; /* silenty ignore other actions */
+}
+
+int snd_pcm_plugin_build_rate(snd_pcm_plug_t *plug,
+ snd_pcm_plugin_format_t *src_format,
+ snd_pcm_plugin_format_t *dst_format,
+ snd_pcm_plugin_t **r_plugin)
+{
+ int err;
+ rate_t *data;
+ snd_pcm_plugin_t *plugin;
+
+ snd_assert(r_plugin != NULL, return -ENXIO);
+ *r_plugin = NULL;
+
+ snd_assert(src_format->channels == dst_format->channels, return -ENXIO);
+ snd_assert(src_format->channels > 0, return -ENXIO);
+ snd_assert(snd_pcm_format_linear(src_format->format) != 0, return -ENXIO);
+ snd_assert(snd_pcm_format_linear(dst_format->format) != 0, return -ENXIO);
+ snd_assert(src_format->rate != dst_format->rate, return -ENXIO);
+
+ err = snd_pcm_plugin_build(plug, "rate conversion",
+ src_format, dst_format,
+ sizeof(rate_t) + src_format->channels * sizeof(rate_channel_t),
+ &plugin);
+ if (err < 0)
+ return err;
+ data = (rate_t *)plugin->extra_data;
+ data->get = getput_index(src_format->format);
+ snd_assert(data->get >= 0 && data->get < 4*2*2, return -EINVAL);
+ data->put = getput_index(dst_format->format);
+ snd_assert(data->put >= 0 && data->put < 4*2*2, return -EINVAL);
+
+ if (src_format->rate < dst_format->rate) {
+ data->pitch = ((src_format->rate << SHIFT) + (dst_format->rate >> 1)) / dst_format->rate;
+ data->func = resample_expand;
+ } else {
+ data->pitch = ((dst_format->rate << SHIFT) + (src_format->rate >> 1)) / src_format->rate;
+ data->func = resample_shrink;
+ }
+ data->pos = 0;
+ rate_init(plugin);
+ data->old_src_frames = data->old_dst_frames = 0;
+ plugin->transfer = rate_transfer;
+ plugin->src_frames = rate_src_frames;
+ plugin->dst_frames = rate_dst_frames;
+ plugin->action = rate_action;
+ *r_plugin = plugin;
+ return 0;
+}
diff --git a/sound/core/oss/route.c b/sound/core/oss/route.c
new file mode 100644
index 00000000000..c955b7dfdb3
--- /dev/null
+++ b/sound/core/oss/route.c
@@ -0,0 +1,519 @@
+/*
+ * Attenuated route Plug-In
+ * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
+ *
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public Lic