| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * f_midi2.c -- USB MIDI 2.0 class function driver
- */
- #include <linux/device.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/slab.h>
- #include <sound/core.h>
- #include <sound/control.h>
- #include <sound/ump.h>
- #include <sound/ump_msg.h>
- #include <sound/ump_convert.h>
- #include <linux/usb/ch9.h>
- #include <linux/usb/func_utils.h>
- #include <linux/usb/gadget.h>
- #include <linux/usb/audio.h>
- #include <linux/usb/midi-v2.h>
- #include "u_midi2.h"
- struct f_midi2;
- struct f_midi2_ep;
- struct f_midi2_usb_ep;
- /* Context for each USB request */
- struct f_midi2_req_ctx {
- struct f_midi2_usb_ep *usb_ep; /* belonging USB EP */
- unsigned int index; /* array index: 0-31 */
- struct usb_request *req; /* assigned request */
- };
- /* Resources for a USB Endpoint */
- struct f_midi2_usb_ep {
- struct f_midi2 *card; /* belonging card */
- struct f_midi2_ep *ep; /* belonging UMP EP (optional) */
- struct usb_ep *usb_ep; /* assigned USB EP */
- void (*complete)(struct usb_ep *usb_ep, struct usb_request *req);
- unsigned long free_reqs; /* bitmap for unused requests */
- unsigned int num_reqs; /* number of allocated requests */
- struct f_midi2_req_ctx *reqs; /* request context array */
- };
- /* Resources for UMP Function Block (and USB Group Terminal Block) */
- struct f_midi2_block {
- struct f_midi2_block_info info; /* FB info, copied from configfs */
- struct snd_ump_block *fb; /* assigned FB */
- unsigned int gtb_id; /* assigned GTB id */
- unsigned int string_id; /* assigned string id */
- };
- /* Temporary buffer for altset 0 MIDI 1.0 handling */
- struct f_midi2_midi1_port {
- unsigned int pending; /* pending bytes on the input buffer */
- u8 buf[32]; /* raw MIDI 1.0 byte input */
- u8 state; /* running status */
- u8 data[2]; /* rendered USB MIDI 1.0 packet data */
- };
- /* MIDI 1.0 message states */
- enum {
- STATE_INITIAL = 0, /* pseudo state */
- STATE_1PARAM,
- STATE_2PARAM_1,
- STATE_2PARAM_2,
- STATE_SYSEX_0,
- STATE_SYSEX_1,
- STATE_SYSEX_2,
- STATE_REAL_TIME,
- STATE_FINISHED, /* pseudo state */
- };
- /* Resources for UMP Endpoint */
- struct f_midi2_ep {
- struct snd_ump_endpoint *ump; /* assigned UMP EP */
- struct f_midi2 *card; /* belonging MIDI 2.0 device */
- struct f_midi2_ep_info info; /* UMP EP info, copied from configfs */
- unsigned int num_blks; /* number of FBs */
- struct f_midi2_block blks[SNDRV_UMP_MAX_BLOCKS]; /* UMP FBs */
- struct f_midi2_usb_ep ep_in; /* USB MIDI EP-in */
- struct f_midi2_usb_ep ep_out; /* USB MIDI EP-out */
- u8 in_group_to_cable[SNDRV_UMP_MAX_GROUPS]; /* map to cable; 1-based! */
- };
- /* indices for USB strings */
- enum {
- STR_IFACE = 0,
- STR_GTB1 = 1,
- };
- /* 1-based GTB id to string id */
- #define gtb_to_str_id(id) (STR_GTB1 + (id) - 1)
- /* mapping from MIDI 1.0 cable to UMP group */
- struct midi1_cable_mapping {
- struct f_midi2_ep *ep;
- unsigned char block;
- unsigned char group;
- };
- /* operation mode */
- enum {
- MIDI_OP_MODE_UNSET, /* no altset set yet */
- MIDI_OP_MODE_MIDI1, /* MIDI 1.0 (altset 0) is used */
- MIDI_OP_MODE_MIDI2, /* MIDI 2.0 (altset 1) is used */
- };
- /* Resources for MIDI 2.0 Device */
- struct f_midi2 {
- struct usb_function func;
- struct usb_gadget *gadget;
- struct snd_card *card;
- /* MIDI 1.0 in/out USB EPs */
- struct f_midi2_usb_ep midi1_ep_in;
- struct f_midi2_usb_ep midi1_ep_out;
- /* number of MIDI 1.0 I/O cables */
- unsigned int num_midi1_in;
- unsigned int num_midi1_out;
- /* conversion for MIDI 1.0 EP-in */
- struct f_midi2_midi1_port midi1_port[MAX_CABLES];
- /* conversion for MIDI 1.0 EP-out */
- struct ump_cvt_to_ump midi1_ump_cvt;
- /* mapping between cables and UMP groups */
- struct midi1_cable_mapping in_cable_mapping[MAX_CABLES];
- struct midi1_cable_mapping out_cable_mapping[MAX_CABLES];
- int midi_if; /* USB MIDI interface number */
- int operation_mode; /* current operation mode */
- spinlock_t queue_lock;
- struct f_midi2_card_info info; /* card info, copied from configfs */
- unsigned int num_eps;
- struct f_midi2_ep midi2_eps[MAX_UMP_EPS];
- unsigned int total_blocks; /* total number of blocks of all EPs */
- struct usb_string *string_defs;
- struct usb_string *strings;
- };
- #define func_to_midi2(f) container_of(f, struct f_midi2, func)
- /* convert from MIDI protocol number (1 or 2) to SNDRV_UMP_EP_INFO_PROTO_* */
- #define to_ump_protocol(v) (((v) & 3) << 8)
- /* get EP name string */
- static const char *ump_ep_name(const struct f_midi2_ep *ep)
- {
- return ep->info.ep_name ? ep->info.ep_name : "MIDI 2.0 Gadget";
- }
- /* get EP product ID string */
- static const char *ump_product_id(const struct f_midi2_ep *ep)
- {
- return ep->info.product_id ? ep->info.product_id : "Unique Product ID";
- }
- /* get FB name string */
- static const char *ump_fb_name(const struct f_midi2_block_info *info)
- {
- return info->name ? info->name : "MIDI 2.0 Gadget I/O";
- }
- /*
- * USB Descriptor Definitions
- */
- /* GTB header descriptor */
- static struct usb_ms20_gr_trm_block_header_descriptor gtb_header_desc = {
- .bLength = sizeof(gtb_header_desc),
- .bDescriptorType = USB_DT_CS_GR_TRM_BLOCK,
- .bDescriptorSubtype = USB_MS_GR_TRM_BLOCK_HEADER,
- .wTotalLength = __cpu_to_le16(0x12), // to be filled
- };
- /* GTB descriptor template: most items are replaced dynamically */
- static struct usb_ms20_gr_trm_block_descriptor gtb_desc = {
- .bLength = sizeof(gtb_desc),
- .bDescriptorType = USB_DT_CS_GR_TRM_BLOCK,
- .bDescriptorSubtype = USB_MS_GR_TRM_BLOCK,
- .bGrpTrmBlkID = 0x01,
- .bGrpTrmBlkType = USB_MS_GR_TRM_BLOCK_TYPE_BIDIRECTIONAL,
- .nGroupTrm = 0x00,
- .nNumGroupTrm = 1,
- .iBlockItem = 0,
- .bMIDIProtocol = USB_MS_MIDI_PROTO_1_0_64,
- .wMaxInputBandwidth = 0,
- .wMaxOutputBandwidth = 0,
- };
- DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1);
- DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(16);
- DECLARE_UAC_AC_HEADER_DESCRIPTOR(1);
- DECLARE_USB_MS20_ENDPOINT_DESCRIPTOR(32);
- #define EP_MAX_PACKET_INT 8
- /* Audio Control Interface */
- static struct usb_interface_descriptor midi2_audio_if_desc = {
- .bLength = USB_DT_INTERFACE_SIZE,
- .bDescriptorType = USB_DT_INTERFACE,
- .bInterfaceNumber = 0, // to be filled
- .bNumEndpoints = 0,
- .bInterfaceClass = USB_CLASS_AUDIO,
- .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
- .bInterfaceProtocol = 0,
- .iInterface = 0,
- };
- static struct uac1_ac_header_descriptor_1 midi2_audio_class_desc = {
- .bLength = 0x09,
- .bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubtype = 0x01,
- .bcdADC = __cpu_to_le16(0x0100),
- .wTotalLength = __cpu_to_le16(0x0009),
- .bInCollection = 0x01,
- .baInterfaceNr = { 0x01 }, // to be filled
- };
- /* MIDI 1.0 Streaming Interface (altset 0) */
- static struct usb_interface_descriptor midi2_midi1_if_desc = {
- .bLength = USB_DT_INTERFACE_SIZE,
- .bDescriptorType = USB_DT_INTERFACE,
- .bInterfaceNumber = 0, // to be filled
- .bAlternateSetting = 0,
- .bNumEndpoints = 2, // to be filled
- .bInterfaceClass = USB_CLASS_AUDIO,
- .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING,
- .bInterfaceProtocol = 0,
- .iInterface = 0, // to be filled
- };
- static struct usb_ms_header_descriptor midi2_midi1_class_desc = {
- .bLength = 0x07,
- .bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubtype = USB_MS_HEADER,
- .bcdMSC = __cpu_to_le16(0x0100),
- .wTotalLength = __cpu_to_le16(0x41), // to be calculated
- };
- /* MIDI 1.0 EP OUT */
- static struct usb_endpoint_descriptor midi2_midi1_ep_out_desc = {
- .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = USB_DIR_OUT | 0, // set up dynamically
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- };
- static struct usb_ss_ep_comp_descriptor midi2_midi1_ep_out_ss_comp_desc = {
- .bLength = sizeof(midi2_midi1_ep_out_ss_comp_desc),
- .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
- };
- static struct usb_ms_endpoint_descriptor_16 midi2_midi1_ep_out_class_desc = {
- .bLength = 0x05, // to be filled
- .bDescriptorType = USB_DT_CS_ENDPOINT,
- .bDescriptorSubtype = USB_MS_GENERAL,
- .bNumEmbMIDIJack = 1,
- .baAssocJackID = { 0x01 },
- };
- /* MIDI 1.0 EP IN */
- static struct usb_endpoint_descriptor midi2_midi1_ep_in_desc = {
- .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = USB_DIR_IN | 0, // set up dynamically
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- };
- static struct usb_ss_ep_comp_descriptor midi2_midi1_ep_in_ss_comp_desc = {
- .bLength = sizeof(midi2_midi1_ep_in_ss_comp_desc),
- .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
- };
- static struct usb_ms_endpoint_descriptor_16 midi2_midi1_ep_in_class_desc = {
- .bLength = 0x05, // to be filled
- .bDescriptorType = USB_DT_CS_ENDPOINT,
- .bDescriptorSubtype = USB_MS_GENERAL,
- .bNumEmbMIDIJack = 1,
- .baAssocJackID = { 0x03 },
- };
- /* MIDI 2.0 Streaming Interface (altset 1) */
- static struct usb_interface_descriptor midi2_midi2_if_desc = {
- .bLength = USB_DT_INTERFACE_SIZE,
- .bDescriptorType = USB_DT_INTERFACE,
- .bInterfaceNumber = 0, // to be filled
- .bAlternateSetting = 1,
- .bNumEndpoints = 2, // to be filled
- .bInterfaceClass = USB_CLASS_AUDIO,
- .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING,
- .bInterfaceProtocol = 0,
- .iInterface = 0, // to be filled
- };
- static struct usb_ms_header_descriptor midi2_midi2_class_desc = {
- .bLength = 0x07,
- .bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubtype = USB_MS_HEADER,
- .bcdMSC = __cpu_to_le16(0x0200),
- .wTotalLength = __cpu_to_le16(0x07),
- };
- /* MIDI 2.0 EP OUT */
- static struct usb_endpoint_descriptor midi2_midi2_ep_out_desc[MAX_UMP_EPS];
- static struct usb_ss_ep_comp_descriptor midi2_midi2_ep_out_ss_comp_desc = {
- .bLength = sizeof(midi2_midi1_ep_out_ss_comp_desc),
- .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
- };
- static struct usb_ms20_endpoint_descriptor_32 midi2_midi2_ep_out_class_desc[MAX_UMP_EPS];
- /* MIDI 2.0 EP IN */
- static struct usb_endpoint_descriptor midi2_midi2_ep_in_desc[MAX_UMP_EPS];
- static struct usb_ss_ep_comp_descriptor midi2_midi2_ep_in_ss_comp_desc = {
- .bLength = sizeof(midi2_midi2_ep_in_ss_comp_desc),
- .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
- };
- static struct usb_ms20_endpoint_descriptor_32 midi2_midi2_ep_in_class_desc[MAX_UMP_EPS];
- /* Arrays of descriptors to be created */
- static void *midi2_audio_descs[] = {
- &midi2_audio_if_desc,
- &midi2_audio_class_desc,
- NULL
- };
- static void *midi2_midi1_descs[] = {
- &midi2_midi1_if_desc,
- &midi2_midi1_class_desc,
- NULL
- };
- static void *midi2_midi1_ep_out_descs[] = {
- &midi2_midi1_ep_out_desc,
- &midi2_midi1_ep_out_class_desc,
- NULL
- };
- static void *midi2_midi1_ep_in_descs[] = {
- &midi2_midi1_ep_in_desc,
- &midi2_midi1_ep_in_class_desc,
- NULL
- };
- static void *midi2_midi1_ep_out_ss_descs[] = {
- &midi2_midi1_ep_out_desc,
- &midi2_midi1_ep_out_ss_comp_desc,
- &midi2_midi1_ep_out_class_desc,
- NULL
- };
- static void *midi2_midi1_ep_in_ss_descs[] = {
- &midi2_midi1_ep_in_desc,
- &midi2_midi1_ep_in_ss_comp_desc,
- &midi2_midi1_ep_in_class_desc,
- NULL
- };
- static void *midi2_midi2_descs[] = {
- &midi2_midi2_if_desc,
- &midi2_midi2_class_desc,
- NULL
- };
- /*
- * USB request handling
- */
- /* get an empty request for the given EP */
- static struct usb_request *get_empty_request(struct f_midi2_usb_ep *usb_ep)
- {
- struct usb_request *req = NULL;
- unsigned long flags;
- int index;
- spin_lock_irqsave(&usb_ep->card->queue_lock, flags);
- if (!usb_ep->free_reqs)
- goto unlock;
- index = find_first_bit(&usb_ep->free_reqs, usb_ep->num_reqs);
- if (index >= usb_ep->num_reqs)
- goto unlock;
- req = usb_ep->reqs[index].req;
- if (!req)
- goto unlock;
- clear_bit(index, &usb_ep->free_reqs);
- req->length = 0;
- unlock:
- spin_unlock_irqrestore(&usb_ep->card->queue_lock, flags);
- return req;
- }
- /* put the empty request back */
- static void put_empty_request(struct usb_request *req)
- {
- struct f_midi2_req_ctx *ctx = req->context;
- unsigned long flags;
- spin_lock_irqsave(&ctx->usb_ep->card->queue_lock, flags);
- set_bit(ctx->index, &ctx->usb_ep->free_reqs);
- spin_unlock_irqrestore(&ctx->usb_ep->card->queue_lock, flags);
- }
- /*
- * UMP v1.1 Stream message handling
- */
- /* queue a request to UMP EP; request is either queued or freed after this */
- static int queue_request_ep_raw(struct usb_request *req)
- {
- struct f_midi2_req_ctx *ctx = req->context;
- int err;
- req->complete = ctx->usb_ep->complete;
- err = usb_ep_queue(ctx->usb_ep->usb_ep, req, GFP_ATOMIC);
- if (err) {
- put_empty_request(req);
- return err;
- }
- return 0;
- }
- /* queue a request with endianness conversion */
- static int queue_request_ep_in(struct usb_request *req)
- {
- /* UMP packets have to be converted to little-endian */
- cpu_to_le32_array((u32 *)req->buf, req->length >> 2);
- return queue_request_ep_raw(req);
- }
- /* reply a UMP packet via EP-in */
- static int reply_ep_in(struct f_midi2_ep *ep, const void *buf, int len)
- {
- struct f_midi2_usb_ep *usb_ep = &ep->ep_in;
- struct usb_request *req;
- req = get_empty_request(usb_ep);
- if (!req)
- return -ENOSPC;
- req->length = len;
- memcpy(req->buf, buf, len);
- return queue_request_ep_in(req);
- }
- /* reply a UMP stream EP info */
- static void reply_ump_stream_ep_info(struct f_midi2_ep *ep)
- {
- struct snd_ump_stream_msg_ep_info rep = {
- .type = UMP_MSG_TYPE_STREAM,
- .status = UMP_STREAM_MSG_STATUS_EP_INFO,
- .ump_version_major = 0x01,
- .ump_version_minor = 0x01,
- .num_function_blocks = ep->num_blks,
- .static_function_block = !!ep->card->info.static_block,
- .protocol = (UMP_STREAM_MSG_EP_INFO_CAP_MIDI1 |
- UMP_STREAM_MSG_EP_INFO_CAP_MIDI2) >> 8,
- };
- reply_ep_in(ep, &rep, sizeof(rep));
- }
- /* reply a UMP EP device info */
- static void reply_ump_stream_ep_device(struct f_midi2_ep *ep)
- {
- struct snd_ump_stream_msg_device_info rep = {
- .type = UMP_MSG_TYPE_STREAM,
- .status = UMP_STREAM_MSG_STATUS_DEVICE_INFO,
- .manufacture_id = ep->info.manufacturer,
- .family_lsb = ep->info.family & 0xff,
- .family_msb = (ep->info.family >> 8) & 0xff,
- .model_lsb = ep->info.model & 0xff,
- .model_msb = (ep->info.model >> 8) & 0xff,
- .sw_revision = ep->info.sw_revision,
- };
- reply_ep_in(ep, &rep, sizeof(rep));
- }
- #define UMP_STREAM_PKT_BYTES 16 /* UMP stream packet size = 16 bytes*/
- #define UMP_STREAM_EP_STR_OFF 2 /* offset of name string for EP info */
- #define UMP_STREAM_FB_STR_OFF 3 /* offset of name string for FB info */
- /* Helper to replay a string */
- static void reply_ump_stream_string(struct f_midi2_ep *ep, const u8 *name,
- unsigned int type, unsigned int extra,
- unsigned int start_ofs)
- {
- struct f_midi2_usb_ep *usb_ep = &ep->ep_in;
- struct f_midi2 *midi2 = ep->card;
- struct usb_request *req;
- unsigned int pos;
- u32 *buf;
- if (!*name)
- return;
- req = get_empty_request(usb_ep);
- if (!req)
- return;
- buf = (u32 *)req->buf;
- pos = start_ofs;
- for (;;) {
- if (pos == start_ofs) {
- memset(buf, 0, UMP_STREAM_PKT_BYTES);
- buf[0] = ump_stream_compose(type, 0) | extra;
- }
- buf[pos / 4] |= *name++ << ((3 - (pos % 4)) * 8);
- if (!*name) {
- if (req->length)
- buf[0] |= UMP_STREAM_MSG_FORMAT_END << 26;
- req->length += UMP_STREAM_PKT_BYTES;
- break;
- }
- if (++pos == UMP_STREAM_PKT_BYTES) {
- if (!req->length)
- buf[0] |= UMP_STREAM_MSG_FORMAT_START << 26;
- else
- buf[0] |= UMP_STREAM_MSG_FORMAT_CONTINUE << 26;
- req->length += UMP_STREAM_PKT_BYTES;
- if (midi2->info.req_buf_size - req->length < UMP_STREAM_PKT_BYTES)
- break;
- buf += 4;
- pos = start_ofs;
- }
- }
- if (req->length)
- queue_request_ep_in(req);
- else
- put_empty_request(req);
- }
- /* Reply a UMP EP name string */
- static void reply_ump_stream_ep_name(struct f_midi2_ep *ep)
- {
- reply_ump_stream_string(ep, ump_ep_name(ep),
- UMP_STREAM_MSG_STATUS_EP_NAME, 0,
- UMP_STREAM_EP_STR_OFF);
- }
- /* Reply a UMP EP product ID string */
- static void reply_ump_stream_ep_pid(struct f_midi2_ep *ep)
- {
- reply_ump_stream_string(ep, ump_product_id(ep),
- UMP_STREAM_MSG_STATUS_PRODUCT_ID, 0,
- UMP_STREAM_EP_STR_OFF);
- }
- /* Reply a UMP EP stream config */
- static void reply_ump_stream_ep_config(struct f_midi2_ep *ep)
- {
- struct snd_ump_stream_msg_stream_cfg rep = {
- .type = UMP_MSG_TYPE_STREAM,
- .status = UMP_STREAM_MSG_STATUS_STREAM_CFG,
- };
- if (ep->info.protocol == 2)
- rep.protocol = UMP_STREAM_MSG_EP_INFO_CAP_MIDI2 >> 8;
- else
- rep.protocol = UMP_STREAM_MSG_EP_INFO_CAP_MIDI1 >> 8;
- reply_ep_in(ep, &rep, sizeof(rep));
- }
- /* Reply a UMP FB info */
- static void reply_ump_stream_fb_info(struct f_midi2_ep *ep, int blk)
- {
- struct f_midi2_block_info *b = &ep->blks[blk].info;
- struct snd_ump_stream_msg_fb_info rep = {
- .type = UMP_MSG_TYPE_STREAM,
- .status = UMP_STREAM_MSG_STATUS_FB_INFO,
- .active = !!b->active,
- .function_block_id = blk,
- .ui_hint = b->ui_hint,
- .midi_10 = b->is_midi1,
- .direction = b->direction,
- .first_group = b->first_group,
- .num_groups = b->num_groups,
- .midi_ci_version = b->midi_ci_version,
- .sysex8_streams = b->sysex8_streams,
- };
- reply_ep_in(ep, &rep, sizeof(rep));
- }
- /* Reply a FB name string */
- static void reply_ump_stream_fb_name(struct f_midi2_ep *ep, unsigned int blk)
- {
- reply_ump_stream_string(ep, ump_fb_name(&ep->blks[blk].info),
- UMP_STREAM_MSG_STATUS_FB_NAME, blk << 8,
- UMP_STREAM_FB_STR_OFF);
- }
- /* Process a UMP Stream message */
- static void process_ump_stream_msg(struct f_midi2_ep *ep, const u32 *data)
- {
- struct f_midi2 *midi2 = ep->card;
- unsigned int format, status, blk;
- format = ump_stream_message_format(*data);
- status = ump_stream_message_status(*data);
- switch (status) {
- case UMP_STREAM_MSG_STATUS_EP_DISCOVERY:
- if (format)
- return; // invalid
- if (data[1] & UMP_STREAM_MSG_REQUEST_EP_INFO)
- reply_ump_stream_ep_info(ep);
- if (data[1] & UMP_STREAM_MSG_REQUEST_DEVICE_INFO)
- reply_ump_stream_ep_device(ep);
- if (data[1] & UMP_STREAM_MSG_REQUEST_EP_NAME)
- reply_ump_stream_ep_name(ep);
- if (data[1] & UMP_STREAM_MSG_REQUEST_PRODUCT_ID)
- reply_ump_stream_ep_pid(ep);
- if (data[1] & UMP_STREAM_MSG_REQUEST_STREAM_CFG)
- reply_ump_stream_ep_config(ep);
- return;
- case UMP_STREAM_MSG_STATUS_STREAM_CFG_REQUEST:
- if (*data & UMP_STREAM_MSG_EP_INFO_CAP_MIDI2) {
- ep->info.protocol = 2;
- DBG(midi2, "Switching Protocol to MIDI2\n");
- } else {
- ep->info.protocol = 1;
- DBG(midi2, "Switching Protocol to MIDI1\n");
- }
- snd_ump_switch_protocol(ep->ump, to_ump_protocol(ep->info.protocol));
- reply_ump_stream_ep_config(ep);
- return;
- case UMP_STREAM_MSG_STATUS_FB_DISCOVERY:
- if (format)
- return; // invalid
- blk = (*data >> 8) & 0xff;
- if (blk == 0xff) {
- /* inquiry for all blocks */
- for (blk = 0; blk < ep->num_blks; blk++) {
- if (*data & UMP_STREAM_MSG_REQUEST_FB_INFO)
- reply_ump_stream_fb_info(ep, blk);
- if (*data & UMP_STREAM_MSG_REQUEST_FB_NAME)
- reply_ump_stream_fb_name(ep, blk);
- }
- } else if (blk < ep->num_blks) {
- /* only the specified block */
- if (*data & UMP_STREAM_MSG_REQUEST_FB_INFO)
- reply_ump_stream_fb_info(ep, blk);
- if (*data & UMP_STREAM_MSG_REQUEST_FB_NAME)
- reply_ump_stream_fb_name(ep, blk);
- }
- return;
- }
- }
- /* Process UMP messages included in a USB request */
- static void process_ump(struct f_midi2_ep *ep, const struct usb_request *req)
- {
- const u32 *data = (u32 *)req->buf;
- int len = req->actual >> 2;
- const u32 *in_buf = ep->ump->input_buf;
- for (; len > 0; len--, data++) {
- if (snd_ump_receive_ump_val(ep->ump, *data) <= 0)
- continue;
- if (ump_message_type(*in_buf) == UMP_MSG_TYPE_STREAM)
- process_ump_stream_msg(ep, in_buf);
- }
- }
- /*
- * MIDI 2.0 UMP USB request handling
- */
- /* complete handler for UMP EP-out requests */
- static void f_midi2_ep_out_complete(struct usb_ep *usb_ep,
- struct usb_request *req)
- {
- struct f_midi2_req_ctx *ctx = req->context;
- struct f_midi2_ep *ep = ctx->usb_ep->ep;
- struct f_midi2 *midi2 = ep->card;
- int status = req->status;
- if (status) {
- DBG(midi2, "%s complete error %d: %d/%d\n",
- usb_ep->name, status, req->actual, req->length);
- goto error;
- }
- /* convert to UMP packet in native endianness */
- le32_to_cpu_array((u32 *)req->buf, req->actual >> 2);
- if (midi2->info.process_ump)
- process_ump(ep, req);
- snd_ump_receive(ep->ump, req->buf, req->actual & ~3);
- if (midi2->operation_mode != MIDI_OP_MODE_MIDI2)
- goto error;
- if (queue_request_ep_raw(req))
- goto error;
- return;
- error:
- put_empty_request(req);
- }
- /* Transmit UMP packets received from user-space to the gadget */
- static void process_ump_transmit(struct f_midi2_ep *ep)
- {
- struct f_midi2_usb_ep *usb_ep = &ep->ep_in;
- struct f_midi2 *midi2 = ep->card;
- struct usb_request *req;
- int len;
- if (!usb_ep->usb_ep->enabled)
- return;
- for (;;) {
- req = get_empty_request(usb_ep);
- if (!req)
- break;
- len = snd_ump_transmit(ep->ump, (u32 *)req->buf,
- midi2->info.req_buf_size);
- if (len <= 0) {
- put_empty_request(req);
- break;
- }
- req->length = len;
- if (queue_request_ep_in(req) < 0)
- break;
- }
- }
- /* Complete handler for UMP EP-in requests */
- static void f_midi2_ep_in_complete(struct usb_ep *usb_ep,
- struct usb_request *req)
- {
- struct f_midi2_req_ctx *ctx = req->context;
- struct f_midi2_ep *ep = ctx->usb_ep->ep;
- struct f_midi2 *midi2 = ep->card;
- int status = req->status;
- put_empty_request(req);
- if (status) {
- DBG(midi2, "%s complete error %d: %d/%d\n",
- usb_ep->name, status, req->actual, req->length);
- return;
- }
- process_ump_transmit(ep);
- }
- /*
- * MIDI1 (altset 0) USB request handling
- */
- /* process one MIDI byte -- copied from f_midi.c
- *
- * fill the packet or request if needed
- * returns true if the request became empty (queued)
- */
- static bool process_midi1_byte(struct f_midi2 *midi2, u8 cable, u8 b,
- struct usb_request **req_p)
- {
- struct f_midi2_midi1_port *port = &midi2->midi1_port[cable];
- u8 p[4] = { cable << 4, 0, 0, 0 };
- int next_state = STATE_INITIAL;
- struct usb_request *req = *req_p;
- switch (b) {
- case 0xf8 ... 0xff:
- /* System Real-Time Messages */
- p[0] |= 0x0f;
- p[1] = b;
- next_state = port->state;
- port->state = STATE_REAL_TIME;
- break;
- case 0xf7:
- /* End of SysEx */
- switch (port->state) {
- case STATE_SYSEX_0:
- p[0] |= 0x05;
- p[1] = 0xf7;
- next_state = STATE_FINISHED;
- break;
- case STATE_SYSEX_1:
- p[0] |= 0x06;
- p[1] = port->data[0];
- p[2] = 0xf7;
- next_state = STATE_FINISHED;
- break;
- case STATE_SYSEX_2:
- p[0] |= 0x07;
- p[1] = port->data[0];
- p[2] = port->data[1];
- p[3] = 0xf7;
- next_state = STATE_FINISHED;
- break;
- default:
- /* Ignore byte */
- next_state = port->state;
- port->state = STATE_INITIAL;
- }
- break;
- case 0xf0 ... 0xf6:
- /* System Common Messages */
- port->data[0] = port->data[1] = 0;
- port->state = STATE_INITIAL;
- switch (b) {
- case 0xf0:
- port->data[0] = b;
- port->data[1] = 0;
- next_state = STATE_SYSEX_1;
- break;
- case 0xf1:
- case 0xf3:
- port->data[0] = b;
- next_state = STATE_1PARAM;
- break;
- case 0xf2:
- port->data[0] = b;
- next_state = STATE_2PARAM_1;
- break;
- case 0xf4:
- case 0xf5:
- next_state = STATE_INITIAL;
- break;
- case 0xf6:
- p[0] |= 0x05;
- p[1] = 0xf6;
- next_state = STATE_FINISHED;
- break;
- }
- break;
- case 0x80 ... 0xef:
- /*
- * Channel Voice Messages, Channel Mode Messages
- * and Control Change Messages.
- */
- port->data[0] = b;
- port->data[1] = 0;
- port->state = STATE_INITIAL;
- if (b >= 0xc0 && b <= 0xdf)
- next_state = STATE_1PARAM;
- else
- next_state = STATE_2PARAM_1;
- break;
- case 0x00 ... 0x7f:
- /* Message parameters */
- switch (port->state) {
- case STATE_1PARAM:
- if (port->data[0] < 0xf0)
- p[0] |= port->data[0] >> 4;
- else
- p[0] |= 0x02;
- p[1] = port->data[0];
- p[2] = b;
- /* This is to allow Running State Messages */
- next_state = STATE_1PARAM;
- break;
- case STATE_2PARAM_1:
- port->data[1] = b;
- next_state = STATE_2PARAM_2;
- break;
- case STATE_2PARAM_2:
- if (port->data[0] < 0xf0)
- p[0] |= port->data[0] >> 4;
- else
- p[0] |= 0x03;
- p[1] = port->data[0];
- p[2] = port->data[1];
- p[3] = b;
- /* This is to allow Running State Messages */
- next_state = STATE_2PARAM_1;
- break;
- case STATE_SYSEX_0:
- port->data[0] = b;
- next_state = STATE_SYSEX_1;
- break;
- case STATE_SYSEX_1:
- port->data[1] = b;
- next_state = STATE_SYSEX_2;
- break;
- case STATE_SYSEX_2:
- p[0] |= 0x04;
- p[1] = port->data[0];
- p[2] = port->data[1];
- p[3] = b;
- next_state = STATE_SYSEX_0;
- break;
- }
- break;
- }
- /* States where we have to write into the USB request */
- if (next_state == STATE_FINISHED ||
- port->state == STATE_SYSEX_2 ||
- port->state == STATE_1PARAM ||
- port->state == STATE_2PARAM_2 ||
- port->state == STATE_REAL_TIME) {
- memcpy(req->buf + req->length, p, sizeof(p));
- req->length += sizeof(p);
- if (next_state == STATE_FINISHED) {
- next_state = STATE_INITIAL;
- port->data[0] = port->data[1] = 0;
- }
- if (midi2->info.req_buf_size - req->length <= 4) {
- queue_request_ep_raw(req);
- *req_p = NULL;
- return true;
- }
- }
- port->state = next_state;
- return false;
- }
- /* process all pending MIDI bytes in the internal buffer;
- * returns true if the request gets empty
- * returns false if all have been processed
- */
- static bool process_midi1_pending_buf(struct f_midi2 *midi2,
- struct usb_request **req_p)
- {
- unsigned int cable, c;
- for (cable = 0; cable < midi2->num_midi1_in; cable++) {
- struct f_midi2_midi1_port *port = &midi2->midi1_port[cable];
- if (!port->pending)
- continue;
- for (c = 0; c < port->pending; c++) {
- if (process_midi1_byte(midi2, cable, port->buf[c],
- req_p)) {
- port->pending -= c;
- if (port->pending)
- memmove(port->buf, port->buf + c,
- port->pending);
- return true;
- }
- }
- port->pending = 0;
- }
- return false;
- }
- /* fill the MIDI bytes onto the temporary buffer
- */
- static void fill_midi1_pending_buf(struct f_midi2 *midi2, u8 cable, u8 *buf,
- unsigned int size)
- {
- struct f_midi2_midi1_port *port = &midi2->midi1_port[cable];
- if (port->pending + size > sizeof(port->buf))
- return;
- memcpy(port->buf + port->pending, buf, size);
- port->pending += size;
- }
- /* try to process data given from the associated UMP stream */
- static void process_midi1_transmit(struct f_midi2 *midi2)
- {
- struct f_midi2_usb_ep *usb_ep = &midi2->midi1_ep_in;
- struct f_midi2_ep *ep = &midi2->midi2_eps[0];
- struct usb_request *req = NULL;
- /* 12 is the largest outcome (4 MIDI1 cmds) for a single UMP packet */
- unsigned char outbuf[12];
- unsigned char group, cable;
- int len, size;
- u32 ump;
- if (!usb_ep->usb_ep || !usb_ep->usb_ep->enabled)
- return;
- for (;;) {
- if (!req) {
- req = get_empty_request(usb_ep);
- if (!req)
- break;
- }
- if (process_midi1_pending_buf(midi2, &req))
- continue;
- len = snd_ump_transmit(ep->ump, &ump, 4);
- if (len <= 0)
- break;
- if (snd_ump_receive_ump_val(ep->ump, ump) <= 0)
- continue;
- size = snd_ump_convert_from_ump(ep->ump->input_buf, outbuf,
- &group);
- if (size <= 0)
- continue;
- cable = ep->in_group_to_cable[group];
- if (!cable)
- continue;
- cable--; /* to 0-base */
- fill_midi1_pending_buf(midi2, cable, outbuf, size);
- }
- if (req) {
- if (req->length)
- queue_request_ep_raw(req);
- else
- put_empty_request(req);
- }
- }
- /* complete handler for MIDI1 EP-in requests */
- static void f_midi2_midi1_ep_in_complete(struct usb_ep *usb_ep,
- struct usb_request *req)
- {
- struct f_midi2_req_ctx *ctx = req->context;
- struct f_midi2 *midi2 = ctx->usb_ep->card;
- int status = req->status;
- put_empty_request(req);
- if (status) {
- DBG(midi2, "%s complete error %d: %d/%d\n",
- usb_ep->name, status, req->actual, req->length);
- return;
- }
- process_midi1_transmit(midi2);
- }
- /* complete handler for MIDI1 EP-out requests */
- static void f_midi2_midi1_ep_out_complete(struct usb_ep *usb_ep,
- struct usb_request *req)
- {
- struct f_midi2_req_ctx *ctx = req->context;
- struct f_midi2 *midi2 = ctx->usb_ep->card;
- struct f_midi2_ep *ep;
- struct ump_cvt_to_ump *cvt = &midi2->midi1_ump_cvt;
- static const u8 midi1_packet_bytes[16] = {
- 0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1
- };
- unsigned int group, cable, bytes, c, len;
- int status = req->status;
- const u8 *buf = req->buf;
- if (status) {
- DBG(midi2, "%s complete error %d: %d/%d\n",
- usb_ep->name, status, req->actual, req->length);
- goto error;
- }
- len = req->actual >> 2;
- for (; len; len--, buf += 4) {
- cable = *buf >> 4;
- ep = midi2->out_cable_mapping[cable].ep;
- if (!ep)
- continue;
- group = midi2->out_cable_mapping[cable].group;
- bytes = midi1_packet_bytes[*buf & 0x0f];
- for (c = 0; c < bytes; c++) {
- snd_ump_convert_to_ump(cvt, group,
- to_ump_protocol(ep->info.protocol),
- buf[c + 1]);
- if (cvt->ump_bytes) {
- snd_ump_receive(ep->ump, cvt->ump,
- cvt->ump_bytes);
- cvt->ump_bytes = 0;
- }
- }
- }
- if (midi2->operation_mode != MIDI_OP_MODE_MIDI1)
- goto error;
- if (queue_request_ep_raw(req))
- goto error;
- return;
- error:
- put_empty_request(req);
- }
- /*
- * Common EP handling helpers
- */
- /* Start MIDI EP */
- static int f_midi2_start_ep(struct f_midi2_usb_ep *usb_ep,
- struct usb_function *fn)
- {
- int err;
- if (!usb_ep->usb_ep)
- return 0;
- usb_ep_disable(usb_ep->usb_ep);
- err = config_ep_by_speed(usb_ep->card->gadget, fn, usb_ep->usb_ep);
- if (err)
- return err;
- return usb_ep_enable(usb_ep->usb_ep);
- }
- /* Drop pending requests */
- static void f_midi2_drop_reqs(struct f_midi2_usb_ep *usb_ep)
- {
- int i;
- if (!usb_ep->usb_ep || !usb_ep->num_reqs)
- return;
- for (i = 0; i < usb_ep->num_reqs; i++) {
- if (!test_bit(i, &usb_ep->free_reqs) && usb_ep->reqs[i].req) {
- usb_ep_dequeue(usb_ep->usb_ep, usb_ep->reqs[i].req);
- set_bit(i, &usb_ep->free_reqs);
- }
- }
- }
- /* Allocate requests for the given EP */
- static int f_midi2_alloc_ep_reqs(struct f_midi2_usb_ep *usb_ep)
- {
- struct f_midi2 *midi2 = usb_ep->card;
- int i;
- if (!usb_ep->usb_ep)
- return 0;
- if (!usb_ep->reqs)
- return -EINVAL;
- for (i = 0; i < midi2->info.num_reqs; i++) {
- if (usb_ep->reqs[i].req)
- continue;
- usb_ep->reqs[i].req = alloc_ep_req(usb_ep->usb_ep,
- midi2->info.req_buf_size);
- if (!usb_ep->reqs[i].req)
- return -ENOMEM;
- usb_ep->reqs[i].req->context = &usb_ep->reqs[i];
- }
- return 0;
- }
- /* Free allocated requests */
- static void f_midi2_free_ep_reqs(struct f_midi2_usb_ep *usb_ep)
- {
- struct f_midi2 *midi2 = usb_ep->card;
- int i;
- for (i = 0; i < midi2->info.num_reqs; i++) {
- if (!usb_ep->reqs[i].req)
- continue;
- free_ep_req(usb_ep->usb_ep, usb_ep->reqs[i].req);
- usb_ep->reqs[i].req = NULL;
- }
- }
- /* Initialize EP */
- static int f_midi2_init_ep(struct f_midi2 *midi2, struct f_midi2_ep *ep,
- struct f_midi2_usb_ep *usb_ep,
- void *desc,
- void (*complete)(struct usb_ep *usb_ep,
- struct usb_request *req))
- {
- int i;
- usb_ep->card = midi2;
- usb_ep->ep = ep;
- usb_ep->usb_ep = usb_ep_autoconfig(midi2->gadget, desc);
- if (!usb_ep->usb_ep)
- return -ENODEV;
- usb_ep->complete = complete;
- usb_ep->reqs = kcalloc(midi2->info.num_reqs, sizeof(*usb_ep->reqs),
- GFP_KERNEL);
- if (!usb_ep->reqs)
- return -ENOMEM;
- for (i = 0; i < midi2->info.num_reqs; i++) {
- usb_ep->reqs[i].index = i;
- usb_ep->reqs[i].usb_ep = usb_ep;
- set_bit(i, &usb_ep->free_reqs);
- usb_ep->num_reqs++;
- }
- return 0;
- }
- /* Free EP */
- static void f_midi2_free_ep(struct f_midi2_usb_ep *usb_ep)
- {
- f_midi2_drop_reqs(usb_ep);
- f_midi2_free_ep_reqs(usb_ep);
- kfree(usb_ep->reqs);
- usb_ep->num_reqs = 0;
- usb_ep->free_reqs = 0;
- usb_ep->reqs = NULL;
- }
- /* Queue requests for EP-out at start */
- static void f_midi2_queue_out_reqs(struct f_midi2_usb_ep *usb_ep)
- {
- int i, err;
- if (!usb_ep->usb_ep)
- return;
- for (i = 0; i < usb_ep->num_reqs; i++) {
- if (!test_bit(i, &usb_ep->free_reqs) || !usb_ep->reqs[i].req)
- continue;
- usb_ep->reqs[i].req->complete = usb_ep->complete;
- err = usb_ep_queue(usb_ep->usb_ep, usb_ep->reqs[i].req,
- GFP_ATOMIC);
- if (!err)
- clear_bit(i, &usb_ep->free_reqs);
- }
- }
- /*
- * Gadget Function callbacks
- */
- /* stop both IN and OUT EPs */
- static void f_midi2_stop_eps(struct f_midi2_usb_ep *ep_in,
- struct f_midi2_usb_ep *ep_out)
- {
- f_midi2_drop_reqs(ep_in);
- f_midi2_drop_reqs(ep_out);
- f_midi2_free_ep_reqs(ep_in);
- f_midi2_free_ep_reqs(ep_out);
- }
- /* start/queue both IN and OUT EPs */
- static int f_midi2_start_eps(struct f_midi2_usb_ep *ep_in,
- struct f_midi2_usb_ep *ep_out,
- struct usb_function *fn)
- {
- int err;
- err = f_midi2_start_ep(ep_in, fn);
- if (err)
- return err;
- err = f_midi2_start_ep(ep_out, fn);
- if (err)
- return err;
- err = f_midi2_alloc_ep_reqs(ep_in);
- if (err)
- return err;
- err = f_midi2_alloc_ep_reqs(ep_out);
- if (err)
- return err;
- f_midi2_queue_out_reqs(ep_out);
- return 0;
- }
- /* gadget function set_alt callback */
- static int f_midi2_set_alt(struct usb_function *fn, unsigned int intf,
- unsigned int alt)
- {
- struct f_midi2 *midi2 = func_to_midi2(fn);
- struct f_midi2_ep *ep;
- int i, op_mode, err;
- if (intf != midi2->midi_if || alt > 1)
- return 0;
- if (alt == 0)
- op_mode = MIDI_OP_MODE_MIDI1;
- else if (alt == 1)
- op_mode = MIDI_OP_MODE_MIDI2;
- else
- op_mode = MIDI_OP_MODE_UNSET;
- if (midi2->operation_mode == op_mode)
- return 0;
- midi2->operation_mode = op_mode;
- if (op_mode != MIDI_OP_MODE_MIDI1)
- f_midi2_stop_eps(&midi2->midi1_ep_in, &midi2->midi1_ep_out);
- if (op_mode != MIDI_OP_MODE_MIDI2) {
- for (i = 0; i < midi2->num_eps; i++) {
- ep = &midi2->midi2_eps[i];
- f_midi2_stop_eps(&ep->ep_in, &ep->ep_out);
- }
- }
- if (op_mode == MIDI_OP_MODE_MIDI1)
- return f_midi2_start_eps(&midi2->midi1_ep_in,
- &midi2->midi1_ep_out, fn);
- if (op_mode == MIDI_OP_MODE_MIDI2) {
- for (i = 0; i < midi2->num_eps; i++) {
- ep = &midi2->midi2_eps[i];
- err = f_midi2_start_eps(&ep->ep_in, &ep->ep_out, fn);
- if (err)
- return err;
- }
- }
- return 0;
- }
- /* gadget function get_alt callback */
- static int f_midi2_get_alt(struct usb_function *fn, unsigned int intf)
- {
- struct f_midi2 *midi2 = func_to_midi2(fn);
- if (intf == midi2->midi_if &&
- midi2->operation_mode == MIDI_OP_MODE_MIDI2)
- return 1;
- return 0;
- }
- /* convert UMP direction to USB MIDI 2.0 direction */
- static unsigned int ump_to_usb_dir(unsigned int ump_dir)
- {
- switch (ump_dir) {
- case SNDRV_UMP_DIR_INPUT:
- return USB_MS_GR_TRM_BLOCK_TYPE_INPUT_ONLY;
- case SNDRV_UMP_DIR_OUTPUT:
- return USB_MS_GR_TRM_BLOCK_TYPE_OUTPUT_ONLY;
- default:
- return USB_MS_GR_TRM_BLOCK_TYPE_BIDIRECTIONAL;
- }
- }
- /* assign GTB descriptors (for the given request) */
- static void assign_block_descriptors(struct f_midi2 *midi2,
- struct usb_request *req,
- int max_len)
- {
- struct usb_ms20_gr_trm_block_header_descriptor header;
- struct usb_ms20_gr_trm_block_descriptor *desc;
- struct f_midi2_block_info *b;
- struct f_midi2_ep *ep;
- int i, blk, len;
- char *data;
- len = sizeof(gtb_header_desc) + sizeof(gtb_desc) * midi2->total_blocks;
- if (WARN_ON(len > midi2->info.req_buf_size))
- return;
- header = gtb_header_desc;
- header.wTotalLength = cpu_to_le16(len);
- if (max_len < len) {
- len = min_t(int, len, sizeof(header));
- memcpy(req->buf, &header, len);
- req->length = len;
- req->zero = len < max_len;
- return;
- }
- memcpy(req->buf, &header, sizeof(header));
- data = req->buf + sizeof(header);
- for (i = 0; i < midi2->num_eps; i++) {
- ep = &midi2->midi2_eps[i];
- for (blk = 0; blk < ep->num_blks; blk++) {
- b = &ep->blks[blk].info;
- desc = (struct usb_ms20_gr_trm_block_descriptor *)data;
- *desc = gtb_desc;
- desc->bGrpTrmBlkID = ep->blks[blk].gtb_id;
- desc->bGrpTrmBlkType = ump_to_usb_dir(b->direction);
- desc->nGroupTrm = b->first_group;
- desc->nNumGroupTrm = b->num_groups;
- desc->iBlockItem = ep->blks[blk].string_id;
- if (ep->info.protocol == 2)
- desc->bMIDIProtocol = USB_MS_MIDI_PROTO_2_0;
- else
- desc->bMIDIProtocol = USB_MS_MIDI_PROTO_1_0_128;
- if (b->is_midi1 == 2) {
- desc->wMaxInputBandwidth = cpu_to_le16(1);
- desc->wMaxOutputBandwidth = cpu_to_le16(1);
- }
- data += sizeof(*desc);
- }
- }
- req->length = len;
- req->zero = len < max_len;
- }
- /* gadget function setup callback: handle GTB requests */
- static int f_midi2_setup(struct usb_function *fn,
- const struct usb_ctrlrequest *ctrl)
- {
- struct f_midi2 *midi2 = func_to_midi2(fn);
- struct usb_composite_dev *cdev = fn->config->cdev;
- struct usb_request *req = cdev->req;
- u16 value, length;
- if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD ||
- ctrl->bRequest != USB_REQ_GET_DESCRIPTOR)
- return -EOPNOTSUPP;
- value = le16_to_cpu(ctrl->wValue);
- length = le16_to_cpu(ctrl->wLength);
- if ((value >> 8) != USB_DT_CS_GR_TRM_BLOCK)
- return -EOPNOTSUPP;
- /* handle only altset 1 */
- if ((value & 0xff) != 1)
- return -EOPNOTSUPP;
- assign_block_descriptors(midi2, req, length);
- return usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
- }
- /* gadget function disable callback */
- static void f_midi2_disable(struct usb_function *fn)
- {
- struct f_midi2 *midi2 = func_to_midi2(fn);
- midi2->operation_mode = MIDI_OP_MODE_UNSET;
- }
- /*
- * ALSA UMP ops: most of them are NOPs, only trigger for write is needed
- */
- static int f_midi2_ump_open(struct snd_ump_endpoint *ump, int dir)
- {
- return 0;
- }
- static void f_midi2_ump_close(struct snd_ump_endpoint *ump, int dir)
- {
- }
- static void f_midi2_ump_trigger(struct snd_ump_endpoint *ump, int dir, int up)
- {
- struct f_midi2_ep *ep = ump->private_data;
- struct f_midi2 *midi2 = ep->card;
- if (up && dir == SNDRV_RAWMIDI_STREAM_OUTPUT) {
- switch (midi2->operation_mode) {
- case MIDI_OP_MODE_MIDI1:
- process_midi1_transmit(midi2);
- break;
- case MIDI_OP_MODE_MIDI2:
- process_ump_transmit(ep);
- break;
- }
- }
- }
- static void f_midi2_ump_drain(struct snd_ump_endpoint *ump, int dir)
- {
- }
- static const struct snd_ump_ops f_midi2_ump_ops = {
- .open = f_midi2_ump_open,
- .close = f_midi2_ump_close,
- .trigger = f_midi2_ump_trigger,
- .drain = f_midi2_ump_drain,
- };
- /*
- * "Operation Mode" control element
- */
- static int f_midi2_operation_mode_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
- {
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 1;
- uinfo->value.integer.min = MIDI_OP_MODE_UNSET;
- uinfo->value.integer.max = MIDI_OP_MODE_MIDI2;
- return 0;
- }
- static int f_midi2_operation_mode_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
- {
- struct f_midi2 *midi2 = snd_kcontrol_chip(kcontrol);
- ucontrol->value.integer.value[0] = midi2->operation_mode;
- return 0;
- }
- static const struct snd_kcontrol_new operation_mode_ctl = {
- .iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
- .name = "Operation Mode",
- .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
- .info = f_midi2_operation_mode_info,
- .get = f_midi2_operation_mode_get,
- };
- /*
- * ALSA UMP instance creation / deletion
- */
- static void f_midi2_free_card(struct f_midi2 *midi2)
- {
- if (midi2->card) {
- snd_card_free_when_closed(midi2->card);
- midi2->card = NULL;
- }
- }
- /* use a reverse direction for the gadget host */
- static int reverse_dir(int dir)
- {
- if (!dir || dir == SNDRV_UMP_DIR_BIDIRECTION)
- return dir;
- return (dir == SNDRV_UMP_DIR_OUTPUT) ?
- SNDRV_UMP_DIR_INPUT : SNDRV_UMP_DIR_OUTPUT;
- }
- static int f_midi2_create_card(struct f_midi2 *midi2)
- {
- struct snd_card *card;
- struct snd_ump_endpoint *ump;
- struct f_midi2_ep *ep;
- int i, id, blk, err;
- __be32 sw;
- err = snd_card_new(&midi2->gadget->dev, -1, NULL, THIS_MODULE, 0,
- &card);
- if (err < 0)
- return err;
- midi2->card = card;
- strcpy(card->driver, "f_midi2");
- strcpy(card->shortname, "MIDI 2.0 Gadget");
- strcpy(card->longname, "MIDI 2.0 Gadget");
- id = 0;
- for (i = 0; i < midi2->num_eps; i++) {
- ep = &midi2->midi2_eps[i];
- err = snd_ump_endpoint_new(card, "MIDI 2.0 Gadget", id,
- 1, 1, &ump);
- if (err < 0)
- goto error;
- id++;
- ep->ump = ump;
- ump->no_process_stream = true;
- ump->private_data = ep;
- ump->ops = &f_midi2_ump_ops;
- if (midi2->info.static_block)
- ump->info.flags |= SNDRV_UMP_EP_INFO_STATIC_BLOCKS;
- ump->info.protocol_caps = (ep->info.protocol_caps & 3) << 8;
- ump->info.protocol = to_ump_protocol(ep->info.protocol);
- ump->info.version = 0x0101;
- ump->info.family_id = ep->info.family;
- ump->info.model_id = ep->info.model;
- ump->info.manufacturer_id = ep->info.manufacturer & 0xffffff;
- sw = cpu_to_be32(ep->info.sw_revision);
- memcpy(ump->info.sw_revision, &sw, 4);
- strscpy(ump->info.name, ump_ep_name(ep),
- sizeof(ump->info.name));
- strscpy(ump->info.product_id, ump_product_id(ep),
- sizeof(ump->info.product_id));
- strscpy(ump->core.name, ump->info.name, sizeof(ump->core.name));
- for (blk = 0; blk < ep->num_blks; blk++) {
- const struct f_midi2_block_info *b = &ep->blks[blk].info;
- struct snd_ump_block *fb;
- err = snd_ump_block_new(ump, blk,
- reverse_dir(b->direction),
- b->first_group, b->num_groups,
- &ep->blks[blk].fb);
- if (err < 0)
- goto error;
- fb = ep->blks[blk].fb;
- fb->info.active = !!b->active;
- fb->info.midi_ci_version = b->midi_ci_version;
- fb->info.ui_hint = reverse_dir(b->ui_hint);
- fb->info.sysex8_streams = b->sysex8_streams;
- if (b->is_midi1 < 2)
- fb->info.flags |= b->is_midi1;
- else
- fb->info.flags |= SNDRV_UMP_BLOCK_IS_MIDI1 |
- SNDRV_UMP_BLOCK_IS_LOWSPEED;
- strscpy(fb->info.name, ump_fb_name(b),
- sizeof(fb->info.name));
- }
- snd_ump_update_group_attrs(ump);
- }
- for (i = 0; i < midi2->num_eps; i++) {
- err = snd_ump_attach_legacy_rawmidi(midi2->midi2_eps[i].ump,
- "Legacy MIDI", id);
- if (err < 0)
- goto error;
- id++;
- }
- err = snd_ctl_add(card, snd_ctl_new1(&operation_mode_ctl, midi2));
- if (err < 0)
- goto error;
- err = snd_card_register(card);
- if (err < 0)
- goto error;
- return 0;
- error:
- f_midi2_free_card(midi2);
- return err;
- }
- /*
- * Creation of USB descriptors
- */
- struct f_midi2_usb_config {
- struct usb_descriptor_header **list;
- unsigned int size;
- unsigned int alloc;
- /* MIDI 1.0 jacks */
- unsigned char jack_in, jack_out, jack_id;
- struct usb_midi_in_jack_descriptor jack_ins[MAX_CABLES];
- struct usb_midi_out_jack_descriptor_1 jack_outs[MAX_CABLES];
- };
- static int append_config(struct f_midi2_usb_config *config, void *d)
- {
- unsigned int size;
- void *buf;
- if (config->size + 2 >= config->alloc) {
- size = config->size + 16;
- buf = krealloc(config->list, size * sizeof(void *), GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
- config->list = buf;
- config->alloc = size;
- }
- config->list[config->size] = d;
- config->size++;
- config->list[config->size] = NULL;
- return 0;
- }
- static int append_configs(struct f_midi2_usb_config *config, void **d)
- {
- int err;
- for (; *d; d++) {
- err = append_config(config, *d);
- if (err)
- return err;
- }
- return 0;
- }
- static int append_midi1_in_jack(struct f_midi2 *midi2,
- struct f_midi2_usb_config *config,
- struct midi1_cable_mapping *map,
- unsigned int type)
- {
- struct usb_midi_in_jack_descriptor *jack =
- &config->jack_ins[config->jack_in++];
- int id = ++config->jack_id;
- int err;
- jack->bLength = 0x06;
- jack->bDescriptorType = USB_DT_CS_INTERFACE;
- jack->bDescriptorSubtype = USB_MS_MIDI_IN_JACK;
- jack->bJackType = type;
- jack->bJackID = id;
- /* use the corresponding block name as jack name */
- if (map->ep)
- jack->iJack = map->ep->blks[map->block].string_id;
- err = append_config(config, jack);
- if (err < 0)
- return err;
- return id;
- }
- static int append_midi1_out_jack(struct f_midi2 *midi2,
- struct f_midi2_usb_config *config,
- struct midi1_cable_mapping *map,
- unsigned int type, unsigned int source)
- {
- struct usb_midi_out_jack_descriptor_1 *jack =
- &config->jack_outs[config->jack_out++];
- int id = ++config->jack_id;
- int err;
- jack->bLength = 0x09;
- jack->bDescriptorType = USB_DT_CS_INTERFACE;
- jack->bDescriptorSubtype = USB_MS_MIDI_OUT_JACK;
- jack->bJackType = type;
- jack->bJackID = id;
- jack->bNrInputPins = 1;
- jack->pins[0].baSourceID = source;
- jack->pins[0].baSourcePin = 0x01;
- /* use the corresponding block name as jack name */
- if (map->ep)
- jack->iJack = map->ep->blks[map->block].string_id;
- err = append_config(config, jack);
- if (err < 0)
- return err;
- return id;
- }
- static int f_midi2_create_usb_configs(struct f_midi2 *midi2,
- struct f_midi2_usb_config *config,
- int speed)
- {
- void **midi1_in_eps, **midi1_out_eps;
- int i, jack, total;
- int err;
- switch (speed) {
- default:
- case USB_SPEED_HIGH:
- midi2_midi1_ep_out_desc.wMaxPacketSize = cpu_to_le16(512);
- midi2_midi1_ep_in_desc.wMaxPacketSize = cpu_to_le16(512);
- for (i = 0; i < midi2->num_eps; i++) {
- midi2_midi2_ep_out_desc[i].wMaxPacketSize =
- cpu_to_le16(512);
- midi2_midi2_ep_in_desc[i].wMaxPacketSize =
- cpu_to_le16(512);
- }
- fallthrough;
- case USB_SPEED_FULL:
- midi1_in_eps = midi2_midi1_ep_in_descs;
- midi1_out_eps = midi2_midi1_ep_out_descs;
- break;
- case USB_SPEED_SUPER:
- midi2_midi1_ep_out_desc.wMaxPacketSize = cpu_to_le16(1024);
- midi2_midi1_ep_in_desc.wMaxPacketSize = cpu_to_le16(1024);
- for (i = 0; i < midi2->num_eps; i++) {
- midi2_midi2_ep_out_desc[i].wMaxPacketSize =
- cpu_to_le16(1024);
- midi2_midi2_ep_in_desc[i].wMaxPacketSize =
- cpu_to_le16(1024);
- }
- midi1_in_eps = midi2_midi1_ep_in_ss_descs;
- midi1_out_eps = midi2_midi1_ep_out_ss_descs;
- break;
- }
- err = append_configs(config, midi2_audio_descs);
- if (err < 0)
- return err;
- if (midi2->num_midi1_in && midi2->num_midi1_out)
- midi2_midi1_if_desc.bNumEndpoints = 2;
- else
- midi2_midi1_if_desc.bNumEndpoints = 1;
- err = append_configs(config, midi2_midi1_descs);
- if (err < 0)
- return err;
- total = USB_DT_MS_HEADER_SIZE;
- if (midi2->num_midi1_out) {
- midi2_midi1_ep_out_class_desc.bLength =
- USB_DT_MS_ENDPOINT_SIZE(midi2->num_midi1_out);
- total += midi2_midi1_ep_out_class_desc.bLength;
- midi2_midi1_ep_out_class_desc.bNumEmbMIDIJack =
- midi2->num_midi1_out;
- total += midi2->num_midi1_out *
- (USB_DT_MIDI_IN_SIZE + USB_DT_MIDI_OUT_SIZE(1));
- for (i = 0; i < midi2->num_midi1_out; i++) {
- jack = append_midi1_in_jack(midi2, config,
- &midi2->in_cable_mapping[i],
- USB_MS_EMBEDDED);
- if (jack < 0)
- return jack;
- midi2_midi1_ep_out_class_desc.baAssocJackID[i] = jack;
- jack = append_midi1_out_jack(midi2, config,
- &midi2->in_cable_mapping[i],
- USB_MS_EXTERNAL, jack);
- if (jack < 0)
- return jack;
- }
- }
- if (midi2->num_midi1_in) {
- midi2_midi1_ep_in_class_desc.bLength =
- USB_DT_MS_ENDPOINT_SIZE(midi2->num_midi1_in);
- total += midi2_midi1_ep_in_class_desc.bLength;
- midi2_midi1_ep_in_class_desc.bNumEmbMIDIJack =
- midi2->num_midi1_in;
- total += midi2->num_midi1_in *
- (USB_DT_MIDI_IN_SIZE + USB_DT_MIDI_OUT_SIZE(1));
- for (i = 0; i < midi2->num_midi1_in; i++) {
- jack = append_midi1_in_jack(midi2, config,
- &midi2->out_cable_mapping[i],
- USB_MS_EXTERNAL);
- if (jack < 0)
- return jack;
- jack = append_midi1_out_jack(midi2, config,
- &midi2->out_cable_mapping[i],
- USB_MS_EMBEDDED, jack);
- if (jack < 0)
- return jack;
- midi2_midi1_ep_in_class_desc.baAssocJackID[i] = jack;
- }
- }
- midi2_midi1_class_desc.wTotalLength = cpu_to_le16(total);
- if (midi2->num_midi1_out) {
- err = append_configs(config, midi1_out_eps);
- if (err < 0)
- return err;
- }
- if (midi2->num_midi1_in) {
- err = append_configs(config, midi1_in_eps);
- if (err < 0)
- return err;
- }
- err = append_configs(config, midi2_midi2_descs);
- if (err < 0)
- return err;
- for (i = 0; i < midi2->num_eps; i++) {
- err = append_config(config, &midi2_midi2_ep_out_desc[i]);
- if (err < 0)
- return err;
- if (speed == USB_SPEED_SUPER || speed == USB_SPEED_SUPER_PLUS) {
- err = append_config(config, &midi2_midi2_ep_out_ss_comp_desc);
- if (err < 0)
- return err;
- }
- err = append_config(config, &midi2_midi2_ep_out_class_desc[i]);
- if (err < 0)
- return err;
- err = append_config(config, &midi2_midi2_ep_in_desc[i]);
- if (err < 0)
- return err;
- if (speed == USB_SPEED_SUPER || speed == USB_SPEED_SUPER_PLUS) {
- err = append_config(config, &midi2_midi2_ep_in_ss_comp_desc);
- if (err < 0)
- return err;
- }
- err = append_config(config, &midi2_midi2_ep_in_class_desc[i]);
- if (err < 0)
- return err;
- }
- return 0;
- }
- static void f_midi2_free_usb_configs(struct f_midi2_usb_config *config)
- {
- kfree(config->list);
- memset(config, 0, sizeof(*config));
- }
- /* as we use the static descriptors for simplicity, serialize bind call */
- static DEFINE_MUTEX(f_midi2_desc_mutex);
- /* fill MIDI2 EP class-specific descriptor */
- static void fill_midi2_class_desc(struct f_midi2_ep *ep,
- struct usb_ms20_endpoint_descriptor_32 *cdesc)
- {
- int blk;
- cdesc->bLength = USB_DT_MS20_ENDPOINT_SIZE(ep->num_blks);
- cdesc->bDescriptorType = USB_DT_CS_ENDPOINT;
- cdesc->bDescriptorSubtype = USB_MS_GENERAL_2_0;
- cdesc->bNumGrpTrmBlock = ep->num_blks;
- for (blk = 0; blk < ep->num_blks; blk++)
- cdesc->baAssoGrpTrmBlkID[blk] = ep->blks[blk].gtb_id;
- }
- /* initialize MIDI2 EP-in */
- static int f_midi2_init_midi2_ep_in(struct f_midi2 *midi2, int index)
- {
- struct f_midi2_ep *ep = &midi2->midi2_eps[index];
- struct usb_endpoint_descriptor *desc = &midi2_midi2_ep_in_desc[index];
- desc->bLength = USB_DT_ENDPOINT_SIZE;
- desc->bDescriptorType = USB_DT_ENDPOINT;
- desc->bEndpointAddress = USB_DIR_IN;
- desc->bmAttributes = USB_ENDPOINT_XFER_INT;
- desc->wMaxPacketSize = cpu_to_le16(EP_MAX_PACKET_INT);
- desc->bInterval = 1;
- fill_midi2_class_desc(ep, &midi2_midi2_ep_in_class_desc[index]);
- return f_midi2_init_ep(midi2, ep, &ep->ep_in, desc,
- f_midi2_ep_in_complete);
- }
- /* initialize MIDI2 EP-out */
- static int f_midi2_init_midi2_ep_out(struct f_midi2 *midi2, int index)
- {
- struct f_midi2_ep *ep = &midi2->midi2_eps[index];
- struct usb_endpoint_descriptor *desc = &midi2_midi2_ep_out_desc[index];
- desc->bLength = USB_DT_ENDPOINT_SIZE;
- desc->bDescriptorType = USB_DT_ENDPOINT;
- desc->bEndpointAddress = USB_DIR_OUT;
- desc->bmAttributes = USB_ENDPOINT_XFER_BULK;
- fill_midi2_class_desc(ep, &midi2_midi2_ep_out_class_desc[index]);
- return f_midi2_init_ep(midi2, ep, &ep->ep_out, desc,
- f_midi2_ep_out_complete);
- }
- /* gadget function bind callback */
- static int f_midi2_bind(struct usb_configuration *c, struct usb_function *f)
- {
- struct usb_composite_dev *cdev = c->cdev;
- struct f_midi2 *midi2 = func_to_midi2(f);
- struct f_midi2_ep *ep;
- struct f_midi2_usb_config config = {};
- struct usb_gadget_strings string_fn = {
- .language = 0x0409, /* en-us */
- .strings = midi2->string_defs,
- };
- struct usb_gadget_strings *strings[] = {
- &string_fn,
- NULL,
- };
- int i, blk, status;
- midi2->gadget = cdev->gadget;
- midi2->operation_mode = MIDI_OP_MODE_UNSET;
- status = f_midi2_create_card(midi2);
- if (status < 0)
- goto fail_register;
- /* maybe allocate device-global string ID */
- midi2->strings = usb_gstrings_attach(c->cdev, strings,
- midi2->total_blocks + 1);
- if (IS_ERR(midi2->strings)) {
- status = PTR_ERR(midi2->strings);
- goto fail_string;
- }
- mutex_lock(&f_midi2_desc_mutex);
- midi2_midi1_if_desc.iInterface = midi2->strings[STR_IFACE].id;
- midi2_midi2_if_desc.iInterface = midi2->strings[STR_IFACE].id;
- for (i = 0; i < midi2->num_eps; i++) {
- ep = &midi2->midi2_eps[i];
- for (blk = 0; blk < ep->num_blks; blk++)
- ep->blks[blk].string_id =
- midi2->strings[gtb_to_str_id(ep->blks[blk].gtb_id)].id;
- }
- midi2_midi2_if_desc.bNumEndpoints = midi2->num_eps * 2;
- /* audio interface */
- status = usb_interface_id(c, f);
- if (status < 0)
- goto fail;
- midi2_audio_if_desc.bInterfaceNumber = status;
- /* MIDI streaming */
- status = usb_interface_id(c, f);
- if (status < 0)
- goto fail;
- midi2->midi_if = status;
- midi2_midi1_if_desc.bInterfaceNumber = status;
- midi2_midi2_if_desc.bInterfaceNumber = status;
- midi2_audio_class_desc.baInterfaceNr[0] = status;
- /* allocate instance-specific endpoints */
- if (midi2->midi2_eps[0].blks[0].info.direction != SNDRV_UMP_DIR_OUTPUT) {
- status = f_midi2_init_ep(midi2, NULL, &midi2->midi1_ep_in,
- &midi2_midi1_ep_in_desc,
- f_midi2_midi1_ep_in_complete);
- if (status)
- goto fail;
- }
- if (midi2->midi2_eps[0].blks[0].info.direction != SNDRV_UMP_DIR_INPUT) {
- status = f_midi2_init_ep(midi2, NULL, &midi2->midi1_ep_out,
- &midi2_midi1_ep_out_desc,
- f_midi2_midi1_ep_out_complete);
- if (status)
- goto fail;
- }
- for (i = 0; i < midi2->num_eps; i++) {
- status = f_midi2_init_midi2_ep_in(midi2, i);
- if (status)
- goto fail;
- status = f_midi2_init_midi2_ep_out(midi2, i);
- if (status)
- goto fail;
- }
- status = f_midi2_create_usb_configs(midi2, &config, USB_SPEED_FULL);
- if (status < 0)
- goto fail;
- f->fs_descriptors = usb_copy_descriptors(config.list);
- if (!f->fs_descriptors) {
- status = -ENOMEM;
- goto fail;
- }
- f_midi2_free_usb_configs(&config);
- status = f_midi2_create_usb_configs(midi2, &config, USB_SPEED_HIGH);
- if (status < 0)
- goto fail;
- f->hs_descriptors = usb_copy_descriptors(config.list);
- if (!f->hs_descriptors) {
- status = -ENOMEM;
- goto fail;
- }
- f_midi2_free_usb_configs(&config);
- status = f_midi2_create_usb_configs(midi2, &config, USB_SPEED_SUPER);
- if (status < 0)
- goto fail;
- f->ss_descriptors = usb_copy_descriptors(config.list);
- if (!f->ss_descriptors) {
- status = -ENOMEM;
- goto fail;
- }
- f_midi2_free_usb_configs(&config);
- mutex_unlock(&f_midi2_desc_mutex);
- return 0;
- fail:
- f_midi2_free_usb_configs(&config);
- mutex_unlock(&f_midi2_desc_mutex);
- usb_free_all_descriptors(f);
- fail_string:
- f_midi2_free_card(midi2);
- fail_register:
- ERROR(midi2, "%s: can't bind, err %d\n", f->name, status);
- return status;
- }
- /* gadget function unbind callback */
- static void f_midi2_unbind(struct usb_configuration *c, struct usb_function *f)
- {
- struct f_midi2 *midi2 = func_to_midi2(f);
- int i;
- f_midi2_free_card(midi2);
- f_midi2_free_ep(&midi2->midi1_ep_in);
- f_midi2_free_ep(&midi2->midi1_ep_out);
- for (i = 0; i < midi2->num_eps; i++) {
- f_midi2_free_ep(&midi2->midi2_eps[i].ep_in);
- f_midi2_free_ep(&midi2->midi2_eps[i].ep_out);
- }
- usb_free_all_descriptors(f);
- }
- /*
- * ConfigFS interface
- */
- /* type conversion helpers */
- static inline struct f_midi2_opts *to_f_midi2_opts(struct config_item *item)
- {
- return container_of(to_config_group(item), struct f_midi2_opts,
- func_inst.group);
- }
- static inline struct f_midi2_ep_opts *
- to_f_midi2_ep_opts(struct config_item *item)
- {
- return container_of(to_config_group(item), struct f_midi2_ep_opts,
- group);
- }
- static inline struct f_midi2_block_opts *
- to_f_midi2_block_opts(struct config_item *item)
- {
- return container_of(to_config_group(item), struct f_midi2_block_opts,
- group);
- }
- /* trim the string to be usable for EP and FB name strings */
- static void make_name_string(char *s)
- {
- char *p;
- p = strchr(s, '\n');
- if (p)
- *p = 0;
- p = s + strlen(s);
- for (; p > s && isspace(*p); p--)
- *p = 0;
- }
- /* configfs helpers: generic show/store for unisnged int */
- static ssize_t f_midi2_opts_uint_show(struct f_midi2_opts *opts,
- u32 val, const char *format, char *page)
- {
- int result;
- mutex_lock(&opts->lock);
- result = sprintf(page, format, val);
- mutex_unlock(&opts->lock);
- return result;
- }
- static ssize_t f_midi2_opts_uint_store(struct f_midi2_opts *opts,
- u32 *valp, u32 minval, u32 maxval,
- const char *page, size_t len)
- {
- int ret;
- u32 val;
- mutex_lock(&opts->lock);
- if (opts->refcnt) {
- ret = -EBUSY;
- goto end;
- }
- ret = kstrtou32(page, 0, &val);
- if (ret)
- goto end;
- if (val < minval || val > maxval) {
- ret = -EINVAL;
- goto end;
- }
- *valp = val;
- ret = len;
- end:
- mutex_unlock(&opts->lock);
- return ret;
- }
- /* generic store for bool */
- static ssize_t f_midi2_opts_bool_store(struct f_midi2_opts *opts,
- bool *valp, const char *page, size_t len)
- {
- int ret;
- bool val;
- mutex_lock(&opts->lock);
- if (opts->refcnt) {
- ret = -EBUSY;
- goto end;
- }
- ret = kstrtobool(page, &val);
- if (ret)
- goto end;
- *valp = val;
- ret = len;
- end:
- mutex_unlock(&opts->lock);
- return ret;
- }
- /* generic show/store for string */
- static ssize_t f_midi2_opts_str_show(struct f_midi2_opts *opts,
- const char *str, char *page)
- {
- int result = 0;
- mutex_lock(&opts->lock);
- if (str)
- result = scnprintf(page, PAGE_SIZE, "%s\n", str);
- mutex_unlock(&opts->lock);
- return result;
- }
- static ssize_t f_midi2_opts_str_store(struct f_midi2_opts *opts,
- const char **strp, size_t maxlen,
- const char *page, size_t len)
- {
- char *c;
- int ret;
- mutex_lock(&opts->lock);
- if (opts->refcnt) {
- ret = -EBUSY;
- goto end;
- }
- c = kstrndup(page, min(len, maxlen), GFP_KERNEL);
- if (!c) {
- ret = -ENOMEM;
- goto end;
- }
- kfree(*strp);
- make_name_string(c);
- *strp = c;
- ret = len;
- end:
- mutex_unlock(&opts->lock);
- return ret;
- }
- /*
- * Definitions for UMP Block config
- */
- /* define an uint option for block */
- #define F_MIDI2_BLOCK_OPT(name, format, minval, maxval) \
- static ssize_t f_midi2_block_opts_##name##_show(struct config_item *item,\
- char *page) \
- { \
- struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item); \
- return f_midi2_opts_uint_show(opts->ep->opts, opts->info.name, \
- format "\n", page); \
- } \
- \
- static ssize_t f_midi2_block_opts_##name##_store(struct config_item *item,\
- const char *page, size_t len) \
- { \
- struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item); \
- return f_midi2_opts_uint_store(opts->ep->opts, &opts->info.name,\
- minval, maxval, page, len); \
- } \
- \
- CONFIGFS_ATTR(f_midi2_block_opts_, name)
- /* define a boolean option for block */
- #define F_MIDI2_BLOCK_BOOL_OPT(name) \
- static ssize_t f_midi2_block_opts_##name##_show(struct config_item *item,\
- char *page) \
- { \
- struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item); \
- return f_midi2_opts_uint_show(opts->ep->opts, opts->info.name, \
- "%u\n", page); \
- } \
- \
- static ssize_t f_midi2_block_opts_##name##_store(struct config_item *item,\
- const char *page, size_t len) \
- { \
- struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item); \
- return f_midi2_opts_bool_store(opts->ep->opts, &opts->info.name,\
- page, len); \
- } \
- \
- CONFIGFS_ATTR(f_midi2_block_opts_, name)
- F_MIDI2_BLOCK_OPT(direction, "0x%x", 1, 3);
- F_MIDI2_BLOCK_OPT(first_group, "0x%x", 0, 15);
- F_MIDI2_BLOCK_OPT(num_groups, "0x%x", 1, 16);
- F_MIDI2_BLOCK_OPT(midi1_first_group, "0x%x", 0, 15);
- F_MIDI2_BLOCK_OPT(midi1_num_groups, "0x%x", 0, 16);
- F_MIDI2_BLOCK_OPT(ui_hint, "0x%x", 0, 3);
- F_MIDI2_BLOCK_OPT(midi_ci_version, "%u", 0, 1);
- F_MIDI2_BLOCK_OPT(sysex8_streams, "%u", 0, 255);
- F_MIDI2_BLOCK_OPT(is_midi1, "%u", 0, 2);
- F_MIDI2_BLOCK_BOOL_OPT(active);
- static ssize_t f_midi2_block_opts_name_show(struct config_item *item,
- char *page)
- {
- struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item);
- return f_midi2_opts_str_show(opts->ep->opts, opts->info.name, page);
- }
- static ssize_t f_midi2_block_opts_name_store(struct config_item *item,
- const char *page, size_t len)
- {
- struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item);
- return f_midi2_opts_str_store(opts->ep->opts, &opts->info.name, 128,
- page, len);
- }
- CONFIGFS_ATTR(f_midi2_block_opts_, name);
- static struct configfs_attribute *f_midi2_block_attrs[] = {
- &f_midi2_block_opts_attr_direction,
- &f_midi2_block_opts_attr_first_group,
- &f_midi2_block_opts_attr_num_groups,
- &f_midi2_block_opts_attr_midi1_first_group,
- &f_midi2_block_opts_attr_midi1_num_groups,
- &f_midi2_block_opts_attr_ui_hint,
- &f_midi2_block_opts_attr_midi_ci_version,
- &f_midi2_block_opts_attr_sysex8_streams,
- &f_midi2_block_opts_attr_is_midi1,
- &f_midi2_block_opts_attr_active,
- &f_midi2_block_opts_attr_name,
- NULL,
- };
- static void f_midi2_block_opts_release(struct config_item *item)
- {
- struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item);
- kfree(opts->info.name);
- kfree(opts);
- }
- static struct configfs_item_operations f_midi2_block_item_ops = {
- .release = f_midi2_block_opts_release,
- };
- static const struct config_item_type f_midi2_block_type = {
- .ct_item_ops = &f_midi2_block_item_ops,
- .ct_attrs = f_midi2_block_attrs,
- .ct_owner = THIS_MODULE,
- };
- /* create a f_midi2_block_opts instance for the given block number */
- static int f_midi2_block_opts_create(struct f_midi2_ep_opts *ep_opts,
- unsigned int blk,
- struct f_midi2_block_opts **block_p)
- {
- struct f_midi2_block_opts *block_opts;
- int ret = 0;
- mutex_lock(&ep_opts->opts->lock);
- if (ep_opts->opts->refcnt || ep_opts->blks[blk]) {
- ret = -EBUSY;
- goto out;
- }
- block_opts = kzalloc(sizeof(*block_opts), GFP_KERNEL);
- if (!block_opts) {
- ret = -ENOMEM;
- goto out;
- }
- block_opts->ep = ep_opts;
- block_opts->id = blk;
- /* set up the default values */
- block_opts->info.direction = SNDRV_UMP_DIR_BIDIRECTION;
- block_opts->info.first_group = 0;
- block_opts->info.num_groups = 1;
- block_opts->info.ui_hint = SNDRV_UMP_BLOCK_UI_HINT_BOTH;
- block_opts->info.active = 1;
- ep_opts->blks[blk] = block_opts;
- *block_p = block_opts;
- out:
- mutex_unlock(&ep_opts->opts->lock);
- return ret;
- }
- /* make_group callback for a block */
- static struct config_group *
- f_midi2_opts_block_make(struct config_group *group, const char *name)
- {
- struct f_midi2_ep_opts *ep_opts;
- struct f_midi2_block_opts *block_opts;
- unsigned int blk;
- int ret;
- if (strncmp(name, "block.", 6))
- return ERR_PTR(-EINVAL);
- ret = kstrtouint(name + 6, 10, &blk);
- if (ret)
- return ERR_PTR(ret);
- ep_opts = to_f_midi2_ep_opts(&group->cg_item);
- if (blk >= SNDRV_UMP_MAX_BLOCKS)
- return ERR_PTR(-EINVAL);
- if (ep_opts->blks[blk])
- return ERR_PTR(-EBUSY);
- ret = f_midi2_block_opts_create(ep_opts, blk, &block_opts);
- if (ret)
- return ERR_PTR(ret);
- config_group_init_type_name(&block_opts->group, name,
- &f_midi2_block_type);
- return &block_opts->group;
- }
- /* drop_item callback for a block */
- static void
- f_midi2_opts_block_drop(struct config_group *group, struct config_item *item)
- {
- struct f_midi2_block_opts *block_opts = to_f_midi2_block_opts(item);
- mutex_lock(&block_opts->ep->opts->lock);
- block_opts->ep->blks[block_opts->id] = NULL;
- mutex_unlock(&block_opts->ep->opts->lock);
- config_item_put(item);
- }
- /*
- * Definitions for UMP Endpoint config
- */
- /* define an uint option for EP */
- #define F_MIDI2_EP_OPT(name, format, minval, maxval) \
- static ssize_t f_midi2_ep_opts_##name##_show(struct config_item *item, \
- char *page) \
- { \
- struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item); \
- return f_midi2_opts_uint_show(opts->opts, opts->info.name, \
- format "\n", page); \
- } \
- \
- static ssize_t f_midi2_ep_opts_##name##_store(struct config_item *item, \
- const char *page, size_t len)\
- { \
- struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item); \
- return f_midi2_opts_uint_store(opts->opts, &opts->info.name, \
- minval, maxval, page, len); \
- } \
- \
- CONFIGFS_ATTR(f_midi2_ep_opts_, name)
- /* define a string option for EP */
- #define F_MIDI2_EP_STR_OPT(name, maxlen) \
- static ssize_t f_midi2_ep_opts_##name##_show(struct config_item *item, \
- char *page) \
- { \
- struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item); \
- return f_midi2_opts_str_show(opts->opts, opts->info.name, page);\
- } \
- \
- static ssize_t f_midi2_ep_opts_##name##_store(struct config_item *item, \
- const char *page, size_t len) \
- { \
- struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item); \
- return f_midi2_opts_str_store(opts->opts, &opts->info.name, maxlen,\
- page, len); \
- } \
- \
- CONFIGFS_ATTR(f_midi2_ep_opts_, name)
- F_MIDI2_EP_OPT(protocol, "0x%x", 1, 2);
- F_MIDI2_EP_OPT(protocol_caps, "0x%x", 1, 3);
- F_MIDI2_EP_OPT(manufacturer, "0x%x", 0, 0xffffff);
- F_MIDI2_EP_OPT(family, "0x%x", 0, 0xffff);
- F_MIDI2_EP_OPT(model, "0x%x", 0, 0xffff);
- F_MIDI2_EP_OPT(sw_revision, "0x%x", 0, 0xffffffff);
- F_MIDI2_EP_STR_OPT(ep_name, 128);
- F_MIDI2_EP_STR_OPT(product_id, 128);
- static struct configfs_attribute *f_midi2_ep_attrs[] = {
- &f_midi2_ep_opts_attr_protocol,
- &f_midi2_ep_opts_attr_protocol_caps,
- &f_midi2_ep_opts_attr_ep_name,
- &f_midi2_ep_opts_attr_product_id,
- &f_midi2_ep_opts_attr_manufacturer,
- &f_midi2_ep_opts_attr_family,
- &f_midi2_ep_opts_attr_model,
- &f_midi2_ep_opts_attr_sw_revision,
- NULL,
- };
- static void f_midi2_ep_opts_release(struct config_item *item)
- {
- struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
- kfree(opts->info.ep_name);
- kfree(opts->info.product_id);
- kfree(opts);
- }
- static struct configfs_item_operations f_midi2_ep_item_ops = {
- .release = f_midi2_ep_opts_release,
- };
- static struct configfs_group_operations f_midi2_ep_group_ops = {
- .make_group = f_midi2_opts_block_make,
- .drop_item = f_midi2_opts_block_drop,
- };
- static const struct config_item_type f_midi2_ep_type = {
- .ct_item_ops = &f_midi2_ep_item_ops,
- .ct_group_ops = &f_midi2_ep_group_ops,
- .ct_attrs = f_midi2_ep_attrs,
- .ct_owner = THIS_MODULE,
- };
- /* create a f_midi2_ep_opts instance */
- static int f_midi2_ep_opts_create(struct f_midi2_opts *opts,
- unsigned int index,
- struct f_midi2_ep_opts **ep_p)
- {
- struct f_midi2_ep_opts *ep_opts;
- ep_opts = kzalloc(sizeof(*ep_opts), GFP_KERNEL);
- if (!ep_opts)
- return -ENOMEM;
- ep_opts->opts = opts;
- ep_opts->index = index;
- /* set up the default values */
- ep_opts->info.protocol = 2;
- ep_opts->info.protocol_caps = 3;
- opts->eps[index] = ep_opts;
- *ep_p = ep_opts;
- return 0;
- }
- /* make_group callback for an EP */
- static struct config_group *
- f_midi2_opts_ep_make(struct config_group *group, const char *name)
- {
- struct f_midi2_opts *opts;
- struct f_midi2_ep_opts *ep_opts;
- unsigned int index;
- int ret;
- if (strncmp(name, "ep.", 3))
- return ERR_PTR(-EINVAL);
- ret = kstrtouint(name + 3, 10, &index);
- if (ret)
- return ERR_PTR(ret);
- opts = to_f_midi2_opts(&group->cg_item);
- if (index >= MAX_UMP_EPS)
- return ERR_PTR(-EINVAL);
- if (opts->eps[index])
- return ERR_PTR(-EBUSY);
- ret = f_midi2_ep_opts_create(opts, index, &ep_opts);
- if (ret)
- return ERR_PTR(ret);
- config_group_init_type_name(&ep_opts->group, name, &f_midi2_ep_type);
- return &ep_opts->group;
- }
- /* drop_item callback for an EP */
- static void
- f_midi2_opts_ep_drop(struct config_group *group, struct config_item *item)
- {
- struct f_midi2_ep_opts *ep_opts = to_f_midi2_ep_opts(item);
- mutex_lock(&ep_opts->opts->lock);
- ep_opts->opts->eps[ep_opts->index] = NULL;
- mutex_unlock(&ep_opts->opts->lock);
- config_item_put(item);
- }
- /*
- * Definitions for card config
- */
- /* define a bool option for card */
- #define F_MIDI2_BOOL_OPT(name) \
- static ssize_t f_midi2_opts_##name##_show(struct config_item *item, \
- char *page) \
- { \
- struct f_midi2_opts *opts = to_f_midi2_opts(item); \
- return f_midi2_opts_uint_show(opts, opts->info.name, \
- "%u\n", page); \
- } \
- \
- static ssize_t f_midi2_opts_##name##_store(struct config_item *item, \
- const char *page, size_t len) \
- { \
- struct f_midi2_opts *opts = to_f_midi2_opts(item); \
- return f_midi2_opts_bool_store(opts, &opts->info.name, \
- page, len); \
- } \
- \
- CONFIGFS_ATTR(f_midi2_opts_, name)
- F_MIDI2_BOOL_OPT(process_ump);
- F_MIDI2_BOOL_OPT(static_block);
- static ssize_t f_midi2_opts_iface_name_show(struct config_item *item,
- char *page)
- {
- struct f_midi2_opts *opts = to_f_midi2_opts(item);
- return f_midi2_opts_str_show(opts, opts->info.iface_name, page);
- }
- static ssize_t f_midi2_opts_iface_name_store(struct config_item *item,
- const char *page, size_t len)
- {
- struct f_midi2_opts *opts = to_f_midi2_opts(item);
- return f_midi2_opts_str_store(opts, &opts->info.iface_name, 128,
- page, len);
- }
- CONFIGFS_ATTR(f_midi2_opts_, iface_name);
- static struct configfs_attribute *f_midi2_attrs[] = {
- &f_midi2_opts_attr_process_ump,
- &f_midi2_opts_attr_static_block,
- &f_midi2_opts_attr_iface_name,
- NULL
- };
- static void f_midi2_opts_release(struct config_item *item)
- {
- struct f_midi2_opts *opts = to_f_midi2_opts(item);
- usb_put_function_instance(&opts->func_inst);
- }
- static struct configfs_item_operations f_midi2_item_ops = {
- .release = f_midi2_opts_release,
- };
- static struct configfs_group_operations f_midi2_group_ops = {
- .make_group = f_midi2_opts_ep_make,
- .drop_item = f_midi2_opts_ep_drop,
- };
- static const struct config_item_type f_midi2_func_type = {
- .ct_item_ops = &f_midi2_item_ops,
- .ct_group_ops = &f_midi2_group_ops,
- .ct_attrs = f_midi2_attrs,
- .ct_owner = THIS_MODULE,
- };
- static void f_midi2_free_inst(struct usb_function_instance *f)
- {
- struct f_midi2_opts *opts;
- opts = container_of(f, struct f_midi2_opts, func_inst);
- kfree(opts->info.iface_name);
- kfree(opts);
- }
- /* gadget alloc_inst */
- static struct usb_function_instance *f_midi2_alloc_inst(void)
- {
- struct f_midi2_opts *opts;
- struct f_midi2_ep_opts *ep_opts;
- struct f_midi2_block_opts *block_opts;
- int ret;
- opts = kzalloc(sizeof(*opts), GFP_KERNEL);
- if (!opts)
- return ERR_PTR(-ENOMEM);
- mutex_init(&opts->lock);
- opts->func_inst.free_func_inst = f_midi2_free_inst;
- opts->info.process_ump = true;
- opts->info.static_block = true;
- opts->info.num_reqs = 32;
- opts->info.req_buf_size = 512;
- /* create the default ep */
- ret = f_midi2_ep_opts_create(opts, 0, &ep_opts);
- if (ret) {
- kfree(opts);
- return ERR_PTR(ret);
- }
- /* create the default block */
- ret = f_midi2_block_opts_create(ep_opts, 0, &block_opts);
- if (ret) {
- kfree(ep_opts);
- kfree(opts);
- return ERR_PTR(ret);
- }
- /* set up the default MIDI1 (that is mandatory) */
- block_opts->info.midi1_num_groups = 1;
- config_group_init_type_name(&opts->func_inst.group, "",
- &f_midi2_func_type);
- config_group_init_type_name(&ep_opts->group, "ep.0",
- &f_midi2_ep_type);
- configfs_add_default_group(&ep_opts->group, &opts->func_inst.group);
- config_group_init_type_name(&block_opts->group, "block.0",
- &f_midi2_block_type);
- configfs_add_default_group(&block_opts->group, &ep_opts->group);
- return &opts->func_inst;
- }
- static void do_f_midi2_free(struct f_midi2 *midi2, struct f_midi2_opts *opts)
- {
- mutex_lock(&opts->lock);
- --opts->refcnt;
- mutex_unlock(&opts->lock);
- kfree(midi2->string_defs);
- kfree(midi2);
- }
- static void f_midi2_free(struct usb_function *f)
- {
- do_f_midi2_free(func_to_midi2(f),
- container_of(f->fi, struct f_midi2_opts, func_inst));
- }
- /* verify the parameters set up via configfs;
- * return the number of EPs or a negative error
- */
- static int verify_parameters(struct f_midi2_opts *opts)
- {
- int i, j, num_eps, num_blks;
- struct f_midi2_ep_info *ep;
- struct f_midi2_block_info *bp;
- for (num_eps = 0; num_eps < MAX_UMP_EPS && opts->eps[num_eps];
- num_eps++)
- ;
- if (!num_eps) {
- pr_err("f_midi2: No EP is defined\n");
- return -EINVAL;
- }
- num_blks = 0;
- for (i = 0; i < num_eps; i++) {
- ep = &opts->eps[i]->info;
- if (!(ep->protocol_caps & ep->protocol)) {
- pr_err("f_midi2: Invalid protocol 0x%x (caps 0x%x) for EP %d\n",
- ep->protocol, ep->protocol_caps, i);
- return -EINVAL;
- }
- for (j = 0; j < SNDRV_UMP_MAX_BLOCKS && opts->eps[i]->blks[j];
- j++, num_blks++) {
- bp = &opts->eps[i]->blks[j]->info;
- if (bp->first_group + bp->num_groups > SNDRV_UMP_MAX_GROUPS) {
- pr_err("f_midi2: Invalid group definitions for block %d:%d\n",
- i, j);
- return -EINVAL;
- }
- if (bp->midi1_num_groups) {
- if (bp->midi1_first_group < bp->first_group ||
- bp->midi1_first_group + bp->midi1_num_groups >
- bp->first_group + bp->num_groups) {
- pr_err("f_midi2: Invalid MIDI1 group definitions for block %d:%d\n",
- i, j);
- return -EINVAL;
- }
- }
- }
- }
- if (!num_blks) {
- pr_err("f_midi2: No block is defined\n");
- return -EINVAL;
- }
- return num_eps;
- }
- /* fill mapping between MIDI 1.0 cable and UMP EP/group */
- static void fill_midi1_cable_mapping(struct f_midi2 *midi2,
- struct f_midi2_ep *ep,
- int blk)
- {
- const struct f_midi2_block_info *binfo = &ep->blks[blk].info;
- struct midi1_cable_mapping *map;
- int i, group;
- if (!binfo->midi1_num_groups)
- return;
- if (binfo->direction != SNDRV_UMP_DIR_OUTPUT) {
- group = binfo->midi1_first_group;
- map = midi2->in_cable_mapping + midi2->num_midi1_in;
- for (i = 0; i < binfo->midi1_num_groups; i++, group++, map++) {
- if (midi2->num_midi1_in >= MAX_CABLES)
- break;
- map->ep = ep;
- map->block = blk;
- map->group = group;
- midi2->num_midi1_in++;
- /* store 1-based cable number */
- ep->in_group_to_cable[group] = midi2->num_midi1_in;
- }
- }
- if (binfo->direction != SNDRV_UMP_DIR_INPUT) {
- group = binfo->midi1_first_group;
- map = midi2->out_cable_mapping + midi2->num_midi1_out;
- for (i = 0; i < binfo->midi1_num_groups; i++, group++, map++) {
- if (midi2->num_midi1_out >= MAX_CABLES)
- break;
- map->ep = ep;
- map->block = blk;
- map->group = group;
- midi2->num_midi1_out++;
- }
- }
- }
- /* gadget alloc callback */
- static struct usb_function *f_midi2_alloc(struct usb_function_instance *fi)
- {
- struct f_midi2 *midi2;
- struct f_midi2_opts *opts;
- struct f_midi2_ep *ep;
- struct f_midi2_block *bp;
- int i, num_eps, blk;
- midi2 = kzalloc(sizeof(*midi2), GFP_KERNEL);
- if (!midi2)
- return ERR_PTR(-ENOMEM);
- opts = container_of(fi, struct f_midi2_opts, func_inst);
- mutex_lock(&opts->lock);
- num_eps = verify_parameters(opts);
- if (num_eps < 0) {
- mutex_unlock(&opts->lock);
- kfree(midi2);
- return ERR_PTR(num_eps);
- }
- ++opts->refcnt;
- mutex_unlock(&opts->lock);
- spin_lock_init(&midi2->queue_lock);
- midi2->func.name = "midi2_func";
- midi2->func.bind = f_midi2_bind;
- midi2->func.unbind = f_midi2_unbind;
- midi2->func.get_alt = f_midi2_get_alt;
- midi2->func.set_alt = f_midi2_set_alt;
- midi2->func.setup = f_midi2_setup;
- midi2->func.disable = f_midi2_disable;
- midi2->func.free_func = f_midi2_free;
- midi2->info = opts->info;
- midi2->num_eps = num_eps;
- for (i = 0; i < num_eps; i++) {
- ep = &midi2->midi2_eps[i];
- ep->info = opts->eps[i]->info;
- ep->card = midi2;
- for (blk = 0; blk < SNDRV_UMP_MAX_BLOCKS &&
- opts->eps[i]->blks[blk]; blk++) {
- bp = &ep->blks[blk];
- ep->num_blks++;
- bp->info = opts->eps[i]->blks[blk]->info;
- bp->gtb_id = ++midi2->total_blocks;
- }
- }
- midi2->string_defs = kcalloc(midi2->total_blocks + 1,
- sizeof(*midi2->string_defs), GFP_KERNEL);
- if (!midi2->string_defs) {
- do_f_midi2_free(midi2, opts);
- return ERR_PTR(-ENOMEM);
- }
- if (opts->info.iface_name && *opts->info.iface_name)
- midi2->string_defs[STR_IFACE].s = opts->info.iface_name;
- else
- midi2->string_defs[STR_IFACE].s = ump_ep_name(&midi2->midi2_eps[0]);
- for (i = 0; i < midi2->num_eps; i++) {
- ep = &midi2->midi2_eps[i];
- for (blk = 0; blk < ep->num_blks; blk++) {
- bp = &ep->blks[blk];
- midi2->string_defs[gtb_to_str_id(bp->gtb_id)].s =
- ump_fb_name(&bp->info);
- fill_midi1_cable_mapping(midi2, ep, blk);
- }
- }
- if (!midi2->num_midi1_in && !midi2->num_midi1_out) {
- pr_err("f_midi2: MIDI1 definition is missing\n");
- do_f_midi2_free(midi2, opts);
- return ERR_PTR(-EINVAL);
- }
- return &midi2->func;
- }
- DECLARE_USB_FUNCTION_INIT(midi2, f_midi2_alloc_inst, f_midi2_alloc);
- MODULE_DESCRIPTION("USB MIDI 2.0 class function driver");
- MODULE_LICENSE("GPL");
|