1/* 2 * Sequencer Interface - main file 3 * Copyright (c) 2000 by Jaroslav Kysela <perex@perex.cz> 4 * Abramo Bagnara <abramo@alsa-project.org> 5 * 6 * 7 * This library is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU Lesser General Public License as 9 * published by the Free Software Foundation; either version 2.1 of 10 * the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 * 21 */ 22 23#include <fcntl.h> 24#include <sys/ioctl.h> 25#include "seq_local.h" 26 27#ifndef PIC 28/* entry for static linking */ 29const char *_snd_module_seq_hw = ""; 30#endif 31 32#ifndef DOC_HIDDEN 33#define SNDRV_FILE_SEQ ALSA_DEVICE_DIRECTORY "seq" 34#define SNDRV_FILE_ALOADSEQ ALOAD_DEVICE_DIRECTORY "aloadSEQ" 35#define SNDRV_SEQ_VERSION_MAX SNDRV_PROTOCOL_VERSION(1, 0, 1) 36 37typedef struct { 38 int fd; 39} snd_seq_hw_t; 40#endif /* DOC_HIDDEN */ 41 42static int snd_seq_hw_close(snd_seq_t *seq) 43{ 44 snd_seq_hw_t *hw = seq->private_data; 45 int err = 0; 46 47 if (close(hw->fd)) { 48 err = -errno; 49 SYSERR("close failed\n"); 50 } 51 free(hw); 52 return err; 53} 54 55static int snd_seq_hw_nonblock(snd_seq_t *seq, int nonblock) 56{ 57 snd_seq_hw_t *hw = seq->private_data; 58 long flags; 59 60 if ((flags = fcntl(hw->fd, F_GETFL)) < 0) { 61 SYSERR("F_GETFL failed"); 62 return -errno; 63 } 64 if (nonblock) 65 flags |= O_NONBLOCK; 66 else 67 flags &= ~O_NONBLOCK; 68 if (fcntl(hw->fd, F_SETFL, flags) < 0) { 69 SYSERR("F_SETFL for O_NONBLOCK failed"); 70 return -errno; 71 } 72 return 0; 73} 74 75static int snd_seq_hw_client_id(snd_seq_t *seq) 76{ 77 snd_seq_hw_t *hw = seq->private_data; 78 int client; 79 if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_CLIENT_ID, &client) < 0) { 80 SYSERR("SNDRV_SEQ_IOCTL_CLIENT_ID failed"); 81 return -errno; 82 } 83 return client; 84} 85 86static int snd_seq_hw_system_info(snd_seq_t *seq, snd_seq_system_info_t * info) 87{ 88 snd_seq_hw_t *hw = seq->private_data; 89 if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SYSTEM_INFO, info) < 0) { 90 SYSERR("SNDRV_SEQ_IOCTL_SYSTEM_INFO failed"); 91 return -errno; 92 } 93 return 0; 94} 95 96static int snd_seq_hw_get_client_info(snd_seq_t *seq, snd_seq_client_info_t * info) 97{ 98 snd_seq_hw_t *hw = seq->private_data; 99 if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_GET_CLIENT_INFO, info) < 0) { 100 /*SYSERR("SNDRV_SEQ_IOCTL_GET_CLIENT_INFO failed");*/ 101 return -errno; 102 } 103 return 0; 104} 105 106static int snd_seq_hw_set_client_info(snd_seq_t *seq, snd_seq_client_info_t * info) 107{ 108 snd_seq_hw_t *hw = seq->private_data; 109 if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, info) < 0) { 110 /*SYSERR("SNDRV_SEQ_IOCTL_SET_CLIENT_INFO failed");*/ 111 return -errno; 112 } 113 return 0; 114} 115 116static int snd_seq_hw_create_port(snd_seq_t *seq, snd_seq_port_info_t * port) 117{ 118 snd_seq_hw_t *hw = seq->private_data; 119 if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_CREATE_PORT, port) < 0) { 120 /*SYSERR("SNDRV_SEQ_IOCTL_CREATE_PORT failed");*/ 121 return -errno; 122 } 123 return 0; 124} 125 126static int snd_seq_hw_delete_port(snd_seq_t *seq, snd_seq_port_info_t * port) 127{ 128 snd_seq_hw_t *hw = seq->private_data; 129 if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_DELETE_PORT, port) < 0) { 130 /*SYSERR("SNDRV_SEQ_IOCTL_DELETE_PORT failed");*/ 131 return -errno; 132 } 133 return 0; 134} 135 136static int snd_seq_hw_get_port_info(snd_seq_t *seq, snd_seq_port_info_t * info) 137{ 138 snd_seq_hw_t *hw = seq->private_data; 139 if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_GET_PORT_INFO, info) < 0) { 140 /*SYSERR("SNDRV_SEQ_IOCTL_GET_PORT_INFO failed");*/ 141 return -errno; 142 } 143 return 0; 144} 145 146static int snd_seq_hw_set_port_info(snd_seq_t *seq, snd_seq_port_info_t * info) 147{ 148 snd_seq_hw_t *hw = seq->private_data; 149 if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SET_PORT_INFO, info) < 0) { 150 /*SYSERR("SNDRV_SEQ_IOCTL_SET_PORT_INFO failed");*/ 151 return -errno; 152 } 153 return 0; 154} 155 156static int snd_seq_hw_get_port_subscription(snd_seq_t *seq, snd_seq_port_subscribe_t * sub) 157{ 158 snd_seq_hw_t *hw = seq->private_data; 159 if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION, sub) < 0) { 160 /*SYSERR("SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION failed");*/ 161 return -errno; 162 } 163 return 0; 164} 165 166static int snd_seq_hw_subscribe_port(snd_seq_t *seq, snd_seq_port_subscribe_t * sub) 167{ 168 snd_seq_hw_t *hw = seq->private_data; 169 if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, sub) < 0) { 170 /*SYSERR("SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT failed");*/ 171 return -errno; 172 } 173 return 0; 174} 175 176static int snd_seq_hw_unsubscribe_port(snd_seq_t *seq, snd_seq_port_subscribe_t * sub) 177{ 178 snd_seq_hw_t *hw = seq->private_data; 179 if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, sub) < 0) { 180 /*SYSERR("SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT failed");*/ 181 return -errno; 182 } 183 return 0; 184} 185 186static int snd_seq_hw_query_port_subscribers(snd_seq_t *seq, snd_seq_query_subscribe_t * subs) 187{ 188 snd_seq_hw_t *hw = seq->private_data; 189 if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_QUERY_SUBS, subs) < 0) { 190 /*SYSERR("SNDRV_SEQ_IOCTL_QUERY_SUBS failed");*/ 191 return -errno; 192 } 193 return 0; 194} 195 196static int snd_seq_hw_get_queue_status(snd_seq_t *seq, snd_seq_queue_status_t * status) 197{ 198 snd_seq_hw_t *hw = seq->private_data; 199 if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS, status) < 0) { 200 /*SYSERR("SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS failed");*/ 201 return -errno; 202 } 203 return 0; 204} 205 206static int snd_seq_hw_get_queue_tempo(snd_seq_t *seq, snd_seq_queue_tempo_t * tempo) 207{ 208 snd_seq_hw_t *hw = seq->private_data; 209 if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO, tempo) < 0) { 210 /*SYSERR("SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO failed");*/ 211 return -errno; 212 } 213 return 0; 214} 215 216static int snd_seq_hw_set_queue_tempo(snd_seq_t *seq, snd_seq_queue_tempo_t * tempo) 217{ 218 snd_seq_hw_t *hw = seq->private_data; 219 if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO, tempo) < 0) { 220 /*SYSERR("SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO failed");*/ 221 return -errno; 222 } 223 return 0; 224} 225 226static int snd_seq_hw_get_queue_timer(snd_seq_t *seq, snd_seq_queue_timer_t * timer) 227{ 228 snd_seq_hw_t *hw = seq->private_data; 229 if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER, timer) < 0) { 230 /*SYSERR("SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER failed");*/ 231 return -errno; 232 } 233 return 0; 234} 235 236static int snd_seq_hw_set_queue_timer(snd_seq_t *seq, snd_seq_queue_timer_t * timer) 237{ 238 snd_seq_hw_t *hw = seq->private_data; 239 if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER, timer) < 0) { 240 /*SYSERR("SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER failed");*/ 241 return -errno; 242 } 243 return 0; 244} 245 246static int snd_seq_hw_get_queue_client(snd_seq_t *seq, snd_seq_queue_client_t * info) 247{ 248 snd_seq_hw_t *hw = seq->private_data; 249 if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT, info) < 0) { 250 /*SYSERR("SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT failed");*/ 251 return -errno; 252 } 253 return 0; 254} 255 256static int snd_seq_hw_set_queue_client(snd_seq_t *seq, snd_seq_queue_client_t * info) 257{ 258 snd_seq_hw_t *hw = seq->private_data; 259 if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT, info) < 0) { 260 /*SYSERR("SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT failed");*/ 261 return -errno; 262 } 263 return 0; 264} 265 266static int snd_seq_hw_create_queue(snd_seq_t *seq, snd_seq_queue_info_t *info) 267{ 268 snd_seq_hw_t *hw = seq->private_data; 269 if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_CREATE_QUEUE, info) < 0) { 270 /*SYSERR("SNDRV_SEQ_IOCTL_CREATE_QUEUE failed");*/ 271 return -errno; 272 } 273 return 0; 274} 275 276static int snd_seq_hw_delete_queue(snd_seq_t *seq, snd_seq_queue_info_t *info) 277{ 278 snd_seq_hw_t *hw = seq->private_data; 279 if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_DELETE_QUEUE, info) < 0) { 280 /*SYSERR("SNDRV_SEQ_IOCTL_DELETE_QUEUE failed");*/ 281 return -errno; 282 } 283 return 0; 284} 285 286static int snd_seq_hw_get_queue_info(snd_seq_t *seq, snd_seq_queue_info_t *info) 287{ 288 snd_seq_hw_t *hw = seq->private_data; 289 if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_GET_QUEUE_INFO, info) < 0) { 290 /*SYSERR("SNDRV_SEQ_IOCTL_GET_QUEUE_INFO failed");*/ 291 return -errno; 292 } 293 return 0; 294} 295 296static int snd_seq_hw_set_queue_info(snd_seq_t *seq, snd_seq_queue_info_t *info) 297{ 298 snd_seq_hw_t *hw = seq->private_data; 299 if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SET_QUEUE_INFO, info) < 0) { 300 /*SYSERR("SNDRV_SEQ_IOCTL_SET_QUEUE_INFO failed");*/ 301 return -errno; 302 } 303 return 0; 304} 305 306static int snd_seq_hw_get_named_queue(snd_seq_t *seq, snd_seq_queue_info_t *info) 307{ 308 snd_seq_hw_t *hw = seq->private_data; 309 if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_GET_NAMED_QUEUE, info) < 0) { 310 /*SYSERR("SNDRV_SEQ_IOCTL_GET_NAMED_QUEUE failed");*/ 311 return -errno; 312 } 313 return 0; 314} 315 316static ssize_t snd_seq_hw_write(snd_seq_t *seq, void *buf, size_t len) 317{ 318 snd_seq_hw_t *hw = seq->private_data; 319 ssize_t result = write(hw->fd, buf, len); 320 if (result < 0) 321 return -errno; 322 return result; 323} 324 325static ssize_t snd_seq_hw_read(snd_seq_t *seq, void *buf, size_t len) 326{ 327 snd_seq_hw_t *hw = seq->private_data; 328 ssize_t result = read(hw->fd, buf, len); 329 if (result < 0) 330 return -errno; 331 return result; 332} 333 334static int snd_seq_hw_remove_events(snd_seq_t *seq, snd_seq_remove_events_t *rmp) 335{ 336 snd_seq_hw_t *hw = seq->private_data; 337 if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_REMOVE_EVENTS, rmp) < 0) { 338 /*SYSERR("SNDRV_SEQ_IOCTL_REMOVE_EVENTS failed");*/ 339 return -errno; 340 } 341 return 0; 342} 343 344static int snd_seq_hw_get_client_pool(snd_seq_t *seq, snd_seq_client_pool_t *info) 345{ 346 snd_seq_hw_t *hw = seq->private_data; 347 if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, info) < 0) { 348 /*SYSERR("SNDRV_SEQ_IOCTL_GET_CLIENT_POOL failed");*/ 349 return -errno; 350 } 351 return 0; 352} 353 354static int snd_seq_hw_set_client_pool(snd_seq_t *seq, snd_seq_client_pool_t *info) 355{ 356 snd_seq_hw_t *hw = seq->private_data; 357 if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, info) < 0) { 358 /*SYSERR("SNDRV_SEQ_IOCTL_SET_CLIENT_POOL failed");*/ 359 return -errno; 360 } 361 return 0; 362} 363 364static int snd_seq_hw_query_next_client(snd_seq_t *seq, snd_seq_client_info_t *info) 365{ 366 snd_seq_hw_t *hw = seq->private_data; 367 if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, info) < 0) { 368 /*SYSERR("SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT failed");*/ 369 return -errno; 370 } 371 return 0; 372} 373 374static int snd_seq_hw_query_next_port(snd_seq_t *seq, snd_seq_port_info_t *info) 375{ 376 snd_seq_hw_t *hw = seq->private_data; 377 if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, info) < 0) { 378 /*SYSERR("SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT failed");*/ 379 return -errno; 380 } 381 return 0; 382} 383 384static const snd_seq_ops_t snd_seq_hw_ops = { 385 .close = snd_seq_hw_close, 386 .nonblock = snd_seq_hw_nonblock, 387 .system_info = snd_seq_hw_system_info, 388 .get_client_info = snd_seq_hw_get_client_info, 389 .set_client_info = snd_seq_hw_set_client_info, 390 .create_port = snd_seq_hw_create_port, 391 .delete_port = snd_seq_hw_delete_port, 392 .get_port_info = snd_seq_hw_get_port_info, 393 .set_port_info = snd_seq_hw_set_port_info, 394 .get_port_subscription = snd_seq_hw_get_port_subscription, 395 .subscribe_port = snd_seq_hw_subscribe_port, 396 .unsubscribe_port = snd_seq_hw_unsubscribe_port, 397 .query_port_subscribers = snd_seq_hw_query_port_subscribers, 398 .get_queue_status = snd_seq_hw_get_queue_status, 399 .get_queue_tempo = snd_seq_hw_get_queue_tempo, 400 .set_queue_tempo = snd_seq_hw_set_queue_tempo, 401 .get_queue_timer = snd_seq_hw_get_queue_timer, 402 .set_queue_timer = snd_seq_hw_set_queue_timer, 403 .get_queue_client = snd_seq_hw_get_queue_client, 404 .set_queue_client = snd_seq_hw_set_queue_client, 405 .create_queue = snd_seq_hw_create_queue, 406 .delete_queue = snd_seq_hw_delete_queue, 407 .get_queue_info = snd_seq_hw_get_queue_info, 408 .set_queue_info = snd_seq_hw_set_queue_info, 409 .get_named_queue = snd_seq_hw_get_named_queue, 410 .write = snd_seq_hw_write, 411 .read = snd_seq_hw_read, 412 .remove_events = snd_seq_hw_remove_events, 413 .get_client_pool = snd_seq_hw_get_client_pool, 414 .set_client_pool = snd_seq_hw_set_client_pool, 415 .query_next_client = snd_seq_hw_query_next_client, 416 .query_next_port = snd_seq_hw_query_next_port, 417}; 418 419int snd_seq_hw_open(snd_seq_t **handle, const char *name, int streams, int mode) 420{ 421 int fd, ver, client, fmode, ret; 422 const char *filename; 423 snd_seq_t *seq; 424 snd_seq_hw_t *hw; 425 426 *handle = NULL; 427 428 switch (streams) { 429 case SND_SEQ_OPEN_OUTPUT: 430 fmode = O_WRONLY; 431 break; 432 case SND_SEQ_OPEN_INPUT: 433 fmode = O_RDONLY; 434 break; 435 case SND_SEQ_OPEN_DUPLEX: 436 fmode = O_RDWR; 437 break; 438 default: 439 assert(0); 440 return -EINVAL; 441 } 442 443 if (mode & SND_SEQ_NONBLOCK) 444 fmode |= O_NONBLOCK; 445 446 filename = SNDRV_FILE_SEQ; 447 fd = snd_open_device(filename, fmode); 448#ifdef SUPPORT_ALOAD 449 if (fd < 0) { 450 fd = snd_open_device(SNDRV_FILE_ALOADSEQ, fmode); 451 if (fd >= 0) 452 close(fd); 453 fd = snd_open_device(filename, fmode); 454 } 455#endif 456 if (fd < 0) { 457 SYSERR("open %s failed", filename); 458 return -errno; 459 } 460 if (ioctl(fd, SNDRV_SEQ_IOCTL_PVERSION, &ver) < 0) { 461 SYSERR("SNDRV_SEQ_IOCTL_PVERSION failed"); 462 ret = -errno; 463 close(fd); 464 return ret; 465 } 466 if (SNDRV_PROTOCOL_INCOMPATIBLE(ver, SNDRV_SEQ_VERSION_MAX)) { 467 close(fd); 468 return -SND_ERROR_INCOMPATIBLE_VERSION; 469 } 470 hw = calloc(1, sizeof(snd_seq_hw_t)); 471 if (hw == NULL) { 472 close(fd); 473 return -ENOMEM; 474 } 475 476 seq = calloc(1, sizeof(snd_seq_t)); 477 if (seq == NULL) { 478 free(hw); 479 close(fd); 480 return -ENOMEM; 481 } 482 hw->fd = fd; 483 if (streams & SND_SEQ_OPEN_OUTPUT) { 484 seq->obuf = (char *) malloc(seq->obufsize = SND_SEQ_OBUF_SIZE); 485 if (!seq->obuf) { 486 free(hw); 487 free(seq); 488 close(fd); 489 return -ENOMEM; 490 } 491 } 492 if (streams & SND_SEQ_OPEN_INPUT) { 493 seq->ibuf = (snd_seq_event_t *) calloc(sizeof(snd_seq_event_t), seq->ibufsize = SND_SEQ_IBUF_SIZE); 494 if (!seq->ibuf) { 495 free(seq->obuf); 496 free(hw); 497 free(seq); 498 close(fd); 499 return -ENOMEM; 500 } 501 } 502 if (name) 503 seq->name = strdup(name); 504 seq->type = SND_SEQ_TYPE_HW; 505 seq->streams = streams; 506 seq->mode = mode; 507 seq->tmpbuf = NULL; 508 seq->tmpbufsize = 0; 509 seq->poll_fd = fd; 510 seq->ops = &snd_seq_hw_ops; 511 seq->private_data = hw; 512 client = snd_seq_hw_client_id(seq); 513 if (client < 0) { 514 snd_seq_close(seq); 515 return client; 516 } else 517 seq->client = client; 518 519#ifdef SNDRV_SEQ_IOCTL_RUNNING_MODE 520 { 521 struct sndrv_seq_running_info run_mode; 522 /* check running mode */ 523 memset(&run_mode, 0, sizeof(run_mode)); 524 run_mode.client = client; 525#ifdef SNDRV_BIG_ENDIAN 526 run_mode.big_endian = 1; 527#else 528 run_mode.big_endian = 0; 529#endif 530 run_mode.cpu_mode = sizeof(long); 531 ioctl(fd, SNDRV_SEQ_IOCTL_RUNNING_MODE, &run_mode); 532 } 533#endif 534 535 *handle = seq; 536 return 0; 537} 538 539int _snd_seq_hw_open(snd_seq_t **handlep, char *name, 540 snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *conf, 541 int streams, int mode) 542{ 543 snd_config_iterator_t i, next; 544 snd_config_for_each(i, next, conf) { 545 snd_config_t *n = snd_config_iterator_entry(i); 546 const char *id; 547 if (snd_config_get_id(n, &id) < 0) 548 continue; 549 if (strcmp(id, "comment") == 0) 550 continue; 551 if (strcmp(id, "type") == 0) 552 continue; 553 return -EINVAL; 554 } 555 return snd_seq_hw_open(handlep, name, streams, mode); 556} 557SND_DLSYM_BUILD_VERSION(_snd_seq_hw_open, SND_SEQ_DLSYM_VERSION); 558