10a76fe7cSHans Petter Selasky /* $FreeBSD$ */ 20a76fe7cSHans Petter Selasky /*- 3*8e06491aSEdward Tomasz Napierala * Copyright (c) 2015 Hans Petter Selasky 4*8e06491aSEdward Tomasz Napierala * Copyright (c) 2018 The FreeBSD Foundation 5*8e06491aSEdward Tomasz Napierala * All rights reserved. 6*8e06491aSEdward Tomasz Napierala * 7*8e06491aSEdward Tomasz Napierala * Portions of this software were developed by Edward Tomasz Napierala 8*8e06491aSEdward Tomasz Napierala * under sponsorship from the FreeBSD Foundation. 90a76fe7cSHans Petter Selasky * 100a76fe7cSHans Petter Selasky * Redistribution and use in source and binary forms, with or without 110a76fe7cSHans Petter Selasky * modification, are permitted provided that the following conditions 120a76fe7cSHans Petter Selasky * are met: 130a76fe7cSHans Petter Selasky * 1. Redistributions of source code must retain the above copyright 140a76fe7cSHans Petter Selasky * notice, this list of conditions and the following disclaimer. 150a76fe7cSHans Petter Selasky * 2. Redistributions in binary form must reproduce the above copyright 160a76fe7cSHans Petter Selasky * notice, this list of conditions and the following disclaimer in the 170a76fe7cSHans Petter Selasky * documentation and/or other materials provided with the distribution. 180a76fe7cSHans Petter Selasky * 190a76fe7cSHans Petter Selasky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 200a76fe7cSHans Petter Selasky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 210a76fe7cSHans Petter Selasky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 220a76fe7cSHans Petter Selasky * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 230a76fe7cSHans Petter Selasky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 240a76fe7cSHans Petter Selasky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 250a76fe7cSHans Petter Selasky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 260a76fe7cSHans Petter Selasky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 270a76fe7cSHans Petter Selasky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 280a76fe7cSHans Petter Selasky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 290a76fe7cSHans Petter Selasky * SUCH DAMAGE. 300a76fe7cSHans Petter Selasky */ 310a76fe7cSHans Petter Selasky 320a76fe7cSHans Petter Selasky /* 330a76fe7cSHans Petter Selasky * This file contains the USB template for an USB MIDI Device. 340a76fe7cSHans Petter Selasky */ 350a76fe7cSHans Petter Selasky 360a76fe7cSHans Petter Selasky #ifdef USB_GLOBAL_INCLUDE_FILE 370a76fe7cSHans Petter Selasky #include USB_GLOBAL_INCLUDE_FILE 380a76fe7cSHans Petter Selasky #else 390a76fe7cSHans Petter Selasky #include <sys/stdint.h> 400a76fe7cSHans Petter Selasky #include <sys/stddef.h> 410a76fe7cSHans Petter Selasky #include <sys/param.h> 420a76fe7cSHans Petter Selasky #include <sys/queue.h> 430a76fe7cSHans Petter Selasky #include <sys/types.h> 440a76fe7cSHans Petter Selasky #include <sys/systm.h> 450a76fe7cSHans Petter Selasky #include <sys/kernel.h> 460a76fe7cSHans Petter Selasky #include <sys/bus.h> 470a76fe7cSHans Petter Selasky #include <sys/module.h> 480a76fe7cSHans Petter Selasky #include <sys/lock.h> 490a76fe7cSHans Petter Selasky #include <sys/mutex.h> 500a76fe7cSHans Petter Selasky #include <sys/condvar.h> 510a76fe7cSHans Petter Selasky #include <sys/sysctl.h> 520a76fe7cSHans Petter Selasky #include <sys/sx.h> 530a76fe7cSHans Petter Selasky #include <sys/unistd.h> 540a76fe7cSHans Petter Selasky #include <sys/callout.h> 550a76fe7cSHans Petter Selasky #include <sys/malloc.h> 560a76fe7cSHans Petter Selasky #include <sys/priv.h> 570a76fe7cSHans Petter Selasky 580a76fe7cSHans Petter Selasky #include <dev/usb/usb.h> 590a76fe7cSHans Petter Selasky #include <dev/usb/usbdi.h> 600a76fe7cSHans Petter Selasky #include <dev/usb/usb_core.h> 61*8e06491aSEdward Tomasz Napierala #include <dev/usb/usb_ioctl.h> 62*8e06491aSEdward Tomasz Napierala #include <dev/usb/usb_util.h> 630a76fe7cSHans Petter Selasky 640a76fe7cSHans Petter Selasky #include <dev/usb/template/usb_template.h> 650a76fe7cSHans Petter Selasky #endif /* USB_GLOBAL_INCLUDE_FILE */ 660a76fe7cSHans Petter Selasky 670a76fe7cSHans Petter Selasky enum { 68*8e06491aSEdward Tomasz Napierala MIDI_LANG_INDEX, 69*8e06491aSEdward Tomasz Napierala MIDI_INTERFACE_INDEX, 70*8e06491aSEdward Tomasz Napierala MIDI_PRODUCT_INDEX, 71*8e06491aSEdward Tomasz Napierala MIDI_MAX_INDEX, 720a76fe7cSHans Petter Selasky }; 730a76fe7cSHans Petter Selasky 74*8e06491aSEdward Tomasz Napierala #define MIDI_DEFAULT_INTERFACE "MIDI interface" 75*8e06491aSEdward Tomasz Napierala #define MIDI_DEFAULT_PRODUCT "MIDI Test Device" 760a76fe7cSHans Petter Selasky 77*8e06491aSEdward Tomasz Napierala static struct usb_string_descriptor midi_interface; 78*8e06491aSEdward Tomasz Napierala static struct usb_string_descriptor midi_product; 790a76fe7cSHans Petter Selasky 80*8e06491aSEdward Tomasz Napierala static struct sysctl_ctx_list midi_ctx_list; 810a76fe7cSHans Petter Selasky 820a76fe7cSHans Petter Selasky /* prototypes */ 830a76fe7cSHans Petter Selasky 840a76fe7cSHans Petter Selasky static const uint8_t midi_desc_raw_0[9] = { 850a76fe7cSHans Petter Selasky 0x09, 0x24, 0x01, 0x00, 0x01, 0x09, 0x00, 0x01, 0x01 860a76fe7cSHans Petter Selasky }; 870a76fe7cSHans Petter Selasky 880a76fe7cSHans Petter Selasky static const void *midi_descs_0[] = { 890a76fe7cSHans Petter Selasky &midi_desc_raw_0, 900a76fe7cSHans Petter Selasky NULL 910a76fe7cSHans Petter Selasky }; 920a76fe7cSHans Petter Selasky 930a76fe7cSHans Petter Selasky static const struct usb_temp_interface_desc midi_iface_0 = { 940a76fe7cSHans Petter Selasky .ppEndpoints = NULL, /* no endpoints */ 950a76fe7cSHans Petter Selasky .ppRawDesc = midi_descs_0, 964ffeccf1SEdward Tomasz Napierala .bInterfaceClass = UICLASS_AUDIO, 974ffeccf1SEdward Tomasz Napierala .bInterfaceSubClass = UISUBCLASS_AUDIOCONTROL, 980a76fe7cSHans Petter Selasky .bInterfaceProtocol = 0, 99*8e06491aSEdward Tomasz Napierala .iInterface = MIDI_INTERFACE_INDEX, 1000a76fe7cSHans Petter Selasky }; 1010a76fe7cSHans Petter Selasky 1020a76fe7cSHans Petter Selasky static const struct usb_temp_packet_size midi_mps = { 1030a76fe7cSHans Petter Selasky .mps[USB_SPEED_LOW] = 8, 1040a76fe7cSHans Petter Selasky .mps[USB_SPEED_FULL] = 64, 1050a76fe7cSHans Petter Selasky .mps[USB_SPEED_HIGH] = 512, 1060a76fe7cSHans Petter Selasky }; 1070a76fe7cSHans Petter Selasky 1080a76fe7cSHans Petter Selasky static const uint8_t midi_desc_raw_7[5] = { 1090a76fe7cSHans Petter Selasky 0x05, 0x25, 0x01, 0x01, 0x01 1100a76fe7cSHans Petter Selasky }; 1110a76fe7cSHans Petter Selasky 1120a76fe7cSHans Petter Selasky static const void *midi_descs_2[] = { 1130a76fe7cSHans Petter Selasky &midi_desc_raw_7, 1140a76fe7cSHans Petter Selasky NULL 1150a76fe7cSHans Petter Selasky }; 1160a76fe7cSHans Petter Selasky 1170a76fe7cSHans Petter Selasky static const struct usb_temp_endpoint_desc midi_bulk_out_ep = { 1180a76fe7cSHans Petter Selasky .ppRawDesc = midi_descs_2, 1190a76fe7cSHans Petter Selasky .pPacketSize = &midi_mps, 1200a76fe7cSHans Petter Selasky .bEndpointAddress = UE_DIR_OUT, 1210a76fe7cSHans Petter Selasky .bmAttributes = UE_BULK, 1220a76fe7cSHans Petter Selasky }; 1230a76fe7cSHans Petter Selasky 1240a76fe7cSHans Petter Selasky static const uint8_t midi_desc_raw_6[5] = { 1250a76fe7cSHans Petter Selasky 0x05, 0x25, 0x01, 0x01, 0x03, 1260a76fe7cSHans Petter Selasky }; 1270a76fe7cSHans Petter Selasky 1280a76fe7cSHans Petter Selasky static const void *midi_descs_3[] = { 1290a76fe7cSHans Petter Selasky &midi_desc_raw_6, 1300a76fe7cSHans Petter Selasky NULL 1310a76fe7cSHans Petter Selasky }; 1320a76fe7cSHans Petter Selasky 1330a76fe7cSHans Petter Selasky static const struct usb_temp_endpoint_desc midi_bulk_in_ep = { 1340a76fe7cSHans Petter Selasky .ppRawDesc = midi_descs_3, 1350a76fe7cSHans Petter Selasky .pPacketSize = &midi_mps, 1360a76fe7cSHans Petter Selasky .bEndpointAddress = UE_DIR_IN, 1370a76fe7cSHans Petter Selasky .bmAttributes = UE_BULK, 1380a76fe7cSHans Petter Selasky }; 1390a76fe7cSHans Petter Selasky 1400a76fe7cSHans Petter Selasky static const struct usb_temp_endpoint_desc *midi_iface_1_ep[] = { 1410a76fe7cSHans Petter Selasky &midi_bulk_out_ep, 1420a76fe7cSHans Petter Selasky &midi_bulk_in_ep, 1430a76fe7cSHans Petter Selasky NULL, 1440a76fe7cSHans Petter Selasky }; 1450a76fe7cSHans Petter Selasky 1460a76fe7cSHans Petter Selasky static const uint8_t midi_desc_raw_1[7] = { 1470a76fe7cSHans Petter Selasky 0x07, 0x24, 0x01, 0x00, 0x01, /* wTotalLength: */ 0x41, 0x00 1480a76fe7cSHans Petter Selasky }; 1490a76fe7cSHans Petter Selasky 1500a76fe7cSHans Petter Selasky static const uint8_t midi_desc_raw_2[6] = { 1510a76fe7cSHans Petter Selasky 0x06, 0x24, 0x02, 0x01, 0x01, 0x00 1520a76fe7cSHans Petter Selasky }; 1530a76fe7cSHans Petter Selasky 1540a76fe7cSHans Petter Selasky static const uint8_t midi_desc_raw_3[6] = { 1550a76fe7cSHans Petter Selasky 0x06, 0x24, 0x02, 0x02, 0x02, 0x00 1560a76fe7cSHans Petter Selasky }; 1570a76fe7cSHans Petter Selasky 1580a76fe7cSHans Petter Selasky static const uint8_t midi_desc_raw_4[9] = { 1590a76fe7cSHans Petter Selasky 0x09, 0x24, 0x03, 0x01, 0x03, 0x01, 0x02, 0x01, 0x00 1600a76fe7cSHans Petter Selasky }; 1610a76fe7cSHans Petter Selasky 1620a76fe7cSHans Petter Selasky static const uint8_t midi_desc_raw_5[9] = { 1630a76fe7cSHans Petter Selasky 0x09, 0x24, 0x03, 0x02, 0x04, 0x01, 0x01, 0x01, 0x00 1640a76fe7cSHans Petter Selasky }; 1650a76fe7cSHans Petter Selasky 1660a76fe7cSHans Petter Selasky static const void *midi_descs_1[] = { 1670a76fe7cSHans Petter Selasky &midi_desc_raw_1, 1680a76fe7cSHans Petter Selasky &midi_desc_raw_2, 1690a76fe7cSHans Petter Selasky &midi_desc_raw_3, 1700a76fe7cSHans Petter Selasky &midi_desc_raw_4, 1710a76fe7cSHans Petter Selasky &midi_desc_raw_5, 1720a76fe7cSHans Petter Selasky NULL 1730a76fe7cSHans Petter Selasky }; 1740a76fe7cSHans Petter Selasky 1750a76fe7cSHans Petter Selasky static const struct usb_temp_interface_desc midi_iface_1 = { 1760a76fe7cSHans Petter Selasky .ppRawDesc = midi_descs_1, 1770a76fe7cSHans Petter Selasky .ppEndpoints = midi_iface_1_ep, 1784ffeccf1SEdward Tomasz Napierala .bInterfaceClass = UICLASS_AUDIO, 1794ffeccf1SEdward Tomasz Napierala .bInterfaceSubClass = UISUBCLASS_MIDISTREAM, 1800a76fe7cSHans Petter Selasky .bInterfaceProtocol = 0, 181*8e06491aSEdward Tomasz Napierala .iInterface = MIDI_INTERFACE_INDEX, 1820a76fe7cSHans Petter Selasky }; 1830a76fe7cSHans Petter Selasky 1840a76fe7cSHans Petter Selasky static const struct usb_temp_interface_desc *midi_interfaces[] = { 1850a76fe7cSHans Petter Selasky &midi_iface_0, 1860a76fe7cSHans Petter Selasky &midi_iface_1, 1870a76fe7cSHans Petter Selasky NULL, 1880a76fe7cSHans Petter Selasky }; 1890a76fe7cSHans Petter Selasky 1900a76fe7cSHans Petter Selasky static const struct usb_temp_config_desc midi_config_desc = { 1910a76fe7cSHans Petter Selasky .ppIfaceDesc = midi_interfaces, 1920a76fe7cSHans Petter Selasky .bmAttributes = UC_BUS_POWERED, 1930a76fe7cSHans Petter Selasky .bMaxPower = 25, /* 50 mA */ 194*8e06491aSEdward Tomasz Napierala .iConfiguration = MIDI_PRODUCT_INDEX, 1950a76fe7cSHans Petter Selasky }; 1960a76fe7cSHans Petter Selasky 1970a76fe7cSHans Petter Selasky static const struct usb_temp_config_desc *midi_configs[] = { 1980a76fe7cSHans Petter Selasky &midi_config_desc, 1990a76fe7cSHans Petter Selasky NULL, 2000a76fe7cSHans Petter Selasky }; 2010a76fe7cSHans Petter Selasky 2020a76fe7cSHans Petter Selasky static usb_temp_get_string_desc_t midi_get_string_desc; 2030a76fe7cSHans Petter Selasky 204*8e06491aSEdward Tomasz Napierala struct usb_temp_device_desc usb_template_midi = { 2050a76fe7cSHans Petter Selasky .getStringDesc = &midi_get_string_desc, 2060a76fe7cSHans Petter Selasky .ppConfigDesc = midi_configs, 2070a76fe7cSHans Petter Selasky .idVendor = USB_TEMPLATE_VENDOR, 2080a76fe7cSHans Petter Selasky .idProduct = 0x00BB, 2090a76fe7cSHans Petter Selasky .bcdDevice = 0x0100, 2100a76fe7cSHans Petter Selasky .bDeviceClass = 0, 2110a76fe7cSHans Petter Selasky .bDeviceSubClass = 0, 2120a76fe7cSHans Petter Selasky .bDeviceProtocol = 0, 2130a76fe7cSHans Petter Selasky .iManufacturer = 0, 214*8e06491aSEdward Tomasz Napierala .iProduct = MIDI_PRODUCT_INDEX, 2150a76fe7cSHans Petter Selasky .iSerialNumber = 0, 2160a76fe7cSHans Petter Selasky }; 2170a76fe7cSHans Petter Selasky 2180a76fe7cSHans Petter Selasky /*------------------------------------------------------------------------* 2190a76fe7cSHans Petter Selasky * midi_get_string_desc 2200a76fe7cSHans Petter Selasky * 2210a76fe7cSHans Petter Selasky * Return values: 2220a76fe7cSHans Petter Selasky * NULL: Failure. No such string. 2230a76fe7cSHans Petter Selasky * Else: Success. Pointer to string descriptor is returned. 2240a76fe7cSHans Petter Selasky *------------------------------------------------------------------------*/ 2250a76fe7cSHans Petter Selasky static const void * 2260a76fe7cSHans Petter Selasky midi_get_string_desc(uint16_t lang_id, uint8_t string_index) 2270a76fe7cSHans Petter Selasky { 228*8e06491aSEdward Tomasz Napierala static const void *ptr[MIDI_MAX_INDEX] = { 229*8e06491aSEdward Tomasz Napierala [MIDI_LANG_INDEX] = &usb_string_lang_en, 230*8e06491aSEdward Tomasz Napierala [MIDI_INTERFACE_INDEX] = &midi_interface, 231*8e06491aSEdward Tomasz Napierala [MIDI_PRODUCT_INDEX] = &midi_product, 2320a76fe7cSHans Petter Selasky }; 2330a76fe7cSHans Petter Selasky 2340a76fe7cSHans Petter Selasky if (string_index == 0) { 2350a76fe7cSHans Petter Selasky return (&usb_string_lang_en); 2360a76fe7cSHans Petter Selasky } 2370a76fe7cSHans Petter Selasky if (lang_id != 0x0409) { 2380a76fe7cSHans Petter Selasky return (NULL); 2390a76fe7cSHans Petter Selasky } 240*8e06491aSEdward Tomasz Napierala if (string_index < MIDI_MAX_INDEX) { 2410a76fe7cSHans Petter Selasky return (ptr[string_index]); 2420a76fe7cSHans Petter Selasky } 2430a76fe7cSHans Petter Selasky return (NULL); 2440a76fe7cSHans Petter Selasky } 245*8e06491aSEdward Tomasz Napierala 246*8e06491aSEdward Tomasz Napierala static void 247*8e06491aSEdward Tomasz Napierala midi_init(void *arg __unused) 248*8e06491aSEdward Tomasz Napierala { 249*8e06491aSEdward Tomasz Napierala struct sysctl_oid *parent; 250*8e06491aSEdward Tomasz Napierala char parent_name[3]; 251*8e06491aSEdward Tomasz Napierala 252*8e06491aSEdward Tomasz Napierala usb_make_str_desc(&midi_interface, sizeof(midi_interface), 253*8e06491aSEdward Tomasz Napierala MIDI_DEFAULT_INTERFACE); 254*8e06491aSEdward Tomasz Napierala usb_make_str_desc(&midi_product, sizeof(midi_product), 255*8e06491aSEdward Tomasz Napierala MIDI_DEFAULT_PRODUCT); 256*8e06491aSEdward Tomasz Napierala 257*8e06491aSEdward Tomasz Napierala snprintf(parent_name, sizeof(parent_name), "%d", USB_TEMP_MIDI); 258*8e06491aSEdward Tomasz Napierala sysctl_ctx_init(&midi_ctx_list); 259*8e06491aSEdward Tomasz Napierala 260*8e06491aSEdward Tomasz Napierala parent = SYSCTL_ADD_NODE(&midi_ctx_list, 261*8e06491aSEdward Tomasz Napierala SYSCTL_STATIC_CHILDREN(_hw_usb_templates), OID_AUTO, 262*8e06491aSEdward Tomasz Napierala parent_name, CTLFLAG_RW, 263*8e06491aSEdward Tomasz Napierala 0, "USB MIDI device side template"); 264*8e06491aSEdward Tomasz Napierala SYSCTL_ADD_U16(&midi_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO, 265*8e06491aSEdward Tomasz Napierala "vendor_id", CTLFLAG_RWTUN, 266*8e06491aSEdward Tomasz Napierala &usb_template_midi.idVendor, 1, "Vendor identifier"); 267*8e06491aSEdward Tomasz Napierala SYSCTL_ADD_U16(&midi_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO, 268*8e06491aSEdward Tomasz Napierala "product_id", CTLFLAG_RWTUN, 269*8e06491aSEdward Tomasz Napierala &usb_template_midi.idProduct, 1, "Product identifier"); 270*8e06491aSEdward Tomasz Napierala #if 0 271*8e06491aSEdward Tomasz Napierala SYSCTL_ADD_PROC(&midi_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO, 272*8e06491aSEdward Tomasz Napierala "interface", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 273*8e06491aSEdward Tomasz Napierala &midi_interface, sizeof(midi_interface), usb_temp_sysctl, 274*8e06491aSEdward Tomasz Napierala "A", "Interface string"); 275*8e06491aSEdward Tomasz Napierala #endif 276*8e06491aSEdward Tomasz Napierala SYSCTL_ADD_PROC(&midi_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO, 277*8e06491aSEdward Tomasz Napierala "product", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 278*8e06491aSEdward Tomasz Napierala &midi_product, sizeof(midi_product), usb_temp_sysctl, 279*8e06491aSEdward Tomasz Napierala "A", "Product string"); 280*8e06491aSEdward Tomasz Napierala } 281*8e06491aSEdward Tomasz Napierala 282*8e06491aSEdward Tomasz Napierala static void 283*8e06491aSEdward Tomasz Napierala midi_uninit(void *arg __unused) 284*8e06491aSEdward Tomasz Napierala { 285*8e06491aSEdward Tomasz Napierala 286*8e06491aSEdward Tomasz Napierala sysctl_ctx_free(&midi_ctx_list); 287*8e06491aSEdward Tomasz Napierala } 288*8e06491aSEdward Tomasz Napierala 289*8e06491aSEdward Tomasz Napierala SYSINIT(midi_init, SI_SUB_LOCK, SI_ORDER_FIRST, midi_init, NULL); 290*8e06491aSEdward Tomasz Napierala SYSUNINIT(midi_init, SI_SUB_LOCK, SI_ORDER_FIRST, midi_uninit, NULL); 291