📄 btaudio.c
字号:
ndst = nsrc >> bta->sampleshift; if ((bta->analog && 0 == bta->sampleshift) || (!bta->analog && 2 == bta->channels)) { /* just copy */ if (copy_to_user(buffer + ret, bta->buf_cpu + bta->read_offset, nsrc)) { if (0 == ret) ret = -EFAULT; break; } } else if (!bta->analog) { /* stereo => mono (digital audio) */ __s16 *src = (__s16*)(bta->buf_cpu + bta->read_offset); __s16 *dst = (__s16*)(buffer + ret); __s16 avg; int n = ndst>>1; if (0 != verify_area(VERIFY_WRITE,dst,ndst)) { if (0 == ret) ret = -EFAULT; break; } for (; n; n--, dst++) { avg = (__s16)le16_to_cpu(*src) / 2; src++; avg += (__s16)le16_to_cpu(*src) / 2; src++; __put_user(cpu_to_le16(avg),(__u16*)(dst)); } } else if (8 == bta->bits) { /* copy + byte downsampling (audio A/D) */ __u8 *src = bta->buf_cpu + bta->read_offset; __u8 *dst = buffer + ret; int n = ndst; if (0 != verify_area(VERIFY_WRITE,dst,ndst)) { if (0 == ret) ret = -EFAULT; break; } for (; n; n--, src += (1 << bta->sampleshift), dst++) __put_user(*src,(__u8*)(dst)); } else { /* copy + word downsampling (audio A/D) */ __u16 *src = (__u16*)(bta->buf_cpu + bta->read_offset); __u16 *dst = (__u16*)(buffer + ret); int n = ndst>>1; if (0 != verify_area(VERIFY_WRITE,dst,ndst)) { if (0 == ret) ret = -EFAULT; break; } for (; n; n--, src += (1 << bta->sampleshift), dst++) __put_user(*src,(__u16*)(dst)); } ret += ndst; swcount -= ndst; hwcount -= nsrc; bta->read_count -= nsrc; bta->read_offset += nsrc; if (bta->read_offset == bta->buf_size) bta->read_offset = 0; } up(&bta->lock); remove_wait_queue(&bta->readq, &wait); current->state = TASK_RUNNING; return ret;}static ssize_t btaudio_dsp_write(struct file *file, const char *buffer, size_t count, loff_t *ppos){ return -EINVAL;}static int btaudio_dsp_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ struct btaudio *bta = file->private_data; int s, i, ret, val = 0; switch (cmd) { case OSS_GETVERSION: return put_user(SOUND_VERSION, (int *)arg); case SNDCTL_DSP_GETCAPS: return 0; case SNDCTL_DSP_SPEED: if (get_user(val, (int*)arg)) return -EFAULT; if (bta->analog) { for (s = 0; s < 16; s++) if (val << s >= HWBASE_AD*4/15) break; for (i = 15; i >= 5; i--) if (val << s <= HWBASE_AD*4/i) break; bta->sampleshift = s; bta->decimation = i; if (debug) printk(KERN_DEBUG "btaudio: rate: req=%d " "dec=%d shift=%d hwrate=%d swrate=%d\n", val,i,s,(HWBASE_AD*4/i),(HWBASE_AD*4/i)>>s); } else { bta->sampleshift = (bta->channels == 2) ? 0 : 1; bta->decimation = 0; } if (bta->recording) { down(&bta->lock); stop_recording(bta); start_recording(bta); up(&bta->lock); } /* fall through */ case SOUND_PCM_READ_RATE: if (bta->analog) { return put_user(HWBASE_AD*4/bta->decimation>>bta->sampleshift, (int*)arg); } else { return put_user(rate, (int*)arg); } case SNDCTL_DSP_STEREO: if (!bta->analog) { if (get_user(val, (int*)arg)) return -EFAULT; bta->channels = (val > 0) ? 2 : 1; bta->sampleshift = (bta->channels == 2) ? 0 : 1; if (debug) printk(KERN_INFO "btaudio: stereo=%d channels=%d\n", val,bta->channels); } else { if (val == 1) return -EFAULT; else { bta->channels = 1; if (debug) printk(KERN_INFO "btaudio: stereo=0 channels=1\n"); } } return put_user((bta->channels)-1, (int *)arg); case SNDCTL_DSP_CHANNELS: if (!analog) { if (get_user(val, (int*)arg)) return -EFAULT; bta->channels = (val > 1) ? 2 : 1; bta->sampleshift = (bta->channels == 2) ? 0 : 1; if (debug) printk(KERN_DEBUG "btaudio: val=%d channels=%d\n", val,bta->channels); } /* fall through */ case SOUND_PCM_READ_CHANNELS: return put_user(bta->channels, (int *)arg); case SNDCTL_DSP_GETFMTS: /* Returns a mask */ if (analog) return put_user(AFMT_S16_LE|AFMT_S8, (int*)arg); else return put_user(AFMT_S16_LE, (int*)arg); case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ if (get_user(val, (int*)arg)) return -EFAULT; if (val != AFMT_QUERY) { if (analog) bta->bits = (val == AFMT_S8) ? 8 : 16; else bta->bits = 16; if (bta->recording) { down(&bta->lock); stop_recording(bta); start_recording(bta); up(&bta->lock); } } if (debug) printk(KERN_DEBUG "btaudio: fmt: bits=%d\n",bta->bits); return put_user((bta->bits==16) ? AFMT_S16_LE : AFMT_S8, (int*)arg); break; case SOUND_PCM_READ_BITS: return put_user(bta->bits, (int*)arg); case SNDCTL_DSP_NONBLOCK: file->f_flags |= O_NONBLOCK; return 0; case SNDCTL_DSP_RESET: if (bta->recording) { down(&bta->lock); stop_recording(bta); up(&bta->lock); } return 0; case SNDCTL_DSP_GETBLKSIZE: if (!bta->recording) { if (0 != (ret = alloc_buffer(bta))) return ret; if (0 != (ret = make_risc(bta))) return ret; } return put_user(bta->block_bytes>>bta->sampleshift,(int*)arg); case SNDCTL_DSP_SYNC: /* NOP */ return 0; case SNDCTL_DSP_GETISPACE: { audio_buf_info info; if (!bta->recording) return -EINVAL; info.fragsize = bta->block_bytes>>bta->sampleshift; info.fragstotal = bta->block_count; info.bytes = bta->read_count; info.fragments = info.bytes / info.fragsize; if (debug) printk(KERN_DEBUG "btaudio: SNDCTL_DSP_GETISPACE " "returns %d/%d/%d/%d\n", info.fragsize, info.fragstotal, info.bytes, info.fragments); if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; return 0; }#if 0 /* TODO */ case SNDCTL_DSP_GETTRIGGER: case SNDCTL_DSP_SETTRIGGER: case SNDCTL_DSP_SETFRAGMENT:#endif default: return -EINVAL; }}static unsigned int btaudio_dsp_poll(struct file *file, struct poll_table_struct *wait){ struct btaudio *bta = file->private_data; unsigned int mask = 0; poll_wait(file, &bta->readq, wait); if (0 == bta->read_count) mask |= (POLLIN | POLLRDNORM); return mask;}static struct file_operations btaudio_digital_dsp_fops = { owner: THIS_MODULE, llseek: no_llseek, open: btaudio_dsp_open_digital, release: btaudio_dsp_release, read: btaudio_dsp_read, write: btaudio_dsp_write, ioctl: btaudio_dsp_ioctl, poll: btaudio_dsp_poll,};static struct file_operations btaudio_analog_dsp_fops = { owner: THIS_MODULE, llseek: no_llseek, open: btaudio_dsp_open_analog, release: btaudio_dsp_release, read: btaudio_dsp_read, write: btaudio_dsp_write, ioctl: btaudio_dsp_ioctl, poll: btaudio_dsp_poll,};/* -------------------------------------------------------------- */static char *irq_name[] = { "", "", "", "OFLOW", "", "", "", "", "", "", "", "RISCI", "FBUS", "FTRGT", "FDSR", "PPERR", "RIPERR", "PABORT", "OCERR", "SCERR" };static void btaudio_irq(int irq, void *dev_id, struct pt_regs * regs){ int count = 0; u32 stat,astat; struct btaudio *bta = dev_id; for (;;) { count++; stat = btread(REG_INT_STAT); astat = stat & btread(REG_INT_MASK); if (!astat) return; btwrite(astat,REG_INT_STAT); if (irq_debug) { int i; printk(KERN_DEBUG "btaudio: irq loop=%d risc=%x, bits:", count, stat>>28); for (i = 0; i < (sizeof(irq_name)/sizeof(char*)); i++) { if (stat & (1 << i)) printk(" %s",irq_name[i]); if (astat & (1 << i)) printk("*"); } printk("\n"); } if (stat & IRQ_RISCI) { int blocks; blocks = (stat >> 28) - bta->dma_block; if (blocks < 0) blocks += bta->block_count; bta->dma_block = stat >> 28; if (bta->read_count + 2*bta->block_bytes > bta->buf_size) { stop_recording(bta); printk(KERN_INFO "btaudio: buffer overrun\n"); } if (blocks > 0) { bta->read_count += blocks * bta->block_bytes; wake_up_interruptible(&bta->readq); } } if (count > 10) { printk(KERN_WARNING "btaudio: Oops - irq mask cleared\n"); btwrite(0, REG_INT_MASK); } } return;}/* -------------------------------------------------------------- */static int __devinit btaudio_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id){ struct btaudio *bta; unsigned char revision,latency; int rc = -EBUSY; if (pci_enable_device(pci_dev)) return -EIO; if (!request_mem_region(pci_resource_start(pci_dev,0), pci_resource_len(pci_dev,0), "btaudio")) { return -EBUSY; } bta = kmalloc(sizeof(*bta),GFP_ATOMIC); if (!bta) { printk(KERN_WARNING "btaudio: not enough memory\n"); rc = -ENOMEM; goto fail1; } memset(bta,0,sizeof(*bta)); bta->pci = pci_dev; bta->irq = pci_dev->irq; bta->mem = pci_resource_start(pci_dev,0); bta->mmio = ioremap(pci_resource_start(pci_dev,0), pci_resource_len(pci_dev,0)); bta->source = 1; bta->bits = 8; bta->channels = 1; if (bta->analog) { bta->decimation = 15; } else { bta->decimation = 0; bta->sampleshift = 1; } init_MUTEX(&bta->lock); init_waitqueue_head(&bta->readq); pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &revision); pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &latency); printk(KERN_INFO "btaudio: Bt%x (rev %d) at %02x:%02x.%x, ", pci_dev->device,revision,pci_dev->bus->number, PCI_SLOT(pci_dev->devfn),PCI_FUNC(pci_dev->devfn)); printk("irq: %d, latency: %d, memory: 0x%lx\n", bta->irq, latency, bta->mem); /* init hw */ btwrite(0, REG_GPIO_DMA_CTL); btwrite(0, REG_INT_MASK); btwrite(~0x0UL, REG_INT_STAT); pci_set_master(pci_dev); if ((rc = request_irq(bta->irq, btaudio_irq, SA_SHIRQ|SA_INTERRUPT, "btaudio",(void *)bta)) < 0) { printk(KERN_WARNING "btaudio: can't request irq (rc=%d)\n",rc); goto fail1; } /* register devices */ if (digital) { rc = bta->dsp_digital = register_sound_dsp(&btaudio_digital_dsp_fops,dsp1); if (rc < 0) { printk(KERN_WARNING "btaudio: can't register digital dsp (rc=%d)\n",rc); goto fail2; } } if (analog) { rc = bta->dsp_analog = register_sound_dsp(&btaudio_analog_dsp_fops,dsp2); if (rc < 0) { printk(KERN_WARNING "btaudio: can't register analog dsp (rc=%d)\n",rc); goto fail3; } rc = bta->mixer_dev = register_sound_mixer(&btaudio_mixer_fops,mixer); if (rc < 0) { printk(KERN_WARNING "btaudio: can't register mixer (rc=%d)\n",rc); goto fail4; } } if (debug) printk(KERN_DEBUG "btaudio: minors: digital=%d, analog=%d, mixer=%d\n", bta->dsp_digital, bta->dsp_analog, bta->mixer_dev); /* hook into linked list */ bta->next = btaudios; btaudios = bta; pci_set_drvdata(pci_dev,bta); return 0; fail4: unregister_sound_dsp(bta->dsp_analog); fail3: if (digital) unregister_sound_dsp(bta->dsp_digital); fail2: free_irq(bta->irq,bta); fail1: release_mem_region(pci_resource_start(pci_dev,0), pci_resource_len(pci_dev,0)); kfree(bta); return rc;}static void __devexit btaudio_remove(struct pci_dev *pci_dev){ struct btaudio *bta = pci_get_drvdata(pci_dev); struct btaudio *walk; /* turn off all DMA / IRQs */ btand(~15, REG_GPIO_DMA_CTL); btwrite(0, REG_INT_MASK); btwrite(~0x0UL, REG_INT_STAT); /* unregister devices */ if (digital) { unregister_sound_dsp(bta->dsp_digital); } if (analog) { unregister_sound_dsp(bta->dsp_analog); unregister_sound_mixer(bta->mixer_dev); } /* free resources */ free_buffer(bta); free_irq(bta->irq,bta); release_mem_region(pci_resource_start(pci_dev,0), pci_resource_len(pci_dev,0)); /* remove from linked list */ if (bta == btaudios) { btaudios = NULL; } else { for (walk = btaudios; walk->next != bta; walk = walk->next) ; /* if (NULL == walk->next) BUG(); */ walk->next = bta->next; } pci_set_drvdata(pci_dev, NULL); kfree(bta); return;}/* -------------------------------------------------------------- */static struct pci_device_id btaudio_pci_tbl[] __devinitdata = { { PCI_VENDOR_ID_BROOKTREE, 0x0878, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { PCI_VENDOR_ID_BROOKTREE, 0x0879, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {0,}};static struct pci_driver btaudio_pci_driver = { name: "btaudio", id_table: btaudio_pci_tbl, probe: btaudio_probe, remove: __devexit_p(btaudio_remove),};int btaudio_init_module(void){ printk(KERN_INFO "btaudio: driver version 0.6 loaded [%s%s%s]\n", analog ? "analog" : "", analog && digital ? "+" : "", digital ? "digital" : ""); return pci_module_init(&btaudio_pci_driver);}void btaudio_cleanup_module(void){ pci_unregister_driver(&btaudio_pci_driver); return;}module_init(btaudio_init_module);module_exit(btaudio_cleanup_module);MODULE_PARM(dsp1,"i");MODULE_PARM(dsp2,"i");MODULE_PARM(mixer,"i");MODULE_PARM(debug,"i");MODULE_PARM(irq_debug,"i");MODULE_PARM(digital,"i");MODULE_PARM(analog,"i");MODULE_PARM(rate,"i");MODULE_DEVICE_TABLE(pci, btaudio_pci_tbl);MODULE_DESCRIPTION("bt878 audio dma driver");MODULE_AUTHOR("Gerd Knorr");MODULE_LICENSE("GPL");/* * Local variables: * c-basic-offset: 8 * End: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -