All of 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 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.