📄 seq_clientmgr.c
字号:
! test_bit(event->type, dest->event_filter)) goto __not_avail; if (filter && !(dest->filter & filter)) goto __not_avail; return dest; /* ok - accessible */__not_avail: snd_seq_client_unlock(dest); return NULL;}/* * Return the error event. * * If the receiver client is a user client, the original event is * encapsulated in SNDRV_SEQ_EVENT_BOUNCE as variable length event. If * the original event is also variable length, the external data is * copied after the event record. * If the receiver client is a kernel client, the original event is * quoted in SNDRV_SEQ_EVENT_KERNEL_ERROR, since this requires no extra * kmalloc. */static int bounce_error_event(struct snd_seq_client *client, struct snd_seq_event *event, int err, int atomic, int hop){ struct snd_seq_event bounce_ev; int result; if (client == NULL || ! (client->filter & SNDRV_SEQ_FILTER_BOUNCE) || ! client->accept_input) return 0; /* ignored */ /* set up quoted error */ memset(&bounce_ev, 0, sizeof(bounce_ev)); bounce_ev.type = SNDRV_SEQ_EVENT_KERNEL_ERROR; bounce_ev.flags = SNDRV_SEQ_EVENT_LENGTH_FIXED; bounce_ev.queue = SNDRV_SEQ_QUEUE_DIRECT; bounce_ev.source.client = SNDRV_SEQ_CLIENT_SYSTEM; bounce_ev.source.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE; bounce_ev.dest.client = client->number; bounce_ev.dest.port = event->source.port; bounce_ev.data.quote.origin = event->dest; bounce_ev.data.quote.event = event; bounce_ev.data.quote.value = -err; /* use positive value */ result = snd_seq_deliver_single_event(NULL, &bounce_ev, 0, atomic, hop + 1); if (result < 0) { client->event_lost++; return result; } return result;}/* * rewrite the time-stamp of the event record with the curren time * of the given queue. * return non-zero if updated. */static int update_timestamp_of_queue(struct snd_seq_event *event, int queue, int real_time){ struct snd_seq_queue *q; q = queueptr(queue); if (! q) return 0; event->queue = queue; event->flags &= ~SNDRV_SEQ_TIME_STAMP_MASK; if (real_time) { event->time.time = snd_seq_timer_get_cur_time(q->timer); event->flags |= SNDRV_SEQ_TIME_STAMP_REAL; } else { event->time.tick = snd_seq_timer_get_cur_tick(q->timer); event->flags |= SNDRV_SEQ_TIME_STAMP_TICK; } queuefree(q); return 1;}/* * deliver an event to the specified destination. * if filter is non-zero, client filter bitmap is tested. * * RETURN VALUE: 0 : if succeeded * <0 : error */static int snd_seq_deliver_single_event(struct snd_seq_client *client, struct snd_seq_event *event, int filter, int atomic, int hop){ struct snd_seq_client *dest = NULL; struct snd_seq_client_port *dest_port = NULL; int result = -ENOENT; int direct; direct = snd_seq_ev_is_direct(event); dest = get_event_dest_client(event, filter); if (dest == NULL) goto __skip; dest_port = snd_seq_port_use_ptr(dest, event->dest.port); if (dest_port == NULL) goto __skip; /* check permission */ if (! check_port_perm(dest_port, SNDRV_SEQ_PORT_CAP_WRITE)) { result = -EPERM; goto __skip; } if (dest_port->timestamping) update_timestamp_of_queue(event, dest_port->time_queue, dest_port->time_real); switch (dest->type) { case USER_CLIENT: if (dest->data.user.fifo) result = snd_seq_fifo_event_in(dest->data.user.fifo, event); break; case KERNEL_CLIENT: if (dest_port->event_input == NULL) break; result = dest_port->event_input(event, direct, dest_port->private_data, atomic, hop); break; default: break; } __skip: if (dest_port) snd_seq_port_unlock(dest_port); if (dest) snd_seq_client_unlock(dest); if (result < 0 && !direct) { result = bounce_error_event(client, event, result, atomic, hop); } return result;}/* * send the event to all subscribers: */static int deliver_to_subscribers(struct snd_seq_client *client, struct snd_seq_event *event, int atomic, int hop){ struct snd_seq_subscribers *subs; int err = 0, num_ev = 0; struct snd_seq_event event_saved; struct snd_seq_client_port *src_port; struct list_head *p; struct snd_seq_port_subs_info *grp; src_port = snd_seq_port_use_ptr(client, event->source.port); if (src_port == NULL) return -EINVAL; /* invalid source port */ /* save original event record */ event_saved = *event; grp = &src_port->c_src; /* lock list */ if (atomic) read_lock(&grp->list_lock); else down_read(&grp->list_mutex); list_for_each(p, &grp->list_head) { subs = list_entry(p, struct snd_seq_subscribers, src_list); event->dest = subs->info.dest; if (subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP) /* convert time according to flag with subscription */ update_timestamp_of_queue(event, subs->info.queue, subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIME_REAL); err = snd_seq_deliver_single_event(client, event, 0, atomic, hop); if (err < 0) break; num_ev++; /* restore original event record */ *event = event_saved; } if (atomic) read_unlock(&grp->list_lock); else up_read(&grp->list_mutex); *event = event_saved; /* restore */ snd_seq_port_unlock(src_port); return (err < 0) ? err : num_ev;}#ifdef SUPPORT_BROADCAST /* * broadcast to all ports: */static int port_broadcast_event(struct snd_seq_client *client, struct snd_seq_event *event, int atomic, int hop){ int num_ev = 0, err = 0; struct snd_seq_client *dest_client; struct list_head *p; dest_client = get_event_dest_client(event, SNDRV_SEQ_FILTER_BROADCAST); if (dest_client == NULL) return 0; /* no matching destination */ read_lock(&dest_client->ports_lock); list_for_each(p, &dest_client->ports_list_head) { struct snd_seq_client_port *port = list_entry(p, struct snd_seq_client_port, list); event->dest.port = port->addr.port; /* pass NULL as source client to avoid error bounce */ err = snd_seq_deliver_single_event(NULL, event, SNDRV_SEQ_FILTER_BROADCAST, atomic, hop); if (err < 0) break; num_ev++; } read_unlock(&dest_client->ports_lock); snd_seq_client_unlock(dest_client); event->dest.port = SNDRV_SEQ_ADDRESS_BROADCAST; /* restore */ return (err < 0) ? err : num_ev;}/* * send the event to all clients: * if destination port is also ADDRESS_BROADCAST, deliver to all ports. */static int broadcast_event(struct snd_seq_client *client, struct snd_seq_event *event, int atomic, int hop){ int err = 0, num_ev = 0; int dest; struct snd_seq_addr addr; addr = event->dest; /* save */ for (dest = 0; dest < SNDRV_SEQ_MAX_CLIENTS; dest++) { /* don't send to itself */ if (dest == client->number) continue; event->dest.client = dest; event->dest.port = addr.port; if (addr.port == SNDRV_SEQ_ADDRESS_BROADCAST) err = port_broadcast_event(client, event, atomic, hop); else /* pass NULL as source client to avoid error bounce */ err = snd_seq_deliver_single_event(NULL, event, SNDRV_SEQ_FILTER_BROADCAST, atomic, hop); if (err < 0) break; num_ev += err; } event->dest = addr; /* restore */ return (err < 0) ? err : num_ev;}/* multicast - not supported yet */static int multicast_event(struct snd_seq_client *client, struct snd_seq_event *event, int atomic, int hop){ snd_printd("seq: multicast not supported yet.\n"); return 0; /* ignored */}#endif /* SUPPORT_BROADCAST *//* deliver an event to the destination port(s). * if the event is to subscribers or broadcast, the event is dispatched * to multiple targets. * * RETURN VALUE: n > 0 : the number of delivered events. * n == 0 : the event was not passed to any client. * n < 0 : error - event was not processed. */static int snd_seq_deliver_event(struct snd_seq_client *client, struct snd_seq_event *event, int atomic, int hop){ int result; hop++; if (hop >= SNDRV_SEQ_MAX_HOPS) { snd_printd("too long delivery path (%d:%d->%d:%d)\n", event->source.client, event->source.port, event->dest.client, event->dest.port); return -EMLINK; } if (event->queue == SNDRV_SEQ_ADDRESS_SUBSCRIBERS || event->dest.client == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) result = deliver_to_subscribers(client, event, atomic, hop);#ifdef SUPPORT_BROADCAST else if (event->queue == SNDRV_SEQ_ADDRESS_BROADCAST || event->dest.client == SNDRV_SEQ_ADDRESS_BROADCAST) result = broadcast_event(client, event, atomic, hop); else if (event->dest.client >= SNDRV_SEQ_MAX_CLIENTS) result = multicast_event(client, event, atomic, hop); else if (event->dest.port == SNDRV_SEQ_ADDRESS_BROADCAST) result = port_broadcast_event(client, event, atomic, hop);#endif else result = snd_seq_deliver_single_event(client, event, 0, atomic, hop); return result;}/* * dispatch an event cell: * This function is called only from queue check routines in timer * interrupts or after enqueued. * The event cell shall be released or re-queued in this function. * * RETURN VALUE: n > 0 : the number of delivered events. * n == 0 : the event was not passed to any client. * n < 0 : error - event was not processed. */int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop){ struct snd_seq_client *client; int result; snd_assert(cell != NULL, return -EINVAL); client = snd_seq_client_use_ptr(cell->event.source.client); if (client == NULL) { snd_seq_cell_free(cell); /* release this cell */ return -EINVAL; } if (cell->event.type == SNDRV_SEQ_EVENT_NOTE) { /* NOTE event: * the event cell is re-used as a NOTE-OFF event and * enqueued again. */ struct snd_seq_event tmpev, *ev; /* reserve this event to enqueue note-off later */ tmpev = cell->event; tmpev.type = SNDRV_SEQ_EVENT_NOTEON; result = snd_seq_deliver_event(client, &tmpev, atomic, hop); /* * This was originally a note event. We now re-use the * cell for the note-off event. */ ev = &cell->event; ev->type = SNDRV_SEQ_EVENT_NOTEOFF; ev->flags |= SNDRV_SEQ_PRIORITY_HIGH; /* add the duration time */ switch (ev->flags & SNDRV_SEQ_TIME_STAMP_MASK) { case SNDRV_SEQ_TIME_STAMP_TICK: ev->time.tick += ev->data.note.duration; break; case SNDRV_SEQ_TIME_STAMP_REAL: /* unit for duration is ms */ ev->time.time.tv_nsec += 1000000 * (ev->data.note.duration % 1000); ev->time.time.tv_sec += ev->data.note.duration / 1000 + ev->time.time.tv_nsec / 1000000000; ev->time.time.tv_nsec %= 1000000000; break; } ev->data.note.velocity = ev->data.note.off_velocity; /* Now queue this cell as the note off event */ if (snd_seq_enqueue_event(cell, atomic, hop) < 0) snd_seq_cell_free(cell); /* release this cell */ } else { /* Normal events: * event cell is freed after processing the event */ result = snd_seq_deliver_event(client, &cell->event, atomic, hop); snd_seq_cell_free(cell); } snd_seq_client_unlock(client); return result;}/* Allocate a cell from client pool and enqueue it to queue: * if pool is empty and blocking is TRUE, sleep until a new cell is * available. */static int snd_seq_client_enqueue_event(struct snd_seq_client *client, struct snd_seq_event *event, struct file *file, int blocking, int atomic, int hop){ struct snd_seq_event_cell *cell; int err; /* special queue values - force direct passing */ if (event->queue == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) { event->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; event->queue = SNDRV_SEQ_QUEUE_DIRECT; } else#ifdef SUPPORT_BROADCAST if (event->queue == SNDRV_SEQ_ADDRESS_BROADCAST) { event->dest.client = SNDRV_SEQ_ADDRESS_BROADCAST; event->queue = SNDRV_SEQ_QUEUE_DIRECT; }#endif if (event->dest.client == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) { /* check presence of source port */ struct snd_seq_client_port *src_port = snd_seq_port_use_ptr(client, event->source.port); if (src_port == NULL) return -EINVAL; snd_seq_port_unlock(src_port); } /* direct event processing without enqueued */ if (snd_seq_ev_is_direct(event)) { if (event->type == SNDRV_SEQ_EVENT_NOTE) return -EINVAL; /* this event must be enqueued! */ return snd_seq_deliver_event(client, event, atomic, hop); } /* Not direct, normal queuing */ if (snd_seq_queue_is_used(event->queue, client->number) <= 0) return -EINVAL; /* invalid queue */ if (! snd_seq_write_pool_allocated(client)) return -ENXIO; /* queue is not allocated */ /* allocate an event cell */ err = snd_seq_event_dup(client->pool, event, &cell, !blocking || atomic, file); if (err < 0) return err; /* we got a cell. enqueue it. */ if ((err = snd_seq_enqueue_event(cell, atomic, hop)) < 0) { snd_seq_cell_free(cell); return err; } return 0;}/* * check validity of event type and data length. * return non-zero if invalid. */static int check_event_type_and_length(struct snd_seq_event *ev){ switch (snd_seq_ev_length_type(ev)) { case SNDRV_SEQ_EVENT_LENGTH_FIXED: if (snd_seq_ev_is_variable_type(ev)) return -EINVAL; break; case SNDRV_SEQ_EVENT_LENGTH_VARIABLE: if (! snd_seq_ev_is_variable_type(ev) || (ev->data.ext.len & ~SNDRV_SEQ_EXT_MASK) >= SNDRV_SEQ_MAX_EVENT_LEN) return -EINVAL; break; case SNDRV_SEQ_EVENT_LENGTH_VARUSR: if (! snd_seq_ev_is_instr_type(ev) || ! snd_seq_ev_is_direct(ev)) return -EINVAL; break; } return 0;}/* handle write() *//* possible error values: * -ENXIO invalid client or file open mode * -ENOMEM malloc failed * -EFAULT seg. fault during copy from user space * -EINVAL invalid event * -EAGAIN no space in output pool * -EINTR interrupts while sleep * -EMLINK too many hops * others depends on return value from driver callback */static ssize_t snd_seq_write(struct file *file, const char __user *buf, size_t count, loff_t *offset){ struct snd_seq_client *client = file->private_data; int written = 0, len; int err = -EINVAL; struct snd_seq_event event; if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT)) return -ENXIO;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -