18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * gmidi.c -- USB MIDI Gadget Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2006 Thumtronics Pty Ltd. 68c2ecf20Sopenharmony_ci * Developed for Thumtronics by Grey Innovation 78c2ecf20Sopenharmony_ci * Ben Williamson <ben.williamson@greyinnovation.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This code is based in part on: 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Gadget Zero driver, Copyright (C) 2003-2004 David Brownell. 128c2ecf20Sopenharmony_ci * USB Audio driver, Copyright (C) 2002 by Takashi Iwai. 138c2ecf20Sopenharmony_ci * USB MIDI driver, Copyright (C) 2002-2005 Clemens Ladisch. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * Refer to the USB Device Class Definition for MIDI Devices: 168c2ecf20Sopenharmony_ci * http://www.usb.org/developers/devclass_docs/midi10.pdf 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* #define VERBOSE_DEBUG */ 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <linux/kernel.h> 228c2ecf20Sopenharmony_ci#include <linux/module.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <sound/initval.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <linux/usb/composite.h> 278c2ecf20Sopenharmony_ci#include <linux/usb/gadget.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include "u_midi.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ben Williamson"); 348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic const char longname[] = "MIDI Gadget"; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ciUSB_GADGET_COMPOSITE_OPTIONS(); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int index = SNDRV_DEFAULT_IDX1; 418c2ecf20Sopenharmony_cimodule_param(index, int, S_IRUGO); 428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for the USB MIDI Gadget adapter."); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic char *id = SNDRV_DEFAULT_STR1; 458c2ecf20Sopenharmony_cimodule_param(id, charp, S_IRUGO); 468c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for the USB MIDI Gadget adapter."); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic unsigned int buflen = 512; 498c2ecf20Sopenharmony_cimodule_param(buflen, uint, S_IRUGO); 508c2ecf20Sopenharmony_ciMODULE_PARM_DESC(buflen, "MIDI buffer length"); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic unsigned int qlen = 32; 538c2ecf20Sopenharmony_cimodule_param(qlen, uint, S_IRUGO); 548c2ecf20Sopenharmony_ciMODULE_PARM_DESC(qlen, "USB read and write request queue length"); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic unsigned int in_ports = 1; 578c2ecf20Sopenharmony_cimodule_param(in_ports, uint, S_IRUGO); 588c2ecf20Sopenharmony_ciMODULE_PARM_DESC(in_ports, "Number of MIDI input ports"); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic unsigned int out_ports = 1; 618c2ecf20Sopenharmony_cimodule_param(out_ports, uint, S_IRUGO); 628c2ecf20Sopenharmony_ciMODULE_PARM_DESC(out_ports, "Number of MIDI output ports"); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* Thanks to Grey Innovation for donating this product ID. 658c2ecf20Sopenharmony_ci * 668c2ecf20Sopenharmony_ci * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! 678c2ecf20Sopenharmony_ci * Instead: allocate your own, using normal USB-IF procedures. 688c2ecf20Sopenharmony_ci */ 698c2ecf20Sopenharmony_ci#define DRIVER_VENDOR_NUM 0x17b3 /* Grey Innovation */ 708c2ecf20Sopenharmony_ci#define DRIVER_PRODUCT_NUM 0x0004 /* Linux-USB "MIDI Gadget" */ 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* string IDs are assigned dynamically */ 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic struct usb_device_descriptor device_desc = { 778c2ecf20Sopenharmony_ci .bLength = USB_DT_DEVICE_SIZE, 788c2ecf20Sopenharmony_ci .bDescriptorType = USB_DT_DEVICE, 798c2ecf20Sopenharmony_ci /* .bcdUSB = DYNAMIC */ 808c2ecf20Sopenharmony_ci .bDeviceClass = USB_CLASS_PER_INTERFACE, 818c2ecf20Sopenharmony_ci .idVendor = cpu_to_le16(DRIVER_VENDOR_NUM), 828c2ecf20Sopenharmony_ci .idProduct = cpu_to_le16(DRIVER_PRODUCT_NUM), 838c2ecf20Sopenharmony_ci /* .iManufacturer = DYNAMIC */ 848c2ecf20Sopenharmony_ci /* .iProduct = DYNAMIC */ 858c2ecf20Sopenharmony_ci .bNumConfigurations = 1, 868c2ecf20Sopenharmony_ci}; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic struct usb_string strings_dev[] = { 898c2ecf20Sopenharmony_ci [USB_GADGET_MANUFACTURER_IDX].s = "Grey Innovation", 908c2ecf20Sopenharmony_ci [USB_GADGET_PRODUCT_IDX].s = "MIDI Gadget", 918c2ecf20Sopenharmony_ci [USB_GADGET_SERIAL_IDX].s = "", 928c2ecf20Sopenharmony_ci [STRING_DESCRIPTION_IDX].s = "MIDI", 938c2ecf20Sopenharmony_ci { } /* end of list */ 948c2ecf20Sopenharmony_ci}; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic struct usb_gadget_strings stringtab_dev = { 978c2ecf20Sopenharmony_ci .language = 0x0409, /* en-us */ 988c2ecf20Sopenharmony_ci .strings = strings_dev, 998c2ecf20Sopenharmony_ci}; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic struct usb_gadget_strings *dev_strings[] = { 1028c2ecf20Sopenharmony_ci &stringtab_dev, 1038c2ecf20Sopenharmony_ci NULL, 1048c2ecf20Sopenharmony_ci}; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic struct usb_function_instance *fi_midi; 1078c2ecf20Sopenharmony_cistatic struct usb_function *f_midi; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic int midi_unbind(struct usb_composite_dev *dev) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci usb_put_function(f_midi); 1128c2ecf20Sopenharmony_ci usb_put_function_instance(fi_midi); 1138c2ecf20Sopenharmony_ci return 0; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic struct usb_configuration midi_config = { 1178c2ecf20Sopenharmony_ci .label = "MIDI Gadget", 1188c2ecf20Sopenharmony_ci .bConfigurationValue = 1, 1198c2ecf20Sopenharmony_ci /* .iConfiguration = DYNAMIC */ 1208c2ecf20Sopenharmony_ci .bmAttributes = USB_CONFIG_ATT_ONE, 1218c2ecf20Sopenharmony_ci .MaxPower = CONFIG_USB_GADGET_VBUS_DRAW, 1228c2ecf20Sopenharmony_ci}; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic int midi_bind_config(struct usb_configuration *c) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci int status; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci f_midi = usb_get_function(fi_midi); 1298c2ecf20Sopenharmony_ci if (IS_ERR(f_midi)) 1308c2ecf20Sopenharmony_ci return PTR_ERR(f_midi); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci status = usb_add_function(c, f_midi); 1338c2ecf20Sopenharmony_ci if (status < 0) { 1348c2ecf20Sopenharmony_ci usb_put_function(f_midi); 1358c2ecf20Sopenharmony_ci return status; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return 0; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic int midi_bind(struct usb_composite_dev *cdev) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci struct f_midi_opts *midi_opts; 1448c2ecf20Sopenharmony_ci int status; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci fi_midi = usb_get_function_instance("midi"); 1478c2ecf20Sopenharmony_ci if (IS_ERR(fi_midi)) 1488c2ecf20Sopenharmony_ci return PTR_ERR(fi_midi); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci midi_opts = container_of(fi_midi, struct f_midi_opts, func_inst); 1518c2ecf20Sopenharmony_ci midi_opts->index = index; 1528c2ecf20Sopenharmony_ci midi_opts->id = id; 1538c2ecf20Sopenharmony_ci midi_opts->in_ports = in_ports; 1548c2ecf20Sopenharmony_ci midi_opts->out_ports = out_ports; 1558c2ecf20Sopenharmony_ci midi_opts->buflen = buflen; 1568c2ecf20Sopenharmony_ci midi_opts->qlen = qlen; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci status = usb_string_ids_tab(cdev, strings_dev); 1598c2ecf20Sopenharmony_ci if (status < 0) 1608c2ecf20Sopenharmony_ci goto put; 1618c2ecf20Sopenharmony_ci device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; 1628c2ecf20Sopenharmony_ci device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; 1638c2ecf20Sopenharmony_ci midi_config.iConfiguration = strings_dev[STRING_DESCRIPTION_IDX].id; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci status = usb_add_config(cdev, &midi_config, midi_bind_config); 1668c2ecf20Sopenharmony_ci if (status < 0) 1678c2ecf20Sopenharmony_ci goto put; 1688c2ecf20Sopenharmony_ci usb_composite_overwrite_options(cdev, &coverwrite); 1698c2ecf20Sopenharmony_ci pr_info("%s\n", longname); 1708c2ecf20Sopenharmony_ci return 0; 1718c2ecf20Sopenharmony_ciput: 1728c2ecf20Sopenharmony_ci usb_put_function_instance(fi_midi); 1738c2ecf20Sopenharmony_ci return status; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic struct usb_composite_driver midi_driver = { 1778c2ecf20Sopenharmony_ci .name = longname, 1788c2ecf20Sopenharmony_ci .dev = &device_desc, 1798c2ecf20Sopenharmony_ci .strings = dev_strings, 1808c2ecf20Sopenharmony_ci .max_speed = USB_SPEED_HIGH, 1818c2ecf20Sopenharmony_ci .bind = midi_bind, 1828c2ecf20Sopenharmony_ci .unbind = midi_unbind, 1838c2ecf20Sopenharmony_ci}; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cimodule_usb_composite_driver(midi_driver); 186