All of lore.kernel.org
 help / color / mirror / Atom feed
* Line6 TonePort UX1 driver
@ 2006-05-08 17:16 Stefano D Angelo
  2006-05-10 13:29 ` Takashi Iwai
  0 siblings, 1 reply; 9+ messages in thread
From: Stefano D Angelo @ 2006-05-08 17:16 UTC (permalink / raw)
  To: alsa-devel

Hello everyone,
I'm an italian university student and I'm developing an ALSA driver for Line6
TonePort UX1 USB sound card.
This is my first driver-writing experience, so maybe this is a dumb question
for you.
This soundcard supports 44.1/48 kHz 16/24-bits modes and full-duplex, but only
in one mode due to USB altesttings.
To be more precise if you are playing some audio stream at 44.1 kHz, 16-bits,
you can only capture data at 44.1 kHz and 16-bits, for example.
The Windows' driver uses some kind of implicit conversion for
playback/capture, so that it uses only 48 kHz, 24-bit mode (unless you use a
program to switch).
Do you think I should use such a compromise too or go on another way?

Stefano D'Angelo

-----------------
Registra il tuo nome di dominio su: http://www.oneonline.it/domini
One On Line ti offre domino + spazio web illimitato + 5 email + 5000 uscite banner + PHP + Link a soli Euro 15 iva compresa



-------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Line6 TonePort UX1 driver
  2006-05-08 17:16 Line6 TonePort UX1 driver Stefano D Angelo
@ 2006-05-10 13:29 ` Takashi Iwai
       [not found]   ` <20060510165635.M757@oneonline.it>
  0 siblings, 1 reply; 9+ messages in thread
From: Takashi Iwai @ 2006-05-10 13:29 UTC (permalink / raw)
  To: Stefano D Angelo; +Cc: alsa-devel

At Mon, 8 May 2006 20:16:19 +0300,
Stefano D Angelo wrote:
> 
> Hello everyone,
> I'm an italian university student and I'm developing an ALSA driver for Line6
> TonePort UX1 USB sound card.
> This is my first driver-writing experience, so maybe this is a dumb question
> for you.
> This soundcard supports 44.1/48 kHz 16/24-bits modes and full-duplex, but only
> in one mode due to USB altesttings.
> To be more precise if you are playing some audio stream at 44.1 kHz, 16-bits,
> you can only capture data at 44.1 kHz and 16-bits, for example.
> The Windows' driver uses some kind of implicit conversion for
> playback/capture, so that it uses only 48 kHz, 24-bit mode (unless you use a
> program to switch).
> Do you think I should use such a compromise too or go on another way?

The condition can be restricted via hw_constraints in ALSA.
When a playback (or a capture) is opened first, the driver remembers
the parameter.  At the secondary open of another side, call
snd_pcm_hw_constraint_*() additionally to restrict the PCM
parameters to be accepted.


Takashi


-------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Line6 TonePort UX1 driver
       [not found]   ` <20060510165635.M757@oneonline.it>
@ 2006-05-10 15:34     ` Takashi Iwai
       [not found]       ` <20060512004915.M47766@oneonline.it>
  0 siblings, 1 reply; 9+ messages in thread
From: Takashi Iwai @ 2006-05-10 15:34 UTC (permalink / raw)
  To: Stefano D Angelo; +Cc: alsa-devel

At Wed, 10 May 2006 16:56:35 +0300,
Stefano D Angelo wrote:
> 
> > At Mon, 8 May 2006 20:16:19 +0300,
> > Stefano D Angelo wrote:
> > > 
> > > Hello everyone,
> > > I'm an italian university student and I'm developing an ALSA driver for Line6
> > > TonePort UX1 USB sound card.
> > > This is my first driver-writing experience, so maybe this is a dumb question
> > > for you.
> > > This soundcard supports 44.1/48 kHz 16/24-bits modes and full-duplex, but only
> > > in one mode due to USB altesttings.
> > > To be more precise if you are playing some audio stream at 44.1 kHz, 16-bits,
> > > you can only capture data at 44.1 kHz and 16-bits, for example.
> > > The Windows' driver uses some kind of implicit conversion for
> > > playback/capture, so that it uses only 48 kHz, 24-bit mode (unless you use a
> > > program to switch).
> > > Do you think I should use such a compromise too or go on another way?
> > 
> > The condition can be restricted via hw_constraints in ALSA.
> > When a playback (or a capture) is opened first, the driver remembers
> > the parameter.  At the secondary open of another side, call
> > snd_pcm_hw_constraint_*() additionally to restrict the PCM
> > parameters to be accepted.
> > 
> > Takashi

[Added alsa-devel back to Cc]

> That's not the point of the question. Probably I'm not a good English speaker
> and writer, but I'll try to make you understand the same.
> Think about what happens perhaps in a recording studio: some output (a tick or
> something like that) is sent to a pair of hearphones and, while listening, you
> have to play your instrument for recording.
> Suppose that the tick is in 44.1 kHz 16-bits format. If you start playing it
> and then you want to record, this sound card lets you do it only at the
> current playback bit/sample-rate (this is due to altsettings of the card).
> So I noticed that the Windows' driver (or some other software built in
> Windows) converts every output sent to the card in 48 kHz 24-bits format, in
> order to be able to record always at the maximum rate. Then you can obviously
> do downsampling if you want.
> I was asking you whether I should do like Windows do (in the ALSA world I
> think it means "use 48 kHz 24-bits only format", then ALSA does the
> conversions) or not.

It's your choice, but I vote for allowing multiple formats.
The native support of 16 bit is preferred in some cases.  The software
conversion of 24bit format may cost much on an old slow machine.

> Right now I did the first way, but now I've got another (strange) problem with
> ALSA.
> 48 kHz 24-bits output mostly works, but when I need a conversion (for example
> playing a 44.1 kHz 16-bits file), even using plughw:x it says me that no
> sample format is available. I used S24_3LE, 48 kHz, 24-bits, stereo as the
> only format supported in hw_params struct.

That's weird.  Do you use the latest ALSA lib?

> But that's not all. I've got some strange underruns before the file is
> actually played, and I noticed that the trigger function is called many times
> before the actual data transfer (commands START and STOP alternated).
> However I'm going to put source files on sf.net as soon as they open a space
> for this project, so that such things can be better discussed.
> If, anyway, you know something about these things please let me know

Please _post_ your source code to ALSA devel ML if you want
debugs/reviews together with us.  Keeping a forked stuff on sf really
sucks.


thanks,

Takashi


-------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Line6 TonePort UX1 driver
       [not found]       ` <20060512004915.M47766@oneonline.it>
@ 2006-05-12  8:15         ` Stefano D Angelo
  0 siblings, 0 replies; 9+ messages in thread
From: Stefano D Angelo @ 2006-05-12  8:15 UTC (permalink / raw)
  To: alsa-devel

[-- Attachment #1: Type: text/plain, Size: 667 bytes --]

Ok,
I attached the code to this e-mail.
I followed your suggestion, Takashi, and, as you can see, now I'm implementing
all supporte formats for playback.
But another problem arouses... I'm trying to set period/buffer sizes through a
rule_add, but now I can't make it work yet.
playback_copy works only for 24-bits 48000 outputs now, but now I'm going to
solve the problem I just told you first.
Please, let me know and thanks for your supporting.

Stefano

-----------------
Registra il tuo nome di dominio su: http://www.oneonline.it/domini
One On Line ti offre domino + spazio web illimitato + 5 email + 5000 uscite banner + PHP + Link a soli Euro 15 iva compresa


[-- Attachment #2: snd_line6_tp_ux1.c --]
[-- Type: application/octet-stream, Size: 25202 bytes --]

/*
 *   Line6 TonePort UX1 (and maybe TonePort UX2) USB driver for ALSA
 *
 *
 *   Copyright (c) 2006 by Stefano D'Angelo <daste@oneonline.it>
 *
 *   Some code borrowed from
 *          USB Audio Driver for ALSA by Takashi Iwai <tiwai@suse.de>
 *          "Writing an ALSA Driver" by Takashi Iwai <tiwai@suse.de>
 *          "Linux USB Driver Basics" at http://www.linuxtecharticles.com
 *   and maybe, consequently, also from audio.c by
 *	    Alan Cox <alan@lxorguk.ukuu.org.uk>
 *	    Thomas Sailer <sailer@ife.ee.ethz.ch>
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 */

#include <linux/usb.h>
#include <linux/device.h>
#include <sound/driver.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/initval.h>
#include <sound/control.h>

#include "snd_line6_tp_ux1.h"

/* urb list struct - TODO: maybe convert to list_head */

struct urb_list {
	struct urb *urb;
	struct urb_list *next;
};

/* chip struct */

struct snd_line6_tp_ux1 {
	int index;			/* device index */

	struct usb_device *dev;
	snd_card_t *card;
	snd_pcm_t *pcm;

	int altsetting;			/* current altsetting */

	unsigned int playback_pos;	/* buffer position in frames */

	unsigned int playback_lvol;	/* master playback left volume */
	unsigned int playback_rvol;	/* master playback right volume */

	struct urb_list *pending_playback_urbs;
	struct urb_list *last_pending_playback_urb;
};

/* function prototypes */

static int snd_line6_tp_ux1_driver_init ();
static void snd_line6_tp_ux1_driver_exit ();

static int snd_line6_tp_ux1_probe (struct usb_interface *intf,
                                   const struct usb_device_id *id);
static int snd_line6_tp_ux1_create (struct usb_device *dev, int idx);
static int snd_line6_tp_ux1_init_msgs (struct usb_device* dev);
static int snd_line6_tp_ux1_new_pcm (struct snd_line6_tp_ux1 *chip);
static void snd_line6_tp_ux1_disconnect (struct usb_interface *intf);
static int snd_line6_tp_ux1_free (struct snd_line6_tp_ux1 *chip_ptr);
static int snd_line6_tp_ux1_dev_free (snd_device_t *device);

static int snd_line6_tp_ux1_submit_control_transfer (struct usb_device* dev,
                                                     unsigned char* packet);
static void snd_line6_tp_ux1_complete_control_transfer (struct urb* urb,
                                                        struct pt_regs* regs);
static void snd_line6_tp_ux1_complete_playback_transfer (struct urb* urb,
                                                         struct pt_regs* regs);

static int snd_line6_tp_ux1_hw_period_by_rate (snd_pcm_hw_params_t *params,
                                               snd_pcm_hw_rule_t *rule);

static int snd_line6_tp_ux1_playback_open (snd_pcm_substream_t *substream);
static int snd_line6_tp_ux1_playback_close (snd_pcm_substream_t *substream);
static int snd_line6_tp_ux1_pcm_hw_params (snd_pcm_substream_t *substream,
                                           snd_pcm_hw_params_t *hw_params);
static int snd_line6_tp_ux1_pcm_hw_free (snd_pcm_substream_t *substream);
static int snd_line6_tp_ux1_pcm_prepare (snd_pcm_substream_t *substream);
static int snd_line6_tp_ux1_pcm_trigger (snd_pcm_substream_t *substream,
                                         int cmd);
static snd_pcm_uframes_t snd_line6_tp_ux1_pcm_pointer (
                                               snd_pcm_substream_t* substream);
static int snd_line6_tp_ux1_playback_copy (snd_pcm_substream_t *substream,
                                           int channel, snd_pcm_uframes_t pos,
                                           void *src, snd_pcm_uframes_t count);
/* static int snd_line6_tp_ux1_playback_silence (snd_pcm_substream_t *substream,
                                              int channel,
                                              snd_pcm_uframes_t pos,
                                              snd_pcm_uframes_t count); */

static inline s32 snd_line6_tp_ux1_volumize_24 (u8 byte1, u8 byte2, u8 byte3,
                                                float norm_vol);

static int snd_line6_tp_ux1_playback_vol_info (snd_kcontrol_t *kcontrol,
                                               snd_ctl_elem_info_t *uinfo);
static int snd_line6_tp_ux1_playback_vol_get (snd_kcontrol_t *kcontrol,
                                              snd_ctl_elem_value_t *ucontrol);
static int snd_line6_tp_ux1_playback_vol_put (snd_kcontrol_t *kcontrol,
                                              snd_ctl_elem_value_t *ucontrol);

/* global variables */

static int  index[SNDRV_CARDS]  = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS]    = SNDRV_DEFAULT_STR;
static int  enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;

static struct snd_line6_tp_ux1 *chip[SNDRV_CARDS] = {NULL};

/* NOTE: add TonePort UX2 PID? */
static struct usb_device_id snd_line6_tp_ux1_id_table [] =
{
	{ USB_DEVICE (LINE6_VENDOR_ID, LINE6_TP_UX1_PID) },
	{ }
};

static struct usb_driver snd_line6_tp_ux1_driver = {
	.owner      = THIS_MODULE,
	.name       = "snd-line6-tp-ux1",
	.id_table   = snd_line6_tp_ux1_id_table,
	.probe      = snd_line6_tp_ux1_probe,
	.disconnect = snd_line6_tp_ux1_disconnect
};

static snd_pcm_hardware_t snd_line6_tp_ux1_playback_hw = {
	.info             = SNDRV_PCM_INFO_INTERLEAVED,
	.formats          = SNDRV_PCM_FMTBIT_S16_LE |
	                    SNDRV_PCM_FMTBIT_S24_3LE,
	.rates            = SNDRV_PCM_RATE_44100 |
	                    SNDRV_PCM_RATE_48000,
	.rate_min         = 44100,
	.rate_max         = 48000,
	.channels_min     = 2,
	.channels_max     = 2,
	/* double buffering */
	.buffer_bytes_max = LINE6_TP_UX1_48000_24_PACKET_SIZE_OUT * 1024 * 2,
	.period_bytes_min = LINE6_TP_UX1_44100_16_PACKET_SIZE_OUT * 2,
	.period_bytes_max = LINE6_TP_UX1_48000_24_PACKET_SIZE_OUT * 2,
	.periods_min      = 1,
	.periods_max      = 1024
};

static snd_device_ops_t snd_line6_tp_ux1_ops =
{
        .dev_free = snd_line6_tp_ux1_dev_free
};

static snd_pcm_ops_t snd_line6_tp_ux1_playback_ops = {
	.open      = snd_line6_tp_ux1_playback_open,
	.close     = snd_line6_tp_ux1_playback_close,
	.ioctl     = snd_pcm_lib_ioctl,
	.hw_params = snd_line6_tp_ux1_pcm_hw_params,
	.hw_free   = snd_line6_tp_ux1_pcm_hw_free,
	.prepare   = snd_line6_tp_ux1_pcm_prepare,
	.trigger   = snd_line6_tp_ux1_pcm_trigger,
	.pointer   = snd_line6_tp_ux1_pcm_pointer,
	.copy      = snd_line6_tp_ux1_playback_copy,
/*	.silence   = snd_line6_tp_ux1_playback_silence */
};

/* TODO: capture ops */

static snd_kcontrol_new_t snd_line6_tp_ux1_playback_vol = {
	.iface  = SNDRV_CTL_ELEM_IFACE_MIXER,
	.name   = "Master Playback Volume",
	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
	.info   = snd_line6_tp_ux1_playback_vol_info,
	.get    = snd_line6_tp_ux1_playback_vol_get,
	.put    = snd_line6_tp_ux1_playback_vol_put
};

/* TODO: capture volume */

/* module related stuff */

module_param_array (index, int, NULL, 0444);
MODULE_PARM_DESC (index, "Index value for the Line6 TonePort UX1 adapter.");
module_param_array (id, charp, NULL, 0444);
MODULE_PARM_DESC (id, "ID string for the Line6 TonePort UX1 adapter.");
module_param_array (enable, bool, NULL, 0444);
MODULE_PARM_DESC (enable, "Enable Line6 TonePort UX1 adapter.");

MODULE_AUTHOR ("Stefano D'Angelo <daste@oneonline.it>");
MODULE_DESCRIPTION ("Line6 TonePort UX1 USB driver for ALSA");
MODULE_LICENSE ("GPL");
MODULE_SUPPORTED_DEVICE ("Line6 TonePort UX1 USB");
MODULE_DEVICE_TABLE (usb, snd_line6_tp_ux1_id_table);

/* functions */

/*
 * driver's entry-point
 */
static int snd_line6_tp_ux1_driver_init ()
{
	return usb_register (&snd_line6_tp_ux1_driver);
}

/*
 * driver's exit-point
 */
static void snd_line6_tp_ux1_driver_exit ()
{
	usb_deregister (&snd_line6_tp_ux1_driver);
}

/*
 * probes the sound card
 *
 * checks whether the device is already registered or not and eventually inits
 * it using the first avaible slot.
 *
 * it doesn't seem to happen that this function is executed multpile times for
 * a single card, but we check the same
 */
static int snd_line6_tp_ux1_probe (struct usb_interface *intf,
                                   const struct usb_device_id *id)
{
	struct usb_device *dev = interface_to_usbdev (intf);
	int i, j;

	j = -1;
	for (i = 0; i < SNDRV_CARDS; i++) {
		if (chip[i]) {
			/* couldn't do the following check before */
			if (dev == chip[i]->dev) {
				/* yes, already registered */
				return 0;
			}
		} else {
			/* oh, we've got a slot */
			if (j == -1)
				j = i;
		}
	}

	if (j == -1) {
		snd_printk (KERN_ERR "no available USB audio device\n");
		return -ENODEV;
	}

	return snd_line6_tp_ux1_create (dev, j);
}

/*
 * inits the soundcard and the drivers' handles
 *
 * TODO: add capture volume
 */
static int snd_line6_tp_ux1_create (struct usb_device *dev, int idx)
{
	snd_card_t* card;
	int i;

	chip[idx] = kzalloc (sizeof (struct snd_line6_tp_ux1), GFP_KERNEL);

	/* create a card instance */
	if (!(chip[idx]->card = snd_card_new (index[idx], id[idx], THIS_MODULE,
	    sizeof (struct snd_line6_tp_ux1*)))) {
		snd_printk (KERN_ERR "cannot create card instance %d\n", idx);
		kfree (chip[idx]);
		chip[idx] = NULL;
		return -ENOMEM;
	}

	card = chip[idx]->card;
	card->private_data = chip[idx];

	/* set the driver ID and name strings */
	strlcat (card->driver, "Line6-TP-UX1", sizeof (card->driver));
	strlcat (card->shortname, "Line6 TonePort UX1 USB",
	         sizeof (card->shortname));
	i = strlcat (card->longname, "Line6 TonePort UX1 USB at ",
	             sizeof (card->longname));
	if (i < sizeof (card->longname)) {
		usb_make_path (dev, card->longname + i,
		               sizeof (card->longname) - i);
        }
	if (dev->speed == USB_SPEED_FULL) {
		strlcat (card->longname, ", full speed",
		         sizeof (card->longname));
	} else {
		strlcat (card->longname, ", unknown speed",
		         sizeof (card->longname));
	}

	/* init USB messages */
	if ((i = snd_line6_tp_ux1_init_msgs (dev))) {
		snd_printk (KERN_ERR "failed to send init messages to the "
		            "sound card\n");
		snd_card_free_in_thread (card);
		kfree (chip[idx]);
		chip[idx] = NULL;
		return i;
	}

	/* create components, such as mixer, MIDI, etc. */
	if ((i = snd_device_new (card, SNDRV_DEV_LOWLEVEL, chip[idx],
	                         &snd_line6_tp_ux1_ops))) {
		snd_printk (KERN_ERR "could not create a device handler for "
		            "the card\n");
		snd_card_free_in_thread (card);
		kfree (chip[idx]);
		chip[idx] = NULL;
		return i;
        }
        if ((i = snd_line6_tp_ux1_new_pcm (chip[idx]))) {
		snd_printk (KERN_ERR "could not create a pcm stream for the "
		            "card\n");
        } else {
		if ((i = snd_ctl_add (card, snd_ctl_new1 (
		                                &snd_line6_tp_ux1_playback_vol,
		                                chip[idx]))) < 0) {
			snd_printk (KERN_ERR "could not add the master playback "
			            "volume control to the card\n");
		}
	}

	/* register the card instance */
	if ((i = snd_card_register (card))) {
		snd_printk (KERN_ERR "could not register the card's "
		            "instance!\n");
		snd_card_free_in_thread (card);
		kfree (chip[idx]);
		chip[idx] = NULL;
		return i;
	}

	/* set some other data */
	chip[idx]->index = idx;
	chip[idx]->dev = dev;
	chip[idx]->altsetting = 0;
	chip[idx]->playback_lvol = LINE6_TP_UX1_VOL_MAX;
	chip[idx]->playback_rvol = LINE6_TP_UX1_VOL_MAX;
	chip[idx]->pending_playback_urbs = NULL;

	return 0;
}

/*
 * sends vendor specific init messages to the card
 */
static int snd_line6_tp_ux1_init_msgs (struct usb_device* dev)
{
	unsigned char msg1[] = LINE6_TP_UX1_MSG_1;
	unsigned char msg2[] = LINE6_TP_UX1_MSG_2;
	unsigned char msg3[] = LINE6_TP_UX1_MSG_3;
	unsigned char msg4[] = LINE6_TP_UX1_MSG_4;
	int i;

	if ((i = snd_line6_tp_ux1_submit_control_transfer (dev, msg1)))
		return i;

	if ((i = snd_line6_tp_ux1_submit_control_transfer (dev, msg2)))
		return i;

	if ((i = snd_line6_tp_ux1_submit_control_transfer (dev, msg3)))
		return i;

	return snd_line6_tp_ux1_submit_control_transfer (dev, msg4);
}

/*
 * creates a pcm stream for the device
 *
 * TODO: add capture
 */
static int snd_line6_tp_ux1_new_pcm (struct snd_line6_tp_ux1 *chip)
{
	snd_pcm_t *pcm;
	int err;

	if ((err = snd_pcm_new (chip->card, "Line6 TonePort UX1", 0, 1, 0,
	                        &pcm)) < 0)
		return err;

	pcm->private_data = chip;
	strcpy (pcm->name, "Line6 TonePort UX1");
	chip->pcm = pcm;

	snd_pcm_set_ops (pcm, SNDRV_PCM_STREAM_PLAYBACK,
	                 &snd_line6_tp_ux1_playback_ops);

	snd_pcm_lib_preallocate_pages_for_all (pcm, SNDRV_DMA_TYPE_DEV,
	                                       chip->dev, 64*1024, 64*1024);

        return 0;
}

/*
 * handles the card's disconnection
 */
static void snd_line6_tp_ux1_disconnect (struct usb_interface *intf)
{
	int i;

	/* find the card */
	for (i = 0; i < SNDRV_CARDS; i++) {
		if (chip[i]) {
			/* couldn't do this check before again */
			if (chip[i]->dev == interface_to_usbdev(intf))
				break;
		}
	}

	if (i != SNDRV_CARDS)
		snd_line6_tp_ux1_free (chip[i]);
}

/*
 * fake component destructor
 */
static int snd_line6_tp_ux1_dev_free (snd_device_t *device)
{
        return 0;
}

/*
 * chip-specific destructor
 */
static int snd_line6_tp_ux1_free (struct snd_line6_tp_ux1 *chip_ptr)
{
	int i;

	if (!(i = snd_card_free_in_thread (chip_ptr->card))) {
		chip[chip_ptr->index] = NULL;
		kfree (chip_ptr);
	}

	return i;
}

/*
 * submits an USB control transfer to the card with packet as setup_packet
 */
static int snd_line6_tp_ux1_submit_control_transfer (struct usb_device* dev,
                                                     unsigned char* packet)
{
	struct urb* urb;
	struct completion done;
	int i;

	urb = usb_alloc_urb (0, GFP_KERNEL);
	init_completion (&done);
	usb_fill_control_urb (urb, dev, usb_sndctrlpipe (dev, 0), packet, NULL,
	                      0, snd_line6_tp_ux1_complete_control_transfer,
	                      &done);
	i = usb_submit_urb (urb, GFP_KERNEL);
	wait_for_completion (&done);

	return i;
}

/*
 * control transfers' completition callback
 *
 * TODO: write some error handling
 */
static void snd_line6_tp_ux1_complete_control_transfer (struct urb* urb,
                                                        struct pt_regs* regs)
{
	if (!urb->status) {
		usb_free_urb (urb);
	}

	complete (urb->context);
}

/*
 * playback transfers' completition callback
 *
 * TODO: error checking?
 */
static void snd_line6_tp_ux1_complete_playback_transfer (struct urb* urb,
                                                         struct pt_regs* regs)
{
	struct urb_list** pend_urbs = &(chip[*((int*)(urb->context))]->
	                                                pending_playback_urbs);
	struct urb_list *tmp;

	if ((*pend_urbs)->next) {
		tmp = *pend_urbs;
		*pend_urbs = (*pend_urbs)->next;
		kfree (tmp);
	} else {
		kfree (*pend_urbs);
		*pend_urbs = NULL;
	}

	kfree (urb->transfer_buffer);
	usb_free_urb (urb);
}

/*
 * sets hw rules for playback at different rates
 */
static int snd_line6_tp_ux1_hw_period_by_rate (snd_pcm_hw_params_t *params,
                                               snd_pcm_hw_rule_t *rule)
{
	snd_interval_t *p = hw_param_interval (params,
	                                       SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
	snd_interval_t *r = hw_param_interval (params,
	                                       SNDRV_PCM_HW_PARAM_RATE);
	snd_interval_t *bs = hw_param_interval (params,
	                                       SNDRV_PCM_HW_PARAM_BUFFER_SIZE);
	snd_interval_t *bb = hw_param_interval (params,
	                                      SNDRV_PCM_HW_PARAM_BUFFER_BYTES);
	snd_interval_t p_size;
	snd_interval_t b_size;
	int i, j;

	snd_interval_any (&p_size);
	snd_interval_any (&b_size);
	if (r->max == 48000) {
		printk ("a\n");
		p_size.min = p_size.max = 48 * 2;
		b_size.min = 48 * 2;
		b_size.max = 48 * 2 * 1024;
		i = snd_interval_refine (bs, &b_size);
		j = snd_interval_refine (p, &p_size);
		printk ("%d %d %d %d %d %d %d %d\n", i, j, bs->min, bs->max, bb->min, bb->max, p->min, p->max);
		return j;
	} else if (r->max == 44100) {
		printk ("b\n");
		p_size.min = p_size.max = 45 * 2;
		b_size.min = 45 * 2;
		b_size.max = 45 * 2 * 1024;
		printk ("%d %d %d %d %d %d %d %d\n", i, j, bs->min, bs->max, bb->min, bb->max, p->min, p->max);
		i = snd_interval_refine (bs, &b_size);
		j = snd_interval_refine (p, &p_size);
		printk ("%d %d %d %d %d %d %d %d\n", i, j, bs->min, bs->max, bb->min, bb->max, p->min, p->max);
		return j;
	}

	return 0;
}

/*
 * sets the hw_params for a playback stream
 */
static int snd_line6_tp_ux1_playback_open (snd_pcm_substream_t *substream)
{
	struct snd_line6_tp_ux1 *chip = snd_pcm_substream_chip (substream);
	snd_pcm_runtime_t *runtime = substream->runtime;
	int err;

	if ((err = snd_pcm_hw_rule_add (runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
	                                snd_line6_tp_ux1_hw_period_by_rate, 0,
	                                SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1)))
		return err;

	runtime->hw = snd_line6_tp_ux1_playback_hw;

	return 0;
}

/*
 * resets the device to the "normal" (non-transfer) mode
 */
static int snd_line6_tp_ux1_playback_close (snd_pcm_substream_t *substream)
{
	return 0;
}

/*
 * hw_params callback
 *
 * TODO: capture
 */
static int snd_line6_tp_ux1_pcm_hw_params (snd_pcm_substream_t *substream,
                                           snd_pcm_hw_params_t *hw_params)
{
	struct snd_line6_tp_ux1 *chip = snd_pcm_substream_chip (substream);
	unsigned char msg[] = LINE6_TP_UX1_MSG_3;
	int err, err2;

	chip->playback_pos = 0;

	if (chip->altsetting)
		return snd_pcm_lib_malloc_pages (substream,
		                              params_buffer_bytes (hw_params));

	switch (params_rate (hw_params)) {
	case 48000:
		if (params_format (hw_params) == SNDRV_PCM_FMTBIT_S16_LE) {
			chip->altsetting = LINE6_TP_UX1_48000_16_ALTSETTING;
		} else {
			chip->altsetting = LINE6_TP_UX1_48000_24_ALTSETTING;
		}
		break;
	case 44100:
		if (params_format (hw_params) == SNDRV_PCM_FMTBIT_S16_LE) {
			chip->altsetting = LINE6_TP_UX1_44100_16_ALTSETTING;
		} else {
			chip->altsetting = LINE6_TP_UX1_44100_24_ALTSETTING;
		}
		break;
	default:
		chip->altsetting = LINE6_TP_UX1_48000_24_ALTSETTING;
		break;
	}

	printk ("%d\n", params_period_size(hw_params));

	if ((err = usb_set_interface (chip->dev, 0, chip->altsetting)) < 0) {
		chip->altsetting = 0;
		return err;
	}

	if ((err = snd_line6_tp_ux1_submit_control_transfer (chip->dev, msg)) <
	    0) {
		chip->altsetting = 0;
		if ((err2 = usb_set_interface (chip->dev, 0, 0)) < 0)
			return err2;

		return err;
	}

	return snd_pcm_lib_malloc_pages (substream,
	                                 params_buffer_bytes (hw_params));
}

/*
 * hw_free callback
 *
 * TODO: unset playback/capture/both modes and relative parameters
 */
static int snd_line6_tp_ux1_pcm_hw_free (snd_pcm_substream_t *substream)
{
	struct snd_line6_tp_ux1 *chip = snd_pcm_substream_chip (substream);
	struct urb_list *list;
	int err;

	if ((list = chip->pending_playback_urbs)) {
		do {
			usb_unlink_urb (list->urb);			
		} while ((list = list->next));
	}

	chip->altsetting = 0;

	if ((err = usb_set_interface (chip->dev, 0 ,0)) < 0)
		return err;

	return snd_pcm_lib_free_pages (substream);
}

/*
 * prepare callback
 *
 * TODO: write it
 */
static int snd_line6_tp_ux1_pcm_prepare (snd_pcm_substream_t *substream)
{
	/* struct snd_line6_tp_ux1 *chip = snd_pcm_substream_chip (substream);
	snd_pcm_runtime_t *runtime = substream->runtime; */

	/* set up the hardware with the current configuration (sample format,
	   sample rate, etc.) */

	return 0;
}

/*
 * trigger callback
 *
 * TODO: write it
 */
static int snd_line6_tp_ux1_pcm_trigger (snd_pcm_substream_t *substream, int cmd)
{
	switch (cmd) {
	case SNDRV_PCM_TRIGGER_START:
		printk ("trigger start\n");
		break;
	case SNDRV_PCM_TRIGGER_STOP:
		printk ("trigger stop\n");
		break;
	default:
		return -EINVAL;
	}

	return 0;
}

/*
 * pointer callback
 *
 * returns the position in frames of the buffer actually sent/received
 *
 * TODO: capture
 */
static snd_pcm_uframes_t snd_line6_tp_ux1_pcm_pointer (
                                               snd_pcm_substream_t* substream)
{
	struct snd_line6_tp_ux1 *chip = snd_pcm_substream_chip (substream);

	return chip->playback_pos;
}

/*
 * does the playback
 *
 * TODO: handle different formats, etc.
 */
static int snd_line6_tp_ux1_playback_copy (snd_pcm_substream_t *substream,
                                           int channel, snd_pcm_uframes_t pos,
                                           void *src, snd_pcm_uframes_t count)
{
	struct snd_line6_tp_ux1 *chip = snd_pcm_substream_chip (substream);
	float norm_lvol = (float) chip->playback_lvol /
	                                          (float) LINE6_TP_UX1_VOL_MAX;
        float norm_rvol = (float) chip->playback_rvol /
	                                          (float) LINE6_TP_UX1_VOL_MAX;
	struct urb* urb;
	u8* data = src;
	u8* buf;
	int val;
	int i = 0;

	urb = usb_alloc_urb (2, GFP_ATOMIC);

	if (!chip->pending_playback_urbs) {
		chip->last_pending_playback_urb = chip->pending_playback_urbs =
		                kmalloc (sizeof (struct urb_list), GFP_ATOMIC);
		chip->pending_playback_urbs->urb = urb;
		chip->pending_playback_urbs->next = NULL;
	} else {
		chip->last_pending_playback_urb =
		chip->last_pending_playback_urb->next =
		                kmalloc (sizeof (struct urb_list), GFP_ATOMIC);
		chip->last_pending_playback_urb->urb = urb;
		chip->last_pending_playback_urb->next = NULL;
	}

	urb->dev = chip->dev;
	urb->pipe = usb_sndisocpipe (urb->dev, 1);
	urb->complete = snd_line6_tp_ux1_complete_playback_transfer;
	urb->context = &chip->index;
	buf = urb->transfer_buffer = kmalloc (
		        LINE6_TP_UX1_48000_24_PACKET_SIZE_OUT * 2, GFP_ATOMIC);
	urb->transfer_buffer_length = LINE6_TP_UX1_48000_24_PACKET_SIZE_OUT *
	                              2;
	urb->transfer_flags = URB_ISO_ASAP;
	urb->interval = 1;
	urb->number_of_packets = 2;
	urb->iso_frame_desc[0].offset = 0;
	urb->iso_frame_desc[0].length = urb->iso_frame_desc[1].offset =
	urb->iso_frame_desc[1].length = LINE6_TP_UX1_48000_24_PACKET_SIZE_OUT;

	while (i < LINE6_TP_UX1_48000_24_PACKET_SIZE_OUT * 2) {
		val = snd_line6_tp_ux1_volumize_24 (data[i + 2], data[i + 1], data[i], norm_lvol);
		buf[i++] = val & 0x000000ff;
		buf[i++] = (val & 0x0000ff00) >> 8;
		buf[i++] = (val & 0x00ff0000) >> 16;
		val = snd_line6_tp_ux1_volumize_24 (data[i + 2], data[i + 1], data[i], norm_rvol);
		buf[i++] = val & 0x000000ff;
		buf[i++] = (val & 0x0000ff00) >> 8;
		buf[i++] = (val & 0x00ff0000) >> 16;
		chip->playback_pos++;
	}

	usb_submit_urb (urb, GFP_ATOMIC);

	return count;
}

/*
 * 24-bit "volumizer" function
 */
static inline s32 snd_line6_tp_ux1_volumize_24 (u8 byte1, u8 byte2, u8 byte3,
                                                float norm_vol)
{
	s32 val = (byte1 << 16) + (byte2 << 8) + byte3;

	if (byte1 & 0x80)
		val |= 0xff000000;

	return (s32) ((float) val * norm_vol);
}

/*
 * sets master playback volume internal representation
 */
static int snd_line6_tp_ux1_playback_vol_info (snd_kcontrol_t *kcontrol,
                                               snd_ctl_elem_info_t *uinfo)
{
	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
	uinfo->count = 2;
	uinfo->value.integer.min = LINE6_TP_UX1_VOL_MIN;
	uinfo->value.integer.max = LINE6_TP_UX1_VOL_MAX;

	return 0;
}

/*
 * puts into ucontrol the current values of playback volumes
 */
static int snd_line6_tp_ux1_playback_vol_get (snd_kcontrol_t *kcontrol,
                                              snd_ctl_elem_value_t *ucontrol)
{
	struct snd_line6_tp_ux1 *chip = snd_kcontrol_chip (kcontrol);

	ucontrol->value.integer.value[0] = chip->playback_lvol;
	ucontrol->value.integer.value[1] = chip->playback_rvol;

	return 0;
}

/*
 * sets values for playback volumes
 */
static int snd_line6_tp_ux1_playback_vol_put (snd_kcontrol_t *kcontrol,
                                              snd_ctl_elem_value_t *ucontrol)
{
	struct snd_line6_tp_ux1 *chip = snd_kcontrol_chip (kcontrol);
	int changed = 0;

	if (chip->playback_lvol != ucontrol->value.integer.value[0]) {
		chip->playback_lvol = ucontrol->value.integer.value[0];
		changed = 1;
	}
	if (chip->playback_rvol != ucontrol->value.integer.value[1]) {
		chip->playback_rvol = ucontrol->value.integer.value[1];
		changed = 1;
	}

	return changed;
}

/* entry and exit-points of this driver */

module_init (snd_line6_tp_ux1_driver_init);
module_exit (snd_line6_tp_ux1_driver_exit);

[-- Attachment #3: snd_line6_tp_ux1.h --]
[-- Type: application/octet-stream, Size: 2563 bytes --]

/*
 *   Line6 TonePort UX1 (and maybe TonePort UX2) USB driver for ALSA
 *
 *
 *   Copyright (c) 2006 by Stefano D'Angelo <daste@oneonline.it>
 *
 *   Some code borrowed from
 *          USB Audio Driver for ALSA by Takashi Iwai <tiwai@suse.de>
 *          "Writing an ALSA Driver" by Takashi Iwai <tiwai@suse.de>
 *          "Linux USB Driver Basics" at http://www.linuxtecharticles.com
 *   and maybe, consequently, also from audio.c by
 *	    Alan Cox <alan@lxorguk.ukuu.org.uk>
 *	    Thomas Sailer <sailer@ife.ee.ethz.ch>
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 */

#ifndef __LINE6_TP_UX1_H_
#define __LINE6_TP_UX1_H_

#define LINE6_VENDOR_ID 0x0e41
#define LINE6_TP_UX1_PID 0x4141

/* obscure control messages */
#define LINE6_TP_UX1_MSG_1 {0x40, 0x67, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00}
#define LINE6_TP_UX1_MSG_2 {0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}
#define LINE6_TP_UX1_MSG_3 {0x40, 0x67, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00}
#define LINE6_TP_UX1_MSG_4 {0x40, 0x67, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00}

/* internal volume representation */
#define LINE6_TP_UX1_VOL_MIN 0x0000
#define LINE6_TP_UX1_VOL_MAX 0xffff

/* 48 kHz, 16-bits */
#define LINE6_TP_UX1_48000_16_ALTSETTING        1
#define LINE6_TP_UX1_48000_16_PACKET_SIZE_OUT 192
#define LINE6_TP_UX1_48000_16_PACKET_SIZE_IN  200

/* 44.1 kHz, 16-bits */
#define LINE6_TP_UX1_44100_16_ALTSETTING        2
#define LINE6_TP_UX1_44100_16_PACKET_SIZE_OUT 180
#define LINE6_TP_UX1_44100_16_PACKET_SIZE_IN  188

/* 48 kHz, 24-bits */
#define LINE6_TP_UX1_48000_24_ALTSETTING        3
#define LINE6_TP_UX1_48000_24_PACKET_SIZE_OUT 288
#define LINE6_TP_UX1_48000_24_PACKET_SIZE_IN  300

/* 44.1 kHz, 24-bits */
#define LINE6_TP_UX1_44100_24_ALTSETTING        4
#define LINE6_TP_UX1_44100_24_PACKET_SIZE_OUT 270
#define LINE6_TP_UX1_44100_24_PACKET_SIZE_IN  282

#endif  /* __LINE6_TP_UX1_H_ */

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Line6 TonePort UX1 driver
@ 2006-05-16  8:57 Stefano D Angelo
  0 siblings, 0 replies; 9+ messages in thread
From: Stefano D Angelo @ 2006-05-16  8:57 UTC (permalink / raw)
  To: alsa-devel

Hey,
are you still receiving my e-mails?

Stefano

-----------------
Registra il tuo nome di dominio su: http://www.oneonline.it/domini
One On Line ti offre domino + spazio web illimitato + 5 email + 5000 uscite banner + PHP + Link a soli Euro 15 iva compresa



-------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Line6 Toneport UX1 driver
@ 2006-06-09  9:30 Stefano D'Angelo
  2006-06-09 13:00 ` Clemens Ladisch
  0 siblings, 1 reply; 9+ messages in thread
From: Stefano D'Angelo @ 2006-06-09  9:30 UTC (permalink / raw)
  To: alsa-devel


[-- Attachment #1.1: Type: text/plain, Size: 1464 bytes --]

I just can't get it working... here's the code I've written:

static void snd_line6_tp_ux1_complete_playback_transfer (struct urb* urb,
                                                         struct pt_regs*
regs)
{
    snd_pcm_substream_t *substream = (snd_pcm_substream_t *)(urb->context);
    struct snd_line6_tp_ux1 *chip = snd_pcm_substream_chip (substream);
    snd_pcm_runtime_t *runtime = substream->runtime;

    urb->dev = chip->dev;
    urb->transfer_dma = runtime->dma_addr;
    urb->transfer_buffer = usb_buffer_alloc (urb->dev,
LINE6_TP_UX1_48000_16_PACKET_SIZE_OUT * 2, GFP_KERNEL, &urb->transfer_dma);
    memcpy (urb->transfer_buffer, runtime->dma_area + chip->playback_pos *
2, LINE6_TP_UX1_48000_16_PACKET_SIZE_OUT * 2);
    urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;

    usb_submit_urb (urb, GFP_ATOMIC);

    chip->playback_pos += LINE6_TP_UX1_48000_16_PACKET_SIZE_OUT * 2 / 4;
    snd_pcm_period_elapsed (substream);
}

don't care about freeing, etc. The problem is that it gives me a kind of
beep sound for anything I play. No underruns, etc., just that sound!
URBs' data seems to be correct and also the transfer seems to happen with no
particular problem. (no error checking right now however).
Another thing is this: I've seen that you decrement the hardware pointer
when it reaches the end of a period, but when I try to do this, it gives me
back garbage values, how is this possible?
What do you think I should do?

[-- Attachment #1.2: Type: text/html, Size: 2050 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



[-- Attachment #3: Type: text/plain, Size: 161 bytes --]

_______________________________________________
Alsa-devel mailing list
Alsa-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Line6 Toneport UX1 driver
  2006-06-09  9:30 Line6 Toneport " Stefano D'Angelo
@ 2006-06-09 13:00 ` Clemens Ladisch
       [not found]   ` <160c13350606100338y63b73447t7e783f2709683e63@mail.gmail.com>
  0 siblings, 1 reply; 9+ messages in thread
From: Clemens Ladisch @ 2006-06-09 13:00 UTC (permalink / raw)
  To: Stefano D'Angelo; +Cc: alsa-devel

Stefano D'Angelo wrote:
>    urb->transfer_dma = runtime->dma_addr;

This will always play the data at the beginning of the buffer.

>    urb->transfer_buffer = usb_buffer_alloc (urb->dev,
> LINE6_TP_UX1_48000_16_PACKET_SIZE_OUT * 2, GFP_KERNEL, &urb->transfer_dma);

This buffer should be allocated when the URB is allocated.

>    memcpy (urb->transfer_buffer, runtime->dma_area + chip->playback_pos *
> 2, LINE6_TP_UX1_48000_16_PACKET_SIZE_OUT * 2);

I hope the buffer size is a multiple of PACKET_SIZE_OUT * 2.

>    urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;

Why the NO_TRANSFER_DMA_MAP?  You shouldn't need to do such
optimizations in the first version of the driver.

>    snd_pcm_period_elapsed (substream);

What is the period size?


Regards,
Clemens

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Line6 Toneport UX1 driver
       [not found]   ` <160c13350606100338y63b73447t7e783f2709683e63@mail.gmail.com>
@ 2006-06-10 11:28     ` Stefano D'Angelo
  0 siblings, 0 replies; 9+ messages in thread
From: Stefano D'Angelo @ 2006-06-10 11:28 UTC (permalink / raw)
  To: alsa-devel


[-- Attachment #1.1: Type: text/plain, Size: 3695 bytes --]

2006/6/9, Clemens Ladisch <clemens@ladisch.de>:
>
> Stefano D'Angelo wrote:
> >    urb->transfer_dma = runtime->dma_addr;
>
> This will always play the data at the beginning of the buffer.


fixed

>    urb->transfer_buffer = usb_buffer_alloc (urb->dev,
> > LINE6_TP_UX1_48000_16_PACKET_SIZE_OUT * 2, GFP_KERNEL,
> &urb->transfer_dma);
>
> This buffer should be allocated when the URB is allocated.


fixed

>    memcpy (urb->transfer_buffer, runtime->dma_area + chip->playback_pos *
> > 2, LINE6_TP_UX1_48000_16_PACKET_SIZE_OUT * 2);
>
> I hope the buffer size is a multiple of PACKET_SIZE_OUT * 2.


it is

>    urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
>
> Why the NO_TRANSFER_DMA_MAP?  You shouldn't need to do such
> optimizations in the first version of the driver.


ok

>    snd_pcm_period_elapsed (substream);
>
> What is the period size?


period size is 96 = LINE6_TP_UX1_48000_16_PACKET_SIZE_OUT * 2 (double
buffering) / 2 (16 bit data), so everytime an urb is sent a period is
elapsed.

Here is how I'm doing right now but I still got the same beep for everything
I play (when I used the copy () callback it kind of worked, is perphaps
possible that the soundcard doesn't support dma transfers?):


static void snd_line6_tp_ux1_complete_playback_transfer (struct urb* urb,
                                                         struct pt_regs*
regs)
{
    snd_pcm_substream_t *substream = (snd_pcm_substream_t *)(urb->context);
    struct snd_line6_tp_ux1 *chip = snd_pcm_substream_chip (substream);
    snd_pcm_runtime_t *runtime = substream->runtime;

    chip->playback_pos += LINE6_TP_UX1_48000_16_PACKET_SIZE_OUT * 2 / 2;
    if (chip->playback_pos == runtime->buffer_size) {
        chip->playback_pos -= runtime->buffer_size;

    }

    memcpy (urb->transfer_buffer, runtime->dma_area + chip->playback_pos *
2, LINE6_TP_UX1_48000_16_PACKET_SIZE_OUT * 2);

    snd_pcm_period_elapsed (substream);

    usb_submit_urb (urb, GFP_ATOMIC);
}

static int snd_line6_tp_ux1_pcm_trigger (snd_pcm_substream_t *substream,
                                         int cmd)

{
    struct snd_line6_tp_ux1 *chip = snd_pcm_substream_chip (substream);
    snd_pcm_runtime_t *runtime = substream->runtime;
    static struct urb* urb;

    switch (cmd) {
    case SNDRV_PCM_TRIGGER_START:
        urb = usb_alloc_urb (2, GFP_ATOMIC);
        urb->dev = chip->dev;
        urb->pipe = usb_sndisocpipe (urb->dev, 1);
        urb->complete = snd_line6_tp_ux1_complete_playback_transfer;
        urb->context = substream;

        urb->transfer_dma = runtime->dma_addr;
        urb->transfer_buffer = usb_buffer_alloc (urb->dev,
                 LINE6_TP_UX1_48000_16_PACKET_SIZE_OUT * 2, GFP_ATOMIC,
                 &urb->transfer_dma);
        memcpy (urb->transfer_buffer, runtime->dma_area,
                            LINE6_TP_UX1_48000_16_PACKET_SIZE_OUT * 2);

        urb->transfer_flags = URB_ISO_ASAP; // | URB_NO_TRANSFER_DMA_MAP;
        urb->interval = 1;
        urb->number_of_packets = 2;
        urb->iso_frame_desc[0].offset = 0;
        urb->iso_frame_desc[0].length = urb->iso_frame_desc[1].offset =
        urb->iso_frame_desc[1].length =
                                 LINE6_TP_UX1_48000_16_PACKET_SIZE_OUT;
        chip->playback_pos = 0;
        usb_submit_urb (urb, GFP_KERNEL);
            break;
    case SNDRV_PCM_TRIGGER_STOP:
        usb_buffer_free (chip->dev,

                         LINE6_TP_UX1_48000_16_PACKET_SIZE_OUT * 2,
                         urb->transfer_buffer, urb->transfer_dma);
        usb_free_urb (urb);
            break;
    default:
            return -EINVAL;
        break;
    }

    return 0;
}

Thanks,
Stefano

[-- Attachment #1.2: Type: text/html, Size: 7938 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



[-- Attachment #3: Type: text/plain, Size: 161 bytes --]

_______________________________________________
Alsa-devel mailing list
Alsa-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Line6 Toneport UX1 driver
@ 2006-06-12  8:54 Stefano D'Angelo
  0 siblings, 0 replies; 9+ messages in thread
From: Stefano D'Angelo @ 2006-06-12  8:54 UTC (permalink / raw)
  To: alsa-devel


[-- Attachment #1.1: Type: text/plain, Size: 3671 bytes --]

Hi,

> Stefano D'Angelo wrote:
> >    urb->transfer_dma = runtime->dma_addr;
>
> This will always play the data at the beginning of the buffer.


fixed

>    urb->transfer_buffer = usb_buffer_alloc (urb->dev,
> > LINE6_TP_UX1_48000_16_PACKET_SIZE_OUT * 2, GFP_KERNEL,
> &urb->transfer_dma);
>
> This buffer should be allocated when the URB is allocated.


fixed

>    memcpy (urb->transfer_buffer, runtime->dma_area + chip->playback_pos *
> > 2, LINE6_TP_UX1_48000_16_PACKET_SIZE_OUT * 2);
>
> I hope the buffer size is a multiple of PACKET_SIZE_OUT * 2.
>

yes, it is

>    urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
>
> Why the NO_TRANSFER_DMA_MAP?  You shouldn't need to do such
> optimizations in the first version of the driver.


ok, put it out

 >    snd_pcm_period_elapsed (substream);
>
> What is the period size?


period size is 96 = LINE6_TP_UX1_48000_16_PACKET_SIZE_OUT * 2 (double
buffering) / 2 (16 bit data), so everytime an urb is sent a period elapses.

Here is how I'm doing right now but I still got the same beep for everything
I play (when I used the copy () callback it kind of worked, is perphaps
possible that this USB soundcard doesn't support dma transfers?):


static void snd_line6_tp_ux1_complete_playback_transfer (struct urb* urb,
                                                         struct pt_regs*
regs)
{
    snd_pcm_substream_t *substream = (snd_pcm_substream_t *)(urb->context);
    struct snd_line6_tp_ux1 *chip = snd_pcm_substream_chip (substream);
    snd_pcm_runtime_t *runtime = substream->runtime;

    chip->playback_pos += LINE6_TP_UX1_48000_16_PACKET_SIZE_OUT * 2 / 2;
    if (chip->playback_pos == runtime->buffer_size) {
        chip->playback_pos -= runtime->buffer_size;

    }

    memcpy (urb->transfer_buffer, runtime->dma_area + chip->playback_pos *
2, LINE6_TP_UX1_48000_16_PACKET_SIZE_OUT * 2);

    snd_pcm_period_elapsed (substream);

    usb_submit_urb (urb, GFP_ATOMIC);
}

static int snd_line6_tp_ux1_pcm_trigger (snd_pcm_substream_t *substream,
                                         int cmd)

{
    struct snd_line6_tp_ux1 *chip = snd_pcm_substream_chip (substream);
    snd_pcm_runtime_t *runtime = substream->runtime;
    static struct urb* urb;

    switch (cmd) {
    case SNDRV_PCM_TRIGGER_START:
        urb = usb_alloc_urb (2, GFP_ATOMIC);
        urb->dev = chip->dev;
        urb->pipe = usb_sndisocpipe (urb->dev, 1);
        urb->complete = snd_line6_tp_ux1_complete_playback_transfer;
        urb->context = substream;

        urb->transfer_dma = runtime->dma_addr;
        urb->transfer_buffer = usb_buffer_alloc (urb->dev,
                 LINE6_TP_UX1_48000_16_PACKET_SIZE_OUT * 2, GFP_ATOMIC,
                 &urb->transfer_dma);
        memcpy (urb->transfer_buffer, runtime->dma_area,
                            LINE6_TP_UX1_48000_16_PACKET_SIZE_OUT * 2);

        urb->transfer_flags = URB_ISO_ASAP; // | URB_NO_TRANSFER_DMA_MAP;
        urb->interval = 1;
        urb->number_of_packets = 2;
        urb->iso_frame_desc[0].offset = 0;
        urb->iso_frame_desc[0].length = urb->iso_frame_desc[1].offset =
        urb->iso_frame_desc[1].length =
                                 LINE6_TP_UX1_48000_16_PACKET_SIZE_OUT;
        chip->playback_pos = 0;
        usb_submit_urb (urb, GFP_KERNEL);
            break;
    case SNDRV_PCM_TRIGGER_STOP:
        usb_buffer_free (chip->dev,

                         LINE6_TP_UX1_48000_16_PACKET_SIZE_OUT * 2,
                         urb->transfer_buffer, urb->transfer_dma);
        usb_free_urb (urb);
            break;
    default:
            return -EINVAL;
        break;
    }

    return 0;
}

Thanks, Stefano

[-- Attachment #1.2: Type: text/html, Size: 7660 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



[-- Attachment #3: Type: text/plain, Size: 161 bytes --]

_______________________________________________
Alsa-devel mailing list
Alsa-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2006-06-12  8:54 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-05-08 17:16 Line6 TonePort UX1 driver Stefano D Angelo
2006-05-10 13:29 ` Takashi Iwai
     [not found]   ` <20060510165635.M757@oneonline.it>
2006-05-10 15:34     ` Takashi Iwai
     [not found]       ` <20060512004915.M47766@oneonline.it>
2006-05-12  8:15         ` Stefano D Angelo
  -- strict thread matches above, loose matches on Subject: below --
2006-05-16  8:57 Stefano D Angelo
2006-06-09  9:30 Line6 Toneport " Stefano D'Angelo
2006-06-09 13:00 ` Clemens Ladisch
     [not found]   ` <160c13350606100338y63b73447t7e783f2709683e63@mail.gmail.com>
2006-06-10 11:28     ` Stefano D'Angelo
2006-06-12  8:54 Stefano D'Angelo

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.