mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 19:21:31 -04:00
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
This commit is contained in:
commit
1da177e4c3
17291 changed files with 6718755 additions and 0 deletions
32
sound/usb/Kconfig
Normal file
32
sound/usb/Kconfig
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# ALSA USB drivers
|
||||
|
||||
menu "USB devices"
|
||||
depends on SND!=n && USB!=n
|
||||
|
||||
config SND_USB_AUDIO
|
||||
tristate "USB Audio/MIDI driver"
|
||||
depends on SND && USB
|
||||
select SND_RAWMIDI
|
||||
select SND_PCM
|
||||
help
|
||||
Say Y here to include support for USB audio and USB MIDI
|
||||
devices.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-usb-audio.
|
||||
|
||||
config SND_USB_USX2Y
|
||||
tristate "Tascam US-122, US-224 and US-428 USB driver"
|
||||
depends on SND && USB && (X86 || PPC || ALPHA)
|
||||
select SND_HWDEP
|
||||
select SND_RAWMIDI
|
||||
select SND_PCM
|
||||
help
|
||||
Say Y here to include support for Tascam USB Audio/MIDI
|
||||
interfaces or controllers US-122, US-224 and US-428.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-usb-usx2y.
|
||||
|
||||
endmenu
|
||||
|
||||
12
sound/usb/Makefile
Normal file
12
sound/usb/Makefile
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#
|
||||
# Makefile for ALSA
|
||||
#
|
||||
|
||||
snd-usb-audio-objs := usbaudio.o usbmixer.o
|
||||
snd-usb-lib-objs := usbmidi.o
|
||||
|
||||
# Toplevel Module Dependency
|
||||
obj-$(CONFIG_SND_USB_AUDIO) += snd-usb-audio.o snd-usb-lib.o
|
||||
obj-$(CONFIG_SND_USB_USX2Y) += snd-usb-lib.o
|
||||
|
||||
obj-$(CONFIG_SND) += usx2y/
|
||||
3337
sound/usb/usbaudio.c
Normal file
3337
sound/usb/usbaudio.c
Normal file
File diff suppressed because it is too large
Load diff
251
sound/usb/usbaudio.h
Normal file
251
sound/usb/usbaudio.h
Normal file
|
|
@ -0,0 +1,251 @@
|
|||
#ifndef __USBAUDIO_H
|
||||
#define __USBAUDIO_H
|
||||
/*
|
||||
* (Tentative) USB Audio Driver for ALSA
|
||||
*
|
||||
* Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#define USB_SUBCLASS_AUDIO_CONTROL 0x01
|
||||
#define USB_SUBCLASS_AUDIO_STREAMING 0x02
|
||||
#define USB_SUBCLASS_MIDI_STREAMING 0x03
|
||||
#define USB_SUBCLASS_VENDOR_SPEC 0xff
|
||||
|
||||
#define CS_AUDIO_UNDEFINED 0x20
|
||||
#define CS_AUDIO_DEVICE 0x21
|
||||
#define CS_AUDIO_CONFIGURATION 0x22
|
||||
#define CS_AUDIO_STRING 0x23
|
||||
#define CS_AUDIO_INTERFACE 0x24
|
||||
#define CS_AUDIO_ENDPOINT 0x25
|
||||
|
||||
#define HEADER 0x01
|
||||
#define INPUT_TERMINAL 0x02
|
||||
#define OUTPUT_TERMINAL 0x03
|
||||
#define MIXER_UNIT 0x04
|
||||
#define SELECTOR_UNIT 0x05
|
||||
#define FEATURE_UNIT 0x06
|
||||
#define PROCESSING_UNIT 0x07
|
||||
#define EXTENSION_UNIT 0x08
|
||||
|
||||
#define AS_GENERAL 0x01
|
||||
#define FORMAT_TYPE 0x02
|
||||
#define FORMAT_SPECIFIC 0x03
|
||||
|
||||
#define EP_GENERAL 0x01
|
||||
|
||||
#define MS_GENERAL 0x01
|
||||
#define MIDI_IN_JACK 0x02
|
||||
#define MIDI_OUT_JACK 0x03
|
||||
|
||||
/* endpoint attributes */
|
||||
#define EP_ATTR_MASK 0x0c
|
||||
#define EP_ATTR_ASYNC 0x04
|
||||
#define EP_ATTR_ADAPTIVE 0x08
|
||||
#define EP_ATTR_SYNC 0x0c
|
||||
|
||||
/* cs endpoint attributes */
|
||||
#define EP_CS_ATTR_SAMPLE_RATE 0x01
|
||||
#define EP_CS_ATTR_PITCH_CONTROL 0x02
|
||||
#define EP_CS_ATTR_FILL_MAX 0x80
|
||||
|
||||
/* Audio Class specific Request Codes */
|
||||
|
||||
#define SET_CUR 0x01
|
||||
#define GET_CUR 0x81
|
||||
#define SET_MIN 0x02
|
||||
#define GET_MIN 0x82
|
||||
#define SET_MAX 0x03
|
||||
#define GET_MAX 0x83
|
||||
#define SET_RES 0x04
|
||||
#define GET_RES 0x84
|
||||
#define SET_MEM 0x05
|
||||
#define GET_MEM 0x85
|
||||
#define GET_STAT 0xff
|
||||
|
||||
/* Terminal Control Selectors */
|
||||
|
||||
#define COPY_PROTECT_CONTROL 0x01
|
||||
|
||||
/* Endpoint Control Selectors */
|
||||
|
||||
#define SAMPLING_FREQ_CONTROL 0x01
|
||||
#define PITCH_CONTROL 0x02
|
||||
|
||||
/* Format Types */
|
||||
#define USB_FORMAT_TYPE_I 0x01
|
||||
#define USB_FORMAT_TYPE_II 0x02
|
||||
#define USB_FORMAT_TYPE_III 0x03
|
||||
|
||||
/* type I */
|
||||
#define USB_AUDIO_FORMAT_PCM 0x01
|
||||
#define USB_AUDIO_FORMAT_PCM8 0x02
|
||||
#define USB_AUDIO_FORMAT_IEEE_FLOAT 0x03
|
||||
#define USB_AUDIO_FORMAT_ALAW 0x04
|
||||
#define USB_AUDIO_FORMAT_MU_LAW 0x05
|
||||
|
||||
/* type II */
|
||||
#define USB_AUDIO_FORMAT_MPEG 0x1001
|
||||
#define USB_AUDIO_FORMAT_AC3 0x1002
|
||||
|
||||
/* type III */
|
||||
#define USB_AUDIO_FORMAT_IEC1937_AC3 0x2001
|
||||
#define USB_AUDIO_FORMAT_IEC1937_MPEG1_LAYER1 0x2002
|
||||
#define USB_AUDIO_FORMAT_IEC1937_MPEG2_NOEXT 0x2003
|
||||
#define USB_AUDIO_FORMAT_IEC1937_MPEG2_EXT 0x2004
|
||||
#define USB_AUDIO_FORMAT_IEC1937_MPEG2_LAYER1_LS 0x2005
|
||||
#define USB_AUDIO_FORMAT_IEC1937_MPEG2_LAYER23_LS 0x2006
|
||||
|
||||
|
||||
/* maximum number of endpoints per interface */
|
||||
#define MIDI_MAX_ENDPOINTS 2
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
typedef struct snd_usb_audio snd_usb_audio_t;
|
||||
|
||||
struct snd_usb_audio {
|
||||
int index;
|
||||
struct usb_device *dev;
|
||||
snd_card_t *card;
|
||||
int shutdown;
|
||||
int num_interfaces;
|
||||
|
||||
struct list_head pcm_list; /* list of pcm streams */
|
||||
int pcm_devs;
|
||||
|
||||
struct list_head midi_list; /* list of midi interfaces */
|
||||
int next_midi_device;
|
||||
|
||||
unsigned int ignore_ctl_error; /* for mixer */
|
||||
};
|
||||
|
||||
/*
|
||||
* Information about devices with broken descriptors
|
||||
*/
|
||||
|
||||
/* special values for .ifnum */
|
||||
#define QUIRK_NO_INTERFACE -2
|
||||
#define QUIRK_ANY_INTERFACE -1
|
||||
|
||||
/* quirk type */
|
||||
#define QUIRK_MIDI_FIXED_ENDPOINT 0
|
||||
#define QUIRK_MIDI_YAMAHA 1
|
||||
#define QUIRK_MIDI_MIDIMAN 2
|
||||
#define QUIRK_COMPOSITE 3
|
||||
#define QUIRK_AUDIO_FIXED_ENDPOINT 4
|
||||
#define QUIRK_AUDIO_STANDARD_INTERFACE 5
|
||||
#define QUIRK_MIDI_STANDARD_INTERFACE 6
|
||||
#define QUIRK_AUDIO_EDIROL_UA700_UA25 7
|
||||
#define QUIRK_AUDIO_EDIROL_UA1000 8
|
||||
#define QUIRK_IGNORE_INTERFACE 9
|
||||
#define QUIRK_MIDI_NOVATION 10
|
||||
#define QUIRK_MIDI_MOTU 11
|
||||
#define QUIRK_MIDI_EMAGIC 12
|
||||
|
||||
typedef struct snd_usb_audio_quirk snd_usb_audio_quirk_t;
|
||||
typedef struct snd_usb_midi_endpoint_info snd_usb_midi_endpoint_info_t;
|
||||
|
||||
struct snd_usb_audio_quirk {
|
||||
const char *vendor_name;
|
||||
const char *product_name;
|
||||
int16_t ifnum;
|
||||
int16_t type;
|
||||
const void *data;
|
||||
};
|
||||
|
||||
/* data for QUIRK_MIDI_FIXED_ENDPOINT */
|
||||
struct snd_usb_midi_endpoint_info {
|
||||
int8_t out_ep; /* ep number, 0 autodetect */
|
||||
uint8_t out_interval; /* interval for interrupt endpoints */
|
||||
int8_t in_ep;
|
||||
uint8_t in_interval;
|
||||
uint16_t out_cables; /* bitmask */
|
||||
uint16_t in_cables; /* bitmask */
|
||||
};
|
||||
|
||||
/* for QUIRK_MIDI_YAMAHA, data is NULL */
|
||||
|
||||
/* for QUIRK_MIDI_MIDIMAN, data points to a snd_usb_midi_endpoint_info
|
||||
* structure (out_cables and in_cables only) */
|
||||
|
||||
/* for QUIRK_COMPOSITE, data points to an array of snd_usb_audio_quirk
|
||||
* structures, terminated with .ifnum = -1 */
|
||||
|
||||
/* for QUIRK_AUDIO_FIXED_ENDPOINT, data points to an audioformat structure */
|
||||
|
||||
/* for QUIRK_AUDIO/MIDI_STANDARD_INTERFACE, data is NULL */
|
||||
|
||||
/* for QUIRK_AUDIO_EDIROL_UA700_UA25/UA1000, data is NULL */
|
||||
|
||||
/* for QUIRK_IGNORE_INTERFACE, data is NULL */
|
||||
|
||||
/* for QUIRK_MIDI_NOVATION and _MOTU, data is NULL */
|
||||
|
||||
/* for QUIRK_MIDI_EMAGIC, data points to a snd_usb_midi_endpoint_info
|
||||
* structure (out_cables and in_cables only) */
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#define combine_word(s) ((*s) | ((unsigned int)(s)[1] << 8))
|
||||
#define combine_triple(s) (combine_word(s) | ((unsigned int)(s)[2] << 16))
|
||||
#define combine_quad(s) (combine_triple(s) | ((unsigned int)(s)[3] << 24))
|
||||
|
||||
unsigned int snd_usb_combine_bytes(unsigned char *bytes, int size);
|
||||
|
||||
void *snd_usb_find_desc(void *descstart, int desclen, void *after, u8 dtype);
|
||||
void *snd_usb_find_csint_desc(void *descstart, int desclen, void *after, u8 dsubtype);
|
||||
|
||||
int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout);
|
||||
|
||||
int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif);
|
||||
|
||||
int snd_usb_create_midi_interface(snd_usb_audio_t *chip, struct usb_interface *iface, const snd_usb_audio_quirk_t *quirk);
|
||||
void snd_usbmidi_input_stop(struct list_head* p);
|
||||
void snd_usbmidi_input_start(struct list_head* p);
|
||||
void snd_usbmidi_disconnect(struct list_head *p, struct usb_driver *driver);
|
||||
|
||||
/*
|
||||
* retrieve usb_interface descriptor from the host interface
|
||||
* (conditional for compatibility with the older API)
|
||||
*/
|
||||
#ifndef get_iface_desc
|
||||
#define get_iface_desc(iface) (&(iface)->desc)
|
||||
#define get_endpoint(alt,ep) (&(alt)->endpoint[ep].desc)
|
||||
#define get_ep_desc(ep) (&(ep)->desc)
|
||||
#define get_cfg_desc(cfg) (&(cfg)->desc)
|
||||
#endif
|
||||
|
||||
#ifndef usb_pipe_needs_resubmit
|
||||
#define usb_pipe_needs_resubmit(pipe) 1
|
||||
#endif
|
||||
|
||||
#ifndef snd_usb_complete_callback
|
||||
#define snd_usb_complete_callback(x) (x)
|
||||
#endif
|
||||
|
||||
#ifndef snd_usb_get_speed
|
||||
#define snd_usb_get_speed(dev) ((dev)->speed)
|
||||
#endif
|
||||
|
||||
#endif /* __USBAUDIO_H */
|
||||
1564
sound/usb/usbmidi.c
Normal file
1564
sound/usb/usbmidi.c
Normal file
File diff suppressed because it is too large
Load diff
1545
sound/usb/usbmixer.c
Normal file
1545
sound/usb/usbmixer.c
Normal file
File diff suppressed because it is too large
Load diff
135
sound/usb/usbmixer_maps.c
Normal file
135
sound/usb/usbmixer_maps.c
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* Additional mixer mapping
|
||||
*
|
||||
* Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
struct usbmix_name_map {
|
||||
int id;
|
||||
const char *name;
|
||||
int control;
|
||||
};
|
||||
|
||||
struct usbmix_ctl_map {
|
||||
int vendor;
|
||||
int product;
|
||||
const struct usbmix_name_map *map;
|
||||
int ignore_ctl_error;
|
||||
};
|
||||
|
||||
/*
|
||||
* USB control mappers for SB Exitigy
|
||||
*/
|
||||
|
||||
/*
|
||||
* Topology of SB Extigy (see on the wide screen :)
|
||||
|
||||
USB_IN[1] --->FU[2]------------------------------+->MU[16]-->PU[17]-+->FU[18]--+->EU[27]--+->EU[21]-->FU[22]--+->FU[23] > Dig_OUT[24]
|
||||
^ | | | |
|
||||
USB_IN[3] -+->SU[5]-->FU[6]--+->MU[14] ->PU[15]->+ | | | +->FU[25] > Dig_OUT[26]
|
||||
^ ^ | | | |
|
||||
Dig_IN[4] -+ | | | | +->FU[28]---------------------> Spk_OUT[19]
|
||||
| | | |
|
||||
Lin-IN[7] -+-->FU[8]---------+ | | +----------------------------------------> Hph_OUT[20]
|
||||
| | |
|
||||
Mic-IN[9] --+->FU[10]----------------------------+ |
|
||||
|| |
|
||||
|| +----------------------------------------------------+
|
||||
VV V
|
||||
++--+->SU[11]-->FU[12] --------------------------------------------------------------------------------------> USB_OUT[13]
|
||||
*/
|
||||
|
||||
static struct usbmix_name_map extigy_map[] = {
|
||||
/* 1: IT pcm */
|
||||
{ 2, "PCM Playback" }, /* FU */
|
||||
/* 3: IT pcm */
|
||||
/* 4: IT digital in */
|
||||
{ 5, NULL }, /* DISABLED: this seems to be bogus on some firmware */
|
||||
{ 6, "Digital In" }, /* FU */
|
||||
/* 7: IT line */
|
||||
{ 8, "Line Playback" }, /* FU */
|
||||
/* 9: IT mic */
|
||||
{ 10, "Mic Playback" }, /* FU */
|
||||
{ 11, "Capture Input Source" }, /* SU */
|
||||
{ 12, "Capture" }, /* FU */
|
||||
/* 13: OT pcm capture */
|
||||
/* 14: MU (w/o controls) */
|
||||
/* 15: PU (3D enh) */
|
||||
/* 16: MU (w/o controls) */
|
||||
{ 17, NULL, 1 }, /* DISABLED: PU-switch (any effect?) */
|
||||
{ 17, "Channel Routing", 2 }, /* PU: mode select */
|
||||
{ 18, "Tone Control - Bass", USB_FEATURE_BASS }, /* FU */
|
||||
{ 18, "Tone Control - Treble", USB_FEATURE_TREBLE }, /* FU */
|
||||
{ 18, "Master Playback" }, /* FU; others */
|
||||
/* 19: OT speaker */
|
||||
/* 20: OT headphone */
|
||||
{ 21, NULL }, /* DISABLED: EU (for what?) */
|
||||
{ 22, "Digital Out Playback" }, /* FU */
|
||||
{ 23, "Digital Out1 Playback" }, /* FU */ /* FIXME: corresponds to 24 */
|
||||
/* 24: OT digital out */
|
||||
{ 25, "IEC958 Optical Playback" }, /* FU */
|
||||
{ 26, "IEC958 Optical Playback" }, /* OT */
|
||||
{ 27, NULL }, /* DISABLED: EU (for what?) */
|
||||
/* 28: FU speaker (mute) */
|
||||
{ 29, NULL }, /* Digital Input Playback Source? */
|
||||
{ 0 } /* terminator */
|
||||
};
|
||||
|
||||
/* LineX FM Transmitter entry - needed to bypass controls bug */
|
||||
static struct usbmix_name_map linex_map[] = {
|
||||
/* 1: IT pcm */
|
||||
/* 2: OT Speaker */
|
||||
{ 3, "Master" }, /* FU: master volume - left / right / mute */
|
||||
{ 0 } /* terminator */
|
||||
};
|
||||
|
||||
/* Section "justlink_map" below added by James Courtier-Dutton <[email protected]>
|
||||
* sourced from Maplin Electronics (http://www.maplin.co.uk), part number A56AK
|
||||
* Part has 2 connectors that act as a single output. (TOSLINK Optical for digital out, and 3.5mm Jack for Analogue out.)
|
||||
* The USB Mixer publishes a Microphone and extra Volume controls for it, but none exist on the device,
|
||||
* so this map removes all unwanted sliders from alsamixer
|
||||
*/
|
||||
|
||||
static struct usbmix_name_map justlink_map[] = {
|
||||
/* 1: IT pcm playback */
|
||||
/* 2: Not present */
|
||||
{ 3, NULL}, /* IT mic (No mic input on device) */
|
||||
/* 4: Not present */
|
||||
/* 5: OT speacker */
|
||||
/* 6: OT pcm capture */
|
||||
{ 7, "Master Playback" }, /* Mute/volume for speaker */
|
||||
{ 8, NULL }, /* Capture Switch (No capture inputs on device) */
|
||||
{ 9, NULL }, /* Capture Mute/volume (No capture inputs on device */
|
||||
/* 0xa: Not present */
|
||||
/* 0xb: MU (w/o controls) */
|
||||
{ 0xc, NULL }, /* Mic feedback Mute/volume (No capture inputs on device) */
|
||||
{ 0 } /* terminator */
|
||||
};
|
||||
|
||||
/*
|
||||
* Control map entries
|
||||
*/
|
||||
|
||||
static struct usbmix_ctl_map usbmix_ctl_maps[] = {
|
||||
{ 0x41e, 0x3000, extigy_map, 1 },
|
||||
{ 0x8bb, 0x2702, linex_map, 1 },
|
||||
{ 0xc45, 0x1158, justlink_map, 0 },
|
||||
{ 0 } /* terminator */
|
||||
};
|
||||
|
||||
1202
sound/usb/usbquirks.h
Normal file
1202
sound/usb/usbquirks.h
Normal file
File diff suppressed because it is too large
Load diff
3
sound/usb/usx2y/Makefile
Normal file
3
sound/usb/usx2y/Makefile
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
snd-usb-usx2y-objs := usbusx2y.o usX2Yhwdep.o usx2yhwdeppcm.o
|
||||
|
||||
obj-$(CONFIG_SND_USB_USX2Y) += snd-usb-usx2y.o
|
||||
280
sound/usb/usx2y/usX2Yhwdep.c
Normal file
280
sound/usb/usx2y/usX2Yhwdep.c
Normal file
|
|
@ -0,0 +1,280 @@
|
|||
/*
|
||||
* Driver for Tascam US-X2Y USB soundcards
|
||||
*
|
||||
* FPGA Loader + ALSA Startup
|
||||
*
|
||||
* Copyright (c) 2003 by Karsten Wiese <annabellesgarden@yahoo.de>
|
||||
*
|
||||
* 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 <sound/driver.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/usb.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/memalloc.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/hwdep.h>
|
||||
#include "usx2y.h"
|
||||
#include "usbusx2y.h"
|
||||
#include "usX2Yhwdep.h"
|
||||
|
||||
int usX2Y_hwdep_pcm_new(snd_card_t* card);
|
||||
|
||||
|
||||
static struct page * snd_us428ctls_vm_nopage(struct vm_area_struct *area, unsigned long address, int *type)
|
||||
{
|
||||
unsigned long offset;
|
||||
struct page * page;
|
||||
void *vaddr;
|
||||
|
||||
snd_printdd("ENTER, start %lXh, ofs %lXh, pgoff %ld, addr %lXh\n",
|
||||
area->vm_start,
|
||||
address - area->vm_start,
|
||||
(address - area->vm_start) >> PAGE_SHIFT,
|
||||
address);
|
||||
|
||||
offset = area->vm_pgoff << PAGE_SHIFT;
|
||||
offset += address - area->vm_start;
|
||||
snd_assert((offset % PAGE_SIZE) == 0, return NOPAGE_OOM);
|
||||
vaddr = (char*)((usX2Ydev_t*)area->vm_private_data)->us428ctls_sharedmem + offset;
|
||||
page = virt_to_page(vaddr);
|
||||
get_page(page);
|
||||
snd_printdd( "vaddr=%p made us428ctls_vm_nopage() return %p; offset=%lX\n", vaddr, page, offset);
|
||||
|
||||
if (type)
|
||||
*type = VM_FAULT_MINOR;
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
static struct vm_operations_struct us428ctls_vm_ops = {
|
||||
.nopage = snd_us428ctls_vm_nopage,
|
||||
};
|
||||
|
||||
static int snd_us428ctls_mmap(snd_hwdep_t * hw, struct file *filp, struct vm_area_struct *area)
|
||||
{
|
||||
unsigned long size = (unsigned long)(area->vm_end - area->vm_start);
|
||||
usX2Ydev_t *us428 = (usX2Ydev_t*)hw->private_data;
|
||||
|
||||
// FIXME this hwdep interface is used twice: fpga download and mmap for controlling Lights etc. Maybe better using 2 hwdep devs?
|
||||
// so as long as the device isn't fully initialised yet we return -EBUSY here.
|
||||
if (!(((usX2Ydev_t*)hw->private_data)->chip_status & USX2Y_STAT_CHIP_INIT))
|
||||
return -EBUSY;
|
||||
|
||||
/* if userspace tries to mmap beyond end of our buffer, fail */
|
||||
if (size > ((PAGE_SIZE - 1 + sizeof(us428ctls_sharedmem_t)) / PAGE_SIZE) * PAGE_SIZE) {
|
||||
snd_printd( "%lu > %lu\n", size, (unsigned long)sizeof(us428ctls_sharedmem_t));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!us428->us428ctls_sharedmem) {
|
||||
init_waitqueue_head(&us428->us428ctls_wait_queue_head);
|
||||
if(!(us428->us428ctls_sharedmem = snd_malloc_pages(sizeof(us428ctls_sharedmem_t), GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
memset(us428->us428ctls_sharedmem, -1, sizeof(us428ctls_sharedmem_t));
|
||||
us428->us428ctls_sharedmem->CtlSnapShotLast = -2;
|
||||
}
|
||||
area->vm_ops = &us428ctls_vm_ops;
|
||||
area->vm_flags |= VM_RESERVED;
|
||||
area->vm_private_data = hw->private_data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int snd_us428ctls_poll(snd_hwdep_t *hw, struct file *file, poll_table *wait)
|
||||
{
|
||||
unsigned int mask = 0;
|
||||
usX2Ydev_t *us428 = (usX2Ydev_t*)hw->private_data;
|
||||
us428ctls_sharedmem_t *shm = us428->us428ctls_sharedmem;
|
||||
if (us428->chip_status & USX2Y_STAT_CHIP_HUP)
|
||||
return POLLHUP;
|
||||
|
||||
poll_wait(file, &us428->us428ctls_wait_queue_head, wait);
|
||||
|
||||
if (shm != NULL && shm->CtlSnapShotLast != shm->CtlSnapShotRed)
|
||||
mask |= POLLIN;
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
|
||||
static int snd_usX2Y_hwdep_open(snd_hwdep_t *hw, struct file *file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_usX2Y_hwdep_release(snd_hwdep_t *hw, struct file *file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_usX2Y_hwdep_dsp_status(snd_hwdep_t *hw, snd_hwdep_dsp_status_t *info)
|
||||
{
|
||||
static char *type_ids[USX2Y_TYPE_NUMS] = {
|
||||
[USX2Y_TYPE_122] = "us122",
|
||||
[USX2Y_TYPE_224] = "us224",
|
||||
[USX2Y_TYPE_428] = "us428",
|
||||
};
|
||||
int id = -1;
|
||||
|
||||
switch (le16_to_cpu(((usX2Ydev_t*)hw->private_data)->chip.dev->descriptor.idProduct)) {
|
||||
case USB_ID_US122:
|
||||
id = USX2Y_TYPE_122;
|
||||
break;
|
||||
case USB_ID_US224:
|
||||
id = USX2Y_TYPE_224;
|
||||
break;
|
||||
case USB_ID_US428:
|
||||
id = USX2Y_TYPE_428;
|
||||
break;
|
||||
}
|
||||
if (0 > id)
|
||||
return -ENODEV;
|
||||
strcpy(info->id, type_ids[id]);
|
||||
info->num_dsps = 2; // 0: Prepad Data, 1: FPGA Code
|
||||
if (((usX2Ydev_t*)hw->private_data)->chip_status & USX2Y_STAT_CHIP_INIT)
|
||||
info->chip_ready = 1;
|
||||
info->version = USX2Y_DRIVER_VERSION;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int usX2Y_create_usbmidi(snd_card_t* card )
|
||||
{
|
||||
static snd_usb_midi_endpoint_info_t quirk_data_1 = {
|
||||
.out_ep =0x06,
|
||||
.in_ep = 0x06,
|
||||
.out_cables = 0x001,
|
||||
.in_cables = 0x001
|
||||
};
|
||||
static snd_usb_audio_quirk_t quirk_1 = {
|
||||
.vendor_name = "TASCAM",
|
||||
.product_name = NAME_ALLCAPS,
|
||||
.ifnum = 0,
|
||||
.type = QUIRK_MIDI_FIXED_ENDPOINT,
|
||||
.data = &quirk_data_1
|
||||
};
|
||||
static snd_usb_midi_endpoint_info_t quirk_data_2 = {
|
||||
.out_ep =0x06,
|
||||
.in_ep = 0x06,
|
||||
.out_cables = 0x003,
|
||||
.in_cables = 0x003
|
||||
};
|
||||
static snd_usb_audio_quirk_t quirk_2 = {
|
||||
.vendor_name = "TASCAM",
|
||||
.product_name = "US428",
|
||||
.ifnum = 0,
|
||||
.type = QUIRK_MIDI_FIXED_ENDPOINT,
|
||||
.data = &quirk_data_2
|
||||
};
|
||||
struct usb_device *dev = usX2Y(card)->chip.dev;
|
||||
struct usb_interface *iface = usb_ifnum_to_if(dev, 0);
|
||||
snd_usb_audio_quirk_t *quirk = le16_to_cpu(dev->descriptor.idProduct) == USB_ID_US428 ? &quirk_2 : &quirk_1;
|
||||
|
||||
snd_printdd("usX2Y_create_usbmidi \n");
|
||||
return snd_usb_create_midi_interface(&usX2Y(card)->chip, iface, quirk);
|
||||
}
|
||||
|
||||
static int usX2Y_create_alsa_devices(snd_card_t* card)
|
||||
{
|
||||
int err;
|
||||
|
||||
do {
|
||||
if ((err = usX2Y_create_usbmidi(card)) < 0) {
|
||||
snd_printk("usX2Y_create_alsa_devices: usX2Y_create_usbmidi error %i \n", err);
|
||||
break;
|
||||
}
|
||||
if ((err = usX2Y_audio_create(card)) < 0)
|
||||
break;
|
||||
if ((err = usX2Y_hwdep_pcm_new(card)) < 0)
|
||||
break;
|
||||
if ((err = snd_card_register(card)) < 0)
|
||||
break;
|
||||
} while (0);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int snd_usX2Y_hwdep_dsp_load(snd_hwdep_t *hw, snd_hwdep_dsp_image_t *dsp)
|
||||
{
|
||||
usX2Ydev_t *priv = hw->private_data;
|
||||
int lret, err = -EINVAL;
|
||||
snd_printdd( "dsp_load %s\n", dsp->name);
|
||||
|
||||
if (access_ok(VERIFY_READ, dsp->image, dsp->length)) {
|
||||
struct usb_device* dev = priv->chip.dev;
|
||||
char *buf = kmalloc(dsp->length, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
if (copy_from_user(buf, dsp->image, dsp->length)) {
|
||||
kfree(buf);
|
||||
return -EFAULT;
|
||||
}
|
||||
err = usb_set_interface(dev, 0, 1);
|
||||
if (err)
|
||||
snd_printk("usb_set_interface error \n");
|
||||
else
|
||||
err = usb_bulk_msg(dev, usb_sndbulkpipe(dev, 2), buf, dsp->length, &lret, 6000);
|
||||
kfree(buf);
|
||||
}
|
||||
if (err)
|
||||
return err;
|
||||
if (dsp->index == 1) {
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
schedule_timeout(HZ/4); // give the device some time
|
||||
err = usX2Y_AsyncSeq04_init(priv);
|
||||
if (err) {
|
||||
snd_printk("usX2Y_AsyncSeq04_init error \n");
|
||||
return err;
|
||||
}
|
||||
err = usX2Y_In04_init(priv);
|
||||
if (err) {
|
||||
snd_printk("usX2Y_In04_init error \n");
|
||||
return err;
|
||||
}
|
||||
err = usX2Y_create_alsa_devices(hw->card);
|
||||
if (err) {
|
||||
snd_printk("usX2Y_create_alsa_devices error %i \n", err);
|
||||
snd_card_free(hw->card);
|
||||
return err;
|
||||
}
|
||||
priv->chip_status |= USX2Y_STAT_CHIP_INIT;
|
||||
snd_printdd("%s: alsa all started\n", hw->name);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int usX2Y_hwdep_new(snd_card_t* card, struct usb_device* device)
|
||||
{
|
||||
int err;
|
||||
snd_hwdep_t *hw;
|
||||
|
||||
if ((err = snd_hwdep_new(card, SND_USX2Y_LOADER_ID, 0, &hw)) < 0)
|
||||
return err;
|
||||
|
||||
hw->iface = SNDRV_HWDEP_IFACE_USX2Y;
|
||||
hw->private_data = usX2Y(card);
|
||||
hw->ops.open = snd_usX2Y_hwdep_open;
|
||||
hw->ops.release = snd_usX2Y_hwdep_release;
|
||||
hw->ops.dsp_status = snd_usX2Y_hwdep_dsp_status;
|
||||
hw->ops.dsp_load = snd_usX2Y_hwdep_dsp_load;
|
||||
hw->ops.mmap = snd_us428ctls_mmap;
|
||||
hw->ops.poll = snd_us428ctls_poll;
|
||||
hw->exclusive = 1;
|
||||
sprintf(hw->name, "/proc/bus/usb/%03d/%03d", device->bus->busnum, device->devnum);
|
||||
return 0;
|
||||
}
|
||||
|
||||
6
sound/usb/usx2y/usX2Yhwdep.h
Normal file
6
sound/usb/usx2y/usX2Yhwdep.h
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#ifndef USX2YHWDEP_H
|
||||
#define USX2YHWDEP_H
|
||||
|
||||
int usX2Y_hwdep_new(snd_card_t* card, struct usb_device* device);
|
||||
|
||||
#endif
|
||||
108
sound/usb/usx2y/usbus428ctldefs.h
Normal file
108
sound/usb/usx2y/usbus428ctldefs.h
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
*
|
||||
* Copyright (c) 2003 by Karsten Wiese <annabellesgarden@yahoo.de>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
enum E_In84{
|
||||
eFader0 = 0,
|
||||
eFader1,
|
||||
eFader2,
|
||||
eFader3,
|
||||
eFader4,
|
||||
eFader5,
|
||||
eFader6,
|
||||
eFader7,
|
||||
eFaderM,
|
||||
eTransport,
|
||||
eModifier = 10,
|
||||
eFilterSelect,
|
||||
eSelect,
|
||||
eMute,
|
||||
|
||||
eSwitch = 15,
|
||||
eWheelGain,
|
||||
eWheelFreq,
|
||||
eWheelQ,
|
||||
eWheelPan,
|
||||
eWheel = 20
|
||||
};
|
||||
|
||||
#define T_RECORD 1
|
||||
#define T_PLAY 2
|
||||
#define T_STOP 4
|
||||
#define T_F_FWD 8
|
||||
#define T_REW 0x10
|
||||
#define T_SOLO 0x20
|
||||
#define T_REC 0x40
|
||||
#define T_NULL 0x80
|
||||
|
||||
|
||||
struct us428_ctls{
|
||||
unsigned char Fader[9];
|
||||
unsigned char Transport;
|
||||
unsigned char Modifier;
|
||||
unsigned char FilterSelect;
|
||||
unsigned char Select;
|
||||
unsigned char Mute;
|
||||
unsigned char UNKNOWN;
|
||||
unsigned char Switch;
|
||||
unsigned char Wheel[5];
|
||||
};
|
||||
|
||||
typedef struct us428_ctls us428_ctls_t;
|
||||
|
||||
typedef struct us428_setByte{
|
||||
unsigned char Offset,
|
||||
Value;
|
||||
}us428_setByte_t;
|
||||
|
||||
enum {
|
||||
eLT_Volume = 0,
|
||||
eLT_Light
|
||||
};
|
||||
|
||||
typedef struct usX2Y_volume {
|
||||
unsigned char Channel,
|
||||
LH,
|
||||
LL,
|
||||
RH,
|
||||
RL;
|
||||
} usX2Y_volume_t;
|
||||
|
||||
struct us428_lights{
|
||||
us428_setByte_t Light[7];
|
||||
};
|
||||
typedef struct us428_lights us428_lights_t;
|
||||
|
||||
typedef struct {
|
||||
char type;
|
||||
union {
|
||||
usX2Y_volume_t vol;
|
||||
us428_lights_t lights;
|
||||
} val;
|
||||
} us428_p4out_t;
|
||||
|
||||
#define N_us428_ctl_BUFS 16
|
||||
#define N_us428_p4out_BUFS 16
|
||||
struct us428ctls_sharedmem{
|
||||
us428_ctls_t CtlSnapShot[N_us428_ctl_BUFS];
|
||||
int CtlSnapShotDiffersAt[N_us428_ctl_BUFS];
|
||||
int CtlSnapShotLast, CtlSnapShotRed;
|
||||
us428_p4out_t p4out[N_us428_p4out_BUFS];
|
||||
int p4outLast, p4outSent;
|
||||
};
|
||||
typedef struct us428ctls_sharedmem us428ctls_sharedmem_t;
|
||||
461
sound/usb/usx2y/usbusx2y.c
Normal file
461
sound/usb/usx2y/usbusx2y.c
Normal file
|
|
@ -0,0 +1,461 @@
|
|||
/*
|
||||
* usbusy2y.c - ALSA USB US-428 Driver
|
||||
*
|
||||
2004-12-14 Karsten Wiese
|
||||
Version 0.8.7.1:
|
||||
snd_pcm_open for rawusb pcm-devices now returns -EBUSY if called without rawusb's hwdep device being open.
|
||||
|
||||
2004-12-02 Karsten Wiese
|
||||
Version 0.8.7:
|
||||
Use macro usb_maxpacket() for portability.
|
||||
|
||||
2004-10-26 Karsten Wiese
|
||||
Version 0.8.6:
|
||||
wake_up() process waiting in usX2Y_urbs_start() on error.
|
||||
|
||||
2004-10-21 Karsten Wiese
|
||||
Version 0.8.5:
|
||||
nrpacks is runtime or compiletime configurable now with tested values from 1 to 4.
|
||||
|
||||
2004-10-03 Karsten Wiese
|
||||
Version 0.8.2:
|
||||
Avoid any possible racing while in prepare callback.
|
||||
|
||||
2004-09-30 Karsten Wiese
|
||||
Version 0.8.0:
|
||||
Simplified things and made ohci work again.
|
||||
|
||||
2004-09-20 Karsten Wiese
|
||||
Version 0.7.3:
|
||||
Use usb_kill_urb() instead of deprecated (kernel 2.6.9) usb_unlink_urb().
|
||||
|
||||
2004-07-13 Karsten Wiese
|
||||
Version 0.7.1:
|
||||
Don't sleep in START/STOP callbacks anymore.
|
||||
us428 channels C/D not handled just for this version, sorry.
|
||||
|
||||
2004-06-21 Karsten Wiese
|
||||
Version 0.6.4:
|
||||
Temporarely suspend midi input
|
||||
to sanely call usb_set_interface() when setting format.
|
||||
|
||||
2004-06-12 Karsten Wiese
|
||||
Version 0.6.3:
|
||||
Made it thus the following rule is enforced:
|
||||
"All pcm substreams of one usX2Y have to operate at the same rate & format."
|
||||
|
||||
2004-04-06 Karsten Wiese
|
||||
Version 0.6.0:
|
||||
Runs on 2.6.5 kernel without any "--with-debug=" things.
|
||||
us224 reported running.
|
||||
|
||||
2004-01-14 Karsten Wiese
|
||||
Version 0.5.1:
|
||||
Runs with 2.6.1 kernel.
|
||||
|
||||
2003-12-30 Karsten Wiese
|
||||
Version 0.4.1:
|
||||
Fix 24Bit 4Channel capturing for the us428.
|
||||
|
||||
2003-11-27 Karsten Wiese, Martin Langer
|
||||
Version 0.4:
|
||||
us122 support.
|
||||
us224 could be tested by uncommenting the sections containing USB_ID_US224
|
||||
|
||||
2003-11-03 Karsten Wiese
|
||||
Version 0.3:
|
||||
24Bit support.
|
||||
"arecord -D hw:1 -c 2 -r 48000 -M -f S24_3LE|aplay -D hw:1 -c 2 -r 48000 -M -f S24_3LE" works.
|
||||
|
||||
2003-08-22 Karsten Wiese
|
||||
Version 0.0.8:
|
||||
Removed EZUSB Firmware. First Stage Firmwaredownload is now done by tascam-firmware downloader.
|
||||
See:
|
||||
http://usb-midi-fw.sourceforge.net/tascam-firmware.tar.gz
|
||||
|
||||
2003-06-18 Karsten Wiese
|
||||
Version 0.0.5:
|
||||
changed to compile with kernel 2.4.21 and alsa 0.9.4
|
||||
|
||||
2002-10-16 Karsten Wiese
|
||||
Version 0.0.4:
|
||||
compiles again with alsa-current.
|
||||
USB_ISO_ASAP not used anymore (most of the time), instead
|
||||
urb->start_frame is calculated here now, some calls inside usb-driver don't need to happen anymore.
|
||||
|
||||
To get the best out of this:
|
||||
Disable APM-support in the kernel as APM-BIOS calls (once each second) hard disable interrupt for many precious milliseconds.
|
||||
This helped me much on my slowish PII 400 & PIII 500.
|
||||
ACPI yet untested but might cause the same bad behaviour.
|
||||
Use a kernel with lowlatency and preemptiv patches applied.
|
||||
To autoload snd-usb-midi append a line
|
||||
post-install snd-usb-us428 modprobe snd-usb-midi
|
||||
to /etc/modules.conf.
|
||||
|
||||
known problems:
|
||||
sliders, knobs, lights not yet handled except MASTER Volume slider.
|
||||
"pcm -c 2" doesn't work. "pcm -c 2 -m direct_interleaved" does.
|
||||
KDE3: "Enable full duplex operation" deadlocks.
|
||||
|
||||
|
||||
2002-08-31 Karsten Wiese
|
||||
Version 0.0.3: audio also simplex;
|
||||
simplifying: iso urbs only 1 packet, melted structs.
|
||||
ASYNC_UNLINK not used anymore: no more crashes so far.....
|
||||
for alsa 0.9 rc3.
|
||||
|
||||
2002-08-09 Karsten Wiese
|
||||
Version 0.0.2: midi works with snd-usb-midi, audio (only fullduplex now) with i.e. bristol.
|
||||
The firmware has been sniffed from win2k us-428 driver 3.09.
|
||||
|
||||
* Copyright (c) 2002 - 2004 Karsten Wiese
|
||||
*
|
||||
* 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 <sound/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/usb.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/pcm.h>
|
||||
|
||||
#include <sound/rawmidi.h>
|
||||
#include "usx2y.h"
|
||||
#include "usbusx2y.h"
|
||||
#include "usX2Yhwdep.h"
|
||||
|
||||
|
||||
|
||||
MODULE_AUTHOR("Karsten Wiese <[email protected]>");
|
||||
MODULE_DESCRIPTION("TASCAM "NAME_ALLCAPS" Version 0.8.7.1");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_SUPPORTED_DEVICE("{{TASCAM(0x1604), "NAME_ALLCAPS"(0x8001)(0x8005)(0x8007) }}");
|
||||
|
||||
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
|
||||
static char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */
|
||||
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
|
||||
|
||||
module_param_array(index, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(index, "Index value for "NAME_ALLCAPS".");
|
||||
module_param_array(id, charp, NULL, 0444);
|
||||
MODULE_PARM_DESC(id, "ID string for "NAME_ALLCAPS".");
|
||||
module_param_array(enable, bool, NULL, 0444);
|
||||
MODULE_PARM_DESC(enable, "Enable "NAME_ALLCAPS".");
|
||||
|
||||
|
||||
static int snd_usX2Y_card_used[SNDRV_CARDS];
|
||||
|
||||
static void usX2Y_usb_disconnect(struct usb_device* usb_device, void* ptr);
|
||||
static void snd_usX2Y_card_private_free(snd_card_t *card);
|
||||
|
||||
/*
|
||||
* pipe 4 is used for switching the lamps, setting samplerate, volumes ....
|
||||
*/
|
||||
static void i_usX2Y_Out04Int(struct urb* urb, struct pt_regs *regs)
|
||||
{
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
if (urb->status) {
|
||||
int i;
|
||||
usX2Ydev_t* usX2Y = urb->context;
|
||||
for (i = 0; i < 10 && usX2Y->AS04.urb[i] != urb; i++);
|
||||
snd_printdd("i_usX2Y_Out04Int() urb %i status=%i\n", i, urb->status);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void i_usX2Y_In04Int(struct urb* urb, struct pt_regs *regs)
|
||||
{
|
||||
int err = 0;
|
||||
usX2Ydev_t *usX2Y = urb->context;
|
||||
us428ctls_sharedmem_t *us428ctls = usX2Y->us428ctls_sharedmem;
|
||||
|
||||
usX2Y->In04IntCalls++;
|
||||
|
||||
if (urb->status) {
|
||||
snd_printdd("Interrupt Pipe 4 came back with status=%i\n", urb->status);
|
||||
return;
|
||||
}
|
||||
|
||||
// printk("%i:0x%02X ", 8, (int)((unsigned char*)usX2Y->In04Buf)[8]); Master volume shows 0 here if fader is at max during boot ?!?
|
||||
if (us428ctls) {
|
||||
int diff = -1;
|
||||
if (-2 == us428ctls->CtlSnapShotLast) {
|
||||
diff = 0;
|
||||
memcpy(usX2Y->In04Last, usX2Y->In04Buf, sizeof(usX2Y->In04Last));
|
||||
us428ctls->CtlSnapShotLast = -1;
|
||||
} else {
|
||||
int i;
|
||||
for (i = 0; i < 21; i++) {
|
||||
if (usX2Y->In04Last[i] != ((char*)usX2Y->In04Buf)[i]) {
|
||||
if (diff < 0)
|
||||
diff = i;
|
||||
usX2Y->In04Last[i] = ((char*)usX2Y->In04Buf)[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (0 <= diff) {
|
||||
int n = us428ctls->CtlSnapShotLast + 1;
|
||||
if (n >= N_us428_ctl_BUFS || n < 0)
|
||||
n = 0;
|
||||
memcpy(us428ctls->CtlSnapShot + n, usX2Y->In04Buf, sizeof(us428ctls->CtlSnapShot[0]));
|
||||
us428ctls->CtlSnapShotDiffersAt[n] = diff;
|
||||
us428ctls->CtlSnapShotLast = n;
|
||||
wake_up(&usX2Y->us428ctls_wait_queue_head);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (usX2Y->US04) {
|
||||
if (0 == usX2Y->US04->submitted)
|
||||
do
|
||||
err = usb_submit_urb(usX2Y->US04->urb[usX2Y->US04->submitted++], GFP_ATOMIC);
|
||||
while (!err && usX2Y->US04->submitted < usX2Y->US04->len);
|
||||
} else
|
||||
if (us428ctls && us428ctls->p4outLast >= 0 && us428ctls->p4outLast < N_us428_p4out_BUFS) {
|
||||
if (us428ctls->p4outLast != us428ctls->p4outSent) {
|
||||
int j, send = us428ctls->p4outSent + 1;
|
||||
if (send >= N_us428_p4out_BUFS)
|
||||
send = 0;
|
||||
for (j = 0; j < URBS_AsyncSeq && !err; ++j)
|
||||
if (0 == usX2Y->AS04.urb[j]->status) {
|
||||
us428_p4out_t *p4out = us428ctls->p4out + send; // FIXME if more then 1 p4out is new, 1 gets lost.
|
||||
usb_fill_bulk_urb(usX2Y->AS04.urb[j], usX2Y->chip.dev,
|
||||
usb_sndbulkpipe(usX2Y->chip.dev, 0x04), &p4out->val.vol,
|
||||
p4out->type == eLT_Light ? sizeof(us428_lights_t) : 5,
|
||||
i_usX2Y_Out04Int, usX2Y);
|
||||
err = usb_submit_urb(usX2Y->AS04.urb[j], GFP_ATOMIC);
|
||||
us428ctls->p4outSent = send;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (err) {
|
||||
snd_printk("In04Int() usb_submit_urb err=%i\n", err);
|
||||
}
|
||||
|
||||
urb->dev = usX2Y->chip.dev;
|
||||
usb_submit_urb(urb, GFP_ATOMIC);
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare some urbs
|
||||
*/
|
||||
int usX2Y_AsyncSeq04_init(usX2Ydev_t* usX2Y)
|
||||
{
|
||||
int err = 0,
|
||||
i;
|
||||
|
||||
if (NULL == (usX2Y->AS04.buffer = kmalloc(URB_DataLen_AsyncSeq*URBS_AsyncSeq, GFP_KERNEL))) {
|
||||
err = -ENOMEM;
|
||||
} else
|
||||
for (i = 0; i < URBS_AsyncSeq; ++i) {
|
||||
if (NULL == (usX2Y->AS04.urb[i] = usb_alloc_urb(0, GFP_KERNEL))) {
|
||||
err = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
usb_fill_bulk_urb( usX2Y->AS04.urb[i], usX2Y->chip.dev,
|
||||
usb_sndbulkpipe(usX2Y->chip.dev, 0x04),
|
||||
usX2Y->AS04.buffer + URB_DataLen_AsyncSeq*i, 0,
|
||||
i_usX2Y_Out04Int, usX2Y
|
||||
);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int usX2Y_In04_init(usX2Ydev_t* usX2Y)
|
||||
{
|
||||
int err = 0;
|
||||
if (! (usX2Y->In04urb = usb_alloc_urb(0, GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
|
||||
if (! (usX2Y->In04Buf = kmalloc(21, GFP_KERNEL))) {
|
||||
usb_free_urb(usX2Y->In04urb);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
init_waitqueue_head(&usX2Y->In04WaitQueue);
|
||||
usb_fill_int_urb(usX2Y->In04urb, usX2Y->chip.dev, usb_rcvintpipe(usX2Y->chip.dev, 0x4),
|
||||
usX2Y->In04Buf, 21,
|
||||
i_usX2Y_In04Int, usX2Y,
|
||||
10);
|
||||
err = usb_submit_urb(usX2Y->In04urb, GFP_KERNEL);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void usX2Y_unlinkSeq(snd_usX2Y_AsyncSeq_t* S)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < URBS_AsyncSeq; ++i) {
|
||||
if (S[i].urb) {
|
||||
usb_kill_urb(S->urb[i]);
|
||||
usb_free_urb(S->urb[i]);
|
||||
S->urb[i] = NULL;
|
||||
}
|
||||
}
|
||||
kfree(S->buffer);
|
||||
}
|
||||
|
||||
|
||||
static struct usb_device_id snd_usX2Y_usb_id_table[] = {
|
||||
{
|
||||
.match_flags = USB_DEVICE_ID_MATCH_DEVICE,
|
||||
.idVendor = 0x1604,
|
||||
.idProduct = USB_ID_US428
|
||||
},
|
||||
{
|
||||
.match_flags = USB_DEVICE_ID_MATCH_DEVICE,
|
||||
.idVendor = 0x1604,
|
||||
.idProduct = USB_ID_US122
|
||||
},
|
||||
{
|
||||
.match_flags = USB_DEVICE_ID_MATCH_DEVICE,
|
||||
.idVendor = 0x1604,
|
||||
.idProduct = USB_ID_US224
|
||||
},
|
||||
{ /* terminator */ }
|
||||
};
|
||||
|
||||
static snd_card_t* usX2Y_create_card(struct usb_device* device)
|
||||
{
|
||||
int dev;
|
||||
snd_card_t* card;
|
||||
for (dev = 0; dev < SNDRV_CARDS; ++dev)
|
||||
if (enable[dev] && !snd_usX2Y_card_used[dev])
|
||||
break;
|
||||
if (dev >= SNDRV_CARDS)
|
||||
return NULL;
|
||||
card = snd_card_new(index[dev], id[dev], THIS_MODULE, sizeof(usX2Ydev_t));
|
||||
if (!card)
|
||||
return NULL;
|
||||
snd_usX2Y_card_used[usX2Y(card)->chip.index = dev] = 1;
|
||||
card->private_free = snd_usX2Y_card_private_free;
|
||||
usX2Y(card)->chip.dev = device;
|
||||
usX2Y(card)->chip.card = card;
|
||||
init_waitqueue_head(&usX2Y(card)->prepare_wait_queue);
|
||||
init_MUTEX (&usX2Y(card)->prepare_mutex);
|
||||
INIT_LIST_HEAD(&usX2Y(card)->chip.midi_list);
|
||||
strcpy(card->driver, "USB "NAME_ALLCAPS"");
|
||||
sprintf(card->shortname, "TASCAM "NAME_ALLCAPS"");
|
||||
sprintf(card->longname, "%s (%x:%x if %d at %03d/%03d)",
|
||||
card->shortname,
|
||||
le16_to_cpu(device->descriptor.idVendor),
|
||||
le16_to_cpu(device->descriptor.idProduct),
|
||||
0,//us428(card)->usbmidi.ifnum,
|
||||
usX2Y(card)->chip.dev->bus->busnum, usX2Y(card)->chip.dev->devnum
|
||||
);
|
||||
snd_card_set_dev(card, &device->dev);
|
||||
return card;
|
||||
}
|
||||
|
||||
|
||||
static void* usX2Y_usb_probe(struct usb_device* device, struct usb_interface *intf, const struct usb_device_id* device_id)
|
||||
{
|
||||
int err;
|
||||
snd_card_t* card;
|
||||
if (le16_to_cpu(device->descriptor.idVendor) != 0x1604 ||
|
||||
(le16_to_cpu(device->descriptor.idProduct) != USB_ID_US122 &&
|
||||
le16_to_cpu(device->descriptor.idProduct) != USB_ID_US224 &&
|
||||
le16_to_cpu(device->descriptor.idProduct) != USB_ID_US428) ||
|
||||
!(card = usX2Y_create_card(device)))
|
||||
return NULL;
|
||||
if ((err = usX2Y_hwdep_new(card, device)) < 0 ||
|
||||
(err = snd_card_register(card)) < 0) {
|
||||
snd_card_free(card);
|
||||
return NULL;
|
||||
}
|
||||
return card;
|
||||
}
|
||||
|
||||
/*
|
||||
* new 2.5 USB kernel API
|
||||
*/
|
||||
static int snd_usX2Y_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
{
|
||||
void *chip;
|
||||
chip = usX2Y_usb_probe(interface_to_usbdev(intf), intf, id);
|
||||
if (chip) {
|
||||
dev_set_drvdata(&intf->dev, chip);
|
||||
return 0;
|
||||
} else
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static void snd_usX2Y_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
usX2Y_usb_disconnect(interface_to_usbdev(intf),
|
||||
dev_get_drvdata(&intf->dev));
|
||||
}
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, snd_usX2Y_usb_id_table);
|
||||
static struct usb_driver snd_usX2Y_usb_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "snd-usb-usx2y",
|
||||
.probe = snd_usX2Y_probe,
|
||||
.disconnect = snd_usX2Y_disconnect,
|
||||
.id_table = snd_usX2Y_usb_id_table,
|
||||
};
|
||||
|
||||
static void snd_usX2Y_card_private_free(snd_card_t *card)
|
||||
{
|
||||
kfree(usX2Y(card)->In04Buf);
|
||||
usb_free_urb(usX2Y(card)->In04urb);
|
||||
if (usX2Y(card)->us428ctls_sharedmem)
|
||||
snd_free_pages(usX2Y(card)->us428ctls_sharedmem, sizeof(*usX2Y(card)->us428ctls_sharedmem));
|
||||
if (usX2Y(card)->chip.index >= 0 && usX2Y(card)->chip.index < SNDRV_CARDS)
|
||||
snd_usX2Y_card_used[usX2Y(card)->chip.index] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Frees the device.
|
||||
*/
|
||||
static void usX2Y_usb_disconnect(struct usb_device* device, void* ptr)
|
||||
{
|
||||
if (ptr) {
|
||||
usX2Ydev_t* usX2Y = usX2Y((snd_card_t*)ptr);
|
||||
struct list_head* p;
|
||||
if (usX2Y->chip_status == USX2Y_STAT_CHIP_HUP) // on 2.6.1 kernel snd_usbmidi_disconnect()
|
||||
return; // calls us back. better leave :-) .
|
||||
usX2Y->chip.shutdown = 1;
|
||||
usX2Y->chip_status = USX2Y_STAT_CHIP_HUP;
|
||||
usX2Y_unlinkSeq(&usX2Y->AS04);
|
||||
usb_kill_urb(usX2Y->In04urb);
|
||||
snd_card_disconnect((snd_card_t*)ptr);
|
||||
/* release the midi resources */
|
||||
list_for_each(p, &usX2Y->chip.midi_list) {
|
||||
snd_usbmidi_disconnect(p, &snd_usX2Y_usb_driver);
|
||||
}
|
||||
if (usX2Y->us428ctls_sharedmem)
|
||||
wake_up(&usX2Y->us428ctls_wait_queue_head);
|
||||
snd_card_free_in_thread((snd_card_t*)ptr);
|
||||
}
|
||||
}
|
||||
|
||||
static int __init snd_usX2Y_module_init(void)
|
||||
{
|
||||
return usb_register(&snd_usX2Y_usb_driver);
|
||||
}
|
||||
|
||||
static void __exit snd_usX2Y_module_exit(void)
|
||||
{
|
||||
usb_deregister(&snd_usX2Y_usb_driver);
|
||||
}
|
||||
|
||||
module_init(snd_usX2Y_module_init)
|
||||
module_exit(snd_usX2Y_module_exit)
|
||||
84
sound/usb/usx2y/usbusx2y.h
Normal file
84
sound/usb/usx2y/usbusx2y.h
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
#ifndef USBUSX2Y_H
|
||||
#define USBUSX2Y_H
|
||||
#include "../usbaudio.h"
|
||||
#include "usbus428ctldefs.h"
|
||||
|
||||
#define NRURBS 2
|
||||
|
||||
|
||||
#define URBS_AsyncSeq 10
|
||||
#define URB_DataLen_AsyncSeq 32
|
||||
typedef struct {
|
||||
struct urb* urb[URBS_AsyncSeq];
|
||||
char* buffer;
|
||||
} snd_usX2Y_AsyncSeq_t;
|
||||
|
||||
typedef struct {
|
||||
int submitted;
|
||||
int len;
|
||||
struct urb* urb[0];
|
||||
} snd_usX2Y_urbSeq_t;
|
||||
|
||||
typedef struct snd_usX2Y_substream snd_usX2Y_substream_t;
|
||||
#include "usx2yhwdeppcm.h"
|
||||
|
||||
typedef struct {
|
||||
snd_usb_audio_t chip;
|
||||
int stride;
|
||||
struct urb *In04urb;
|
||||
void *In04Buf;
|
||||
char In04Last[24];
|
||||
unsigned In04IntCalls;
|
||||
snd_usX2Y_urbSeq_t *US04;
|
||||
wait_queue_head_t In04WaitQueue;
|
||||
snd_usX2Y_AsyncSeq_t AS04;
|
||||
unsigned int rate,
|
||||
format;
|
||||
int chip_status;
|
||||
struct semaphore prepare_mutex;
|
||||
us428ctls_sharedmem_t *us428ctls_sharedmem;
|
||||
int wait_iso_frame;
|
||||
wait_queue_head_t us428ctls_wait_queue_head;
|
||||
snd_usX2Y_hwdep_pcm_shm_t *hwdep_pcm_shm;
|
||||
snd_usX2Y_substream_t *subs[4];
|
||||
snd_usX2Y_substream_t * volatile prepare_subs;
|
||||
wait_queue_head_t prepare_wait_queue;
|
||||
} usX2Ydev_t;
|
||||
|
||||
|
||||
struct snd_usX2Y_substream {
|
||||
usX2Ydev_t *usX2Y;
|
||||
snd_pcm_substream_t *pcm_substream;
|
||||
|
||||
int endpoint;
|
||||
unsigned int maxpacksize; /* max packet size in bytes */
|
||||
|
||||
atomic_t state;
|
||||
#define state_STOPPED 0
|
||||
#define state_STARTING1 1
|
||||
#define state_STARTING2 2
|
||||
#define state_STARTING3 3
|
||||
#define state_PREPARED 4
|
||||
#define state_PRERUNNING 6
|
||||
#define state_RUNNING 8
|
||||
|
||||
int hwptr; /* free frame position in the buffer (only for playback) */
|
||||
int hwptr_done; /* processed frame position in the buffer */
|
||||
int transfer_done; /* processed frames since last period update */
|
||||
|
||||
struct urb *urb[NRURBS]; /* data urb table */
|
||||
struct urb *completed_urb;
|
||||
char *tmpbuf; /* temporary buffer for playback */
|
||||
};
|
||||
|
||||
|
||||
#define usX2Y(c) ((usX2Ydev_t*)(c)->private_data)
|
||||
|
||||
int usX2Y_audio_create(snd_card_t* card);
|
||||
|
||||
int usX2Y_AsyncSeq04_init(usX2Ydev_t* usX2Y);
|
||||
int usX2Y_In04_init(usX2Ydev_t* usX2Y);
|
||||
|
||||
#define NAME_ALLCAPS "US-X2Y"
|
||||
|
||||
#endif
|
||||
1026
sound/usb/usx2y/usbusx2yaudio.c
Normal file
1026
sound/usb/usx2y/usbusx2yaudio.c
Normal file
File diff suppressed because it is too large
Load diff
51
sound/usb/usx2y/usx2y.h
Normal file
51
sound/usb/usx2y/usx2y.h
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Driver for Tascam US-X2Y USB soundcards
|
||||
*
|
||||
* Copyright (c) 2003 by Karsten Wiese <annabellesgarden@yahoo.de>
|
||||
*
|
||||
* 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 __SOUND_USX2Y_COMMON_H
|
||||
#define __SOUND_USX2Y_COMMON_H
|
||||
|
||||
|
||||
#define USX2Y_DRIVER_VERSION 0x0100 /* 0.1.0 */
|
||||
|
||||
|
||||
/* hwdep id string */
|
||||
#define SND_USX2Y_LOADER_ID "USX2Y Loader"
|
||||
#define SND_USX2Y_USBPCM_ID "USX2Y USBPCM"
|
||||
|
||||
/* hardware type */
|
||||
enum {
|
||||
USX2Y_TYPE_122,
|
||||
USX2Y_TYPE_224,
|
||||
USX2Y_TYPE_428,
|
||||
USX2Y_TYPE_NUMS
|
||||
};
|
||||
|
||||
#define USB_ID_US122 0x8007
|
||||
#define USB_ID_US224 0x8005
|
||||
#define USB_ID_US428 0x8001
|
||||
|
||||
/* chip status */
|
||||
enum {
|
||||
USX2Y_STAT_CHIP_INIT = (1 << 0), /* all operational */
|
||||
USX2Y_STAT_CHIP_MMAP_PCM_URBS = (1 << 1), /* pcm transport over mmaped urbs */
|
||||
USX2Y_STAT_CHIP_HUP = (1 << 31), /* all operational */
|
||||
};
|
||||
|
||||
#endif /* __SOUND_USX2Y_COMMON_H */
|
||||
807
sound/usb/usx2y/usx2yhwdeppcm.c
Normal file
807
sound/usb/usx2y/usx2yhwdeppcm.c
Normal file
|
|
@ -0,0 +1,807 @@
|
|||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/* USX2Y "rawusb" aka hwdep_pcm implementation
|
||||
|
||||
Its usb's unableness to atomically handle power of 2 period sized data chuncs
|
||||
at standard samplerates,
|
||||
what led to this part of the usx2y module:
|
||||
It provides the alsa kernel half of the usx2y-alsa-jack driver pair.
|
||||
The pair uses a hardware dependant alsa-device for mmaped pcm transport.
|
||||
Advantage achieved:
|
||||
The usb_hc moves pcm data from/into memory via DMA.
|
||||
That memory is mmaped by jack's usx2y driver.
|
||||
Jack's usx2y driver is the first/last to read/write pcm data.
|
||||
Read/write is a combination of power of 2 period shaping and
|
||||
float/int conversation.
|
||||
Compared to mainline alsa/jack we leave out power of 2 period shaping inside
|
||||
snd-usb-usx2y which needs memcpy() and additional buffers.
|
||||
As a side effect possible unwanted pcm-data coruption resulting of
|
||||
standard alsa's snd-usb-usx2y period shaping scheme falls away.
|
||||
Result is sane jack operation at buffering schemes down to 128frames,
|
||||
2 periods.
|
||||
plain usx2y alsa mode is able to achieve 64frames, 4periods, but only at the
|
||||
cost of easier triggered i.e. aeolus xruns (128 or 256frames,
|
||||
2periods works but is useless cause of crackling).
|
||||
|
||||
This is a first "proof of concept" implementation.
|
||||
Later, funcionalities should migrate to more apropriate places:
|
||||
Userland:
|
||||
- The jackd could mmap its float-pcm buffers directly from alsa-lib.
|
||||
- alsa-lib could provide power of 2 period sized shaping combined with int/float
|
||||
conversation.
|
||||
Currently the usx2y jack driver provides above 2 services.
|
||||
Kernel:
|
||||
- rawusb dma pcm buffer transport should go to snd-usb-lib, so also snd-usb-audio
|
||||
devices can use it.
|
||||
Currently rawusb dma pcm buffer transport (this file) is only available to snd-usb-usx2y.
|
||||
*/
|
||||
|
||||
#include "usbusx2yaudio.c"
|
||||
|
||||
#if defined(USX2Y_NRPACKS_VARIABLE) || (!defined(USX2Y_NRPACKS_VARIABLE) && USX2Y_NRPACKS == 1)
|
||||
|
||||
#include <sound/hwdep.h>
|
||||
|
||||
|
||||
static int usX2Y_usbpcm_urb_capt_retire(snd_usX2Y_substream_t *subs)
|
||||
{
|
||||
struct urb *urb = subs->completed_urb;
|
||||
snd_pcm_runtime_t *runtime = subs->pcm_substream->runtime;
|
||||
int i, lens = 0, hwptr_done = subs->hwptr_done;
|
||||
usX2Ydev_t *usX2Y = subs->usX2Y;
|
||||
if (0 > usX2Y->hwdep_pcm_shm->capture_iso_start) { //FIXME
|
||||
int head = usX2Y->hwdep_pcm_shm->captured_iso_head + 1;
|
||||
if (head >= ARRAY_SIZE(usX2Y->hwdep_pcm_shm->captured_iso))
|
||||
head = 0;
|
||||
usX2Y->hwdep_pcm_shm->capture_iso_start = head;
|
||||
snd_printdd("cap start %i\n", head);
|
||||
}
|
||||
for (i = 0; i < nr_of_packs(); i++) {
|
||||
if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */
|
||||
snd_printk("activ frame status %i. Most propably some hardware problem.\n", urb->iso_frame_desc[i].status);
|
||||
return urb->iso_frame_desc[i].status;
|
||||
}
|
||||
lens += urb->iso_frame_desc[i].actual_length / usX2Y->stride;
|
||||
}
|
||||
if ((hwptr_done += lens) >= runtime->buffer_size)
|
||||
hwptr_done -= runtime->buffer_size;
|
||||
subs->hwptr_done = hwptr_done;
|
||||
subs->transfer_done += lens;
|
||||
/* update the pointer, call callback if necessary */
|
||||
if (subs->transfer_done >= runtime->period_size) {
|
||||
subs->transfer_done -= runtime->period_size;
|
||||
snd_pcm_period_elapsed(subs->pcm_substream);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int usX2Y_iso_frames_per_buffer(snd_pcm_runtime_t *runtime, usX2Ydev_t * usX2Y)
|
||||
{
|
||||
return (runtime->buffer_size * 1000) / usX2Y->rate + 1; //FIXME: so far only correct period_size == 2^x ?
|
||||
}
|
||||
|
||||
/*
|
||||
* prepare urb for playback data pipe
|
||||
*
|
||||
* we copy the data directly from the pcm buffer.
|
||||
* the current position to be copied is held in hwptr field.
|
||||
* since a urb can handle only a single linear buffer, if the total
|
||||
* transferred area overflows the buffer boundary, we cannot send
|
||||
* it directly from the buffer. thus the data is once copied to
|
||||
* a temporary buffer and urb points to that.
|
||||
*/
|
||||
static int usX2Y_hwdep_urb_play_prepare(snd_usX2Y_substream_t *subs,
|
||||
struct urb *urb)
|
||||
{
|
||||
int count, counts, pack;
|
||||
usX2Ydev_t *usX2Y = subs->usX2Y;
|
||||
struct snd_usX2Y_hwdep_pcm_shm *shm = usX2Y->hwdep_pcm_shm;
|
||||
snd_pcm_runtime_t *runtime = subs->pcm_substream->runtime;
|
||||
|
||||
if (0 > shm->playback_iso_start) {
|
||||
shm->playback_iso_start = shm->captured_iso_head -
|
||||
usX2Y_iso_frames_per_buffer(runtime, usX2Y);
|
||||
if (0 > shm->playback_iso_start)
|
||||
shm->playback_iso_start += ARRAY_SIZE(shm->captured_iso);
|
||||
shm->playback_iso_head = shm->playback_iso_start;
|
||||
}
|
||||
|
||||
count = 0;
|
||||
for (pack = 0; pack < nr_of_packs(); pack++) {
|
||||
/* calculate the size of a packet */
|
||||
counts = shm->captured_iso[shm->playback_iso_head].length / usX2Y->stride;
|
||||
if (counts < 43 || counts > 50) {
|
||||
snd_printk("should not be here with counts=%i\n", counts);
|
||||
return -EPIPE;
|
||||
}
|
||||
/* set up descriptor */
|
||||
urb->iso_frame_desc[pack].offset = shm->captured_iso[shm->playback_iso_head].offset;
|
||||
urb->iso_frame_desc[pack].length = shm->captured_iso[shm->playback_iso_head].length;
|
||||
if (atomic_read(&subs->state) != state_RUNNING)
|
||||
memset((char *)urb->transfer_buffer + urb->iso_frame_desc[pack].offset, 0,
|
||||
urb->iso_frame_desc[pack].length);
|
||||
if (++shm->playback_iso_head >= ARRAY_SIZE(shm->captured_iso))
|
||||
shm->playback_iso_head = 0;
|
||||
count += counts;
|
||||
}
|
||||
urb->transfer_buffer_length = count * usX2Y->stride;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static inline void usX2Y_usbpcm_urb_capt_iso_advance(snd_usX2Y_substream_t *subs, struct urb *urb)
|
||||
{
|
||||
int pack;
|
||||
for (pack = 0; pack < nr_of_packs(); ++pack) {
|
||||
struct usb_iso_packet_descriptor *desc = urb->iso_frame_desc + pack;
|
||||
if (NULL != subs) {
|
||||
snd_usX2Y_hwdep_pcm_shm_t *shm = subs->usX2Y->hwdep_pcm_shm;
|
||||
int head = shm->captured_iso_head + 1;
|
||||
if (head >= ARRAY_SIZE(shm->captured_iso))
|
||||
head = 0;
|
||||
shm->captured_iso[head].frame = urb->start_frame + pack;
|
||||
shm->captured_iso[head].offset = desc->offset;
|
||||
shm->captured_iso[head].length = desc->actual_length;
|
||||
shm->captured_iso_head = head;
|
||||
shm->captured_iso_frames++;
|
||||
}
|
||||
if ((desc->offset += desc->length * NRURBS*nr_of_packs()) +
|
||||
desc->length >= SSS)
|
||||
desc->offset -= (SSS - desc->length);
|
||||
}
|
||||
}
|
||||
|
||||
static inline int usX2Y_usbpcm_usbframe_complete(snd_usX2Y_substream_t *capsubs,
|
||||
snd_usX2Y_substream_t *capsubs2,
|
||||
snd_usX2Y_substream_t *playbacksubs, int frame)
|
||||
{
|
||||
int err, state;
|
||||
struct urb *urb = playbacksubs->completed_urb;
|
||||
|
||||
state = atomic_read(&playbacksubs->state);
|
||||
if (NULL != urb) {
|
||||
if (state == state_RUNNING)
|
||||
usX2Y_urb_play_retire(playbacksubs, urb);
|
||||
else
|
||||
if (state >= state_PRERUNNING) {
|
||||
atomic_inc(&playbacksubs->state);
|
||||
}
|
||||
} else {
|
||||
switch (state) {
|
||||
case state_STARTING1:
|
||||
urb = playbacksubs->urb[0];
|
||||
atomic_inc(&playbacksubs->state);
|
||||
break;
|
||||
case state_STARTING2:
|
||||
urb = playbacksubs->urb[1];
|
||||
atomic_inc(&playbacksubs->state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (urb) {
|
||||
if ((err = usX2Y_hwdep_urb_play_prepare(playbacksubs, urb)) ||
|
||||
(err = usX2Y_urb_submit(playbacksubs, urb, frame))) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
playbacksubs->completed_urb = NULL;
|
||||
|
||||
state = atomic_read(&capsubs->state);
|
||||
if (state >= state_PREPARED) {
|
||||
if (state == state_RUNNING) {
|
||||
if ((err = usX2Y_usbpcm_urb_capt_retire(capsubs)))
|
||||
return err;
|
||||
} else {
|
||||
if (state >= state_PRERUNNING)
|
||||
atomic_inc(&capsubs->state);
|
||||
}
|
||||
usX2Y_usbpcm_urb_capt_iso_advance(capsubs, capsubs->completed_urb);
|
||||
if (NULL != capsubs2)
|
||||
usX2Y_usbpcm_urb_capt_iso_advance(NULL, capsubs2->completed_urb);
|
||||
if ((err = usX2Y_urb_submit(capsubs, capsubs->completed_urb, frame)))
|
||||
return err;
|
||||
if (NULL != capsubs2)
|
||||
if ((err = usX2Y_urb_submit(capsubs2, capsubs2->completed_urb, frame)))
|
||||
return err;
|
||||
}
|
||||
capsubs->completed_urb = NULL;
|
||||
if (NULL != capsubs2)
|
||||
capsubs2->completed_urb = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void i_usX2Y_usbpcm_urb_complete(struct urb *urb, struct pt_regs *regs)
|
||||
{
|
||||
snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t*)urb->context;
|
||||
usX2Ydev_t *usX2Y = subs->usX2Y;
|
||||
snd_usX2Y_substream_t *capsubs, *capsubs2, *playbacksubs;
|
||||
|
||||
if (unlikely(atomic_read(&subs->state) < state_PREPARED)) {
|
||||
snd_printdd("hcd_frame=%i ep=%i%s status=%i start_frame=%i\n", usb_get_current_frame_number(usX2Y->chip.dev), subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out", urb->status, urb->start_frame);
|
||||
return;
|
||||
}
|
||||
if (unlikely(urb->status)) {
|
||||
usX2Y_error_urb_status(usX2Y, subs, urb);
|
||||
return;
|
||||
}
|
||||
if (likely((0xFFFF & urb->start_frame) == usX2Y->wait_iso_frame))
|
||||
subs->completed_urb = urb;
|
||||
else {
|
||||
usX2Y_error_sequence(usX2Y, subs, urb);
|
||||
return;
|
||||
}
|
||||
|
||||
capsubs = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
|
||||
capsubs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
|
||||
playbacksubs = usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
|
||||
if (capsubs->completed_urb && atomic_read(&capsubs->state) >= state_PREPARED &&
|
||||
(NULL == capsubs2 || capsubs2->completed_urb) &&
|
||||
(playbacksubs->completed_urb || atomic_read(&playbacksubs->state) < state_PREPARED)) {
|
||||
if (!usX2Y_usbpcm_usbframe_complete(capsubs, capsubs2, playbacksubs, urb->start_frame)) {
|
||||
if (nr_of_packs() <= urb->start_frame &&
|
||||
urb->start_frame <= (2 * nr_of_packs() - 1)) // uhci and ohci
|
||||
usX2Y->wait_iso_frame = urb->start_frame - nr_of_packs();
|
||||
else
|
||||
usX2Y->wait_iso_frame += nr_of_packs();
|
||||
} else {
|
||||
snd_printdd("\n");
|
||||
usX2Y_clients_stop(usX2Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void usX2Y_hwdep_urb_release(struct urb** urb)
|
||||
{
|
||||
usb_kill_urb(*urb);
|
||||
usb_free_urb(*urb);
|
||||
*urb = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* release a substream
|
||||
*/
|
||||
static void usX2Y_usbpcm_urbs_release(snd_usX2Y_substream_t *subs)
|
||||
{
|
||||
int i;
|
||||
snd_printdd("snd_usX2Y_urbs_release() %i\n", subs->endpoint);
|
||||
for (i = 0; i < NRURBS; i++)
|
||||
usX2Y_hwdep_urb_release(subs->urb + i);
|
||||
}
|
||||
|
||||
static void usX2Y_usbpcm_subs_startup_finish(usX2Ydev_t * usX2Y)
|
||||
{
|
||||
usX2Y_urbs_set_complete(usX2Y, i_usX2Y_usbpcm_urb_complete);
|
||||
usX2Y->prepare_subs = NULL;
|
||||
}
|
||||
|
||||
static void i_usX2Y_usbpcm_subs_startup(struct urb *urb, struct pt_regs *regs)
|
||||
{
|
||||
snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t*)urb->context;
|
||||
usX2Ydev_t *usX2Y = subs->usX2Y;
|
||||
snd_usX2Y_substream_t *prepare_subs = usX2Y->prepare_subs;
|
||||
if (NULL != prepare_subs &&
|
||||
urb->start_frame == prepare_subs->urb[0]->start_frame) {
|
||||
atomic_inc(&prepare_subs->state);
|
||||
if (prepare_subs == usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE]) {
|
||||
snd_usX2Y_substream_t *cap_subs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
|
||||
if (cap_subs2 != NULL)
|
||||
atomic_inc(&cap_subs2->state);
|
||||
}
|
||||
usX2Y_usbpcm_subs_startup_finish(usX2Y);
|
||||
wake_up(&usX2Y->prepare_wait_queue);
|
||||
}
|
||||
|
||||
i_usX2Y_usbpcm_urb_complete(urb, regs);
|
||||
}
|
||||
|
||||
/*
|
||||
* initialize a substream's urbs
|
||||
*/
|
||||
static int usX2Y_usbpcm_urbs_allocate(snd_usX2Y_substream_t *subs)
|
||||
{
|
||||
int i;
|
||||
unsigned int pipe;
|
||||
int is_playback = subs == subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
|
||||
struct usb_device *dev = subs->usX2Y->chip.dev;
|
||||
|
||||
pipe = is_playback ? usb_sndisocpipe(dev, subs->endpoint) :
|
||||
usb_rcvisocpipe(dev, subs->endpoint);
|
||||
subs->maxpacksize = usb_maxpacket(dev, pipe, is_playback);
|
||||
if (!subs->maxpacksize)
|
||||
return -EINVAL;
|
||||
|
||||
/* allocate and initialize data urbs */
|
||||
for (i = 0; i < NRURBS; i++) {
|
||||
struct urb** purb = subs->urb + i;
|
||||
if (*purb) {
|
||||
usb_kill_urb(*purb);
|
||||
continue;
|
||||
}
|
||||
*purb = usb_alloc_urb(nr_of_packs(), GFP_KERNEL);
|
||||
if (NULL == *purb) {
|
||||
usX2Y_usbpcm_urbs_release(subs);
|
||||
return -ENOMEM;
|
||||
}
|
||||
(*purb)->transfer_buffer = is_playback ?
|
||||
subs->usX2Y->hwdep_pcm_shm->playback : (
|
||||
subs->endpoint == 0x8 ?
|
||||
subs->usX2Y->hwdep_pcm_shm->capture0x8 :
|
||||
subs->usX2Y->hwdep_pcm_shm->capture0xA);
|
||||
|
||||
(*purb)->dev = dev;
|
||||
(*purb)->pipe = pipe;
|
||||
(*purb)->number_of_packets = nr_of_packs();
|
||||
(*purb)->context = subs;
|
||||
(*purb)->interval = 1;
|
||||
(*purb)->complete = i_usX2Y_usbpcm_subs_startup;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* free the buffer
|
||||
*/
|
||||
static int snd_usX2Y_usbpcm_hw_free(snd_pcm_substream_t *substream)
|
||||
{
|
||||
snd_pcm_runtime_t *runtime = substream->runtime;
|
||||
snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t *)runtime->private_data,
|
||||
*cap_subs2 = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
|
||||
down(&subs->usX2Y->prepare_mutex);
|
||||
snd_printdd("snd_usX2Y_usbpcm_hw_free(%p)\n", substream);
|
||||
|
||||
if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) {
|
||||
snd_usX2Y_substream_t *cap_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
|
||||
atomic_set(&subs->state, state_STOPPED);
|
||||
usX2Y_usbpcm_urbs_release(subs);
|
||||
if (!cap_subs->pcm_substream ||
|
||||
!cap_subs->pcm_substream->runtime ||
|
||||
!cap_subs->pcm_substream->runtime->status ||
|
||||
cap_subs->pcm_substream->runtime->status->state < SNDRV_PCM_STATE_PREPARED) {
|
||||
atomic_set(&cap_subs->state, state_STOPPED);
|
||||
if (NULL != cap_subs2)
|
||||
atomic_set(&cap_subs2->state, state_STOPPED);
|
||||
usX2Y_usbpcm_urbs_release(cap_subs);
|
||||
if (NULL != cap_subs2)
|
||||
usX2Y_usbpcm_urbs_release(cap_subs2);
|
||||
}
|
||||
} else {
|
||||
snd_usX2Y_substream_t *playback_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
|
||||
if (atomic_read(&playback_subs->state) < state_PREPARED) {
|
||||
atomic_set(&subs->state, state_STOPPED);
|
||||
if (NULL != cap_subs2)
|
||||
atomic_set(&cap_subs2->state, state_STOPPED);
|
||||
usX2Y_usbpcm_urbs_release(subs);
|
||||
if (NULL != cap_subs2)
|
||||
usX2Y_usbpcm_urbs_release(cap_subs2);
|
||||
}
|
||||
}
|
||||
up(&subs->usX2Y->prepare_mutex);
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
}
|
||||
|
||||
static void usX2Y_usbpcm_subs_startup(snd_usX2Y_substream_t *subs)
|
||||
{
|
||||
usX2Ydev_t * usX2Y = subs->usX2Y;
|
||||
usX2Y->prepare_subs = subs;
|
||||
subs->urb[0]->start_frame = -1;
|
||||
smp_wmb(); // Make shure above modifications are seen by i_usX2Y_subs_startup()
|
||||
usX2Y_urbs_set_complete(usX2Y, i_usX2Y_usbpcm_subs_startup);
|
||||
}
|
||||
|
||||
static int usX2Y_usbpcm_urbs_start(snd_usX2Y_substream_t *subs)
|
||||
{
|
||||
int p, u, err,
|
||||
stream = subs->pcm_substream->stream;
|
||||
usX2Ydev_t *usX2Y = subs->usX2Y;
|
||||
|
||||
if (SNDRV_PCM_STREAM_CAPTURE == stream) {
|
||||
usX2Y->hwdep_pcm_shm->captured_iso_head = -1;
|
||||
usX2Y->hwdep_pcm_shm->captured_iso_frames = 0;
|
||||
}
|
||||
|
||||
for (p = 0; 3 >= (stream + p); p += 2) {
|
||||
snd_usX2Y_substream_t *subs = usX2Y->subs[stream + p];
|
||||
if (subs != NULL) {
|
||||
if ((err = usX2Y_usbpcm_urbs_allocate(subs)) < 0)
|
||||
return err;
|
||||
subs->completed_urb = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
for (p = 0; p < 4; p++) {
|
||||
snd_usX2Y_substream_t *subs = usX2Y->subs[p];
|
||||
if (subs != NULL && atomic_read(&subs->state) >= state_PREPARED)
|
||||
goto start;
|
||||
}
|
||||
usX2Y->wait_iso_frame = -1;
|
||||
|
||||
start:
|
||||
usX2Y_usbpcm_subs_startup(subs);
|
||||
for (u = 0; u < NRURBS; u++) {
|
||||
for (p = 0; 3 >= (stream + p); p += 2) {
|
||||
snd_usX2Y_substream_t *subs = usX2Y->subs[stream + p];
|
||||
if (subs != NULL) {
|
||||
struct urb *urb = subs->urb[u];
|
||||
if (usb_pipein(urb->pipe)) {
|
||||
unsigned long pack;
|
||||
if (0 == u)
|
||||
atomic_set(&subs->state, state_STARTING3);
|
||||
urb->dev = usX2Y->chip.dev;
|
||||
urb->transfer_flags = URB_ISO_ASAP;
|
||||
for (pack = 0; pack < nr_of_packs(); pack++) {
|
||||
urb->iso_frame_desc[pack].offset = subs->maxpacksize * (pack + u * nr_of_packs());
|
||||
urb->iso_frame_desc[pack].length = subs->maxpacksize;
|
||||
}
|
||||
urb->transfer_buffer_length = subs->maxpacksize * nr_of_packs();
|
||||
if ((err = usb_submit_urb(urb, GFP_KERNEL)) < 0) {
|
||||
snd_printk (KERN_ERR "cannot usb_submit_urb() for urb %d, err = %d\n", u, err);
|
||||
err = -EPIPE;
|
||||
goto cleanup;
|
||||
} else {
|
||||
snd_printdd("%i\n", urb->start_frame);
|
||||
if (0 > usX2Y->wait_iso_frame)
|
||||
usX2Y->wait_iso_frame = urb->start_frame;
|
||||
}
|
||||
urb->transfer_flags = 0;
|
||||
} else {
|
||||
atomic_set(&subs->state, state_STARTING1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
err = 0;
|
||||
wait_event(usX2Y->prepare_wait_queue, NULL == usX2Y->prepare_subs);
|
||||
if (atomic_read(&subs->state) != state_PREPARED)
|
||||
err = -EPIPE;
|
||||
|
||||
cleanup:
|
||||
if (err) {
|
||||
usX2Y_subs_startup_finish(usX2Y); // Call it now
|
||||
usX2Y_clients_stop(usX2Y); // something is completely wroong > stop evrything
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* prepare callback
|
||||
*
|
||||
* set format and initialize urbs
|
||||
*/
|
||||
static int snd_usX2Y_usbpcm_prepare(snd_pcm_substream_t *substream)
|
||||
{
|
||||
snd_pcm_runtime_t *runtime = substream->runtime;
|
||||
snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t *)runtime->private_data;
|
||||
usX2Ydev_t *usX2Y = subs->usX2Y;
|
||||
snd_usX2Y_substream_t *capsubs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
|
||||
int err = 0;
|
||||
snd_printdd("snd_usX2Y_pcm_prepare(%p)\n", substream);
|
||||
|
||||
if (NULL == usX2Y->hwdep_pcm_shm) {
|
||||
if (NULL == (usX2Y->hwdep_pcm_shm = snd_malloc_pages(sizeof(snd_usX2Y_hwdep_pcm_shm_t), GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
memset(usX2Y->hwdep_pcm_shm, 0, sizeof(snd_usX2Y_hwdep_pcm_shm_t));
|
||||
}
|
||||
|
||||
down(&usX2Y->prepare_mutex);
|
||||
usX2Y_subs_prepare(subs);
|
||||
// Start hardware streams
|
||||
// SyncStream first....
|
||||
if (atomic_read(&capsubs->state) < state_PREPARED) {
|
||||
if (usX2Y->format != runtime->format)
|
||||
if ((err = usX2Y_format_set(usX2Y, runtime->format)) < 0)
|
||||
goto up_prepare_mutex;
|
||||
if (usX2Y->rate != runtime->rate)
|
||||
if ((err = usX2Y_rate_set(usX2Y, runtime->rate)) < 0)
|
||||
goto up_prepare_mutex;
|
||||
snd_printdd("starting capture pipe for %s\n", subs == capsubs ? "self" : "playpipe");
|
||||
if (0 > (err = usX2Y_usbpcm_urbs_start(capsubs)))
|
||||
goto up_prepare_mutex;
|
||||
}
|
||||
|
||||
if (subs != capsubs) {
|
||||
usX2Y->hwdep_pcm_shm->playback_iso_start = -1;
|
||||
if (atomic_read(&subs->state) < state_PREPARED) {
|
||||
while (usX2Y_iso_frames_per_buffer(runtime, usX2Y) > usX2Y->hwdep_pcm_shm->captured_iso_frames) {
|
||||
signed long timeout;
|
||||
snd_printd("Wait: iso_frames_per_buffer=%i,captured_iso_frames=%i\n", usX2Y_iso_frames_per_buffer(runtime, usX2Y), usX2Y->hwdep_pcm_shm->captured_iso_frames);
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
timeout = schedule_timeout(HZ/100 + 1);
|
||||
if (signal_pending(current)) {
|
||||
err = -ERESTARTSYS;
|
||||
goto up_prepare_mutex;
|
||||
}
|
||||
}
|
||||
if (0 > (err = usX2Y_usbpcm_urbs_start(subs)))
|
||||
goto up_prepare_mutex;
|
||||
}
|
||||
snd_printd("Ready: iso_frames_per_buffer=%i,captured_iso_frames=%i\n", usX2Y_iso_frames_per_buffer(runtime, usX2Y), usX2Y->hwdep_pcm_shm->captured_iso_frames);
|
||||
} else
|
||||
usX2Y->hwdep_pcm_shm->capture_iso_start = -1;
|
||||
|
||||
up_prepare_mutex:
|
||||
up(&usX2Y->prepare_mutex);
|
||||
return err;
|
||||
}
|
||||
|
||||
static snd_pcm_hardware_t snd_usX2Y_4c =
|
||||
{
|
||||
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP_VALID),
|
||||
.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 = 4,
|
||||
.buffer_bytes_max = (2*128*1024),
|
||||
.period_bytes_min = 64,
|
||||
.period_bytes_max = (128*1024),
|
||||
.periods_min = 2,
|
||||
.periods_max = 1024,
|
||||
.fifo_size = 0
|
||||
};
|
||||
|
||||
|
||||
|
||||
static int snd_usX2Y_usbpcm_open(snd_pcm_substream_t *substream)
|
||||
{
|
||||
snd_usX2Y_substream_t *subs = ((snd_usX2Y_substream_t **)
|
||||
snd_pcm_substream_chip(substream))[substream->stream];
|
||||
snd_pcm_runtime_t *runtime = substream->runtime;
|
||||
|
||||
if (!(subs->usX2Y->chip_status & USX2Y_STAT_CHIP_MMAP_PCM_URBS))
|
||||
return -EBUSY;
|
||||
|
||||
runtime->hw = SNDRV_PCM_STREAM_PLAYBACK == substream->stream ? snd_usX2Y_2c :
|
||||
(subs->usX2Y->subs[3] ? snd_usX2Y_4c : snd_usX2Y_2c);
|
||||
runtime->private_data = subs;
|
||||
subs->pcm_substream = substream;
|
||||
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 1000, 200000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int snd_usX2Y_usbpcm_close(snd_pcm_substream_t *substream)
|
||||
{
|
||||
snd_pcm_runtime_t *runtime = substream->runtime;
|
||||
snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t *)runtime->private_data;
|
||||
int err = 0;
|
||||
snd_printd("\n");
|
||||
subs->pcm_substream = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
static snd_pcm_ops_t snd_usX2Y_usbpcm_ops =
|
||||
{
|
||||
.open = snd_usX2Y_usbpcm_open,
|
||||
.close = snd_usX2Y_usbpcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = snd_usX2Y_pcm_hw_params,
|
||||
.hw_free = snd_usX2Y_usbpcm_hw_free,
|
||||
.prepare = snd_usX2Y_usbpcm_prepare,
|
||||
.trigger = snd_usX2Y_pcm_trigger,
|
||||
.pointer = snd_usX2Y_pcm_pointer,
|
||||
};
|
||||
|
||||
|
||||
static int usX2Y_pcms_lock_check(snd_card_t *card)
|
||||
{
|
||||
struct list_head *list;
|
||||
snd_device_t *dev;
|
||||
snd_pcm_t *pcm;
|
||||
int err = 0;
|
||||
list_for_each(list, &card->devices) {
|
||||
dev = snd_device(list);
|
||||
if (dev->type != SNDRV_DEV_PCM)
|
||||
continue;
|
||||
pcm = dev->device_data;
|
||||
down(&pcm->open_mutex);
|
||||
}
|
||||
list_for_each(list, &card->devices) {
|
||||
int s;
|
||||
dev = snd_device(list);
|
||||
if (dev->type != SNDRV_DEV_PCM)
|
||||
continue;
|
||||
pcm = dev->device_data;
|
||||
for (s = 0; s < 2; ++s) {
|
||||
snd_pcm_substream_t *substream;
|
||||
substream = pcm->streams[s].substream;
|
||||
if (substream && substream->open_flag)
|
||||
err = -EBUSY;
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
static void usX2Y_pcms_unlock(snd_card_t *card)
|
||||
{
|
||||
struct list_head *list;
|
||||
snd_device_t *dev;
|
||||
snd_pcm_t *pcm;
|
||||
list_for_each(list, &card->devices) {
|
||||
dev = snd_device(list);
|
||||
if (dev->type != SNDRV_DEV_PCM)
|
||||
continue;
|
||||
pcm = dev->device_data;
|
||||
up(&pcm->open_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int snd_usX2Y_hwdep_pcm_open(snd_hwdep_t *hw, struct file *file)
|
||||
{
|
||||
// we need to be the first
|
||||
snd_card_t *card = hw->card;
|
||||
int err = usX2Y_pcms_lock_check(card);
|
||||
if (0 == err)
|
||||
usX2Y(card)->chip_status |= USX2Y_STAT_CHIP_MMAP_PCM_URBS;
|
||||
usX2Y_pcms_unlock(card);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
static int snd_usX2Y_hwdep_pcm_release(snd_hwdep_t *hw, struct file *file)
|
||||
{
|
||||
snd_card_t *card = hw->card;
|
||||
int err = usX2Y_pcms_lock_check(card);
|
||||
if (0 == err)
|
||||
usX2Y(hw->card)->chip_status &= ~USX2Y_STAT_CHIP_MMAP_PCM_URBS;
|
||||
usX2Y_pcms_unlock(card);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
static void snd_usX2Y_hwdep_pcm_vm_open(struct vm_area_struct *area)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static void snd_usX2Y_hwdep_pcm_vm_close(struct vm_area_struct *area)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static struct page * snd_usX2Y_hwdep_pcm_vm_nopage(struct vm_area_struct *area, unsigned long address, int *type)
|
||||
{
|
||||
unsigned long offset;
|
||||
struct page *page;
|
||||
void *vaddr;
|
||||
|
||||
offset = area->vm_pgoff << PAGE_SHIFT;
|
||||
offset += address - area->vm_start;
|
||||
snd_assert((offset % PAGE_SIZE) == 0, return NOPAGE_OOM);
|
||||
vaddr = (char*)((usX2Ydev_t*)area->vm_private_data)->hwdep_pcm_shm + offset;
|
||||
page = virt_to_page(vaddr);
|
||||
|
||||
if (type)
|
||||
*type = VM_FAULT_MINOR;
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
|
||||
static struct vm_operations_struct snd_usX2Y_hwdep_pcm_vm_ops = {
|
||||
.open = snd_usX2Y_hwdep_pcm_vm_open,
|
||||
.close = snd_usX2Y_hwdep_pcm_vm_close,
|
||||
.nopage = snd_usX2Y_hwdep_pcm_vm_nopage,
|
||||
};
|
||||
|
||||
|
||||
static int snd_usX2Y_hwdep_pcm_mmap(snd_hwdep_t * hw, struct file *filp, struct vm_area_struct *area)
|
||||
{
|
||||
unsigned long size = (unsigned long)(area->vm_end - area->vm_start);
|
||||
usX2Ydev_t *usX2Y = (usX2Ydev_t*)hw->private_data;
|
||||
|
||||
if (!(((usX2Ydev_t*)hw->private_data)->chip_status & USX2Y_STAT_CHIP_INIT))
|
||||
return -EBUSY;
|
||||
|
||||
/* if userspace tries to mmap beyond end of our buffer, fail */
|
||||
if (size > PAGE_ALIGN(sizeof(snd_usX2Y_hwdep_pcm_shm_t))) {
|
||||
snd_printd("%lu > %lu\n", size, (unsigned long)sizeof(snd_usX2Y_hwdep_pcm_shm_t));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!usX2Y->hwdep_pcm_shm) {
|
||||
return -ENODEV;
|
||||
}
|
||||
area->vm_ops = &snd_usX2Y_hwdep_pcm_vm_ops;
|
||||
area->vm_flags |= VM_RESERVED;
|
||||
snd_printd("vm_flags=0x%lX\n", area->vm_flags);
|
||||
area->vm_private_data = hw->private_data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void snd_usX2Y_hwdep_pcm_private_free(snd_hwdep_t *hwdep)
|
||||
{
|
||||
usX2Ydev_t *usX2Y = (usX2Ydev_t *)hwdep->private_data;
|
||||
if (NULL != usX2Y->hwdep_pcm_shm)
|
||||
snd_free_pages(usX2Y->hwdep_pcm_shm, sizeof(snd_usX2Y_hwdep_pcm_shm_t));
|
||||
}
|
||||
|
||||
|
||||
static void snd_usX2Y_usbpcm_private_free(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_lib_preallocate_free_for_all(pcm);
|
||||
}
|
||||
|
||||
|
||||
int usX2Y_hwdep_pcm_new(snd_card_t* card)
|
||||
{
|
||||
int err;
|
||||
snd_hwdep_t *hw;
|
||||
snd_pcm_t *pcm;
|
||||
struct usb_device *dev = usX2Y(card)->chip.dev;
|
||||
if (1 != nr_of_packs())
|
||||
return 0;
|
||||
|
||||
if ((err = snd_hwdep_new(card, SND_USX2Y_USBPCM_ID, 1, &hw)) < 0) {
|
||||
snd_printd("\n");
|
||||
return err;
|
||||
}
|
||||
hw->iface = SNDRV_HWDEP_IFACE_USX2Y_PCM;
|
||||
hw->private_data = usX2Y(card);
|
||||
hw->private_free = snd_usX2Y_hwdep_pcm_private_free;
|
||||
hw->ops.open = snd_usX2Y_hwdep_pcm_open;
|
||||
hw->ops.release = snd_usX2Y_hwdep_pcm_release;
|
||||
hw->ops.mmap = snd_usX2Y_hwdep_pcm_mmap;
|
||||
hw->exclusive = 1;
|
||||
sprintf(hw->name, "/proc/bus/usb/%03d/%03d/hwdeppcm", dev->bus->busnum, dev->devnum);
|
||||
|
||||
err = snd_pcm_new(card, NAME_ALLCAPS" hwdep Audio", 2, 1, 1, &pcm);
|
||||
if (err < 0) {
|
||||
return err;
|
||||
}
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usX2Y_usbpcm_ops);
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usX2Y_usbpcm_ops);
|
||||
|
||||
pcm->private_data = usX2Y(card)->subs;
|
||||
pcm->private_free = snd_usX2Y_usbpcm_private_free;
|
||||
pcm->info_flags = 0;
|
||||
|
||||
sprintf(pcm->name, NAME_ALLCAPS" hwdep Audio");
|
||||
if (0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
|
||||
SNDRV_DMA_TYPE_CONTINUOUS,
|
||||
snd_dma_continuous_data(GFP_KERNEL),
|
||||
64*1024, 128*1024)) ||
|
||||
0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
|
||||
SNDRV_DMA_TYPE_CONTINUOUS,
|
||||
snd_dma_continuous_data(GFP_KERNEL),
|
||||
64*1024, 128*1024))) {
|
||||
snd_usX2Y_usbpcm_private_free(pcm);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int usX2Y_hwdep_pcm_new(snd_card_t* card)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
21
sound/usb/usx2y/usx2yhwdeppcm.h
Normal file
21
sound/usb/usx2y/usx2yhwdeppcm.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#define MAXPACK 50
|
||||
#define MAXBUFFERMS 100
|
||||
#define MAXSTRIDE 3
|
||||
|
||||
#define SSS (((MAXPACK*MAXBUFFERMS*MAXSTRIDE + 4096) / 4096) * 4096)
|
||||
struct snd_usX2Y_hwdep_pcm_shm {
|
||||
char playback[SSS];
|
||||
char capture0x8[SSS];
|
||||
char capture0xA[SSS];
|
||||
volatile int playback_iso_head;
|
||||
int playback_iso_start;
|
||||
struct {
|
||||
int frame,
|
||||
offset,
|
||||
length;
|
||||
} captured_iso[128];
|
||||
volatile int captured_iso_head;
|
||||
volatile unsigned captured_iso_frames;
|
||||
int capture_iso_start;
|
||||
};
|
||||
typedef struct snd_usX2Y_hwdep_pcm_shm snd_usX2Y_hwdep_pcm_shm_t;
|
||||
Loading…
Reference in a new issue