📄 i810_audio.c
字号:
return -EINVAL; spin_lock_irqsave(&state->card->lock, flags); i810_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; spin_lock_irqsave(&state->card->lock, flags); i810_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; spin_lock_irqsave(&state->card->lock, flags); i810_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 & I810_FMT_STEREO) ? 2 : 1, (int *)arg); case SOUND_PCM_READ_BITS: return put_user(AFMT_S16_LE, (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 i810_open(struct inode *inode, struct file *file){ int i = 0; struct i810_card *card = devs; struct i810_state *state = NULL; struct dmabuf *dmabuf = NULL; /* find an avaiable virtual channel (instance of /dev/dsp) */ while (card != NULL) { for (i = 0; i < NR_HW_CH; i++) { if (card->states[i] == NULL) { state = card->states[i] = (struct i810_state *) kmalloc(sizeof(struct i810_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; memset(state, 0, sizeof(struct i810_state)); dmabuf = &state->dmabuf; goto found_virt; } } 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 = I810_STATE_MAGIC; init_waitqueue_head(&dmabuf->wait); init_MUTEX(&state->open_sem); file->private_data = state; down(&state->open_sem); /* 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 &= ~I810_FMT_MASK; dmabuf->fmt |= I810_FMT_16BIT; dmabuf->ossfragshift = 0; dmabuf->ossmaxfrags = 0; dmabuf->subdivision = 0; i810_set_dac_rate(state, 48000); } if (file->f_mode & FMODE_READ) { dmabuf->fmt &= ~I810_FMT_MASK; dmabuf->fmt |= I810_FMT_16BIT; dmabuf->ossfragshift = 0; dmabuf->ossmaxfrags = 0; dmabuf->subdivision = 0; i810_set_adc_rate(state, 48000); } state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); up(&state->open_sem); return 0;}static int i810_release(struct inode *inode, struct file *file){ struct i810_state *state = (struct i810_state *)file->private_data; struct dmabuf *dmabuf = &state->dmabuf; lock_kernel(); if (file->f_mode & FMODE_WRITE) { i810_clear_tail(state); drain_dac(state, file->f_flags & O_NONBLOCK); } /* stop DMA state machine and free DMA buffers/channels */ down(&state->open_sem); if (file->f_mode & FMODE_WRITE) { stop_dac(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); } /* we're covered by the open_sem */ up(&state->open_sem); kfree(state->card->states[state->virt]); state->card->states[state->virt] = NULL; unlock_kernel(); return 0;}static /*const*/ struct file_operations i810_audio_fops = { owner: THIS_MODULE, llseek: i810_llseek, read: i810_read, write: i810_write, poll: i810_poll, ioctl: i810_ioctl, mmap: i810_mmap, open: i810_open, release: i810_release,};/* Write AC97 codec registers */static u16 i810_ac97_get(struct ac97_codec *dev, u8 reg){ struct i810_card *card = dev->private_data; int count = 100; while(count-- && (inb(card->iobase + CAS) & 1)) udelay(1); return inw(card->ac97base + (reg&0x7f));}static void i810_ac97_set(struct ac97_codec *dev, u8 reg, u16 data){ struct i810_card *card = dev->private_data; int count = 100; while(count-- && (inb(card->iobase + CAS) & 1)) udelay(1); outw(data, card->ac97base + (reg&0x7f));}/* OSS /dev/mixer file operation methods */static int i810_open_mixdev(struct inode *inode, struct file *file){ int i; int minor = MINOR(inode->i_rdev); struct i810_card *card = devs; for (card = devs; card != NULL; card = card->next) for (i = 0; i < NR_AC97; i++) if (card->ac97_codec[i] != NULL && card->ac97_codec[i]->dev_mixer == minor) goto match; if (!card) return -ENODEV; match: file->private_data = card->ac97_codec[i]; return 0;}static int i810_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ struct ac97_codec *codec = (struct ac97_codec *)file->private_data; return codec->mixer_ioctl(codec, cmd, arg);}static /*const*/ struct file_operations i810_mixer_fops = { owner: THIS_MODULE, llseek: i810_llseek, ioctl: i810_ioctl_mixdev, open: i810_open_mixdev,};/* AC97 codec initialisation. */static int __init i810_ac97_init(struct i810_card *card){ int num_ac97 = 0; int ready_2nd = 0; struct ac97_codec *codec; u16 eid; int i=0; u32 reg; reg = inl(card->iobase + GLOB_CNT); if((reg&2)==0) /* Cold required */ reg|=2; else reg|=4; /* Warm */ reg&=~8; /* ACLink on */ outl(reg , card->iobase + GLOB_CNT); while(i<10) { if((inl(card->iobase+GLOB_CNT)&4)==0) break; current->state = TASK_UNINTERRUPTIBLE; schedule_timeout(HZ/20); i++; } if(i==10) { printk(KERN_ERR "i810_audio: AC'97 reset failed.\n"); return 0; } current->state = TASK_UNINTERRUPTIBLE; schedule_timeout(HZ/5); inw(card->ac97base); for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL) return -ENOMEM; memset(codec, 0, sizeof(struct ac97_codec)); /* initialize some basic codec information, other fields will be filled in ac97_probe_codec */ codec->private_data = card; codec->id = num_ac97; codec->codec_read = i810_ac97_get; codec->codec_write = i810_ac97_set; if (ac97_probe_codec(codec) == 0) break; eid = i810_ac97_get(codec, AC97_EXTENDED_ID); if(eid==0xFFFFFF) { printk(KERN_WARNING "i810_audio: no codec attached ?\n"); kfree(codec); break; } card->ac97_features = eid; /* Now check the codec for useful features to make up for the dumbness of the 810 hardware engine */ if(!(eid&0x0001)) printk(KERN_WARNING "i810_audio: only 48Khz playback available.\n"); else { /* Enable variable rate mode */ i810_ac97_set(codec, AC97_EXTENDED_STATUS, 9); i810_ac97_set(codec,AC97_EXTENDED_STATUS, i810_ac97_get(codec, AC97_EXTENDED_STATUS)|0xE800); /* power up everything, modify this when implementing power saving */ i810_ac97_set(codec, AC97_POWER_CONTROL, i810_ac97_get(codec, AC97_POWER_CONTROL) & ~0x7f00); /* wait for analog ready */ for (i=10; i && ((i810_ac97_get(codec, AC97_POWER_CONTROL) & 0xf) != 0xf); i--) { current->state = TASK_UNINTERRUPTIBLE; schedule_timeout(HZ/20); } if(!(i810_ac97_get(codec, AC97_EXTENDED_STATUS)&1)) { printk(KERN_WARNING "i810_audio: Codec refused to allow VRA, using 48Khz only.\n"); card->ac97_features&=~1; } } if ((codec->dev_mixer = register_sound_mixer(&i810_mixer_fops, -1)) < 0) { printk(KERN_ERR "i810_audio: couldn't register mixer!\n"); kfree(codec); break; } card->ac97_codec[num_ac97] = codec; /* if there is no secondary codec at all, don't probe any more */ if (!ready_2nd) return num_ac97+1; } return num_ac97;}/* install the driver, we do not allocate hardware channel nor DMA buffer now, they are defered untill "ACCESS" time (in prog_dmabuf called by open/read/write/ioctl/mmap) */ static int __init i810_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id){ struct i810_card *card; if (!pci_dma_supported(pci_dev, I810_DMA_MASK)) { printk(KERN_ERR "intel810: architecture does not support" " 32bit PCI busmaster DMA\n"); return -ENODEV; } if (pci_enable_device(pci_dev)) return -EIO; if ((card = kmalloc(sizeof(struct i810_card), GFP_KERNEL)) == NULL) { printk(KERN_ERR "i810_audio: out of memory\n"); return -ENOMEM; } memset(card, 0, sizeof(*card)); card->iobase = pci_resource_start (pci_dev, 1); card->ac97base = pci_resource_start (pci_dev, 0); card->pci_dev = pci_dev; card->pci_id = pci_id->device; card->irq = pci_dev->irq; card->next = devs; card->magic = I810_CARD_MAGIC; spin_lock_init(&card->lock); devs = card; pci_set_master(pci_dev); printk(KERN_INFO "i810: %s found at IO 0x%04lx and 0x%04lx, IRQ %d\n", card_names[pci_id->driver_data], card->iobase, card->ac97base, card->irq); card->alloc_pcm_channel = i810_alloc_pcm_channel; card->alloc_rec_pcm_channel = i810_alloc_rec_pcm_channel; card->free_pcm_channel = i810_free_pcm_channel; /* claim our iospace and irq */ request_region(card->iobase, 64, card_names[pci_id->driver_data]); request_region(card->ac97base, 256, card_names[pci_id->driver_data]); if (request_irq(card->irq, &i810_interrupt, SA_SHIRQ, card_names[pci_id->driver_data], card)) { printk(KERN_ERR "i810_audio: unable to allocate irq %d\n", card->irq); release_region(card->iobase, 64); release_region(card->ac97base, 256); kfree(card); return -ENODEV; } /* register /dev/dsp */ if ((card->dev_audio = register_sound_dsp(&i810_audio_fops, -1)) < 0) { printk(KERN_ERR "i810_audio: couldn't register DSP device!\n"); release_region(card->iobase, 64); release_region(card->ac97base, 256); free_irq(card->irq, card); kfree(card); return -ENODEV; } /* initialize AC97 codec and register /dev/mixer */ if (i810_ac97_init(card) <= 0) { unregister_sound_dsp(card->dev_audio); release_region(card->iobase, 64); release_region(card->ac97base, 256); free_irq(card->irq, card); kfree(card); return -ENODEV; } pci_dev->driver_data = card; pci_dev->dma_mask = I810_DMA_MASK; return 0;}static void __exit i810_remove(struct pci_dev *pci_dev){ int i; struct i810_card *card = pci_dev->driver_data; /* free hardware resources */ free_irq(card->irq, devs); release_region(card->iobase, 64); release_region(card->ac97base, 256); /* unregister audio devices */ for (i = 0; i < NR_AC97; i++) if (devs->ac97_codec[i] != NULL) { unregister_sound_mixer(card->ac97_codec[i]->dev_mixer); kfree (card->ac97_codec[i]); } unregister_sound_dsp(card->dev_audio); kfree(card);}MODULE_AUTHOR("");MODULE_DESCRIPTION("Intel 810 audio support");MODULE_PARM(ftsodell, "i");MODULE_PARM(clocking, "i");#define I810_MODULE_NAME "intel810_audio"static struct pci_driver i810_pci_driver = { name: I810_MODULE_NAME, id_table: i810_pci_tbl, probe: i810_probe, remove: i810_remove,};static int __init i810_init_module (void){ if (!pci_present()) /* No PCI bus in this machine! */ return -ENODEV; if(ftsodell==1) clocking=41194; printk(KERN_INFO "Intel 810 + AC97 Audio, version " DRIVER_VERSION ", " __TIME__ " " __DATE__ "\n"); if (!pci_register_driver(&i810_pci_driver)) { pci_unregister_driver(&i810_pci_driver); return -ENODEV; } return 0;}static void __exit i810_cleanup_module (void){ pci_unregister_driver(&i810_pci_driver);}module_init(i810_init_module);module_exit(i810_cleanup_module);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -