/* get an empty request for the given EP */ staticstruct usb_request *get_empty_request(struct f_midi2_usb_ep *usb_ep)
{ struct usb_request *req = NULL; unsignedlong 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 */ staticvoid put_empty_request(struct usb_request *req)
{ struct f_midi2_req_ctx *ctx = req->context; unsignedlong flags;
/* queue a request to UMP EP; request is either queued or freed after this */ staticint queue_request_ep_raw(struct usb_request *req)
{ struct f_midi2_req_ctx *ctx = req->context; int err;
#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 */
/* Process a UMP Stream message */ staticvoid process_ump_stream_msg(struct f_midi2_ep *ep, const u32 *data)
{ struct f_midi2 *midi2 = ep->card; unsignedint 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);
}
} elseif (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 */ staticvoid process_ump(struct f_midi2_ep *ep, conststruct 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);
}
}
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);
/* process all pending MIDI bytes in the internal buffer; * returns true if the request gets empty * returns false if all have been processed
*/ staticbool process_midi1_pending_buf(struct f_midi2 *midi2, struct usb_request **req_p)
{ unsignedint cable, c;
/* try to process data given from the associated UMP stream */ staticvoid 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 */ unsignedchar outbuf[12]; unsignedchar 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 */ staticvoid 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;
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;
}
}
/* Queue requests for EP-out at start */ staticvoid 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 */ staticvoid 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 */ staticint 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 */ staticint f_midi2_set_alt(struct usb_function *fn, unsignedint intf, unsignedint alt)
{ struct f_midi2 *midi2 = func_to_midi2(fn); struct f_midi2_ep *ep; int i, op_mode, err;
/* convert UMP direction to USB MIDI 2.0 direction */ staticunsignedint ump_to_usb_dir(unsignedint 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) */ staticvoid 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;
/* * ALSA UMP ops: most of them are NOPs, only trigger for write is needed
*/ staticint f_midi2_ump_open(struct snd_ump_endpoint *ump, int dir)
{ return 0;
}
staticvoid f_midi2_ump_close(struct snd_ump_endpoint *ump, int dir)
{
}
staticvoid 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;
}
}
}
staticvoid f_midi2_ump_drain(struct snd_ump_endpoint *ump, int dir)
{
}
/* use a reverse direction for the gadget host */ staticint 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;
}
staticint 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;
}
staticint append_midi1_in_jack(struct f_midi2 *midi2, struct f_midi2_usb_config *config, struct midi1_cable_mapping *map, unsignedint 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;
staticint 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;
/* 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);
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);
}
/* create a f_midi2_block_opts instance for the given block number */ staticint f_midi2_block_opts_create(struct f_midi2_ep_opts *ep_opts, unsignedint 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;
/* make_group callback for a block */ staticstruct config_group *
f_midi2_opts_block_make(struct config_group *group, constchar *name)
{ struct f_midi2_ep_opts *ep_opts; struct f_midi2_block_opts *block_opts; unsignedint 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);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.