📄 rme96xx.c
字号:
{ int i,j; int cnt = count/dma->inchannels; for (i=0;i < dma->inchannels;i++) { short* sbuf = (short*)buffer + i; short* hwbuf =(short*) &dma->s->recbuf[(dma->inoffset + i)*RME96xx_DMA_MAX_SAMPLES]; hwbuf+=(swptr>>1); for (j=0;j<(cnt>>1);j++) { hwbuf++; __put_user(*hwbuf++,sbuf++); sbuf+=(dma->inchannels-1); } } swptr += (cnt<<1); break; } default: printk(RME_MESS" unsupported format\n"); return -1; } /* switch */ swptr&=((dma->s->fragsize<<1) -1); dma->readptr = swptr; return 0;}static void rme96xx_interrupt(int irq, void *dev_id, struct pt_regs *regs){ int i; rme96xx_info *s = (rme96xx_info *)dev_id; struct dmabuf *db; u32 status; unsigned long flags; status = readl(s->iobase + RME96xx_status_register); if (!(status & RME96xx_IRQ)) { return; } spin_lock_irqsave(&s->lock,flags); writel(0,s->iobase + RME96xx_irq_clear); s->hwbufid = (status & RME96xx_buffer_id)>>26; if ((status & 0xffc0) <= 256) s->hwptr = 0; for(i=0;i<devices;i++) { db = &(s->dma[i]); if(db->started > 0) wake_up(&(db->wait)); } spin_unlock_irqrestore(&s->lock,flags);}/*---------------------------------------------------------------------------- PCI detection and module initialization stuff ----------------------------------------------------------------------------*/void* busmaster_malloc(int size) { int pg; /* 2 s exponent of memory size */ char *buf; DBG(printk("kernel malloc pages ..\n")); for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); buf = (char *) __get_free_pages(GFP_KERNEL | GFP_DMA, pg); if (buf) { struct page* page, *last_page; page = virt_to_page(buf); last_page = virt_to_page(buf + (1 << pg)); DBG(printk("setting reserved bit\n")); while (page < last_page) { SetPageReserved(page); page++; } return buf; } DBG(printk("allocated %ld",(long)buf)); return NULL;}void busmaster_free(void* ptr,int size) { int pg; struct page* page, *last_page; if (ptr == NULL) return; for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); page = virt_to_page(ptr); last_page = page + (1 << pg); while (page < last_page) { ClearPageReserved(page); page++; } DBG(printk("freeing pages\n")); free_pages((unsigned long) ptr, pg); DBG(printk("done\n"));}/* initialize those parts of the info structure which are not pci detectable resources */static int rme96xx_dmabuf_init(rme96xx_info * s,struct dmabuf* dma,int ioffset,int ooffset) { init_MUTEX(&dma->open_sem); init_waitqueue_head(&dma->open_wait); init_waitqueue_head(&dma->wait); dma->s = s; dma->error = 0; dma->format = AFMT_S32_BLOCKED; dma->formatshift = 0; dma->inchannels = dma->outchannels = 1; dma->inoffset = ioffset; dma->outoffset = ooffset; dma->opened=0; dma->started=0; dma->mmapped=0; dma->open_mode=0; dma->mono=0; rme96xx_clearbufs(dma); return 0;}int rme96xx_init(rme96xx_info* s){ int i; DBG(printk(__FUNCTION__"\n")); numcards++; s->magic = RME96xx_MAGIC; spin_lock_init(&s->lock); COMM ("setup busmaster memory") s->recbuf = busmaster_malloc(RME96xx_DMA_MAX_SIZE_ALL); s->playbuf = busmaster_malloc(RME96xx_DMA_MAX_SIZE_ALL); if (!s->recbuf || !s->playbuf) { printk(KERN_ERR RME_MESS" Unable to allocate busmaster memory\n"); return -ENODEV; } COMM ("setting rec and playbuffers") writel((u32) virt_to_bus(s->recbuf),s->iobase + RME96xx_rec_buffer); writel((u32) virt_to_bus(s->playbuf),s->iobase + RME96xx_play_buffer); COMM ("initializing control register") rme96xx_unset_ctrl(s,0xffffffff); rme96xx_set_ctrl(s,RME96xx_ctrl_init); COMM ("setup devices") for (i=0;i < devices;i++) { struct dmabuf * dma = &s->dma[i]; rme96xx_dmabuf_init(s,dma,2*i,2*i); } s->started = 0; rme96xx_setlatency(s,7); printk(KERN_INFO RME_MESS" card %d initialized\n",numcards); return 0;}/* open uses this to figure out which device was opened .. this seems to be unnecessary complex */static LIST_HEAD(devs);static int __devinit rme96xx_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid){ int i; rme96xx_info *s; DBG(printk(__FUNCTION__"\n")); if (pcidev->irq == 0) return -1; if (!pci_dma_supported(pcidev, 0xffffffff)) { printk(KERN_WARNING RME_MESS" architecture does not support 32bit PCI busmaster DMA\n"); return -1; } if (!(s = kmalloc(sizeof(rme96xx_info), GFP_KERNEL))) { printk(KERN_WARNING RME_MESS" out of memory\n"); return -1; } memset(s, 0, sizeof(rme96xx_info)); s->pcidev = pcidev; s->iobase = ioremap(pci_resource_start(pcidev, 0),RME96xx_IO_EXTENT); s->irq = pcidev->irq; DBG(printk("remapped iobase: %lx irq %d\n",(long)s->iobase,s->irq)); if (pci_enable_device(pcidev)) goto err_irq; if (request_irq(s->irq, rme96xx_interrupt, SA_SHIRQ, "es1370", s)) { printk(KERN_ERR RME_MESS" irq %u in use\n", s->irq); goto err_irq; } /* initialize the card */ i = 0; if (rme96xx_init(s) < 0) { printk(KERN_ERR RME_MESS" initialization failed\n"); goto err_devices; } for (i=0;i<devices;i++) { if ((s->dspnum[i] = register_sound_dsp(&rme96xx_audio_fops, -1)) < 0) goto err_devices; } if ((s->mixer = register_sound_mixer(&rme96xx_mixer_fops, -1)) < 0) goto err_devices; pci_set_drvdata(pcidev, s); pcidev->dma_mask = 0xffffffff; /* ????? */ /* put it into driver list */ list_add_tail(&s->devs, &devs); DBG(printk("initialization successful\n")); return 0; /* error handler */ err_devices: while (i--) unregister_sound_dsp(s->dspnum[i]); free_irq(s->irq,s); err_irq: kfree(s); return -1;}static void __devinit rme96xx_remove(struct pci_dev *dev){ int i; rme96xx_info *s = pci_get_drvdata(dev); if (!s) { printk(KERN_ERR"device structure not valid\n"); return ; } if (s->started) rme96xx_startcard(s,0); i = devices; while (i) { i--; unregister_sound_dsp(s->dspnum[i]); } unregister_sound_mixer(s->mixer);/* synchronize_irq(); This call got lost somehow ? */ free_irq(s->irq,s); busmaster_free(s->recbuf,RME96xx_DMA_MAX_SIZE_ALL); busmaster_free(s->playbuf,RME96xx_DMA_MAX_SIZE_ALL); kfree(s); pci_set_drvdata(dev, NULL);}#ifndef PCI_VENDOR_ID_RME #define PCI_VENDOR_ID_RME 0x10ee#endif#ifndef PCI_DEVICE_ID_RME9652#define PCI_DEVICE_ID_RME9652 0x3fc4#endif#ifndef PCI_ANY_ID#define PCI_ANY_ID 0#endifstatic struct pci_device_id id_table[] __devinitdata = { { PCI_VENDOR_ID_RME, PCI_DEVICE_ID_RME9652, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, { 0, }};MODULE_DEVICE_TABLE(pci, id_table);static struct pci_driver rme96xx_driver = { name: "rme96xx", id_table: id_table, probe: rme96xx_probe, remove: rme96xx_remove};static int __init init_rme96xx(void){ if (!pci_present()) /* No PCI bus in this machine! */ return -ENODEV; printk(KERN_INFO RME_MESS" version "RMEVERSION" time " __TIME__ " " __DATE__ "\n"); printk(KERN_INFO RME_MESS" reserving %d dsp device(s)\n",devices); numcards = 0; return pci_module_init(&rme96xx_driver);}static void __exit cleanup_rme96xx(void){ printk(KERN_INFO RME_MESS" unloading\n"); pci_unregister_driver(&rme96xx_driver);}module_init(init_rme96xx);module_exit(cleanup_rme96xx);/*-------------------------------------------------------------------------- Implementation of file operations ---------------------------------------------------------------------------*/#define RME96xx_FMT (AFMT_S16_LE|AFMT_U8|AFMT_S32_BLOCKED)static int rme96xx_ioctl(struct inode *in, struct file *file, unsigned int cmd, unsigned long arg){ struct dmabuf * dma = (struct dmabuf *)file->private_data; rme96xx_info *s = dma->s; unsigned long flags; audio_buf_info abinfo; count_info cinfo; int count; int val = 0; VALIDATE_STATE(s); DBG(printk("ioctl %ud\n",cmd)); switch (cmd) { case OSS_GETVERSION: return put_user(SOUND_VERSION, (int *)arg); case SNDCTL_DSP_SYNC:#if 0 if (file->f_mode & FMODE_WRITE) return drain_dac2(s, 0/*file->f_flags & O_NONBLOCK*/);#endif return 0; case SNDCTL_DSP_SETDUPLEX: return 0; case SNDCTL_DSP_GETCAPS: return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); case SNDCTL_DSP_RESET:// rme96xx_clearbufs(dma); return 0; case SNDCTL_DSP_SPEED: if (get_user(val, (int *)arg)) return -EFAULT; if (val >= 0) {/* generally it's not a problem if we change the speed if (dma->open_mode & (~file->f_mode) & (FMODE_READ|FMODE_WRITE)) return -EINVAL;*/ spin_lock_irqsave(&s->lock, flags); switch (val) { case 44100: case 88200: rme96xx_unset_ctrl(s,RME96xx_freq); break; case 48000: case 96000: rme96xx_set_ctrl(s,RME96xx_freq); break; default: rme96xx_unset_ctrl(s,RME96xx_freq); val = 44100; } if (val > 50000) rme96xx_set_ctrl(s,RME96xx_DS); else rme96xx_unset_ctrl(s,RME96xx_DS); s->rate = val; spin_unlock_irqrestore(&s->lock, flags); } DBG(printk("speed set to %d\n",val)); return put_user(val, (int *)arg); case SNDCTL_DSP_STEREO: /* this plays a mono file on two channels */ if (get_user(val, (int *)arg)) return -EFAULT; if (!val) { DBG(printk("setting to mono\n")); dma->mono=1; dma->inchannels = 1; dma->outchannels = 1; } else { DBG(printk("setting to stereo\n")); dma->mono = 0; dma->inchannels = 2; dma->outchannels = 2; } return 0; case SNDCTL_DSP_CHANNELS: /* remember to check for resonable offset/channel pairs here */ if (get_user(val, (int *)arg)) return -EFAULT; if (file->f_mode & FMODE_WRITE) { if (val > 0 && (dma->outoffset + val) <= RME96xx_CHANNELS_PER_CARD) dma->outchannels = val; else dma->outchannels = val = 2; DBG(printk("setting to outchannels %d\n",val)); } if (file->f_mode & FMODE_READ) { if (val > 0 && (dma->inoffset + val) <= RME96xx_CHANNELS_PER_CARD) dma->inchannels = val; else dma->inchannels = val = 2; DBG(printk("setting to inchannels %d\n",val)); } dma->mono=0; return put_user(val, (int *)arg); case SNDCTL_DSP_GETFMTS: /* Returns a mask */ return put_user(RME96xx_FMT, (int *)arg); case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ DBG(printk("setting to format %x\n",val)); if (get_user(val, (int *)arg)) return -EFAULT; if (val != AFMT_QUERY) { if (val & RME96xx_FMT) dma->format = val; switch (dma->format) { case AFMT_S16_LE: dma->formatshift=1; break; case AFMT_S32_BLOCKED: dma->formatshift=0; break; } } return put_user(dma->format, (int *)arg); case SNDCTL_DSP_POST: return 0; case SNDCTL_DSP_GETTRIGGER: val = 0;#if 0 if (file->f_mode & FMODE_READ && s->ctrl & CTRL_ADC_EN) val |= PCM_ENABLE_INPUT; if (file->f_mode & FMODE_WRITE && s->ctrl & CTRL_DAC2_EN) val |= PCM_ENABLE_OUTPUT;#endif return put_user(val, (int *)arg); case SNDCTL_DSP_SETTRIGGER: if (get_user(val, (int *)arg)) return -EFAULT;#if 0 if (file->f_mode & FMODE_READ) { if (val & PCM_ENABLE_INPUT) { if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) return ret; start_adc(s); } else stop_adc(s); } if (file->f_mode & FMODE_WRITE) { if (val & PCM_ENABLE_OUTPUT) { if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s))) return ret; start_dac2(s); } else stop_dac2(s); }#endif return 0; case SNDCTL_DSP_GETOSPACE: if (!(file->f_mode & FMODE_WRITE)) return -EINVAL; val = rme96xx_gethwptr(dma->s,0); count = rme96xx_getospace(dma,val); if (!s->started) count = s->fragsize*2; abinfo.fragsize =(s->fragsize*dma->outchannels)>>dma->formatshift; abinfo.bytes = (count*dma->outchannels)>>dma->formatshift; abinfo.fragstotal = 2; abinfo.fragments = (count > s->fragsize); return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; case SNDCTL_DSP_GETISPACE: if (!(file->f_mode & FMODE_READ)) return -EINVAL; val = rme96xx_gethwptr(dma->s,0); count = rme96xx_getispace(dma,val);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -