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

📄 usbmidi.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * usbmidi.c - ALSA USB MIDI driver * * Copyright (c) 2002-2007 Clemens Ladisch * All rights reserved. * * Based on the OSS usb-midi driver by NAGANO Daisuke, *          NetBSD's umidi driver by Takuya SHIOZAKI, *          the "USB Device Class Definition for MIDI Devices" by Roland * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright *    notice, this list of conditions, and the following disclaimer, *    without modification. * 2. The name of the author may not be used to endorse or promote products *    derived from this software without specific prior written permission. * * Alternatively, this software may be distributed and/or modified under the * terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#include <sound/driver.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/bitops.h>#include <linux/interrupt.h>#include <linux/spinlock.h>#include <linux/string.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/timer.h>#include <linux/usb.h>#include <sound/core.h>#include <sound/rawmidi.h>#include <sound/asequencer.h>#include "usbaudio.h"/* * define this to log all USB packets *//* #define DUMP_PACKETS *//* * how long to wait after some USB errors, so that khubd can disconnect() us * without too many spurious errors */#define ERROR_DELAY_JIFFIES (HZ / 10)MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");MODULE_DESCRIPTION("USB Audio/MIDI helper module");MODULE_LICENSE("Dual BSD/GPL");struct usb_ms_header_descriptor {	__u8  bLength;	__u8  bDescriptorType;	__u8  bDescriptorSubtype;	__u8  bcdMSC[2];	__le16 wTotalLength;} __attribute__ ((packed));struct usb_ms_endpoint_descriptor {	__u8  bLength;	__u8  bDescriptorType;	__u8  bDescriptorSubtype;	__u8  bNumEmbMIDIJack;	__u8  baAssocJackID[0];} __attribute__ ((packed));struct snd_usb_midi_in_endpoint;struct snd_usb_midi_out_endpoint;struct snd_usb_midi_endpoint;struct usb_protocol_ops {	void (*input)(struct snd_usb_midi_in_endpoint*, uint8_t*, int);	void (*output)(struct snd_usb_midi_out_endpoint*);	void (*output_packet)(struct urb*, uint8_t, uint8_t, uint8_t, uint8_t);	void (*init_out_endpoint)(struct snd_usb_midi_out_endpoint*);	void (*finish_out_endpoint)(struct snd_usb_midi_out_endpoint*);};struct snd_usb_midi {	struct snd_usb_audio *chip;	struct usb_interface *iface;	const struct snd_usb_audio_quirk *quirk;	struct snd_rawmidi *rmidi;	struct usb_protocol_ops* usb_protocol_ops;	struct list_head list;	struct timer_list error_timer;	struct snd_usb_midi_endpoint {		struct snd_usb_midi_out_endpoint *out;		struct snd_usb_midi_in_endpoint *in;	} endpoints[MIDI_MAX_ENDPOINTS];	unsigned long input_triggered;};struct snd_usb_midi_out_endpoint {	struct snd_usb_midi* umidi;	struct urb* urb;	int urb_active;	int max_transfer;		/* size of urb buffer */	struct tasklet_struct tasklet;	spinlock_t buffer_lock;	struct usbmidi_out_port {		struct snd_usb_midi_out_endpoint* ep;		struct snd_rawmidi_substream *substream;		int active;		uint8_t cable;		/* cable number << 4 */		uint8_t state;#define STATE_UNKNOWN	0#define STATE_1PARAM	1#define STATE_2PARAM_1	2#define STATE_2PARAM_2	3#define STATE_SYSEX_0	4#define STATE_SYSEX_1	5#define STATE_SYSEX_2	6		uint8_t data[2];	} ports[0x10];	int current_port;};struct snd_usb_midi_in_endpoint {	struct snd_usb_midi* umidi;	struct urb* urb;	struct usbmidi_in_port {		struct snd_rawmidi_substream *substream;		u8 running_status_length;	} ports[0x10];	u8 seen_f5;	u8 error_resubmit;	int current_port;};static void snd_usbmidi_do_output(struct snd_usb_midi_out_endpoint* ep);static const uint8_t snd_usbmidi_cin_length[] = {	0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1};/* * Submits the URB, with error handling. */static int snd_usbmidi_submit_urb(struct urb* urb, gfp_t flags){	int err = usb_submit_urb(urb, flags);	if (err < 0 && err != -ENODEV)		snd_printk(KERN_ERR "usb_submit_urb: %d\n", err);	return err;}/* * Error handling for URB completion functions. */static int snd_usbmidi_urb_error(int status){	switch (status) {	/* manually unlinked, or device gone */	case -ENOENT:	case -ECONNRESET:	case -ESHUTDOWN:	case -ENODEV:		return -ENODEV;	/* errors that might occur during unplugging */	case -EPROTO:	case -ETIME:	case -EILSEQ:		return -EIO;	default:		snd_printk(KERN_ERR "urb status %d\n", status);		return 0; /* continue */	}}/* * Receives a chunk of MIDI data. */static void snd_usbmidi_input_data(struct snd_usb_midi_in_endpoint* ep, int portidx,				   uint8_t* data, int length){	struct usbmidi_in_port* port = &ep->ports[portidx];	if (!port->substream) {		snd_printd("unexpected port %d!\n", portidx);		return;	}	if (!test_bit(port->substream->number, &ep->umidi->input_triggered))		return;	snd_rawmidi_receive(port->substream, data, length);}#ifdef DUMP_PACKETSstatic void dump_urb(const char *type, const u8 *data, int length){	snd_printk(KERN_DEBUG "%s packet: [", type);	for (; length > 0; ++data, --length)		printk(" %02x", *data);	printk(" ]\n");}#else#define dump_urb(type, data, length) /* nothing */#endif/* * Processes the data read from the device. */static void snd_usbmidi_in_urb_complete(struct urb* urb){	struct snd_usb_midi_in_endpoint* ep = urb->context;	if (urb->status == 0) {		dump_urb("received", urb->transfer_buffer, urb->actual_length);		ep->umidi->usb_protocol_ops->input(ep, urb->transfer_buffer,						   urb->actual_length);	} else {		int err = snd_usbmidi_urb_error(urb->status);		if (err < 0) {			if (err != -ENODEV) {				ep->error_resubmit = 1;				mod_timer(&ep->umidi->error_timer,					  jiffies + ERROR_DELAY_JIFFIES);			}			return;		}	}	urb->dev = ep->umidi->chip->dev;	snd_usbmidi_submit_urb(urb, GFP_ATOMIC);}static void snd_usbmidi_out_urb_complete(struct urb* urb){	struct snd_usb_midi_out_endpoint* ep = urb->context;	spin_lock(&ep->buffer_lock);	ep->urb_active = 0;	spin_unlock(&ep->buffer_lock);	if (urb->status < 0) {		int err = snd_usbmidi_urb_error(urb->status);		if (err < 0) {			if (err != -ENODEV)				mod_timer(&ep->umidi->error_timer,					  jiffies + ERROR_DELAY_JIFFIES);			return;		}	}	snd_usbmidi_do_output(ep);}/* * This is called when some data should be transferred to the device * (from one or more substreams). */static void snd_usbmidi_do_output(struct snd_usb_midi_out_endpoint* ep){	struct urb* urb = ep->urb;	unsigned long flags;	spin_lock_irqsave(&ep->buffer_lock, flags);	if (ep->urb_active || ep->umidi->chip->shutdown) {		spin_unlock_irqrestore(&ep->buffer_lock, flags);		return;	}	urb->transfer_buffer_length = 0;	ep->umidi->usb_protocol_ops->output(ep);	if (urb->transfer_buffer_length > 0) {		dump_urb("sending", urb->transfer_buffer,			 urb->transfer_buffer_length);		urb->dev = ep->umidi->chip->dev;		ep->urb_active = snd_usbmidi_submit_urb(urb, GFP_ATOMIC) >= 0;	}	spin_unlock_irqrestore(&ep->buffer_lock, flags);}static void snd_usbmidi_out_tasklet(unsigned long data){	struct snd_usb_midi_out_endpoint* ep = (struct snd_usb_midi_out_endpoint *) data;	snd_usbmidi_do_output(ep);}/* called after transfers had been interrupted due to some USB error */static void snd_usbmidi_error_timer(unsigned long data){	struct snd_usb_midi *umidi = (struct snd_usb_midi *)data;	int i;	for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {		struct snd_usb_midi_in_endpoint *in = umidi->endpoints[i].in;		if (in && in->error_resubmit) {			in->error_resubmit = 0;			in->urb->dev = umidi->chip->dev;			snd_usbmidi_submit_urb(in->urb, GFP_ATOMIC);		}		if (umidi->endpoints[i].out)			snd_usbmidi_do_output(umidi->endpoints[i].out);	}}/* helper function to send static data that may not DMA-able */static int send_bulk_static_data(struct snd_usb_midi_out_endpoint* ep,				 const void *data, int len){	int err;	void *buf = kmemdup(data, len, GFP_KERNEL);	if (!buf)		return -ENOMEM;	dump_urb("sending", buf, len);	err = usb_bulk_msg(ep->umidi->chip->dev, ep->urb->pipe, buf, len,			   NULL, 250);	kfree(buf);	return err;}/* * Standard USB MIDI protocol: see the spec. * Midiman protocol: like the standard protocol, but the control byte is the * fourth byte in each packet, and uses length instead of CIN. */static void snd_usbmidi_standard_input(struct snd_usb_midi_in_endpoint* ep,				       uint8_t* buffer, int buffer_length){	int i;	for (i = 0; i + 3 < buffer_length; i += 4)		if (buffer[i] != 0) {			int cable = buffer[i] >> 4;			int length = snd_usbmidi_cin_length[buffer[i] & 0x0f];			snd_usbmidi_input_data(ep, cable, &buffer[i + 1], length);		}}static void snd_usbmidi_midiman_input(struct snd_usb_midi_in_endpoint* ep,				      uint8_t* buffer, int buffer_length){	int i;	for (i = 0; i + 3 < buffer_length; i += 4)		if (buffer[i + 3] != 0) {			int port = buffer[i + 3] >> 4;			int length = buffer[i + 3] & 3;			snd_usbmidi_input_data(ep, port, &buffer[i], length);		}}/* * Buggy M-Audio device: running status on input results in a packet that has * the data bytes but not the status byte and that is marked with CIN 4. */static void snd_usbmidi_maudio_broken_running_status_input(					struct snd_usb_midi_in_endpoint* ep,					uint8_t* buffer, int buffer_length){	int i;	for (i = 0; i + 3 < buffer_length; i += 4)		if (buffer[i] != 0) {			int cable = buffer[i] >> 4;			u8 cin = buffer[i] & 0x0f;			struct usbmidi_in_port *port = &ep->ports[cable];			int length;						length = snd_usbmidi_cin_length[cin];			if (cin == 0xf && buffer[i + 1] >= 0xf8)				; /* realtime msg: no running status change */			else if (cin >= 0x8 && cin <= 0xe)				/* channel msg */				port->running_status_length = length - 1;			else if (cin == 0x4 &&				 port->running_status_length != 0 &&				 buffer[i + 1] < 0x80)				/* CIN 4 that is not a SysEx */				length = port->running_status_length;			else				/*				 * All other msgs cannot begin running status.				 * (A channel msg sent as two or three CIN 0xF				 * packets could in theory, but this device				 * doesn't use this format.)				 */				port->running_status_length = 0;			snd_usbmidi_input_data(ep, cable, &buffer[i + 1], length);		}}/* * CME protocol: like the standard protocol, but SysEx commands are sent as a * single USB packet preceded by a 0x0F byte. */static void snd_usbmidi_cme_input(struct snd_usb_midi_in_endpoint *ep,				  uint8_t *buffer, int buffer_length){	if (buffer_length < 2 || (buffer[0] & 0x0f) != 0x0f)		snd_usbmidi_standard_input(ep, buffer, buffer_length);	else		snd_usbmidi_input_data(ep, buffer[0] >> 4,				       &buffer[1], buffer_length - 1);}/* * Adds one USB MIDI packet to the output buffer. */static void snd_usbmidi_output_standard_packet(struct urb* urb, uint8_t p0,					       uint8_t p1, uint8_t p2, uint8_t p3){	uint8_t* buf = (uint8_t*)urb->transfer_buffer + urb->transfer_buffer_length;	buf[0] = p0;	buf[1] = p1;	buf[2] = p2;	buf[3] = p3;	urb->transfer_buffer_length += 4;}/* * Adds one Midiman packet to the output buffer. */static void snd_usbmidi_output_midiman_packet(struct urb* urb, uint8_t p0,					      uint8_t p1, uint8_t p2, uint8_t p3){	uint8_t* buf = (uint8_t*)urb->transfer_buffer + urb->transfer_buffer_length;

⌨️ 快捷键说明

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