⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 audio.c

📁 USB Audio Class Driver
💻 C
📖 第 1 页 / 共 5 页
字号:
{
	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, struct urb *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;
	}
	urb->interval = 1;
	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, struct urb *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) {
			dprintk((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 */
			dprintk((KERN_DEBUG "usbaudio: no sample format conversion\n"));
			dmabuf_copyin(&u->dma, cp, cnt);
		} else {
			/* we need sampling format conversion */
			dprintk((KERN_DEBUG "usbaudio: sample format conversion %x != %x\n", u->format, u->dma.format));
			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 pt_regs *regs)
{
	struct usb_audiodev *as = (struct usb_audiodev *)urb->context;
	struct usbin *u = &as->usbin;
	unsigned long flags;
	unsigned int mask;
	int suret = 0;

#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");
	}
	urb->dev = as->state->usbdev;
	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, GFP_ATOMIC)) == 0) {
		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);
}

/*
 * we output sync data
 */
static int usbin_sync_prepare_desc(struct usbin *u, struct urb *urb)
{
	unsigned char *cp = urb->transfer_buffer;
	unsigned int i, offs;
	
	for (i = offs = 0; i < SYNCFRAMES; i++, offs += 3, cp += 3) {
		urb->iso_frame_desc[i].length = 3;
		urb->iso_frame_desc[i].offset = offs;
		cp[0] = u->freqn;
		cp[1] = u->freqn >> 8;
		cp[2] = u->freqn >> 16;
	}
	urb->interval = 1;
	return 0;
}

/*
 * return value: 0 if descriptor should be restarted, -1 otherwise
 */
static int usbin_sync_retire_desc(struct usbin *u, struct urb *urb)
{
	unsigned int i;
	
	for (i = 0; i < SYNCFRAMES; i++)
		if (urb->iso_frame_desc[0].status)
			dprintk((KERN_DEBUG "usbin_sync_retire_desc: frame %u status %d\n", i, urb->iso_frame_desc[i].status));
	return 0;
}

static void usbin_sync_completed(struct urb *urb, struct pt_regs *regs)
{
	struct usb_audiodev *as = (struct usb_audiodev *)urb->context;
	struct usbin *u = &as->usbin;
	unsigned long flags;
	unsigned int mask;
	int suret = 0;

#if 0
	printk(KERN_DEBUG "usbin_sync_completed: status %d errcnt %d flags 0x%x\n", urb->status, urb->error_count, u->flags);
#endif
	if (urb == u->surb[0].urb)
		mask = FLG_SYNC0RUNNING;
	else if (urb == u->surb[1].urb)
		mask = FLG_SYNC1RUNNING;
	else {
		mask = 0;
		printk(KERN_ERR "usbin_sync_completed: panic: unknown URB\n");
	}
	urb->dev = as->state->usbdev;
	spin_lock_irqsave(&as->lock, flags);
	if (!usbin_sync_retire_desc(u, urb) &&
	    u->flags & FLG_RUNNING &&
	    !usbin_sync_prepare_desc(u, urb) && 
	    (suret = usb_submit_urb(urb, GFP_ATOMIC)) == 0) {
		u->flags |= mask;
	} else {
		u->flags &= ~(mask | FLG_RUNNING);
		wake_up(&u->dma.wait);
		dprintk((KERN_DEBUG "usbin_sync_completed: descriptor not restarted (usb_submit_urb: %d)\n", suret));
	}
	spin_unlock_irqrestore(&as->lock, flags);
}

static int usbin_start(struct usb_audiodev *as)
{
	struct usb_device *dev = as->state->usbdev;
	struct usbin *u = &as->usbin;
	struct urb *urb;
	unsigned long flags;
	unsigned int maxsze, bufsz;

#if 0
	printk(KERN_DEBUG "usbin_start: device %d ufmt 0x%08x dfmt 0x%08x srate %d\n",
	       dev->devnum, u->format, u->dma.format, u->dma.srate);
#endif
	/* allocate USB storage if not already done */
	spin_lock_irqsave(&as->lock, flags);
	if (!(u->flags & FLG_CONNECTED)) {
		spin_unlock_irqrestore(&as->lock, flags);
		return -EIO;
	}
	if (!(u->flags & FLG_RUNNING)) {
		spin_unlock_irqrestore(&as->lock, flags);
		u->freqn = ((u->dma.srate << 11) + 62) / 125; /* this will overflow at approx 2MSPS */
		u->freqmax = u->freqn + (u->freqn >> 2);
		u->phase = 0;
		maxsze = (u->freqmax + 0x3fff) >> (14 - AFMT_BYTESSHIFT(u->format));
		bufsz = DESCFRAMES * maxsze;
		if (u->durb[0].urb->transfer_buffer)
			kfree(u->durb[0].urb->transfer_buffer);
		u->durb[0].urb->transfer_buffer = kmalloc(bufsz, GFP_KERNEL);
		u->durb[0].urb->transfer_buffer_length = bufsz;
		if (u->durb[1].urb->transfer_buffer)
			kfree(u->durb[1].urb->transfer_buffer);
		u->durb[1].urb->transfer_buffer = kmalloc(bufsz, GFP_KERNEL);
		u->durb[1].urb->transfer_buffer_length = bufsz;
		if (u->syncpipe) {
			if (u->surb[0].urb->transfer_buffer)
				kfree(u->surb[0].urb->transfer_buffer);
			u->surb[0].urb->transfer_buffer = kmalloc(3*SYNCFRAMES, GFP_KERNEL);
			u->surb[0].urb->transfer_buffer_length = 3*SYNCFRAMES;
			if (u->surb[1].urb->transfer_buffer)
				kfree(u->surb[1].urb->transfer_buffer);
			u->surb[1].urb->transfer_buffer = kmalloc(3*SYNCFRAMES, GFP_KERNEL);
			u->surb[1].urb->transfer_buffer_length = 3*SYNCFRAMES;
		}
		if (!u->durb[0].urb->transfer_buffer || !u->durb[1].urb->transfer_buffer || 
		    (u->syncpipe && (!u->surb[0].urb->transfer_buffer || !u->surb[1].urb->transfer_buffer))) {
			printk(KERN_ERR "usbaudio: cannot start playback device %d\n", dev->devnum);
			return 0;
		}
		spin_lock_irqsave(&as->lock, flags);
	}
	if (u->dma.count >= u->dma.dmasize && !u->dma.mapped) {
		spin_unlock_irqrestore(&as->lock, flags);
		return 0;
	}
	u->flags |= FLG_RUNNING;
	if (!(u->flags & FLG_URB0RUNNING)) {
		urb = u->durb[0].urb;
		urb->dev = dev;
		urb->pipe = u->datapipe;
		urb->transfer_flags = URB_ISO_ASAP;
		urb->number_of_packets = DESCFRAMES;
		urb->context = as;
		urb->complete = usbin_completed;
		if (!usbin_prepare_desc(u, urb) && !usb_submit_urb(urb, GFP_KERNEL))
			u->flags |= FLG_URB0RUNNING;
		else
			u->flags &= ~FLG_RUNNING;
	}
	if (u->flags & FLG_RUNNING && !(u->flags & FLG_URB1RUNNING)) {
		urb = u->durb[1].urb;
		urb->dev = dev;
		urb->pipe = u->datapipe;
		urb->transfer_flags = URB_ISO_ASAP;
		urb->number_of_packets = DESCFRAMES;
		urb->context = as;
		urb->complete = usbin_completed;
		if (!usbin_prepare_desc(u, urb) && !usb_submit_urb(urb, GFP_KERNEL))
			u->flags |= FLG_URB1RUNNING;
		else
			u->flags &= ~FLG_RUNNING;
	}
	if (u->syncpipe) {
		if (u->flags & FLG_RUNNING && !(u->flags & FLG_SYNC0RUNNING)) {
			urb = u->surb[0].urb;
			urb->dev = dev;
			urb->pipe = u->syncpipe;
			urb->transfer_flags = URB_ISO_ASAP;
			urb->number_of_packets = SYNCFRAMES;
			urb->context = as;
			urb->complete = usbin_sync_completed;
			/* stride: u->syncinterval */
			if (!usbin_sync_prepare_desc(u, urb) && !usb_submit_urb(urb, GFP_KERNEL))
				u->flags |= FLG_SYNC0RUNNING;
			else
				u->flags &= ~FLG_RUNNING;
		}
		if (u->flags & FLG_RUNNING && !(u->flags & FLG_SYNC1RUNNING)) {
			urb = u->surb[1].urb;
			urb->dev = dev;
			urb->pipe = u->syncpipe;
			urb->transfer_flags = URB_ISO_ASAP;
			urb->number_of_packets = SYNCFRAMES;
			urb->context = as;
			urb->complete = usbin_sync_completed;
			/* stride: u->syncinterval */
			if (!usbin_sync_prepare_desc(u, urb) && !usb_submit_urb(urb, GFP_KERNEL))
				u->flags |= FLG_SYNC1RUNNING;
			else
				u->flags &= ~FLG_RUNNING;
		}
	}
	spin_unlock_irqrestore(&as->lock, flags);
	return 0;
}

static void usbout_stop(struct usb_audiodev *as)
{
	struct usbout *u = &as->usbout;
	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 usbout_release(struct usb_audiodev *as)
{
	usbout_stop(as);
}

static void usbout_disc(struct usb_audiodev *as)
{
	struct usbout *u = &as->usbout;
	unsigned long flags;

	spin_lock_irqsave(&as->lock, flags);
	u->flags &= ~(FLG_RUNNING | FLG_CONNECTED);
	spin_unlock_irqrestore(&as->lock, flags);
	usbout_stop(as);
}

static void usbout_convert(struct usbout *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;
		dmabuf_copyout(&u->dma, tmp.b, scnt << dfmtsh);
		conversion(tmp.b, u->dma.format, buffer, u->format, tmp.b, scnt);
		buffer += scnt << ufmtsh;
		samples -= scnt;
	}
}		

static int usbout_prepare_desc(struct usbout *u, struct urb *urb)
{
	unsigned int i, ufmtsh, dfmtsh, err = 0, cnt, scnt, offs;
	unsigned char *cp = urb->transfer_buffer;

	ufmtsh = AFMT_BYTESSHIFT(u->format);
	dfmtsh = AFMT_BYTESSHIFT(u->dma.format);
	for (i = offs = 0; i < DESCFRAMES; i++) {
		urb->iso_frame_desc[i].offset = offs;
		u->phase = (u->phase & 0x3fff) + u->freqm;
		scnt = u->phase >> 14;
		if (!scnt) {
			urb->iso_frame_desc[i].length = 0;
			continue;
		}
		cnt = scnt << dfmtsh;
		if (!u->dma.mapped) {
			if (cnt > u->dma.count) {
				scnt = u->dma.count >> dfmtsh;
				cnt = scnt << dfmtsh;
				err++;
			}
			u->dma.count -= cnt;
		} else
			u->dma.count += cnt;
		if (u->format == u->dma.format) {

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -