📄 audio.c
字号:
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;
ClearPageReserved(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_BYTESSHIFT(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_zeroed_page(GFP_KERNEL);
if (!p)
return -ENOMEM;
db->sgbuf[nr] = p;
SetPageReserved(virt_to_page(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;
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 vm_area_struct *vma, 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++) {
unsigned long pfn;
pfn = virt_to_phys(db->sgbuf[nr]) >> PAGE_SHIFT;
if (remap_pfn_range(vma, start, pfn, 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;
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;
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 __user *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;
buffer += pgrem;
ptr += pgrem;
if (ptr >= db->dmasize)
ptr = 0;
}
}
static int dmabuf_copyout_user(struct dmabuf *db, unsigned int ptr, void __user *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;
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_kill_urb(u->durb[0].urb);
if (i & FLG_URB1RUNNING)
usb_kill_urb(u->durb[1].urb);
if (i & FLG_SYNC0RUNNING)
usb_kill_urb(u->surb[0].urb);
if (i & FLG_SYNC1RUNNING)
usb_kill_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)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -