📄 trident.c
字号:
if (val == AFMT_S16_LE) dmabuf->fmt |= TRIDENT_FMT_16BIT; else dmabuf->fmt &= ~TRIDENT_FMT_16BIT; } if (file->f_mode & FMODE_READ) { stop_adc(state); dmabuf->ready = 0; if (val == AFMT_S16_LE) dmabuf->fmt |= TRIDENT_FMT_16BIT; else dmabuf->fmt &= ~TRIDENT_FMT_16BIT; } } unlock_set_fmt(state); return put_user((dmabuf->fmt & TRIDENT_FMT_16BIT) ? AFMT_S16_LE : AFMT_U8, (int *)arg); case SNDCTL_DSP_CHANNELS: if (get_user(val, (int *)arg)) return -EFAULT; if (val != 0) { lock_set_fmt(state); if (file->f_mode & FMODE_WRITE) { stop_dac(state); dmabuf->ready = 0; //prevent from memory leak if ((state->chans_num > 2) && (state->chans_num != val)) { ali_free_other_states_resources(state); state->chans_num = 1; } if (val >= 2) { dmabuf->fmt |= TRIDENT_FMT_STEREO; if ((val == 6) && (state->card->pci_id == PCI_DEVICE_ID_ALI_5451)) { ali_setup_multi_channels(state->card, 6); down(&state->card->open_sem); ret = ali_allocate_other_states_resources(state, 6); up(&state->card->open_sem); if (ret < 0) { unlock_set_fmt(state); return ret; } } else val = 2; /*yield to 2-channels*/ } else dmabuf->fmt &= ~TRIDENT_FMT_STEREO; state->chans_num = val; } if (file->f_mode & FMODE_READ) { stop_adc(state); dmabuf->ready = 0; if (val >= 2) { if (!((file->f_mode & FMODE_WRITE) && (val == 6))) val = 2; dmabuf->fmt |= TRIDENT_FMT_STEREO; } else dmabuf->fmt &= ~TRIDENT_FMT_STEREO; state->chans_num = val; } unlock_set_fmt(state); } return put_user(val, (int *)arg); case SNDCTL_DSP_POST: /* FIXME: the same as RESET ?? */ return 0; case SNDCTL_DSP_SUBDIVIDE: if (dmabuf->subdivision) return -EINVAL; if (get_user(val, (int *)arg)) return -EFAULT; if (val != 1 && val != 2 && val != 4) return -EINVAL; dmabuf->subdivision = val; return 0; case SNDCTL_DSP_SETFRAGMENT: if (get_user(val, (int *)arg)) return -EFAULT; dmabuf->ossfragshift = val & 0xffff; dmabuf->ossmaxfrags = (val >> 16) & 0xffff; if (dmabuf->ossfragshift < 4) dmabuf->ossfragshift = 4; if (dmabuf->ossfragshift > 15) dmabuf->ossfragshift = 15; if (dmabuf->ossmaxfrags < 4) dmabuf->ossmaxfrags = 4; return 0; case SNDCTL_DSP_GETOSPACE: if (!(file->f_mode & FMODE_WRITE)) return -EINVAL; if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) return val; spin_lock_irqsave(&state->card->lock, flags); trident_update_ptr(state); abinfo.fragsize = dmabuf->fragsize; abinfo.bytes = dmabuf->dmasize - dmabuf->count; abinfo.fragstotal = dmabuf->numfrag; abinfo.fragments = abinfo.bytes >> dmabuf->fragshift; spin_unlock_irqrestore(&state->card->lock, flags); return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; case SNDCTL_DSP_GETISPACE: if (!(file->f_mode & FMODE_READ)) return -EINVAL; if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0) return val; spin_lock_irqsave(&state->card->lock, flags); trident_update_ptr(state); abinfo.fragsize = dmabuf->fragsize; abinfo.bytes = dmabuf->count; abinfo.fragstotal = dmabuf->numfrag; abinfo.fragments = abinfo.bytes >> dmabuf->fragshift; spin_unlock_irqrestore(&state->card->lock, flags); return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; case SNDCTL_DSP_NONBLOCK: file->f_flags |= O_NONBLOCK; return 0; case SNDCTL_DSP_GETCAPS: return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP|DSP_CAP_BIND, (int *)arg); case SNDCTL_DSP_GETTRIGGER: val = 0; if (file->f_mode & FMODE_READ && dmabuf->enable) val |= PCM_ENABLE_INPUT; if (file->f_mode & FMODE_WRITE && dmabuf->enable) val |= PCM_ENABLE_OUTPUT; return put_user(val, (int *)arg); case SNDCTL_DSP_SETTRIGGER: if (get_user(val, (int *)arg)) return -EFAULT; if (file->f_mode & FMODE_READ) { if (val & PCM_ENABLE_INPUT) { if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) return ret; start_adc(state); } else stop_adc(state); } if (file->f_mode & FMODE_WRITE) { if (val & PCM_ENABLE_OUTPUT) { if (!dmabuf->ready && (ret = prog_dmabuf(state, 0))) return ret; start_dac(state); } else stop_dac(state); } return 0; case SNDCTL_DSP_GETIPTR: if (!(file->f_mode & FMODE_READ)) return -EINVAL; if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0) return val; spin_lock_irqsave(&state->card->lock, flags); trident_update_ptr(state); cinfo.bytes = dmabuf->total_bytes; cinfo.blocks = dmabuf->count >> dmabuf->fragshift; cinfo.ptr = dmabuf->hwptr; if (dmabuf->mapped) dmabuf->count &= dmabuf->fragsize-1; spin_unlock_irqrestore(&state->card->lock, flags); return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); case SNDCTL_DSP_GETOPTR: if (!(file->f_mode & FMODE_WRITE)) return -EINVAL; if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) return val; spin_lock_irqsave(&state->card->lock, flags); trident_update_ptr(state); cinfo.bytes = dmabuf->total_bytes; cinfo.blocks = dmabuf->count >> dmabuf->fragshift; cinfo.ptr = dmabuf->hwptr; if (dmabuf->mapped) dmabuf->count &= dmabuf->fragsize-1; spin_unlock_irqrestore(&state->card->lock, flags); return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); case SNDCTL_DSP_SETDUPLEX: return -EINVAL; case SNDCTL_DSP_GETODELAY: if (!(file->f_mode & FMODE_WRITE)) return -EINVAL; if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) return val; spin_lock_irqsave(&state->card->lock, flags); trident_update_ptr(state); val = dmabuf->count; spin_unlock_irqrestore(&state->card->lock, flags); return put_user(val, (int *)arg); case SOUND_PCM_READ_RATE: return put_user(dmabuf->rate, (int *)arg); case SOUND_PCM_READ_CHANNELS: return put_user((dmabuf->fmt & TRIDENT_FMT_STEREO) ? 2 : 1, (int *)arg); case SOUND_PCM_READ_BITS: return put_user((dmabuf->fmt & TRIDENT_FMT_16BIT) ? AFMT_S16_LE : AFMT_U8, (int *)arg); case SNDCTL_DSP_GETCHANNELMASK: return put_user(DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE, (int *)arg); case SNDCTL_DSP_BIND_CHANNEL: if (state->card->pci_id != PCI_DEVICE_ID_SI_7018) return -EINVAL; if (get_user(val, (int *)arg)) return -EFAULT; if (val == DSP_BIND_QUERY) { val = dmabuf->channel->attribute | 0x3c00; val = attr2mask[val >> 8]; } else { dmabuf->ready = 0; if (file->f_mode & FMODE_READ) dmabuf->channel->attribute = (CHANNEL_REC|SRC_ENABLE); if (file->f_mode & FMODE_WRITE) dmabuf->channel->attribute = (CHANNEL_SPC_PB|SRC_ENABLE); dmabuf->channel->attribute |= mask2attr[ffs(val)]; } return put_user(val, (int *)arg); case SNDCTL_DSP_MAPINBUF: case SNDCTL_DSP_MAPOUTBUF: case SNDCTL_DSP_SETSYNCRO: case SOUND_PCM_WRITE_FILTER: case SOUND_PCM_READ_FILTER: return -EINVAL; } return -EINVAL;}static int trident_open(struct inode *inode, struct file *file){ int i = 0; int minor = MINOR(inode->i_rdev); struct trident_card *card = devs; struct trident_state *state = NULL; struct dmabuf *dmabuf = NULL; /* find an avaiable virtual channel (instance of /dev/dsp) */ while (card != NULL) { down(&card->open_sem); for (i = 0; i < NR_HW_CH; i++) { if (card->states[i] == NULL) { state = card->states[i] = (struct trident_state *) kmalloc(sizeof(struct trident_state), GFP_KERNEL); if (state == NULL) { return -ENOMEM; } memset(state, 0, sizeof(struct trident_state)); dmabuf = &state->dmabuf; goto found_virt; } } up(&card->open_sem); card = card->next; } /* no more virtual channel avaiable */ if (!state) { return -ENODEV; } found_virt: /* found a free virtual channel, allocate hardware channels */ if(file->f_mode & FMODE_READ) dmabuf->channel = card->alloc_rec_pcm_channel(card); else dmabuf->channel = card->alloc_pcm_channel(card); if (dmabuf->channel == NULL) { kfree (card->states[i]); card->states[i] = NULL; return -ENODEV; } /* initialize the virtual channel */ state->virt = i; state->card = card; state->magic = TRIDENT_STATE_MAGIC; init_waitqueue_head(&dmabuf->wait); file->private_data = state; /* set default sample format. According to OSS Programmer's Guide /dev/dsp should be default to unsigned 8-bits, mono, with sample rate 8kHz and /dev/dspW will accept 16-bits sample */ if (file->f_mode & FMODE_WRITE) { dmabuf->fmt &= ~TRIDENT_FMT_MASK; if ((minor & 0x0f) == SND_DEV_DSP16) dmabuf->fmt |= TRIDENT_FMT_16BIT; dmabuf->ossfragshift = 0; dmabuf->ossmaxfrags = 0; dmabuf->subdivision = 0; if (card->pci_id == PCI_DEVICE_ID_SI_7018) { /* set default channel attribute to normal playback */ dmabuf->channel->attribute = CHANNEL_PB; } trident_set_dac_rate(state, 8000); } if (file->f_mode & FMODE_READ) { /* FIXME: Trident 4d can only record in signed 16-bits stereo, 48kHz sample, to be dealed with in trident_set_adc_rate() ?? */ dmabuf->fmt &= ~TRIDENT_FMT_MASK; if ((minor & 0x0f) == SND_DEV_DSP16) dmabuf->fmt |= TRIDENT_FMT_16BIT; dmabuf->ossfragshift = 0; dmabuf->ossmaxfrags = 0; dmabuf->subdivision = 0; if (card->pci_id == PCI_DEVICE_ID_SI_7018) { /* set default channel attribute to 0x8a80, record from PCM L/R FIFO and mono = (left + right + 1)/2*/ dmabuf->channel->attribute = (CHANNEL_REC|PCM_LR|MONO_MIX); } trident_set_adc_rate(state, 8000); } state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); up(&card->open_sem);#ifdef DEBUG printk(KERN_ERR "trident: open virtual channel %d, hard channel %d\n", state->virt, dmabuf->channel->num);#endif return 0;}static int trident_release(struct inode *inode, struct file *file){ struct trident_state *state = (struct trident_state *)file->private_data; struct trident_card *card; struct dmabuf *dmabuf; unsigned long flags; lock_kernel(); card = state->card; dmabuf = &state->dmabuf; VALIDATE_STATE(state); if (file->f_mode & FMODE_WRITE) { trident_clear_tail(state); drain_dac(state, file->f_flags & O_NONBLOCK); } /* stop DMA state machine and free DMA buffers/channels */ down(&card->open_sem); if (file->f_mode & FMODE_WRITE) { stop_dac(state); lock_set_fmt(state); if (state->chans_num > 2) ali_free_other_states_resources(state); unlock_set_fmt(state); dealloc_dmabuf(state); state->card->free_pcm_channel(state->card, dmabuf->channel->num); } if (file->f_mode & FMODE_READ) { stop_adc(state); dealloc_dmabuf(state); state->card->free_pcm_channel(state->card, dmabuf->channel->num); } card->states[state->virt] = NULL; kfree(state); /* we're covered by the open_sem */ up(&card->open_sem); unlock_kernel(); return 0;}static /*const*/ struct file_operations trident_audio_fops = { owner: THIS_MODULE, llseek: trident_llseek, read: trident_read, write: trident_write, poll: trident_poll, ioctl: trident_ioctl, mmap: trident_mmap, open: trident_open, release: trident_release,};/* trident specific AC97 functions *//* Write AC97 codec registers */static void trident_ac97_set(struct ac97_codec *codec, u8 reg, u16 val){ struct trident_card *card = (struct trident_card *)codec->private_data; unsigned int address, mask, busy; unsigned short count = 0xffff; unsigned long flags; u32 data; data = ((u32) val) << 16; switch (card->pci_id) { default: case PCI_DEVICE_ID_SI_7018: address = SI_AC97_WRITE; mask = SI_AC97_BUSY_WRITE | SI_AC97_AUDIO_BUSY; if (codec->id) mask |= SI_AC97_SECONDARY; busy = SI_AC97_BUSY_WRITE; break; case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: address = DX_ACR0_AC97_W; mask = busy = DX_AC97_BUSY_WRITE; break; case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: address = NX_ACR1_AC97_W; mask = NX_AC97_BUSY_WRITE; if (codec->id) mask |= NX_AC97_WRITE_SECONDARY; busy = NX_AC97_BUSY_WRITE; break; } spin_lock_irqsave(&card->lock, flags); do { if ((inw(TRID_REG(card, address)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -