1d5ac70f0Sopenharmony_ci/* 2d5ac70f0Sopenharmony_ci * RawMIDI - Hardware 3d5ac70f0Sopenharmony_ci * Copyright (c) 2000 by Jaroslav Kysela <perex@perex.cz> 4d5ac70f0Sopenharmony_ci * Abramo Bagnara <abramo@alsa-project.org> 5d5ac70f0Sopenharmony_ci * 6d5ac70f0Sopenharmony_ci * 7d5ac70f0Sopenharmony_ci * This library is free software; you can redistribute it and/or modify 8d5ac70f0Sopenharmony_ci * it under the terms of the GNU Lesser General Public License as 9d5ac70f0Sopenharmony_ci * published by the Free Software Foundation; either version 2.1 of 10d5ac70f0Sopenharmony_ci * the License, or (at your option) any later version. 11d5ac70f0Sopenharmony_ci * 12d5ac70f0Sopenharmony_ci * This program is distributed in the hope that it will be useful, 13d5ac70f0Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 14d5ac70f0Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15d5ac70f0Sopenharmony_ci * GNU Lesser General Public License for more details. 16d5ac70f0Sopenharmony_ci * 17d5ac70f0Sopenharmony_ci * You should have received a copy of the GNU Lesser General Public 18d5ac70f0Sopenharmony_ci * License along with this library; if not, write to the Free Software 19d5ac70f0Sopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20d5ac70f0Sopenharmony_ci * 21d5ac70f0Sopenharmony_ci */ 22d5ac70f0Sopenharmony_ci 23d5ac70f0Sopenharmony_ci#include "config.h" 24d5ac70f0Sopenharmony_ci#include <stdio.h> 25d5ac70f0Sopenharmony_ci#include <stdlib.h> 26d5ac70f0Sopenharmony_ci#include <unistd.h> 27d5ac70f0Sopenharmony_ci#include <string.h> 28d5ac70f0Sopenharmony_ci#include <fcntl.h> 29d5ac70f0Sopenharmony_ci#include <sys/ioctl.h> 30d5ac70f0Sopenharmony_ci#include "../control/control_local.h" 31d5ac70f0Sopenharmony_ci#include "rawmidi_local.h" 32d5ac70f0Sopenharmony_ci 33d5ac70f0Sopenharmony_ci#ifndef PIC 34d5ac70f0Sopenharmony_ci/* entry for static linking */ 35d5ac70f0Sopenharmony_ciconst char *_snd_module_rawmidi_hw = ""; 36d5ac70f0Sopenharmony_ci#endif 37d5ac70f0Sopenharmony_ci 38d5ac70f0Sopenharmony_ci#define SNDRV_FILE_RAWMIDI ALSA_DEVICE_DIRECTORY "midiC%iD%i" 39d5ac70f0Sopenharmony_ci#define SNDRV_FILE_UMP_RAWMIDI ALSA_DEVICE_DIRECTORY "umpC%iD%i" 40d5ac70f0Sopenharmony_ci#define SNDRV_RAWMIDI_VERSION_MAX SNDRV_PROTOCOL_VERSION(2, 0, 0) 41d5ac70f0Sopenharmony_ci 42d5ac70f0Sopenharmony_ci#ifndef DOC_HIDDEN 43d5ac70f0Sopenharmony_citypedef struct { 44d5ac70f0Sopenharmony_ci int open; 45d5ac70f0Sopenharmony_ci int fd; 46d5ac70f0Sopenharmony_ci int card, device, subdevice; 47d5ac70f0Sopenharmony_ci unsigned char *buf; 48d5ac70f0Sopenharmony_ci size_t buf_size; /* total buffer size in bytes */ 49d5ac70f0Sopenharmony_ci size_t buf_fill; /* filled buffer size in bytes */ 50d5ac70f0Sopenharmony_ci size_t buf_pos; /* offset to frame in the read buffer (bytes) */ 51d5ac70f0Sopenharmony_ci size_t buf_fpos; /* offset to the frame data array (bytes 0-16) */ 52d5ac70f0Sopenharmony_ci} snd_rawmidi_hw_t; 53d5ac70f0Sopenharmony_ci#endif 54d5ac70f0Sopenharmony_ci 55d5ac70f0Sopenharmony_cistatic void buf_reset(snd_rawmidi_hw_t *hw) 56d5ac70f0Sopenharmony_ci{ 57d5ac70f0Sopenharmony_ci hw->buf_fill = 0; 58d5ac70f0Sopenharmony_ci hw->buf_pos = 0; 59d5ac70f0Sopenharmony_ci hw->buf_fpos = 0; 60d5ac70f0Sopenharmony_ci} 61d5ac70f0Sopenharmony_ci 62d5ac70f0Sopenharmony_cistatic int snd_rawmidi_hw_close(snd_rawmidi_t *rmidi) 63d5ac70f0Sopenharmony_ci{ 64d5ac70f0Sopenharmony_ci snd_rawmidi_hw_t *hw = rmidi->private_data; 65d5ac70f0Sopenharmony_ci int err = 0; 66d5ac70f0Sopenharmony_ci 67d5ac70f0Sopenharmony_ci hw->open--; 68d5ac70f0Sopenharmony_ci if (hw->open) 69d5ac70f0Sopenharmony_ci return 0; 70d5ac70f0Sopenharmony_ci if (close(hw->fd)) { 71d5ac70f0Sopenharmony_ci err = -errno; 72d5ac70f0Sopenharmony_ci SYSMSG("close failed"); 73d5ac70f0Sopenharmony_ci } 74d5ac70f0Sopenharmony_ci free(hw->buf); 75d5ac70f0Sopenharmony_ci free(hw); 76d5ac70f0Sopenharmony_ci return err; 77d5ac70f0Sopenharmony_ci} 78d5ac70f0Sopenharmony_ci 79d5ac70f0Sopenharmony_cistatic int snd_rawmidi_hw_nonblock(snd_rawmidi_t *rmidi, int nonblock) 80d5ac70f0Sopenharmony_ci{ 81d5ac70f0Sopenharmony_ci snd_rawmidi_hw_t *hw = rmidi->private_data; 82d5ac70f0Sopenharmony_ci long flags; 83d5ac70f0Sopenharmony_ci 84d5ac70f0Sopenharmony_ci if ((flags = fcntl(hw->fd, F_GETFL)) < 0) { 85d5ac70f0Sopenharmony_ci SYSMSG("F_GETFL failed"); 86d5ac70f0Sopenharmony_ci return -errno; 87d5ac70f0Sopenharmony_ci } 88d5ac70f0Sopenharmony_ci if (nonblock) 89d5ac70f0Sopenharmony_ci flags |= O_NONBLOCK; 90d5ac70f0Sopenharmony_ci else 91d5ac70f0Sopenharmony_ci flags &= ~O_NONBLOCK; 92d5ac70f0Sopenharmony_ci if (fcntl(hw->fd, F_SETFL, flags) < 0) { 93d5ac70f0Sopenharmony_ci SYSMSG("F_SETFL for O_NONBLOCK failed"); 94d5ac70f0Sopenharmony_ci return -errno; 95d5ac70f0Sopenharmony_ci } 96d5ac70f0Sopenharmony_ci return 0; 97d5ac70f0Sopenharmony_ci} 98d5ac70f0Sopenharmony_ci 99d5ac70f0Sopenharmony_cistatic int snd_rawmidi_hw_info(snd_rawmidi_t *rmidi, snd_rawmidi_info_t * info) 100d5ac70f0Sopenharmony_ci{ 101d5ac70f0Sopenharmony_ci snd_rawmidi_hw_t *hw = rmidi->private_data; 102d5ac70f0Sopenharmony_ci info->stream = rmidi->stream; 103d5ac70f0Sopenharmony_ci if (ioctl(hw->fd, SNDRV_RAWMIDI_IOCTL_INFO, info) < 0) { 104d5ac70f0Sopenharmony_ci SYSMSG("SNDRV_RAWMIDI_IOCTL_INFO failed"); 105d5ac70f0Sopenharmony_ci return -errno; 106d5ac70f0Sopenharmony_ci } 107d5ac70f0Sopenharmony_ci return 0; 108d5ac70f0Sopenharmony_ci} 109d5ac70f0Sopenharmony_ci 110d5ac70f0Sopenharmony_cistatic int snd_rawmidi_hw_params(snd_rawmidi_t *rmidi, snd_rawmidi_params_t * params) 111d5ac70f0Sopenharmony_ci{ 112d5ac70f0Sopenharmony_ci snd_rawmidi_hw_t *hw = rmidi->private_data; 113d5ac70f0Sopenharmony_ci int tstamp; 114d5ac70f0Sopenharmony_ci params->stream = rmidi->stream; 115d5ac70f0Sopenharmony_ci if (ioctl(hw->fd, SNDRV_RAWMIDI_IOCTL_PARAMS, params) < 0) { 116d5ac70f0Sopenharmony_ci SYSMSG("SNDRV_RAWMIDI_IOCTL_PARAMS failed"); 117d5ac70f0Sopenharmony_ci return -errno; 118d5ac70f0Sopenharmony_ci } 119d5ac70f0Sopenharmony_ci buf_reset(hw); 120d5ac70f0Sopenharmony_ci tstamp = (params->mode & SNDRV_RAWMIDI_MODE_FRAMING_MASK) == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP; 121d5ac70f0Sopenharmony_ci if (hw->buf && !tstamp) { 122d5ac70f0Sopenharmony_ci free(hw->buf); 123d5ac70f0Sopenharmony_ci hw->buf = NULL; 124d5ac70f0Sopenharmony_ci hw->buf_size = 0; 125d5ac70f0Sopenharmony_ci } else if (tstamp) { 126d5ac70f0Sopenharmony_ci size_t alloc_size; 127d5ac70f0Sopenharmony_ci void *buf; 128d5ac70f0Sopenharmony_ci 129d5ac70f0Sopenharmony_ci alloc_size = page_size(); 130d5ac70f0Sopenharmony_ci if (params->buffer_size > alloc_size) 131d5ac70f0Sopenharmony_ci alloc_size = params->buffer_size; 132d5ac70f0Sopenharmony_ci if (alloc_size != hw->buf_size) { 133d5ac70f0Sopenharmony_ci buf = realloc(hw->buf, alloc_size); 134d5ac70f0Sopenharmony_ci if (buf == NULL) 135d5ac70f0Sopenharmony_ci return -ENOMEM; 136d5ac70f0Sopenharmony_ci hw->buf = buf; 137d5ac70f0Sopenharmony_ci hw->buf_size = alloc_size; 138d5ac70f0Sopenharmony_ci } 139d5ac70f0Sopenharmony_ci } 140d5ac70f0Sopenharmony_ci return 0; 141d5ac70f0Sopenharmony_ci} 142d5ac70f0Sopenharmony_ci 143d5ac70f0Sopenharmony_cistatic int snd_rawmidi_hw_status(snd_rawmidi_t *rmidi, snd_rawmidi_status_t * status) 144d5ac70f0Sopenharmony_ci{ 145d5ac70f0Sopenharmony_ci snd_rawmidi_hw_t *hw = rmidi->private_data; 146d5ac70f0Sopenharmony_ci status->stream = rmidi->stream; 147d5ac70f0Sopenharmony_ci if (ioctl(hw->fd, SNDRV_RAWMIDI_IOCTL_STATUS, status) < 0) { 148d5ac70f0Sopenharmony_ci SYSMSG("SNDRV_RAWMIDI_IOCTL_STATUS failed"); 149d5ac70f0Sopenharmony_ci return -errno; 150d5ac70f0Sopenharmony_ci } 151d5ac70f0Sopenharmony_ci return 0; 152d5ac70f0Sopenharmony_ci} 153d5ac70f0Sopenharmony_ci 154d5ac70f0Sopenharmony_cistatic int snd_rawmidi_hw_drop(snd_rawmidi_t *rmidi) 155d5ac70f0Sopenharmony_ci{ 156d5ac70f0Sopenharmony_ci snd_rawmidi_hw_t *hw = rmidi->private_data; 157d5ac70f0Sopenharmony_ci int str = rmidi->stream; 158d5ac70f0Sopenharmony_ci if (ioctl(hw->fd, SNDRV_RAWMIDI_IOCTL_DROP, &str) < 0) { 159d5ac70f0Sopenharmony_ci SYSMSG("SNDRV_RAWMIDI_IOCTL_DROP failed"); 160d5ac70f0Sopenharmony_ci return -errno; 161d5ac70f0Sopenharmony_ci } 162d5ac70f0Sopenharmony_ci buf_reset(hw); 163d5ac70f0Sopenharmony_ci return 0; 164d5ac70f0Sopenharmony_ci} 165d5ac70f0Sopenharmony_ci 166d5ac70f0Sopenharmony_cistatic int snd_rawmidi_hw_drain(snd_rawmidi_t *rmidi) 167d5ac70f0Sopenharmony_ci{ 168d5ac70f0Sopenharmony_ci snd_rawmidi_hw_t *hw = rmidi->private_data; 169d5ac70f0Sopenharmony_ci int str = rmidi->stream; 170d5ac70f0Sopenharmony_ci if (ioctl(hw->fd, SNDRV_RAWMIDI_IOCTL_DRAIN, &str) < 0) { 171d5ac70f0Sopenharmony_ci SYSMSG("SNDRV_RAWMIDI_IOCTL_DRAIN failed"); 172d5ac70f0Sopenharmony_ci return -errno; 173d5ac70f0Sopenharmony_ci } 174d5ac70f0Sopenharmony_ci return 0; 175d5ac70f0Sopenharmony_ci} 176d5ac70f0Sopenharmony_ci 177d5ac70f0Sopenharmony_cistatic ssize_t snd_rawmidi_hw_write(snd_rawmidi_t *rmidi, const void *buffer, size_t size) 178d5ac70f0Sopenharmony_ci{ 179d5ac70f0Sopenharmony_ci snd_rawmidi_hw_t *hw = rmidi->private_data; 180d5ac70f0Sopenharmony_ci ssize_t result; 181d5ac70f0Sopenharmony_ci result = write(hw->fd, buffer, size); 182d5ac70f0Sopenharmony_ci if (result < 0) 183d5ac70f0Sopenharmony_ci return -errno; 184d5ac70f0Sopenharmony_ci return result; 185d5ac70f0Sopenharmony_ci} 186d5ac70f0Sopenharmony_ci 187d5ac70f0Sopenharmony_cistatic ssize_t snd_rawmidi_hw_read(snd_rawmidi_t *rmidi, void *buffer, size_t size) 188d5ac70f0Sopenharmony_ci{ 189d5ac70f0Sopenharmony_ci snd_rawmidi_hw_t *hw = rmidi->private_data; 190d5ac70f0Sopenharmony_ci ssize_t result; 191d5ac70f0Sopenharmony_ci result = read(hw->fd, buffer, size); 192d5ac70f0Sopenharmony_ci if (result < 0) 193d5ac70f0Sopenharmony_ci return -errno; 194d5ac70f0Sopenharmony_ci return result; 195d5ac70f0Sopenharmony_ci} 196d5ac70f0Sopenharmony_ci 197d5ac70f0Sopenharmony_cistatic ssize_t read_from_ts_buf(snd_rawmidi_hw_t *hw, struct timespec *tstamp, 198d5ac70f0Sopenharmony_ci void *buffer, size_t size) 199d5ac70f0Sopenharmony_ci{ 200d5ac70f0Sopenharmony_ci struct snd_rawmidi_framing_tstamp *f; 201d5ac70f0Sopenharmony_ci size_t flen; 202d5ac70f0Sopenharmony_ci ssize_t result = 0; 203d5ac70f0Sopenharmony_ci 204d5ac70f0Sopenharmony_ci f = (struct snd_rawmidi_framing_tstamp *)(hw->buf + hw->buf_pos); 205d5ac70f0Sopenharmony_ci while (hw->buf_fill >= sizeof(*f)) { 206d5ac70f0Sopenharmony_ci if (f->frame_type == 0) { 207d5ac70f0Sopenharmony_ci tstamp->tv_sec = f->tv_sec; 208d5ac70f0Sopenharmony_ci tstamp->tv_nsec = f->tv_nsec; 209d5ac70f0Sopenharmony_ci break; 210d5ac70f0Sopenharmony_ci } 211d5ac70f0Sopenharmony_ci hw->buf_pos += sizeof(*f); 212d5ac70f0Sopenharmony_ci hw->buf_fill -= sizeof(*f); 213d5ac70f0Sopenharmony_ci f++; 214d5ac70f0Sopenharmony_ci } 215d5ac70f0Sopenharmony_ci while (size > 0 && hw->buf_fill >= sizeof(*f)) { 216d5ac70f0Sopenharmony_ci /* skip other frames */ 217d5ac70f0Sopenharmony_ci if (f->frame_type != 0) 218d5ac70f0Sopenharmony_ci goto __next; 219d5ac70f0Sopenharmony_ci if (f->length == 0 || f->length > SNDRV_RAWMIDI_FRAMING_DATA_LENGTH) 220d5ac70f0Sopenharmony_ci return -EINVAL; 221d5ac70f0Sopenharmony_ci if (tstamp->tv_sec != (time_t)f->tv_sec || 222d5ac70f0Sopenharmony_ci tstamp->tv_nsec != f->tv_nsec) 223d5ac70f0Sopenharmony_ci break; 224d5ac70f0Sopenharmony_ci flen = f->length - hw->buf_fpos; 225d5ac70f0Sopenharmony_ci if (size < flen) { 226d5ac70f0Sopenharmony_ci /* partial copy */ 227d5ac70f0Sopenharmony_ci memcpy(buffer, f->data + hw->buf_fpos, size); 228d5ac70f0Sopenharmony_ci hw->buf_fpos += size; 229d5ac70f0Sopenharmony_ci result += size; 230d5ac70f0Sopenharmony_ci break; 231d5ac70f0Sopenharmony_ci } 232d5ac70f0Sopenharmony_ci memcpy(buffer, f->data + hw->buf_fpos, flen); 233d5ac70f0Sopenharmony_ci hw->buf_fpos = 0; 234d5ac70f0Sopenharmony_ci buffer += flen; 235d5ac70f0Sopenharmony_ci size -= flen; 236d5ac70f0Sopenharmony_ci result += flen; 237d5ac70f0Sopenharmony_ci __next: 238d5ac70f0Sopenharmony_ci hw->buf_pos += sizeof(*f); 239d5ac70f0Sopenharmony_ci hw->buf_fill -= sizeof(*f); 240d5ac70f0Sopenharmony_ci f++; 241d5ac70f0Sopenharmony_ci } 242d5ac70f0Sopenharmony_ci return result; 243d5ac70f0Sopenharmony_ci} 244d5ac70f0Sopenharmony_ci 245d5ac70f0Sopenharmony_cistatic ssize_t snd_rawmidi_hw_tread(snd_rawmidi_t *rmidi, struct timespec *tstamp, 246d5ac70f0Sopenharmony_ci void *buffer, size_t size) 247d5ac70f0Sopenharmony_ci{ 248d5ac70f0Sopenharmony_ci snd_rawmidi_hw_t *hw = rmidi->private_data; 249d5ac70f0Sopenharmony_ci ssize_t result = 0, ret; 250d5ac70f0Sopenharmony_ci 251d5ac70f0Sopenharmony_ci /* no timestamp */ 252d5ac70f0Sopenharmony_ci tstamp->tv_sec = tstamp->tv_nsec = 0; 253d5ac70f0Sopenharmony_ci 254d5ac70f0Sopenharmony_ci /* copy buffered frames */ 255d5ac70f0Sopenharmony_ci if (hw->buf_fill > 0) { 256d5ac70f0Sopenharmony_ci result = read_from_ts_buf(hw, tstamp, buffer, size); 257d5ac70f0Sopenharmony_ci if (result < 0 || size == (size_t)result || 258d5ac70f0Sopenharmony_ci hw->buf_fill >= sizeof(struct snd_rawmidi_framing_tstamp)) 259d5ac70f0Sopenharmony_ci return result; 260d5ac70f0Sopenharmony_ci buffer += result; 261d5ac70f0Sopenharmony_ci size -= result; 262d5ac70f0Sopenharmony_ci } 263d5ac70f0Sopenharmony_ci 264d5ac70f0Sopenharmony_ci buf_reset(hw); 265d5ac70f0Sopenharmony_ci ret = read(hw->fd, hw->buf, hw->buf_size); 266d5ac70f0Sopenharmony_ci if (ret < 0) 267d5ac70f0Sopenharmony_ci return result > 0 ? result : -errno; 268d5ac70f0Sopenharmony_ci if (ret < (ssize_t)sizeof(struct snd_rawmidi_framing_tstamp)) 269d5ac70f0Sopenharmony_ci return result; 270d5ac70f0Sopenharmony_ci hw->buf_fill = ret; 271d5ac70f0Sopenharmony_ci ret = read_from_ts_buf(hw, tstamp, buffer, size); 272d5ac70f0Sopenharmony_ci if (ret < 0 && result > 0) 273d5ac70f0Sopenharmony_ci return result; 274d5ac70f0Sopenharmony_ci return ret + result; 275d5ac70f0Sopenharmony_ci} 276d5ac70f0Sopenharmony_ci 277d5ac70f0Sopenharmony_cistatic int snd_rawmidi_hw_ump_endpoint_info(snd_rawmidi_t *rmidi, void *buf) 278d5ac70f0Sopenharmony_ci{ 279d5ac70f0Sopenharmony_ci snd_rawmidi_hw_t *hw = rmidi->private_data; 280d5ac70f0Sopenharmony_ci 281d5ac70f0Sopenharmony_ci if (rmidi->version < SNDRV_PROTOCOL_VERSION(2, 0, 3)) 282d5ac70f0Sopenharmony_ci return -ENXIO; 283d5ac70f0Sopenharmony_ci if (ioctl(hw->fd, SNDRV_UMP_IOCTL_ENDPOINT_INFO, buf) < 0) 284d5ac70f0Sopenharmony_ci return -errno; 285d5ac70f0Sopenharmony_ci return 0; 286d5ac70f0Sopenharmony_ci} 287d5ac70f0Sopenharmony_ci 288d5ac70f0Sopenharmony_cistatic int snd_rawmidi_hw_ump_block_info(snd_rawmidi_t *rmidi, void *buf) 289d5ac70f0Sopenharmony_ci{ 290d5ac70f0Sopenharmony_ci snd_rawmidi_hw_t *hw = rmidi->private_data; 291d5ac70f0Sopenharmony_ci 292d5ac70f0Sopenharmony_ci if (rmidi->version < SNDRV_PROTOCOL_VERSION(2, 0, 3)) 293d5ac70f0Sopenharmony_ci return -ENXIO; 294d5ac70f0Sopenharmony_ci if (ioctl(hw->fd, SNDRV_UMP_IOCTL_BLOCK_INFO, buf) < 0) 295d5ac70f0Sopenharmony_ci return -errno; 296d5ac70f0Sopenharmony_ci return 0; 297d5ac70f0Sopenharmony_ci} 298d5ac70f0Sopenharmony_ci 299d5ac70f0Sopenharmony_cistatic const snd_rawmidi_ops_t snd_rawmidi_hw_ops = { 300d5ac70f0Sopenharmony_ci .close = snd_rawmidi_hw_close, 301d5ac70f0Sopenharmony_ci .nonblock = snd_rawmidi_hw_nonblock, 302d5ac70f0Sopenharmony_ci .info = snd_rawmidi_hw_info, 303d5ac70f0Sopenharmony_ci .params = snd_rawmidi_hw_params, 304d5ac70f0Sopenharmony_ci .status = snd_rawmidi_hw_status, 305d5ac70f0Sopenharmony_ci .drop = snd_rawmidi_hw_drop, 306d5ac70f0Sopenharmony_ci .drain = snd_rawmidi_hw_drain, 307d5ac70f0Sopenharmony_ci .write = snd_rawmidi_hw_write, 308d5ac70f0Sopenharmony_ci .read = snd_rawmidi_hw_read, 309d5ac70f0Sopenharmony_ci .tread = snd_rawmidi_hw_tread, 310d5ac70f0Sopenharmony_ci .ump_endpoint_info = snd_rawmidi_hw_ump_endpoint_info, 311d5ac70f0Sopenharmony_ci .ump_block_info = snd_rawmidi_hw_ump_block_info, 312d5ac70f0Sopenharmony_ci}; 313d5ac70f0Sopenharmony_ci 314d5ac70f0Sopenharmony_ci 315d5ac70f0Sopenharmony_ciint snd_rawmidi_hw_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp, 316d5ac70f0Sopenharmony_ci const char *name, int card, int device, int subdevice, 317d5ac70f0Sopenharmony_ci int mode) 318d5ac70f0Sopenharmony_ci{ 319d5ac70f0Sopenharmony_ci int fd, ver, ret; 320d5ac70f0Sopenharmony_ci int attempt = 0; 321d5ac70f0Sopenharmony_ci char filename[sizeof(SNDRV_FILE_RAWMIDI) + 20]; 322d5ac70f0Sopenharmony_ci snd_ctl_t *ctl; 323d5ac70f0Sopenharmony_ci snd_rawmidi_t *rmidi; 324d5ac70f0Sopenharmony_ci snd_rawmidi_hw_t *hw = NULL; 325d5ac70f0Sopenharmony_ci snd_rawmidi_info_t info; 326d5ac70f0Sopenharmony_ci int is_ump; 327d5ac70f0Sopenharmony_ci int fmode; 328d5ac70f0Sopenharmony_ci 329d5ac70f0Sopenharmony_ci is_ump = !!(mode & _SND_RAWMIDI_OPEN_UMP); 330d5ac70f0Sopenharmony_ci mode &= ~_SND_RAWMIDI_OPEN_UMP; 331d5ac70f0Sopenharmony_ci 332d5ac70f0Sopenharmony_ci if (inputp) 333d5ac70f0Sopenharmony_ci *inputp = NULL; 334d5ac70f0Sopenharmony_ci if (outputp) 335d5ac70f0Sopenharmony_ci *outputp = NULL; 336d5ac70f0Sopenharmony_ci if (!inputp && !outputp) 337d5ac70f0Sopenharmony_ci return -EINVAL; 338d5ac70f0Sopenharmony_ci 339d5ac70f0Sopenharmony_ci if ((ret = snd_ctl_hw_open(&ctl, NULL, card, 0)) < 0) 340d5ac70f0Sopenharmony_ci return ret; 341d5ac70f0Sopenharmony_ci if (is_ump) 342d5ac70f0Sopenharmony_ci sprintf(filename, SNDRV_FILE_UMP_RAWMIDI, card, device); 343d5ac70f0Sopenharmony_ci else 344d5ac70f0Sopenharmony_ci sprintf(filename, SNDRV_FILE_RAWMIDI, card, device); 345d5ac70f0Sopenharmony_ci 346d5ac70f0Sopenharmony_ci __again: 347d5ac70f0Sopenharmony_ci if (attempt++ > 3) { 348d5ac70f0Sopenharmony_ci snd_ctl_close(ctl); 349d5ac70f0Sopenharmony_ci return -EBUSY; 350d5ac70f0Sopenharmony_ci } 351d5ac70f0Sopenharmony_ci ret = snd_ctl_rawmidi_prefer_subdevice(ctl, subdevice); 352d5ac70f0Sopenharmony_ci if (ret < 0) { 353d5ac70f0Sopenharmony_ci snd_ctl_close(ctl); 354d5ac70f0Sopenharmony_ci return ret; 355d5ac70f0Sopenharmony_ci } 356d5ac70f0Sopenharmony_ci 357d5ac70f0Sopenharmony_ci if (!inputp) 358d5ac70f0Sopenharmony_ci fmode = O_WRONLY; 359d5ac70f0Sopenharmony_ci else if (!outputp) 360d5ac70f0Sopenharmony_ci fmode = O_RDONLY; 361d5ac70f0Sopenharmony_ci else 362d5ac70f0Sopenharmony_ci fmode = O_RDWR; 363d5ac70f0Sopenharmony_ci 364d5ac70f0Sopenharmony_ci if (mode & SND_RAWMIDI_APPEND) { 365d5ac70f0Sopenharmony_ci assert(outputp); 366d5ac70f0Sopenharmony_ci fmode |= O_APPEND; 367d5ac70f0Sopenharmony_ci } 368d5ac70f0Sopenharmony_ci 369d5ac70f0Sopenharmony_ci if (mode & SND_RAWMIDI_NONBLOCK) { 370d5ac70f0Sopenharmony_ci fmode |= O_NONBLOCK; 371d5ac70f0Sopenharmony_ci } 372d5ac70f0Sopenharmony_ci 373d5ac70f0Sopenharmony_ci if (mode & SND_RAWMIDI_SYNC) { 374d5ac70f0Sopenharmony_ci fmode |= O_SYNC; 375d5ac70f0Sopenharmony_ci } 376d5ac70f0Sopenharmony_ci 377d5ac70f0Sopenharmony_ci assert(!(mode & ~(SND_RAWMIDI_APPEND|SND_RAWMIDI_NONBLOCK|SND_RAWMIDI_SYNC))); 378d5ac70f0Sopenharmony_ci 379d5ac70f0Sopenharmony_ci fd = snd_open_device(filename, fmode); 380d5ac70f0Sopenharmony_ci if (fd < 0) { 381d5ac70f0Sopenharmony_ci snd_card_load(card); 382d5ac70f0Sopenharmony_ci fd = snd_open_device(filename, fmode); 383d5ac70f0Sopenharmony_ci if (fd < 0) { 384d5ac70f0Sopenharmony_ci snd_ctl_close(ctl); 385d5ac70f0Sopenharmony_ci SYSMSG("open %s failed", filename); 386d5ac70f0Sopenharmony_ci return -errno; 387d5ac70f0Sopenharmony_ci } 388d5ac70f0Sopenharmony_ci } 389d5ac70f0Sopenharmony_ci if (ioctl(fd, SNDRV_RAWMIDI_IOCTL_PVERSION, &ver) < 0) { 390d5ac70f0Sopenharmony_ci ret = -errno; 391d5ac70f0Sopenharmony_ci SYSMSG("SNDRV_RAWMIDI_IOCTL_PVERSION failed"); 392d5ac70f0Sopenharmony_ci close(fd); 393d5ac70f0Sopenharmony_ci snd_ctl_close(ctl); 394d5ac70f0Sopenharmony_ci return ret; 395d5ac70f0Sopenharmony_ci } 396d5ac70f0Sopenharmony_ci if (SNDRV_PROTOCOL_INCOMPATIBLE(ver, SNDRV_RAWMIDI_VERSION_MAX)) { 397d5ac70f0Sopenharmony_ci close(fd); 398d5ac70f0Sopenharmony_ci snd_ctl_close(ctl); 399d5ac70f0Sopenharmony_ci return -SND_ERROR_INCOMPATIBLE_VERSION; 400d5ac70f0Sopenharmony_ci } 401d5ac70f0Sopenharmony_ci if (SNDRV_PROTOCOL_VERSION(2, 0, 2) <= ver) { 402d5ac70f0Sopenharmony_ci /* inform the protocol version we're supporting */ 403d5ac70f0Sopenharmony_ci unsigned int user_ver = SNDRV_RAWMIDI_VERSION; 404d5ac70f0Sopenharmony_ci ioctl(fd, SNDRV_RAWMIDI_IOCTL_USER_PVERSION, &user_ver); 405d5ac70f0Sopenharmony_ci } 406d5ac70f0Sopenharmony_ci if (subdevice >= 0) { 407d5ac70f0Sopenharmony_ci memset(&info, 0, sizeof(info)); 408d5ac70f0Sopenharmony_ci info.stream = outputp ? SNDRV_RAWMIDI_STREAM_OUTPUT : SNDRV_RAWMIDI_STREAM_INPUT; 409d5ac70f0Sopenharmony_ci if (ioctl(fd, SNDRV_RAWMIDI_IOCTL_INFO, &info) < 0) { 410d5ac70f0Sopenharmony_ci SYSMSG("SNDRV_RAWMIDI_IOCTL_INFO failed"); 411d5ac70f0Sopenharmony_ci ret = -errno; 412d5ac70f0Sopenharmony_ci close(fd); 413d5ac70f0Sopenharmony_ci snd_ctl_close(ctl); 414d5ac70f0Sopenharmony_ci return ret; 415d5ac70f0Sopenharmony_ci } 416d5ac70f0Sopenharmony_ci if (info.subdevice != (unsigned int) subdevice) { 417d5ac70f0Sopenharmony_ci close(fd); 418d5ac70f0Sopenharmony_ci goto __again; 419d5ac70f0Sopenharmony_ci } 420d5ac70f0Sopenharmony_ci } 421d5ac70f0Sopenharmony_ci snd_ctl_close(ctl); 422d5ac70f0Sopenharmony_ci 423d5ac70f0Sopenharmony_ci hw = calloc(1, sizeof(snd_rawmidi_hw_t)); 424d5ac70f0Sopenharmony_ci if (hw == NULL) 425d5ac70f0Sopenharmony_ci goto _nomem; 426d5ac70f0Sopenharmony_ci hw->card = card; 427d5ac70f0Sopenharmony_ci hw->device = device; 428d5ac70f0Sopenharmony_ci hw->subdevice = subdevice; 429d5ac70f0Sopenharmony_ci hw->fd = fd; 430d5ac70f0Sopenharmony_ci 431d5ac70f0Sopenharmony_ci if (inputp) { 432d5ac70f0Sopenharmony_ci rmidi = calloc(1, sizeof(snd_rawmidi_t)); 433d5ac70f0Sopenharmony_ci if (rmidi == NULL) 434d5ac70f0Sopenharmony_ci goto _nomem; 435d5ac70f0Sopenharmony_ci if (name) 436d5ac70f0Sopenharmony_ci rmidi->name = strdup(name); 437d5ac70f0Sopenharmony_ci rmidi->type = SND_RAWMIDI_TYPE_HW; 438d5ac70f0Sopenharmony_ci rmidi->stream = SND_RAWMIDI_STREAM_INPUT; 439d5ac70f0Sopenharmony_ci rmidi->mode = mode; 440d5ac70f0Sopenharmony_ci rmidi->poll_fd = fd; 441d5ac70f0Sopenharmony_ci rmidi->ops = &snd_rawmidi_hw_ops; 442d5ac70f0Sopenharmony_ci rmidi->private_data = hw; 443d5ac70f0Sopenharmony_ci rmidi->version = ver; 444d5ac70f0Sopenharmony_ci hw->open++; 445d5ac70f0Sopenharmony_ci *inputp = rmidi; 446d5ac70f0Sopenharmony_ci } 447d5ac70f0Sopenharmony_ci if (outputp) { 448d5ac70f0Sopenharmony_ci rmidi = calloc(1, sizeof(snd_rawmidi_t)); 449d5ac70f0Sopenharmony_ci if (rmidi == NULL) 450d5ac70f0Sopenharmony_ci goto _nomem; 451d5ac70f0Sopenharmony_ci if (name) 452d5ac70f0Sopenharmony_ci rmidi->name = strdup(name); 453d5ac70f0Sopenharmony_ci rmidi->type = SND_RAWMIDI_TYPE_HW; 454d5ac70f0Sopenharmony_ci rmidi->stream = SND_RAWMIDI_STREAM_OUTPUT; 455d5ac70f0Sopenharmony_ci rmidi->mode = mode; 456d5ac70f0Sopenharmony_ci rmidi->poll_fd = fd; 457d5ac70f0Sopenharmony_ci rmidi->ops = &snd_rawmidi_hw_ops; 458d5ac70f0Sopenharmony_ci rmidi->private_data = hw; 459d5ac70f0Sopenharmony_ci rmidi->version = ver; 460d5ac70f0Sopenharmony_ci hw->open++; 461d5ac70f0Sopenharmony_ci *outputp = rmidi; 462d5ac70f0Sopenharmony_ci } 463d5ac70f0Sopenharmony_ci return 0; 464d5ac70f0Sopenharmony_ci 465d5ac70f0Sopenharmony_ci _nomem: 466d5ac70f0Sopenharmony_ci close(fd); 467d5ac70f0Sopenharmony_ci free(hw); 468d5ac70f0Sopenharmony_ci if (inputp) 469d5ac70f0Sopenharmony_ci free(*inputp); 470d5ac70f0Sopenharmony_ci if (outputp) 471d5ac70f0Sopenharmony_ci free(*outputp); 472d5ac70f0Sopenharmony_ci return -ENOMEM; 473d5ac70f0Sopenharmony_ci} 474d5ac70f0Sopenharmony_ci 475d5ac70f0Sopenharmony_ciint _snd_rawmidi_hw_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp, 476d5ac70f0Sopenharmony_ci char *name, snd_config_t *root ATTRIBUTE_UNUSED, 477d5ac70f0Sopenharmony_ci snd_config_t *conf, int mode) 478d5ac70f0Sopenharmony_ci{ 479d5ac70f0Sopenharmony_ci snd_config_iterator_t i, next; 480d5ac70f0Sopenharmony_ci long card = -1, device = 0, subdevice = -1; 481d5ac70f0Sopenharmony_ci int err; 482d5ac70f0Sopenharmony_ci snd_config_for_each(i, next, conf) { 483d5ac70f0Sopenharmony_ci snd_config_t *n = snd_config_iterator_entry(i); 484d5ac70f0Sopenharmony_ci const char *id; 485d5ac70f0Sopenharmony_ci if (snd_config_get_id(n, &id) < 0) 486d5ac70f0Sopenharmony_ci continue; 487d5ac70f0Sopenharmony_ci if (snd_rawmidi_conf_generic_id(id)) 488d5ac70f0Sopenharmony_ci continue; 489d5ac70f0Sopenharmony_ci if (strcmp(id, "card") == 0) { 490d5ac70f0Sopenharmony_ci err = snd_config_get_card(n); 491d5ac70f0Sopenharmony_ci if (err < 0) 492d5ac70f0Sopenharmony_ci return err; 493d5ac70f0Sopenharmony_ci card = err; 494d5ac70f0Sopenharmony_ci continue; 495d5ac70f0Sopenharmony_ci } 496d5ac70f0Sopenharmony_ci if (strcmp(id, "device") == 0) { 497d5ac70f0Sopenharmony_ci err = snd_config_get_integer(n, &device); 498d5ac70f0Sopenharmony_ci if (err < 0) 499d5ac70f0Sopenharmony_ci return err; 500d5ac70f0Sopenharmony_ci continue; 501d5ac70f0Sopenharmony_ci } 502d5ac70f0Sopenharmony_ci if (strcmp(id, "subdevice") == 0) { 503d5ac70f0Sopenharmony_ci err = snd_config_get_integer(n, &subdevice); 504d5ac70f0Sopenharmony_ci if (err < 0) 505d5ac70f0Sopenharmony_ci return err; 506d5ac70f0Sopenharmony_ci continue; 507d5ac70f0Sopenharmony_ci } 508d5ac70f0Sopenharmony_ci return -EINVAL; 509d5ac70f0Sopenharmony_ci } 510d5ac70f0Sopenharmony_ci if (card < 0) 511d5ac70f0Sopenharmony_ci return -EINVAL; 512d5ac70f0Sopenharmony_ci return snd_rawmidi_hw_open(inputp, outputp, name, card, device, subdevice, mode); 513d5ac70f0Sopenharmony_ci} 514d5ac70f0Sopenharmony_ciSND_DLSYM_BUILD_VERSION(_snd_rawmidi_hw_open, SND_RAWMIDI_DLSYM_VERSION); 515