| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 1 | /* | 
|  | 2 | * gmidi.c -- USB MIDI Gadget Driver | 
|  | 3 | * | 
|  | 4 | * Copyright (C) 2006 Thumtronics Pty Ltd. | 
|  | 5 | * Developed for Thumtronics by Grey Innovation | 
|  | 6 | * Ben Williamson <ben.williamson@greyinnovation.com> | 
|  | 7 | * | 
|  | 8 | * This software is distributed under the terms of the GNU General Public | 
|  | 9 | * License ("GPL") version 2, as published by the Free Software Foundation. | 
|  | 10 | * | 
|  | 11 | * This code is based in part on: | 
|  | 12 | * | 
|  | 13 | * Gadget Zero driver, Copyright (C) 2003-2004 David Brownell. | 
|  | 14 | * USB Audio driver, Copyright (C) 2002 by Takashi Iwai. | 
|  | 15 | * USB MIDI driver, Copyright (C) 2002-2005 Clemens Ladisch. | 
|  | 16 | * | 
|  | 17 | * Refer to the USB Device Class Definition for MIDI Devices: | 
|  | 18 | * http://www.usb.org/developers/devclass_docs/midi10.pdf | 
|  | 19 | */ | 
|  | 20 |  | 
| David Brownell | 8c07021 | 2007-08-02 00:01:27 -0700 | [diff] [blame] | 21 | /* #define VERBOSE_DEBUG */ | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 22 |  | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 23 | #include <linux/kernel.h> | 
| Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 24 | #include <linux/slab.h> | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 25 | #include <linux/utsname.h> | 
| Paul Gortmaker | 6eb0de8 | 2011-07-03 16:09:31 -0400 | [diff] [blame] | 26 | #include <linux/module.h> | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 27 | #include <linux/device.h> | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 28 |  | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 29 | #include <sound/core.h> | 
|  | 30 | #include <sound/initval.h> | 
|  | 31 | #include <sound/rawmidi.h> | 
|  | 32 |  | 
| David Brownell | 5f84813 | 2006-12-16 15:34:53 -0800 | [diff] [blame] | 33 | #include <linux/usb/ch9.h> | 
| David Brownell | 9454a57 | 2007-10-04 18:05:17 -0700 | [diff] [blame] | 34 | #include <linux/usb/gadget.h> | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 35 | #include <linux/usb/audio.h> | 
|  | 36 | #include <linux/usb/midi.h> | 
|  | 37 |  | 
|  | 38 | #include "gadget_chips.h" | 
|  | 39 |  | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 40 | #include "composite.c" | 
| David Brownell | 77f754c | 2008-08-18 17:42:04 -0700 | [diff] [blame] | 41 | #include "usbstring.c" | 
|  | 42 | #include "config.c" | 
|  | 43 | #include "epautoconf.c" | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 44 | #include "f_midi.c" | 
| David Brownell | 77f754c | 2008-08-18 17:42:04 -0700 | [diff] [blame] | 45 |  | 
|  | 46 | /*-------------------------------------------------------------------------*/ | 
|  | 47 |  | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 48 | MODULE_AUTHOR("Ben Williamson"); | 
|  | 49 | MODULE_LICENSE("GPL v2"); | 
|  | 50 |  | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 51 | static const char shortname[] = "g_midi"; | 
|  | 52 | static const char longname[] = "MIDI Gadget"; | 
|  | 53 |  | 
|  | 54 | static int index = SNDRV_DEFAULT_IDX1; | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 55 | module_param(index, int, S_IRUGO); | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 56 | MODULE_PARM_DESC(index, "Index value for the USB MIDI Gadget adapter."); | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 57 |  | 
|  | 58 | static char *id = SNDRV_DEFAULT_STR1; | 
|  | 59 | module_param(id, charp, S_IRUGO); | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 60 | MODULE_PARM_DESC(id, "ID string for the USB MIDI Gadget adapter."); | 
|  | 61 |  | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 62 | static unsigned int buflen = 256; | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 63 | module_param(buflen, uint, S_IRUGO); | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 64 | MODULE_PARM_DESC(buflen, "MIDI buffer length"); | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 65 |  | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 66 | static unsigned int qlen = 32; | 
|  | 67 | module_param(qlen, uint, S_IRUGO); | 
|  | 68 | MODULE_PARM_DESC(qlen, "USB read request queue length"); | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 69 |  | 
| Daniel Mack | c8933c3 | 2011-09-28 16:41:34 +0200 | [diff] [blame] | 70 | static unsigned int in_ports = 1; | 
|  | 71 | module_param(in_ports, uint, S_IRUGO); | 
|  | 72 | MODULE_PARM_DESC(in_ports, "Number of MIDI input ports"); | 
|  | 73 |  | 
|  | 74 | static unsigned int out_ports = 1; | 
|  | 75 | module_param(out_ports, uint, S_IRUGO); | 
|  | 76 | MODULE_PARM_DESC(out_ports, "Number of MIDI output ports"); | 
|  | 77 |  | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 78 | /* Thanks to Grey Innovation for donating this product ID. | 
|  | 79 | * | 
|  | 80 | * DO NOT REUSE THESE IDs with a protocol-incompatible driver!!  Ever!! | 
|  | 81 | * Instead:  allocate your own, using normal USB-IF procedures. | 
|  | 82 | */ | 
|  | 83 | #define DRIVER_VENDOR_NUM	0x17b3		/* Grey Innovation */ | 
|  | 84 | #define DRIVER_PRODUCT_NUM	0x0004		/* Linux-USB "MIDI Gadget" */ | 
|  | 85 |  | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 86 | /* string IDs are assigned dynamically */ | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 87 |  | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 88 | #define STRING_MANUFACTURER_IDX		0 | 
|  | 89 | #define STRING_PRODUCT_IDX		1 | 
|  | 90 | #define STRING_DESCRIPTION_IDX		2 | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 91 |  | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 92 | static struct usb_device_descriptor device_desc = { | 
|  | 93 | .bLength =		USB_DT_DEVICE_SIZE, | 
|  | 94 | .bDescriptorType =	USB_DT_DEVICE, | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 95 | .bcdUSB =		__constant_cpu_to_le16(0x0200), | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 96 | .bDeviceClass =		USB_CLASS_PER_INTERFACE, | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 97 | .idVendor =		__constant_cpu_to_le16(DRIVER_VENDOR_NUM), | 
|  | 98 | .idProduct =		__constant_cpu_to_le16(DRIVER_PRODUCT_NUM), | 
|  | 99 | /* .iManufacturer =	DYNAMIC */ | 
|  | 100 | /* .iProduct =		DYNAMIC */ | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 101 | .bNumConfigurations =	1, | 
|  | 102 | }; | 
|  | 103 |  | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 104 | static struct usb_string strings_dev[] = { | 
|  | 105 | [STRING_MANUFACTURER_IDX].s	= "Grey Innovation", | 
|  | 106 | [STRING_PRODUCT_IDX].s		= "MIDI Gadget", | 
|  | 107 | [STRING_DESCRIPTION_IDX].s	= "MIDI", | 
|  | 108 | {  } /* end of list */ | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 109 | }; | 
|  | 110 |  | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 111 | static struct usb_gadget_strings stringtab_dev = { | 
|  | 112 | .language	= 0x0409,	/* en-us */ | 
|  | 113 | .strings	= strings_dev, | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 114 | }; | 
|  | 115 |  | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 116 | static struct usb_gadget_strings *dev_strings[] = { | 
|  | 117 | &stringtab_dev, | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 118 | NULL, | 
|  | 119 | }; | 
|  | 120 |  | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 121 | static int __exit midi_unbind(struct usb_composite_dev *dev) | 
|  | 122 | { | 
|  | 123 | return 0; | 
|  | 124 | } | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 125 |  | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 126 | static struct usb_configuration midi_config = { | 
|  | 127 | .label		= "MIDI Gadget", | 
|  | 128 | .bConfigurationValue = 1, | 
|  | 129 | /* .iConfiguration = DYNAMIC */ | 
|  | 130 | .bmAttributes	= USB_CONFIG_ATT_ONE, | 
|  | 131 | .bMaxPower	= CONFIG_USB_GADGET_VBUS_DRAW / 2, | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 132 | }; | 
|  | 133 |  | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 134 | static int __init midi_bind_config(struct usb_configuration *c) | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 135 | { | 
| Daniel Mack | c8933c3 | 2011-09-28 16:41:34 +0200 | [diff] [blame] | 136 | return f_midi_bind_config(c, index, id, | 
|  | 137 | in_ports, out_ports, | 
|  | 138 | buflen, qlen); | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 139 | } | 
|  | 140 |  | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 141 | static int __init midi_bind(struct usb_composite_dev *cdev) | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 142 | { | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 143 | struct usb_gadget *gadget = cdev->gadget; | 
|  | 144 | int gcnum, status; | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 145 |  | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 146 | status = usb_string_id(cdev); | 
|  | 147 | if (status < 0) | 
|  | 148 | return status; | 
|  | 149 | strings_dev[STRING_MANUFACTURER_IDX].id = status; | 
|  | 150 | device_desc.iManufacturer = status; | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 151 |  | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 152 | status = usb_string_id(cdev); | 
|  | 153 | if (status < 0) | 
|  | 154 | return status; | 
|  | 155 | strings_dev[STRING_PRODUCT_IDX].id = status; | 
|  | 156 | device_desc.iProduct = status; | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 157 |  | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 158 | /* config description */ | 
|  | 159 | status = usb_string_id(cdev); | 
|  | 160 | if (status < 0) | 
|  | 161 | return status; | 
|  | 162 | strings_dev[STRING_DESCRIPTION_IDX].id = status; | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 163 |  | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 164 | midi_config.iConfiguration = status; | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 165 |  | 
|  | 166 | gcnum = usb_gadget_controller_number(gadget); | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 167 | if (gcnum < 0) { | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 168 | /* gmidi is so simple (no altsettings) that | 
|  | 169 | * it SHOULD NOT have problems with bulk-capable hardware. | 
|  | 170 | * so warn about unrecognized controllers, don't panic. | 
|  | 171 | */ | 
| David Brownell | 0027492 | 2007-11-19 12:58:36 -0800 | [diff] [blame] | 172 | pr_warning("%s: controller '%s' not recognized\n", | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 173 | __func__, gadget->name); | 
| Harvey Harrison | 551509d | 2009-02-11 14:11:36 -0800 | [diff] [blame] | 174 | device_desc.bcdDevice = cpu_to_le16(0x9999); | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 175 | } else { | 
|  | 176 | device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum); | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 177 | } | 
|  | 178 |  | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 179 | status = usb_add_config(cdev, &midi_config, midi_bind_config); | 
|  | 180 | if (status < 0) | 
|  | 181 | return status; | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 182 |  | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 183 | pr_info("%s\n", longname); | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 184 | return 0; | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 185 | } | 
|  | 186 |  | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 187 | static struct usb_composite_driver midi_driver = { | 
|  | 188 | .name		= (char *) longname, | 
|  | 189 | .dev		= &device_desc, | 
|  | 190 | .strings	= dev_strings, | 
|  | 191 | .max_speed	= USB_SPEED_HIGH, | 
|  | 192 | .unbind		= __exit_p(midi_unbind), | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 193 | }; | 
|  | 194 |  | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 195 | static int __init midi_init(void) | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 196 | { | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 197 | return usb_composite_probe(&midi_driver, midi_bind); | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 198 | } | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 199 | module_init(midi_init); | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 200 |  | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 201 | static void __exit midi_cleanup(void) | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 202 | { | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 203 | usb_composite_unregister(&midi_driver); | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 204 | } | 
| Daniel Mack | 2672eea | 2011-09-28 16:41:33 +0200 | [diff] [blame] | 205 | module_exit(midi_cleanup); | 
| Ben Williamson | f2ebf92c | 2006-08-01 11:28:16 +1000 | [diff] [blame] | 206 |  |