Alsa-Devel Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: "Stefano D Angelo" <daste@oneonline.it>
To: alsa-devel@lists.sourceforge.net
Subject: Re: Line6 TonePort UX1 driver
Date: Fri, 12 May 2006 11:15:52 +0300	[thread overview]
Message-ID: <20060512111552.M16903@oneonline.it> (raw)
In-Reply-To: <20060512004915.M47766@oneonline.it>

[-- 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_ */

  parent reply	other threads:[~2006-05-12  8:15 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 [this message]
  -- 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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20060512111552.M16903@oneonline.it \
    --to=daste@oneonline.it \
    --cc=alsa-devel@lists.sourceforge.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox