1/** 2 * \file control/control_hw.c 3 * \brief CTL HW Plugin Interface 4 * \author Jaroslav Kysela <perex@perex.cz> 5 * \date 2000 6 */ 7/* 8 * Control Interface - Hardware 9 * Copyright (c) 1998,1999,2000 by Jaroslav Kysela <perex@perex.cz> 10 * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org> 11 * 12 * 13 * This library is free software; you can redistribute it and/or modify 14 * it under the terms of the GNU Lesser General Public License as 15 * published by the Free Software Foundation; either version 2.1 of 16 * the License, or (at your option) any later version. 17 * 18 * This program is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 * GNU Lesser General Public License for more details. 22 * 23 * You should have received a copy of the GNU Lesser General Public 24 * License along with this library; if not, write to the Free Software 25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 26 * 27 */ 28 29#include "control_local.h" 30#include <stdio.h> 31#include <stdlib.h> 32#include <unistd.h> 33#include <signal.h> 34#include <string.h> 35#include <fcntl.h> 36#include <sys/ioctl.h> 37 38#ifndef PIC 39/* entry for static linking */ 40const char *_snd_module_control_hw = ""; 41#endif 42 43#ifndef DOC_HIDDEN 44 45#ifndef F_SETSIG 46#define F_SETSIG 10 47#endif 48 49#define SNDRV_FILE_CONTROL ALSA_DEVICE_DIRECTORY "controlC%i" 50#define SNDRV_CTL_VERSION_MAX SNDRV_PROTOCOL_VERSION(2, 0, 4) 51 52typedef struct { 53 int card; 54 int fd; 55 unsigned int protocol; 56} snd_ctl_hw_t; 57#endif /* DOC_HIDDEN */ 58 59static int snd_ctl_hw_close(snd_ctl_t *handle) 60{ 61 snd_ctl_hw_t *hw = handle->private_data; 62 int res; 63 res = close(hw->fd) < 0 ? -errno : 0; 64 free(hw); 65 return res; 66} 67 68static int snd_ctl_hw_nonblock(snd_ctl_t *handle, int nonblock) 69{ 70 snd_ctl_hw_t *hw = handle->private_data; 71 long flags; 72 int fd = hw->fd; 73 if ((flags = fcntl(fd, F_GETFL)) < 0) { 74 SYSERR("F_GETFL failed"); 75 return -errno; 76 } 77 if (nonblock) 78 flags |= O_NONBLOCK; 79 else 80 flags &= ~O_NONBLOCK; 81 if (fcntl(fd, F_SETFL, flags) < 0) { 82 SYSERR("F_SETFL for O_NONBLOCK failed"); 83 return -errno; 84 } 85 return 0; 86} 87 88static int snd_ctl_hw_async(snd_ctl_t *ctl, int sig, pid_t pid) 89{ 90 long flags; 91 snd_ctl_hw_t *hw = ctl->private_data; 92 int fd = hw->fd; 93 94 if ((flags = fcntl(fd, F_GETFL)) < 0) { 95 SYSERR("F_GETFL failed"); 96 return -errno; 97 } 98 if (sig >= 0) 99 flags |= O_ASYNC; 100 else 101 flags &= ~O_ASYNC; 102 if (fcntl(fd, F_SETFL, flags) < 0) { 103 SYSERR("F_SETFL for O_ASYNC failed"); 104 return -errno; 105 } 106 if (sig < 0) 107 return 0; 108 if (fcntl(fd, F_SETSIG, (long)sig) < 0) { 109 SYSERR("F_SETSIG failed"); 110 return -errno; 111 } 112 if (fcntl(fd, F_SETOWN, (long)pid) < 0) { 113 SYSERR("F_SETOWN failed"); 114 return -errno; 115 } 116 return 0; 117} 118 119static int snd_ctl_hw_subscribe_events(snd_ctl_t *handle, int subscribe) 120{ 121 snd_ctl_hw_t *hw = handle->private_data; 122 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, &subscribe) < 0) { 123 SYSERR("SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS failed"); 124 return -errno; 125 } 126 return 0; 127} 128 129static int snd_ctl_hw_card_info(snd_ctl_t *handle, snd_ctl_card_info_t *info) 130{ 131 snd_ctl_hw_t *hw = handle->private_data; 132 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_CARD_INFO, info) < 0) { 133 SYSERR("SNDRV_CTL_IOCTL_CARD_INFO failed"); 134 return -errno; 135 } 136 return 0; 137} 138 139static int snd_ctl_hw_elem_list(snd_ctl_t *handle, snd_ctl_elem_list_t *list) 140{ 141 snd_ctl_hw_t *hw = handle->private_data; 142 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_LIST, list) < 0) 143 return -errno; 144 return 0; 145} 146 147static int snd_ctl_hw_elem_info(snd_ctl_t *handle, snd_ctl_elem_info_t *info) 148{ 149 snd_ctl_hw_t *hw = handle->private_data; 150 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_INFO, info) < 0) 151 return -errno; 152 return 0; 153} 154 155static int snd_ctl_hw_elem_add(snd_ctl_t *handle, snd_ctl_elem_info_t *info) 156{ 157 snd_ctl_hw_t *hw = handle->private_data; 158 159 if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED && 160 hw->protocol < SNDRV_PROTOCOL_VERSION(2, 0, 7)) 161 return -ENXIO; 162 163 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_ADD, info) < 0) 164 return -errno; 165 return 0; 166} 167 168static int snd_ctl_hw_elem_replace(snd_ctl_t *handle, snd_ctl_elem_info_t *info) 169{ 170 snd_ctl_hw_t *hw = handle->private_data; 171 172 if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED && 173 hw->protocol < SNDRV_PROTOCOL_VERSION(2, 0, 7)) 174 return -ENXIO; 175 176 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_REPLACE, info) < 0) 177 return -errno; 178 return 0; 179} 180 181static int snd_ctl_hw_elem_remove(snd_ctl_t *handle, snd_ctl_elem_id_t *id) 182{ 183 snd_ctl_hw_t *hw = handle->private_data; 184 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_REMOVE, id) < 0) 185 return -errno; 186 return 0; 187} 188 189static int snd_ctl_hw_elem_read(snd_ctl_t *handle, snd_ctl_elem_value_t *control) 190{ 191 snd_ctl_hw_t *hw = handle->private_data; 192 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_READ, control) < 0) 193 return -errno; 194 return 0; 195} 196 197static int snd_ctl_hw_elem_write(snd_ctl_t *handle, snd_ctl_elem_value_t *control) 198{ 199 snd_ctl_hw_t *hw = handle->private_data; 200 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, control) < 0) 201 return -errno; 202 return 0; 203} 204 205static int snd_ctl_hw_elem_lock(snd_ctl_t *handle, snd_ctl_elem_id_t *id) 206{ 207 snd_ctl_hw_t *hw = handle->private_data; 208 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_LOCK, id) < 0) 209 return -errno; 210 return 0; 211} 212 213static int snd_ctl_hw_elem_unlock(snd_ctl_t *handle, snd_ctl_elem_id_t *id) 214{ 215 snd_ctl_hw_t *hw = handle->private_data; 216 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_UNLOCK, id) < 0) 217 return -errno; 218 return 0; 219} 220 221static int snd_ctl_hw_elem_tlv(snd_ctl_t *handle, int op_flag, 222 unsigned int numid, 223 unsigned int *tlv, unsigned int tlv_size) 224{ 225 unsigned int inum; 226 snd_ctl_hw_t *hw = handle->private_data; 227 struct snd_ctl_tlv *xtlv; 228 229 /* we don't support TLV on protocol ver 2.0.3 or earlier */ 230 if (hw->protocol < SNDRV_PROTOCOL_VERSION(2, 0, 4)) 231 return -ENXIO; 232 233 switch (op_flag) { 234 case -1: inum = SNDRV_CTL_IOCTL_TLV_COMMAND; break; 235 case 0: inum = SNDRV_CTL_IOCTL_TLV_READ; break; 236 case 1: inum = SNDRV_CTL_IOCTL_TLV_WRITE; break; 237 default: return -EINVAL; 238 } 239 xtlv = malloc(sizeof(struct snd_ctl_tlv) + tlv_size); 240 if (xtlv == NULL) 241 return -ENOMEM; 242 xtlv->numid = numid; 243 xtlv->length = tlv_size; 244 memcpy(xtlv->tlv, tlv, tlv_size); 245 if (ioctl(hw->fd, inum, xtlv) < 0) { 246 free(xtlv); 247 return -errno; 248 } 249 if (op_flag == 0) { 250 unsigned int size; 251 size = xtlv->tlv[SNDRV_CTL_TLVO_LEN] + 2 * sizeof(unsigned int); 252 if (size > tlv_size) { 253 free(xtlv); 254 return -EFAULT; 255 } 256 memcpy(tlv, xtlv->tlv, size); 257 } 258 free(xtlv); 259 return 0; 260} 261 262static int snd_ctl_hw_hwdep_next_device(snd_ctl_t *handle, int * device) 263{ 264 snd_ctl_hw_t *hw = handle->private_data; 265 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE, device) < 0) 266 return -errno; 267 return 0; 268} 269 270static int snd_ctl_hw_hwdep_info(snd_ctl_t *handle, snd_hwdep_info_t * info) 271{ 272 snd_ctl_hw_t *hw = handle->private_data; 273 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_HWDEP_INFO, info) < 0) 274 return -errno; 275 return 0; 276} 277 278static int snd_ctl_hw_pcm_next_device(snd_ctl_t *handle, int * device) 279{ 280 snd_ctl_hw_t *hw = handle->private_data; 281 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE, device) < 0) 282 return -errno; 283 return 0; 284} 285 286static int snd_ctl_hw_pcm_info(snd_ctl_t *handle, snd_pcm_info_t * info) 287{ 288 snd_ctl_hw_t *hw = handle->private_data; 289 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_PCM_INFO, info) < 0) 290 return -errno; 291 /* may be configurable (optional) */ 292 if (__snd_pcm_info_eld_fixup_check(info)) 293 return __snd_pcm_info_eld_fixup(info); 294 return 0; 295} 296 297static int snd_ctl_hw_pcm_prefer_subdevice(snd_ctl_t *handle, int subdev) 298{ 299 snd_ctl_hw_t *hw = handle->private_data; 300 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE, &subdev) < 0) 301 return -errno; 302 return 0; 303} 304 305static int snd_ctl_hw_rawmidi_next_device(snd_ctl_t *handle, int * device) 306{ 307 snd_ctl_hw_t *hw = handle->private_data; 308 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE, device) < 0) 309 return -errno; 310 return 0; 311} 312 313static int snd_ctl_hw_rawmidi_info(snd_ctl_t *handle, snd_rawmidi_info_t * info) 314{ 315 snd_ctl_hw_t *hw = handle->private_data; 316 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_RAWMIDI_INFO, info) < 0) 317 return -errno; 318 return 0; 319} 320 321static int snd_ctl_hw_rawmidi_prefer_subdevice(snd_ctl_t *handle, int subdev) 322{ 323 snd_ctl_hw_t *hw = handle->private_data; 324 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE, &subdev) < 0) 325 return -errno; 326 return 0; 327} 328 329static int snd_ctl_hw_ump_next_device(snd_ctl_t *handle, int *device) 330{ 331 snd_ctl_hw_t *hw = handle->private_data; 332 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE, device) < 0) 333 return -errno; 334 return 0; 335} 336 337static int snd_ctl_hw_ump_endpoint_info(snd_ctl_t *handle, 338 snd_ump_endpoint_info_t *info) 339{ 340 snd_ctl_hw_t *hw = handle->private_data; 341 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_UMP_ENDPOINT_INFO, info) < 0) 342 return -errno; 343 return 0; 344} 345 346static int snd_ctl_hw_ump_block_info(snd_ctl_t *handle, 347 snd_ump_block_info_t *info) 348{ 349 snd_ctl_hw_t *hw = handle->private_data; 350 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_UMP_BLOCK_INFO, info) < 0) 351 return -errno; 352 return 0; 353} 354 355static int snd_ctl_hw_set_power_state(snd_ctl_t *handle, unsigned int state) 356{ 357 snd_ctl_hw_t *hw = handle->private_data; 358 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_POWER, &state) < 0) 359 return -errno; 360 return 0; 361} 362 363static int snd_ctl_hw_get_power_state(snd_ctl_t *handle, unsigned int *state) 364{ 365 snd_ctl_hw_t *hw = handle->private_data; 366 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_POWER_STATE, state) < 0) 367 return -errno; 368 return 0; 369} 370 371static int snd_ctl_hw_read(snd_ctl_t *handle, snd_ctl_event_t *event) 372{ 373 snd_ctl_hw_t *hw = handle->private_data; 374 ssize_t res = read(hw->fd, event, sizeof(*event)); 375 if (res <= 0) 376 return -errno; 377 if (CHECK_SANITY(res != sizeof(*event))) { 378 SNDMSG("snd_ctl_hw_read: read size error (req:%d, got:%d)", 379 sizeof(*event), res); 380 return -EINVAL; 381 } 382 return 1; 383} 384 385static const snd_ctl_ops_t snd_ctl_hw_ops = { 386 .close = snd_ctl_hw_close, 387 .nonblock = snd_ctl_hw_nonblock, 388 .async = snd_ctl_hw_async, 389 .subscribe_events = snd_ctl_hw_subscribe_events, 390 .card_info = snd_ctl_hw_card_info, 391 .element_list = snd_ctl_hw_elem_list, 392 .element_info = snd_ctl_hw_elem_info, 393 .element_add = snd_ctl_hw_elem_add, 394 .element_replace = snd_ctl_hw_elem_replace, 395 .element_remove = snd_ctl_hw_elem_remove, 396 .element_read = snd_ctl_hw_elem_read, 397 .element_write = snd_ctl_hw_elem_write, 398 .element_lock = snd_ctl_hw_elem_lock, 399 .element_unlock = snd_ctl_hw_elem_unlock, 400 .element_tlv = snd_ctl_hw_elem_tlv, 401 .hwdep_next_device = snd_ctl_hw_hwdep_next_device, 402 .hwdep_info = snd_ctl_hw_hwdep_info, 403 .pcm_next_device = snd_ctl_hw_pcm_next_device, 404 .pcm_info = snd_ctl_hw_pcm_info, 405 .pcm_prefer_subdevice = snd_ctl_hw_pcm_prefer_subdevice, 406 .rawmidi_next_device = snd_ctl_hw_rawmidi_next_device, 407 .rawmidi_info = snd_ctl_hw_rawmidi_info, 408 .rawmidi_prefer_subdevice = snd_ctl_hw_rawmidi_prefer_subdevice, 409 .ump_next_device = snd_ctl_hw_ump_next_device, 410 .ump_endpoint_info = snd_ctl_hw_ump_endpoint_info, 411 .ump_block_info = snd_ctl_hw_ump_block_info, 412 .set_power_state = snd_ctl_hw_set_power_state, 413 .get_power_state = snd_ctl_hw_get_power_state, 414 .read = snd_ctl_hw_read, 415}; 416 417/** 418 * \brief Creates a new hw control 419 * \param handle Returns created control handle 420 * \param name Name of control device 421 * \param card Number of card 422 * \param mode Control mode 423 * \retval zero on success otherwise a negative error code 424 * \warning Using of this function might be dangerous in the sense 425 * of compatibility reasons. The prototype might be freely 426 * changed in future. 427 */ 428int snd_ctl_hw_open(snd_ctl_t **handle, const char *name, int card, int mode) 429{ 430 int fd, ver; 431 char filename[sizeof(SNDRV_FILE_CONTROL) + 10]; 432 int fmode; 433 snd_ctl_t *ctl; 434 snd_ctl_hw_t *hw; 435 int err; 436 437 *handle = NULL; 438 439 if (CHECK_SANITY(card < 0 || card >= SND_MAX_CARDS)) { 440 SNDMSG("Invalid card index %d", card); 441 return -EINVAL; 442 } 443 sprintf(filename, SNDRV_FILE_CONTROL, card); 444 if (mode & SND_CTL_READONLY) 445 fmode = O_RDONLY; 446 else 447 fmode = O_RDWR; 448 if (mode & SND_CTL_NONBLOCK) 449 fmode |= O_NONBLOCK; 450 if (mode & SND_CTL_ASYNC) 451 fmode |= O_ASYNC; 452 fd = snd_open_device(filename, fmode); 453 if (fd < 0) { 454 snd_card_load(card); 455 fd = snd_open_device(filename, fmode); 456 if (fd < 0) 457 return -errno; 458 } 459 if (ioctl(fd, SNDRV_CTL_IOCTL_PVERSION, &ver) < 0) { 460 err = -errno; 461 close(fd); 462 return err; 463 } 464 if (SNDRV_PROTOCOL_INCOMPATIBLE(ver, SNDRV_CTL_VERSION_MAX)) { 465 close(fd); 466 return -SND_ERROR_INCOMPATIBLE_VERSION; 467 } 468 hw = calloc(1, sizeof(snd_ctl_hw_t)); 469 if (hw == NULL) { 470 close(fd); 471 return -ENOMEM; 472 } 473 hw->card = card; 474 hw->fd = fd; 475 hw->protocol = ver; 476 477 err = snd_ctl_new(&ctl, SND_CTL_TYPE_HW, name, mode); 478 if (err < 0) { 479 close(fd); 480 free(hw); 481 return err; 482 } 483 ctl->ops = &snd_ctl_hw_ops; 484 ctl->private_data = hw; 485 ctl->poll_fd = fd; 486 *handle = ctl; 487 return 0; 488} 489 490/*! \page control_plugins 491 492\section control_plugins_hw Plugin: hw 493 494This plugin communicates directly with the ALSA kernel driver. It is a raw 495communication without any conversions. 496 497\code 498control.name { 499 type hw # Kernel PCM 500 card INT/STR # Card name (string) or number (integer) 501} 502\endcode 503 504\subsection control_plugins_hw_funcref Function reference 505 506<UL> 507 <LI>snd_ctl_hw_open() 508 <LI>_snd_ctl_hw_open() 509</UL> 510 511*/ 512 513/** 514 * \brief Creates a new hw control handle 515 * \param handlep Returns created control handle 516 * \param name Name of control device 517 * \param root Root configuration node 518 * \param conf Configuration node with hw PCM description 519 * \param mode Control Mode 520 * \warning Using of this function might be dangerous in the sense 521 * of compatibility reasons. The prototype might be freely 522 * changed in future. 523 */ 524int _snd_ctl_hw_open(snd_ctl_t **handlep, char *name, snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *conf, int mode) 525{ 526 snd_config_iterator_t i, next; 527 long card = -1; 528 int err; 529 snd_config_for_each(i, next, conf) { 530 snd_config_t *n = snd_config_iterator_entry(i); 531 const char *id; 532 if (snd_config_get_id(n, &id) < 0) 533 continue; 534 if (_snd_conf_generic_id(id)) 535 continue; 536 if (strcmp(id, "card") == 0) { 537 err = snd_config_get_card(n); 538 if (err < 0) 539 return err; 540 card = err; 541 continue; 542 } 543 return -EINVAL; 544 } 545 if (card < 0) 546 return -EINVAL; 547 return snd_ctl_hw_open(handlep, name, card, mode); 548} 549#ifndef DOC_HIDDEN 550SND_DLSYM_BUILD_VERSION(_snd_ctl_hw_open, SND_CONTROL_DLSYM_VERSION); 551#endif 552