xref: /openbmc/linux/sound/synth/emux/soundfont.c (revision f91ca89e924eb287915522664a31afc71a49c05b)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  Soundfont generic routines.
41da177e4SLinus Torvalds  *	It is intended that these should be used by any driver that is willing
51da177e4SLinus Torvalds  *	to accept soundfont patches.
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  *  Copyright (C) 1999 Steve Ratcliffe
81da177e4SLinus Torvalds  *  Copyright (c) 1999-2000 Takashi Iwai <tiwai@suse.de>
91da177e4SLinus Torvalds  */
101da177e4SLinus Torvalds /*
111da177e4SLinus Torvalds  * Deal with reading in of a soundfont.  Code follows the OSS way
121da177e4SLinus Torvalds  * of doing things so that the old sfxload utility can be used.
131da177e4SLinus Torvalds  * Everything may change when there is an alsa way of doing things.
141da177e4SLinus Torvalds  */
15976412fbSTakashi Iwai #include <linux/uaccess.h>
161da177e4SLinus Torvalds #include <linux/slab.h>
17d81a6d71SPaul Gortmaker #include <linux/export.h>
181da177e4SLinus Torvalds #include <sound/core.h>
191da177e4SLinus Torvalds #include <sound/soundfont.h>
201da177e4SLinus Torvalds #include <sound/seq_oss_legacy.h>
211da177e4SLinus Torvalds 
221da177e4SLinus Torvalds /* Prototypes for static functions */
231da177e4SLinus Torvalds 
2403da312aSTakashi Iwai static int open_patch(struct snd_sf_list *sflist, const char __user *data,
2503da312aSTakashi Iwai 		      int count, int client);
2603da312aSTakashi Iwai static struct snd_soundfont *newsf(struct snd_sf_list *sflist, int type, char *name);
2703da312aSTakashi Iwai static int is_identical_font(struct snd_soundfont *sf, int type, unsigned char *name);
2803da312aSTakashi Iwai static int close_patch(struct snd_sf_list *sflist);
2903da312aSTakashi Iwai static int probe_data(struct snd_sf_list *sflist, int sample_id);
3003da312aSTakashi Iwai static void set_zone_counter(struct snd_sf_list *sflist,
3103da312aSTakashi Iwai 			     struct snd_soundfont *sf, struct snd_sf_zone *zp);
3203da312aSTakashi Iwai static struct snd_sf_zone *sf_zone_new(struct snd_sf_list *sflist,
3303da312aSTakashi Iwai 				       struct snd_soundfont *sf);
3403da312aSTakashi Iwai static void set_sample_counter(struct snd_sf_list *sflist,
3503da312aSTakashi Iwai 			       struct snd_soundfont *sf, struct snd_sf_sample *sp);
3603da312aSTakashi Iwai static struct snd_sf_sample *sf_sample_new(struct snd_sf_list *sflist,
3703da312aSTakashi Iwai 					   struct snd_soundfont *sf);
3803da312aSTakashi Iwai static void sf_sample_delete(struct snd_sf_list *sflist,
3903da312aSTakashi Iwai 			     struct snd_soundfont *sf, struct snd_sf_sample *sp);
4003da312aSTakashi Iwai static int load_map(struct snd_sf_list *sflist, const void __user *data, int count);
4103da312aSTakashi Iwai static int load_info(struct snd_sf_list *sflist, const void __user *data, long count);
4203da312aSTakashi Iwai static int remove_info(struct snd_sf_list *sflist, struct snd_soundfont *sf,
4303da312aSTakashi Iwai 		       int bank, int instr);
4403da312aSTakashi Iwai static void init_voice_info(struct soundfont_voice_info *avp);
4503da312aSTakashi Iwai static void init_voice_parm(struct soundfont_voice_parm *pp);
4603da312aSTakashi Iwai static struct snd_sf_sample *set_sample(struct snd_soundfont *sf,
4703da312aSTakashi Iwai 					struct soundfont_voice_info *avp);
4803da312aSTakashi Iwai static struct snd_sf_sample *find_sample(struct snd_soundfont *sf, int sample_id);
4903da312aSTakashi Iwai static int load_data(struct snd_sf_list *sflist, const void __user *data, long count);
5003da312aSTakashi Iwai static void rebuild_presets(struct snd_sf_list *sflist);
5103da312aSTakashi Iwai static void add_preset(struct snd_sf_list *sflist, struct snd_sf_zone *cur);
5203da312aSTakashi Iwai static void delete_preset(struct snd_sf_list *sflist, struct snd_sf_zone *zp);
5303da312aSTakashi Iwai static struct snd_sf_zone *search_first_zone(struct snd_sf_list *sflist,
5403da312aSTakashi Iwai 					     int bank, int preset, int key);
5503da312aSTakashi Iwai static int search_zones(struct snd_sf_list *sflist, int *notep, int vel,
5603da312aSTakashi Iwai 			int preset, int bank, struct snd_sf_zone **table,
5703da312aSTakashi Iwai 			int max_layers, int level);
581da177e4SLinus Torvalds static int get_index(int bank, int instr, int key);
5903da312aSTakashi Iwai static void snd_sf_init(struct snd_sf_list *sflist);
6003da312aSTakashi Iwai static void snd_sf_clear(struct snd_sf_list *sflist);
611da177e4SLinus Torvalds 
621da177e4SLinus Torvalds /*
631da177e4SLinus Torvalds  * lock access to sflist
641da177e4SLinus Torvalds  */
651da177e4SLinus Torvalds static void
lock_preset(struct snd_sf_list * sflist)6603da312aSTakashi Iwai lock_preset(struct snd_sf_list *sflist)
671da177e4SLinus Torvalds {
681da177e4SLinus Torvalds 	unsigned long flags;
69ef9f0a42SIngo Molnar 	mutex_lock(&sflist->presets_mutex);
701da177e4SLinus Torvalds 	spin_lock_irqsave(&sflist->lock, flags);
711da177e4SLinus Torvalds 	sflist->presets_locked = 1;
721da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sflist->lock, flags);
731da177e4SLinus Torvalds }
741da177e4SLinus Torvalds 
751da177e4SLinus Torvalds 
761da177e4SLinus Torvalds /*
771da177e4SLinus Torvalds  * remove lock
781da177e4SLinus Torvalds  */
791da177e4SLinus Torvalds static void
unlock_preset(struct snd_sf_list * sflist)8003da312aSTakashi Iwai unlock_preset(struct snd_sf_list *sflist)
811da177e4SLinus Torvalds {
821da177e4SLinus Torvalds 	unsigned long flags;
831da177e4SLinus Torvalds 	spin_lock_irqsave(&sflist->lock, flags);
841da177e4SLinus Torvalds 	sflist->presets_locked = 0;
851da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sflist->lock, flags);
86ef9f0a42SIngo Molnar 	mutex_unlock(&sflist->presets_mutex);
871da177e4SLinus Torvalds }
881da177e4SLinus Torvalds 
891da177e4SLinus Torvalds 
901da177e4SLinus Torvalds /*
911da177e4SLinus Torvalds  * close the patch if the patch was opened by this client.
921da177e4SLinus Torvalds  */
931da177e4SLinus Torvalds int
snd_soundfont_close_check(struct snd_sf_list * sflist,int client)9403da312aSTakashi Iwai snd_soundfont_close_check(struct snd_sf_list *sflist, int client)
951da177e4SLinus Torvalds {
961da177e4SLinus Torvalds 	unsigned long flags;
971da177e4SLinus Torvalds 	spin_lock_irqsave(&sflist->lock, flags);
981da177e4SLinus Torvalds 	if (sflist->open_client == client)  {
991da177e4SLinus Torvalds 		spin_unlock_irqrestore(&sflist->lock, flags);
1001da177e4SLinus Torvalds 		return close_patch(sflist);
1011da177e4SLinus Torvalds 	}
1021da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sflist->lock, flags);
1031da177e4SLinus Torvalds 	return 0;
1041da177e4SLinus Torvalds }
1051da177e4SLinus Torvalds 
1061da177e4SLinus Torvalds 
1071da177e4SLinus Torvalds /*
1081da177e4SLinus Torvalds  * Deal with a soundfont patch.  Any driver could use these routines
1091da177e4SLinus Torvalds  * although it was designed for the AWE64.
1101da177e4SLinus Torvalds  *
111ced7c287Sgushengxian  * The sample_write and callargs parameters allow a callback into
1121da177e4SLinus Torvalds  * the actual driver to write sample data to the board or whatever
1131da177e4SLinus Torvalds  * it wants to do with it.
1141da177e4SLinus Torvalds  */
1151da177e4SLinus Torvalds int
snd_soundfont_load(struct snd_sf_list * sflist,const void __user * data,long count,int client)11603da312aSTakashi Iwai snd_soundfont_load(struct snd_sf_list *sflist, const void __user *data,
11703da312aSTakashi Iwai 		   long count, int client)
1181da177e4SLinus Torvalds {
11903da312aSTakashi Iwai 	struct soundfont_patch_info patch;
1201da177e4SLinus Torvalds 	unsigned long flags;
1211da177e4SLinus Torvalds 	int  rc;
1221da177e4SLinus Torvalds 
1231da177e4SLinus Torvalds 	if (count < (long)sizeof(patch)) {
12442b0158bSTakashi Iwai 		snd_printk(KERN_ERR "patch record too small %ld\n", count);
1251da177e4SLinus Torvalds 		return -EINVAL;
1261da177e4SLinus Torvalds 	}
1271da177e4SLinus Torvalds 	if (copy_from_user(&patch, data, sizeof(patch)))
1281da177e4SLinus Torvalds 		return -EFAULT;
1291da177e4SLinus Torvalds 
1301da177e4SLinus Torvalds 	count -= sizeof(patch);
1311da177e4SLinus Torvalds 	data += sizeof(patch);
1321da177e4SLinus Torvalds 
1331da177e4SLinus Torvalds 	if (patch.key != SNDRV_OSS_SOUNDFONT_PATCH) {
13442b0158bSTakashi Iwai 		snd_printk(KERN_ERR "The wrong kind of patch %x\n", patch.key);
1351da177e4SLinus Torvalds 		return -EINVAL;
1361da177e4SLinus Torvalds 	}
1371da177e4SLinus Torvalds 	if (count < patch.len) {
13842b0158bSTakashi Iwai 		snd_printk(KERN_ERR "Patch too short %ld, need %d\n",
13942b0158bSTakashi Iwai 			   count, patch.len);
1401da177e4SLinus Torvalds 		return -EINVAL;
1411da177e4SLinus Torvalds 	}
1421da177e4SLinus Torvalds 	if (patch.len < 0) {
14342b0158bSTakashi Iwai 		snd_printk(KERN_ERR "poor length %d\n", patch.len);
1441da177e4SLinus Torvalds 		return -EINVAL;
1451da177e4SLinus Torvalds 	}
1461da177e4SLinus Torvalds 
1471da177e4SLinus Torvalds 	if (patch.type == SNDRV_SFNT_OPEN_PATCH) {
1481da177e4SLinus Torvalds 		/* grab sflist to open */
1491da177e4SLinus Torvalds 		lock_preset(sflist);
1501da177e4SLinus Torvalds 		rc = open_patch(sflist, data, count, client);
1511da177e4SLinus Torvalds 		unlock_preset(sflist);
1521da177e4SLinus Torvalds 		return rc;
1531da177e4SLinus Torvalds 	}
1541da177e4SLinus Torvalds 
1551da177e4SLinus Torvalds 	/* check if other client already opened patch */
1561da177e4SLinus Torvalds 	spin_lock_irqsave(&sflist->lock, flags);
1571da177e4SLinus Torvalds 	if (sflist->open_client != client) {
1581da177e4SLinus Torvalds 		spin_unlock_irqrestore(&sflist->lock, flags);
1591da177e4SLinus Torvalds 		return -EBUSY;
1601da177e4SLinus Torvalds 	}
1611da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sflist->lock, flags);
1621da177e4SLinus Torvalds 
1631da177e4SLinus Torvalds 	lock_preset(sflist);
1641da177e4SLinus Torvalds 	rc = -EINVAL;
1651da177e4SLinus Torvalds 	switch (patch.type) {
1661da177e4SLinus Torvalds 	case SNDRV_SFNT_LOAD_INFO:
1671da177e4SLinus Torvalds 		rc = load_info(sflist, data, count);
1681da177e4SLinus Torvalds 		break;
1691da177e4SLinus Torvalds 	case SNDRV_SFNT_LOAD_DATA:
1701da177e4SLinus Torvalds 		rc = load_data(sflist, data, count);
1711da177e4SLinus Torvalds 		break;
1721da177e4SLinus Torvalds 	case SNDRV_SFNT_CLOSE_PATCH:
1731da177e4SLinus Torvalds 		rc = close_patch(sflist);
1741da177e4SLinus Torvalds 		break;
1751da177e4SLinus Torvalds 	case SNDRV_SFNT_REPLACE_DATA:
1761da177e4SLinus Torvalds 		/*rc = replace_data(&patch, data, count);*/
1771da177e4SLinus Torvalds 		break;
1781da177e4SLinus Torvalds 	case SNDRV_SFNT_MAP_PRESET:
1791da177e4SLinus Torvalds 		rc = load_map(sflist, data, count);
1801da177e4SLinus Torvalds 		break;
1811da177e4SLinus Torvalds 	case SNDRV_SFNT_PROBE_DATA:
1821da177e4SLinus Torvalds 		rc = probe_data(sflist, patch.optarg);
1831da177e4SLinus Torvalds 		break;
1841da177e4SLinus Torvalds 	case SNDRV_SFNT_REMOVE_INFO:
1851da177e4SLinus Torvalds 		/* patch must be opened */
186d20cad60SEric Sesterhenn 		if (!sflist->currsf) {
18742b0158bSTakashi Iwai 			snd_printk(KERN_ERR "soundfont: remove_info: "
18842b0158bSTakashi Iwai 				   "patch not opened\n");
1891da177e4SLinus Torvalds 			rc = -EINVAL;
1901da177e4SLinus Torvalds 		} else {
1911da177e4SLinus Torvalds 			int bank, instr;
1921da177e4SLinus Torvalds 			bank = ((unsigned short)patch.optarg >> 8) & 0xff;
1931da177e4SLinus Torvalds 			instr = (unsigned short)patch.optarg & 0xff;
1941da177e4SLinus Torvalds 			if (! remove_info(sflist, sflist->currsf, bank, instr))
1951da177e4SLinus Torvalds 				rc = -EINVAL;
1961da177e4SLinus Torvalds 			else
1971da177e4SLinus Torvalds 				rc = 0;
1981da177e4SLinus Torvalds 		}
1991da177e4SLinus Torvalds 		break;
2001da177e4SLinus Torvalds 	}
2011da177e4SLinus Torvalds 	unlock_preset(sflist);
2021da177e4SLinus Torvalds 
2031da177e4SLinus Torvalds 	return rc;
2041da177e4SLinus Torvalds }
2051da177e4SLinus Torvalds 
2061da177e4SLinus Torvalds 
2071da177e4SLinus Torvalds /* check if specified type is special font (GUS or preset-alias) */
2081da177e4SLinus Torvalds static inline int
is_special_type(int type)2091da177e4SLinus Torvalds is_special_type(int type)
2101da177e4SLinus Torvalds {
2111da177e4SLinus Torvalds 	type &= 0x0f;
2121da177e4SLinus Torvalds 	return (type == SNDRV_SFNT_PAT_TYPE_GUS ||
2131da177e4SLinus Torvalds 		type == SNDRV_SFNT_PAT_TYPE_MAP);
2141da177e4SLinus Torvalds }
2151da177e4SLinus Torvalds 
2161da177e4SLinus Torvalds 
2171da177e4SLinus Torvalds /* open patch; create sf list */
2181da177e4SLinus Torvalds static int
open_patch(struct snd_sf_list * sflist,const char __user * data,int count,int client)21903da312aSTakashi Iwai open_patch(struct snd_sf_list *sflist, const char __user *data,
22003da312aSTakashi Iwai 	   int count, int client)
2211da177e4SLinus Torvalds {
22203da312aSTakashi Iwai 	struct soundfont_open_parm parm;
22303da312aSTakashi Iwai 	struct snd_soundfont *sf;
2241da177e4SLinus Torvalds 	unsigned long flags;
2251da177e4SLinus Torvalds 
2261da177e4SLinus Torvalds 	spin_lock_irqsave(&sflist->lock, flags);
2271da177e4SLinus Torvalds 	if (sflist->open_client >= 0 || sflist->currsf) {
2281da177e4SLinus Torvalds 		spin_unlock_irqrestore(&sflist->lock, flags);
2291da177e4SLinus Torvalds 		return -EBUSY;
2301da177e4SLinus Torvalds 	}
2311da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sflist->lock, flags);
2321da177e4SLinus Torvalds 
2331da177e4SLinus Torvalds 	if (copy_from_user(&parm, data, sizeof(parm)))
2341da177e4SLinus Torvalds 		return -EFAULT;
2351da177e4SLinus Torvalds 
2361da177e4SLinus Torvalds 	if (is_special_type(parm.type)) {
2371da177e4SLinus Torvalds 		parm.type |= SNDRV_SFNT_PAT_SHARED;
2381da177e4SLinus Torvalds 		sf = newsf(sflist, parm.type, NULL);
2391da177e4SLinus Torvalds 	} else
2401da177e4SLinus Torvalds 		sf = newsf(sflist, parm.type, parm.name);
2411da177e4SLinus Torvalds 	if (sf == NULL) {
2421da177e4SLinus Torvalds 		return -ENOMEM;
2431da177e4SLinus Torvalds 	}
2441da177e4SLinus Torvalds 
2451da177e4SLinus Torvalds 	spin_lock_irqsave(&sflist->lock, flags);
2461da177e4SLinus Torvalds 	sflist->open_client = client;
2471da177e4SLinus Torvalds 	sflist->currsf = sf;
2481da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sflist->lock, flags);
2491da177e4SLinus Torvalds 
2501da177e4SLinus Torvalds 	return 0;
2511da177e4SLinus Torvalds }
2521da177e4SLinus Torvalds 
2531da177e4SLinus Torvalds /*
2541da177e4SLinus Torvalds  * Allocate a new soundfont structure.
2551da177e4SLinus Torvalds  */
25603da312aSTakashi Iwai static struct snd_soundfont *
newsf(struct snd_sf_list * sflist,int type,char * name)25703da312aSTakashi Iwai newsf(struct snd_sf_list *sflist, int type, char *name)
2581da177e4SLinus Torvalds {
25903da312aSTakashi Iwai 	struct snd_soundfont *sf;
2601da177e4SLinus Torvalds 
2611da177e4SLinus Torvalds 	/* check the shared fonts */
2621da177e4SLinus Torvalds 	if (type & SNDRV_SFNT_PAT_SHARED) {
2631da177e4SLinus Torvalds 		for (sf = sflist->fonts; sf; sf = sf->next) {
2641da177e4SLinus Torvalds 			if (is_identical_font(sf, type, name)) {
2651da177e4SLinus Torvalds 				return sf;
2661da177e4SLinus Torvalds 			}
2671da177e4SLinus Torvalds 		}
2681da177e4SLinus Torvalds 	}
2691da177e4SLinus Torvalds 
2701da177e4SLinus Torvalds 	/* not found -- create a new one */
271561b220aSTakashi Iwai 	sf = kzalloc(sizeof(*sf), GFP_KERNEL);
2721da177e4SLinus Torvalds 	if (sf == NULL)
2731da177e4SLinus Torvalds 		return NULL;
2741da177e4SLinus Torvalds 	sf->id = sflist->fonts_size;
2751da177e4SLinus Torvalds 	sflist->fonts_size++;
2761da177e4SLinus Torvalds 
2771da177e4SLinus Torvalds 	/* prepend this record */
2781da177e4SLinus Torvalds 	sf->next = sflist->fonts;
2791da177e4SLinus Torvalds 	sflist->fonts = sf;
2801da177e4SLinus Torvalds 
2811da177e4SLinus Torvalds 	sf->type = type;
2821da177e4SLinus Torvalds 	sf->zones = NULL;
2831da177e4SLinus Torvalds 	sf->samples = NULL;
2841da177e4SLinus Torvalds 	if (name)
2851da177e4SLinus Torvalds 		memcpy(sf->name, name, SNDRV_SFNT_PATCH_NAME_LEN);
2861da177e4SLinus Torvalds 
2871da177e4SLinus Torvalds 	return sf;
2881da177e4SLinus Torvalds }
2891da177e4SLinus Torvalds 
2901da177e4SLinus Torvalds /* check if the given name matches to the existing list */
2911da177e4SLinus Torvalds static int
is_identical_font(struct snd_soundfont * sf,int type,unsigned char * name)29203da312aSTakashi Iwai is_identical_font(struct snd_soundfont *sf, int type, unsigned char *name)
2931da177e4SLinus Torvalds {
2941da177e4SLinus Torvalds 	return ((sf->type & SNDRV_SFNT_PAT_SHARED) &&
2951da177e4SLinus Torvalds 		(sf->type & 0x0f) == (type & 0x0f) &&
2961da177e4SLinus Torvalds 		(name == NULL ||
2971da177e4SLinus Torvalds 		 memcmp(sf->name, name, SNDRV_SFNT_PATCH_NAME_LEN) == 0));
2981da177e4SLinus Torvalds }
2991da177e4SLinus Torvalds 
3001da177e4SLinus Torvalds /*
3011da177e4SLinus Torvalds  * Close the current patch.
3021da177e4SLinus Torvalds  */
3031da177e4SLinus Torvalds static int
close_patch(struct snd_sf_list * sflist)30403da312aSTakashi Iwai close_patch(struct snd_sf_list *sflist)
3051da177e4SLinus Torvalds {
3061da177e4SLinus Torvalds 	unsigned long flags;
3071da177e4SLinus Torvalds 
3081da177e4SLinus Torvalds 	spin_lock_irqsave(&sflist->lock, flags);
3091da177e4SLinus Torvalds 	sflist->currsf = NULL;
3101da177e4SLinus Torvalds 	sflist->open_client = -1;
3111da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sflist->lock, flags);
3121da177e4SLinus Torvalds 
3131da177e4SLinus Torvalds 	rebuild_presets(sflist);
3141da177e4SLinus Torvalds 
3151da177e4SLinus Torvalds 	return 0;
3161da177e4SLinus Torvalds 
3171da177e4SLinus Torvalds }
3181da177e4SLinus Torvalds 
3191da177e4SLinus Torvalds /* probe sample in the current list -- nothing to be loaded */
3201da177e4SLinus Torvalds static int
probe_data(struct snd_sf_list * sflist,int sample_id)32103da312aSTakashi Iwai probe_data(struct snd_sf_list *sflist, int sample_id)
3221da177e4SLinus Torvalds {
3231da177e4SLinus Torvalds 	/* patch must be opened */
3241da177e4SLinus Torvalds 	if (sflist->currsf) {
3251da177e4SLinus Torvalds 		/* search the specified sample by optarg */
3261da177e4SLinus Torvalds 		if (find_sample(sflist->currsf, sample_id))
3271da177e4SLinus Torvalds 			return 0;
3281da177e4SLinus Torvalds 	}
3291da177e4SLinus Torvalds 	return -EINVAL;
3301da177e4SLinus Torvalds }
3311da177e4SLinus Torvalds 
3321da177e4SLinus Torvalds /*
3331da177e4SLinus Torvalds  * increment zone counter
3341da177e4SLinus Torvalds  */
3351da177e4SLinus Torvalds static void
set_zone_counter(struct snd_sf_list * sflist,struct snd_soundfont * sf,struct snd_sf_zone * zp)33603da312aSTakashi Iwai set_zone_counter(struct snd_sf_list *sflist, struct snd_soundfont *sf,
33703da312aSTakashi Iwai 		 struct snd_sf_zone *zp)
3381da177e4SLinus Torvalds {
3391da177e4SLinus Torvalds 	zp->counter = sflist->zone_counter++;
3401da177e4SLinus Torvalds 	if (sf->type & SNDRV_SFNT_PAT_LOCKED)
3411da177e4SLinus Torvalds 		sflist->zone_locked = sflist->zone_counter;
3421da177e4SLinus Torvalds }
3431da177e4SLinus Torvalds 
3441da177e4SLinus Torvalds /*
3451da177e4SLinus Torvalds  * allocate a new zone record
3461da177e4SLinus Torvalds  */
34703da312aSTakashi Iwai static struct snd_sf_zone *
sf_zone_new(struct snd_sf_list * sflist,struct snd_soundfont * sf)34803da312aSTakashi Iwai sf_zone_new(struct snd_sf_list *sflist, struct snd_soundfont *sf)
3491da177e4SLinus Torvalds {
35003da312aSTakashi Iwai 	struct snd_sf_zone *zp;
3511da177e4SLinus Torvalds 
352dd1fc3c5STakashi Iwai 	zp = kzalloc(sizeof(*zp), GFP_KERNEL);
353dd1fc3c5STakashi Iwai 	if (!zp)
3541da177e4SLinus Torvalds 		return NULL;
3551da177e4SLinus Torvalds 	zp->next = sf->zones;
3561da177e4SLinus Torvalds 	sf->zones = zp;
3571da177e4SLinus Torvalds 
3581da177e4SLinus Torvalds 	init_voice_info(&zp->v);
3591da177e4SLinus Torvalds 
3601da177e4SLinus Torvalds 	set_zone_counter(sflist, sf, zp);
3611da177e4SLinus Torvalds 	return zp;
3621da177e4SLinus Torvalds }
3631da177e4SLinus Torvalds 
3641da177e4SLinus Torvalds 
3651da177e4SLinus Torvalds /*
366fbfecd37SUwe Kleine-König  * increment sample counter
3671da177e4SLinus Torvalds  */
3681da177e4SLinus Torvalds static void
set_sample_counter(struct snd_sf_list * sflist,struct snd_soundfont * sf,struct snd_sf_sample * sp)36903da312aSTakashi Iwai set_sample_counter(struct snd_sf_list *sflist, struct snd_soundfont *sf,
37003da312aSTakashi Iwai 		   struct snd_sf_sample *sp)
3711da177e4SLinus Torvalds {
3721da177e4SLinus Torvalds 	sp->counter = sflist->sample_counter++;
3731da177e4SLinus Torvalds 	if (sf->type & SNDRV_SFNT_PAT_LOCKED)
3741da177e4SLinus Torvalds 		sflist->sample_locked = sflist->sample_counter;
3751da177e4SLinus Torvalds }
3761da177e4SLinus Torvalds 
3771da177e4SLinus Torvalds /*
3781da177e4SLinus Torvalds  * allocate a new sample list record
3791da177e4SLinus Torvalds  */
38003da312aSTakashi Iwai static struct snd_sf_sample *
sf_sample_new(struct snd_sf_list * sflist,struct snd_soundfont * sf)38103da312aSTakashi Iwai sf_sample_new(struct snd_sf_list *sflist, struct snd_soundfont *sf)
3821da177e4SLinus Torvalds {
38303da312aSTakashi Iwai 	struct snd_sf_sample *sp;
3841da177e4SLinus Torvalds 
385dd1fc3c5STakashi Iwai 	sp = kzalloc(sizeof(*sp), GFP_KERNEL);
386dd1fc3c5STakashi Iwai 	if (!sp)
3871da177e4SLinus Torvalds 		return NULL;
3881da177e4SLinus Torvalds 
3891da177e4SLinus Torvalds 	sp->next = sf->samples;
3901da177e4SLinus Torvalds 	sf->samples = sp;
3911da177e4SLinus Torvalds 
3921da177e4SLinus Torvalds 	set_sample_counter(sflist, sf, sp);
3931da177e4SLinus Torvalds 	return sp;
3941da177e4SLinus Torvalds }
3951da177e4SLinus Torvalds 
3961da177e4SLinus Torvalds /*
3971da177e4SLinus Torvalds  * delete sample list -- this is an exceptional job.
3981da177e4SLinus Torvalds  * only the last allocated sample can be deleted.
3991da177e4SLinus Torvalds  */
4001da177e4SLinus Torvalds static void
sf_sample_delete(struct snd_sf_list * sflist,struct snd_soundfont * sf,struct snd_sf_sample * sp)40103da312aSTakashi Iwai sf_sample_delete(struct snd_sf_list *sflist, struct snd_soundfont *sf,
40203da312aSTakashi Iwai 		 struct snd_sf_sample *sp)
4031da177e4SLinus Torvalds {
4041da177e4SLinus Torvalds 	/* only last sample is accepted */
4051da177e4SLinus Torvalds 	if (sp == sf->samples) {
4061da177e4SLinus Torvalds 		sf->samples = sp->next;
4071da177e4SLinus Torvalds 		kfree(sp);
4081da177e4SLinus Torvalds 	}
4091da177e4SLinus Torvalds }
4101da177e4SLinus Torvalds 
4111da177e4SLinus Torvalds 
4121da177e4SLinus Torvalds /* load voice map */
4131da177e4SLinus Torvalds static int
load_map(struct snd_sf_list * sflist,const void __user * data,int count)41403da312aSTakashi Iwai load_map(struct snd_sf_list *sflist, const void __user *data, int count)
4151da177e4SLinus Torvalds {
41603da312aSTakashi Iwai 	struct snd_sf_zone *zp, *prevp;
41703da312aSTakashi Iwai 	struct snd_soundfont *sf;
41803da312aSTakashi Iwai 	struct soundfont_voice_map map;
4191da177e4SLinus Torvalds 
4201da177e4SLinus Torvalds 	/* get the link info */
4211da177e4SLinus Torvalds 	if (count < (int)sizeof(map))
4221da177e4SLinus Torvalds 		return -EINVAL;
4231da177e4SLinus Torvalds 	if (copy_from_user(&map, data, sizeof(map)))
4241da177e4SLinus Torvalds 		return -EFAULT;
4251da177e4SLinus Torvalds 
4261da177e4SLinus Torvalds 	if (map.map_instr < 0 || map.map_instr >= SF_MAX_INSTRUMENTS)
4271da177e4SLinus Torvalds 		return -EINVAL;
4281da177e4SLinus Torvalds 
4291da177e4SLinus Torvalds 	sf = newsf(sflist, SNDRV_SFNT_PAT_TYPE_MAP|SNDRV_SFNT_PAT_SHARED, NULL);
4301da177e4SLinus Torvalds 	if (sf == NULL)
4311da177e4SLinus Torvalds 		return -ENOMEM;
4321da177e4SLinus Torvalds 
4331da177e4SLinus Torvalds 	prevp = NULL;
4341da177e4SLinus Torvalds 	for (zp = sf->zones; zp; prevp = zp, zp = zp->next) {
4351da177e4SLinus Torvalds 		if (zp->mapped &&
4361da177e4SLinus Torvalds 		    zp->instr == map.map_instr &&
4371da177e4SLinus Torvalds 		    zp->bank == map.map_bank &&
4381da177e4SLinus Torvalds 		    zp->v.low == map.map_key &&
4391da177e4SLinus Torvalds 		    zp->v.start == map.src_instr &&
4401da177e4SLinus Torvalds 		    zp->v.end == map.src_bank &&
4411da177e4SLinus Torvalds 		    zp->v.fixkey == map.src_key) {
4421da177e4SLinus Torvalds 			/* the same mapping is already present */
4431da177e4SLinus Torvalds 			/* relink this record to the link head */
4441da177e4SLinus Torvalds 			if (prevp) {
4451da177e4SLinus Torvalds 				prevp->next = zp->next;
4461da177e4SLinus Torvalds 				zp->next = sf->zones;
4471da177e4SLinus Torvalds 				sf->zones = zp;
4481da177e4SLinus Torvalds 			}
4491da177e4SLinus Torvalds 			/* update the counter */
4501da177e4SLinus Torvalds 			set_zone_counter(sflist, sf, zp);
4511da177e4SLinus Torvalds 			return 0;
4521da177e4SLinus Torvalds 		}
4531da177e4SLinus Torvalds 	}
4541da177e4SLinus Torvalds 
4551da177e4SLinus Torvalds 	/* create a new zone */
456dd1fc3c5STakashi Iwai 	zp = sf_zone_new(sflist, sf);
457dd1fc3c5STakashi Iwai 	if (!zp)
4581da177e4SLinus Torvalds 		return -ENOMEM;
4591da177e4SLinus Torvalds 
4601da177e4SLinus Torvalds 	zp->bank = map.map_bank;
4611da177e4SLinus Torvalds 	zp->instr = map.map_instr;
4621da177e4SLinus Torvalds 	zp->mapped = 1;
4631da177e4SLinus Torvalds 	if (map.map_key >= 0) {
4641da177e4SLinus Torvalds 		zp->v.low = map.map_key;
4651da177e4SLinus Torvalds 		zp->v.high = map.map_key;
4661da177e4SLinus Torvalds 	}
4671da177e4SLinus Torvalds 	zp->v.start = map.src_instr;
4681da177e4SLinus Torvalds 	zp->v.end = map.src_bank;
4691da177e4SLinus Torvalds 	zp->v.fixkey = map.src_key;
4701da177e4SLinus Torvalds 	zp->v.sf_id = sf->id;
4711da177e4SLinus Torvalds 
4721da177e4SLinus Torvalds 	add_preset(sflist, zp);
4731da177e4SLinus Torvalds 
4741da177e4SLinus Torvalds 	return 0;
4751da177e4SLinus Torvalds }
4761da177e4SLinus Torvalds 
4771da177e4SLinus Torvalds 
4781da177e4SLinus Torvalds /* remove the present instrument layers */
4791da177e4SLinus Torvalds static int
remove_info(struct snd_sf_list * sflist,struct snd_soundfont * sf,int bank,int instr)48003da312aSTakashi Iwai remove_info(struct snd_sf_list *sflist, struct snd_soundfont *sf,
48103da312aSTakashi Iwai 	    int bank, int instr)
4821da177e4SLinus Torvalds {
48303da312aSTakashi Iwai 	struct snd_sf_zone *prev, *next, *p;
4841da177e4SLinus Torvalds 	int removed = 0;
4851da177e4SLinus Torvalds 
4861da177e4SLinus Torvalds 	prev = NULL;
4871da177e4SLinus Torvalds 	for (p = sf->zones; p; p = next) {
4881da177e4SLinus Torvalds 		next = p->next;
4891da177e4SLinus Torvalds 		if (! p->mapped &&
4901da177e4SLinus Torvalds 		    p->bank == bank && p->instr == instr) {
4911da177e4SLinus Torvalds 			/* remove this layer */
4921da177e4SLinus Torvalds 			if (prev)
4931da177e4SLinus Torvalds 				prev->next = next;
4941da177e4SLinus Torvalds 			else
4951da177e4SLinus Torvalds 				sf->zones = next;
4961da177e4SLinus Torvalds 			removed++;
4971da177e4SLinus Torvalds 			kfree(p);
4981da177e4SLinus Torvalds 		} else
4991da177e4SLinus Torvalds 			prev = p;
5001da177e4SLinus Torvalds 	}
5011da177e4SLinus Torvalds 	if (removed)
5021da177e4SLinus Torvalds 		rebuild_presets(sflist);
5031da177e4SLinus Torvalds 	return removed;
5041da177e4SLinus Torvalds }
5051da177e4SLinus Torvalds 
5061da177e4SLinus Torvalds 
5071da177e4SLinus Torvalds /*
5081da177e4SLinus Torvalds  * Read an info record from the user buffer and save it on the current
5091da177e4SLinus Torvalds  * open soundfont.
5101da177e4SLinus Torvalds  */
5111da177e4SLinus Torvalds static int
load_info(struct snd_sf_list * sflist,const void __user * data,long count)51203da312aSTakashi Iwai load_info(struct snd_sf_list *sflist, const void __user *data, long count)
5131da177e4SLinus Torvalds {
51403da312aSTakashi Iwai 	struct snd_soundfont *sf;
51503da312aSTakashi Iwai 	struct snd_sf_zone *zone;
51603da312aSTakashi Iwai 	struct soundfont_voice_rec_hdr hdr;
5171da177e4SLinus Torvalds 	int i;
5181da177e4SLinus Torvalds 
5191da177e4SLinus Torvalds 	/* patch must be opened */
520dd1fc3c5STakashi Iwai 	sf = sflist->currsf;
521dd1fc3c5STakashi Iwai 	if (!sf)
5221da177e4SLinus Torvalds 		return -EINVAL;
5231da177e4SLinus Torvalds 
5241da177e4SLinus Torvalds 	if (is_special_type(sf->type))
5251da177e4SLinus Torvalds 		return -EINVAL;
5261da177e4SLinus Torvalds 
5271da177e4SLinus Torvalds 	if (count < (long)sizeof(hdr)) {
52842b0158bSTakashi Iwai 		printk(KERN_ERR "Soundfont error: invalid patch zone length\n");
5291da177e4SLinus Torvalds 		return -EINVAL;
5301da177e4SLinus Torvalds 	}
5311da177e4SLinus Torvalds 	if (copy_from_user((char*)&hdr, data, sizeof(hdr)))
5321da177e4SLinus Torvalds 		return -EFAULT;
5331da177e4SLinus Torvalds 
5341da177e4SLinus Torvalds 	data += sizeof(hdr);
5351da177e4SLinus Torvalds 	count -= sizeof(hdr);
5361da177e4SLinus Torvalds 
5371da177e4SLinus Torvalds 	if (hdr.nvoices <= 0 || hdr.nvoices >= 100) {
53842b0158bSTakashi Iwai 		printk(KERN_ERR "Soundfont error: Illegal voice number %d\n",
53942b0158bSTakashi Iwai 		       hdr.nvoices);
5401da177e4SLinus Torvalds 		return -EINVAL;
5411da177e4SLinus Torvalds 	}
5421da177e4SLinus Torvalds 
54303da312aSTakashi Iwai 	if (count < (long)sizeof(struct soundfont_voice_info) * hdr.nvoices) {
54442b0158bSTakashi Iwai 		printk(KERN_ERR "Soundfont Error: "
54542b0158bSTakashi Iwai 		       "patch length(%ld) is smaller than nvoices(%d)\n",
5461da177e4SLinus Torvalds 		       count, hdr.nvoices);
5471da177e4SLinus Torvalds 		return -EINVAL;
5481da177e4SLinus Torvalds 	}
5491da177e4SLinus Torvalds 
5501da177e4SLinus Torvalds 	switch (hdr.write_mode) {
5511da177e4SLinus Torvalds 	case SNDRV_SFNT_WR_EXCLUSIVE:
5521da177e4SLinus Torvalds 		/* exclusive mode - if the instrument already exists,
5531da177e4SLinus Torvalds 		   return error */
5541da177e4SLinus Torvalds 		for (zone = sf->zones; zone; zone = zone->next) {
5551da177e4SLinus Torvalds 			if (!zone->mapped &&
5561da177e4SLinus Torvalds 			    zone->bank == hdr.bank &&
5571da177e4SLinus Torvalds 			    zone->instr == hdr.instr)
5581da177e4SLinus Torvalds 				return -EINVAL;
5591da177e4SLinus Torvalds 		}
5601da177e4SLinus Torvalds 		break;
5611da177e4SLinus Torvalds 	case SNDRV_SFNT_WR_REPLACE:
5621da177e4SLinus Torvalds 		/* replace mode - remove the instrument if it already exists */
5631da177e4SLinus Torvalds 		remove_info(sflist, sf, hdr.bank, hdr.instr);
5641da177e4SLinus Torvalds 		break;
5651da177e4SLinus Torvalds 	}
5661da177e4SLinus Torvalds 
5671da177e4SLinus Torvalds 	for (i = 0; i < hdr.nvoices; i++) {
56803da312aSTakashi Iwai 		struct snd_sf_zone tmpzone;
5691da177e4SLinus Torvalds 
5701da177e4SLinus Torvalds 		/* copy awe_voice_info parameters */
5711da177e4SLinus Torvalds 		if (copy_from_user(&tmpzone.v, data, sizeof(tmpzone.v))) {
5721da177e4SLinus Torvalds 			return -EFAULT;
5731da177e4SLinus Torvalds 		}
5741da177e4SLinus Torvalds 
5751da177e4SLinus Torvalds 		data += sizeof(tmpzone.v);
5761da177e4SLinus Torvalds 		count -= sizeof(tmpzone.v);
5771da177e4SLinus Torvalds 
5781da177e4SLinus Torvalds 		tmpzone.bank = hdr.bank;
5791da177e4SLinus Torvalds 		tmpzone.instr = hdr.instr;
5801da177e4SLinus Torvalds 		tmpzone.mapped = 0;
5811da177e4SLinus Torvalds 		tmpzone.v.sf_id = sf->id;
5821da177e4SLinus Torvalds 		if (tmpzone.v.mode & SNDRV_SFNT_MODE_INIT_PARM)
5831da177e4SLinus Torvalds 			init_voice_parm(&tmpzone.v.parm);
5841da177e4SLinus Torvalds 
5851da177e4SLinus Torvalds 		/* create a new zone */
586dd1fc3c5STakashi Iwai 		zone = sf_zone_new(sflist, sf);
587dd1fc3c5STakashi Iwai 		if (!zone)
5881da177e4SLinus Torvalds 			return -ENOMEM;
5891da177e4SLinus Torvalds 
5901da177e4SLinus Torvalds 		/* copy the temporary data */
5911da177e4SLinus Torvalds 		zone->bank = tmpzone.bank;
5921da177e4SLinus Torvalds 		zone->instr = tmpzone.instr;
5931da177e4SLinus Torvalds 		zone->v = tmpzone.v;
5941da177e4SLinus Torvalds 
5951da177e4SLinus Torvalds 		/* look up the sample */
5961da177e4SLinus Torvalds 		zone->sample = set_sample(sf, &zone->v);
5971da177e4SLinus Torvalds 	}
5981da177e4SLinus Torvalds 
5991da177e4SLinus Torvalds 	return 0;
6001da177e4SLinus Torvalds }
6011da177e4SLinus Torvalds 
6021da177e4SLinus Torvalds 
6031da177e4SLinus Torvalds /* initialize voice_info record */
6041da177e4SLinus Torvalds static void
init_voice_info(struct soundfont_voice_info * avp)60503da312aSTakashi Iwai init_voice_info(struct soundfont_voice_info *avp)
6061da177e4SLinus Torvalds {
6071da177e4SLinus Torvalds 	memset(avp, 0, sizeof(*avp));
6081da177e4SLinus Torvalds 
6091da177e4SLinus Torvalds 	avp->root = 60;
6101da177e4SLinus Torvalds 	avp->high = 127;
6111da177e4SLinus Torvalds 	avp->velhigh = 127;
6121da177e4SLinus Torvalds 	avp->fixkey = -1;
6131da177e4SLinus Torvalds 	avp->fixvel = -1;
6141da177e4SLinus Torvalds 	avp->fixpan = -1;
6151da177e4SLinus Torvalds 	avp->pan = -1;
6161da177e4SLinus Torvalds 	avp->amplitude = 127;
6171da177e4SLinus Torvalds 	avp->scaleTuning = 100;
6181da177e4SLinus Torvalds 
6191da177e4SLinus Torvalds 	init_voice_parm(&avp->parm);
6201da177e4SLinus Torvalds }
6211da177e4SLinus Torvalds 
6221da177e4SLinus Torvalds /* initialize voice_parm record:
6231da177e4SLinus Torvalds  * Env1/2: delay=0, attack=0, hold=0, sustain=0, decay=0, release=0.
6241da177e4SLinus Torvalds  * Vibrato and Tremolo effects are zero.
6251da177e4SLinus Torvalds  * Cutoff is maximum.
6261da177e4SLinus Torvalds  * Chorus and Reverb effects are zero.
6271da177e4SLinus Torvalds  */
6281da177e4SLinus Torvalds static void
init_voice_parm(struct soundfont_voice_parm * pp)62903da312aSTakashi Iwai init_voice_parm(struct soundfont_voice_parm *pp)
6301da177e4SLinus Torvalds {
6311da177e4SLinus Torvalds 	memset(pp, 0, sizeof(*pp));
6321da177e4SLinus Torvalds 
6331da177e4SLinus Torvalds 	pp->moddelay = 0x8000;
6341da177e4SLinus Torvalds 	pp->modatkhld = 0x7f7f;
6351da177e4SLinus Torvalds 	pp->moddcysus = 0x7f7f;
6361da177e4SLinus Torvalds 	pp->modrelease = 0x807f;
6371da177e4SLinus Torvalds 
6381da177e4SLinus Torvalds 	pp->voldelay = 0x8000;
6391da177e4SLinus Torvalds 	pp->volatkhld = 0x7f7f;
6401da177e4SLinus Torvalds 	pp->voldcysus = 0x7f7f;
6411da177e4SLinus Torvalds 	pp->volrelease = 0x807f;
6421da177e4SLinus Torvalds 
6431da177e4SLinus Torvalds 	pp->lfo1delay = 0x8000;
6441da177e4SLinus Torvalds 	pp->lfo2delay = 0x8000;
6451da177e4SLinus Torvalds 
6461da177e4SLinus Torvalds 	pp->cutoff = 0xff;
6471da177e4SLinus Torvalds }
6481da177e4SLinus Torvalds 
6491da177e4SLinus Torvalds /* search the specified sample */
65003da312aSTakashi Iwai static struct snd_sf_sample *
set_sample(struct snd_soundfont * sf,struct soundfont_voice_info * avp)65103da312aSTakashi Iwai set_sample(struct snd_soundfont *sf, struct soundfont_voice_info *avp)
6521da177e4SLinus Torvalds {
65303da312aSTakashi Iwai 	struct snd_sf_sample *sample;
6541da177e4SLinus Torvalds 
6551da177e4SLinus Torvalds 	sample = find_sample(sf, avp->sample);
6561da177e4SLinus Torvalds 	if (sample == NULL)
6571da177e4SLinus Torvalds 		return NULL;
6581da177e4SLinus Torvalds 
6591da177e4SLinus Torvalds 	/* add in the actual sample offsets:
6601da177e4SLinus Torvalds 	 * The voice_info addresses define only the relative offset
6611da177e4SLinus Torvalds 	 * from sample pointers.  Here we calculate the actual DRAM
6621da177e4SLinus Torvalds 	 * offset from sample pointers.
6631da177e4SLinus Torvalds 	 */
6641da177e4SLinus Torvalds 	avp->start += sample->v.start;
6651da177e4SLinus Torvalds 	avp->end += sample->v.end;
6661da177e4SLinus Torvalds 	avp->loopstart += sample->v.loopstart;
6671da177e4SLinus Torvalds 	avp->loopend += sample->v.loopend;
6681da177e4SLinus Torvalds 
6691da177e4SLinus Torvalds 	/* copy mode flags */
6701da177e4SLinus Torvalds 	avp->sample_mode = sample->v.mode_flags;
6711da177e4SLinus Torvalds 
6721da177e4SLinus Torvalds 	return sample;
6731da177e4SLinus Torvalds }
6741da177e4SLinus Torvalds 
6751da177e4SLinus Torvalds /* find the sample pointer with the given id in the soundfont */
67603da312aSTakashi Iwai static struct snd_sf_sample *
find_sample(struct snd_soundfont * sf,int sample_id)67703da312aSTakashi Iwai find_sample(struct snd_soundfont *sf, int sample_id)
6781da177e4SLinus Torvalds {
67903da312aSTakashi Iwai 	struct snd_sf_sample *p;
6801da177e4SLinus Torvalds 
6811da177e4SLinus Torvalds 	if (sf == NULL)
6821da177e4SLinus Torvalds 		return NULL;
6831da177e4SLinus Torvalds 
6841da177e4SLinus Torvalds 	for (p = sf->samples; p; p = p->next) {
6851da177e4SLinus Torvalds 		if (p->v.sample == sample_id)
6861da177e4SLinus Torvalds 			return p;
6871da177e4SLinus Torvalds 	}
6881da177e4SLinus Torvalds 	return NULL;
6891da177e4SLinus Torvalds }
6901da177e4SLinus Torvalds 
6911da177e4SLinus Torvalds 
6921da177e4SLinus Torvalds /*
6931da177e4SLinus Torvalds  * Load sample information, this can include data to be loaded onto
6941da177e4SLinus Torvalds  * the soundcard.  It can also just be a pointer into soundcard ROM.
6951da177e4SLinus Torvalds  * If there is data it will be written to the soundcard via the callback
6961da177e4SLinus Torvalds  * routine.
6971da177e4SLinus Torvalds  */
6981da177e4SLinus Torvalds static int
load_data(struct snd_sf_list * sflist,const void __user * data,long count)69903da312aSTakashi Iwai load_data(struct snd_sf_list *sflist, const void __user *data, long count)
7001da177e4SLinus Torvalds {
70103da312aSTakashi Iwai 	struct snd_soundfont *sf;
70203da312aSTakashi Iwai 	struct soundfont_sample_info sample_info;
70303da312aSTakashi Iwai 	struct snd_sf_sample *sp;
7041da177e4SLinus Torvalds 
7051da177e4SLinus Torvalds 	/* patch must be opened */
706dd1fc3c5STakashi Iwai 	sf = sflist->currsf;
707dd1fc3c5STakashi Iwai 	if (!sf)
7081da177e4SLinus Torvalds 		return -EINVAL;
7091da177e4SLinus Torvalds 
7101da177e4SLinus Torvalds 	if (is_special_type(sf->type))
7111da177e4SLinus Torvalds 		return -EINVAL;
7121da177e4SLinus Torvalds 
713*d8f5ce3cSOswald Buddenhagen 	if (count < (long)sizeof(sample_info)) {
714*d8f5ce3cSOswald Buddenhagen 		return -EINVAL;
715*d8f5ce3cSOswald Buddenhagen 	}
7161da177e4SLinus Torvalds 	if (copy_from_user(&sample_info, data, sizeof(sample_info)))
7171da177e4SLinus Torvalds 		return -EFAULT;
718*d8f5ce3cSOswald Buddenhagen 	data += sizeof(sample_info);
719*d8f5ce3cSOswald Buddenhagen 	count -= sizeof(sample_info);
7201da177e4SLinus Torvalds 
721*d8f5ce3cSOswald Buddenhagen 	// SoundFont uses S16LE samples.
722*d8f5ce3cSOswald Buddenhagen 	if (sample_info.size * 2 != count)
7231da177e4SLinus Torvalds 		return -EINVAL;
7241da177e4SLinus Torvalds 
7251da177e4SLinus Torvalds 	/* Check for dup */
7261da177e4SLinus Torvalds 	if (find_sample(sf, sample_info.sample)) {
7271da177e4SLinus Torvalds 		/* if shared sample, skip this data */
7281da177e4SLinus Torvalds 		if (sf->type & SNDRV_SFNT_PAT_SHARED)
7291da177e4SLinus Torvalds 			return 0;
7301da177e4SLinus Torvalds 		return -EINVAL;
7311da177e4SLinus Torvalds 	}
7321da177e4SLinus Torvalds 
7331da177e4SLinus Torvalds 	/* Allocate a new sample structure */
734dd1fc3c5STakashi Iwai 	sp = sf_sample_new(sflist, sf);
735dd1fc3c5STakashi Iwai 	if (!sp)
7361da177e4SLinus Torvalds 		return -ENOMEM;
7371da177e4SLinus Torvalds 
7381da177e4SLinus Torvalds 	sp->v = sample_info;
7391da177e4SLinus Torvalds 	sp->v.sf_id = sf->id;
7401da177e4SLinus Torvalds 	sp->v.dummy = 0;
7411da177e4SLinus Torvalds 	sp->v.truesize = sp->v.size;
7421da177e4SLinus Torvalds 
7431da177e4SLinus Torvalds 	/*
7441da177e4SLinus Torvalds 	 * If there is wave data then load it.
7451da177e4SLinus Torvalds 	 */
7461da177e4SLinus Torvalds 	if (sp->v.size > 0) {
7471da177e4SLinus Torvalds 		int  rc;
7481da177e4SLinus Torvalds 		rc = sflist->callback.sample_new
7491da177e4SLinus Torvalds 			(sflist->callback.private_data, sp, sflist->memhdr,
750*d8f5ce3cSOswald Buddenhagen 			 data, count);
7511da177e4SLinus Torvalds 		if (rc < 0) {
7521da177e4SLinus Torvalds 			sf_sample_delete(sflist, sf, sp);
7531da177e4SLinus Torvalds 			return rc;
7541da177e4SLinus Torvalds 		}
7551da177e4SLinus Torvalds 		sflist->mem_used += sp->v.truesize;
7561da177e4SLinus Torvalds 	}
7571da177e4SLinus Torvalds 
7581da177e4SLinus Torvalds 	return count;
7591da177e4SLinus Torvalds }
7601da177e4SLinus Torvalds 
7611da177e4SLinus Torvalds 
7621da177e4SLinus Torvalds /* log2_tbl[i] = log2(i+128) * 0x10000 */
76355a6921bSTakashi Iwai static const int log_tbl[129] = {
7641da177e4SLinus Torvalds 	0x70000, 0x702df, 0x705b9, 0x7088e, 0x70b5d, 0x70e26, 0x710eb, 0x713aa,
7651da177e4SLinus Torvalds 	0x71663, 0x71918, 0x71bc8, 0x71e72, 0x72118, 0x723b9, 0x72655, 0x728ed,
7661da177e4SLinus Torvalds 	0x72b80, 0x72e0e, 0x73098, 0x7331d, 0x7359e, 0x7381b, 0x73a93, 0x73d08,
7671da177e4SLinus Torvalds 	0x73f78, 0x741e4, 0x7444c, 0x746b0, 0x74910, 0x74b6c, 0x74dc4, 0x75019,
7681da177e4SLinus Torvalds 	0x75269, 0x754b6, 0x75700, 0x75946, 0x75b88, 0x75dc7, 0x76002, 0x7623a,
7691da177e4SLinus Torvalds 	0x7646e, 0x766a0, 0x768cd, 0x76af8, 0x76d1f, 0x76f43, 0x77164, 0x77382,
7701da177e4SLinus Torvalds 	0x7759d, 0x777b4, 0x779c9, 0x77bdb, 0x77dea, 0x77ff5, 0x781fe, 0x78404,
7711da177e4SLinus Torvalds 	0x78608, 0x78808, 0x78a06, 0x78c01, 0x78df9, 0x78fef, 0x791e2, 0x793d2,
7721da177e4SLinus Torvalds 	0x795c0, 0x797ab, 0x79993, 0x79b79, 0x79d5d, 0x79f3e, 0x7a11d, 0x7a2f9,
7731da177e4SLinus Torvalds 	0x7a4d3, 0x7a6ab, 0x7a880, 0x7aa53, 0x7ac24, 0x7adf2, 0x7afbe, 0x7b188,
7741da177e4SLinus Torvalds 	0x7b350, 0x7b515, 0x7b6d8, 0x7b899, 0x7ba58, 0x7bc15, 0x7bdd0, 0x7bf89,
7751da177e4SLinus Torvalds 	0x7c140, 0x7c2f5, 0x7c4a7, 0x7c658, 0x7c807, 0x7c9b3, 0x7cb5e, 0x7cd07,
7761da177e4SLinus Torvalds 	0x7ceae, 0x7d053, 0x7d1f7, 0x7d398, 0x7d538, 0x7d6d6, 0x7d872, 0x7da0c,
7771da177e4SLinus Torvalds 	0x7dba4, 0x7dd3b, 0x7ded0, 0x7e063, 0x7e1f4, 0x7e384, 0x7e512, 0x7e69f,
7781da177e4SLinus Torvalds 	0x7e829, 0x7e9b3, 0x7eb3a, 0x7ecc0, 0x7ee44, 0x7efc7, 0x7f148, 0x7f2c8,
7791da177e4SLinus Torvalds 	0x7f446, 0x7f5c2, 0x7f73d, 0x7f8b7, 0x7fa2f, 0x7fba5, 0x7fd1a, 0x7fe8d,
7801da177e4SLinus Torvalds 	0x80000,
7811da177e4SLinus Torvalds };
7821da177e4SLinus Torvalds 
7831da177e4SLinus Torvalds /* convert from linear to log value
7841da177e4SLinus Torvalds  *
7851da177e4SLinus Torvalds  * conversion: value = log2(amount / base) * ratio
7861da177e4SLinus Torvalds  *
7871da177e4SLinus Torvalds  * argument:
7881da177e4SLinus Torvalds  *   amount = linear value (unsigned, 32bit max)
7891da177e4SLinus Torvalds  *   offset = base offset (:= log2(base) * 0x10000)
7901da177e4SLinus Torvalds  *   ratio = division ratio
7911da177e4SLinus Torvalds  *
7921da177e4SLinus Torvalds  */
7931da177e4SLinus Torvalds int
snd_sf_linear_to_log(unsigned int amount,int offset,int ratio)7941da177e4SLinus Torvalds snd_sf_linear_to_log(unsigned int amount, int offset, int ratio)
7951da177e4SLinus Torvalds {
7961da177e4SLinus Torvalds 	int v;
7971da177e4SLinus Torvalds 	int s, low, bit;
7981da177e4SLinus Torvalds 
7991da177e4SLinus Torvalds 	if (amount < 2)
8001da177e4SLinus Torvalds 		return 0;
8011da177e4SLinus Torvalds 	for (bit = 0; ! (amount & 0x80000000L); bit++)
8021da177e4SLinus Torvalds 		amount <<= 1;
8031da177e4SLinus Torvalds 	s = (amount >> 24) & 0x7f;
8041da177e4SLinus Torvalds 	low = (amount >> 16) & 0xff;
805ced7c287Sgushengxian 	/* linear approximation by lower 8 bit */
8061da177e4SLinus Torvalds 	v = (log_tbl[s + 1] * low + log_tbl[s] * (0x100 - low)) >> 8;
8071da177e4SLinus Torvalds 	v -= offset;
8081da177e4SLinus Torvalds 	v = (v * ratio) >> 16;
8091da177e4SLinus Torvalds 	v += (24 - bit) * ratio;
8101da177e4SLinus Torvalds 	return v;
8111da177e4SLinus Torvalds }
8121da177e4SLinus Torvalds 
81395ff1756STakashi Iwai EXPORT_SYMBOL(snd_sf_linear_to_log);
81495ff1756STakashi Iwai 
81595ff1756STakashi Iwai 
8161da177e4SLinus Torvalds #define OFFSET_MSEC		653117		/* base = 1000 */
8171da177e4SLinus Torvalds #define OFFSET_ABSCENT		851781		/* base = 8176 */
8181da177e4SLinus Torvalds #define OFFSET_SAMPLERATE	1011119		/* base = 44100 */
8191da177e4SLinus Torvalds 
8201da177e4SLinus Torvalds #define ABSCENT_RATIO		1200
8211da177e4SLinus Torvalds #define TIMECENT_RATIO		1200
8221da177e4SLinus Torvalds #define SAMPLERATE_RATIO	4096
8231da177e4SLinus Torvalds 
8241da177e4SLinus Torvalds /*
8251da177e4SLinus Torvalds  * mHz to abscent
8261da177e4SLinus Torvalds  * conversion: abscent = log2(MHz / 8176) * 1200
8271da177e4SLinus Torvalds  */
8281da177e4SLinus Torvalds static int
freq_to_note(int mhz)8291da177e4SLinus Torvalds freq_to_note(int mhz)
8301da177e4SLinus Torvalds {
8311da177e4SLinus Torvalds 	return snd_sf_linear_to_log(mhz, OFFSET_ABSCENT, ABSCENT_RATIO);
8321da177e4SLinus Torvalds }
8331da177e4SLinus Torvalds 
8341da177e4SLinus Torvalds /* convert Hz to AWE32 rate offset:
8351da177e4SLinus Torvalds  * sample pitch offset for the specified sample rate
8361da177e4SLinus Torvalds  * rate=44100 is no offset, each 4096 is 1 octave (twice).
8371da177e4SLinus Torvalds  * eg, when rate is 22050, this offset becomes -4096.
8381da177e4SLinus Torvalds  *
8391da177e4SLinus Torvalds  * conversion: offset = log2(Hz / 44100) * 4096
8401da177e4SLinus Torvalds  */
8411da177e4SLinus Torvalds static int
calc_rate_offset(int hz)8421da177e4SLinus Torvalds calc_rate_offset(int hz)
8431da177e4SLinus Torvalds {
8441da177e4SLinus Torvalds 	return snd_sf_linear_to_log(hz, OFFSET_SAMPLERATE, SAMPLERATE_RATIO);
8451da177e4SLinus Torvalds }
8461da177e4SLinus Torvalds 
8471da177e4SLinus Torvalds 
8481da177e4SLinus Torvalds /* calculate GUS envelope time */
8491da177e4SLinus Torvalds static int
calc_gus_envelope_time(int rate,int start,int end)8501da177e4SLinus Torvalds calc_gus_envelope_time(int rate, int start, int end)
8511da177e4SLinus Torvalds {
8521da177e4SLinus Torvalds 	int r, p, t;
8531da177e4SLinus Torvalds 	r = (3 - ((rate >> 6) & 3)) * 3;
8541da177e4SLinus Torvalds 	p = rate & 0x3f;
8552854cd34SDan Carpenter 	if (!p)
8562854cd34SDan Carpenter 		p = 1;
8571da177e4SLinus Torvalds 	t = end - start;
8581da177e4SLinus Torvalds 	if (t < 0) t = -t;
8591da177e4SLinus Torvalds 	if (13 > r)
8601da177e4SLinus Torvalds 		t = t << (13 - r);
8611da177e4SLinus Torvalds 	else
8621da177e4SLinus Torvalds 		t = t >> (r - 13);
8631da177e4SLinus Torvalds 	return (t * 10) / (p * 441);
8641da177e4SLinus Torvalds }
8651da177e4SLinus Torvalds 
8661da177e4SLinus Torvalds /* convert envelope time parameter to soundfont parameters */
8671da177e4SLinus Torvalds 
8681da177e4SLinus Torvalds /* attack & decay/release time table (msec) */
86955a6921bSTakashi Iwai static const short attack_time_tbl[128] = {
8701da177e4SLinus Torvalds 32767, 32767, 5989, 4235, 2994, 2518, 2117, 1780, 1497, 1373, 1259, 1154, 1058, 970, 890, 816,
8711da177e4SLinus Torvalds 707, 691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377,
8721da177e4SLinus Torvalds 361, 345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188,
8731da177e4SLinus Torvalds 180, 172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94,
8741da177e4SLinus Torvalds 90, 86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47,
8751da177e4SLinus Torvalds 45, 43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23,
8761da177e4SLinus Torvalds 22, 21, 20, 19, 19, 18, 17, 16, 16, 15, 15, 14, 13, 13, 12, 12,
8771da177e4SLinus Torvalds 11, 11, 10, 10, 10, 9, 9, 8, 8, 8, 8, 7, 7, 7, 6, 0,
8781da177e4SLinus Torvalds };
8791da177e4SLinus Torvalds 
88055a6921bSTakashi Iwai static const short decay_time_tbl[128] = {
8811da177e4SLinus Torvalds 32767, 32767, 22614, 15990, 11307, 9508, 7995, 6723, 5653, 5184, 4754, 4359, 3997, 3665, 3361, 3082,
8821da177e4SLinus Torvalds 2828, 2765, 2648, 2535, 2428, 2325, 2226, 2132, 2042, 1955, 1872, 1793, 1717, 1644, 1574, 1507,
8831da177e4SLinus Torvalds 1443, 1382, 1324, 1267, 1214, 1162, 1113, 1066, 978, 936, 897, 859, 822, 787, 754, 722,
8841da177e4SLinus Torvalds 691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377, 361,
8851da177e4SLinus Torvalds 345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188, 180,
8861da177e4SLinus Torvalds 172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94, 90,
8871da177e4SLinus Torvalds 86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47, 45,
8881da177e4SLinus Torvalds 43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23, 22,
8891da177e4SLinus Torvalds };
8901da177e4SLinus Torvalds 
8911da177e4SLinus Torvalds /* delay time = 0x8000 - msec/92 */
8921da177e4SLinus Torvalds int
snd_sf_calc_parm_hold(int msec)8931da177e4SLinus Torvalds snd_sf_calc_parm_hold(int msec)
8941da177e4SLinus Torvalds {
8951da177e4SLinus Torvalds 	int val = (0x7f * 92 - msec) / 92;
8961da177e4SLinus Torvalds 	if (val < 1) val = 1;
8971da177e4SLinus Torvalds 	if (val >= 126) val = 126;
8981da177e4SLinus Torvalds 	return val;
8991da177e4SLinus Torvalds }
9001da177e4SLinus Torvalds 
9011da177e4SLinus Torvalds /* search an index for specified time from given time table */
9021da177e4SLinus Torvalds static int
calc_parm_search(int msec,const short * table)90355a6921bSTakashi Iwai calc_parm_search(int msec, const short *table)
9041da177e4SLinus Torvalds {
9051da177e4SLinus Torvalds 	int left = 1, right = 127, mid;
9061da177e4SLinus Torvalds 	while (left < right) {
9071da177e4SLinus Torvalds 		mid = (left + right) / 2;
9081da177e4SLinus Torvalds 		if (msec < (int)table[mid])
9091da177e4SLinus Torvalds 			left = mid + 1;
9101da177e4SLinus Torvalds 		else
9111da177e4SLinus Torvalds 			right = mid;
9121da177e4SLinus Torvalds 	}
9131da177e4SLinus Torvalds 	return left;
9141da177e4SLinus Torvalds }
9151da177e4SLinus Torvalds 
9161da177e4SLinus Torvalds /* attack time: search from time table */
9171da177e4SLinus Torvalds int
snd_sf_calc_parm_attack(int msec)9181da177e4SLinus Torvalds snd_sf_calc_parm_attack(int msec)
9191da177e4SLinus Torvalds {
9201da177e4SLinus Torvalds 	return calc_parm_search(msec, attack_time_tbl);
9211da177e4SLinus Torvalds }
9221da177e4SLinus Torvalds 
9231da177e4SLinus Torvalds /* decay/release time: search from time table */
9241da177e4SLinus Torvalds int
snd_sf_calc_parm_decay(int msec)9251da177e4SLinus Torvalds snd_sf_calc_parm_decay(int msec)
9261da177e4SLinus Torvalds {
9271da177e4SLinus Torvalds 	return calc_parm_search(msec, decay_time_tbl);
9281da177e4SLinus Torvalds }
9291da177e4SLinus Torvalds 
9301da177e4SLinus Torvalds int snd_sf_vol_table[128] = {
9311da177e4SLinus Torvalds 	255,111,95,86,79,74,70,66,63,61,58,56,54,52,50,49,
9321da177e4SLinus Torvalds 	47,46,45,43,42,41,40,39,38,37,36,35,34,34,33,32,
9331da177e4SLinus Torvalds 	31,31,30,29,29,28,27,27,26,26,25,24,24,23,23,22,
9341da177e4SLinus Torvalds 	22,21,21,21,20,20,19,19,18,18,18,17,17,16,16,16,
9351da177e4SLinus Torvalds 	15,15,15,14,14,14,13,13,13,12,12,12,11,11,11,10,
9361da177e4SLinus Torvalds 	10,10,10,9,9,9,8,8,8,8,7,7,7,7,6,6,
9371da177e4SLinus Torvalds 	6,6,5,5,5,5,5,4,4,4,4,3,3,3,3,3,
9381da177e4SLinus Torvalds 	2,2,2,2,2,1,1,1,1,1,0,0,0,0,0,0,
9391da177e4SLinus Torvalds };
9401da177e4SLinus Torvalds 
9411da177e4SLinus Torvalds 
9421da177e4SLinus Torvalds #define calc_gus_sustain(val)  (0x7f - snd_sf_vol_table[(val)/2])
9431da177e4SLinus Torvalds #define calc_gus_attenuation(val)	snd_sf_vol_table[(val)/2]
9441da177e4SLinus Torvalds 
9451da177e4SLinus Torvalds /* load GUS patch */
9461da177e4SLinus Torvalds static int
load_guspatch(struct snd_sf_list * sflist,const char __user * data,long count,int client)94703da312aSTakashi Iwai load_guspatch(struct snd_sf_list *sflist, const char __user *data,
94803da312aSTakashi Iwai 	      long count, int client)
9491da177e4SLinus Torvalds {
9501da177e4SLinus Torvalds 	struct patch_info patch;
95103da312aSTakashi Iwai 	struct snd_soundfont *sf;
95203da312aSTakashi Iwai 	struct snd_sf_zone *zone;
95303da312aSTakashi Iwai 	struct snd_sf_sample *smp;
9541da177e4SLinus Torvalds 	int note, sample_id;
9551da177e4SLinus Torvalds 	int rc;
9561da177e4SLinus Torvalds 
9571da177e4SLinus Torvalds 	if (count < (long)sizeof(patch)) {
95842b0158bSTakashi Iwai 		snd_printk(KERN_ERR "patch record too small %ld\n", count);
9591da177e4SLinus Torvalds 		return -EINVAL;
9601da177e4SLinus Torvalds 	}
9611da177e4SLinus Torvalds 	if (copy_from_user(&patch, data, sizeof(patch)))
9621da177e4SLinus Torvalds 		return -EFAULT;
9631da177e4SLinus Torvalds 	count -= sizeof(patch);
9641da177e4SLinus Torvalds 	data += sizeof(patch);
9651da177e4SLinus Torvalds 
966*d8f5ce3cSOswald Buddenhagen 	if ((patch.len << (patch.mode & WAVE_16_BITS ? 1 : 0)) != count)
967*d8f5ce3cSOswald Buddenhagen 		return -EINVAL;
968*d8f5ce3cSOswald Buddenhagen 
9691da177e4SLinus Torvalds 	sf = newsf(sflist, SNDRV_SFNT_PAT_TYPE_GUS|SNDRV_SFNT_PAT_SHARED, NULL);
9701da177e4SLinus Torvalds 	if (sf == NULL)
9711da177e4SLinus Torvalds 		return -ENOMEM;
972dd1fc3c5STakashi Iwai 	smp = sf_sample_new(sflist, sf);
973dd1fc3c5STakashi Iwai 	if (!smp)
9741da177e4SLinus Torvalds 		return -ENOMEM;
9751da177e4SLinus Torvalds 	sample_id = sflist->sample_counter;
9761da177e4SLinus Torvalds 	smp->v.sample = sample_id;
9771da177e4SLinus Torvalds 	smp->v.start = 0;
9781da177e4SLinus Torvalds 	smp->v.end = patch.len;
9791da177e4SLinus Torvalds 	smp->v.loopstart = patch.loop_start;
9801da177e4SLinus Torvalds 	smp->v.loopend = patch.loop_end;
9811da177e4SLinus Torvalds 	smp->v.size = patch.len;
9821da177e4SLinus Torvalds 
9831da177e4SLinus Torvalds 	/* set up mode flags */
9841da177e4SLinus Torvalds 	smp->v.mode_flags = 0;
9851da177e4SLinus Torvalds 	if (!(patch.mode & WAVE_16_BITS))
9861da177e4SLinus Torvalds 		smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_8BITS;
9871da177e4SLinus Torvalds 	if (patch.mode & WAVE_UNSIGNED)
9881da177e4SLinus Torvalds 		smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_UNSIGNED;
9891da177e4SLinus Torvalds 	smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_NO_BLANK;
9901da177e4SLinus Torvalds 	if (!(patch.mode & (WAVE_LOOPING|WAVE_BIDIR_LOOP|WAVE_LOOP_BACK)))
9911da177e4SLinus Torvalds 		smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_SINGLESHOT;
9921da177e4SLinus Torvalds 	if (patch.mode & WAVE_BIDIR_LOOP)
9931da177e4SLinus Torvalds 		smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_BIDIR_LOOP;
9941da177e4SLinus Torvalds 	if (patch.mode & WAVE_LOOP_BACK)
9951da177e4SLinus Torvalds 		smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_REVERSE_LOOP;
9961da177e4SLinus Torvalds 
9971da177e4SLinus Torvalds 	if (patch.mode & WAVE_16_BITS) {
9981da177e4SLinus Torvalds 		/* convert to word offsets */
9991da177e4SLinus Torvalds 		smp->v.size /= 2;
10001da177e4SLinus Torvalds 		smp->v.end /= 2;
10011da177e4SLinus Torvalds 		smp->v.loopstart /= 2;
10021da177e4SLinus Torvalds 		smp->v.loopend /= 2;
10031da177e4SLinus Torvalds 	}
10041da177e4SLinus Torvalds 	/*smp->v.loopend++;*/
10051da177e4SLinus Torvalds 
10061da177e4SLinus Torvalds 	smp->v.dummy = 0;
10071da177e4SLinus Torvalds 	smp->v.truesize = 0;
10081da177e4SLinus Torvalds 	smp->v.sf_id = sf->id;
10091da177e4SLinus Torvalds 
10101da177e4SLinus Torvalds 	/* set up voice info */
1011dd1fc3c5STakashi Iwai 	zone = sf_zone_new(sflist, sf);
1012dd1fc3c5STakashi Iwai 	if (!zone) {
10131da177e4SLinus Torvalds 		sf_sample_delete(sflist, sf, smp);
10141da177e4SLinus Torvalds 		return -ENOMEM;
10151da177e4SLinus Torvalds 	}
10161da177e4SLinus Torvalds 
10171da177e4SLinus Torvalds 	/*
10181da177e4SLinus Torvalds 	 * load wave data
10191da177e4SLinus Torvalds 	 */
10201da177e4SLinus Torvalds 	if (sflist->callback.sample_new) {
10211da177e4SLinus Torvalds 		rc = sflist->callback.sample_new
102203da312aSTakashi Iwai 			(sflist->callback.private_data, smp, sflist->memhdr,
102303da312aSTakashi Iwai 			 data, count);
10241da177e4SLinus Torvalds 		if (rc < 0) {
10251da177e4SLinus Torvalds 			sf_sample_delete(sflist, sf, smp);
102614577c25SRickard Strandqvist 			kfree(zone);
10271da177e4SLinus Torvalds 			return rc;
10281da177e4SLinus Torvalds 		}
10291da177e4SLinus Torvalds 		/* memory offset is updated after */
10301da177e4SLinus Torvalds 	}
10311da177e4SLinus Torvalds 
10321da177e4SLinus Torvalds 	/* update the memory offset here */
10331da177e4SLinus Torvalds 	sflist->mem_used += smp->v.truesize;
10341da177e4SLinus Torvalds 
10351da177e4SLinus Torvalds 	zone->v.sample = sample_id; /* the last sample */
10361da177e4SLinus Torvalds 	zone->v.rate_offset = calc_rate_offset(patch.base_freq);
10371da177e4SLinus Torvalds 	note = freq_to_note(patch.base_note);
10381da177e4SLinus Torvalds 	zone->v.root = note / 100;
10391da177e4SLinus Torvalds 	zone->v.tune = -(note % 100);
10401da177e4SLinus Torvalds 	zone->v.low = (freq_to_note(patch.low_note) + 99) / 100;
10411da177e4SLinus Torvalds 	zone->v.high = freq_to_note(patch.high_note) / 100;
10421da177e4SLinus Torvalds 	/* panning position; -128 - 127 => 0-127 */
10431da177e4SLinus Torvalds 	zone->v.pan = (patch.panning + 128) / 2;
10441da177e4SLinus Torvalds #if 0
104542b0158bSTakashi Iwai 	snd_printk(KERN_DEBUG
104642b0158bSTakashi Iwai 		   "gus: basefrq=%d (ofs=%d) root=%d,tune=%d, range:%d-%d\n",
10471da177e4SLinus Torvalds 		   (int)patch.base_freq, zone->v.rate_offset,
10481da177e4SLinus Torvalds 		   zone->v.root, zone->v.tune, zone->v.low, zone->v.high);
10491da177e4SLinus Torvalds #endif
10501da177e4SLinus Torvalds 
10511da177e4SLinus Torvalds 	/* detuning is ignored */
10521da177e4SLinus Torvalds 	/* 6points volume envelope */
10531da177e4SLinus Torvalds 	if (patch.mode & WAVE_ENVELOPES) {
10541da177e4SLinus Torvalds 		int attack, hold, decay, release;
10551da177e4SLinus Torvalds 		attack = calc_gus_envelope_time
10561da177e4SLinus Torvalds 			(patch.env_rate[0], 0, patch.env_offset[0]);
10571da177e4SLinus Torvalds 		hold = calc_gus_envelope_time
10581da177e4SLinus Torvalds 			(patch.env_rate[1], patch.env_offset[0],
10591da177e4SLinus Torvalds 			 patch.env_offset[1]);
10601da177e4SLinus Torvalds 		decay = calc_gus_envelope_time
10611da177e4SLinus Torvalds 			(patch.env_rate[2], patch.env_offset[1],
10621da177e4SLinus Torvalds 			 patch.env_offset[2]);
10631da177e4SLinus Torvalds 		release = calc_gus_envelope_time
10641da177e4SLinus Torvalds 			(patch.env_rate[3], patch.env_offset[1],
10651da177e4SLinus Torvalds 			 patch.env_offset[4]);
10661da177e4SLinus Torvalds 		release += calc_gus_envelope_time
10671da177e4SLinus Torvalds 			(patch.env_rate[4], patch.env_offset[3],
10681da177e4SLinus Torvalds 			 patch.env_offset[4]);
10691da177e4SLinus Torvalds 		release += calc_gus_envelope_time
10701da177e4SLinus Torvalds 			(patch.env_rate[5], patch.env_offset[4],
10711da177e4SLinus Torvalds 			 patch.env_offset[5]);
10721da177e4SLinus Torvalds 		zone->v.parm.volatkhld =
10731da177e4SLinus Torvalds 			(snd_sf_calc_parm_hold(hold) << 8) |
10741da177e4SLinus Torvalds 			snd_sf_calc_parm_attack(attack);
10751da177e4SLinus Torvalds 		zone->v.parm.voldcysus = (calc_gus_sustain(patch.env_offset[2]) << 8) |
10761da177e4SLinus Torvalds 			snd_sf_calc_parm_decay(decay);
10771da177e4SLinus Torvalds 		zone->v.parm.volrelease = 0x8000 | snd_sf_calc_parm_decay(release);
10781da177e4SLinus Torvalds 		zone->v.attenuation = calc_gus_attenuation(patch.env_offset[0]);
10791da177e4SLinus Torvalds #if 0
108042b0158bSTakashi Iwai 		snd_printk(KERN_DEBUG
108142b0158bSTakashi Iwai 			   "gus: atkhld=%x, dcysus=%x, volrel=%x, att=%d\n",
10821da177e4SLinus Torvalds 			   zone->v.parm.volatkhld,
10831da177e4SLinus Torvalds 			   zone->v.parm.voldcysus,
10841da177e4SLinus Torvalds 			   zone->v.parm.volrelease,
10851da177e4SLinus Torvalds 			   zone->v.attenuation);
10861da177e4SLinus Torvalds #endif
10871da177e4SLinus Torvalds 	}
10881da177e4SLinus Torvalds 
10891da177e4SLinus Torvalds 	/* fast release */
10901da177e4SLinus Torvalds 	if (patch.mode & WAVE_FAST_RELEASE) {
10911da177e4SLinus Torvalds 		zone->v.parm.volrelease = 0x807f;
10921da177e4SLinus Torvalds 	}
10931da177e4SLinus Torvalds 
10941da177e4SLinus Torvalds 	/* tremolo effect */
10951da177e4SLinus Torvalds 	if (patch.mode & WAVE_TREMOLO) {
10961da177e4SLinus Torvalds 		int rate = (patch.tremolo_rate * 1000 / 38) / 42;
10971da177e4SLinus Torvalds 		zone->v.parm.tremfrq = ((patch.tremolo_depth / 2) << 8) | rate;
10981da177e4SLinus Torvalds 	}
10991da177e4SLinus Torvalds 	/* vibrato effect */
11001da177e4SLinus Torvalds 	if (patch.mode & WAVE_VIBRATO) {
11011da177e4SLinus Torvalds 		int rate = (patch.vibrato_rate * 1000 / 38) / 42;
11021da177e4SLinus Torvalds 		zone->v.parm.fm2frq2 = ((patch.vibrato_depth / 6) << 8) | rate;
11031da177e4SLinus Torvalds 	}
11041da177e4SLinus Torvalds 
11051da177e4SLinus Torvalds 	/* scale_freq, scale_factor, volume, and fractions not implemented */
11061da177e4SLinus Torvalds 
11071da177e4SLinus Torvalds 	if (!(smp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT))
11081da177e4SLinus Torvalds 		zone->v.mode = SNDRV_SFNT_MODE_LOOPING;
11091da177e4SLinus Torvalds 	else
11101da177e4SLinus Torvalds 		zone->v.mode = 0;
11111da177e4SLinus Torvalds 
11121da177e4SLinus Torvalds 	/* append to the tail of the list */
11131da177e4SLinus Torvalds 	/*zone->bank = ctrls[AWE_MD_GUS_BANK];*/
11141da177e4SLinus Torvalds 	zone->bank = 0;
11151da177e4SLinus Torvalds 	zone->instr = patch.instr_no;
11161da177e4SLinus Torvalds 	zone->mapped = 0;
11171da177e4SLinus Torvalds 	zone->v.sf_id = sf->id;
11181da177e4SLinus Torvalds 
11191da177e4SLinus Torvalds 	zone->sample = set_sample(sf, &zone->v);
11201da177e4SLinus Torvalds 
11211da177e4SLinus Torvalds 	/* rebuild preset now */
11221da177e4SLinus Torvalds 	add_preset(sflist, zone);
11231da177e4SLinus Torvalds 
11241da177e4SLinus Torvalds 	return 0;
11251da177e4SLinus Torvalds }
11261da177e4SLinus Torvalds 
11271da177e4SLinus Torvalds /* load GUS patch */
11281da177e4SLinus Torvalds int
snd_soundfont_load_guspatch(struct snd_sf_list * sflist,const char __user * data,long count,int client)112903da312aSTakashi Iwai snd_soundfont_load_guspatch(struct snd_sf_list *sflist, const char __user *data,
11301da177e4SLinus Torvalds 			    long count, int client)
11311da177e4SLinus Torvalds {
11321da177e4SLinus Torvalds 	int rc;
11331da177e4SLinus Torvalds 	lock_preset(sflist);
11341da177e4SLinus Torvalds 	rc = load_guspatch(sflist, data, count, client);
11351da177e4SLinus Torvalds 	unlock_preset(sflist);
11361da177e4SLinus Torvalds 	return rc;
11371da177e4SLinus Torvalds }
11381da177e4SLinus Torvalds 
11391da177e4SLinus Torvalds 
11401da177e4SLinus Torvalds /*
11411da177e4SLinus Torvalds  * Rebuild the preset table.  This is like a hash table in that it allows
11421da177e4SLinus Torvalds  * quick access to the zone information.  For each preset there are zone
11431da177e4SLinus Torvalds  * structures linked by next_instr and by next_zone.  Former is the whole
11441da177e4SLinus Torvalds  * link for this preset, and latter is the link for zone (i.e. instrument/
11451da177e4SLinus Torvalds  * bank/key combination).
11461da177e4SLinus Torvalds  */
11471da177e4SLinus Torvalds static void
rebuild_presets(struct snd_sf_list * sflist)114803da312aSTakashi Iwai rebuild_presets(struct snd_sf_list *sflist)
11491da177e4SLinus Torvalds {
115003da312aSTakashi Iwai 	struct snd_soundfont *sf;
115103da312aSTakashi Iwai 	struct snd_sf_zone *cur;
11521da177e4SLinus Torvalds 
11531da177e4SLinus Torvalds 	/* clear preset table */
11541da177e4SLinus Torvalds 	memset(sflist->presets, 0, sizeof(sflist->presets));
11551da177e4SLinus Torvalds 
11561da177e4SLinus Torvalds 	/* search all fonts and insert each font */
11571da177e4SLinus Torvalds 	for (sf = sflist->fonts; sf; sf = sf->next) {
11581da177e4SLinus Torvalds 		for (cur = sf->zones; cur; cur = cur->next) {
11591da177e4SLinus Torvalds 			if (! cur->mapped && cur->sample == NULL) {
11601da177e4SLinus Torvalds 				/* try again to search the corresponding sample */
11611da177e4SLinus Torvalds 				cur->sample = set_sample(sf, &cur->v);
11621da177e4SLinus Torvalds 				if (cur->sample == NULL)
11631da177e4SLinus Torvalds 					continue;
11641da177e4SLinus Torvalds 			}
11651da177e4SLinus Torvalds 
11661da177e4SLinus Torvalds 			add_preset(sflist, cur);
11671da177e4SLinus Torvalds 		}
11681da177e4SLinus Torvalds 	}
11691da177e4SLinus Torvalds }
11701da177e4SLinus Torvalds 
11711da177e4SLinus Torvalds 
11721da177e4SLinus Torvalds /*
11731da177e4SLinus Torvalds  * add the given zone to preset table
11741da177e4SLinus Torvalds  */
11751da177e4SLinus Torvalds static void
add_preset(struct snd_sf_list * sflist,struct snd_sf_zone * cur)117603da312aSTakashi Iwai add_preset(struct snd_sf_list *sflist, struct snd_sf_zone *cur)
11771da177e4SLinus Torvalds {
117803da312aSTakashi Iwai 	struct snd_sf_zone *zone;
11791da177e4SLinus Torvalds 	int index;
11801da177e4SLinus Torvalds 
11811da177e4SLinus Torvalds 	zone = search_first_zone(sflist, cur->bank, cur->instr, cur->v.low);
11821da177e4SLinus Torvalds 	if (zone && zone->v.sf_id != cur->v.sf_id) {
11831da177e4SLinus Torvalds 		/* different instrument was already defined */
118403da312aSTakashi Iwai 		struct snd_sf_zone *p;
11851da177e4SLinus Torvalds 		/* compare the allocated time */
11861da177e4SLinus Torvalds 		for (p = zone; p; p = p->next_zone) {
11871da177e4SLinus Torvalds 			if (p->counter > cur->counter)
11881da177e4SLinus Torvalds 				/* the current is older.. skipped */
11891da177e4SLinus Torvalds 				return;
11901da177e4SLinus Torvalds 		}
11911da177e4SLinus Torvalds 		/* remove old zones */
11921da177e4SLinus Torvalds 		delete_preset(sflist, zone);
11931da177e4SLinus Torvalds 		zone = NULL; /* do not forget to clear this! */
11941da177e4SLinus Torvalds 	}
11951da177e4SLinus Torvalds 
11961da177e4SLinus Torvalds 	/* prepend this zone */
1197dd1fc3c5STakashi Iwai 	index = get_index(cur->bank, cur->instr, cur->v.low);
1198dd1fc3c5STakashi Iwai 	if (index < 0)
11991da177e4SLinus Torvalds 		return;
12001da177e4SLinus Torvalds 	cur->next_zone = zone; /* zone link */
12011da177e4SLinus Torvalds 	cur->next_instr = sflist->presets[index]; /* preset table link */
12021da177e4SLinus Torvalds 	sflist->presets[index] = cur;
12031da177e4SLinus Torvalds }
12041da177e4SLinus Torvalds 
12051da177e4SLinus Torvalds /*
12061da177e4SLinus Torvalds  * delete the given zones from preset_table
12071da177e4SLinus Torvalds  */
12081da177e4SLinus Torvalds static void
delete_preset(struct snd_sf_list * sflist,struct snd_sf_zone * zp)120903da312aSTakashi Iwai delete_preset(struct snd_sf_list *sflist, struct snd_sf_zone *zp)
12101da177e4SLinus Torvalds {
12111da177e4SLinus Torvalds 	int index;
121203da312aSTakashi Iwai 	struct snd_sf_zone *p;
12131da177e4SLinus Torvalds 
1214dd1fc3c5STakashi Iwai 	index = get_index(zp->bank, zp->instr, zp->v.low);
1215dd1fc3c5STakashi Iwai 	if (index < 0)
12161da177e4SLinus Torvalds 		return;
12171da177e4SLinus Torvalds 	for (p = sflist->presets[index]; p; p = p->next_instr) {
12181da177e4SLinus Torvalds 		while (p->next_instr == zp) {
12191da177e4SLinus Torvalds 			p->next_instr = zp->next_instr;
12201da177e4SLinus Torvalds 			zp = zp->next_zone;
12211da177e4SLinus Torvalds 			if (zp == NULL)
12221da177e4SLinus Torvalds 				return;
12231da177e4SLinus Torvalds 		}
12241da177e4SLinus Torvalds 	}
12251da177e4SLinus Torvalds }
12261da177e4SLinus Torvalds 
12271da177e4SLinus Torvalds 
12281da177e4SLinus Torvalds /*
12291da177e4SLinus Torvalds  * Search matching zones from preset table.
12301da177e4SLinus Torvalds  * The note can be rewritten by preset mapping (alias).
12311da177e4SLinus Torvalds  * The found zones are stored on 'table' array.  max_layers defines
12321da177e4SLinus Torvalds  * the maximum number of elements in this array.
12331da177e4SLinus Torvalds  * This function returns the number of found zones.  0 if not found.
12341da177e4SLinus Torvalds  */
12351da177e4SLinus Torvalds int
snd_soundfont_search_zone(struct snd_sf_list * sflist,int * notep,int vel,int preset,int bank,int def_preset,int def_bank,struct snd_sf_zone ** table,int max_layers)123603da312aSTakashi Iwai snd_soundfont_search_zone(struct snd_sf_list *sflist, int *notep, int vel,
12371da177e4SLinus Torvalds 			  int preset, int bank,
12381da177e4SLinus Torvalds 			  int def_preset, int def_bank,
123903da312aSTakashi Iwai 			  struct snd_sf_zone **table, int max_layers)
12401da177e4SLinus Torvalds {
12411da177e4SLinus Torvalds 	int nvoices;
12421da177e4SLinus Torvalds 	unsigned long flags;
12431da177e4SLinus Torvalds 
12441da177e4SLinus Torvalds 	/* this function is supposed to be called atomically,
12451da177e4SLinus Torvalds 	 * so we check the lock.  if it's busy, just returns 0 to
12461da177e4SLinus Torvalds 	 * tell the caller the busy state
12471da177e4SLinus Torvalds 	 */
12481da177e4SLinus Torvalds 	spin_lock_irqsave(&sflist->lock, flags);
12491da177e4SLinus Torvalds 	if (sflist->presets_locked) {
12501da177e4SLinus Torvalds 		spin_unlock_irqrestore(&sflist->lock, flags);
12511da177e4SLinus Torvalds 		return 0;
12521da177e4SLinus Torvalds 	}
125303da312aSTakashi Iwai 	nvoices = search_zones(sflist, notep, vel, preset, bank,
125403da312aSTakashi Iwai 			       table, max_layers, 0);
12551da177e4SLinus Torvalds 	if (! nvoices) {
12561da177e4SLinus Torvalds 		if (preset != def_preset || bank != def_bank)
125703da312aSTakashi Iwai 			nvoices = search_zones(sflist, notep, vel,
125803da312aSTakashi Iwai 					       def_preset, def_bank,
125903da312aSTakashi Iwai 					       table, max_layers, 0);
12601da177e4SLinus Torvalds 	}
12611da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sflist->lock, flags);
12621da177e4SLinus Torvalds 	return nvoices;
12631da177e4SLinus Torvalds }
12641da177e4SLinus Torvalds 
12651da177e4SLinus Torvalds 
12661da177e4SLinus Torvalds /*
12671da177e4SLinus Torvalds  * search the first matching zone
12681da177e4SLinus Torvalds  */
126903da312aSTakashi Iwai static struct snd_sf_zone *
search_first_zone(struct snd_sf_list * sflist,int bank,int preset,int key)127003da312aSTakashi Iwai search_first_zone(struct snd_sf_list *sflist, int bank, int preset, int key)
12711da177e4SLinus Torvalds {
12721da177e4SLinus Torvalds 	int index;
127303da312aSTakashi Iwai 	struct snd_sf_zone *zp;
12741da177e4SLinus Torvalds 
1275dd1fc3c5STakashi Iwai 	index = get_index(bank, preset, key);
1276dd1fc3c5STakashi Iwai 	if (index < 0)
12771da177e4SLinus Torvalds 		return NULL;
12781da177e4SLinus Torvalds 	for (zp = sflist->presets[index]; zp; zp = zp->next_instr) {
12791da177e4SLinus Torvalds 		if (zp->instr == preset && zp->bank == bank)
12801da177e4SLinus Torvalds 			return zp;
12811da177e4SLinus Torvalds 	}
12821da177e4SLinus Torvalds 	return NULL;
12831da177e4SLinus Torvalds }
12841da177e4SLinus Torvalds 
12851da177e4SLinus Torvalds 
12861da177e4SLinus Torvalds /*
12871da177e4SLinus Torvalds  * search matching zones from sflist.  can be called recursively.
12881da177e4SLinus Torvalds  */
12891da177e4SLinus Torvalds static int
search_zones(struct snd_sf_list * sflist,int * notep,int vel,int preset,int bank,struct snd_sf_zone ** table,int max_layers,int level)129003da312aSTakashi Iwai search_zones(struct snd_sf_list *sflist, int *notep, int vel,
129103da312aSTakashi Iwai 	     int preset, int bank, struct snd_sf_zone **table,
129203da312aSTakashi Iwai 	     int max_layers, int level)
12931da177e4SLinus Torvalds {
129403da312aSTakashi Iwai 	struct snd_sf_zone *zp;
12951da177e4SLinus Torvalds 	int nvoices;
12961da177e4SLinus Torvalds 
12971da177e4SLinus Torvalds 	zp = search_first_zone(sflist, bank, preset, *notep);
12981da177e4SLinus Torvalds 	nvoices = 0;
12991da177e4SLinus Torvalds 	for (; zp; zp = zp->next_zone) {
13001da177e4SLinus Torvalds 		if (*notep >= zp->v.low && *notep <= zp->v.high &&
13011da177e4SLinus Torvalds 		    vel >= zp->v.vellow && vel <= zp->v.velhigh) {
13021da177e4SLinus Torvalds 			if (zp->mapped) {
13031da177e4SLinus Torvalds 				/* search preset mapping (aliasing) */
13041da177e4SLinus Torvalds 				int key = zp->v.fixkey;
13051da177e4SLinus Torvalds 				preset = zp->v.start;
13061da177e4SLinus Torvalds 				bank = zp->v.end;
13071da177e4SLinus Torvalds 
13081da177e4SLinus Torvalds 				if (level > 5) /* too deep alias level */
13091da177e4SLinus Torvalds 					return 0;
13101da177e4SLinus Torvalds 				if (key < 0)
13111da177e4SLinus Torvalds 					key = *notep;
13121da177e4SLinus Torvalds 				nvoices = search_zones(sflist, &key, vel,
13131da177e4SLinus Torvalds 						       preset, bank, table,
13141da177e4SLinus Torvalds 						       max_layers, level + 1);
13151da177e4SLinus Torvalds 				if (nvoices > 0)
13161da177e4SLinus Torvalds 					*notep = key;
13171da177e4SLinus Torvalds 				break;
13181da177e4SLinus Torvalds 			}
13191da177e4SLinus Torvalds 			table[nvoices++] = zp;
13201da177e4SLinus Torvalds 			if (nvoices >= max_layers)
13211da177e4SLinus Torvalds 				break;
13221da177e4SLinus Torvalds 		}
13231da177e4SLinus Torvalds 	}
13241da177e4SLinus Torvalds 
13251da177e4SLinus Torvalds 	return nvoices;
13261da177e4SLinus Torvalds }
13271da177e4SLinus Torvalds 
13281da177e4SLinus Torvalds 
13291da177e4SLinus Torvalds /* calculate the index of preset table:
13301da177e4SLinus Torvalds  * drums are mapped from 128 to 255 according to its note key.
13311da177e4SLinus Torvalds  * other instruments are mapped from 0 to 127.
13321da177e4SLinus Torvalds  * if the index is out of range, return -1.
13331da177e4SLinus Torvalds  */
13341da177e4SLinus Torvalds static int
get_index(int bank,int instr,int key)13351da177e4SLinus Torvalds get_index(int bank, int instr, int key)
13361da177e4SLinus Torvalds {
13371da177e4SLinus Torvalds 	int index;
13381da177e4SLinus Torvalds 	if (SF_IS_DRUM_BANK(bank))
13391da177e4SLinus Torvalds 		index = key + SF_MAX_INSTRUMENTS;
13401da177e4SLinus Torvalds 	else
13411da177e4SLinus Torvalds 		index = instr;
13421da177e4SLinus Torvalds 	index = index % SF_MAX_PRESETS;
13431da177e4SLinus Torvalds 	if (index < 0)
13441da177e4SLinus Torvalds 		return -1;
13451da177e4SLinus Torvalds 	return index;
13461da177e4SLinus Torvalds }
13471da177e4SLinus Torvalds 
13481da177e4SLinus Torvalds /*
13491da177e4SLinus Torvalds  * Initialise the sflist structure.
13501da177e4SLinus Torvalds  */
13511da177e4SLinus Torvalds static void
snd_sf_init(struct snd_sf_list * sflist)135203da312aSTakashi Iwai snd_sf_init(struct snd_sf_list *sflist)
13531da177e4SLinus Torvalds {
13541da177e4SLinus Torvalds 	memset(sflist->presets, 0, sizeof(sflist->presets));
13551da177e4SLinus Torvalds 
13561da177e4SLinus Torvalds 	sflist->mem_used = 0;
13571da177e4SLinus Torvalds 	sflist->currsf = NULL;
13581da177e4SLinus Torvalds 	sflist->open_client = -1;
13591da177e4SLinus Torvalds 	sflist->fonts = NULL;
13601da177e4SLinus Torvalds 	sflist->fonts_size = 0;
13611da177e4SLinus Torvalds 	sflist->zone_counter = 0;
13621da177e4SLinus Torvalds 	sflist->sample_counter = 0;
13631da177e4SLinus Torvalds 	sflist->zone_locked = 0;
13641da177e4SLinus Torvalds 	sflist->sample_locked = 0;
13651da177e4SLinus Torvalds }
13661da177e4SLinus Torvalds 
13671da177e4SLinus Torvalds /*
13681da177e4SLinus Torvalds  * Release all list records
13691da177e4SLinus Torvalds  */
13701da177e4SLinus Torvalds static void
snd_sf_clear(struct snd_sf_list * sflist)137103da312aSTakashi Iwai snd_sf_clear(struct snd_sf_list *sflist)
13721da177e4SLinus Torvalds {
137303da312aSTakashi Iwai 	struct snd_soundfont *sf, *nextsf;
137403da312aSTakashi Iwai 	struct snd_sf_zone *zp, *nextzp;
137503da312aSTakashi Iwai 	struct snd_sf_sample *sp, *nextsp;
13761da177e4SLinus Torvalds 
13771da177e4SLinus Torvalds 	for (sf = sflist->fonts; sf; sf = nextsf) {
13781da177e4SLinus Torvalds 		nextsf = sf->next;
13791da177e4SLinus Torvalds 		for (zp = sf->zones; zp; zp = nextzp) {
13801da177e4SLinus Torvalds 			nextzp = zp->next;
13811da177e4SLinus Torvalds 			kfree(zp);
13821da177e4SLinus Torvalds 		}
13831da177e4SLinus Torvalds 		for (sp = sf->samples; sp; sp = nextsp) {
13841da177e4SLinus Torvalds 			nextsp = sp->next;
13851da177e4SLinus Torvalds 			if (sflist->callback.sample_free)
138603da312aSTakashi Iwai 				sflist->callback.sample_free(sflist->callback.private_data,
138703da312aSTakashi Iwai 							     sp, sflist->memhdr);
13881da177e4SLinus Torvalds 			kfree(sp);
13891da177e4SLinus Torvalds 		}
13901da177e4SLinus Torvalds 		kfree(sf);
13911da177e4SLinus Torvalds 	}
13921da177e4SLinus Torvalds 
13931da177e4SLinus Torvalds 	snd_sf_init(sflist);
13941da177e4SLinus Torvalds }
13951da177e4SLinus Torvalds 
13961da177e4SLinus Torvalds 
13971da177e4SLinus Torvalds /*
13981da177e4SLinus Torvalds  * Create a new sflist structure
13991da177e4SLinus Torvalds  */
140003da312aSTakashi Iwai struct snd_sf_list *
snd_sf_new(struct snd_sf_callback * callback,struct snd_util_memhdr * hdr)140103da312aSTakashi Iwai snd_sf_new(struct snd_sf_callback *callback, struct snd_util_memhdr *hdr)
14021da177e4SLinus Torvalds {
140303da312aSTakashi Iwai 	struct snd_sf_list *sflist;
14041da177e4SLinus Torvalds 
1405dd1fc3c5STakashi Iwai 	sflist = kzalloc(sizeof(*sflist), GFP_KERNEL);
1406dd1fc3c5STakashi Iwai 	if (!sflist)
14071da177e4SLinus Torvalds 		return NULL;
14081da177e4SLinus Torvalds 
1409ef9f0a42SIngo Molnar 	mutex_init(&sflist->presets_mutex);
14101da177e4SLinus Torvalds 	spin_lock_init(&sflist->lock);
14111da177e4SLinus Torvalds 	sflist->memhdr = hdr;
14121da177e4SLinus Torvalds 
14131da177e4SLinus Torvalds 	if (callback)
14141da177e4SLinus Torvalds 		sflist->callback = *callback;
14151da177e4SLinus Torvalds 
14161da177e4SLinus Torvalds 	snd_sf_init(sflist);
14171da177e4SLinus Torvalds 	return sflist;
14181da177e4SLinus Torvalds }
14191da177e4SLinus Torvalds 
14201da177e4SLinus Torvalds 
14211da177e4SLinus Torvalds /*
14221da177e4SLinus Torvalds  * Free everything allocated off the sflist structure.
14231da177e4SLinus Torvalds  */
14241da177e4SLinus Torvalds void
snd_sf_free(struct snd_sf_list * sflist)142503da312aSTakashi Iwai snd_sf_free(struct snd_sf_list *sflist)
14261da177e4SLinus Torvalds {
14271da177e4SLinus Torvalds 	if (sflist == NULL)
14281da177e4SLinus Torvalds 		return;
14291da177e4SLinus Torvalds 
14301da177e4SLinus Torvalds 	lock_preset(sflist);
14311da177e4SLinus Torvalds 	if (sflist->callback.sample_reset)
14321da177e4SLinus Torvalds 		sflist->callback.sample_reset(sflist->callback.private_data);
14331da177e4SLinus Torvalds 	snd_sf_clear(sflist);
14341da177e4SLinus Torvalds 	unlock_preset(sflist);
14351da177e4SLinus Torvalds 
14361da177e4SLinus Torvalds 	kfree(sflist);
14371da177e4SLinus Torvalds }
14381da177e4SLinus Torvalds 
14391da177e4SLinus Torvalds /*
14401da177e4SLinus Torvalds  * Remove all samples
1441ced7c287Sgushengxian  * The soundcard should be silent before calling this function.
14421da177e4SLinus Torvalds  */
14431da177e4SLinus Torvalds int
snd_soundfont_remove_samples(struct snd_sf_list * sflist)144403da312aSTakashi Iwai snd_soundfont_remove_samples(struct snd_sf_list *sflist)
14451da177e4SLinus Torvalds {
14461da177e4SLinus Torvalds 	lock_preset(sflist);
14471da177e4SLinus Torvalds 	if (sflist->callback.sample_reset)
14481da177e4SLinus Torvalds 		sflist->callback.sample_reset(sflist->callback.private_data);
14491da177e4SLinus Torvalds 	snd_sf_clear(sflist);
14501da177e4SLinus Torvalds 	unlock_preset(sflist);
14511da177e4SLinus Torvalds 
14521da177e4SLinus Torvalds 	return 0;
14531da177e4SLinus Torvalds }
14541da177e4SLinus Torvalds 
14551da177e4SLinus Torvalds /*
14561da177e4SLinus Torvalds  * Remove unlocked samples.
14571da177e4SLinus Torvalds  * The soundcard should be silent before calling this function.
14581da177e4SLinus Torvalds  */
14591da177e4SLinus Torvalds int
snd_soundfont_remove_unlocked(struct snd_sf_list * sflist)146003da312aSTakashi Iwai snd_soundfont_remove_unlocked(struct snd_sf_list *sflist)
14611da177e4SLinus Torvalds {
146203da312aSTakashi Iwai 	struct snd_soundfont *sf;
146303da312aSTakashi Iwai 	struct snd_sf_zone *zp, *nextzp;
146403da312aSTakashi Iwai 	struct snd_sf_sample *sp, *nextsp;
14651da177e4SLinus Torvalds 
14661da177e4SLinus Torvalds 	lock_preset(sflist);
14671da177e4SLinus Torvalds 
14681da177e4SLinus Torvalds 	if (sflist->callback.sample_reset)
14691da177e4SLinus Torvalds 		sflist->callback.sample_reset(sflist->callback.private_data);
14701da177e4SLinus Torvalds 
14711da177e4SLinus Torvalds 	/* to be sure */
14721da177e4SLinus Torvalds 	memset(sflist->presets, 0, sizeof(sflist->presets));
14731da177e4SLinus Torvalds 
14741da177e4SLinus Torvalds 	for (sf = sflist->fonts; sf; sf = sf->next) {
14751da177e4SLinus Torvalds 		for (zp = sf->zones; zp; zp = nextzp) {
14761da177e4SLinus Torvalds 			if (zp->counter < sflist->zone_locked)
14771da177e4SLinus Torvalds 				break;
14781da177e4SLinus Torvalds 			nextzp = zp->next;
14791da177e4SLinus Torvalds 			sf->zones = nextzp;
14801da177e4SLinus Torvalds 			kfree(zp);
14811da177e4SLinus Torvalds 		}
14821da177e4SLinus Torvalds 
14831da177e4SLinus Torvalds 		for (sp = sf->samples; sp; sp = nextsp) {
14841da177e4SLinus Torvalds 			if (sp->counter < sflist->sample_locked)
14851da177e4SLinus Torvalds 				break;
14861da177e4SLinus Torvalds 			nextsp = sp->next;
14871da177e4SLinus Torvalds 			sf->samples = nextsp;
14881da177e4SLinus Torvalds 			sflist->mem_used -= sp->v.truesize;
14891da177e4SLinus Torvalds 			if (sflist->callback.sample_free)
149003da312aSTakashi Iwai 				sflist->callback.sample_free(sflist->callback.private_data,
149103da312aSTakashi Iwai 							     sp, sflist->memhdr);
14921da177e4SLinus Torvalds 			kfree(sp);
14931da177e4SLinus Torvalds 		}
14941da177e4SLinus Torvalds 	}
14951da177e4SLinus Torvalds 
14961da177e4SLinus Torvalds 	sflist->zone_counter = sflist->zone_locked;
14971da177e4SLinus Torvalds 	sflist->sample_counter = sflist->sample_locked;
14981da177e4SLinus Torvalds 
14991da177e4SLinus Torvalds 	rebuild_presets(sflist);
15001da177e4SLinus Torvalds 
15011da177e4SLinus Torvalds 	unlock_preset(sflist);
15021da177e4SLinus Torvalds 	return 0;
15031da177e4SLinus Torvalds }
1504