1*0cdbd964Sratchov /* $OpenBSD: sock.c,v 1.53 2024/12/21 08:57:18 ratchov Exp $ */ 287bc9f6aSratchov /* 387bc9f6aSratchov * Copyright (c) 2008-2012 Alexandre Ratchov <alex@caoua.org> 487bc9f6aSratchov * 587bc9f6aSratchov * Permission to use, copy, modify, and distribute this software for any 687bc9f6aSratchov * purpose with or without fee is hereby granted, provided that the above 787bc9f6aSratchov * copyright notice and this permission notice appear in all copies. 887bc9f6aSratchov * 987bc9f6aSratchov * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1087bc9f6aSratchov * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1187bc9f6aSratchov * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1287bc9f6aSratchov * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1387bc9f6aSratchov * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1487bc9f6aSratchov * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1587bc9f6aSratchov * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1687bc9f6aSratchov */ 1787bc9f6aSratchov #include <sys/types.h> 1872ca3e97Sratchov #include <sys/socket.h> 1987bc9f6aSratchov #include <netinet/in.h> 2087bc9f6aSratchov #include <errno.h> 2187bc9f6aSratchov #include <poll.h> 2287bc9f6aSratchov #include <stdio.h> 2387bc9f6aSratchov #include <stdlib.h> 2487bc9f6aSratchov #include <string.h> 2587bc9f6aSratchov #include <unistd.h> 2687bc9f6aSratchov 2787bc9f6aSratchov #include "abuf.h" 2887bc9f6aSratchov #include "defs.h" 2987bc9f6aSratchov #include "dev.h" 3087bc9f6aSratchov #include "file.h" 3187bc9f6aSratchov #include "midi.h" 3287bc9f6aSratchov #include "opt.h" 3387bc9f6aSratchov #include "sock.h" 3487bc9f6aSratchov #include "utils.h" 3587bc9f6aSratchov 363e5ee6d4Sratchov #define SOCK_CTLDESC_SIZE 0x800 /* size of s->ctldesc */ 37d07fece6Sratchov 38fcda7a7eSratchov void sock_close(struct sock *); 39fcda7a7eSratchov void sock_slot_fill(void *); 40fcda7a7eSratchov void sock_slot_flush(void *); 41fcda7a7eSratchov void sock_slot_eof(void *); 426f413563Sratchov void sock_slot_onmove(void *); 434182f7f9Sratchov void sock_slot_onvol(void *); 44fcda7a7eSratchov void sock_midi_imsg(void *, unsigned char *, int); 45fcda7a7eSratchov void sock_midi_omsg(void *, unsigned char *, int); 46fcda7a7eSratchov void sock_midi_fill(void *, int); 47e575fbdeSratchov void sock_ctl_sync(void *); 48fcda7a7eSratchov struct sock *sock_new(int); 49fcda7a7eSratchov void sock_exit(void *); 50fcda7a7eSratchov int sock_fdwrite(struct sock *, void *, int); 51fcda7a7eSratchov int sock_fdread(struct sock *, void *, int); 52fcda7a7eSratchov int sock_rmsg(struct sock *); 53fcda7a7eSratchov int sock_wmsg(struct sock *); 54fcda7a7eSratchov int sock_rdata(struct sock *); 55fcda7a7eSratchov int sock_wdata(struct sock *); 56fcda7a7eSratchov int sock_setpar(struct sock *); 57fcda7a7eSratchov int sock_auth(struct sock *); 58fcda7a7eSratchov int sock_hello(struct sock *); 5987bc9f6aSratchov int sock_execmsg(struct sock *); 6087bc9f6aSratchov int sock_buildmsg(struct sock *); 61fcda7a7eSratchov int sock_read(struct sock *); 62fcda7a7eSratchov int sock_write(struct sock *); 6387bc9f6aSratchov int sock_pollfd(void *, struct pollfd *); 6487bc9f6aSratchov int sock_revents(void *, struct pollfd *); 6587bc9f6aSratchov void sock_in(void *); 6687bc9f6aSratchov void sock_out(void *); 6787bc9f6aSratchov void sock_hup(void *); 6887bc9f6aSratchov 6987bc9f6aSratchov struct fileops sock_fileops = { 7087bc9f6aSratchov "sock", 7187bc9f6aSratchov sock_pollfd, 7287bc9f6aSratchov sock_revents, 7387bc9f6aSratchov sock_in, 7487bc9f6aSratchov sock_out, 7587bc9f6aSratchov sock_hup 7687bc9f6aSratchov }; 7787bc9f6aSratchov 7887bc9f6aSratchov struct slotops sock_slotops = { 7987bc9f6aSratchov sock_slot_onmove, 8087bc9f6aSratchov sock_slot_onvol, 8187bc9f6aSratchov sock_slot_fill, 8287bc9f6aSratchov sock_slot_flush, 8387bc9f6aSratchov sock_slot_eof, 8487bc9f6aSratchov sock_exit 8587bc9f6aSratchov }; 8687bc9f6aSratchov 8787bc9f6aSratchov struct midiops sock_midiops = { 8887bc9f6aSratchov sock_midi_imsg, 8987bc9f6aSratchov sock_midi_omsg, 9087bc9f6aSratchov sock_midi_fill, 9187bc9f6aSratchov sock_exit 9287bc9f6aSratchov }; 9387bc9f6aSratchov 94d07fece6Sratchov struct ctlops sock_ctlops = { 95e575fbdeSratchov sock_exit, 96e575fbdeSratchov sock_ctl_sync 97d07fece6Sratchov }; 98d07fece6Sratchov 9987bc9f6aSratchov struct sock *sock_list = NULL; 10087bc9f6aSratchov unsigned int sock_sesrefs = 0; /* connections to the session */ 10187bc9f6aSratchov uint8_t sock_sescookie[AMSG_COOKIELEN]; /* owner of the session */ 10287bc9f6aSratchov 10336355b88Sratchov /* 10436355b88Sratchov * Old clients used to send dev number and opt name. This routine 10536355b88Sratchov * finds proper opt pointer for the given device. 10636355b88Sratchov */ 10736355b88Sratchov static struct opt * 10836355b88Sratchov legacy_opt(int devnum, char *optname) 10936355b88Sratchov { 11036355b88Sratchov struct dev *d; 11136355b88Sratchov struct opt *o; 11236355b88Sratchov 11336355b88Sratchov d = dev_bynum(devnum); 11436355b88Sratchov if (d == NULL) 11536355b88Sratchov return NULL; 11636355b88Sratchov if (strcmp(optname, "default") == 0) { 11736355b88Sratchov for (o = opt_list; o != NULL; o = o->next) { 11836355b88Sratchov if (strcmp(o->name, d->name) == 0) 11936355b88Sratchov return o; 12036355b88Sratchov } 12136355b88Sratchov return NULL; 12236355b88Sratchov } else { 12336355b88Sratchov o = opt_byname(optname); 12436355b88Sratchov return (o != NULL && o->dev == d) ? o : NULL; 12536355b88Sratchov } 12636355b88Sratchov } 12736355b88Sratchov 12836355b88Sratchov /* 12936355b88Sratchov * If control slot is associated to a particular opt, then 13036355b88Sratchov * remove the unused group part of the control name to make mixer 13136355b88Sratchov * look nicer 13236355b88Sratchov */ 13336355b88Sratchov static char * 13436355b88Sratchov ctlgroup(struct sock *f, struct ctl *c) 13536355b88Sratchov { 13636355b88Sratchov if (f->ctlslot->opt == NULL) 13736355b88Sratchov return c->group; 13836355b88Sratchov if (strcmp(c->group, f->ctlslot->opt->name) == 0) 13936355b88Sratchov return ""; 14036355b88Sratchov if (strcmp(c->group, f->ctlslot->opt->dev->name) == 0) 14136355b88Sratchov return ""; 14236355b88Sratchov return c->group; 14336355b88Sratchov } 14436355b88Sratchov 14587bc9f6aSratchov void 14687bc9f6aSratchov sock_close(struct sock *f) 14787bc9f6aSratchov { 14836355b88Sratchov struct opt *o; 14987bc9f6aSratchov struct sock **pf; 150326545d4Sratchov unsigned int tags, i; 15187bc9f6aSratchov 15287bc9f6aSratchov for (pf = &sock_list; *pf != f; pf = &(*pf)->next) { 15387bc9f6aSratchov #ifdef DEBUG 15487bc9f6aSratchov if (*pf == NULL) { 1557b639200Sratchov logx(0, "%s: not on list", __func__); 15687bc9f6aSratchov panic(); 15787bc9f6aSratchov } 15887bc9f6aSratchov #endif 15987bc9f6aSratchov } 16087bc9f6aSratchov *pf = f->next; 16187bc9f6aSratchov 16287bc9f6aSratchov #ifdef DEBUG 1637b639200Sratchov logx(3, "sock %d: closing", f->fd); 16487bc9f6aSratchov #endif 16587bc9f6aSratchov if (f->pstate > SOCK_AUTH) 16672ca3e97Sratchov sock_sesrefs -= f->sesrefs; 16787bc9f6aSratchov if (f->slot) { 16887bc9f6aSratchov slot_del(f->slot); 16987bc9f6aSratchov f->slot = NULL; 17087bc9f6aSratchov } 17187bc9f6aSratchov if (f->midi) { 172326545d4Sratchov tags = midi_tags(f->midi); 173326545d4Sratchov for (i = 0; i < DEV_NMAX; i++) { 17436355b88Sratchov if ((tags & (1 << i)) && (o = opt_bynum(i)) != NULL) 17536355b88Sratchov opt_unref(o); 176326545d4Sratchov } 17787bc9f6aSratchov midi_del(f->midi); 17887bc9f6aSratchov f->midi = NULL; 17987bc9f6aSratchov } 1809fd7fddfSratchov if (f->port) { 1819fd7fddfSratchov port_unref(f->port); 1829fd7fddfSratchov f->port = NULL; 1839fd7fddfSratchov } 184d07fece6Sratchov if (f->ctlslot) { 185d07fece6Sratchov ctlslot_del(f->ctlslot); 186d07fece6Sratchov f->ctlslot = NULL; 187d07fece6Sratchov xfree(f->ctldesc); 188d07fece6Sratchov } 18987bc9f6aSratchov file_del(f->file); 19087bc9f6aSratchov close(f->fd); 191e6f25aa0Sratchov file_slowaccept = 0; 19287bc9f6aSratchov xfree(f); 19387bc9f6aSratchov } 19487bc9f6aSratchov 19587bc9f6aSratchov void 19687bc9f6aSratchov sock_slot_fill(void *arg) 19787bc9f6aSratchov { 19887bc9f6aSratchov struct sock *f = arg; 19987bc9f6aSratchov struct slot *s = f->slot; 20087bc9f6aSratchov 20187bc9f6aSratchov f->fillpending += s->round; 20287bc9f6aSratchov #ifdef DEBUG 2037b639200Sratchov logx(4, "%s%u: fill, rmax -> %d, pending -> %d", 2047b639200Sratchov s->name, s->unit, f->rmax, f->fillpending); 20587bc9f6aSratchov #endif 20687bc9f6aSratchov } 20787bc9f6aSratchov 20887bc9f6aSratchov void 20987bc9f6aSratchov sock_slot_flush(void *arg) 21087bc9f6aSratchov { 21187bc9f6aSratchov struct sock *f = arg; 21287bc9f6aSratchov struct slot *s = f->slot; 21387bc9f6aSratchov 21487bc9f6aSratchov f->wmax += s->round * s->sub.bpf; 21587bc9f6aSratchov #ifdef DEBUG 2167b639200Sratchov logx(4, "%s%u: flush, wmax -> %d", s->name, s->unit, f->wmax); 21787bc9f6aSratchov #endif 21887bc9f6aSratchov } 21987bc9f6aSratchov 22087bc9f6aSratchov void 22187bc9f6aSratchov sock_slot_eof(void *arg) 22287bc9f6aSratchov { 22387bc9f6aSratchov struct sock *f = arg; 22487bc9f6aSratchov #ifdef DEBUG 2257b639200Sratchov struct slot *s = f->slot; 2267b639200Sratchov 2277b639200Sratchov logx(3, "%s%u: eof", s->name, s->unit); 22887bc9f6aSratchov #endif 22987bc9f6aSratchov f->stoppending = 1; 23087bc9f6aSratchov } 23187bc9f6aSratchov 23287bc9f6aSratchov void 2336f413563Sratchov sock_slot_onmove(void *arg) 23487bc9f6aSratchov { 23587bc9f6aSratchov struct sock *f = (struct sock *)arg; 23687bc9f6aSratchov struct slot *s = f->slot; 23787bc9f6aSratchov 23887bc9f6aSratchov #ifdef DEBUG 2397b639200Sratchov logx(4, "%s%u: onmove: delta -> %d", s->name, s->unit, s->delta); 24087bc9f6aSratchov #endif 24187bc9f6aSratchov if (s->pstate != SOCK_START) 24287bc9f6aSratchov return; 24387bc9f6aSratchov f->tickpending++; 24487bc9f6aSratchov } 24587bc9f6aSratchov 24687bc9f6aSratchov void 2474182f7f9Sratchov sock_slot_onvol(void *arg) 24887bc9f6aSratchov { 24987bc9f6aSratchov struct sock *f = (struct sock *)arg; 25087bc9f6aSratchov struct slot *s = f->slot; 25187bc9f6aSratchov 25287bc9f6aSratchov #ifdef DEBUG 2537b639200Sratchov logx(4, "%s%u: onvol: vol -> %d", s->name, s->unit, s->vol); 25487bc9f6aSratchov #endif 25587bc9f6aSratchov if (s->pstate != SOCK_START) 25687bc9f6aSratchov return; 25787bc9f6aSratchov } 25887bc9f6aSratchov 25987bc9f6aSratchov void 26087bc9f6aSratchov sock_midi_imsg(void *arg, unsigned char *msg, int size) 26187bc9f6aSratchov { 26287bc9f6aSratchov struct sock *f = arg; 26387bc9f6aSratchov 26487bc9f6aSratchov midi_send(f->midi, msg, size); 26587bc9f6aSratchov } 26687bc9f6aSratchov 26787bc9f6aSratchov void 26887bc9f6aSratchov sock_midi_omsg(void *arg, unsigned char *msg, int size) 26987bc9f6aSratchov { 27087bc9f6aSratchov struct sock *f = arg; 27187bc9f6aSratchov 27287bc9f6aSratchov midi_out(f->midi, msg, size); 27387bc9f6aSratchov } 27487bc9f6aSratchov 27587bc9f6aSratchov void 27687bc9f6aSratchov sock_midi_fill(void *arg, int count) 27787bc9f6aSratchov { 27887bc9f6aSratchov struct sock *f = arg; 27987bc9f6aSratchov 28087bc9f6aSratchov f->fillpending += count; 28187bc9f6aSratchov } 28287bc9f6aSratchov 283e575fbdeSratchov void 284e575fbdeSratchov sock_ctl_sync(void *arg) 285e575fbdeSratchov { 286e575fbdeSratchov struct sock *f = arg; 287e575fbdeSratchov 288e575fbdeSratchov if (f->ctlops & SOCK_CTLDESC) 289e575fbdeSratchov f->ctlsyncpending = 1; 290e575fbdeSratchov } 291e575fbdeSratchov 29287bc9f6aSratchov struct sock * 29387bc9f6aSratchov sock_new(int fd) 29487bc9f6aSratchov { 29587bc9f6aSratchov struct sock *f; 29687bc9f6aSratchov 29787bc9f6aSratchov f = xmalloc(sizeof(struct sock)); 29887bc9f6aSratchov f->pstate = SOCK_AUTH; 29987bc9f6aSratchov f->slot = NULL; 300fd35ec67Sratchov f->port = NULL; 30187bc9f6aSratchov f->midi = NULL; 302d07fece6Sratchov f->ctlslot = NULL; 30387bc9f6aSratchov f->tickpending = 0; 30487bc9f6aSratchov f->fillpending = 0; 30587bc9f6aSratchov f->stoppending = 0; 30687bc9f6aSratchov f->wstate = SOCK_WIDLE; 30787bc9f6aSratchov f->wtodo = 0xdeadbeef; 30887bc9f6aSratchov f->rstate = SOCK_RMSG; 30987bc9f6aSratchov f->rtodo = sizeof(struct amsg); 31087bc9f6aSratchov f->wmax = f->rmax = 0; 31187bc9f6aSratchov f->lastvol = -1; 312d07fece6Sratchov f->ctlops = 0; 313d07fece6Sratchov f->ctlsyncpending = 0; 31487bc9f6aSratchov f->file = file_new(&sock_fileops, f, "sock", 1); 31587bc9f6aSratchov f->fd = fd; 31687bc9f6aSratchov if (f->file == NULL) { 31787bc9f6aSratchov xfree(f); 31887bc9f6aSratchov return NULL; 31987bc9f6aSratchov } 32087bc9f6aSratchov f->next = sock_list; 32187bc9f6aSratchov sock_list = f; 32287bc9f6aSratchov return f; 32387bc9f6aSratchov } 32487bc9f6aSratchov 32587bc9f6aSratchov void 32687bc9f6aSratchov sock_exit(void *arg) 32787bc9f6aSratchov { 32887bc9f6aSratchov struct sock *f = (struct sock *)arg; 32987bc9f6aSratchov 33087bc9f6aSratchov #ifdef DEBUG 3317b639200Sratchov logx(3, "sock %d: exit", f->fd); 33287bc9f6aSratchov #endif 33387bc9f6aSratchov sock_close(f); 33487bc9f6aSratchov } 33587bc9f6aSratchov 33687bc9f6aSratchov /* 3377c71888cSratchov * write on the socket fd and handle errors 33887bc9f6aSratchov */ 33987bc9f6aSratchov int 34087bc9f6aSratchov sock_fdwrite(struct sock *f, void *data, int count) 34187bc9f6aSratchov { 34287bc9f6aSratchov int n; 34387bc9f6aSratchov 34487bc9f6aSratchov n = write(f->fd, data, count); 345f933f6d7Sratchov if (n == -1) { 34687bc9f6aSratchov #ifdef DEBUG 34787bc9f6aSratchov if (errno == EFAULT) { 3487b639200Sratchov logx(0, "%s: fault", __func__); 34987bc9f6aSratchov panic(); 35087bc9f6aSratchov } 35187bc9f6aSratchov #endif 35287bc9f6aSratchov if (errno != EAGAIN) { 3537b639200Sratchov logx(1, "sock %d: write failed, errno = %d", f->fd, errno); 35487bc9f6aSratchov sock_close(f); 35587bc9f6aSratchov } else { 35687bc9f6aSratchov #ifdef DEBUG 3577b639200Sratchov logx(4, "sock %d: write blocked", f->fd); 35887bc9f6aSratchov #endif 35987bc9f6aSratchov } 36087bc9f6aSratchov return 0; 36187bc9f6aSratchov } 36287bc9f6aSratchov if (n == 0) { 36387bc9f6aSratchov sock_close(f); 36487bc9f6aSratchov return 0; 36587bc9f6aSratchov } 36687bc9f6aSratchov return n; 36787bc9f6aSratchov } 36887bc9f6aSratchov 36987bc9f6aSratchov /* 3707c71888cSratchov * read from the socket fd and handle errors 37187bc9f6aSratchov */ 37287bc9f6aSratchov int 37387bc9f6aSratchov sock_fdread(struct sock *f, void *data, int count) 37487bc9f6aSratchov { 37587bc9f6aSratchov int n; 37687bc9f6aSratchov 37787bc9f6aSratchov n = read(f->fd, data, count); 378f933f6d7Sratchov if (n == -1) { 37987bc9f6aSratchov #ifdef DEBUG 38087bc9f6aSratchov if (errno == EFAULT) { 3817b639200Sratchov logx(0, "%s: fault", __func__); 38287bc9f6aSratchov panic(); 38387bc9f6aSratchov } 38487bc9f6aSratchov #endif 38587bc9f6aSratchov if (errno != EAGAIN) { 3867b639200Sratchov logx(1, "sock %d: read failed, errno = %d", f->fd, errno); 38787bc9f6aSratchov sock_close(f); 38887bc9f6aSratchov } else { 38987bc9f6aSratchov #ifdef DEBUG 3907b639200Sratchov logx(4, "sock %d: read blocked", f->fd); 39187bc9f6aSratchov #endif 39287bc9f6aSratchov } 39387bc9f6aSratchov return 0; 39487bc9f6aSratchov } 39587bc9f6aSratchov if (n == 0) { 39687bc9f6aSratchov sock_close(f); 39787bc9f6aSratchov return 0; 39887bc9f6aSratchov } 39987bc9f6aSratchov return n; 40087bc9f6aSratchov } 40187bc9f6aSratchov 40287bc9f6aSratchov /* 40387bc9f6aSratchov * read the next message into f->rmsg, return 1 on success 40487bc9f6aSratchov */ 40587bc9f6aSratchov int 40687bc9f6aSratchov sock_rmsg(struct sock *f) 40787bc9f6aSratchov { 40887bc9f6aSratchov int n; 40987bc9f6aSratchov char *data; 41087bc9f6aSratchov 41187bc9f6aSratchov #ifdef DEBUG 41287bc9f6aSratchov if (f->rtodo == 0) { 4137b639200Sratchov logx(0, "%s: sock %d: nothing to read", __func__, f->fd); 41487bc9f6aSratchov panic(); 41587bc9f6aSratchov } 41687bc9f6aSratchov #endif 41787bc9f6aSratchov data = (char *)&f->rmsg + sizeof(struct amsg) - f->rtodo; 41887bc9f6aSratchov n = sock_fdread(f, data, f->rtodo); 41987bc9f6aSratchov if (n == 0) 42087bc9f6aSratchov return 0; 42187bc9f6aSratchov if (n < f->rtodo) { 42287bc9f6aSratchov f->rtodo -= n; 42387bc9f6aSratchov return 0; 42487bc9f6aSratchov } 42587bc9f6aSratchov f->rtodo = 0; 42687bc9f6aSratchov #ifdef DEBUG 4277b639200Sratchov logx(4, "sock %d: read full message", f->fd); 42887bc9f6aSratchov #endif 42987bc9f6aSratchov return 1; 43087bc9f6aSratchov } 43187bc9f6aSratchov 43287bc9f6aSratchov /* 43387bc9f6aSratchov * write the message in f->rmsg, return 1 on success 43487bc9f6aSratchov */ 43587bc9f6aSratchov int 43687bc9f6aSratchov sock_wmsg(struct sock *f) 43787bc9f6aSratchov { 43887bc9f6aSratchov int n; 43987bc9f6aSratchov char *data; 44087bc9f6aSratchov 44187bc9f6aSratchov #ifdef DEBUG 44287bc9f6aSratchov if (f->wtodo == 0) { 4437b639200Sratchov logx(0, "%s: sock %d: already written", __func__, f->fd); 4447b639200Sratchov /* XXX: this is fatal and we should exit here */ 44587bc9f6aSratchov } 44687bc9f6aSratchov #endif 44787bc9f6aSratchov data = (char *)&f->wmsg + sizeof(struct amsg) - f->wtodo; 44887bc9f6aSratchov n = sock_fdwrite(f, data, f->wtodo); 44987bc9f6aSratchov if (n == 0) 45087bc9f6aSratchov return 0; 45187bc9f6aSratchov if (n < f->wtodo) { 45287bc9f6aSratchov f->wtodo -= n; 45387bc9f6aSratchov return 0; 45487bc9f6aSratchov } 45587bc9f6aSratchov f->wtodo = 0; 45687bc9f6aSratchov #ifdef DEBUG 4577b639200Sratchov logx(4, "sock %d: wrote full message", f->fd); 45887bc9f6aSratchov #endif 45987bc9f6aSratchov return 1; 46087bc9f6aSratchov } 46187bc9f6aSratchov 46287bc9f6aSratchov /* 46387bc9f6aSratchov * read data into the slot/midi ring buffer 46487bc9f6aSratchov */ 46587bc9f6aSratchov int 46687bc9f6aSratchov sock_rdata(struct sock *f) 46787bc9f6aSratchov { 46807826207Sratchov unsigned char midibuf[MIDI_BUFSZ]; 46987bc9f6aSratchov unsigned char *data; 47087bc9f6aSratchov int n, count; 47187bc9f6aSratchov 47287bc9f6aSratchov #ifdef DEBUG 47387bc9f6aSratchov if (f->rtodo == 0) { 4747b639200Sratchov logx(0, "%s: sock %d: data block already read", __func__, f->fd); 47587bc9f6aSratchov panic(); 47687bc9f6aSratchov } 47787bc9f6aSratchov #endif 47887bc9f6aSratchov while (f->rtodo > 0) { 47907826207Sratchov if (f->slot) 48007826207Sratchov data = abuf_wgetblk(&f->slot->mix.buf, &count); 48107826207Sratchov else { 48207826207Sratchov data = midibuf; 48307826207Sratchov count = MIDI_BUFSZ; 48407826207Sratchov } 48587bc9f6aSratchov if (count > f->rtodo) 48687bc9f6aSratchov count = f->rtodo; 48787bc9f6aSratchov n = sock_fdread(f, data, count); 48887bc9f6aSratchov if (n == 0) 48987bc9f6aSratchov return 0; 49087bc9f6aSratchov f->rtodo -= n; 49107826207Sratchov if (f->slot) 49207826207Sratchov abuf_wcommit(&f->slot->mix.buf, n); 49307826207Sratchov else 49407826207Sratchov midi_in(f->midi, midibuf, n); 49587bc9f6aSratchov } 49687bc9f6aSratchov #ifdef DEBUG 4977b639200Sratchov logx(4, "sock %d: read complete block", f->fd); 49887bc9f6aSratchov #endif 49987bc9f6aSratchov if (f->slot) 50087bc9f6aSratchov slot_write(f->slot); 50187bc9f6aSratchov return 1; 50287bc9f6aSratchov } 50387bc9f6aSratchov 50487bc9f6aSratchov /* 5057c71888cSratchov * write data to the slot/midi ring buffer 50687bc9f6aSratchov */ 50787bc9f6aSratchov int 50887bc9f6aSratchov sock_wdata(struct sock *f) 50987bc9f6aSratchov { 51087bc9f6aSratchov static unsigned char dummy[AMSG_DATAMAX]; 51187bc9f6aSratchov unsigned char *data = NULL; 51287bc9f6aSratchov int n, count; 51387bc9f6aSratchov 51487bc9f6aSratchov #ifdef DEBUG 51587bc9f6aSratchov if (f->wtodo == 0) { 5167b639200Sratchov logx(0, "%s: sock %d: zero-sized data block", __func__, f->fd); 51787bc9f6aSratchov panic(); 51887bc9f6aSratchov } 51987bc9f6aSratchov #endif 52087bc9f6aSratchov if (f->pstate == SOCK_STOP) { 52187bc9f6aSratchov while (f->wtodo > 0) { 52287bc9f6aSratchov n = sock_fdwrite(f, dummy, f->wtodo); 52387bc9f6aSratchov if (n == 0) 52487bc9f6aSratchov return 0; 52587bc9f6aSratchov f->wtodo -= n; 52687bc9f6aSratchov } 52787bc9f6aSratchov #ifdef DEBUG 5287b639200Sratchov logx(4, "sock %d: zero-filled remaining block", f->fd); 52987bc9f6aSratchov #endif 53087bc9f6aSratchov return 1; 53187bc9f6aSratchov } 53287bc9f6aSratchov while (f->wtodo > 0) { 533f2c5b8a8Sratchov /* 534f2c5b8a8Sratchov * f->slot and f->midi are set by sock_hello(), so 535f2c5b8a8Sratchov * count is always properly initialized 536f2c5b8a8Sratchov */ 5377947a9ddSratchov if (f->slot) 5387947a9ddSratchov data = abuf_rgetblk(&f->slot->sub.buf, &count); 5397947a9ddSratchov else if (f->midi) 5407947a9ddSratchov data = abuf_rgetblk(&f->midi->obuf, &count); 541d07fece6Sratchov else { 5423e5ee6d4Sratchov data = f->ctldesc + (f->wsize - f->wtodo); 543d07fece6Sratchov count = f->wtodo; 544d07fece6Sratchov } 54587bc9f6aSratchov if (count > f->wtodo) 54687bc9f6aSratchov count = f->wtodo; 54787bc9f6aSratchov n = sock_fdwrite(f, data, count); 54887bc9f6aSratchov if (n == 0) 54987bc9f6aSratchov return 0; 55087bc9f6aSratchov f->wtodo -= n; 5517947a9ddSratchov if (f->slot) 5527947a9ddSratchov abuf_rdiscard(&f->slot->sub.buf, n); 5537947a9ddSratchov else if (f->midi) 5547947a9ddSratchov abuf_rdiscard(&f->midi->obuf, n); 55587bc9f6aSratchov } 55687bc9f6aSratchov if (f->slot) 55787bc9f6aSratchov slot_read(f->slot); 55887bc9f6aSratchov if (f->midi) 55987bc9f6aSratchov midi_fill(f->midi); 56087bc9f6aSratchov #ifdef DEBUG 5617b639200Sratchov logx(4, "sock %d: wrote complete block", f->fd); 56287bc9f6aSratchov #endif 56387bc9f6aSratchov return 1; 56487bc9f6aSratchov } 56587bc9f6aSratchov 56687bc9f6aSratchov int 56787bc9f6aSratchov sock_setpar(struct sock *f) 56887bc9f6aSratchov { 56987bc9f6aSratchov struct slot *s = f->slot; 570c67d5b9aSratchov struct dev *d = s->opt->dev; 57187bc9f6aSratchov struct amsg_par *p = &f->rmsg.u.par; 572bc4f1c78Sratchov unsigned int min, max; 573bc4f1c78Sratchov uint32_t rate, appbufsz; 574bc4f1c78Sratchov uint16_t pchan, rchan; 57587bc9f6aSratchov 57687bc9f6aSratchov rchan = ntohs(p->rchan); 57787bc9f6aSratchov pchan = ntohs(p->pchan); 57887bc9f6aSratchov appbufsz = ntohl(p->appbufsz); 57987bc9f6aSratchov rate = ntohl(p->rate); 58087bc9f6aSratchov 58187bc9f6aSratchov if (AMSG_ISSET(p->bits)) { 58287bc9f6aSratchov if (p->bits < BITS_MIN || p->bits > BITS_MAX) { 58387bc9f6aSratchov #ifdef DEBUG 5847b639200Sratchov logx(1, "sock %d: %d: bits out of bounds", f->fd, p->bits); 58587bc9f6aSratchov #endif 58687bc9f6aSratchov return 0; 58787bc9f6aSratchov } 58887bc9f6aSratchov if (AMSG_ISSET(p->bps)) { 58987bc9f6aSratchov if (p->bps < ((p->bits + 7) / 8) || p->bps > 4) { 59087bc9f6aSratchov #ifdef DEBUG 5917b639200Sratchov logx(1, "sock %d: %d: wrong bytes per sample", 5927b639200Sratchov f->fd, p->bps); 59387bc9f6aSratchov #endif 59487bc9f6aSratchov return 0; 59587bc9f6aSratchov } 59687bc9f6aSratchov } else 59787bc9f6aSratchov p->bps = APARAMS_BPS(p->bits); 59887bc9f6aSratchov s->par.bits = p->bits; 59987bc9f6aSratchov s->par.bps = p->bps; 60087bc9f6aSratchov } 60187bc9f6aSratchov if (AMSG_ISSET(p->sig)) 60287bc9f6aSratchov s->par.sig = p->sig ? 1 : 0; 60387bc9f6aSratchov if (AMSG_ISSET(p->le)) 60487bc9f6aSratchov s->par.le = p->le ? 1 : 0; 60587bc9f6aSratchov if (AMSG_ISSET(p->msb)) 60687bc9f6aSratchov s->par.msb = p->msb ? 1 : 0; 60787bc9f6aSratchov if (AMSG_ISSET(rchan) && (s->mode & MODE_RECMASK)) { 60887bc9f6aSratchov if (rchan < 1) 60987bc9f6aSratchov rchan = 1; 610875135e3Sratchov else if (rchan > NCHAN_MAX) 61187bc9f6aSratchov rchan = NCHAN_MAX; 61223321a5cSratchov s->sub.nch = rchan; 61387bc9f6aSratchov } 61487bc9f6aSratchov if (AMSG_ISSET(pchan) && (s->mode & MODE_PLAY)) { 61587bc9f6aSratchov if (pchan < 1) 61687bc9f6aSratchov pchan = 1; 617875135e3Sratchov else if (pchan > NCHAN_MAX) 61887bc9f6aSratchov pchan = NCHAN_MAX; 61923321a5cSratchov s->mix.nch = pchan; 62087bc9f6aSratchov } 62187bc9f6aSratchov if (AMSG_ISSET(rate)) { 62287bc9f6aSratchov if (rate < RATE_MIN) 62387bc9f6aSratchov rate = RATE_MIN; 624875135e3Sratchov else if (rate > RATE_MAX) 62587bc9f6aSratchov rate = RATE_MAX; 62687bc9f6aSratchov s->round = dev_roundof(d, rate); 62787bc9f6aSratchov s->rate = rate; 6287b639200Sratchov if (!AMSG_ISSET(appbufsz)) 62987bc9f6aSratchov appbufsz = d->bufsz / d->round * s->round; 63087bc9f6aSratchov } 63187bc9f6aSratchov if (AMSG_ISSET(p->xrun)) { 63287bc9f6aSratchov if (p->xrun != XRUN_IGNORE && 63387bc9f6aSratchov p->xrun != XRUN_SYNC && 63487bc9f6aSratchov p->xrun != XRUN_ERROR) { 63587bc9f6aSratchov #ifdef DEBUG 6367b639200Sratchov logx(1, "sock %d: %u: bad xrun policy", f->fd, p->xrun); 63787bc9f6aSratchov #endif 63887bc9f6aSratchov return 0; 63987bc9f6aSratchov } 64087bc9f6aSratchov s->xrun = p->xrun; 6410e6be583Sratchov if (s->opt->mtc != NULL && s->xrun == XRUN_IGNORE) 64287bc9f6aSratchov s->xrun = XRUN_SYNC; 64387bc9f6aSratchov } 64487bc9f6aSratchov if (AMSG_ISSET(appbufsz)) { 64587bc9f6aSratchov rate = s->rate; 64687bc9f6aSratchov min = 1; 64787bc9f6aSratchov max = 1 + rate / d->round; 64887bc9f6aSratchov min *= s->round; 64987bc9f6aSratchov max *= s->round; 650fd3cf84aSratchov appbufsz += s->round / 2; 65187bc9f6aSratchov appbufsz -= appbufsz % s->round; 65287bc9f6aSratchov if (appbufsz < min) 65387bc9f6aSratchov appbufsz = min; 65487bc9f6aSratchov if (appbufsz > max) 65587bc9f6aSratchov appbufsz = max; 65687bc9f6aSratchov s->appbufsz = appbufsz; 65787bc9f6aSratchov } 65887bc9f6aSratchov return 1; 65987bc9f6aSratchov } 66087bc9f6aSratchov 66187bc9f6aSratchov int 66287bc9f6aSratchov sock_auth(struct sock *f) 66387bc9f6aSratchov { 66487bc9f6aSratchov struct amsg_auth *p = &f->rmsg.u.auth; 66572ca3e97Sratchov uid_t euid; 66672ca3e97Sratchov gid_t egid; 66772ca3e97Sratchov 66872ca3e97Sratchov /* 669d9a51c35Sjmc * root bypasses any authentication checks and has no session 67072ca3e97Sratchov */ 67172ca3e97Sratchov if (getpeereid(f->fd, &euid, &egid) == 0 && euid == 0) { 67272ca3e97Sratchov f->pstate = SOCK_HELLO; 67372ca3e97Sratchov f->sesrefs = 0; 67472ca3e97Sratchov return 1; 67572ca3e97Sratchov } 67687bc9f6aSratchov 67787bc9f6aSratchov if (sock_sesrefs == 0) { 67887bc9f6aSratchov /* start a new session */ 67987bc9f6aSratchov memcpy(sock_sescookie, p->cookie, AMSG_COOKIELEN); 68072ca3e97Sratchov f->sesrefs = 1; 68187bc9f6aSratchov } else if (memcmp(sock_sescookie, p->cookie, AMSG_COOKIELEN) != 0) { 68287bc9f6aSratchov /* another session is active, drop connection */ 68387bc9f6aSratchov return 0; 68487bc9f6aSratchov } 68572ca3e97Sratchov sock_sesrefs += f->sesrefs; 68687bc9f6aSratchov f->pstate = SOCK_HELLO; 68787bc9f6aSratchov return 1; 68887bc9f6aSratchov } 68987bc9f6aSratchov 69087bc9f6aSratchov int 69187bc9f6aSratchov sock_hello(struct sock *f) 69287bc9f6aSratchov { 69387bc9f6aSratchov struct amsg_hello *p = &f->rmsg.u.hello; 69487bc9f6aSratchov struct port *c; 695f218ca2bSratchov struct opt *opt; 69687bc9f6aSratchov unsigned int mode; 6972988007fSratchov unsigned int id; 69887bc9f6aSratchov 69987bc9f6aSratchov mode = ntohs(p->mode); 7002988007fSratchov id = ntohl(p->id); 70187bc9f6aSratchov #ifdef DEBUG 7027b639200Sratchov logx(3, "sock %d: hello from <%s>, mode %x, ver %d", 7037b639200Sratchov f->fd, p->who, mode, p->version); 70487bc9f6aSratchov #endif 70587bc9f6aSratchov if (p->version != AMSG_VERSION) { 7067b639200Sratchov logx(1, "sock %d: %u: unsupported version", f->fd, p->version); 70787bc9f6aSratchov return 0; 70887bc9f6aSratchov } 70987bc9f6aSratchov switch (mode) { 71087bc9f6aSratchov case MODE_MIDIIN: 71187bc9f6aSratchov case MODE_MIDIOUT: 71287bc9f6aSratchov case MODE_MIDIOUT | MODE_MIDIIN: 71387bc9f6aSratchov case MODE_REC: 71487bc9f6aSratchov case MODE_PLAY: 71587bc9f6aSratchov case MODE_PLAY | MODE_REC: 716d07fece6Sratchov case MODE_CTLREAD: 717d07fece6Sratchov case MODE_CTLWRITE: 718d07fece6Sratchov case MODE_CTLREAD | MODE_CTLWRITE: 71987bc9f6aSratchov break; 72087bc9f6aSratchov default: 72187bc9f6aSratchov #ifdef DEBUG 7227b639200Sratchov logx(1, "sock %d: %u: unsupported mode", f->fd, mode); 72387bc9f6aSratchov #endif 72487bc9f6aSratchov return 0; 72587bc9f6aSratchov } 72687bc9f6aSratchov f->pstate = SOCK_INIT; 7279fd7fddfSratchov f->port = NULL; 7286bd694d1Sratchov if (mode & MODE_MIDIMASK) { 72987bc9f6aSratchov f->slot = NULL; 73087bc9f6aSratchov f->midi = midi_new(&sock_midiops, f, mode); 73187bc9f6aSratchov if (f->midi == NULL) 73287bc9f6aSratchov return 0; 73387bc9f6aSratchov /* XXX: add 'devtype' to libsndio */ 73436355b88Sratchov if (p->devnum == AMSG_NODEV) { 73536355b88Sratchov opt = opt_byname(p->opt); 7365fb8f3c9Sratchov if (opt == NULL) 7375fb8f3c9Sratchov return 0; 73836355b88Sratchov if (!opt_ref(opt)) 73936355b88Sratchov return 0; 74036355b88Sratchov midi_tag(f->midi, opt->num); 74136355b88Sratchov } else if (p->devnum < 16) { 74236355b88Sratchov opt = legacy_opt(p->devnum, p->opt); 74336355b88Sratchov if (opt == NULL) 74436355b88Sratchov return 0; 74536355b88Sratchov if (!opt_ref(opt)) 746326545d4Sratchov return 0; 7475fb8f3c9Sratchov midi_tag(f->midi, opt->num); 74887bc9f6aSratchov } else if (p->devnum < 32) { 74987bc9f6aSratchov midi_tag(f->midi, p->devnum); 75087bc9f6aSratchov } else if (p->devnum < 48) { 75136355b88Sratchov c = port_alt_ref(p->devnum - 32); 75236355b88Sratchov if (c == NULL) 75387bc9f6aSratchov return 0; 7549fd7fddfSratchov f->port = c; 75507826207Sratchov midi_link(f->midi, c->midi); 75687bc9f6aSratchov } else 75787bc9f6aSratchov return 0; 75887bc9f6aSratchov return 1; 75987bc9f6aSratchov } 760d07fece6Sratchov if (mode & MODE_CTLMASK) { 76136355b88Sratchov if (p->devnum == AMSG_NODEV) { 76236355b88Sratchov opt = opt_byname(p->opt); 763bb6cfcd4Sratchov if (opt == NULL) 764bb6cfcd4Sratchov return 0; 76536355b88Sratchov } else { 76636355b88Sratchov opt = legacy_opt(p->devnum, p->opt); 76736355b88Sratchov if (opt == NULL) 76836355b88Sratchov return 0; 76936355b88Sratchov } 770bb6cfcd4Sratchov f->ctlslot = ctlslot_new(opt, &sock_ctlops, f); 771d07fece6Sratchov if (f->ctlslot == NULL) { 7727b639200Sratchov logx(2, "sock %d: couldn't get ctlslot", f->fd); 773d07fece6Sratchov return 0; 774d07fece6Sratchov } 7753e5ee6d4Sratchov f->ctldesc = xmalloc(SOCK_CTLDESC_SIZE); 776d07fece6Sratchov f->ctlops = 0; 777d07fece6Sratchov f->ctlsyncpending = 0; 778d07fece6Sratchov return 1; 779d07fece6Sratchov } 78036355b88Sratchov opt = (p->devnum == AMSG_NODEV) ? 78136355b88Sratchov opt_byname(p->opt) : legacy_opt(p->devnum, p->opt); 782f218ca2bSratchov if (opt == NULL) 78387bc9f6aSratchov return 0; 784c67d5b9aSratchov f->slot = slot_new(opt, id, p->who, &sock_slotops, f, mode); 785edbb4404Sratchov if (f->slot == NULL) 78687bc9f6aSratchov return 0; 78787bc9f6aSratchov f->midi = NULL; 78887bc9f6aSratchov return 1; 78987bc9f6aSratchov } 79087bc9f6aSratchov 79187bc9f6aSratchov /* 79287bc9f6aSratchov * execute the message in f->rmsg, return 1 on success 79387bc9f6aSratchov */ 79487bc9f6aSratchov int 79587bc9f6aSratchov sock_execmsg(struct sock *f) 79687bc9f6aSratchov { 797d07fece6Sratchov struct ctl *c; 79887bc9f6aSratchov struct slot *s = f->slot; 79987bc9f6aSratchov struct amsg *m = &f->rmsg; 800*0cdbd964Sratchov struct conv conv; 80187bc9f6aSratchov unsigned char *data; 80255a46d88Sratchov unsigned int size, ctl; 8033e5ee6d4Sratchov int cmd; 80487bc9f6aSratchov 8053e5ee6d4Sratchov cmd = ntohl(m->cmd); 8063e5ee6d4Sratchov switch (cmd) { 80787bc9f6aSratchov case AMSG_DATA: 80887bc9f6aSratchov #ifdef DEBUG 8097b639200Sratchov logx(4, "sock %d: DATA message", f->fd); 81087bc9f6aSratchov #endif 81187bc9f6aSratchov if (s != NULL && f->pstate != SOCK_START) { 81287bc9f6aSratchov #ifdef DEBUG 8137b639200Sratchov logx(1, "sock %d: DATA, wrong state", f->fd); 81487bc9f6aSratchov #endif 81587bc9f6aSratchov sock_close(f); 81687bc9f6aSratchov return 0; 81787bc9f6aSratchov } 81887bc9f6aSratchov if ((f->slot && !(f->slot->mode & MODE_PLAY)) || 81987bc9f6aSratchov (f->midi && !(f->midi->mode & MODE_MIDIOUT))) { 82087bc9f6aSratchov #ifdef DEBUG 8217b639200Sratchov logx(1, "sock %d: DATA, input-only mode", f->fd); 82287bc9f6aSratchov #endif 82387bc9f6aSratchov sock_close(f); 82487bc9f6aSratchov return 0; 82587bc9f6aSratchov } 82687bc9f6aSratchov size = ntohl(m->u.data.size); 82755a46d88Sratchov if (size == 0) { 82887bc9f6aSratchov #ifdef DEBUG 8297b639200Sratchov logx(1, "sock %d: zero size payload", f->fd); 83087bc9f6aSratchov #endif 83187bc9f6aSratchov sock_close(f); 83287bc9f6aSratchov return 0; 83387bc9f6aSratchov } 83487bc9f6aSratchov if (s != NULL && size % s->mix.bpf != 0) { 83587bc9f6aSratchov #ifdef DEBUG 8367b639200Sratchov logx(1, "sock %d: not aligned to frame", f->fd); 83787bc9f6aSratchov #endif 83887bc9f6aSratchov sock_close(f); 83987bc9f6aSratchov return 0; 84087bc9f6aSratchov } 84187bc9f6aSratchov if (s != NULL && size > f->ralign) { 84287bc9f6aSratchov #ifdef DEBUG 8437b639200Sratchov logx(1, "sock %d: size = %d, ralign = %d: " 8447b639200Sratchov "not aligned to block", f->fd, size, f->ralign); 84587bc9f6aSratchov #endif 84687bc9f6aSratchov sock_close(f); 84787bc9f6aSratchov return 0; 84887bc9f6aSratchov } 84987bc9f6aSratchov f->rstate = SOCK_RDATA; 85087bc9f6aSratchov f->rsize = f->rtodo = size; 85187bc9f6aSratchov if (s != NULL) { 85287bc9f6aSratchov f->ralign -= size; 85387bc9f6aSratchov if (f->ralign == 0) 85487bc9f6aSratchov f->ralign = s->round * s->mix.bpf; 85587bc9f6aSratchov } 85687bc9f6aSratchov if (f->rtodo > f->rmax) { 85787bc9f6aSratchov #ifdef DEBUG 8587b639200Sratchov logx(1, "sock %d: unexpected data, size = %u, rmax = %d", 8597b639200Sratchov f->fd, size, f->rmax); 86087bc9f6aSratchov #endif 86187bc9f6aSratchov sock_close(f); 86287bc9f6aSratchov return 0; 86387bc9f6aSratchov } 86487bc9f6aSratchov f->rmax -= f->rtodo; 86587bc9f6aSratchov if (f->rtodo == 0) { 86687bc9f6aSratchov #ifdef DEBUG 8677b639200Sratchov logx(1, "sock %d: zero-length data chunk", f->fd); 86887bc9f6aSratchov #endif 86987bc9f6aSratchov sock_close(f); 87087bc9f6aSratchov return 0; 87187bc9f6aSratchov } 87287bc9f6aSratchov break; 87387bc9f6aSratchov case AMSG_START: 87487bc9f6aSratchov #ifdef DEBUG 8757b639200Sratchov logx(3, "sock %d: START message", f->fd); 87687bc9f6aSratchov #endif 8777947a9ddSratchov if (f->pstate != SOCK_INIT || s == NULL) { 87887bc9f6aSratchov #ifdef DEBUG 8797b639200Sratchov logx(1, "sock %d: START, wrong state", f->fd); 88087bc9f6aSratchov #endif 88187bc9f6aSratchov sock_close(f); 88287bc9f6aSratchov return 0; 88387bc9f6aSratchov } 88487bc9f6aSratchov f->tickpending = 0; 88587bc9f6aSratchov f->stoppending = 0; 88687bc9f6aSratchov slot_start(s); 88787bc9f6aSratchov if (s->mode & MODE_PLAY) { 88887bc9f6aSratchov f->fillpending = s->appbufsz; 88987bc9f6aSratchov f->ralign = s->round * s->mix.bpf; 89087bc9f6aSratchov f->rmax = 0; 89187bc9f6aSratchov } 89287bc9f6aSratchov if (s->mode & MODE_RECMASK) { 89387bc9f6aSratchov f->walign = s->round * s->sub.bpf; 89487bc9f6aSratchov f->wmax = 0; 89587bc9f6aSratchov } 89687bc9f6aSratchov f->pstate = SOCK_START; 89787bc9f6aSratchov f->rstate = SOCK_RMSG; 89887bc9f6aSratchov f->rtodo = sizeof(struct amsg); 89987bc9f6aSratchov break; 90087bc9f6aSratchov case AMSG_STOP: 90187bc9f6aSratchov #ifdef DEBUG 9027b639200Sratchov logx(3, "sock %d: STOP message", f->fd); 90387bc9f6aSratchov #endif 90487bc9f6aSratchov if (f->pstate != SOCK_START) { 90587bc9f6aSratchov #ifdef DEBUG 9067b639200Sratchov logx(1, "sock %d: STOP, wrong state", f->fd); 90787bc9f6aSratchov #endif 90887bc9f6aSratchov sock_close(f); 90987bc9f6aSratchov return 0; 91087bc9f6aSratchov } 91187bc9f6aSratchov f->rmax = 0; 91287bc9f6aSratchov if (!(s->mode & MODE_PLAY)) 91387bc9f6aSratchov f->stoppending = 1; 91487bc9f6aSratchov f->pstate = SOCK_STOP; 91587bc9f6aSratchov f->rstate = SOCK_RMSG; 91687bc9f6aSratchov f->rtodo = sizeof(struct amsg); 91787bc9f6aSratchov if (s->mode & MODE_PLAY) { 91887bc9f6aSratchov if (f->ralign < s->round * s->mix.bpf) { 91987bc9f6aSratchov data = abuf_wgetblk(&s->mix.buf, &size); 92087bc9f6aSratchov #ifdef DEBUG 92187bc9f6aSratchov if (size < f->ralign) { 9227b639200Sratchov logx(0, "sock %d: unaligned stop, " 9237b639200Sratchov "size = %u, ralign = %u", 9247b639200Sratchov f->fd, size, f->ralign); 92587bc9f6aSratchov panic(); 92687bc9f6aSratchov } 92787bc9f6aSratchov #endif 928*0cdbd964Sratchov enc_init(&conv, &s->par, s->mix.nch); 929*0cdbd964Sratchov enc_sil_do(&conv, data, f->ralign / s->mix.bpf); 93087bc9f6aSratchov abuf_wcommit(&s->mix.buf, f->ralign); 93187bc9f6aSratchov f->ralign = s->round * s->mix.bpf; 93287bc9f6aSratchov } 93387bc9f6aSratchov } 934ec8a3410Sratchov slot_stop(s, AMSG_ISSET(m->u.stop.drain) ? m->u.stop.drain : 1); 93587bc9f6aSratchov break; 93687bc9f6aSratchov case AMSG_SETPAR: 93787bc9f6aSratchov #ifdef DEBUG 9387b639200Sratchov logx(3, "sock %d: SETPAR message", f->fd); 93987bc9f6aSratchov #endif 9407947a9ddSratchov if (f->pstate != SOCK_INIT || s == NULL) { 94187bc9f6aSratchov #ifdef DEBUG 9427b639200Sratchov logx(1, "sock %d: SETPAR, wrong state", f->fd); 94387bc9f6aSratchov #endif 94487bc9f6aSratchov sock_close(f); 94587bc9f6aSratchov return 0; 94687bc9f6aSratchov } 94787bc9f6aSratchov if (!sock_setpar(f)) { 94887bc9f6aSratchov sock_close(f); 94987bc9f6aSratchov return 0; 95087bc9f6aSratchov } 95187bc9f6aSratchov f->rtodo = sizeof(struct amsg); 95287bc9f6aSratchov f->rstate = SOCK_RMSG; 95387bc9f6aSratchov break; 95487bc9f6aSratchov case AMSG_GETPAR: 95587bc9f6aSratchov #ifdef DEBUG 9567b639200Sratchov logx(3, "sock %d: GETPAR message", f->fd); 95787bc9f6aSratchov #endif 9587947a9ddSratchov if (f->pstate != SOCK_INIT || s == NULL) { 95987bc9f6aSratchov #ifdef DEBUG 9607b639200Sratchov logx(1, "sock %d: GETPAR, wrong state", f->fd); 96187bc9f6aSratchov #endif 96287bc9f6aSratchov sock_close(f); 96387bc9f6aSratchov return 0; 96487bc9f6aSratchov } 96587bc9f6aSratchov AMSG_INIT(m); 96687bc9f6aSratchov m->cmd = htonl(AMSG_GETPAR); 96787bc9f6aSratchov m->u.par.legacy_mode = s->mode; 968c095e34bSratchov m->u.par.xrun = s->xrun; 96987bc9f6aSratchov m->u.par.bits = s->par.bits; 97087bc9f6aSratchov m->u.par.bps = s->par.bps; 97187bc9f6aSratchov m->u.par.sig = s->par.sig; 97287bc9f6aSratchov m->u.par.le = s->par.le; 97387bc9f6aSratchov m->u.par.msb = s->par.msb; 97423321a5cSratchov if (s->mode & MODE_PLAY) 97523321a5cSratchov m->u.par.pchan = htons(s->mix.nch); 97623321a5cSratchov if (s->mode & MODE_RECMASK) 97723321a5cSratchov m->u.par.rchan = htons(s->sub.nch); 97887bc9f6aSratchov m->u.par.rate = htonl(s->rate); 97987bc9f6aSratchov m->u.par.appbufsz = htonl(s->appbufsz); 98087bc9f6aSratchov m->u.par.bufsz = htonl(SLOT_BUFSZ(s)); 98187bc9f6aSratchov m->u.par.round = htonl(s->round); 98287bc9f6aSratchov f->rstate = SOCK_RRET; 98387bc9f6aSratchov f->rtodo = sizeof(struct amsg); 98487bc9f6aSratchov break; 98587bc9f6aSratchov case AMSG_SETVOL: 98687bc9f6aSratchov #ifdef DEBUG 9877b639200Sratchov logx(3, "sock %d: SETVOL message", f->fd); 98887bc9f6aSratchov #endif 9897947a9ddSratchov if (f->pstate < SOCK_INIT || s == NULL) { 99087bc9f6aSratchov #ifdef DEBUG 9917b639200Sratchov logx(1, "sock %d: SETVOL, wrong state", f->fd); 99287bc9f6aSratchov #endif 99387bc9f6aSratchov sock_close(f); 99487bc9f6aSratchov return 0; 99587bc9f6aSratchov } 99687bc9f6aSratchov ctl = ntohl(m->u.vol.ctl); 99787bc9f6aSratchov if (ctl > MIDI_MAXCTL) { 99887bc9f6aSratchov #ifdef DEBUG 9997b639200Sratchov logx(1, "sock %d: SETVOL, volume out of range", f->fd); 100087bc9f6aSratchov #endif 100187bc9f6aSratchov sock_close(f); 100287bc9f6aSratchov return 0; 100387bc9f6aSratchov } 100487bc9f6aSratchov f->rtodo = sizeof(struct amsg); 100587bc9f6aSratchov f->rstate = SOCK_RMSG; 100687bc9f6aSratchov f->lastvol = ctl; /* dont trigger feedback message */ 100787bc9f6aSratchov slot_setvol(s, ctl); 1008c67d5b9aSratchov dev_midi_vol(s->opt->dev, s); 100999580020Sratchov ctl_onval(CTL_SLOT_LEVEL, s, NULL, ctl); 1010d07fece6Sratchov break; 10114d8b188fSratchov case AMSG_CTLSUB_OLD: 1012d07fece6Sratchov case AMSG_CTLSUB: 1013d07fece6Sratchov #ifdef DEBUG 10147b639200Sratchov logx(3, "sock %d: CTLSUB message, desc = 0x%x, val = 0x%x", 10157b639200Sratchov f->fd, m->u.ctlsub.desc, m->u.ctlsub.val); 1016d07fece6Sratchov #endif 1017d07fece6Sratchov if (f->pstate != SOCK_INIT || f->ctlslot == NULL) { 1018d07fece6Sratchov #ifdef DEBUG 10197b639200Sratchov logx(1, "sock %d: CTLSUB, wrong state", f->fd); 1020d07fece6Sratchov #endif 1021d07fece6Sratchov sock_close(f); 1022d07fece6Sratchov return 0; 1023d07fece6Sratchov } 1024d07fece6Sratchov if (m->u.ctlsub.desc) { 1025d07fece6Sratchov if (!(f->ctlops & SOCK_CTLDESC)) { 1026c7054416Sratchov ctl = f->ctlslot->self; 102799580020Sratchov c = ctl_list; 1028d07fece6Sratchov while (c != NULL) { 102999580020Sratchov if (ctlslot_visible(f->ctlslot, c)) 1030d07fece6Sratchov c->desc_mask |= ctl; 1031d07fece6Sratchov c = c->next; 1032d07fece6Sratchov } 1033d07fece6Sratchov f->ctlops |= SOCK_CTLDESC; 1034d07fece6Sratchov f->ctlsyncpending = 1; 10354d8b188fSratchov f->ctl_desc_size = (cmd == AMSG_CTLSUB) ? 10364d8b188fSratchov sizeof(struct amsg_ctl_desc) : 10374d8b188fSratchov AMSG_OLD_DESC_SIZE; 1038e575fbdeSratchov } 1039d07fece6Sratchov } else 1040d07fece6Sratchov f->ctlops &= ~SOCK_CTLDESC; 1041d07fece6Sratchov if (m->u.ctlsub.val) { 1042d07fece6Sratchov f->ctlops |= SOCK_CTLVAL; 1043d07fece6Sratchov } else 1044d07fece6Sratchov f->ctlops &= ~SOCK_CTLVAL; 1045d07fece6Sratchov f->rstate = SOCK_RMSG; 1046d07fece6Sratchov f->rtodo = sizeof(struct amsg); 1047d07fece6Sratchov break; 1048d07fece6Sratchov case AMSG_CTLSET: 1049d07fece6Sratchov #ifdef DEBUG 10507b639200Sratchov logx(3, "sock %d: CTLSET message", f->fd); 1051d07fece6Sratchov #endif 1052d07fece6Sratchov if (f->pstate < SOCK_INIT || f->ctlslot == NULL) { 1053d07fece6Sratchov #ifdef DEBUG 10547b639200Sratchov logx(1, "sock %d: CTLSET, wrong state", f->fd); 1055d07fece6Sratchov #endif 1056d07fece6Sratchov sock_close(f); 1057d07fece6Sratchov return 0; 1058d07fece6Sratchov } 105999580020Sratchov 106099580020Sratchov c = ctlslot_lookup(f->ctlslot, ntohs(m->u.ctlset.addr)); 106199580020Sratchov if (c == NULL) { 1062d07fece6Sratchov #ifdef DEBUG 10637b639200Sratchov logx(1, "sock %d: CTLSET, wrong addr", f->fd); 106499580020Sratchov #endif 106599580020Sratchov sock_close(f); 106699580020Sratchov return 0; 106799580020Sratchov } 106899580020Sratchov if (!ctl_setval(c, ntohs(m->u.ctlset.val))) { 106999580020Sratchov #ifdef DEBUG 10707b639200Sratchov logx(1, "sock %d: CTLSET, bad value", f->fd); 1071d07fece6Sratchov #endif 1072d07fece6Sratchov sock_close(f); 1073d07fece6Sratchov return 0; 1074d07fece6Sratchov } 1075d07fece6Sratchov f->rtodo = sizeof(struct amsg); 1076d07fece6Sratchov f->rstate = SOCK_RMSG; 107787bc9f6aSratchov break; 107887bc9f6aSratchov case AMSG_AUTH: 107987bc9f6aSratchov #ifdef DEBUG 10807b639200Sratchov logx(3, "sock %d: AUTH message", f->fd); 108187bc9f6aSratchov #endif 108287bc9f6aSratchov if (f->pstate != SOCK_AUTH) { 108387bc9f6aSratchov #ifdef DEBUG 10847b639200Sratchov logx(1, "sock %d: AUTH, wrong state", f->fd); 108587bc9f6aSratchov #endif 108687bc9f6aSratchov sock_close(f); 108787bc9f6aSratchov return 0; 108887bc9f6aSratchov } 108987bc9f6aSratchov if (!sock_auth(f)) { 109087bc9f6aSratchov sock_close(f); 109187bc9f6aSratchov return 0; 109287bc9f6aSratchov } 109387bc9f6aSratchov f->rstate = SOCK_RMSG; 109487bc9f6aSratchov f->rtodo = sizeof(struct amsg); 109587bc9f6aSratchov break; 109687bc9f6aSratchov case AMSG_HELLO: 109787bc9f6aSratchov #ifdef DEBUG 10987b639200Sratchov logx(3, "sock %d: HELLO message", f->fd); 109987bc9f6aSratchov #endif 110087bc9f6aSratchov if (f->pstate != SOCK_HELLO) { 110187bc9f6aSratchov #ifdef DEBUG 11027b639200Sratchov logx(1, "sock %d: HELLO, wrong state", f->fd); 110387bc9f6aSratchov #endif 110487bc9f6aSratchov sock_close(f); 110587bc9f6aSratchov return 0; 110687bc9f6aSratchov } 110787bc9f6aSratchov if (!sock_hello(f)) { 110887bc9f6aSratchov sock_close(f); 110987bc9f6aSratchov return 0; 111087bc9f6aSratchov } 111187bc9f6aSratchov AMSG_INIT(m); 111287bc9f6aSratchov m->cmd = htonl(AMSG_ACK); 111387bc9f6aSratchov f->rstate = SOCK_RRET; 111487bc9f6aSratchov f->rtodo = sizeof(struct amsg); 111587bc9f6aSratchov break; 111687bc9f6aSratchov case AMSG_BYE: 111787bc9f6aSratchov #ifdef DEBUG 11187b639200Sratchov logx(3, "sock %d: BYE message", f->fd); 111987bc9f6aSratchov #endif 112087bc9f6aSratchov if (s != NULL && f->pstate != SOCK_INIT) { 112187bc9f6aSratchov #ifdef DEBUG 11227b639200Sratchov logx(1, "sock %d: BYE, wrong state", f->fd); 112387bc9f6aSratchov #endif 112487bc9f6aSratchov } 112587bc9f6aSratchov sock_close(f); 112687bc9f6aSratchov return 0; 112787bc9f6aSratchov default: 112887bc9f6aSratchov #ifdef DEBUG 11297b639200Sratchov logx(1, "sock %d: unknown command in message", f->fd); 113087bc9f6aSratchov #endif 113187bc9f6aSratchov sock_close(f); 113287bc9f6aSratchov return 0; 113387bc9f6aSratchov } 113487bc9f6aSratchov return 1; 113587bc9f6aSratchov } 113687bc9f6aSratchov 113787bc9f6aSratchov /* 113887bc9f6aSratchov * build a message in f->wmsg, return 1 on success and 0 if 113987bc9f6aSratchov * there's nothing to do. Assume f->wstate is SOCK_WIDLE 114087bc9f6aSratchov */ 114187bc9f6aSratchov int 114287bc9f6aSratchov sock_buildmsg(struct sock *f) 114387bc9f6aSratchov { 114499580020Sratchov unsigned int size, type, mask; 1145d07fece6Sratchov struct amsg_ctl_desc *desc; 1146d07fece6Sratchov struct ctl *c, **pc; 114787bc9f6aSratchov 114887bc9f6aSratchov /* 114987bc9f6aSratchov * If pos changed (or initial tick), build a MOVE message. 115087bc9f6aSratchov */ 115187bc9f6aSratchov if (f->tickpending) { 115287bc9f6aSratchov #ifdef DEBUG 11537b639200Sratchov logx(4, "sock %d: building MOVE message, delta = %d", f->fd, f->slot->delta); 115487bc9f6aSratchov #endif 115587bc9f6aSratchov AMSG_INIT(&f->wmsg); 115687bc9f6aSratchov f->wmsg.cmd = htonl(AMSG_MOVE); 115787bc9f6aSratchov f->wmsg.u.ts.delta = htonl(f->slot->delta); 115887bc9f6aSratchov f->wtodo = sizeof(struct amsg); 115987bc9f6aSratchov f->wstate = SOCK_WMSG; 116087bc9f6aSratchov f->tickpending = 0; 116187bc9f6aSratchov /* 116287bc9f6aSratchov * XXX: use tickpending as accumulator rather than 116387bc9f6aSratchov * slot->delta 116487bc9f6aSratchov */ 116587bc9f6aSratchov f->slot->delta = 0; 116687bc9f6aSratchov return 1; 116787bc9f6aSratchov } 116887bc9f6aSratchov 116987bc9f6aSratchov if (f->fillpending > 0) { 117087bc9f6aSratchov AMSG_INIT(&f->wmsg); 117187bc9f6aSratchov f->wmsg.cmd = htonl(AMSG_FLOWCTL); 117287bc9f6aSratchov f->wmsg.u.ts.delta = htonl(f->fillpending); 117387bc9f6aSratchov size = f->fillpending; 117487bc9f6aSratchov if (f->slot) 117587bc9f6aSratchov size *= f->slot->mix.bpf; 117687bc9f6aSratchov f->rmax += size; 117787bc9f6aSratchov #ifdef DEBUG 11787b639200Sratchov logx(4, "sock %d: building FLOWCTL message, " 11797b639200Sratchov "count = %d, rmax -> %d", f->fd, f->fillpending, f->rmax); 118087bc9f6aSratchov #endif 118187bc9f6aSratchov f->wtodo = sizeof(struct amsg); 118287bc9f6aSratchov f->wstate = SOCK_WMSG; 118387bc9f6aSratchov f->fillpending = 0; 118487bc9f6aSratchov return 1; 118587bc9f6aSratchov } 118687bc9f6aSratchov 118787bc9f6aSratchov /* 118887bc9f6aSratchov * if volume changed build a SETVOL message 118987bc9f6aSratchov */ 119087bc9f6aSratchov if (f->pstate >= SOCK_START && f->slot->vol != f->lastvol) { 119187bc9f6aSratchov #ifdef DEBUG 11927b639200Sratchov logx(3, "sock %d: building SETVOL message, vol = %d", f->fd, 11937b639200Sratchov f->slot->vol); 119487bc9f6aSratchov #endif 119587bc9f6aSratchov AMSG_INIT(&f->wmsg); 119687bc9f6aSratchov f->wmsg.cmd = htonl(AMSG_SETVOL); 119787bc9f6aSratchov f->wmsg.u.vol.ctl = htonl(f->slot->vol); 119887bc9f6aSratchov f->wtodo = sizeof(struct amsg); 119987bc9f6aSratchov f->wstate = SOCK_WMSG; 120087bc9f6aSratchov f->lastvol = f->slot->vol; 120187bc9f6aSratchov return 1; 120287bc9f6aSratchov } 120387bc9f6aSratchov 120487bc9f6aSratchov if (f->midi != NULL && f->midi->obuf.used > 0) { 120587bc9f6aSratchov size = f->midi->obuf.used; 120687bc9f6aSratchov if (size > AMSG_DATAMAX) 120787bc9f6aSratchov size = AMSG_DATAMAX; 120887bc9f6aSratchov AMSG_INIT(&f->wmsg); 120987bc9f6aSratchov f->wmsg.cmd = htonl(AMSG_DATA); 121087bc9f6aSratchov f->wmsg.u.data.size = htonl(size); 121187bc9f6aSratchov f->wtodo = sizeof(struct amsg); 121287bc9f6aSratchov f->wstate = SOCK_WMSG; 121387bc9f6aSratchov return 1; 121487bc9f6aSratchov } 121587bc9f6aSratchov 121687bc9f6aSratchov /* 121787bc9f6aSratchov * If data available, build a DATA message. 121887bc9f6aSratchov */ 12191d0df325Sratchov if (f->slot != NULL && f->wmax > 0 && f->slot->sub.buf.used > 0) { 122087bc9f6aSratchov size = f->slot->sub.buf.used; 122187bc9f6aSratchov if (size > AMSG_DATAMAX) 122287bc9f6aSratchov size = AMSG_DATAMAX; 122387bc9f6aSratchov if (size > f->walign) 122487bc9f6aSratchov size = f->walign; 122587bc9f6aSratchov if (size > f->wmax) 122687bc9f6aSratchov size = f->wmax; 122787bc9f6aSratchov size -= size % f->slot->sub.bpf; 122887bc9f6aSratchov #ifdef DEBUG 122987bc9f6aSratchov if (size == 0) { 12307b639200Sratchov logx(0, "sock %d: sock_buildmsg size == 0", f->fd); 123187bc9f6aSratchov panic(); 123287bc9f6aSratchov } 123387bc9f6aSratchov #endif 123487bc9f6aSratchov f->walign -= size; 123587bc9f6aSratchov f->wmax -= size; 123687bc9f6aSratchov if (f->walign == 0) 123787bc9f6aSratchov f->walign = f->slot->round * f->slot->sub.bpf; 123887bc9f6aSratchov #ifdef DEBUG 12397b639200Sratchov logx(4, "sock %d: building audio DATA message, size = %d", f->fd, size); 124087bc9f6aSratchov #endif 124187bc9f6aSratchov AMSG_INIT(&f->wmsg); 124287bc9f6aSratchov f->wmsg.cmd = htonl(AMSG_DATA); 124387bc9f6aSratchov f->wmsg.u.data.size = htonl(size); 124487bc9f6aSratchov f->wtodo = sizeof(struct amsg); 124587bc9f6aSratchov f->wstate = SOCK_WMSG; 124687bc9f6aSratchov return 1; 124787bc9f6aSratchov } 124887bc9f6aSratchov 124987bc9f6aSratchov if (f->stoppending) { 125087bc9f6aSratchov #ifdef DEBUG 12517b639200Sratchov logx(3, "sock %d: building STOP message", f->fd); 125287bc9f6aSratchov #endif 125387bc9f6aSratchov f->stoppending = 0; 125487bc9f6aSratchov f->pstate = SOCK_INIT; 125587bc9f6aSratchov AMSG_INIT(&f->wmsg); 125687bc9f6aSratchov f->wmsg.cmd = htonl(AMSG_STOP); 125787bc9f6aSratchov f->wtodo = sizeof(struct amsg); 125887bc9f6aSratchov f->wstate = SOCK_WMSG; 125987bc9f6aSratchov return 1; 126087bc9f6aSratchov } 1261d07fece6Sratchov 1262d07fece6Sratchov /* 1263d07fece6Sratchov * XXX: add a flag indicating if there are changes 1264d07fece6Sratchov * in controls not seen by this client, rather 1265d07fece6Sratchov * than walking through the full list of control 1266d07fece6Sratchov * searching for the {desc,val}_mask bits 1267d07fece6Sratchov */ 1268d07fece6Sratchov if (f->ctlslot && (f->ctlops & SOCK_CTLDESC)) { 1269c7054416Sratchov mask = f->ctlslot->self; 1270d07fece6Sratchov size = 0; 127199580020Sratchov pc = &ctl_list; 1272d07fece6Sratchov while ((c = *pc) != NULL) { 1273d07fece6Sratchov if ((c->desc_mask & mask) == 0 || 1274d07fece6Sratchov (c->refs_mask & mask) == 0) { 1275d07fece6Sratchov pc = &c->next; 1276d07fece6Sratchov continue; 1277d07fece6Sratchov } 12784d8b188fSratchov if (size + f->ctl_desc_size > SOCK_CTLDESC_SIZE) 1279d07fece6Sratchov break; 12803e5ee6d4Sratchov desc = (struct amsg_ctl_desc *)(f->ctldesc + size); 1281d07fece6Sratchov c->desc_mask &= ~mask; 1282d07fece6Sratchov c->val_mask &= ~mask; 128399580020Sratchov type = ctlslot_visible(f->ctlslot, c) ? 128499580020Sratchov c->type : CTL_NONE; 128536355b88Sratchov strlcpy(desc->group, ctlgroup(f, c), AMSG_CTL_NAMEMAX); 1286d07fece6Sratchov strlcpy(desc->node0.name, c->node0.name, 1287d07fece6Sratchov AMSG_CTL_NAMEMAX); 1288d07fece6Sratchov desc->node0.unit = ntohs(c->node0.unit); 1289d07fece6Sratchov strlcpy(desc->node1.name, c->node1.name, 1290d07fece6Sratchov AMSG_CTL_NAMEMAX); 1291d07fece6Sratchov desc->node1.unit = ntohs(c->node1.unit); 129299580020Sratchov desc->type = type; 1293d07fece6Sratchov strlcpy(desc->func, c->func, AMSG_CTL_NAMEMAX); 1294d07fece6Sratchov desc->addr = htons(c->addr); 1295d07fece6Sratchov desc->maxval = htons(c->maxval); 1296d07fece6Sratchov desc->curval = htons(c->curval); 12974d8b188fSratchov 12984d8b188fSratchov /* old clients don't have the 'display' member */ 12994d8b188fSratchov if (f->ctl_desc_size >= offsetof(struct amsg_ctl_desc, 13004d8b188fSratchov display) + AMSG_CTL_DISPLAYMAX) { 13014d8b188fSratchov strlcpy(desc->display, c->display, AMSG_CTL_DISPLAYMAX); 13024d8b188fSratchov } 13034d8b188fSratchov 13044d8b188fSratchov size += f->ctl_desc_size; 1305d07fece6Sratchov 1306d07fece6Sratchov /* if this is a deleted entry unref it */ 130799580020Sratchov if (type == CTL_NONE) { 1308d07fece6Sratchov c->refs_mask &= ~mask; 1309d07fece6Sratchov if (c->refs_mask == 0) { 1310d07fece6Sratchov *pc = c->next; 1311d07fece6Sratchov xfree(c); 1312d07fece6Sratchov continue; 1313d07fece6Sratchov } 1314d07fece6Sratchov } 1315d07fece6Sratchov 1316d07fece6Sratchov pc = &c->next; 1317d07fece6Sratchov } 1318d07fece6Sratchov if (size > 0) { 1319d07fece6Sratchov AMSG_INIT(&f->wmsg); 1320d07fece6Sratchov f->wmsg.cmd = htonl(AMSG_DATA); 1321d07fece6Sratchov f->wmsg.u.data.size = htonl(size); 1322d07fece6Sratchov f->wtodo = sizeof(struct amsg); 1323d07fece6Sratchov f->wstate = SOCK_WMSG; 1324d07fece6Sratchov #ifdef DEBUG 13257b639200Sratchov logx(3, "sock %d: building control DATA message", f->fd); 1326d07fece6Sratchov #endif 1327d07fece6Sratchov return 1; 1328d07fece6Sratchov } 1329d07fece6Sratchov } 1330d07fece6Sratchov if (f->ctlslot && (f->ctlops & SOCK_CTLVAL)) { 1331c7054416Sratchov mask = f->ctlslot->self; 133299580020Sratchov for (c = ctl_list; c != NULL; c = c->next) { 133399580020Sratchov if (!ctlslot_visible(f->ctlslot, c)) 133499580020Sratchov continue; 1335d07fece6Sratchov if ((c->val_mask & mask) == 0) 1336d07fece6Sratchov continue; 1337d07fece6Sratchov c->val_mask &= ~mask; 1338d07fece6Sratchov AMSG_INIT(&f->wmsg); 1339d07fece6Sratchov f->wmsg.cmd = htonl(AMSG_CTLSET); 1340d07fece6Sratchov f->wmsg.u.ctlset.addr = htons(c->addr); 1341d07fece6Sratchov f->wmsg.u.ctlset.val = htons(c->curval); 1342d07fece6Sratchov f->wtodo = sizeof(struct amsg); 1343d07fece6Sratchov f->wstate = SOCK_WMSG; 1344d07fece6Sratchov #ifdef DEBUG 13457b639200Sratchov logx(3, "sock %d: building CTLSET message", f->fd); 1346d07fece6Sratchov #endif 1347d07fece6Sratchov return 1; 1348d07fece6Sratchov } 1349d07fece6Sratchov } 1350d07fece6Sratchov if (f->ctlslot && f->ctlsyncpending) { 1351d07fece6Sratchov f->ctlsyncpending = 0; 1352d07fece6Sratchov f->wmsg.cmd = htonl(AMSG_CTLSYNC); 1353d07fece6Sratchov f->wtodo = sizeof(struct amsg); 1354d07fece6Sratchov f->wstate = SOCK_WMSG; 1355d07fece6Sratchov #ifdef DEBUG 13567b639200Sratchov logx(3, "sock %d: building CTLSYNC message", f->fd); 1357d07fece6Sratchov #endif 1358d07fece6Sratchov return 1; 1359d07fece6Sratchov } 136087bc9f6aSratchov #ifdef DEBUG 13617b639200Sratchov logx(4, "sock %d: no messages to build anymore, idling...", f->fd); 136287bc9f6aSratchov #endif 136387bc9f6aSratchov f->wstate = SOCK_WIDLE; 136487bc9f6aSratchov return 0; 136587bc9f6aSratchov } 136687bc9f6aSratchov 136787bc9f6aSratchov /* 136887bc9f6aSratchov * iteration of the socket reader loop, return 1 on success 136987bc9f6aSratchov */ 137087bc9f6aSratchov int 137187bc9f6aSratchov sock_read(struct sock *f) 137287bc9f6aSratchov { 137387bc9f6aSratchov #ifdef DEBUG 13747b639200Sratchov logx(4, "sock %d: reading %u todo", f->fd, f->rtodo); 137587bc9f6aSratchov #endif 137687bc9f6aSratchov switch (f->rstate) { 137787bc9f6aSratchov case SOCK_RIDLE: 137887bc9f6aSratchov return 0; 137987bc9f6aSratchov case SOCK_RMSG: 138087bc9f6aSratchov if (!sock_rmsg(f)) 138187bc9f6aSratchov return 0; 138287bc9f6aSratchov if (!sock_execmsg(f)) 138387bc9f6aSratchov return 0; 138487bc9f6aSratchov break; 138587bc9f6aSratchov case SOCK_RDATA: 138687bc9f6aSratchov if (!sock_rdata(f)) 138787bc9f6aSratchov return 0; 138887bc9f6aSratchov f->rstate = SOCK_RMSG; 138987bc9f6aSratchov f->rtodo = sizeof(struct amsg); 139087bc9f6aSratchov break; 139187bc9f6aSratchov case SOCK_RRET: 139287bc9f6aSratchov if (f->wstate != SOCK_WIDLE) { 139387bc9f6aSratchov #ifdef DEBUG 13947b639200Sratchov logx(4, "sock %d: can't reply, write-end blocked", f->fd); 139587bc9f6aSratchov #endif 139687bc9f6aSratchov return 0; 139787bc9f6aSratchov } 139887bc9f6aSratchov f->wmsg = f->rmsg; 139987bc9f6aSratchov f->wstate = SOCK_WMSG; 140087bc9f6aSratchov f->wtodo = sizeof(struct amsg); 140187bc9f6aSratchov f->rstate = SOCK_RMSG; 140287bc9f6aSratchov f->rtodo = sizeof(struct amsg); 140387bc9f6aSratchov #ifdef DEBUG 14047b639200Sratchov logx(4, "sock %d: copied RRET message", f->fd); 140587bc9f6aSratchov #endif 140687bc9f6aSratchov } 140787bc9f6aSratchov return 1; 140887bc9f6aSratchov } 140987bc9f6aSratchov 141087bc9f6aSratchov /* 141187bc9f6aSratchov * iteration of the socket writer loop, return 1 on success 141287bc9f6aSratchov */ 141387bc9f6aSratchov int 141487bc9f6aSratchov sock_write(struct sock *f) 141587bc9f6aSratchov { 141687bc9f6aSratchov #ifdef DEBUG 14177b639200Sratchov logx(4, "sock %d: writing", f->fd); 141887bc9f6aSratchov #endif 141987bc9f6aSratchov switch (f->wstate) { 142087bc9f6aSratchov case SOCK_WMSG: 142187bc9f6aSratchov if (!sock_wmsg(f)) 142287bc9f6aSratchov return 0; 1423f2c5b8a8Sratchov /* 1424f2c5b8a8Sratchov * f->wmsg is either build by sock_buildmsg() or 1425f2c5b8a8Sratchov * copied from f->rmsg (in the SOCK_RRET state), so 1426f2c5b8a8Sratchov * it's safe. 1427f2c5b8a8Sratchov */ 142887bc9f6aSratchov if (ntohl(f->wmsg.cmd) != AMSG_DATA) { 142987bc9f6aSratchov f->wstate = SOCK_WIDLE; 143087bc9f6aSratchov f->wtodo = 0xdeadbeef; 143187bc9f6aSratchov break; 143287bc9f6aSratchov } 143387bc9f6aSratchov f->wstate = SOCK_WDATA; 143487bc9f6aSratchov f->wsize = f->wtodo = ntohl(f->wmsg.u.data.size); 1435591be731Sjsg /* FALLTHROUGH */ 143687bc9f6aSratchov case SOCK_WDATA: 143787bc9f6aSratchov if (!sock_wdata(f)) 143887bc9f6aSratchov return 0; 143987bc9f6aSratchov if (f->wtodo > 0) 144087bc9f6aSratchov break; 144187bc9f6aSratchov f->wstate = SOCK_WIDLE; 144287bc9f6aSratchov f->wtodo = 0xdeadbeef; 144387bc9f6aSratchov if (f->pstate == SOCK_STOP) { 144487bc9f6aSratchov f->pstate = SOCK_INIT; 144587bc9f6aSratchov f->wmax = 0; 144687bc9f6aSratchov #ifdef DEBUG 14477b639200Sratchov logx(4, "sock %d: drained, moved to INIT state", f->fd); 144887bc9f6aSratchov #endif 144987bc9f6aSratchov } 1450591be731Sjsg /* FALLTHROUGH */ 145187bc9f6aSratchov case SOCK_WIDLE: 145287bc9f6aSratchov if (f->rstate == SOCK_RRET) { 145387bc9f6aSratchov f->wmsg = f->rmsg; 145487bc9f6aSratchov f->wstate = SOCK_WMSG; 145587bc9f6aSratchov f->wtodo = sizeof(struct amsg); 145687bc9f6aSratchov f->rstate = SOCK_RMSG; 145787bc9f6aSratchov f->rtodo = sizeof(struct amsg); 145887bc9f6aSratchov #ifdef DEBUG 14597b639200Sratchov logx(4, "sock %d: copied RRET message", f->fd); 146087bc9f6aSratchov #endif 146187bc9f6aSratchov } else { 146287bc9f6aSratchov if (!sock_buildmsg(f)) 146387bc9f6aSratchov return 0; 146487bc9f6aSratchov } 146587bc9f6aSratchov break; 146687bc9f6aSratchov #ifdef DEBUG 146787bc9f6aSratchov default: 14687b639200Sratchov logx(0, "sock %d: bad writing end state", f->fd); 146987bc9f6aSratchov panic(); 147087bc9f6aSratchov #endif 147187bc9f6aSratchov } 147287bc9f6aSratchov return 1; 147387bc9f6aSratchov } 147487bc9f6aSratchov 147587bc9f6aSratchov int 147687bc9f6aSratchov sock_pollfd(void *arg, struct pollfd *pfd) 147787bc9f6aSratchov { 147887bc9f6aSratchov struct sock *f = arg; 147987bc9f6aSratchov int events = 0; 148087bc9f6aSratchov 148187bc9f6aSratchov /* 148287bc9f6aSratchov * feedback counters, clock ticks and alike may have changed, 148387bc9f6aSratchov * prepare a message to trigger writes 148487bc9f6aSratchov * 148587bc9f6aSratchov * XXX: doing this at the beginning of the cycle is not optimal, 148687bc9f6aSratchov * because state is changed at the end of the read cycle, and 148787bc9f6aSratchov * thus counters, ret message and alike are generated then. 148887bc9f6aSratchov */ 148987bc9f6aSratchov if (f->wstate == SOCK_WIDLE && f->rstate != SOCK_RRET) 149087bc9f6aSratchov sock_buildmsg(f); 149187bc9f6aSratchov 149287bc9f6aSratchov if (f->rstate == SOCK_RMSG || 149387bc9f6aSratchov f->rstate == SOCK_RDATA) 149487bc9f6aSratchov events |= POLLIN; 149587bc9f6aSratchov if (f->rstate == SOCK_RRET || 149687bc9f6aSratchov f->wstate == SOCK_WMSG || 149787bc9f6aSratchov f->wstate == SOCK_WDATA) 149887bc9f6aSratchov events |= POLLOUT; 149987bc9f6aSratchov pfd->fd = f->fd; 150087bc9f6aSratchov pfd->events = events; 150187bc9f6aSratchov return 1; 150287bc9f6aSratchov } 150387bc9f6aSratchov 150487bc9f6aSratchov int 150587bc9f6aSratchov sock_revents(void *arg, struct pollfd *pfd) 150687bc9f6aSratchov { 150787bc9f6aSratchov return pfd->revents; 150887bc9f6aSratchov } 150987bc9f6aSratchov 151087bc9f6aSratchov void 151187bc9f6aSratchov sock_in(void *arg) 151287bc9f6aSratchov { 151387bc9f6aSratchov struct sock *f = arg; 151487bc9f6aSratchov 151587bc9f6aSratchov while (sock_read(f)) 151687bc9f6aSratchov ; 151787bc9f6aSratchov } 151887bc9f6aSratchov 151987bc9f6aSratchov void 152087bc9f6aSratchov sock_out(void *arg) 152187bc9f6aSratchov { 152287bc9f6aSratchov struct sock *f = arg; 152387bc9f6aSratchov 152487bc9f6aSratchov while (sock_write(f)) 152587bc9f6aSratchov ; 152687bc9f6aSratchov } 152787bc9f6aSratchov 152887bc9f6aSratchov void 152987bc9f6aSratchov sock_hup(void *arg) 153087bc9f6aSratchov { 153187bc9f6aSratchov struct sock *f = arg; 153287bc9f6aSratchov 153387bc9f6aSratchov sock_close(f); 153487bc9f6aSratchov } 1535