📄 seq_clientmgr.c
字号:
! 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(snd_seq_event_t *event, int queue, int real_time){ queue_t *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(client_t *client, snd_seq_event_t *event, int filter, int atomic, int hop){ client_t *dest = NULL; client_port_t *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(client_t *client, snd_seq_event_t *event, int atomic, int hop){ subscribers_t *subs; int err = 0, num_ev = 0; snd_seq_event_t event_saved; client_port_t *src_port; struct list_head *p; port_subs_info_t *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, subscribers_t, 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(client_t *client, snd_seq_event_t *event, int atomic, int hop){ int num_ev = 0, err = 0; client_t *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) { client_port_t *port = list_entry(p, client_port_t, 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(client_t *client, snd_seq_event_t *event, int atomic, int hop){ int err = 0, num_ev = 0; int dest; snd_seq_addr_t 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(client_t *client, snd_seq_event_t *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(client_t *client, snd_seq_event_t *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(snd_seq_event_cell_t *cell, int atomic, int hop){ client_t *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. */ snd_seq_event_t 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(client_t *client, snd_seq_event_t *event, struct file *file, int blocking, int atomic, int hop){ snd_seq_event_cell_t *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 */ client_port_t *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(snd_seq_event_t *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){ client_t *client = (client_t *) file->private_data; int written = 0, len; int err = -EINVAL; snd_seq_event_t event; if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT)) return -ENXIO; /* check client structures are in place */ snd_assert(client != NULL, return -ENXIO); if (!client->accept_output || client->pool == NULL) return -ENXIO; /* allocate the pool now if the pool is not allocated yet */ if (client->pool->size > 0 && !snd_seq_write_pool_allocated(client)) { if (snd_seq_pool_init(client->pool) < 0) return -ENOMEM; } /* only process whole events */ while (count >= sizeof(snd_seq_event_t)) { /* Read in the event header from the user */ len = sizeof(event); if (copy_from_user(&event, buf, len)) { err = -EFAULT; break; } event.source.client = client->number; /* fill in client number */ /* Check for extension data length */ if (check_event_type_and_length(&event)) { err = -EINVAL; break; } /* check for special events */ if (event.type == SNDRV_SEQ_EVENT_NONE) goto __skip_event; else if (snd_seq_ev_is_reserved(&event)) { err = -EINVAL; break; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -