📄 seq_ports.c
字号:
/* set port name */ if (info->name[0]) strlcpy(port->name, info->name, sizeof(port->name)); /* set capabilities */ port->capability = info->capability; /* get port type */ port->type = info->type; /* information about supported channels/voices */ port->midi_channels = info->midi_channels; port->midi_voices = info->midi_voices; port->synth_voices = info->synth_voices; /* timestamping */ port->timestamping = (info->flags & SNDRV_SEQ_PORT_FLG_TIMESTAMP) ? 1 : 0; port->time_real = (info->flags & SNDRV_SEQ_PORT_FLG_TIME_REAL) ? 1 : 0; port->time_queue = info->time_queue; return 0;}/* get port info fields */int snd_seq_get_port_info(client_port_t * port, snd_seq_port_info_t * info){ snd_assert(port && info, return -EINVAL); /* get port name */ strlcpy(info->name, port->name, sizeof(info->name)); /* get capabilities */ info->capability = port->capability; /* get port type */ info->type = port->type; /* information about supported channels/voices */ info->midi_channels = port->midi_channels; info->midi_voices = port->midi_voices; info->synth_voices = port->synth_voices; /* get subscriber counts */ info->read_use = port->c_src.count; info->write_use = port->c_dest.count; /* timestamping */ info->flags = 0; if (port->timestamping) { info->flags |= SNDRV_SEQ_PORT_FLG_TIMESTAMP; if (port->time_real) info->flags |= SNDRV_SEQ_PORT_FLG_TIME_REAL; info->time_queue = port->time_queue; } return 0;}/* * call callback functions (if any): * the callbacks are invoked only when the first (for connection) or * the last subscription (for disconnection) is done. Second or later * subscription results in increment of counter, but no callback is * invoked. * This feature is useful if these callbacks are associated with * initialization or termination of devices (see seq_midi.c). * * If callback_all option is set, the callback function is invoked * at each connnection/disconnection. */static int subscribe_port(client_t *client, client_port_t *port, port_subs_info_t *grp, snd_seq_port_subscribe_t *info, int send_ack){ int err = 0; if (!try_module_get(port->owner)) return -EFAULT; grp->count++; if (grp->open && (port->callback_all || grp->count == 1)) { err = grp->open(port->private_data, info); if (err < 0) { module_put(port->owner); grp->count--; } } if (err >= 0 && send_ack && client->type == USER_CLIENT) snd_seq_client_notify_subscription(port->addr.client, port->addr.port, info, SNDRV_SEQ_EVENT_PORT_SUBSCRIBED); return err;}static int unsubscribe_port(client_t *client, client_port_t *port, port_subs_info_t *grp, snd_seq_port_subscribe_t *info, int send_ack){ int err = 0; if (! grp->count) return -EINVAL; grp->count--; if (grp->close && (port->callback_all || grp->count == 0)) err = grp->close(port->private_data, info); if (send_ack && client->type == USER_CLIENT) snd_seq_client_notify_subscription(port->addr.client, port->addr.port, info, SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED); module_put(port->owner); return err;}/* check if both addresses are identical */static inline int addr_match(snd_seq_addr_t *r, snd_seq_addr_t *s){ return (r->client == s->client) && (r->port == s->port);}/* check the two subscribe info match *//* if flags is zero, checks only sender and destination addresses */static int match_subs_info(snd_seq_port_subscribe_t *r, snd_seq_port_subscribe_t *s){ if (addr_match(&r->sender, &s->sender) && addr_match(&r->dest, &s->dest)) { if (r->flags && r->flags == s->flags) return r->queue == s->queue; else if (! r->flags) return 1; } return 0;}/* connect two ports */int snd_seq_port_connect(client_t *connector, client_t *src_client, client_port_t *src_port, client_t *dest_client, client_port_t *dest_port, snd_seq_port_subscribe_t *info){ port_subs_info_t *src = &src_port->c_src; port_subs_info_t *dest = &dest_port->c_dest; subscribers_t *subs; struct list_head *p; int err, src_called = 0; unsigned long flags; int exclusive; subs = kzalloc(sizeof(*subs), GFP_KERNEL); if (! subs) return -ENOMEM; subs->info = *info; atomic_set(&subs->ref_count, 2); down_write(&src->list_mutex); down_write(&dest->list_mutex); exclusive = info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE ? 1 : 0; err = -EBUSY; if (exclusive) { if (! list_empty(&src->list_head) || ! list_empty(&dest->list_head)) goto __error; } else { if (src->exclusive || dest->exclusive) goto __error; /* check whether already exists */ list_for_each(p, &src->list_head) { subscribers_t *s = list_entry(p, subscribers_t, src_list); if (match_subs_info(info, &s->info)) goto __error; } list_for_each(p, &dest->list_head) { subscribers_t *s = list_entry(p, subscribers_t, dest_list); if (match_subs_info(info, &s->info)) goto __error; } } if ((err = subscribe_port(src_client, src_port, src, info, connector->number != src_client->number)) < 0) goto __error; src_called = 1; if ((err = subscribe_port(dest_client, dest_port, dest, info, connector->number != dest_client->number)) < 0) goto __error; /* add to list */ write_lock_irqsave(&src->list_lock, flags); // write_lock(&dest->list_lock); // no other lock yet list_add_tail(&subs->src_list, &src->list_head); list_add_tail(&subs->dest_list, &dest->list_head); // write_unlock(&dest->list_lock); // no other lock yet write_unlock_irqrestore(&src->list_lock, flags); src->exclusive = dest->exclusive = exclusive; up_write(&dest->list_mutex); up_write(&src->list_mutex); return 0; __error: if (src_called) unsubscribe_port(src_client, src_port, src, info, connector->number != src_client->number); kfree(subs); up_write(&dest->list_mutex); up_write(&src->list_mutex); return err;}/* remove the connection */int snd_seq_port_disconnect(client_t *connector, client_t *src_client, client_port_t *src_port, client_t *dest_client, client_port_t *dest_port, snd_seq_port_subscribe_t *info){ port_subs_info_t *src = &src_port->c_src; port_subs_info_t *dest = &dest_port->c_dest; subscribers_t *subs; struct list_head *p; int err = -ENOENT; unsigned long flags; down_write(&src->list_mutex); down_write(&dest->list_mutex); /* look for the connection */ list_for_each(p, &src->list_head) { subs = list_entry(p, subscribers_t, src_list); if (match_subs_info(info, &subs->info)) { write_lock_irqsave(&src->list_lock, flags); // write_lock(&dest->list_lock); // no lock yet list_del(&subs->src_list); list_del(&subs->dest_list); // write_unlock(&dest->list_lock); write_unlock_irqrestore(&src->list_lock, flags); src->exclusive = dest->exclusive = 0; unsubscribe_port(src_client, src_port, src, info, connector->number != src_client->number); unsubscribe_port(dest_client, dest_port, dest, info, connector->number != dest_client->number); kfree(subs); err = 0; break; } } up_write(&dest->list_mutex); up_write(&src->list_mutex); return err;}/* get matched subscriber */subscribers_t *snd_seq_port_get_subscription(port_subs_info_t *src_grp, snd_seq_addr_t *dest_addr){ struct list_head *p; subscribers_t *s, *found = NULL; down_read(&src_grp->list_mutex); list_for_each(p, &src_grp->list_head) { s = list_entry(p, subscribers_t, src_list); if (addr_match(dest_addr, &s->info.dest)) { found = s; break; } } up_read(&src_grp->list_mutex); return found;}/* * Attach a device driver that wants to receive events from the * sequencer. Returns the new port number on success. * A driver that wants to receive the events converted to midi, will * use snd_seq_midisynth_register_port(). *//* exported */int snd_seq_event_port_attach(int client, snd_seq_port_callback_t *pcbp, int cap, int type, int midi_channels, int midi_voices, char *portname){ snd_seq_port_info_t portinfo; int ret; /* Set up the port */ memset(&portinfo, 0, sizeof(portinfo)); portinfo.addr.client = client; strlcpy(portinfo.name, portname ? portname : "Unamed port", sizeof(portinfo.name)); portinfo.capability = cap; portinfo.type = type; portinfo.kernel = pcbp; portinfo.midi_channels = midi_channels; portinfo.midi_voices = midi_voices; /* Create it */ ret = snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_CREATE_PORT, &portinfo); if (ret >= 0) ret = portinfo.addr.port; return ret;}/* * Detach the driver from a port. *//* exported */int snd_seq_event_port_detach(int client, int port){ snd_seq_port_info_t portinfo; int err; memset(&portinfo, 0, sizeof(portinfo)); portinfo.addr.client = client; portinfo.addr.port = port; err = snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_DELETE_PORT, &portinfo); return err;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -