* 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