1/*
2 *   Generic Instrument routines for ALSA sequencer
3 *   Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
4 *
5 *   This program is free software; you can redistribute it and/or modify
6 *   it under the terms of the GNU General Public License as published by
7 *   the Free Software Foundation; either version 2 of the License, or
8 *   (at your option) any later version.
9 *
10 *   This program is distributed in the hope that it will be useful,
11 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
12 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 *   GNU General Public License for more details.
14 *
15 *   You should have received a copy of the GNU General Public License
16 *   along with this program; if not, write to the Free Software
17 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18 *
19 */
20
21#include <sound/driver.h>
22#include <linux/init.h>
23#include <linux/slab.h>
24#include <sound/core.h>
25#include "seq_clientmgr.h"
26#include <sound/seq_instr.h>
27#include <sound/initval.h>
28
29MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
30MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer instrument library.");
31MODULE_LICENSE("GPL");
32
33
34static void snd_instr_lock_ops(struct snd_seq_kinstr_list *list)
35{
36	if (!(list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT)) {
37		spin_lock_irqsave(&list->ops_lock, list->ops_flags);
38	} else {
39		mutex_lock(&list->ops_mutex);
40	}
41}
42
43static void snd_instr_unlock_ops(struct snd_seq_kinstr_list *list)
44{
45	if (!(list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT)) {
46		spin_unlock_irqrestore(&list->ops_lock, list->ops_flags);
47	} else {
48		mutex_unlock(&list->ops_mutex);
49	}
50}
51
52static struct snd_seq_kinstr *snd_seq_instr_new(int add_len, int atomic)
53{
54	struct snd_seq_kinstr *instr;
55
56	instr = kzalloc(sizeof(struct snd_seq_kinstr) + add_len, atomic ? GFP_ATOMIC : GFP_KERNEL);
57	if (instr == NULL)
58		return NULL;
59	instr->add_len = add_len;
60	return instr;
61}
62
63static int snd_seq_instr_free(struct snd_seq_kinstr *instr, int atomic)
64{
65	int result = 0;
66
67	if (instr == NULL)
68		return -EINVAL;
69	if (instr->ops && instr->ops->remove)
70		result = instr->ops->remove(instr->ops->private_data, instr, 1);
71	if (!result)
72		kfree(instr);
73	return result;
74}
75
76struct snd_seq_kinstr_list *snd_seq_instr_list_new(void)
77{
78	struct snd_seq_kinstr_list *list;
79
80	list = kzalloc(sizeof(struct snd_seq_kinstr_list), GFP_KERNEL);
81	if (list == NULL)
82		return NULL;
83	spin_lock_init(&list->lock);
84	spin_lock_init(&list->ops_lock);
85	mutex_init(&list->ops_mutex);
86	list->owner = -1;
87	return list;
88}
89
90void snd_seq_instr_list_free(struct snd_seq_kinstr_list **list_ptr)
91{
92	struct snd_seq_kinstr_list *list;
93	struct snd_seq_kinstr *instr;
94	struct snd_seq_kcluster *cluster;
95	int idx;
96	unsigned long flags;
97
98	if (list_ptr == NULL)
99		return;
100	list = *list_ptr;
101	*list_ptr = NULL;
102	if (list == NULL)
103		return;
104
105	for (idx = 0; idx < SNDRV_SEQ_INSTR_HASH_SIZE; idx++) {
106		while ((instr = list->hash[idx]) != NULL) {
107			list->hash[idx] = instr->next;
108			list->count--;
109			spin_lock_irqsave(&list->lock, flags);
110			while (instr->use) {
111				spin_unlock_irqrestore(&list->lock, flags);
112				schedule_timeout_interruptible(1);
113				spin_lock_irqsave(&list->lock, flags);
114			}
115			spin_unlock_irqrestore(&list->lock, flags);
116			if (snd_seq_instr_free(instr, 0)<0)
117				snd_printk(KERN_WARNING "instrument free problem\n");
118		}
119		while ((cluster = list->chash[idx]) != NULL) {
120			list->chash[idx] = cluster->next;
121			list->ccount--;
122			kfree(cluster);
123		}
124	}
125	kfree(list);
126}
127
128static int instr_free_compare(struct snd_seq_kinstr *instr,
129			      struct snd_seq_instr_header *ifree,
130			      unsigned int client)
131{
132	switch (ifree->cmd) {
133	case SNDRV_SEQ_INSTR_FREE_CMD_ALL:
134		/* all, except private for other clients */
135		if ((instr->instr.std & 0xff000000) == 0)
136			return 0;
137		if (((instr->instr.std >> 24) & 0xff) == client)
138			return 0;
139		return 1;
140	case SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE:
141		/* all my private instruments */
142		if ((instr->instr.std & 0xff000000) == 0)
143			return 1;
144		if (((instr->instr.std >> 24) & 0xff) == client)
145			return 0;
146		return 1;
147	case SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER:
148		/* all my private instruments */
149		if ((instr->instr.std & 0xff000000) == 0) {
150			if (instr->instr.cluster == ifree->id.cluster)
151				return 0;
152			return 1;
153		}
154		if (((instr->instr.std >> 24) & 0xff) == client) {
155			if (instr->instr.cluster == ifree->id.cluster)
156				return 0;
157		}
158		return 1;
159	}
160	return 1;
161}
162
163int snd_seq_instr_list_free_cond(struct snd_seq_kinstr_list *list,
164			         struct snd_seq_instr_header *ifree,
165			         int client,
166			         int atomic)
167{
168	struct snd_seq_kinstr *instr, *prev, *next, *flist;
169	int idx;
170	unsigned long flags;
171
172	snd_instr_lock_ops(list);
173	for (idx = 0; idx < SNDRV_SEQ_INSTR_HASH_SIZE; idx++) {
174		spin_lock_irqsave(&list->lock, flags);
175		instr = list->hash[idx];
176		prev = flist = NULL;
177		while (instr) {
178			while (instr && instr_free_compare(instr, ifree, (unsigned int)client)) {
179				prev = instr;
180				instr = instr->next;
181			}
182			if (instr == NULL)
183				continue;
184			if (instr->ops && instr->ops->notify)
185				instr->ops->notify(instr->ops->private_data, instr, SNDRV_SEQ_INSTR_NOTIFY_REMOVE);
186			next = instr->next;
187			if (prev == NULL) {
188				list->hash[idx] = next;
189			} else {
190				prev->next = next;
191			}
192			list->count--;
193			instr->next = flist;
194			flist = instr;
195			instr = next;
196		}
197		spin_unlock_irqrestore(&list->lock, flags);
198		while (flist) {
199			instr = flist;
200			flist = instr->next;
201			while (instr->use)
202				schedule_timeout_interruptible(1);
203			if (snd_seq_instr_free(instr, atomic)<0)
204				snd_printk(KERN_WARNING "instrument free problem\n");
205			instr = next;
206		}
207	}
208	snd_instr_unlock_ops(list);
209	return 0;
210}
211
212static int compute_hash_instr_key(struct snd_seq_instr *instr)
213{
214	int result;
215
216	result = instr->bank | (instr->prg << 16);
217	result += result >> 24;
218	result += result >> 16;
219	result += result >> 8;
220	return result & (SNDRV_SEQ_INSTR_HASH_SIZE-1);
221}
222
223
224static int compare_instr(struct snd_seq_instr *i1, struct snd_seq_instr *i2, int exact)
225{
226	if (exact) {
227		if (i1->cluster != i2->cluster ||
228		    i1->bank != i2->bank ||
229		    i1->prg != i2->prg)
230			return 1;
231		if ((i1->std & 0xff000000) != (i2->std & 0xff000000))
232			return 1;
233		if (!(i1->std & i2->std))
234			return 1;
235		return 0;
236	} else {
237		unsigned int client_check;
238
239		if (i2->cluster && i1->cluster != i2->cluster)
240			return 1;
241		client_check = i2->std & 0xff000000;
242		if (client_check) {
243			if ((i1->std & 0xff000000) != client_check)
244				return 1;
245		} else {
246			if ((i1->std & i2->std) != i2->std)
247				return 1;
248		}
249		return i1->bank != i2->bank || i1->prg != i2->prg;
250	}
251}
252
253struct snd_seq_kinstr *snd_seq_instr_find(struct snd_seq_kinstr_list *list,
254					  struct snd_seq_instr *instr,
255					  int exact,
256					  int follow_alias)
257{
258	unsigned long flags;
259	int depth = 0;
260	struct snd_seq_kinstr *result;
261
262	if (list == NULL || instr == NULL)
263		return NULL;
264	spin_lock_irqsave(&list->lock, flags);
265      __again:
266	result = list->hash[compute_hash_instr_key(instr)];
267	while (result) {
268		if (!compare_instr(&result->instr, instr, exact)) {
269			if (follow_alias && (result->type == SNDRV_SEQ_INSTR_ATYPE_ALIAS)) {
270				instr = (struct snd_seq_instr *)KINSTR_DATA(result);
271				if (++depth > 10)
272					goto __not_found;
273				goto __again;
274			}
275			result->use++;
276			spin_unlock_irqrestore(&list->lock, flags);
277			return result;
278		}
279		result = result->next;
280	}
281      __not_found:
282	spin_unlock_irqrestore(&list->lock, flags);
283	return NULL;
284}
285
286void snd_seq_instr_free_use(struct snd_seq_kinstr_list *list,
287			    struct snd_seq_kinstr *instr)
288{
289	unsigned long flags;
290
291	if (list == NULL || instr == NULL)
292		return;
293	spin_lock_irqsave(&list->lock, flags);
294	if (instr->use <= 0) {
295		snd_printk(KERN_ERR "free_use: fatal!!! use = %i, name = '%s'\n", instr->use, instr->name);
296	} else {
297		instr->use--;
298	}
299	spin_unlock_irqrestore(&list->lock, flags);
300}
301
302static struct snd_seq_kinstr_ops *instr_ops(struct snd_seq_kinstr_ops *ops,
303					    char *instr_type)
304{
305	while (ops) {
306		if (!strcmp(ops->instr_type, instr_type))
307			return ops;
308		ops = ops->next;
309	}
310	return NULL;
311}
312
313static int instr_result(struct snd_seq_event *ev,
314			int type, int result,
315			int atomic)
316{
317	struct snd_seq_event sev;
318
319	memset(&sev, 0, sizeof(sev));
320	sev.type = SNDRV_SEQ_EVENT_RESULT;
321	sev.flags = SNDRV_SEQ_TIME_STAMP_REAL | SNDRV_SEQ_EVENT_LENGTH_FIXED |
322	            SNDRV_SEQ_PRIORITY_NORMAL;
323	sev.source = ev->dest;
324	sev.dest = ev->source;
325	sev.data.result.event = type;
326	sev.data.result.result = result;
327	return snd_seq_kernel_client_dispatch(sev.source.client, &sev, atomic, 0);
328}
329
330static int instr_begin(struct snd_seq_kinstr_ops *ops,
331		       struct snd_seq_kinstr_list *list,
332		       struct snd_seq_event *ev,
333		       int atomic, int hop)
334{
335	unsigned long flags;
336
337	spin_lock_irqsave(&list->lock, flags);
338	if (list->owner >= 0 && list->owner != ev->source.client) {
339		spin_unlock_irqrestore(&list->lock, flags);
340		return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_BEGIN, -EBUSY, atomic);
341	}
342	list->owner = ev->source.client;
343	spin_unlock_irqrestore(&list->lock, flags);
344	return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_BEGIN, 0, atomic);
345}
346
347static int instr_end(struct snd_seq_kinstr_ops *ops,
348		     struct snd_seq_kinstr_list *list,
349		     struct snd_seq_event *ev,
350		     int atomic, int hop)
351{
352	unsigned long flags;
353
354	/* TODO: timeout handling */
355	spin_lock_irqsave(&list->lock, flags);
356	if (list->owner == ev->source.client) {
357		list->owner = -1;
358		spin_unlock_irqrestore(&list->lock, flags);
359		return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_END, 0, atomic);
360	}
361	spin_unlock_irqrestore(&list->lock, flags);
362	return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_END, -EINVAL, atomic);
363}
364
365static int instr_info(struct snd_seq_kinstr_ops *ops,
366		      struct snd_seq_kinstr_list *list,
367		      struct snd_seq_event *ev,
368		      int atomic, int hop)
369{
370	return -ENXIO;
371}
372
373static int instr_format_info(struct snd_seq_kinstr_ops *ops,
374			     struct snd_seq_kinstr_list *list,
375			     struct snd_seq_event *ev,
376			     int atomic, int hop)
377{
378	return -ENXIO;
379}
380
381static int instr_reset(struct snd_seq_kinstr_ops *ops,
382		       struct snd_seq_kinstr_list *list,
383		       struct snd_seq_event *ev,
384		       int atomic, int hop)
385{
386	return -ENXIO;
387}
388
389static int instr_status(struct snd_seq_kinstr_ops *ops,
390			struct snd_seq_kinstr_list *list,
391			struct snd_seq_event *ev,
392			int atomic, int hop)
393{
394	return -ENXIO;
395}
396
397static int instr_put(struct snd_seq_kinstr_ops *ops,
398		     struct snd_seq_kinstr_list *list,
399		     struct snd_seq_event *ev,
400		     int atomic, int hop)
401{
402	unsigned long flags;
403	struct snd_seq_instr_header put;
404	struct snd_seq_kinstr *instr;
405	int result = -EINVAL, len, key;
406
407	if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARUSR)
408		goto __return;
409
410	if (ev->data.ext.len < sizeof(struct snd_seq_instr_header))
411		goto __return;
412	if (copy_from_user(&put, (void __user *)ev->data.ext.ptr,
413			   sizeof(struct snd_seq_instr_header))) {
414		result = -EFAULT;
415		goto __return;
416	}
417	snd_instr_lock_ops(list);
418	if (put.id.instr.std & 0xff000000) {	/* private instrument */
419		put.id.instr.std &= 0x00ffffff;
420		put.id.instr.std |= (unsigned int)ev->source.client << 24;
421	}
422	if ((instr = snd_seq_instr_find(list, &put.id.instr, 1, 0))) {
423		snd_seq_instr_free_use(list, instr);
424		snd_instr_unlock_ops(list);
425		result = -EBUSY;
426		goto __return;
427	}
428	ops = instr_ops(ops, put.data.data.format);
429	if (ops == NULL) {
430		snd_instr_unlock_ops(list);
431		goto __return;
432	}
433	len = ops->add_len;
434	if (put.data.type == SNDRV_SEQ_INSTR_ATYPE_ALIAS)
435		len = sizeof(struct snd_seq_instr);
436	instr = snd_seq_instr_new(len, atomic);
437	if (instr == NULL) {
438		snd_instr_unlock_ops(list);
439		result = -ENOMEM;
440		goto __return;
441	}
442	instr->ops = ops;
443	instr->instr = put.id.instr;
444	strlcpy(instr->name, put.data.name, sizeof(instr->name));
445	instr->type = put.data.type;
446	if (instr->type == SNDRV_SEQ_INSTR_ATYPE_DATA) {
447		result = ops->put(ops->private_data,
448				  instr,
449				  (void __user *)ev->data.ext.ptr + sizeof(struct snd_seq_instr_header),
450				  ev->data.ext.len - sizeof(struct snd_seq_instr_header),
451				  atomic,
452				  put.cmd);
453		if (result < 0) {
454			snd_seq_instr_free(instr, atomic);
455			snd_instr_unlock_ops(list);
456			goto __return;
457		}
458	}
459	key = compute_hash_instr_key(&instr->instr);
460	spin_lock_irqsave(&list->lock, flags);
461	instr->next = list->hash[key];
462	list->hash[key] = instr;
463	list->count++;
464	spin_unlock_irqrestore(&list->lock, flags);
465	snd_instr_unlock_ops(list);
466	result = 0;
467      __return:
468	instr_result(ev, SNDRV_SEQ_EVENT_INSTR_PUT, result, atomic);
469	return result;
470}
471
472static int instr_get(struct snd_seq_kinstr_ops *ops,
473		     struct snd_seq_kinstr_list *list,
474		     struct snd_seq_event *ev,
475		     int atomic, int hop)
476{
477	return -ENXIO;
478}
479
480static int instr_free(struct snd_seq_kinstr_ops *ops,
481		      struct snd_seq_kinstr_list *list,
482		      struct snd_seq_event *ev,
483		      int atomic, int hop)
484{
485	struct snd_seq_instr_header ifree;
486	struct snd_seq_kinstr *instr, *prev;
487	int result = -EINVAL;
488	unsigned long flags;
489	unsigned int hash;
490
491	if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARUSR)
492		goto __return;
493
494	if (ev->data.ext.len < sizeof(struct snd_seq_instr_header))
495		goto __return;
496	if (copy_from_user(&ifree, (void __user *)ev->data.ext.ptr,
497			   sizeof(struct snd_seq_instr_header))) {
498		result = -EFAULT;
499		goto __return;
500	}
501	if (ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_ALL ||
502	    ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE ||
503	    ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER) {
504	    	result = snd_seq_instr_list_free_cond(list, &ifree, ev->dest.client, atomic);
505	    	goto __return;
506	}
507	if (ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_SINGLE) {
508		if (ifree.id.instr.std & 0xff000000) {
509			ifree.id.instr.std &= 0x00ffffff;
510			ifree.id.instr.std |= (unsigned int)ev->source.client << 24;
511		}
512		hash = compute_hash_instr_key(&ifree.id.instr);
513		snd_instr_lock_ops(list);
514		spin_lock_irqsave(&list->lock, flags);
515		instr = list->hash[hash];
516		prev = NULL;
517		while (instr) {
518			if (!compare_instr(&instr->instr, &ifree.id.instr, 1))
519				goto __free_single;
520			prev = instr;
521			instr = instr->next;
522		}
523		result = -ENOENT;
524		spin_unlock_irqrestore(&list->lock, flags);
525		snd_instr_unlock_ops(list);
526		goto __return;
527
528	      __free_single:
529		if (prev) {
530			prev->next = instr->next;
531		} else {
532			list->hash[hash] = instr->next;
533		}
534		if (instr->ops && instr->ops->notify)
535			instr->ops->notify(instr->ops->private_data, instr,
536					   SNDRV_SEQ_INSTR_NOTIFY_REMOVE);
537		while (instr->use) {
538			spin_unlock_irqrestore(&list->lock, flags);
539			schedule_timeout_interruptible(1);
540			spin_lock_irqsave(&list->lock, flags);
541		}
542		spin_unlock_irqrestore(&list->lock, flags);
543		result = snd_seq_instr_free(instr, atomic);
544		snd_instr_unlock_ops(list);
545		goto __return;
546	}
547
548      __return:
549	instr_result(ev, SNDRV_SEQ_EVENT_INSTR_FREE, result, atomic);
550	return result;
551}
552
553static int instr_list(struct snd_seq_kinstr_ops *ops,
554		      struct snd_seq_kinstr_list *list,
555		      struct snd_seq_event *ev,
556		      int atomic, int hop)
557{
558	return -ENXIO;
559}
560
561static int instr_cluster(struct snd_seq_kinstr_ops *ops,
562			 struct snd_seq_kinstr_list *list,
563			 struct snd_seq_event *ev,
564			 int atomic, int hop)
565{
566	return -ENXIO;
567}
568
569int snd_seq_instr_event(struct snd_seq_kinstr_ops *ops,
570			struct snd_seq_kinstr_list *list,
571			struct snd_seq_event *ev,
572			int client,
573			int atomic,
574			int hop)
575{
576	int direct = 0;
577
578	snd_assert(ops != NULL && list != NULL && ev != NULL, return -EINVAL);
579	if (snd_seq_ev_is_direct(ev)) {
580		direct = 1;
581		switch (ev->type) {
582		case SNDRV_SEQ_EVENT_INSTR_BEGIN:
583			return instr_begin(ops, list, ev, atomic, hop);
584		case SNDRV_SEQ_EVENT_INSTR_END:
585			return instr_end(ops, list, ev, atomic, hop);
586		}
587	}
588	if ((list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT) && !direct)
589		return -EINVAL;
590	switch (ev->type) {
591	case SNDRV_SEQ_EVENT_INSTR_INFO:
592		return instr_info(ops, list, ev, atomic, hop);
593	case SNDRV_SEQ_EVENT_INSTR_FINFO:
594		return instr_format_info(ops, list, ev, atomic, hop);
595	case SNDRV_SEQ_EVENT_INSTR_RESET:
596		return instr_reset(ops, list, ev, atomic, hop);
597	case SNDRV_SEQ_EVENT_INSTR_STATUS:
598		return instr_status(ops, list, ev, atomic, hop);
599	case SNDRV_SEQ_EVENT_INSTR_PUT:
600		return instr_put(ops, list, ev, atomic, hop);
601	case SNDRV_SEQ_EVENT_INSTR_GET:
602		return instr_get(ops, list, ev, atomic, hop);
603	case SNDRV_SEQ_EVENT_INSTR_FREE:
604		return instr_free(ops, list, ev, atomic, hop);
605	case SNDRV_SEQ_EVENT_INSTR_LIST:
606		return instr_list(ops, list, ev, atomic, hop);
607	case SNDRV_SEQ_EVENT_INSTR_CLUSTER:
608		return instr_cluster(ops, list, ev, atomic, hop);
609	}
610	return -EINVAL;
611}
612
613/*
614 *  Init part
615 */
616
617static int __init alsa_seq_instr_init(void)
618{
619	return 0;
620}
621
622static void __exit alsa_seq_instr_exit(void)
623{
624}
625
626module_init(alsa_seq_instr_init)
627module_exit(alsa_seq_instr_exit)
628
629EXPORT_SYMBOL(snd_seq_instr_list_new);
630EXPORT_SYMBOL(snd_seq_instr_list_free);
631EXPORT_SYMBOL(snd_seq_instr_list_free_cond);
632EXPORT_SYMBOL(snd_seq_instr_find);
633EXPORT_SYMBOL(snd_seq_instr_free_use);
634EXPORT_SYMBOL(snd_seq_instr_event);
635