📄 audio.c
字号:
if (!p) return -ENOMEM; db->sgbuf[nr] = p; mem_map_reserve(MAP_NR(p)); } memset(db->sgbuf[nr], AFMT_ISUNSIGNED(db->format) ? 0x80 : 0, PAGE_SIZE); if ((nr << PAGE_SHIFT) >= db->dmasize) break; } db->bufsize = nr << PAGE_SHIFT; db->ready = 1; printk(KERN_DEBUG "dmabuf_init: bytepersec %d bufs %d ossfragshift %d ossmaxfrags %d " "fragshift %d fragsize %d numfrag %d dmasize %d bufsize %d\n", bytepersec, bufs, db->ossfragshift, db->ossmaxfrags, db->fragshift, db->fragsize, db->numfrag, db->dmasize, db->bufsize); 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; copy_from_user_ret((db->sgbuf[ptr >> PAGE_SHIFT]) + (ptr & (PAGE_SIZE-1)), buffer, pgrem, -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; copy_to_user_ret(buffer, (db->sgbuf[ptr >> PAGE_SHIFT]) + (ptr & (PAGE_SIZE-1)), pgrem, -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 void conversion(const void *ibuf, unsigned int ifmt, void *obuf, unsigned int ofmt, void *tmp, unsigned int scnt){ unsigned int cnt, i; __s16 *sp, *sp2, s; unsigned char *bp; cnt = scnt; if (AFMT_ISSTEREO(ifmt)) cnt <<= 1; sp = ((__s16 *)tmp) + cnt; switch (ifmt & ~AFMT_STEREO) { case AFMT_U8: for (bp = ((unsigned char *)ibuf)+cnt, i = 0; i < cnt; i++) { bp--; sp--; *sp = (*bp ^ 0x80) << 8; } break; case AFMT_S8: for (bp = ((unsigned char *)ibuf)+cnt, i = 0; i < cnt; i++) { bp--; sp--; *sp = *bp << 8; } break; case AFMT_U16_LE: for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i < cnt; i++) { bp -= 2; sp--; *sp = (bp[0] | (bp[1] << 8)) ^ 0x8000; } break; case AFMT_U16_BE: for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i < cnt; i++) { bp -= 2; sp--; *sp = (bp[1] | (bp[0] << 8)) ^ 0x8000; } break; case AFMT_S16_LE: for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i < cnt; i++) { bp -= 2; sp--; *sp = bp[0] | (bp[1] << 8); } break; case AFMT_S16_BE: for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i < cnt; i++) { bp -= 2; sp--; *sp = bp[1] | (bp[0] << 8); } break; } if (!AFMT_ISSTEREO(ifmt) && AFMT_ISSTEREO(ofmt)) { /* expand from mono to stereo */ for (sp = ((__s16 *)tmp)+scnt, sp2 = ((__s16 *)tmp)+2*scnt, i = 0; i < scnt; i++) { sp--; sp2 -= 2; sp2[0] = sp2[1] = sp[0]; } } if (AFMT_ISSTEREO(ifmt) && !AFMT_ISSTEREO(ofmt)) { /* contract from stereo to mono */ for (sp = sp2 = ((__s16 *)tmp), i = 0; i < scnt; i++, sp++, sp2 += 2) sp[0] = (sp2[0] + sp2[1]) >> 1; } cnt = scnt; if (AFMT_ISSTEREO(ofmt)) cnt <<= 1; sp = ((__s16 *)tmp); bp = ((unsigned char *)obuf); switch (ofmt & ~AFMT_STEREO) { case AFMT_U8: for (i = 0; i < cnt; i++, sp++, bp++) *bp = (*sp >> 8) ^ 0x80; break; case AFMT_S8: for (i = 0; i < cnt; i++, sp++, bp++) *bp = *sp >> 8; break; case AFMT_U16_LE: for (i = 0; i < cnt; i++, sp++, bp += 2) { s = *sp; bp[0] = s; bp[1] = (s >> 8) ^ 0x80; } break; case AFMT_U16_BE: for (i = 0; i < cnt; i++, sp++, bp += 2) { s = *sp; bp[1] = s; bp[0] = (s >> 8) ^ 0x80; } break; case AFMT_S16_LE: for (i = 0; i < cnt; i++, sp++, bp += 2) { s = *sp; bp[0] = s; bp[1] = s >> 8; } break; case AFMT_S16_BE: for (i = 0; i < cnt; i++, sp++, bp += 2) { s = *sp; bp[1] = s; bp[0] = s >> 8; } break; } }static void usbin_convert(struct usbin *u, unsigned char *buffer, unsigned int samples){ union { __s16 s[64]; unsigned char b[0]; } tmp; unsigned int scnt, maxs, ufmtsh, dfmtsh; ufmtsh = AFMT_BYTESSHIFT(u->format); dfmtsh = AFMT_BYTESSHIFT(u->dma.format); maxs = (AFMT_ISSTEREO(u->dma.format | u->format)) ? 32 : 64; while (samples > 0) { scnt = samples; if (scnt > maxs) scnt = maxs; conversion(buffer, u->format, tmp.b, u->dma.format, tmp.b, scnt); dmabuf_copyin(&u->dma, tmp.b, scnt << dfmtsh); buffer += scnt << ufmtsh; samples -= scnt; }} static int usbin_prepare_desc(struct usbin *u, purb_t urb){ unsigned int i, maxsize, offs; maxsize = (u->freqmax + 0x3fff) >> (14 - AFMT_BYTESSHIFT(u->format)); //printk(KERN_DEBUG "usbin_prepare_desc: maxsize %d freq 0x%x format 0x%x\n", maxsize, u->freqn, u->format); for (i = offs = 0; i < DESCFRAMES; i++, offs += maxsize) { urb->iso_frame_desc[i].length = maxsize; urb->iso_frame_desc[i].offset = offs; } return 0;}/* * return value: 0 if descriptor should be restarted, -1 otherwise * convert sample format on the fly if necessary */static int usbin_retire_desc(struct usbin *u, purb_t urb){ unsigned int i, ufmtsh, dfmtsh, err = 0, cnt, scnt, dmafree; unsigned char *cp; ufmtsh = AFMT_BYTESSHIFT(u->format); dfmtsh = AFMT_BYTESSHIFT(u->dma.format); for (i = 0; i < DESCFRAMES; i++) { cp = ((unsigned char *)urb->transfer_buffer) + urb->iso_frame_desc[i].offset; if (urb->iso_frame_desc[i].status) { printk(KERN_DEBUG "usbin_retire_desc: frame %u status %d\n", i, urb->iso_frame_desc[i].status); continue; } scnt = urb->iso_frame_desc[i].actual_length >> ufmtsh; if (!scnt) continue; cnt = scnt << dfmtsh; if (!u->dma.mapped) { dmafree = u->dma.dmasize - u->dma.count; if (cnt > dmafree) { scnt = dmafree >> dfmtsh; cnt = scnt << dfmtsh; err++; } } u->dma.count += cnt; if (u->format == u->dma.format) { /* we do not need format conversion */ dmabuf_copyin(&u->dma, cp, cnt); } else { /* we need sampling format conversion */ usbin_convert(u, cp, scnt); } } if (err) u->dma.error++; if (u->dma.count >= (signed)u->dma.fragsize) wake_up(&u->dma.wait); return err ? -1 : 0;}static void usbin_completed(struct urb *urb){ struct usb_audiodev *as = (struct usb_audiodev *)urb->context; struct usbin *u = &as->usbin; unsigned long flags; unsigned int mask; int suret = USB_ST_NOERROR;#if 0 printk(KERN_DEBUG "usbin_completed: status %d errcnt %d flags 0x%x\n", urb->status, urb->error_count, u->flags);#endif if (urb == &u->durb[0].urb) mask = FLG_URB0RUNNING; else if (urb == &u->durb[1].urb) mask = FLG_URB1RUNNING; else { mask = 0; printk(KERN_ERR "usbin_completed: panic: unknown URB\n"); } spin_lock_irqsave(&as->lock, flags); if (!usbin_retire_desc(u, urb) && u->flags & FLG_RUNNING && !usbin_prepare_desc(u, urb) && (suret = usb_submit_urb(urb)) == USB_ST_NOERROR) { u->flags |= mask; } else { u->flags &= ~(mask | FLG_RUNNING); wake_up(&u->dma.wait); printk(KERN_DEBUG "usbin_completed: descriptor not restarted (usb_submit_urb: %d)\n", suret); } spin_unlock_irqrestore(&as->lock, flags);}/*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -