瀏覽代碼

[ALSA] emu10k1 - Fix ABI for older ld10k1

Fix ABI for older ld10k1.  When no EMU10K1_PVERSION ioctl is issued,
the driver accepts ioctls with the old struct size without TLV information.
Also, changed the struct field to make the conversion easier from the
old to the new structs.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@suse.cz>
Takashi Iwai 18 年之前
父節點
當前提交
f7ba7fc617
共有 2 個文件被更改,包括 96 次插入25 次删除
  1. 17 4
      include/sound/emu10k1.h
  2. 79 21
      sound/pci/emu10k1/emufx.c

+ 17 - 4
include/sound/emu10k1.h

@@ -1449,6 +1449,7 @@ struct snd_emu10k1 {
 	unsigned int tos_link: 1,		/* tos link detected */
 	unsigned int tos_link: 1,		/* tos link detected */
 		rear_ac97: 1,			/* rear channels are on AC'97 */
 		rear_ac97: 1,			/* rear channels are on AC'97 */
 		enable_ir: 1;
 		enable_ir: 1;
+	unsigned int support_tlv :1;
 	/* Contains profile of card capabilities */
 	/* Contains profile of card capabilities */
 	const struct snd_emu_chip_details *card_capabilities;
 	const struct snd_emu_chip_details *card_capabilities;
 	unsigned int audigy;			/* is Audigy? */
 	unsigned int audigy;			/* is Audigy? */
@@ -1901,11 +1902,20 @@ struct snd_emu10k1_fx8010_control_gpr {
 	unsigned int value[32];		/* initial values */
 	unsigned int value[32];		/* initial values */
 	unsigned int min;		/* minimum range */
 	unsigned int min;		/* minimum range */
 	unsigned int max;		/* maximum range */
 	unsigned int max;		/* maximum range */
-	union {
-		snd_kcontrol_tlv_rw_t *c;
-		unsigned int *p;
-	} tlv;
 	unsigned int translation;	/* translation type (EMU10K1_GPR_TRANSLATION*) */
 	unsigned int translation;	/* translation type (EMU10K1_GPR_TRANSLATION*) */
+	unsigned int *tlv;
+};
+
+/* old ABI without TLV support */
+struct snd_emu10k1_fx8010_control_old_gpr {
+	struct snd_ctl_elem_id id;
+	unsigned int vcount;
+	unsigned int count;
+	unsigned short gpr[32];
+	unsigned int value[32];
+	unsigned int min;
+	unsigned int max;
+	unsigned int translation;
 };
 };
 
 
 struct snd_emu10k1_fx8010_code {
 struct snd_emu10k1_fx8010_code {
@@ -1956,6 +1966,8 @@ struct snd_emu10k1_fx8010_pcm_rec {
 	unsigned int res2;		/* reserved */
 	unsigned int res2;		/* reserved */
 };
 };
 
 
+#define SNDRV_EMU10K1_VERSION		SNDRV_PROTOCOL_VERSION(1, 0, 1)
+
 #define SNDRV_EMU10K1_IOCTL_INFO	_IOR ('H', 0x10, struct snd_emu10k1_fx8010_info)
 #define SNDRV_EMU10K1_IOCTL_INFO	_IOR ('H', 0x10, struct snd_emu10k1_fx8010_info)
 #define SNDRV_EMU10K1_IOCTL_CODE_POKE	_IOW ('H', 0x11, struct snd_emu10k1_fx8010_code)
 #define SNDRV_EMU10K1_IOCTL_CODE_POKE	_IOW ('H', 0x11, struct snd_emu10k1_fx8010_code)
 #define SNDRV_EMU10K1_IOCTL_CODE_PEEK	_IOWR('H', 0x12, struct snd_emu10k1_fx8010_code)
 #define SNDRV_EMU10K1_IOCTL_CODE_PEEK	_IOWR('H', 0x12, struct snd_emu10k1_fx8010_code)
@@ -1964,6 +1976,7 @@ struct snd_emu10k1_fx8010_pcm_rec {
 #define SNDRV_EMU10K1_IOCTL_TRAM_PEEK	_IOWR('H', 0x22, struct snd_emu10k1_fx8010_tram)
 #define SNDRV_EMU10K1_IOCTL_TRAM_PEEK	_IOWR('H', 0x22, struct snd_emu10k1_fx8010_tram)
 #define SNDRV_EMU10K1_IOCTL_PCM_POKE	_IOW ('H', 0x30, struct snd_emu10k1_fx8010_pcm_rec)
 #define SNDRV_EMU10K1_IOCTL_PCM_POKE	_IOW ('H', 0x30, struct snd_emu10k1_fx8010_pcm_rec)
 #define SNDRV_EMU10K1_IOCTL_PCM_PEEK	_IOWR('H', 0x31, struct snd_emu10k1_fx8010_pcm_rec)
 #define SNDRV_EMU10K1_IOCTL_PCM_PEEK	_IOWR('H', 0x31, struct snd_emu10k1_fx8010_pcm_rec)
+#define SNDRV_EMU10K1_IOCTL_PVERSION	_IOR ('H', 0x40, int)
 #define SNDRV_EMU10K1_IOCTL_STOP	_IO  ('H', 0x80)
 #define SNDRV_EMU10K1_IOCTL_STOP	_IO  ('H', 0x80)
 #define SNDRV_EMU10K1_IOCTL_CONTINUE	_IO  ('H', 0x81)
 #define SNDRV_EMU10K1_IOCTL_CONTINUE	_IO  ('H', 0x81)
 #define SNDRV_EMU10K1_IOCTL_ZERO_TRAM_COUNTER _IO ('H', 0x82)
 #define SNDRV_EMU10K1_IOCTL_ZERO_TRAM_COUNTER _IO ('H', 0x82)

+ 79 - 21
sound/pci/emu10k1/emufx.c

@@ -655,13 +655,66 @@ snd_emu10k1_look_for_ctl(struct snd_emu10k1 *emu, struct snd_ctl_elem_id *id)
 	return NULL;
 	return NULL;
 }
 }
 
 
+#define MAX_TLV_SIZE	256
+
+static unsigned int *copy_tlv(unsigned int __user *_tlv)
+{
+	unsigned int data[2];
+	unsigned int *tlv;
+
+	if (!_tlv)
+		return NULL;
+	if (copy_from_user(data, _tlv, sizeof(data)))
+		return NULL;
+	if (data[1] >= MAX_TLV_SIZE)
+		return NULL;
+	tlv = kmalloc(data[1] * 4 + sizeof(data), GFP_KERNEL);
+	if (!tlv)
+		return NULL;
+	memcpy(tlv, data, sizeof(data));
+	if (copy_from_user(tlv + 2, _tlv + 2, data[1])) {
+		kfree(tlv);
+		return NULL;
+	}
+	return tlv;
+}
+
+static int copy_gctl(struct snd_emu10k1 *emu,
+		     struct snd_emu10k1_fx8010_control_gpr *gctl,
+		     struct snd_emu10k1_fx8010_control_gpr __user *_gctl,
+		     int idx)
+{
+	struct snd_emu10k1_fx8010_control_old_gpr __user *octl;
+
+	if (emu->support_tlv)
+		return copy_from_user(gctl, &_gctl[idx], sizeof(*gctl));
+	octl = (struct snd_emu10k1_fx8010_control_old_gpr __user *)_gctl;
+	if (copy_from_user(gctl, &octl[idx], sizeof(*octl)))
+		return -EFAULT;
+	gctl->tlv = NULL;
+	return 0;
+}
+
+static int copy_gctl_to_user(struct snd_emu10k1 *emu,
+		     struct snd_emu10k1_fx8010_control_gpr __user *_gctl,
+		     struct snd_emu10k1_fx8010_control_gpr *gctl,
+		     int idx)
+{
+	struct snd_emu10k1_fx8010_control_old_gpr __user *octl;
+
+	if (emu->support_tlv)
+		return copy_to_user(&_gctl[idx], gctl, sizeof(*gctl));
+	
+	octl = (struct snd_emu10k1_fx8010_control_old_gpr __user *)_gctl;
+	return copy_to_user(&octl[idx], gctl, sizeof(*octl));
+}
+
 static int snd_emu10k1_verify_controls(struct snd_emu10k1 *emu,
 static int snd_emu10k1_verify_controls(struct snd_emu10k1 *emu,
 				       struct snd_emu10k1_fx8010_code *icode)
 				       struct snd_emu10k1_fx8010_code *icode)
 {
 {
 	unsigned int i;
 	unsigned int i;
 	struct snd_ctl_elem_id __user *_id;
 	struct snd_ctl_elem_id __user *_id;
 	struct snd_ctl_elem_id id;
 	struct snd_ctl_elem_id id;
-	struct snd_emu10k1_fx8010_control_gpr __user *_gctl;
 	struct snd_emu10k1_fx8010_control_gpr *gctl;
 	struct snd_emu10k1_fx8010_control_gpr *gctl;
 	int err;
 	int err;
 	
 	
@@ -676,9 +729,8 @@ static int snd_emu10k1_verify_controls(struct snd_emu10k1 *emu,
 	if (! gctl)
 	if (! gctl)
 		return -ENOMEM;
 		return -ENOMEM;
 	err = 0;
 	err = 0;
-	for (i = 0, _gctl = icode->gpr_add_controls;
-	     i < icode->gpr_add_control_count; i++, _gctl++) {
-		if (copy_from_user(gctl, _gctl, sizeof(*gctl))) {
+	for (i = 0; i < icode->gpr_add_control_count; i++) {
+		if (copy_gctl(emu, gctl, icode->gpr_add_controls, i)) {
 			err = -EFAULT;
 			err = -EFAULT;
 			goto __error;
 			goto __error;
 		}
 		}
@@ -697,10 +749,9 @@ static int snd_emu10k1_verify_controls(struct snd_emu10k1 *emu,
 			goto __error;
 			goto __error;
 		}
 		}
 	}
 	}
-	for (i = 0, _gctl = icode->gpr_list_controls;
-	     i < icode->gpr_list_control_count; i++, _gctl++) {
+	for (i = 0; i < icode->gpr_list_control_count; i++) {
 	     	/* FIXME: we need to check the WRITE access */
 	     	/* FIXME: we need to check the WRITE access */
-		if (copy_from_user(gctl, _gctl, sizeof(*gctl))) {
+		if (copy_gctl(emu, gctl, icode->gpr_list_controls, i)) {
 			err = -EFAULT;
 			err = -EFAULT;
 			goto __error;
 			goto __error;
 		}
 		}
@@ -718,13 +769,14 @@ static void snd_emu10k1_ctl_private_free(struct snd_kcontrol *kctl)
 	kctl->private_value = 0;
 	kctl->private_value = 0;
 	list_del(&ctl->list);
 	list_del(&ctl->list);
 	kfree(ctl);
 	kfree(ctl);
+	if (kctl->tlv.p)
+		kfree(kctl->tlv.p);
 }
 }
 
 
 static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu,
 static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu,
 				    struct snd_emu10k1_fx8010_code *icode)
 				    struct snd_emu10k1_fx8010_code *icode)
 {
 {
 	unsigned int i, j;
 	unsigned int i, j;
-	struct snd_emu10k1_fx8010_control_gpr __user *_gctl;
 	struct snd_emu10k1_fx8010_control_gpr *gctl;
 	struct snd_emu10k1_fx8010_control_gpr *gctl;
 	struct snd_emu10k1_fx8010_ctl *ctl, *nctl;
 	struct snd_emu10k1_fx8010_ctl *ctl, *nctl;
 	struct snd_kcontrol_new knew;
 	struct snd_kcontrol_new knew;
@@ -740,9 +792,8 @@ static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu,
 		goto __error;
 		goto __error;
 	}
 	}
 
 
-	for (i = 0, _gctl = icode->gpr_add_controls;
-	     i < icode->gpr_add_control_count; i++, _gctl++) {
-		if (copy_from_user(gctl, _gctl, sizeof(*gctl))) {
+	for (i = 0; i < icode->gpr_add_control_count; i++) {
+		if (copy_gctl(emu, gctl, icode->gpr_add_controls, i)) {
 			err = -EFAULT;
 			err = -EFAULT;
 			goto __error;
 			goto __error;
 		}
 		}
@@ -763,11 +814,10 @@ static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu,
 		knew.device = gctl->id.device;
 		knew.device = gctl->id.device;
 		knew.subdevice = gctl->id.subdevice;
 		knew.subdevice = gctl->id.subdevice;
 		knew.info = snd_emu10k1_gpr_ctl_info;
 		knew.info = snd_emu10k1_gpr_ctl_info;
-		if (gctl->tlv.p) {
-			knew.tlv.p = gctl->tlv.p;
+		knew.tlv.p = copy_tlv(gctl->tlv);
+		if (knew.tlv.p)
 			knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
 			knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
 				SNDRV_CTL_ELEM_ACCESS_TLV_READ;
 				SNDRV_CTL_ELEM_ACCESS_TLV_READ;
-		} 
 		knew.get = snd_emu10k1_gpr_ctl_get;
 		knew.get = snd_emu10k1_gpr_ctl_get;
 		knew.put = snd_emu10k1_gpr_ctl_put;
 		knew.put = snd_emu10k1_gpr_ctl_put;
 		memset(nctl, 0, sizeof(*nctl));
 		memset(nctl, 0, sizeof(*nctl));
@@ -785,12 +835,14 @@ static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu,
 			ctl = kmalloc(sizeof(*ctl), GFP_KERNEL);
 			ctl = kmalloc(sizeof(*ctl), GFP_KERNEL);
 			if (ctl == NULL) {
 			if (ctl == NULL) {
 				err = -ENOMEM;
 				err = -ENOMEM;
+				kfree(knew.tlv.p);
 				goto __error;
 				goto __error;
 			}
 			}
 			knew.private_value = (unsigned long)ctl;
 			knew.private_value = (unsigned long)ctl;
 			*ctl = *nctl;
 			*ctl = *nctl;
 			if ((err = snd_ctl_add(emu->card, kctl = snd_ctl_new1(&knew, emu))) < 0) {
 			if ((err = snd_ctl_add(emu->card, kctl = snd_ctl_new1(&knew, emu))) < 0) {
 				kfree(ctl);
 				kfree(ctl);
+				kfree(knew.tlv.p);
 				goto __error;
 				goto __error;
 			}
 			}
 			kctl->private_free = snd_emu10k1_ctl_private_free;
 			kctl->private_free = snd_emu10k1_ctl_private_free;
@@ -841,7 +893,6 @@ static int snd_emu10k1_list_controls(struct snd_emu10k1 *emu,
 	unsigned int i = 0, j;
 	unsigned int i = 0, j;
 	unsigned int total = 0;
 	unsigned int total = 0;
 	struct snd_emu10k1_fx8010_control_gpr *gctl;
 	struct snd_emu10k1_fx8010_control_gpr *gctl;
-	struct snd_emu10k1_fx8010_control_gpr __user *_gctl;
 	struct snd_emu10k1_fx8010_ctl *ctl;
 	struct snd_emu10k1_fx8010_ctl *ctl;
 	struct snd_ctl_elem_id *id;
 	struct snd_ctl_elem_id *id;
 	struct list_head *list;
 	struct list_head *list;
@@ -850,11 +901,11 @@ static int snd_emu10k1_list_controls(struct snd_emu10k1 *emu,
 	if (! gctl)
 	if (! gctl)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
-	_gctl = icode->gpr_list_controls;	
 	list_for_each(list, &emu->fx8010.gpr_ctl) {
 	list_for_each(list, &emu->fx8010.gpr_ctl) {
 		ctl = emu10k1_gpr_ctl(list);
 		ctl = emu10k1_gpr_ctl(list);
 		total++;
 		total++;
-		if (_gctl && i < icode->gpr_list_control_count) {
+		if (icode->gpr_list_controls &&
+		    i < icode->gpr_list_control_count) {
 			memset(gctl, 0, sizeof(*gctl));
 			memset(gctl, 0, sizeof(*gctl));
 			id = &ctl->kcontrol->id;
 			id = &ctl->kcontrol->id;
 			gctl->id.iface = id->iface;
 			gctl->id.iface = id->iface;
@@ -871,11 +922,11 @@ static int snd_emu10k1_list_controls(struct snd_emu10k1 *emu,
 			gctl->min = ctl->min;
 			gctl->min = ctl->min;
 			gctl->max = ctl->max;
 			gctl->max = ctl->max;
 			gctl->translation = ctl->translation;
 			gctl->translation = ctl->translation;
-			if (copy_to_user(_gctl, gctl, sizeof(*gctl))) {
+			if (copy_gctl_to_user(emu, icode->gpr_list_controls,
+					      gctl, i)) {
 				kfree(gctl);
 				kfree(gctl);
 				return -EFAULT;
 				return -EFAULT;
 			}
 			}
-			_gctl++;
 			i++;
 			i++;
 		}
 		}
 	}
 	}
@@ -1026,7 +1077,7 @@ snd_emu10k1_init_mono_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
 	ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
 	ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
 	ctl->min = 0;
 	ctl->min = 0;
 	ctl->max = 100;
 	ctl->max = 100;
-	ctl->tlv.p = snd_emu10k1_db_scale1;
+	ctl->tlv = snd_emu10k1_db_scale1;
 	ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100;	
 	ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100;	
 }
 }
 
 
@@ -1041,7 +1092,7 @@ snd_emu10k1_init_stereo_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
 	ctl->gpr[1] = gpr + 1; ctl->value[1] = defval;
 	ctl->gpr[1] = gpr + 1; ctl->value[1] = defval;
 	ctl->min = 0;
 	ctl->min = 0;
 	ctl->max = 100;
 	ctl->max = 100;
-	ctl->tlv.p = snd_emu10k1_db_scale1;
+	ctl->tlv = snd_emu10k1_db_scale1;
 	ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100;
 	ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100;
 }
 }
 
 
@@ -1566,7 +1617,9 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
 	seg = snd_enter_user();
 	seg = snd_enter_user();
 	icode->gpr_add_control_count = nctl;
 	icode->gpr_add_control_count = nctl;
 	icode->gpr_add_controls = (struct snd_emu10k1_fx8010_control_gpr __user *)controls;
 	icode->gpr_add_controls = (struct snd_emu10k1_fx8010_control_gpr __user *)controls;
+	emu->support_tlv = 1; /* support TLV */
 	err = snd_emu10k1_icode_poke(emu, icode);
 	err = snd_emu10k1_icode_poke(emu, icode);
+	emu->support_tlv = 0; /* clear again */
 	snd_leave_user(seg);
 	snd_leave_user(seg);
 
 
  __err:
  __err:
@@ -2183,7 +2236,9 @@ static int __devinit _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
 	seg = snd_enter_user();
 	seg = snd_enter_user();
 	icode->gpr_add_control_count = i;
 	icode->gpr_add_control_count = i;
 	icode->gpr_add_controls = (struct snd_emu10k1_fx8010_control_gpr __user *)controls;
 	icode->gpr_add_controls = (struct snd_emu10k1_fx8010_control_gpr __user *)controls;
+	emu->support_tlv = 1; /* support TLV */
 	err = snd_emu10k1_icode_poke(emu, icode);
 	err = snd_emu10k1_icode_poke(emu, icode);
+	emu->support_tlv = 0; /* clear again */
 	snd_leave_user(seg);
 	snd_leave_user(seg);
 	if (err >= 0)
 	if (err >= 0)
 		err = snd_emu10k1_ipcm_poke(emu, ipcm);
 		err = snd_emu10k1_ipcm_poke(emu, ipcm);
@@ -2327,6 +2382,9 @@ static int snd_emu10k1_fx8010_ioctl(struct snd_hwdep * hw, struct file *file, un
 	int res;
 	int res;
 	
 	
 	switch (cmd) {
 	switch (cmd) {
+	case SNDRV_EMU10K1_IOCTL_PVERSION:
+		emu->support_tlv = 1;
+		return put_user(SNDRV_EMU10K1_VERSION, (int __user *)argp);
 	case SNDRV_EMU10K1_IOCTL_INFO:
 	case SNDRV_EMU10K1_IOCTL_INFO:
 		info = kmalloc(sizeof(*info), GFP_KERNEL);
 		info = kmalloc(sizeof(*info), GFP_KERNEL);
 		if (!info)
 		if (!info)