📄 btaudio.c
字号:
__put_user(cpu_to_le16(avg),dst); } } else if (8 == bta->bits) { /* copy + byte downsampling (audio A/D) */ __u8 *src = bta->buf_cpu + bta->read_offset; __u8 __user *dst = buffer + ret; int n = ndst; if (!access_ok(VERIFY_WRITE, dst, ndst)) { if (0 == ret) ret = -EFAULT; break; } for (; n; n--, src += (1 << bta->sampleshift), dst++) __put_user(*src, dst); } else { /* copy + word downsampling (audio A/D) */ __u16 *src = (__u16*)(bta->buf_cpu + bta->read_offset); __u16 __user *dst = (__u16 __user *)(buffer + ret); int n = ndst>>1; if (!access_ok(VERIFY_WRITE,dst,ndst)) { if (0 == ret) ret = -EFAULT; break; } for (; n; n--, src += (1 << bta->sampleshift), dst++) __put_user(*src, 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; } mutex_unlock(&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 __user *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; void __user *argp = (void __user *)arg; int __user *p = argp; switch (cmd) { case OSS_GETVERSION: return put_user(SOUND_VERSION, p); case SNDCTL_DSP_GETCAPS: return 0; case SNDCTL_DSP_SPEED: if (get_user(val, p)) 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) { mutex_lock(&bta->lock); stop_recording(bta); start_recording(bta); mutex_unlock(&bta->lock); } /* fall through */ case SOUND_PCM_READ_RATE: if (bta->analog) { return put_user(HWBASE_AD*4/bta->decimation>>bta->sampleshift, p); } else { return put_user(bta->rate, p); } case SNDCTL_DSP_STEREO: if (!bta->analog) { if (get_user(val, p)) 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, p); case SNDCTL_DSP_CHANNELS: if (!bta->analog) { if (get_user(val, p)) 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, p); case SNDCTL_DSP_GETFMTS: /* Returns a mask */ if (bta->analog) return put_user(AFMT_S16_LE|AFMT_S8, p); else return put_user(AFMT_S16_LE, p); case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ if (get_user(val, p)) return -EFAULT; if (val != AFMT_QUERY) { if (bta->analog) bta->bits = (val == AFMT_S8) ? 8 : 16; else bta->bits = 16; if (bta->recording) { mutex_lock(&bta->lock); stop_recording(bta); start_recording(bta); mutex_unlock(&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, p); break; case SOUND_PCM_READ_BITS: return put_user(bta->bits, p); case SNDCTL_DSP_NONBLOCK: file->f_flags |= O_NONBLOCK; return 0; case SNDCTL_DSP_RESET: if (bta->recording) { mutex_lock(&bta->lock); stop_recording(bta); mutex_unlock(&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,p); 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(argp, &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 const 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 const 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 irqreturn_t btaudio_irq(int irq, void *dev_id){ int count = 0; u32 stat,astat; struct btaudio *bta = dev_id; int handled = 0; for (;;) { count++; stat = btread(REG_INT_STAT); astat = stat & btread(REG_INT_MASK); if (!astat) return IRQ_RETVAL(handled); handled = 1; 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 IRQ_NONE;}/* -------------------------------------------------------------- */static unsigned int dsp1 = -1;static unsigned int dsp2 = -1;static unsigned int mixer = -1;static int latency = -1;static int digital = 1;static int analog = 1;static int rate;#define BTA_OSPREY200 1static struct cardinfo cards[] = { [0] = { .name = "default", .rate = 32000, }, [BTA_OSPREY200] = { .name = "Osprey 200", .rate = 44100, },};static int __devinit btaudio_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id){ struct btaudio *bta; struct cardinfo *card = &cards[pci_id->driver_data]; unsigned char revision,lat; 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 = kzalloc(sizeof(*bta),GFP_ATOMIC); if (!bta) { rc = -ENOMEM; goto fail0; } 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; } /* sample rate */ bta->rate = card->rate; if (rate) bta->rate = rate; mutex_init(&bta->lock); init_waitqueue_head(&bta->readq); if (-1 != latency) { printk(KERN_INFO "btaudio: setting pci latency timer to %d\n", latency); pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, latency); } pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &revision); pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &lat); 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, mmio: 0x%lx\n", bta->irq, lat, bta->mem); printk("btaudio: using card config \"%s\"\n", card->name); /* init hw */ btwrite(0, REG_GPIO_DMA_CTL); btwrite(0, REG_INT_MASK); btwrite(~0U, REG_INT_STAT); pci_set_master(pci_dev); if ((rc = request_irq(bta->irq, btaudio_irq, IRQF_SHARED|IRQF_DISABLED, "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; } printk(KERN_INFO "btaudio: registered device dsp%d [digital]\n", bta->dsp_digital >> 4); } 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; } printk(KERN_INFO "btaudio: registered device dsp%d [analog]\n", bta->dsp_analog >> 4); 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; } printk(KERN_INFO "btaudio: registered device mixer%d\n", bta->mixer_dev >> 4); } /* 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: iounmap(bta->mmio); kfree(bta); fail0: release_mem_region(pci_resource_start(pci_dev,0), pci_resource_len(pci_dev,0)); 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(~0U, 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)); iounmap(bta->mmio); /* 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[] = { { .vendor = PCI_VENDOR_ID_BROOKTREE, .device = 0x0878, .subvendor = 0x0070, .subdevice = 0xff01, .driver_data = BTA_OSPREY200, },{ .vendor = PCI_VENDOR_ID_BROOKTREE, .device = 0x0878, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, },{ .vendor = PCI_VENDOR_ID_BROOKTREE, .device = 0x0878, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, },{ /* --- end of list --- */ }};static struct pci_driver btaudio_pci_driver = { .name = "btaudio", .id_table = btaudio_pci_tbl, .probe = btaudio_probe, .remove = __devexit_p(btaudio_remove),};static int btaudio_init_module(void){ printk(KERN_INFO "btaudio: driver version 0.7 loaded [%s%s%s]\n", digital ? "digital" : "", analog && digital ? "+" : "", analog ? "analog" : ""); return pci_register_driver(&btaudio_pci_driver);}static void btaudio_cleanup_module(void){ pci_unregister_driver(&btaudio_pci_driver); return;}module_init(btaudio_init_module);module_exit(btaudio_cleanup_module);module_param(dsp1, int, S_IRUGO);module_param(dsp2, int, S_IRUGO);module_param(mixer, int, S_IRUGO);module_param(debug, int, S_IRUGO | S_IWUSR);module_param(irq_debug, int, S_IRUGO | S_IWUSR);module_param(digital, int, S_IRUGO);module_param(analog, int, S_IRUGO);module_param(rate, int, S_IRUGO);module_param(latency, int, S_IRUGO);MODULE_PARM_DESC(latency,"pci latency timer");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 + -