📄 i810_audio.c
字号:
set_current_state(TASK_RUNNING); remove_wait_queue(&dmabuf->wait, &waita); return ret;}/* No kernel lock - we have our own spinlock */static unsigned int i810_poll(struct file *file, struct poll_table_struct *wait){ struct i810_state *state = (struct i810_state *)file->private_data; struct dmabuf *dmabuf = &state->dmabuf; unsigned long flags; unsigned int mask = 0; if(!dmabuf->ready) return 0; poll_wait(file, &dmabuf->wait, wait); spin_lock_irqsave(&state->card->lock, flags); if (dmabuf->enable & ADC_RUNNING || dmabuf->trigger & PCM_ENABLE_INPUT) { if (i810_get_available_read_data(state) >= (signed)dmabuf->userfragsize) mask |= POLLIN | POLLRDNORM; } if (dmabuf->enable & DAC_RUNNING || dmabuf->trigger & PCM_ENABLE_OUTPUT) { if (i810_get_free_write_space(state) >= (signed)dmabuf->userfragsize) mask |= POLLOUT | POLLWRNORM; } spin_unlock_irqrestore(&state->card->lock, flags); return mask;}static int i810_mmap(struct file *file, struct vm_area_struct *vma){ struct i810_state *state = (struct i810_state *)file->private_data; struct dmabuf *dmabuf = &state->dmabuf; int ret = -EINVAL; unsigned long size; lock_kernel(); if (vma->vm_flags & VM_WRITE) { if (!dmabuf->write_channel && (dmabuf->write_channel = state->card->alloc_pcm_channel(state->card)) == NULL) { ret = -EBUSY; goto out; } } if (vma->vm_flags & VM_READ) { if (!dmabuf->read_channel && (dmabuf->read_channel = state->card->alloc_rec_pcm_channel(state->card)) == NULL) { ret = -EBUSY; goto out; } } if ((ret = prog_dmabuf(state, 0)) != 0) goto out; ret = -EINVAL; if (vma->vm_pgoff != 0) goto out; size = vma->vm_end - vma->vm_start; if (size > (PAGE_SIZE << dmabuf->buforder)) goto out; ret = -EAGAIN; if (remap_pfn_range(vma, vma->vm_start, virt_to_phys(dmabuf->rawbuf) >> PAGE_SHIFT, size, vma->vm_page_prot)) goto out; dmabuf->mapped = 1; dmabuf->trigger = 0; ret = 0;#ifdef DEBUG_MMAP printk("i810_audio: mmap'ed %ld bytes of data space\n", size);#endifout: unlock_kernel(); return ret;}static int i810_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ struct i810_state *state = (struct i810_state *)file->private_data; struct i810_channel *c = NULL; struct dmabuf *dmabuf = &state->dmabuf; unsigned long flags; audio_buf_info abinfo; count_info cinfo; unsigned int i_glob_cnt; int val = 0, ret; struct ac97_codec *codec = state->card->ac97_codec[0]; void __user *argp = (void __user *)arg; int __user *p = argp;#ifdef DEBUG printk("i810_audio: i810_ioctl, arg=0x%x, cmd=", arg ? *p : 0);#endif switch (cmd) { case OSS_GETVERSION:#ifdef DEBUG printk("OSS_GETVERSION\n");#endif return put_user(SOUND_VERSION, p); case SNDCTL_DSP_RESET:#ifdef DEBUG printk("SNDCTL_DSP_RESET\n");#endif spin_lock_irqsave(&state->card->lock, flags); if (dmabuf->enable == DAC_RUNNING) { c = dmabuf->write_channel; __stop_dac(state); } if (dmabuf->enable == ADC_RUNNING) { c = dmabuf->read_channel; __stop_adc(state); } if (c != NULL) { I810_IOWRITEB(2, state->card, c->port+OFF_CR); /* reset DMA machine */ while ( I810_IOREADB(state->card, c->port+OFF_CR) & 2 ) cpu_relax(); I810_IOWRITEL((u32)state->card->chandma + c->num*sizeof(struct i810_channel), state->card, c->port+OFF_BDBAR); CIV_TO_LVI(state->card, c->port, 0); } spin_unlock_irqrestore(&state->card->lock, flags); synchronize_irq(state->card->pci_dev->irq); dmabuf->ready = 0; dmabuf->swptr = dmabuf->hwptr = 0; dmabuf->count = dmabuf->total_bytes = 0; return 0; case SNDCTL_DSP_SYNC:#ifdef DEBUG printk("SNDCTL_DSP_SYNC\n");#endif if (dmabuf->enable != DAC_RUNNING || file->f_flags & O_NONBLOCK) return 0; if((val = drain_dac(state, 1))) return val; dmabuf->total_bytes = 0; return 0; case SNDCTL_DSP_SPEED: /* set smaple rate */#ifdef DEBUG printk("SNDCTL_DSP_SPEED\n");#endif if (get_user(val, p)) return -EFAULT; if (val >= 0) { if (file->f_mode & FMODE_WRITE) { if ( (state->card->ac97_status & SPDIF_ON) ) { /* S/PDIF Enabled */ /* AD1886 only supports 48000, need to check that */ if ( i810_valid_spdif_rate ( codec, val ) ) { /* Set DAC rate */ i810_set_spdif_output ( state, -1, 0 ); stop_dac(state); dmabuf->ready = 0; spin_lock_irqsave(&state->card->lock, flags); i810_set_dac_rate(state, val); spin_unlock_irqrestore(&state->card->lock, flags); /* Set S/PDIF transmitter rate. */ i810_set_spdif_output ( state, AC97_EA_SPSA_3_4, val ); if ( ! (state->card->ac97_status & SPDIF_ON) ) { val = dmabuf->rate; } } else { /* Not a valid rate for S/PDIF, ignore it */ val = dmabuf->rate; } } else { stop_dac(state); dmabuf->ready = 0; spin_lock_irqsave(&state->card->lock, flags); i810_set_dac_rate(state, val); spin_unlock_irqrestore(&state->card->lock, flags); } } if (file->f_mode & FMODE_READ) { stop_adc(state); dmabuf->ready = 0; spin_lock_irqsave(&state->card->lock, flags); i810_set_adc_rate(state, val); spin_unlock_irqrestore(&state->card->lock, flags); } } return put_user(dmabuf->rate, p); case SNDCTL_DSP_STEREO: /* set stereo or mono channel */#ifdef DEBUG printk("SNDCTL_DSP_STEREO\n");#endif if (dmabuf->enable & DAC_RUNNING) { stop_dac(state); } if (dmabuf->enable & ADC_RUNNING) { stop_adc(state); } return put_user(1, p); case SNDCTL_DSP_GETBLKSIZE: if (file->f_mode & FMODE_WRITE) { if (!dmabuf->ready && (val = prog_dmabuf(state, 0))) return val; } if (file->f_mode & FMODE_READ) { if (!dmabuf->ready && (val = prog_dmabuf(state, 1))) return val; }#ifdef DEBUG printk("SNDCTL_DSP_GETBLKSIZE %d\n", dmabuf->userfragsize);#endif return put_user(dmabuf->userfragsize, p); case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/#ifdef DEBUG printk("SNDCTL_DSP_GETFMTS\n");#endif return put_user(AFMT_S16_LE, p); case SNDCTL_DSP_SETFMT: /* Select sample format */#ifdef DEBUG printk("SNDCTL_DSP_SETFMT\n");#endif return put_user(AFMT_S16_LE, p); case SNDCTL_DSP_CHANNELS:#ifdef DEBUG printk("SNDCTL_DSP_CHANNELS\n");#endif if (get_user(val, p)) return -EFAULT; if (val > 0) { if (dmabuf->enable & DAC_RUNNING) { stop_dac(state); } if (dmabuf->enable & ADC_RUNNING) { stop_adc(state); } } else { return put_user(state->card->channels, p); } /* ICH and ICH0 only support 2 channels */ if ( state->card->pci_id == PCI_DEVICE_ID_INTEL_82801AA_5 || state->card->pci_id == PCI_DEVICE_ID_INTEL_82801AB_5) return put_user(2, p); /* Multi-channel support was added with ICH2. Bits in */ /* Global Status and Global Control register are now */ /* used to indicate this. */ i_glob_cnt = I810_IOREADL(state->card, GLOB_CNT); /* Current # of channels enabled */ if ( i_glob_cnt & 0x0100000 ) ret = 4; else if ( i_glob_cnt & 0x0200000 ) ret = 6; else ret = 2; switch ( val ) { case 2: /* 2 channels is always supported */ I810_IOWRITEL(i_glob_cnt & 0xffcfffff, state->card, GLOB_CNT); /* Do we need to change mixer settings???? */ break; case 4: /* Supported on some chipsets, better check first */ if ( state->card->channels >= 4 ) { I810_IOWRITEL((i_glob_cnt & 0xffcfffff) | 0x100000, state->card, GLOB_CNT); /* Do we need to change mixer settings??? */ } else { val = ret; } break; case 6: /* Supported on some chipsets, better check first */ if ( state->card->channels >= 6 ) { I810_IOWRITEL((i_glob_cnt & 0xffcfffff) | 0x200000, state->card, GLOB_CNT); /* Do we need to change mixer settings??? */ } else { val = ret; } break; default: /* nothing else is ever supported by the chipset */ val = ret; break; } return put_user(val, p); case SNDCTL_DSP_POST: /* the user has sent all data and is notifying us */ /* we update the swptr to the end of the last sg segment then return */#ifdef DEBUG printk("SNDCTL_DSP_POST\n");#endif if(!dmabuf->ready || (dmabuf->enable != DAC_RUNNING)) return 0; if((dmabuf->swptr % dmabuf->fragsize) != 0) { val = dmabuf->fragsize - (dmabuf->swptr % dmabuf->fragsize); dmabuf->swptr += val; dmabuf->count += val; } return 0; case SNDCTL_DSP_SUBDIVIDE: if (dmabuf->subdivision) return -EINVAL; if (get_user(val, p)) return -EFAULT; if (val != 1 && val != 2 && val != 4) return -EINVAL;#ifdef DEBUG printk("SNDCTL_DSP_SUBDIVIDE %d\n", val);#endif dmabuf->subdivision = val; dmabuf->ready = 0; return 0; case SNDCTL_DSP_SETFRAGMENT: if (get_user(val, p)) return -EFAULT; dmabuf->ossfragsize = 1<<(val & 0xffff); dmabuf->ossmaxfrags = (val >> 16) & 0xffff; if (!dmabuf->ossfragsize || !dmabuf->ossmaxfrags) return -EINVAL; /* * Bound the frag size into our allowed range of 256 - 4096 */ if (dmabuf->ossfragsize < 256) dmabuf->ossfragsize = 256; else if (dmabuf->ossfragsize > 4096) dmabuf->ossfragsize = 4096; /* * The numfrags could be something reasonable, or it could * be 0xffff meaning "Give me as much as possible". So, * we check the numfrags * fragsize doesn't exceed our * 64k buffer limit, nor is it less than our 8k minimum. * If it fails either one of these checks, then adjust the * number of fragments, not the size of them. It's OK if * our number of fragments doesn't equal 32 or anything * like our hardware based number now since we are using * a different frag count for the hardware. Before we get * into this though, bound the maxfrags to avoid overflow * issues. A reasonable bound would be 64k / 256 since our * maximum buffer size is 64k and our minimum frag size is * 256. On the other end, our minimum buffer size is 8k and * our maximum frag size is 4k, so the lower bound should * be 2. */ if(dmabuf->ossmaxfrags > 256) dmabuf->ossmaxfrags = 256; else if (dmabuf->ossmaxfrags < 2) dmabuf->ossmaxfrags = 2; val = dmabuf->ossfragsize * dmabuf->ossmaxfrags; while (val < 8192) { val <<= 1; dmabuf->ossmaxfrags <<= 1; } while (val > 65536) { val >>= 1; dmabuf->ossmaxfrags >>= 1; } dmabuf->ready = 0;#ifdef DEBUG printk("SNDCTL_DSP_SETFRAGMENT 0x%x, %d, %d\n", val, dmabuf->ossfragsize, dmabuf->ossmaxfrags);#endif 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); i810_update_ptr(state); abinfo.fragsize = dmabuf->userfragsize; abinfo.fragstotal = dmabuf->userfrags; if (dmabuf->mapped) abinfo.bytes = dmabuf->dmasize; else abinfo.bytes = i810_get_free_write_space(state); abinfo.fragments = abinfo.bytes / dmabuf->userfragsize; spin_unlock_irqrestore(&state->card->lock, flags);#if defined(DEBUG) || defined(DEBUG_MMAP) printk("SNDCTL_DSP_GETOSPACE %d, %d, %d, %d\n", abinfo.bytes, abinfo.fragsize, abinfo.fragments, abinfo.fragstotal);#endif return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; 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); val = i810_get_free_write_space(state); cinfo.bytes = dmabuf->total_bytes; cinfo.ptr = dmabuf->hwptr; cinfo.blocks = val/dmabuf->userfragsize; if (dmabuf->mapped && (dmabuf->trigger & PCM_ENABLE_OUTPUT)) { dmabuf->count += val; dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize; __i810_update_lvi(state, 0); } spin_unlock_irqrestore(&state->card->lock, flags);#if defined(DEBUG) || defined(DEBUG_MMAP) printk("SNDCTL_DSP_GETOPTR %d, %d, %d, %d\n", cinfo.bytes, cinfo.blocks, cinfo.ptr, dmabuf->count);#endif return copy_to_user(argp, &cinfo, sizeof(cinfo)) ? -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); abinfo.bytes = i810_get_available_read_data(state); abinfo.fragsize = dmabuf->userfragsize; abinfo.fragstotal = dmabuf->userfrags; abinfo.fragments = abinfo.bytes / dmabuf->userfragsize; spin_unlock_irqrestore(&state->card->lock, flags);#if defined(DEBUG) || defined(DEBUG_MMAP) printk("SNDCTL_DSP_GETISPACE %d, %d, %d, %d\n", abinfo.bytes, abinfo.fragsize, abinfo.fragments, abinfo.fragstotal);#endif return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; case SNDCTL_DSP_GETIPTR: if (!(file->f_mode & FMODE_READ)) return -EINVAL; if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) return val; spin_lock_irqsave(&state->card->lock, flags); val = i810_get_available_read_data(state); cinfo.bytes = dmabuf->total_bytes; cinfo.blocks = val/dmabuf->userfragsize; cinfo.ptr
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -