📄 gmidi.c
字号:
/* * gmidi.c -- USB MIDI Gadget Driver * * Copyright (C) 2006 Thumtronics Pty Ltd. * Developed for Thumtronics by Grey Innovation * Ben Williamson <ben.williamson@greyinnovation.com> * * This software is distributed under the terms of the GNU General Public * License ("GPL") version 2, as published by the Free Software Foundation. * * This code is based in part on: * * Gadget Zero driver, Copyright (C) 2003-2004 David Brownell. * USB Audio driver, Copyright (C) 2002 by Takashi Iwai. * USB MIDI driver, Copyright (C) 2002-2005 Clemens Ladisch. * * Refer to the USB Device Class Definition for MIDI Devices: * http://www.usb.org/developers/devclass_docs/midi10.pdf *//* #define VERBOSE_DEBUG */#include <linux/kernel.h>#include <linux/utsname.h>#include <linux/device.h>#include <sound/driver.h>#include <sound/core.h>#include <sound/initval.h>#include <sound/rawmidi.h>#include <linux/usb/ch9.h>#include <linux/usb/gadget.h>#include <linux/usb/audio.h>#include <linux/usb/midi.h>#include "gadget_chips.h"MODULE_AUTHOR("Ben Williamson");MODULE_LICENSE("GPL v2");#define DRIVER_VERSION "25 Jul 2006"static const char shortname[] = "g_midi";static const char longname[] = "MIDI Gadget";static int index = SNDRV_DEFAULT_IDX1;static char *id = SNDRV_DEFAULT_STR1;module_param(index, int, 0444);MODULE_PARM_DESC(index, "Index value for the USB MIDI Gadget adapter.");module_param(id, charp, 0444);MODULE_PARM_DESC(id, "ID string for the USB MIDI Gadget adapter.");/* Some systems will want different product identifers published in the * device descriptor, either numbers or strings or both. These string * parameters are in UTF-8 (superset of ASCII's 7 bit characters). */static ushort idVendor;module_param(idVendor, ushort, S_IRUGO);MODULE_PARM_DESC(idVendor, "USB Vendor ID");static ushort idProduct;module_param(idProduct, ushort, S_IRUGO);MODULE_PARM_DESC(idProduct, "USB Product ID");static ushort bcdDevice;module_param(bcdDevice, ushort, S_IRUGO);MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)");static char *iManufacturer;module_param(iManufacturer, charp, S_IRUGO);MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string");static char *iProduct;module_param(iProduct, charp, S_IRUGO);MODULE_PARM_DESC(iProduct, "USB Product string");static char *iSerialNumber;module_param(iSerialNumber, charp, S_IRUGO);MODULE_PARM_DESC(iSerialNumber, "SerialNumber");/* * this version autoconfigures as much as possible, * which is reasonable for most "bulk-only" drivers. */static const char *EP_IN_NAME;static const char *EP_OUT_NAME;/* big enough to hold our biggest descriptor */#define USB_BUFSIZ 256/* This is a gadget, and the IN/OUT naming is from the host's perspective. USB -> OUT endpoint -> rawmidi USB <- IN endpoint <- rawmidi */struct gmidi_in_port { struct gmidi_device* dev; 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];};struct gmidi_device { spinlock_t lock; struct usb_gadget *gadget; struct usb_request *req; /* for control responses */ u8 config; struct usb_ep *in_ep, *out_ep; struct snd_card *card; struct snd_rawmidi *rmidi; struct snd_rawmidi_substream *in_substream; struct snd_rawmidi_substream *out_substream; /* For the moment we only support one port in each direction, but in_port is kept as a separate struct so we can have more later. */ struct gmidi_in_port in_port; unsigned long out_triggered; struct tasklet_struct tasklet;};static void gmidi_transmit(struct gmidi_device* dev, struct usb_request* req);#define DBG(d, fmt, args...) \ dev_dbg(&(d)->gadget->dev , fmt , ## args)#define VDBG(d, fmt, args...) \ dev_vdbg(&(d)->gadget->dev , fmt , ## args)#define ERROR(d, fmt, args...) \ dev_err(&(d)->gadget->dev , fmt , ## args)#define WARN(d, fmt, args...) \ dev_warn(&(d)->gadget->dev , fmt , ## args)#define INFO(d, fmt, args...) \ dev_info(&(d)->gadget->dev , fmt , ## args)static unsigned buflen = 256;static unsigned qlen = 32;module_param(buflen, uint, S_IRUGO);module_param(qlen, uint, S_IRUGO);/* Thanks to Grey Innovation for donating this product ID. * * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! * Instead: allocate your own, using normal USB-IF procedures. */#define DRIVER_VENDOR_NUM 0x17b3 /* Grey Innovation */#define DRIVER_PRODUCT_NUM 0x0004 /* Linux-USB "MIDI Gadget" *//* * DESCRIPTORS ... most are static, but strings and (full) * configuration descriptors are built on demand. */#define STRING_MANUFACTURER 25#define STRING_PRODUCT 42#define STRING_SERIAL 101#define STRING_MIDI_GADGET 250/* We only have the one configuration, it's number 1. */#define GMIDI_CONFIG 1/* We have two interfaces- AudioControl and MIDIStreaming */#define GMIDI_AC_INTERFACE 0#define GMIDI_MS_INTERFACE 1#define GMIDI_NUM_INTERFACES 2DECLARE_USB_AC_HEADER_DESCRIPTOR(1);DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1);DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(1);/* B.1 Device Descriptor */static struct usb_device_descriptor device_desc = { .bLength = USB_DT_DEVICE_SIZE, .bDescriptorType = USB_DT_DEVICE, .bcdUSB = __constant_cpu_to_le16(0x0200), .bDeviceClass = USB_CLASS_PER_INTERFACE, .idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_NUM), .idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_NUM), .iManufacturer = STRING_MANUFACTURER, .iProduct = STRING_PRODUCT, .bNumConfigurations = 1,};/* B.2 Configuration Descriptor */static struct usb_config_descriptor config_desc = { .bLength = USB_DT_CONFIG_SIZE, .bDescriptorType = USB_DT_CONFIG, /* compute wTotalLength on the fly */ .bNumInterfaces = GMIDI_NUM_INTERFACES, .bConfigurationValue = GMIDI_CONFIG, .iConfiguration = STRING_MIDI_GADGET, /* * FIXME: When embedding this driver in a device, * these need to be set to reflect the actual * power properties of the device. Is it selfpowered? */ .bmAttributes = USB_CONFIG_ATT_ONE, .bMaxPower = 1,};/* B.3.1 Standard AC Interface Descriptor */static const struct usb_interface_descriptor ac_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = GMIDI_AC_INTERFACE, .bNumEndpoints = 0, .bInterfaceClass = USB_CLASS_AUDIO, .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, .iInterface = STRING_MIDI_GADGET,};/* B.3.2 Class-Specific AC Interface Descriptor */static const struct usb_ac_header_descriptor_1 ac_header_desc = { .bLength = USB_DT_AC_HEADER_SIZE(1), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = USB_MS_HEADER, .bcdADC = __constant_cpu_to_le16(0x0100), .wTotalLength = USB_DT_AC_HEADER_SIZE(1), .bInCollection = 1, .baInterfaceNr = { [0] = GMIDI_MS_INTERFACE, }};/* B.4.1 Standard MS Interface Descriptor */static const struct usb_interface_descriptor ms_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = GMIDI_MS_INTERFACE, .bNumEndpoints = 2, .bInterfaceClass = USB_CLASS_AUDIO, .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING, .iInterface = STRING_MIDI_GADGET,};/* B.4.2 Class-Specific MS Interface Descriptor */static const struct usb_ms_header_descriptor ms_header_desc = { .bLength = USB_DT_MS_HEADER_SIZE, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = USB_MS_HEADER, .bcdMSC = __constant_cpu_to_le16(0x0100), .wTotalLength = USB_DT_MS_HEADER_SIZE + 2*USB_DT_MIDI_IN_SIZE + 2*USB_DT_MIDI_OUT_SIZE(1),};#define JACK_IN_EMB 1#define JACK_IN_EXT 2#define JACK_OUT_EMB 3#define JACK_OUT_EXT 4/* B.4.3 MIDI IN Jack Descriptors */static const struct usb_midi_in_jack_descriptor jack_in_emb_desc = { .bLength = USB_DT_MIDI_IN_SIZE, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = USB_MS_MIDI_IN_JACK, .bJackType = USB_MS_EMBEDDED, .bJackID = JACK_IN_EMB,};static const struct usb_midi_in_jack_descriptor jack_in_ext_desc = { .bLength = USB_DT_MIDI_IN_SIZE, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = USB_MS_MIDI_IN_JACK, .bJackType = USB_MS_EXTERNAL, .bJackID = JACK_IN_EXT,};/* B.4.4 MIDI OUT Jack Descriptors */static const struct usb_midi_out_jack_descriptor_1 jack_out_emb_desc = { .bLength = USB_DT_MIDI_OUT_SIZE(1), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = USB_MS_MIDI_OUT_JACK, .bJackType = USB_MS_EMBEDDED, .bJackID = JACK_OUT_EMB, .bNrInputPins = 1, .pins = { [0] = { .baSourceID = JACK_IN_EXT, .baSourcePin = 1, } }};static const struct usb_midi_out_jack_descriptor_1 jack_out_ext_desc = { .bLength = USB_DT_MIDI_OUT_SIZE(1), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = USB_MS_MIDI_OUT_JACK, .bJackType = USB_MS_EXTERNAL, .bJackID = JACK_OUT_EXT, .bNrInputPins = 1, .pins = { [0] = { .baSourceID = JACK_IN_EMB, .baSourcePin = 1, } }};/* B.5.1 Standard Bulk OUT Endpoint Descriptor */static struct usb_endpoint_descriptor bulk_out_desc = { .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK,};/* B.5.2 Class-specific MS Bulk OUT Endpoint Descriptor */static const struct usb_ms_endpoint_descriptor_1 ms_out_desc = { .bLength = USB_DT_MS_ENDPOINT_SIZE(1), .bDescriptorType = USB_DT_CS_ENDPOINT, .bDescriptorSubtype = USB_MS_GENERAL, .bNumEmbMIDIJack = 1, .baAssocJackID = { [0] = JACK_IN_EMB, }};/* B.6.1 Standard Bulk IN Endpoint Descriptor */static struct usb_endpoint_descriptor bulk_in_desc = { .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_BULK,};/* B.6.2 Class-specific MS Bulk IN Endpoint Descriptor */static const struct usb_ms_endpoint_descriptor_1 ms_in_desc = { .bLength = USB_DT_MS_ENDPOINT_SIZE(1), .bDescriptorType = USB_DT_CS_ENDPOINT, .bDescriptorSubtype = USB_MS_GENERAL, .bNumEmbMIDIJack = 1, .baAssocJackID = { [0] = JACK_OUT_EMB, }};static const struct usb_descriptor_header *gmidi_function [] = { (struct usb_descriptor_header *)&ac_interface_desc, (struct usb_descriptor_header *)&ac_header_desc, (struct usb_descriptor_header *)&ms_interface_desc, (struct usb_descriptor_header *)&ms_header_desc, (struct usb_descriptor_header *)&jack_in_emb_desc, (struct usb_descriptor_header *)&jack_in_ext_desc, (struct usb_descriptor_header *)&jack_out_emb_desc, (struct usb_descriptor_header *)&jack_out_ext_desc, /* If you add more jacks, update ms_header_desc.wTotalLength */ (struct usb_descriptor_header *)&bulk_out_desc, (struct usb_descriptor_header *)&ms_out_desc, (struct usb_descriptor_header *)&bulk_in_desc, (struct usb_descriptor_header *)&ms_in_desc, NULL,};static char manufacturer[50];static char product_desc[40] = "MIDI Gadget";static char serial_number[20];/* static strings, in UTF-8 */static struct usb_string strings [] = { { STRING_MANUFACTURER, manufacturer, }, { STRING_PRODUCT, product_desc, }, { STRING_SERIAL, serial_number, }, { STRING_MIDI_GADGET, longname, }, { } /* end of list */};static struct usb_gadget_strings stringtab = { .language = 0x0409, /* en-us */ .strings = strings,};static int config_buf(struct usb_gadget *gadget, u8 *buf, u8 type, unsigned index){ int len; /* only one configuration */ if (index != 0) { return -EINVAL; } len = usb_gadget_config_buf(&config_desc, buf, USB_BUFSIZ, gmidi_function); if (len < 0) { return len; } ((struct usb_config_descriptor *)buf)->bDescriptorType = type; return len;}static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length){ struct usb_request *req; req = usb_ep_alloc_request(ep, GFP_ATOMIC); if (req) { req->length = length; req->buf = kmalloc(length, GFP_ATOMIC); if (!req->buf) { usb_ep_free_request(ep, req); req = NULL; } } return req;}static void free_ep_req(struct usb_ep *ep, struct usb_request *req){ kfree(req->buf); usb_ep_free_request(ep, req);}static const uint8_t gmidi_cin_length[] = { 0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1};/* * Receives a chunk of MIDI data. */static void gmidi_read_data(struct usb_ep *ep, int cable, uint8_t *data, int length){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -