Home | History | Annotate | Download | only in alisp
      1 /*
      2  *  ALSA lisp implementation - sound related commands
      3  *  Copyright (c) 2003 by Jaroslav Kysela <perex (at) perex.cz>
      4  *
      5  *
      6  *   This library is free software; you can redistribute it and/or modify
      7  *   it under the terms of the GNU Lesser General Public License as
      8  *   published by the Free Software Foundation; either version 2.1 of
      9  *   the License, or (at your option) any later version.
     10  *
     11  *   This program is distributed in the hope that it will be useful,
     12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14  *   GNU Lesser General Public License for more details.
     15  *
     16  *   You should have received a copy of the GNU Lesser General Public
     17  *   License along with this library; if not, write to the Free Software
     18  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
     19  *
     20  */
     21 
     22 struct acall_table {
     23 	const char *name;
     24 	struct alisp_object * (*func) (struct alisp_instance *instance, struct acall_table * item, struct alisp_object * args);
     25 	void * xfunc;
     26 	const char *prefix;
     27 };
     28 
     29 /*
     30  *  helper functions
     31  */
     32 
     33 static inline int get_integer(struct alisp_object * obj)
     34 {
     35 	if (alisp_compare_type(obj, ALISP_OBJ_INTEGER))
     36 		return obj->value.i;
     37 	return 0;
     38 }
     39 
     40 static inline const void *get_pointer(struct alisp_object * obj)
     41 {
     42 	if (alisp_compare_type(obj, ALISP_OBJ_POINTER))
     43 		return obj->value.ptr;
     44 	return NULL;
     45 }
     46 
     47 static const char *get_string(struct alisp_object * obj, const char * deflt)
     48 {
     49 	if (obj == &alsa_lisp_t)
     50 		return "true";
     51 	if (alisp_compare_type(obj, ALISP_OBJ_STRING) ||
     52 	    alisp_compare_type(obj, ALISP_OBJ_IDENTIFIER))
     53 		return obj->value.s;
     54 	return deflt;
     55 }
     56 
     57 struct flags {
     58 	const char *key;
     59 	unsigned int mask;
     60 };
     61 
     62 static unsigned int get_flags(struct alisp_instance * instance,
     63 			      struct alisp_object * obj,
     64 			      const struct flags * flags,
     65 			      unsigned int deflt)
     66 {
     67 	const char *key;
     68 	int invert;
     69 	unsigned int result;
     70 	const struct flags *ptr;
     71 	struct alisp_object *n;
     72 
     73 	if (obj == &alsa_lisp_nil)
     74 		return deflt;
     75 	result = deflt;
     76 	do {
     77 		key = get_string(obj, NULL);
     78 		if (key) {
     79 			invert = key[0] == '!';
     80 			key += invert;
     81 			ptr = flags;
     82 			while (ptr->key) {
     83 				if (!strcmp(ptr->key, key)) {
     84 					if (invert)
     85 						result &= ~ptr->mask;
     86 					else
     87 						result |= ptr->mask;
     88 					break;
     89 				}
     90 				ptr++;
     91 			}
     92 		}
     93 		delete_tree(instance, car(obj));
     94 		obj = cdr(n = obj);
     95 		delete_object(instance, n);
     96 	} while (obj != &alsa_lisp_nil);
     97 	return result;
     98 }
     99 
    100 static const void *get_ptr(struct alisp_instance * instance,
    101 			   struct alisp_object * obj,
    102 			   const char *_ptr_id)
    103 {
    104 	const char *ptr_id;
    105 	const void *ptr;
    106 
    107 	ptr_id = get_string(car(obj), NULL);
    108 	if (ptr_id == NULL) {
    109 		delete_tree(instance, obj);
    110 		return NULL;
    111 	}
    112 	if (strcmp(ptr_id, _ptr_id)) {
    113 		delete_tree(instance, obj);
    114 		return NULL;
    115 	}
    116 	ptr = get_pointer(cdr(obj));
    117 	delete_tree(instance, obj);
    118 	return ptr;
    119 }
    120 
    121 static struct alisp_object * new_lexpr(struct alisp_instance * instance, int err)
    122 {
    123 	struct alisp_object * lexpr;
    124 
    125 	lexpr = new_object(instance, ALISP_OBJ_CONS);
    126 	if (lexpr == NULL)
    127 		return NULL;
    128 	lexpr->value.c.car = new_integer(instance, err);
    129 	if (lexpr->value.c.car == NULL) {
    130 		delete_object(instance, lexpr);
    131 		return NULL;
    132 	}
    133 	lexpr->value.c.cdr = new_object(instance, ALISP_OBJ_CONS);
    134 	if (lexpr->value.c.cdr == NULL) {
    135 		delete_object(instance, lexpr->value.c.car);
    136 		delete_object(instance, lexpr);
    137 		return NULL;
    138 	}
    139 	return lexpr;
    140 }
    141 
    142 static struct alisp_object * add_cons(struct alisp_instance * instance,
    143 				      struct alisp_object *lexpr,
    144 				      int cdr, const char *id,
    145 				      struct alisp_object *obj)
    146 {
    147 	struct alisp_object * p1, * p2;
    148 
    149 	if (lexpr == NULL || obj == NULL) {
    150 		delete_tree(instance, obj);
    151 		return NULL;
    152 	}
    153 	if (cdr) {
    154 		p1 = lexpr->value.c.cdr = new_object(instance, ALISP_OBJ_CONS);
    155 	} else {
    156 		p1 = lexpr->value.c.car = new_object(instance, ALISP_OBJ_CONS);
    157 	}
    158 	lexpr = p1;
    159 	if (p1 == NULL) {
    160 		delete_tree(instance, obj);
    161 		return NULL;
    162 	}
    163 	p1->value.c.car = new_object(instance, ALISP_OBJ_CONS);
    164 	if ((p2 = p1->value.c.car) == NULL)
    165 		goto __err;
    166 	p2->value.c.car = new_string(instance, id);
    167 	if (p2->value.c.car == NULL) {
    168 	      __err:
    169 		if (cdr)
    170 			lexpr->value.c.cdr = NULL;
    171 		else
    172 			lexpr->value.c.car = NULL;
    173 		delete_tree(instance, p1);
    174 		delete_tree(instance, obj);
    175 		return NULL;
    176 	}
    177 	p2->value.c.cdr = obj;
    178 	return lexpr;
    179 }
    180 
    181 static struct alisp_object * add_cons2(struct alisp_instance * instance,
    182 				       struct alisp_object *lexpr,
    183 				       int cdr, struct alisp_object *obj)
    184 {
    185 	struct alisp_object * p1;
    186 
    187 	if (lexpr == NULL || obj == NULL) {
    188 		delete_tree(instance, obj);
    189 		return NULL;
    190 	}
    191 	if (cdr) {
    192 		p1 = lexpr->value.c.cdr = new_object(instance, ALISP_OBJ_CONS);
    193 	} else {
    194 		p1 = lexpr->value.c.car = new_object(instance, ALISP_OBJ_CONS);
    195 	}
    196 	lexpr = p1;
    197 	if (p1 == NULL) {
    198 		delete_tree(instance, obj);
    199 		return NULL;
    200 	}
    201 	p1->value.c.car = obj;
    202 	return lexpr;
    203 }
    204 
    205 static struct alisp_object * new_result1(struct alisp_instance * instance,
    206 					 int err, const char *ptr_id, void *ptr)
    207 {
    208 	struct alisp_object * lexpr, * p1;
    209 
    210 	if (err < 0)
    211 		ptr = NULL;
    212 	lexpr = new_object(instance, ALISP_OBJ_CONS);
    213 	if (lexpr == NULL)
    214 		return NULL;
    215 	lexpr->value.c.car = new_integer(instance, err);
    216 	if (lexpr->value.c.car == NULL) {
    217 		delete_object(instance, lexpr);
    218 		return NULL;
    219 	}
    220 	p1 = add_cons(instance, lexpr, 1, ptr_id, new_pointer(instance, ptr));
    221 	if (p1 == NULL) {
    222 		delete_object(instance, lexpr);
    223 		return NULL;
    224 	}
    225 	return lexpr;
    226 }
    227 
    228 static struct alisp_object * new_result2(struct alisp_instance * instance,
    229 					 int err, int val)
    230 {
    231 	struct alisp_object * lexpr, * p1;
    232 
    233 	if (err < 0)
    234 		val = 0;
    235 	lexpr = new_lexpr(instance, err);
    236 	if (lexpr == NULL)
    237 		return NULL;
    238 	p1 = lexpr->value.c.cdr;
    239 	p1->value.c.car = new_integer(instance, val);
    240 	if (p1->value.c.car == NULL) {
    241 		delete_object(instance, lexpr);
    242 		return NULL;
    243 	}
    244 	return lexpr;
    245 }
    246 
    247 static struct alisp_object * new_result3(struct alisp_instance * instance,
    248 					 int err, const char *str)
    249 {
    250 	struct alisp_object * lexpr, * p1;
    251 
    252 	if (err < 0)
    253 		str = "";
    254 	lexpr = new_lexpr(instance, err);
    255 	if (lexpr == NULL)
    256 		return NULL;
    257 	p1 = lexpr->value.c.cdr;
    258 	p1->value.c.car = new_string(instance, str);
    259 	if (p1->value.c.car == NULL) {
    260 		delete_object(instance, lexpr);
    261 		return NULL;
    262 	}
    263 	return lexpr;
    264 }
    265 
    266 /*
    267  *  macros
    268  */
    269 
    270 /*
    271  *  HCTL functions
    272  */
    273 
    274 typedef int (*snd_int_pp_strp_int_t)(void **rctl, const char *name, int mode);
    275 typedef int (*snd_int_pp_p_t)(void **rctl, void *handle);
    276 typedef int (*snd_int_p_t)(void *rctl);
    277 typedef char * (*snd_str_p_t)(void *rctl);
    278 typedef int (*snd_int_intp_t)(int *val);
    279 typedef int (*snd_int_str_t)(const char *str);
    280 typedef int (*snd_int_int_strp_t)(int val, char **str);
    281 typedef void *(*snd_p_p_t)(void *handle);
    282 
    283 static struct alisp_object * FA_int_pp_strp_int(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args)
    284 {
    285 	const char *name;
    286 	int err, mode;
    287 	void *handle;
    288 	struct alisp_object *p1, *p2;
    289 	static const struct flags flags[] = {
    290 		{ "nonblock", SND_CTL_NONBLOCK },
    291 		{ "async", SND_CTL_ASYNC },
    292 		{ "readonly", SND_CTL_READONLY },
    293 		{ NULL, 0 }
    294 	};
    295 
    296 	name = get_string(p1 = eval(instance, car(args)), NULL);
    297 	if (name == NULL)
    298 		return &alsa_lisp_nil;
    299 	mode = get_flags(instance, p2 = eval(instance, car(cdr(args))), flags, 0);
    300 	delete_tree(instance, cdr(cdr(args)));
    301 	delete_object(instance, cdr(args));
    302 	delete_object(instance, args);
    303 	delete_tree(instance, p2);
    304 	err = ((snd_int_pp_strp_int_t)item->xfunc)(&handle, name, mode);
    305 	delete_tree(instance, p1);
    306 	return new_result1(instance, err, item->prefix, handle);
    307 }
    308 
    309 static struct alisp_object * FA_int_pp_p(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args)
    310 {
    311 	int err;
    312 	void *handle;
    313 	const char *prefix1;
    314 	struct alisp_object *p1;
    315 
    316 	if (item->xfunc == &snd_hctl_open_ctl)
    317 		prefix1 = "ctl";
    318 	else {
    319 		delete_tree(instance, args);
    320 		return &alsa_lisp_nil;
    321 	}
    322 	p1 = eval(instance, car(args));
    323 	delete_tree(instance, cdr(args));
    324 	delete_object(instance, args);
    325 	handle = (void *)get_ptr(instance, p1, prefix1);
    326 	if (handle == NULL)
    327 		return &alsa_lisp_nil;
    328 	err = ((snd_int_pp_p_t)item->xfunc)(&handle, handle);
    329 	return new_result1(instance, err, item->prefix, handle);
    330 }
    331 
    332 static struct alisp_object * FA_p_p(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args)
    333 {
    334 	void *handle;
    335 	const char *prefix1;
    336 	struct alisp_object * p1;
    337 
    338 	if (item->xfunc == &snd_hctl_first_elem ||
    339 	    item->xfunc == &snd_hctl_last_elem ||
    340 	    item->xfunc == &snd_hctl_elem_next ||
    341 	    item->xfunc == &snd_hctl_elem_prev)
    342 		prefix1 = "hctl_elem";
    343 	else if (item->xfunc == &snd_hctl_ctl)
    344 		prefix1 = "ctl";
    345 	else {
    346 		delete_tree(instance, args);
    347 		return &alsa_lisp_nil;
    348 	}
    349 	p1 = eval(instance, car(args));
    350 	delete_tree(instance, cdr(args));
    351 	delete_object(instance, args);
    352 	handle = (void *)get_ptr(instance, p1, item->prefix);
    353 	if (handle == NULL)
    354 		return &alsa_lisp_nil;
    355 	handle = ((snd_p_p_t)item->xfunc)(handle);
    356 	return new_cons_pointer(instance, prefix1, handle);
    357 }
    358 
    359 static struct alisp_object * FA_int_p(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args)
    360 {
    361 	void *handle;
    362 	struct alisp_object * p1;
    363 
    364 	p1 = eval(instance, car(args));
    365 	delete_tree(instance, cdr(args));
    366 	delete_object(instance, args);
    367 	handle = (void *)get_ptr(instance, p1, item->prefix);
    368 	if (handle == NULL)
    369 		return &alsa_lisp_nil;
    370 	return new_integer(instance, ((snd_int_p_t)item->xfunc)(handle));
    371 }
    372 
    373 static struct alisp_object * FA_str_p(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args)
    374 {
    375 	void *handle;
    376 	struct alisp_object * p1;
    377 
    378 	p1 = eval(instance, car(args));
    379 	delete_tree(instance, cdr(args));
    380 	delete_object(instance, args);
    381 	handle = (void *)get_ptr(instance, p1, item->prefix);
    382 	if (handle == NULL)
    383 		return &alsa_lisp_nil;
    384 	return new_string(instance, ((snd_str_p_t)item->xfunc)(handle));
    385 }
    386 
    387 static struct alisp_object * FA_int_intp(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args)
    388 {
    389 	int val, err;
    390 	struct alisp_object * p1;
    391 
    392 	p1 = eval(instance, car(args));
    393 	delete_tree(instance, cdr(args));
    394 	delete_object(instance, args);
    395 	if (!alisp_compare_type(p1, ALISP_OBJ_INTEGER)) {
    396 		delete_tree(instance, p1);
    397 		return &alsa_lisp_nil;
    398 	}
    399 	val = p1->value.i;
    400 	delete_tree(instance, p1);
    401 	err = ((snd_int_intp_t)item->xfunc)(&val);
    402 	return new_result2(instance, err, val);
    403 }
    404 
    405 static struct alisp_object * FA_int_str(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args)
    406 {
    407 	int err;
    408 	struct alisp_object * p1;
    409 
    410 	p1 = eval(instance, car(args));
    411 	delete_tree(instance, cdr(args));
    412 	delete_object(instance, args);
    413 	if (!alisp_compare_type(p1, ALISP_OBJ_STRING) &&
    414 	    !alisp_compare_type(p1, ALISP_OBJ_IDENTIFIER)) {
    415 		delete_tree(instance, p1);
    416 		return &alsa_lisp_nil;
    417 	}
    418 	err = ((snd_int_str_t)item->xfunc)(p1->value.s);
    419 	delete_tree(instance, p1);
    420 	return new_integer(instance, err);
    421 }
    422 
    423 static struct alisp_object * FA_int_int_strp(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args)
    424 {
    425 	int err;
    426 	char *str;
    427 	long val;
    428 	struct alisp_object * p1;
    429 
    430 	p1 = eval(instance, car(args));
    431 	delete_tree(instance, cdr(args));
    432 	delete_object(instance, args);
    433 	if (!alisp_compare_type(p1, ALISP_OBJ_INTEGER)) {
    434 		delete_tree(instance, p1);
    435 		return &alsa_lisp_nil;
    436 	}
    437 	val = p1->value.i;
    438 	delete_tree(instance, p1);
    439 	err = ((snd_int_int_strp_t)item->xfunc)(val, &str);
    440 	return new_result3(instance, err, str);
    441 }
    442 
    443 static struct alisp_object * FA_card_info(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args)
    444 {
    445 	snd_ctl_t *handle;
    446 	struct alisp_object * lexpr, * p1;
    447 	snd_ctl_card_info_t * info;
    448 	int err;
    449 
    450 	p1 = eval(instance, car(args));
    451 	delete_tree(instance, cdr(args));
    452 	delete_object(instance, args);
    453 	handle = (snd_ctl_t *)get_ptr(instance, p1, item->prefix);
    454 	if (handle == NULL)
    455 		return &alsa_lisp_nil;
    456 	snd_ctl_card_info_alloca(&info);
    457 	err = snd_ctl_card_info(handle, info);
    458 	lexpr = new_lexpr(instance, err);
    459 	if (err < 0)
    460 		return lexpr;
    461 	p1 = add_cons(instance, lexpr->value.c.cdr, 0, "id", new_string(instance, snd_ctl_card_info_get_id(info)));
    462 	p1 = add_cons(instance, p1, 1, "driver", new_string(instance, snd_ctl_card_info_get_driver(info)));
    463 	p1 = add_cons(instance, p1, 1, "name", new_string(instance, snd_ctl_card_info_get_name(info)));
    464 	p1 = add_cons(instance, p1, 1, "longname", new_string(instance, snd_ctl_card_info_get_longname(info)));
    465 	p1 = add_cons(instance, p1, 1, "mixername", new_string(instance, snd_ctl_card_info_get_mixername(info)));
    466 	p1 = add_cons(instance, p1, 1, "components", new_string(instance, snd_ctl_card_info_get_components(info)));
    467 	if (p1 == NULL) {
    468 		delete_tree(instance, lexpr);
    469 		return NULL;
    470 	}
    471 	return lexpr;
    472 }
    473 
    474 static struct alisp_object * create_ctl_elem_id(struct alisp_instance * instance, snd_ctl_elem_id_t * id, struct alisp_object * cons)
    475 {
    476 	cons = add_cons(instance, cons, 0, "numid", new_integer(instance, snd_ctl_elem_id_get_numid(id)));
    477 	cons = add_cons(instance, cons, 1, "iface", new_string(instance, snd_ctl_elem_iface_name(snd_ctl_elem_id_get_interface(id))));
    478 	cons = add_cons(instance, cons, 1, "dev", new_integer(instance, snd_ctl_elem_id_get_device(id)));
    479 	cons = add_cons(instance, cons, 1, "subdev", new_integer(instance, snd_ctl_elem_id_get_subdevice(id)));
    480 	cons = add_cons(instance, cons, 1, "name", new_string(instance, snd_ctl_elem_id_get_name(id)));
    481 	cons = add_cons(instance, cons, 1, "index", new_integer(instance, snd_ctl_elem_id_get_index(id)));
    482 	return cons;
    483 }
    484 
    485 static int parse_ctl_elem_id(struct alisp_instance * instance,
    486 			     struct alisp_object * cons,
    487 			     snd_ctl_elem_id_t * id)
    488 {
    489 	struct alisp_object *p1;
    490 	const char *xid;
    491 
    492 	if (cons == NULL)
    493 		return -ENOMEM;
    494 	snd_ctl_elem_id_clear(id);
    495 	id->numid = 0;
    496 	do {
    497 		p1 = car(cons);
    498 		if (alisp_compare_type(p1, ALISP_OBJ_CONS)) {
    499 			xid = get_string(p1->value.c.car, NULL);
    500 			if (xid == NULL) {
    501 				/* noop */
    502 			} else if (!strcmp(xid, "numid")) {
    503 				snd_ctl_elem_id_set_numid(id, get_integer(p1->value.c.cdr));
    504 			} else if (!strcmp(xid, "iface")) {
    505 				snd_ctl_elem_id_set_interface(id, snd_config_get_ctl_iface_ascii(get_string(p1->value.c.cdr, "0")));
    506 			} else if (!strcmp(xid, "dev")) {
    507 				snd_ctl_elem_id_set_device(id, get_integer(p1->value.c.cdr));
    508 			} else if (!strcmp(xid, "subdev")) {
    509 				snd_ctl_elem_id_set_subdevice(id, get_integer(p1->value.c.cdr));
    510 			} else if (!strcmp(xid, "name")) {
    511 				snd_ctl_elem_id_set_name(id, get_string(p1->value.c.cdr, "?"));
    512 			} else if (!strcmp(xid, "index")) {
    513 				snd_ctl_elem_id_set_index(id, get_integer(p1->value.c.cdr));
    514 			}
    515 		}
    516 		delete_tree(instance, p1);
    517 	        cons = cdr(p1 = cons);
    518 	        delete_object(instance, p1);
    519 	} while (cons != &alsa_lisp_nil);
    520 	return 0;
    521 }
    522 
    523 static struct alisp_object * FA_hctl_find_elem(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args)
    524 {
    525 	snd_hctl_t *handle;
    526 	snd_ctl_elem_id_t *id;
    527 	struct alisp_object *p1;
    528 
    529 	handle = (snd_hctl_t *)get_ptr(instance, car(args), item->prefix);
    530 	if (handle == NULL) {
    531 		delete_tree(instance, cdr(args));
    532 		delete_object(instance, args);
    533 		return &alsa_lisp_nil;
    534 	}
    535 	snd_ctl_elem_id_alloca(&id);
    536 	p1 = car(cdr(args));
    537 	delete_tree(instance, cdr(cdr(args)));
    538 	delete_object(instance, cdr(args));
    539 	delete_object(instance, args);
    540 	if (parse_ctl_elem_id(instance, eval(instance, p1), id) < 0)
    541 		return &alsa_lisp_nil;
    542 	return new_cons_pointer(instance, "hctl_elem", snd_hctl_find_elem(handle, id));
    543 }
    544 
    545 static struct alisp_object * FA_hctl_elem_info(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args)
    546 {
    547 	snd_hctl_elem_t *handle;
    548 	struct alisp_object * lexpr, * p1, * p2;
    549 	snd_ctl_elem_info_t *info;
    550 	snd_ctl_elem_id_t *id;
    551 	snd_ctl_elem_type_t type;
    552 	int err;
    553 
    554 	p1 = eval(instance, car(args));
    555 	delete_tree(instance, cdr(args));
    556 	delete_object(instance, args);
    557 	handle = (snd_hctl_elem_t *)get_ptr(instance, p1, item->prefix);
    558 	if (handle == NULL)
    559 		return &alsa_lisp_nil;
    560 	snd_ctl_elem_info_alloca(&info);
    561 	snd_ctl_elem_id_alloca(&id);
    562 	err = snd_hctl_elem_info(handle, info);
    563 	lexpr = new_lexpr(instance, err);
    564 	if (err < 0)
    565 		return lexpr;
    566 	type = snd_ctl_elem_info_get_type(info);
    567 	p1 = add_cons(instance, lexpr->value.c.cdr, 0, "id", p2 = new_object(instance, ALISP_OBJ_CONS));
    568 	snd_ctl_elem_info_get_id(info, id);
    569 	if (create_ctl_elem_id(instance, id, p2) == NULL) {
    570 		delete_tree(instance, lexpr);
    571 		return NULL;
    572 	}
    573 	p1 = add_cons(instance, p1, 1, "type", new_string(instance, snd_ctl_elem_type_name(type)));
    574 	p1 = add_cons(instance, p1, 1, "readable", new_integer(instance, snd_ctl_elem_info_is_readable(info)));
    575 	p1 = add_cons(instance, p1, 1, "writeable", new_integer(instance, snd_ctl_elem_info_is_writable(info)));
    576 	p1 = add_cons(instance, p1, 1, "volatile", new_integer(instance, snd_ctl_elem_info_is_volatile(info)));
    577 	p1 = add_cons(instance, p1, 1, "inactive", new_integer(instance, snd_ctl_elem_info_is_inactive(info)));
    578 	p1 = add_cons(instance, p1, 1, "locked", new_integer(instance, snd_ctl_elem_info_is_locked(info)));
    579 	p1 = add_cons(instance, p1, 1, "isowner", new_integer(instance, snd_ctl_elem_info_is_owner(info)));
    580 	p1 = add_cons(instance, p1, 1, "owner", new_integer(instance, snd_ctl_elem_info_get_owner(info)));
    581 	p1 = add_cons(instance, p1, 1, "count", new_integer(instance, snd_ctl_elem_info_get_count(info)));
    582 	err = snd_ctl_elem_info_get_dimensions(info);
    583 	if (err > 0) {
    584 		int idx;
    585 		p1 = add_cons(instance, p1, 1, "dimensions", p2 = new_object(instance, ALISP_OBJ_CONS));
    586 		for (idx = 0; idx < err; idx++)
    587 			p2 = add_cons2(instance, p2, idx > 0, new_integer(instance, snd_ctl_elem_info_get_dimension(info, idx)));
    588 	}
    589 	switch (type) {
    590 	case SND_CTL_ELEM_TYPE_ENUMERATED: {
    591 		unsigned int items, item;
    592 		items = snd_ctl_elem_info_get_items(info);
    593 		p1 = add_cons(instance, p1, 1, "items", p2 = new_object(instance, ALISP_OBJ_CONS));
    594 		for (item = 0; item < items; item++) {
    595 			snd_ctl_elem_info_set_item(info, item);
    596 			err = snd_hctl_elem_info(handle, info);
    597 			if (err < 0) {
    598 				p2 = add_cons2(instance, p2, item, &alsa_lisp_nil);
    599 			} else {
    600 				p2 = add_cons2(instance, p2, item, new_string(instance, snd_ctl_elem_info_get_item_name(info)));
    601 			}
    602 		}
    603 		break;
    604 	}
    605 	case SND_CTL_ELEM_TYPE_INTEGER:
    606 		p1 = add_cons(instance, p1, 1, "min", new_integer(instance, snd_ctl_elem_info_get_min(info)));
    607 		p1 = add_cons(instance, p1, 1, "max", new_integer(instance, snd_ctl_elem_info_get_max(info)));
    608 		p1 = add_cons(instance, p1, 1, "step", new_integer(instance, snd_ctl_elem_info_get_step(info)));
    609 		break;
    610 	case SND_CTL_ELEM_TYPE_INTEGER64:
    611 		p1 = add_cons(instance, p1, 1, "min64", new_float(instance, snd_ctl_elem_info_get_min64(info)));
    612 		p1 = add_cons(instance, p1, 1, "max64", new_float(instance, snd_ctl_elem_info_get_max64(info)));
    613 		p1 = add_cons(instance, p1, 1, "step64", new_float(instance, snd_ctl_elem_info_get_step64(info)));
    614 		break;
    615 	default:
    616 		break;
    617 	}
    618 	if (p1 == NULL) {
    619 		delete_tree(instance, lexpr);
    620 		return NULL;
    621 	}
    622 	return lexpr;
    623 }
    624 
    625 static struct alisp_object * FA_hctl_elem_read(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args)
    626 {
    627 	snd_hctl_elem_t *handle;
    628 	struct alisp_object * lexpr, * p1 = NULL, * obj;
    629 	snd_ctl_elem_info_t *info;
    630 	snd_ctl_elem_value_t *value;
    631 	snd_ctl_elem_type_t type;
    632 	unsigned int idx, count;
    633 	int err;
    634 
    635 	p1 = eval(instance, car(args));
    636 	delete_tree(instance, cdr(args));
    637 	delete_object(instance, args);
    638 	handle = (snd_hctl_elem_t *)get_ptr(instance, p1, item->prefix);
    639 	if (handle == NULL)
    640 		return &alsa_lisp_nil;
    641 	snd_ctl_elem_info_alloca(&info);
    642 	snd_ctl_elem_value_alloca(&value);
    643 	err = snd_hctl_elem_info(handle, info);
    644 	if (err >= 0)
    645 		err = snd_hctl_elem_read(handle, value);
    646 	lexpr = new_lexpr(instance, err);
    647 	if (err < 0)
    648 		return lexpr;
    649 	type = snd_ctl_elem_info_get_type(info);
    650 	count = snd_ctl_elem_info_get_count(info);
    651 	if (type == SND_CTL_ELEM_TYPE_IEC958) {
    652 		count = sizeof(snd_aes_iec958_t);
    653 		type = SND_CTL_ELEM_TYPE_BYTES;
    654 	}
    655 	for (idx = 0; idx < count; idx++) {
    656 		switch (type) {
    657 		case SND_CTL_ELEM_TYPE_BOOLEAN:
    658 			obj = new_integer(instance, snd_ctl_elem_value_get_boolean(value, idx));
    659 			break;
    660 		case SND_CTL_ELEM_TYPE_INTEGER:
    661 			obj = new_integer(instance, snd_ctl_elem_value_get_integer(value, idx));
    662 			break;
    663 		case SND_CTL_ELEM_TYPE_INTEGER64:
    664 			obj = new_integer(instance, snd_ctl_elem_value_get_integer64(value, idx));
    665 			break;
    666 		case SND_CTL_ELEM_TYPE_ENUMERATED:
    667 			obj = new_integer(instance, snd_ctl_elem_value_get_enumerated(value, idx));
    668 			break;
    669 		case SND_CTL_ELEM_TYPE_BYTES:
    670 			obj = new_integer(instance, snd_ctl_elem_value_get_byte(value, idx));
    671 			break;
    672 		default:
    673 			obj = NULL;
    674 			break;
    675 		}
    676 		if (idx == 0) {
    677 			p1 = add_cons2(instance, lexpr->value.c.cdr, 0, obj);
    678 		} else {
    679 			p1 = add_cons2(instance, p1, 1, obj);
    680 		}
    681 	}
    682 	if (p1 == NULL) {
    683 		delete_tree(instance, lexpr);
    684 		return &alsa_lisp_nil;
    685 	}
    686 	return lexpr;
    687 }
    688 
    689 static struct alisp_object * FA_hctl_elem_write(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args)
    690 {
    691 	snd_hctl_elem_t *handle;
    692 	struct alisp_object * p1 = NULL, * obj;
    693 	snd_ctl_elem_info_t *info;
    694 	snd_ctl_elem_value_t *value;
    695 	snd_ctl_elem_type_t type;
    696 	unsigned int idx, count;
    697 	int err;
    698 
    699 	p1 = car(cdr(args));
    700 	obj = eval(instance, car(args));
    701 	delete_tree(instance, cdr(cdr(args)));
    702 	delete_object(instance, cdr(args));
    703 	delete_object(instance, args);
    704 	handle = (snd_hctl_elem_t *)get_ptr(instance, obj, item->prefix);
    705 	if (handle == NULL) {
    706 		delete_tree(instance, p1);
    707 		return &alsa_lisp_nil;
    708 	}
    709 	snd_ctl_elem_info_alloca(&info);
    710 	snd_ctl_elem_value_alloca(&value);
    711 	err = snd_hctl_elem_info(handle, info);
    712 	if (err < 0) {
    713 		delete_tree(instance, p1);
    714 		return new_integer(instance, err);
    715 	}
    716 	type = snd_ctl_elem_info_get_type(info);
    717 	count = snd_ctl_elem_info_get_count(info);
    718 	if (type == SND_CTL_ELEM_TYPE_IEC958) {
    719 		count = sizeof(snd_aes_iec958_t);
    720 		type = SND_CTL_ELEM_TYPE_BYTES;
    721 	}
    722 	idx = -1;
    723 	do {
    724 		if (++idx >= count) {
    725 			delete_tree(instance, p1);
    726 			break;
    727 		}
    728 		obj = car(p1);
    729 		switch (type) {
    730 		case SND_CTL_ELEM_TYPE_BOOLEAN:
    731 			snd_ctl_elem_value_set_boolean(value, idx, get_integer(obj));
    732 			break;
    733 		case SND_CTL_ELEM_TYPE_INTEGER:
    734 			snd_ctl_elem_value_set_integer(value, idx, get_integer(obj));
    735 			break;
    736 		case SND_CTL_ELEM_TYPE_INTEGER64:
    737 			snd_ctl_elem_value_set_integer64(value, idx, get_integer(obj));
    738 			break;
    739 		case SND_CTL_ELEM_TYPE_ENUMERATED:
    740 			snd_ctl_elem_value_set_enumerated(value, idx, get_integer(obj));
    741 			break;
    742 		case SND_CTL_ELEM_TYPE_BYTES:
    743 			snd_ctl_elem_value_set_byte(value, idx, get_integer(obj));
    744 			break;
    745 		default:
    746 			break;
    747 		}
    748 		delete_tree(instance, obj);
    749 		p1 = cdr(obj = p1);
    750 		delete_object(instance, obj);
    751 	} while (p1 != &alsa_lisp_nil);
    752 	err = snd_hctl_elem_write(handle, value);
    753 	return new_integer(instance, err);
    754 }
    755 
    756 static struct alisp_object * FA_pcm_info(struct alisp_instance * instance, struct acall_table * item, struct alisp_object * args)
    757 {
    758 	snd_pcm_t *handle;
    759 	struct alisp_object * lexpr, * p1;
    760 	snd_pcm_info_t *info;
    761 	int err;
    762 
    763 	p1 = eval(instance, car(args));
    764 	delete_tree(instance, cdr(args));
    765 	delete_object(instance, args);
    766 	handle = (snd_pcm_t *)get_ptr(instance, p1, item->prefix);
    767 	if (handle == NULL)
    768 		return &alsa_lisp_nil;
    769 	snd_pcm_info_alloca(&info);
    770 	err = snd_pcm_info(handle, info);
    771 	lexpr = new_lexpr(instance, err);
    772 	if (err < 0)
    773 		return lexpr;
    774 	p1 = add_cons(instance, lexpr->value.c.cdr, 0, "card", new_integer(instance, snd_pcm_info_get_card(info)));
    775 	p1 = add_cons(instance, p1, 1, "device", new_integer(instance, snd_pcm_info_get_device(info)));
    776 	p1 = add_cons(instance, p1, 1, "subdevice", new_integer(instance, snd_pcm_info_get_subdevice(info)));
    777 	p1 = add_cons(instance, p1, 1, "id", new_string(instance, snd_pcm_info_get_id(info)));
    778 	p1 = add_cons(instance, p1, 1, "name", new_string(instance, snd_pcm_info_get_name(info)));
    779 	p1 = add_cons(instance, p1, 1, "subdevice_name", new_string(instance, snd_pcm_info_get_subdevice_name(info)));
    780 	p1 = add_cons(instance, p1, 1, "class", new_integer(instance, snd_pcm_info_get_class(info)));
    781 	p1 = add_cons(instance, p1, 1, "subclass", new_integer(instance, snd_pcm_info_get_subclass(info)));
    782 	p1 = add_cons(instance, p1, 1, "subdevices_count", new_integer(instance, snd_pcm_info_get_subdevices_count(info)));
    783 	p1 = add_cons(instance, p1, 1, "subdevices_avail", new_integer(instance, snd_pcm_info_get_subdevices_avail(info)));
    784 	//p1 = add_cons(instance, p1, 1, "sync", new_string(instance, snd_pcm_info_get_sync(info)));
    785 	return lexpr;
    786 }
    787 
    788 /*
    789  *  main code
    790  */
    791 
    792 static const struct acall_table acall_table[] = {
    793 	{ "card_get_index", &FA_int_str, (void *)snd_card_get_index, NULL },
    794 	{ "card_get_longname", &FA_int_int_strp, (void *)snd_card_get_longname, NULL },
    795 	{ "card_get_name", &FA_int_int_strp, (void *)snd_card_get_name, NULL },
    796 	{ "card_next", &FA_int_intp, (void *)&snd_card_next, NULL },
    797 	{ "ctl_card_info", &FA_card_info, NULL, "ctl" },
    798 	{ "ctl_close", &FA_int_p, (void *)&snd_ctl_close, "ctl" },
    799 	{ "ctl_open", &FA_int_pp_strp_int, (void *)&snd_ctl_open, "ctl" },
    800 	{ "hctl_close", &FA_int_p, (void *)&snd_hctl_close, "hctl" },
    801 	{ "hctl_ctl", &FA_p_p, (void *)&snd_hctl_ctl, "hctl" },
    802 	{ "hctl_elem_info", &FA_hctl_elem_info, (void *)&snd_hctl_elem_info, "hctl_elem" },
    803 	{ "hctl_elem_next", &FA_p_p, (void *)&snd_hctl_elem_next, "hctl_elem" },
    804 	{ "hctl_elem_prev", &FA_p_p, (void *)&snd_hctl_elem_prev, "hctl_elem" },
    805 	{ "hctl_elem_read", &FA_hctl_elem_read, (void *)&snd_hctl_elem_read, "hctl_elem" },
    806 	{ "hctl_elem_write", &FA_hctl_elem_write, (void *)&snd_hctl_elem_write, "hctl_elem" },
    807 	{ "hctl_find_elem", &FA_hctl_find_elem, (void *)&snd_hctl_find_elem, "hctl" },
    808 	{ "hctl_first_elem", &FA_p_p, (void *)&snd_hctl_first_elem, "hctl" },
    809 	{ "hctl_free", &FA_int_p, (void *)&snd_hctl_free, "hctl" },
    810 	{ "hctl_last_elem", &FA_p_p, (void *)&snd_hctl_last_elem, "hctl" },
    811 	{ "hctl_load", &FA_int_p, (void *)&snd_hctl_load, "hctl" },
    812 	{ "hctl_open", &FA_int_pp_strp_int, (void *)&snd_hctl_open, "hctl" },
    813 	{ "hctl_open_ctl", &FA_int_pp_p, (void *)&snd_hctl_open_ctl, "hctl" },
    814 	{ "pcm_info", &FA_pcm_info, NULL, "pcm" },
    815 	{ "pcm_name", &FA_str_p, (void *)&snd_pcm_name, "pcm" },
    816 };
    817 
    818 static int acall_compar(const void *p1, const void *p2)
    819 {
    820 	return strcmp(((struct acall_table *)p1)->name,
    821         	      ((struct acall_table *)p2)->name);
    822 }
    823 
    824 static struct alisp_object * F_acall(struct alisp_instance *instance, struct alisp_object * args)
    825 {
    826 	struct alisp_object * p1, *p2;
    827 	struct acall_table key, *item;
    828 
    829 	p1 = eval(instance, car(args));
    830 	p2 = cdr(args);
    831 	delete_object(instance, args);
    832 	if (!alisp_compare_type(p1, ALISP_OBJ_IDENTIFIER) &&
    833 	    !alisp_compare_type(p1, ALISP_OBJ_STRING)) {
    834 	    	delete_tree(instance, p2);
    835 		return &alsa_lisp_nil;
    836 	}
    837 	key.name = p1->value.s;
    838 	if ((item = bsearch(&key, acall_table,
    839 			    sizeof acall_table / sizeof acall_table[0],
    840 			    sizeof acall_table[0], acall_compar)) != NULL) {
    841 		delete_tree(instance, p1);
    842 		return item->func(instance, item, p2);
    843 	}
    844 	delete_tree(instance, p1);
    845 	delete_tree(instance, p2);
    846 	lisp_warn(instance, "acall function %s' is undefined", p1->value.s);
    847 	return &alsa_lisp_nil;
    848 }
    849 
    850 static struct alisp_object * F_ahandle(struct alisp_instance *instance, struct alisp_object * args)
    851 {
    852 	struct alisp_object *p1;
    853 
    854 	p1 = eval(instance, car(args));
    855 	delete_tree(instance, cdr(args));
    856 	delete_object(instance, args);
    857 	args = car(cdr(p1));
    858 	delete_tree(instance, cdr(cdr(p1)));
    859 	delete_object(instance, cdr(p1));
    860 	delete_tree(instance, car(p1));
    861 	delete_object(instance, p1);
    862 	return args;
    863 }
    864 
    865 static struct alisp_object * F_aerror(struct alisp_instance *instance, struct alisp_object * args)
    866 {
    867 	struct alisp_object *p1;
    868 
    869 	p1 = eval(instance, car(args));
    870 	delete_tree(instance, cdr(args));
    871 	delete_object(instance, args);
    872 	args = car(p1);
    873 	if (args == &alsa_lisp_nil) {
    874 		delete_tree(instance, p1);
    875 		return new_integer(instance, SND_ERROR_ALISP_NIL);
    876 	} else {
    877 		delete_tree(instance, cdr(p1));
    878 		delete_object(instance, p1);
    879 	}
    880 	return args;
    881 }
    882 
    883 static int common_error(snd_output_t **rout, struct alisp_instance *instance, struct alisp_object * args)
    884 {
    885 	struct alisp_object * p = args, * p1;
    886 	snd_output_t *out;
    887 	int err;
    888 
    889 	err = snd_output_buffer_open(&out);
    890 	if (err < 0) {
    891 		delete_tree(instance, args);
    892 		return err;
    893 	}
    894 
    895 	do {
    896 		p1 = eval(instance, car(p));
    897 		if (alisp_compare_type(p1, ALISP_OBJ_STRING))
    898 			snd_output_printf(out, "%s", p1->value.s);
    899 		else
    900 			princ_object(out, p1);
    901 		delete_tree(instance, p1);
    902 		p = cdr(p1 = p);
    903 		delete_object(instance, p1);
    904 	} while (p != &alsa_lisp_nil);
    905 
    906 	*rout = out;
    907 	return 0;
    908 }
    909 
    910 static struct alisp_object * F_snderr(struct alisp_instance *instance, struct alisp_object * args)
    911 {
    912 	snd_output_t *out;
    913 	char *str;
    914 
    915 	if (common_error(&out, instance, args) < 0)
    916 		return &alsa_lisp_nil;
    917 	snd_output_buffer_string(out, &str);
    918 	SNDERR(str);
    919 	snd_output_close(out);
    920 	return &alsa_lisp_t;
    921 }
    922 
    923 static struct alisp_object * F_syserr(struct alisp_instance *instance, struct alisp_object * args)
    924 {
    925 	snd_output_t *out;
    926 	char *str;
    927 
    928 	if (common_error(&out, instance, args) < 0)
    929 		return &alsa_lisp_nil;
    930 	snd_output_buffer_string(out, &str);
    931 	SYSERR(str);
    932 	snd_output_close(out);
    933 	return &alsa_lisp_t;
    934 }
    935 
    936 static const struct intrinsic snd_intrinsics[] = {
    937 	{ "Acall", F_acall },
    938 	{ "Aerror", F_aerror },
    939 	{ "Ahandle", F_ahandle },
    940 	{ "Aresult", F_ahandle },
    941 	{ "Asnderr", F_snderr },
    942 	{ "Asyserr", F_syserr }
    943 };
    944