📄 audio.c
字号:
# define AFMT_S24_BE 0x00001000 #endif#ifndef AFMT_U24_LE# define AFMT_U24_LE 0x00002000 #endif#ifndef AFMT_U24_BE# define AFMT_U24_BE 0x00004000 #endif#ifndef AFMT_S32_LE# define AFMT_S32_LE 0x00008000 #endif#ifndef AFMT_S32_BE# define AFMT_S32_BE 0x00010000 #endif#ifndef AFMT_U32_LE# define AFMT_U32_LE 0x00020000 #endif#ifndef AFMT_U32_BE# define AFMT_U32_BE 0x00040000 #endif/* private audio format extensions */#define AFMT_STEREO 0x01000000#define AFMT_CHMASK 0xff000000#define AFMT_8MASK (AFMT_U8 | AFMT_S8)#define AFMT_16MASK (AFMT_U16_LE | AFMT_S16_LE | AFMT_U16_BE | AFMT_S16_BE)#define AFMT_24MASK (AFMT_U24_LE | AFMT_S24_LE | AFMT_U24_BE | AFMT_S24_BE)#define AFMT_32MASK (AFMT_U32_LE | AFMT_S32_LE | AFMT_U32_BE | AFMT_S32_BE)#define AFMT_SIGNMASK (AFMT_S8 | AFMT_S16_LE | AFMT_S16_BE |\ AFMT_S24_LE | AFMT_S24_BE |\ AFMT_S32_LE | AFMT_S32_BE)/* a little odd, but the code counts on byte formats being identified as 'big endian' */#define AFMT_ENDIANMASK (AFMT_S8 | AFMT_U8 |\ AFMT_S16_BE | AFMT_U16_BE |\ AFMT_S24_BE | AFMT_U24_BE |\ AFMT_S32_BE | AFMT_U32_BE)#define AFMT_ISSTEREO(x) (((x) & 0xff000000) == AFMT_STEREO)#define AFMT_CHANNELS(x) (((unsigned)(x) >> 24) + 1)#define AFMT_BYTES(x) ( (((x)&AFMT_8MASK)!=0)+\ (((x)&AFMT_16MASK)!=0)*2+\ (((x)&AFMT_24MASK)!=0)*3+\ (((x)&AFMT_32MASK)!=0)*4 )#define AFMT_SAMPLEBYTES(x) (AFMT_BYTES(x)*AFMT_CHANNELS(x))#define AFMT_SIGN(x) ((x)&AFMT_SIGNMASK)#define AFMT_ENDIAN(x) ((x)&AFMT_ENDIANMASK)/* --------------------------------------------------------------------- *//* prevent picking up a bogus abs macro */#undef absstatic inline int abs(int x){ if (x < 0) return -x; return x;} /* --------------------------------------------------------------------- */static inline unsigned ld2(unsigned int x){ unsigned r = 0; if (x >= 0x10000) { x >>= 16; r += 16; } if (x >= 0x100) { x >>= 8; r += 8; } if (x >= 0x10) { x >>= 4; r += 4; } if (x >= 4) { x >>= 2; r += 2; } if (x >= 2) r++; return r;}/* --------------------------------------------------------------------- *//* * OSS compatible ring buffer management. The ring buffer may be mmap'ed into * an application address space. * * I first used the rvmalloc stuff copied from bttv. Alan Cox did not like it, so * we now use an array of pointers to a single page each. This saves us the * kernel page table manipulations, but we have to do a page table alike mechanism * (though only one indirection) in software. */static void dmabuf_release(struct dmabuf *db){ unsigned int nr; void *p; for(nr = 0; nr < NRSGBUF; nr++) { if (!(p = db->sgbuf[nr])) continue; mem_map_unreserve(virt_to_page(p)); free_page((unsigned long)p); db->sgbuf[nr] = NULL; } db->mapped = db->ready = 0;}static int dmabuf_init(struct dmabuf *db){ unsigned int nr, bytepersec, bufs; void *p; /* initialize some fields */ db->rdptr = db->wrptr = db->total_bytes = db->count = db->error = 0; /* calculate required buffer size */ bytepersec = db->srate * AFMT_SAMPLEBYTES(db->format); bufs = 1U << DMABUFSHIFT; if (db->ossfragshift) { if ((1000 << db->ossfragshift) < bytepersec) db->fragshift = ld2(bytepersec/1000); else db->fragshift = db->ossfragshift; } else { db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); if (db->fragshift < 3) db->fragshift = 3; } db->numfrag = bufs >> db->fragshift; while (db->numfrag < 4 && db->fragshift > 3) { db->fragshift--; db->numfrag = bufs >> db->fragshift; } db->fragsize = 1 << db->fragshift; if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) db->numfrag = db->ossmaxfrags; db->dmasize = db->numfrag << db->fragshift; for(nr = 0; nr < NRSGBUF; nr++) { if (!db->sgbuf[nr]) { p = (void *)get_free_page(GFP_KERNEL); if (!p) return -ENOMEM; db->sgbuf[nr] = p; mem_map_reserve(virt_to_page(p)); } memset(db->sgbuf[nr], AFMT_SIGN(db->format) ? 0 : 0x80, PAGE_SIZE); if ((nr << PAGE_SHIFT) >= db->dmasize) break; } db->bufsize = nr << PAGE_SHIFT; db->ready = 1; dprintk((KERN_DEBUG "usbaudio: dmabuf_init bytepersec %d bufs %d ossfragshift %d ossmaxfrags %d " "fragshift %d fragsize %d numfrag %d dmasize %d bufsize %d fmt 0x%x srate %d\n", bytepersec, bufs, db->ossfragshift, db->ossmaxfrags, db->fragshift, db->fragsize, db->numfrag, db->dmasize, db->bufsize, db->format, db->srate)); return 0;}static int dmabuf_mmap(struct dmabuf *db, unsigned long start, unsigned long size, pgprot_t prot){ unsigned int nr; if (!db->ready || db->mapped || (start | size) & (PAGE_SIZE-1) || size > db->bufsize) return -EINVAL; size >>= PAGE_SHIFT; for(nr = 0; nr < size; nr++) if (!db->sgbuf[nr]) return -EINVAL; db->mapped = 1; for(nr = 0; nr < size; nr++) { if (remap_page_range(start, virt_to_phys(db->sgbuf[nr]), PAGE_SIZE, prot)) return -EAGAIN; start += PAGE_SIZE; } return 0;}static void dmabuf_copyin(struct dmabuf *db, const void *buffer, unsigned int size){ unsigned int pgrem, rem; db->total_bytes += size; for (;;) { if (size <= 0) return; pgrem = ((~db->wrptr) & (PAGE_SIZE-1)) + 1; if (pgrem > size) pgrem = size; rem = db->dmasize - db->wrptr; if (pgrem > rem) pgrem = rem; memcpy((db->sgbuf[db->wrptr >> PAGE_SHIFT]) + (db->wrptr & (PAGE_SIZE-1)), buffer, pgrem); size -= pgrem; (char *)buffer += pgrem; db->wrptr += pgrem; if (db->wrptr >= db->dmasize) db->wrptr = 0; }}static void dmabuf_copyout(struct dmabuf *db, void *buffer, unsigned int size){ unsigned int pgrem, rem; db->total_bytes += size; for (;;) { if (size <= 0) return; pgrem = ((~db->rdptr) & (PAGE_SIZE-1)) + 1; if (pgrem > size) pgrem = size; rem = db->dmasize - db->rdptr; if (pgrem > rem) pgrem = rem; memcpy(buffer, (db->sgbuf[db->rdptr >> PAGE_SHIFT]) + (db->rdptr & (PAGE_SIZE-1)), pgrem); size -= pgrem; (char *)buffer += pgrem; db->rdptr += pgrem; if (db->rdptr >= db->dmasize) db->rdptr = 0; }}static int dmabuf_copyin_user(struct dmabuf *db, unsigned int ptr, const void *buffer, unsigned int size){ unsigned int pgrem, rem; if (!db->ready || db->mapped) return -EINVAL; for (;;) { if (size <= 0) return 0; pgrem = ((~ptr) & (PAGE_SIZE-1)) + 1; if (pgrem > size) pgrem = size; rem = db->dmasize - ptr; if (pgrem > rem) pgrem = rem; if (copy_from_user((db->sgbuf[ptr >> PAGE_SHIFT]) + (ptr & (PAGE_SIZE-1)), buffer, pgrem)) return -EFAULT; size -= pgrem; (char *)buffer += pgrem; ptr += pgrem; if (ptr >= db->dmasize) ptr = 0; }}static int dmabuf_copyout_user(struct dmabuf *db, unsigned int ptr, void *buffer, unsigned int size){ unsigned int pgrem, rem; if (!db->ready || db->mapped) return -EINVAL; for (;;) { if (size <= 0) return 0; pgrem = ((~ptr) & (PAGE_SIZE-1)) + 1; if (pgrem > size) pgrem = size; rem = db->dmasize - ptr; if (pgrem > rem) pgrem = rem; if (copy_to_user(buffer, (db->sgbuf[ptr >> PAGE_SHIFT]) + (ptr & (PAGE_SIZE-1)), pgrem)) return -EFAULT; size -= pgrem; (char *)buffer += pgrem; ptr += pgrem; if (ptr >= db->dmasize) ptr = 0; }}/* --------------------------------------------------------------------- *//* * USB I/O code. We do sample format conversion if necessary */static void usbin_stop(struct usb_audiodev *as){ struct usbin *u = &as->usbin; unsigned long flags; unsigned int i, notkilled = 1; spin_lock_irqsave(&as->lock, flags); u->flags &= ~FLG_RUNNING; i = u->flags; spin_unlock_irqrestore(&as->lock, flags); while (i & (FLG_URB0RUNNING|FLG_URB1RUNNING|FLG_SYNC0RUNNING|FLG_SYNC1RUNNING)) { set_current_state(notkilled ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); schedule_timeout(1); spin_lock_irqsave(&as->lock, flags); i = u->flags; spin_unlock_irqrestore(&as->lock, flags); if (notkilled && signal_pending(current)) { if (i & FLG_URB0RUNNING) usb_unlink_urb(&u->durb[0].urb); if (i & FLG_URB1RUNNING) usb_unlink_urb(&u->durb[1].urb); if (i & FLG_SYNC0RUNNING) usb_unlink_urb(&u->surb[0].urb); if (i & FLG_SYNC1RUNNING) usb_unlink_urb(&u->surb[1].urb); notkilled = 0; } } set_current_state(TASK_RUNNING); if (u->durb[0].urb.transfer_buffer) kfree(u->durb[0].urb.transfer_buffer); if (u->durb[1].urb.transfer_buffer) kfree(u->durb[1].urb.transfer_buffer); if (u->surb[0].urb.transfer_buffer) kfree(u->surb[0].urb.transfer_buffer); if (u->surb[1].urb.transfer_buffer) kfree(u->surb[1].urb.transfer_buffer); u->durb[0].urb.transfer_buffer = u->durb[1].urb.transfer_buffer = u->surb[0].urb.transfer_buffer = u->surb[1].urb.transfer_buffer = NULL;}static inline void usbin_release(struct usb_audiodev *as){ usbin_stop(as);}static void usbin_disc(struct usb_audiodev *as){ struct usbin *u = &as->usbin; unsigned long flags; spin_lock_irqsave(&as->lock, flags); u->flags &= ~(FLG_RUNNING | FLG_CONNECTED); spin_unlock_irqrestore(&as->lock, flags); usbin_stop(as);}static inline int iconvert(unsigned char **xx,int jump){ int value=0; unsigned char *x=*xx; /* conversion fall-through cascade compiles to a jump table */ switch(jump){ case 0: /* 32 bit BE */ value = x[3]; case 1: /* 24 bit BE */ value |= x[2] << 8; case 2: /* 16 bit BE */ value |= x[1] << 16; case 3: /* 8 bit */ value |= x[0] << 24; x+=(4-jump); break; case 4: /* 32 bit LE */ value = *x++; case 5: /* 24 bit LE */ value |= *x++ << 8; case 6: /* 16 bit LE */ value |= *x++ << 16; value |= *x++ << 24; break; } *xx=x; return(value);}static inline void oconvert(unsigned char **yy,int jump,int value){ unsigned char *y=*yy; /* conversion fall-through cascade compiles to a jump table */ switch(jump){ case 0: /* 32 bit BE */ y[3] = value; case 1: /* 24 bit BE */ y[2] = value >> 8; case 2: /* 16 bit BE */ y[1] = value >> 16; case 3: /* 8 bit */ y[0] = value >> 24; y+=(4-jump); break; case 4: /* 32 bit LE */ *y++ = value; case 5: /* 24 bit LE */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -