usbmidi.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,273 行 · 第 1/3 页
C
1,273 行
/* * usbmidi.c - ALSA USB MIDI driver * * Copyright (c) 2002-2004 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/interrupt.h>#include <linux/spinlock.h>#include <linux/string.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/usb.h>#include <sound/core.h>#include <sound/minors.h>#include <sound/rawmidi.h>#include "usbaudio.h"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]; __u16 wTotalLength;} __attribute__ ((packed));struct usb_ms_endpoint_descriptor { __u8 bLength; __u8 bDescriptorType; __u8 bDescriptorSubtype; __u8 bNumEmbMIDIJack; __u8 baAssocJackID[0];} __attribute__ ((packed));typedef struct snd_usb_midi snd_usb_midi_t;typedef struct snd_usb_midi_endpoint snd_usb_midi_endpoint_t;typedef struct snd_usb_midi_out_endpoint snd_usb_midi_out_endpoint_t;typedef struct snd_usb_midi_in_endpoint snd_usb_midi_in_endpoint_t;typedef struct usbmidi_out_port usbmidi_out_port_t;typedef struct usbmidi_in_port usbmidi_in_port_t;struct snd_usb_midi { snd_usb_audio_t *chip; struct usb_interface *iface; const snd_usb_audio_quirk_t *quirk; snd_rawmidi_t* rmidi; struct list_head list; struct snd_usb_midi_endpoint { snd_usb_midi_out_endpoint_t *out; snd_usb_midi_in_endpoint_t *in; } endpoints[MIDI_MAX_ENDPOINTS];};struct snd_usb_midi_out_endpoint { snd_usb_midi_t* umidi; struct urb* urb; int max_transfer; /* size of urb buffer */ struct tasklet_struct tasklet; spinlock_t buffer_lock; struct usbmidi_out_port { snd_usb_midi_out_endpoint_t* ep; snd_rawmidi_substream_t* 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];};struct snd_usb_midi_in_endpoint { snd_usb_midi_t* umidi; struct urb* urb; struct usbmidi_in_port { snd_rawmidi_substream_t* substream; } ports[0x10];};static void snd_usbmidi_do_output(snd_usb_midi_out_endpoint_t* 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, int 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){ if (status == -ENOENT) return status; /* killed */ if (status == -EILSEQ || status == -ECONNRESET || status == -ETIMEDOUT) return -ENODEV; /* device removed/shutdown */ snd_printk(KERN_ERR "urb status %d\n", status); return 0; /* continue */}/* * Receives a USB MIDI packet. */static void snd_usbmidi_input_packet(snd_usb_midi_in_endpoint_t* ep, uint8_t packet[4]){ int cable = packet[0] >> 4; usbmidi_in_port_t* port = &ep->ports[cable]; if (!port->substream) { snd_printd("unexpected port %d!\n", cable); return; } if (!port->substream->runtime || !port->substream->runtime->trigger) return; snd_rawmidi_receive(port->substream, &packet[1], snd_usbmidi_cin_length[packet[0] & 0x0f]);}/* * Processes the data read from the device. */static void snd_usbmidi_in_urb_complete(struct urb* urb, struct pt_regs *regs){ snd_usb_midi_in_endpoint_t* ep = urb->context; if (urb->status == 0) { uint8_t* buffer = (uint8_t*)ep->urb->transfer_buffer; int i; for (i = 0; i + 4 <= urb->actual_length; i += 4) if (buffer[i] != 0) snd_usbmidi_input_packet(ep, &buffer[i]); } else { if (snd_usbmidi_urb_error(urb->status) < 0) return; } if (usb_pipe_needs_resubmit(urb->pipe)) { urb->dev = ep->umidi->chip->dev; snd_usbmidi_submit_urb(urb, GFP_ATOMIC); }}/* * Converts the data read from a Midiman device to standard USB MIDI packets. */static void snd_usbmidi_in_midiman_complete(struct urb* urb, struct pt_regs *regs){ if (urb->status == 0) { uint8_t* buffer = (uint8_t*)urb->transfer_buffer; int i; for (i = 0; i + 4 <= urb->actual_length; i += 4) { if (buffer[i + 3] != 0) { /* * snd_usbmidi_input_packet() doesn't check the * contents of the message, so we simply use * some random CIN with the desired length. */ static const uint8_t cin[4] = { 0x0, 0xf, 0x2, 0x3 }; uint8_t ctl = buffer[i + 3]; buffer[i + 3] = buffer[i + 2]; buffer[i + 2] = buffer[i + 1]; buffer[i + 1] = buffer[i + 0]; buffer[i + 0] = (ctl & 0xf0) | cin[ctl & 3]; } else { buffer[i + 0] = 0; } } } snd_usbmidi_in_urb_complete(urb, regs);}static void snd_usbmidi_out_urb_complete(struct urb* urb, struct pt_regs *regs){ snd_usb_midi_out_endpoint_t* ep = urb->context; if (urb->status < 0) { if (snd_usbmidi_urb_error(urb->status) < 0) return; } snd_usbmidi_do_output(ep);}/* * Converts standard USB MIDI packets to what Midman devices expect. */static void snd_usbmidi_convert_to_midiman(struct urb* urb){ uint8_t* buffer = (uint8_t*)urb->transfer_buffer; int i; for (i = 0; i + 4 <= urb->transfer_buffer_length; i += 4) { uint8_t cin = buffer[i]; buffer[i + 0] = buffer[i + 1]; buffer[i + 1] = buffer[i + 2]; buffer[i + 2] = buffer[i + 3]; buffer[i + 3] = (cin & 0xf0) | snd_usbmidi_cin_length[cin & 0x0f]; }}/* * Adds one USB MIDI packet to the output buffer. */static inline void output_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;}/* * Converts MIDI commands to USB MIDI packets. */static void snd_usbmidi_transmit_byte(usbmidi_out_port_t* port, uint8_t b, struct urb* urb){ uint8_t p0 = port->cable; if (b >= 0xf8) { output_packet(urb, p0 | 0x0f, b, 0, 0); } else if (b >= 0xf0) { switch (b) { case 0xf0: port->data[0] = b; port->state = STATE_SYSEX_1; break; case 0xf1: case 0xf3: port->data[0] = b; port->state = STATE_1PARAM; break; case 0xf2: port->data[0] = b; port->state = STATE_2PARAM_1; break; case 0xf4: case 0xf5: port->state = STATE_UNKNOWN; break; case 0xf6: output_packet(urb, p0 | 0x05, 0xf6, 0, 0); port->state = STATE_UNKNOWN; break; case 0xf7: switch (port->state) { case STATE_SYSEX_0: output_packet(urb, p0 | 0x05, 0xf7, 0, 0); break; case STATE_SYSEX_1: output_packet(urb, p0 | 0x06, port->data[0], 0xf7, 0); break; case STATE_SYSEX_2: output_packet(urb, p0 | 0x07, port->data[0], port->data[1], 0xf7); break; } port->state = STATE_UNKNOWN; break; } } else if (b >= 0x80) { port->data[0] = b; if (b >= 0xc0 && b <= 0xdf) port->state = STATE_1PARAM; else port->state = STATE_2PARAM_1; } else { /* b < 0x80 */ switch (port->state) { case STATE_1PARAM: if (port->data[0] < 0xf0) { p0 |= port->data[0] >> 4; } else { p0 |= 0x02; port->state = STATE_UNKNOWN; } output_packet(urb, p0, port->data[0], b, 0); break; case STATE_2PARAM_1: port->data[1] = b; port->state = STATE_2PARAM_2; break; case STATE_2PARAM_2: if (port->data[0] < 0xf0) { p0 |= port->data[0] >> 4; port->state = STATE_2PARAM_1; } else { p0 |= 0x03; port->state = STATE_UNKNOWN; } output_packet(urb, p0, port->data[0], port->data[1], b); break; case STATE_SYSEX_0: port->data[0] = b; port->state = STATE_SYSEX_1; break; case STATE_SYSEX_1: port->data[1] = b; port->state = STATE_SYSEX_2; break; case STATE_SYSEX_2: output_packet(urb, p0 | 0x04, port->data[0], port->data[1], b); port->state = STATE_SYSEX_0; break; } }}/* * Moves data from one substream buffer to the URB transfer buffer. */static void snd_usbmidi_transmit(snd_usb_midi_out_endpoint_t* ep, int port_idx){ struct urb* urb = ep->urb; usbmidi_out_port_t* port = &ep->ports[port_idx]; while (urb->transfer_buffer_length < ep->max_transfer) { uint8_t b; if (snd_rawmidi_transmit_peek(port->substream, &b, 1) != 1) { port->active = 0; break; } snd_usbmidi_transmit_byte(port, b, urb); snd_rawmidi_transmit_ack(port->substream, 1); }}/* * This is called when some data should be transferred to the device * (from one or more substreams). */static void snd_usbmidi_do_output(snd_usb_midi_out_endpoint_t* ep){ int p; struct urb* urb = ep->urb; unsigned long flags; spin_lock_irqsave(&ep->buffer_lock, flags); if (urb->status == -EINPROGRESS || ep->umidi->chip->shutdown) { spin_unlock_irqrestore(&ep->buffer_lock, flags); return; } urb->transfer_buffer_length = 0; for (p= 0; p < 0x10; ++p) if (ep->ports[p].active) snd_usbmidi_transmit(ep, p); if (urb->transfer_buffer_length > 0) { if (ep->umidi->quirk && ep->umidi->quirk->type == QUIRK_MIDI_MIDIMAN) snd_usbmidi_convert_to_midiman(urb); urb->dev = ep->umidi->chip->dev; snd_usbmidi_submit_urb(urb, GFP_ATOMIC); } spin_unlock_irqrestore(&ep->buffer_lock, flags);}static void snd_usbmidi_out_tasklet(unsigned long data){ snd_usb_midi_out_endpoint_t* ep = (snd_usb_midi_out_endpoint_t *) data;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?