hi,
please check the hotplug strategy outlined in the forward.
If it's ok, i'll prepare a next patch based on what is in the forward.

regards,
Karsten
--- Begin Message ---
hi colegas,

have been trying the 0.1 and yes, the fpga code does it also for us428 !
concerning cooperation of snd-usb-us428.o and rbtload i've implemented a  
hack:
when a hotplug occurs (EZUSB Firmware not being looked at here, works as 
before), the kernel 
1.) asks snd-usb-us428.o, if it wants to have the device. snd-usb-us428.o says 
'yes!'. this latest snd-usb-us428.o will not start any ALSA-Activity in this 
stage. It just owns the interface to the us428 (and implements 2 new 
ioctl-able entriepoints).
2.) starts rbtload (by means of the hotplug-scripts) to download the fpga 
code. this hacked rbtload does the download by means of standard ioctl() - 
calls to snd-usb-us428.o. Thus we don't need libusb here anymore. (libusb is 
still used here for minimizing changing efforts.) if download is finished, 
snd-usb-us428.o ALSA-Activities are started by another ioctl to 
snd-usb-us428.o.

Result: us428 works as before.

What do you think about setting up a 0.2 this way? 

If you might wonder why snd-usb-us428 is still seperate from snd-usb-audio:
- us428 (and possibly usx2x) use a non standard way of synching audio in & 
out.
- us428 and us224 (don't know about us122) have those knobs & sliders. they 
are also special.
if everything works fine some day, i'd gladly say yes to integrate those 
non-standard stuffs into snd-usb-audio. until then the code is (getting ;-) 
smaller and thus easier to handle.

attached are for illustrating purposes:
1.) rbtload.c : still needs some polishing. libusb can be taken out.
2.) usbus428.c : implements the us428_ioctl(). we can further migrate it to a 
usbusx2x.c for a snd-usb-usx2x.o. 
3.) usbusx2xioctl.h : ioctl codes
everything else is unchanged from the latest versions.

t+,
Karsten
/*
 * rbtload.c
 * Loads a rbt file (fpga configuration) into a tascam usb device
 *
 * Copyright (c) 2003 Pedro López-Cabanillas <[EMAIL PROTECTED]>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer,
 *    without modification.
 * 2. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * Alternatively, this software may be distributed and/or modified 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 SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * This program supports loading firmware into a target USB device
 * that is discovered and referenced by the hotplug usb agent.
 *
 *     -I <path>       -- Download this firmware (rbt bitstream format)
 *     -D <path>       -- Use this device, instead of $DEVICE
 *     -V              -- Print version ID for program
 *     -v              -- more verbose output
 *     -i              -- USB interface number, defaults to 0
 *     -c              -- USB configuration, defaults to 0
 *     -a              -- USB alternate setting, defaults to 0
 *     -e              -- USB endpoint, defaults to 2
 *     -p              -- prepadding bytes, defaults to 0
 *     -t              -- USB bulk transfer timeout, defaults to 1000 ms
 *
 * This program is intended to be started by hotplug scripts in
 * response to a device appearing on the bus. It therefore also
 * expects these environment variables which are passed by hotplug to
 * its sub-scripts:
 *
 *     DEVICE=<path>
 *         This is the path to the device is /proc/bus/usb. It is the
 *         complete path to the device, that I can pass to open and
 *         manipulate as a USB device.
 */

#include  <stdlib.h>
#include  <stdio.h>
#include  <getopt.h>
#include  <string.h>
#include  <ctype.h>
#include  <errno.h>
#include  <stdarg.h>
#include  <sys/types.h>
#include  <sys/stat.h>
#include  <sys/ioctl.h> 
#include  <fcntl.h>
#include  <unistd.h>
#include  <usb.h>
#include  <linux/usbdevice_fs.h>
#include  "usbusx2xioctl.h"

#ifndef	RBTLOAD_VERSION
#	define RBTLOAD_VERSION ("0.2")
#endif
#define PROGNAME "rbtload"

typedef unsigned char byte;

static int verbose = 0;

void usage()
{
	fprintf(stderr,
		"usage: " PROGNAME
		" [-h] [-vV] [-D devpath] [-t timeout]\n");
	fprintf(stderr,
		"               [-i interface] [-c configuration] [-a altsetting]\n");
	fprintf(stderr,
		"               [-e endpoint] [-p prepadding] -I rbt_file\n");
	fprintf(stderr, "[-D devpath] overrides DEVICE= in env\n");
	fprintf(stderr, "[-e endpoint] defaults to 2\n");
	fprintf(stderr, "[-t timeout] defaults to 1000\n");
	exit(EXIT_FAILURE);
}

/*
 * read a xilinx bitstream file
 */
static byte *read_xilinx_image(const char *fname, int *imglen)
{
	FILE *fp;
	char buf[256];
	int data, c, idx, length;
	char *p;
	byte *imgbuf;

	if ((fp = fopen(fname, "r")) == NULL) {
		fprintf(stderr, PROGNAME ": cannot open %s\n", fname);
		exit(EXIT_FAILURE);
	}

	c = 0;
	data = 0;
	idx = 0;
	length = 0;
	while (fgets(buf, sizeof(buf), fp)) {
		if (strncmp(buf, "Bits:", 5) == 0) {
			for (p = buf + 5; *p && isspace(*p); p++);
			if (!*p) {
				fprintf(stderr,
					PROGNAME
					": corrupted file %s in Bits line\n",
					fname);
				fclose(fp);
				exit(EXIT_FAILURE);
			}
			length = atoi(p);
			length /= 8;
			if (length <= 0) {
				fprintf(stderr,
					PROGNAME
					": corrupted file %s, detected length = %d\n",
					fname, length);
				fclose(fp);
				exit(EXIT_FAILURE);
			}
			*imglen = length;
			imgbuf = malloc(length);
			if (!imgbuf) {
				fprintf(stderr,
					PROGNAME
					": cannot alloc %d bytes\n",
					length);
				fclose(fp);
				exit(EXIT_FAILURE);
			}
			continue;
		}
		if ((buf[0] != '0') && (buf[0] != '1')) {
			//printf("line %d skipped\n", line);
			continue;
		}
		if (length <= 0) {
			fprintf(stderr,
				PROGNAME
				": corrupted file %s, starting without Bits line\n",
				fname);
			fclose(fp);
			exit(EXIT_FAILURE);
		}
		//printf("\n idx=%d", idx);
		for (p = buf; *p == '0' || *p == '1'; p++) {
			data |= (*p - '0') << c;
			c++;
			if (c >= 8) {
				//printf(" %02X", data);
				imgbuf[idx++] = data;
				data = c = 0;
				if (idx > length)
					break;
			}
		}
	}
	if (c)
		imgbuf[idx++] = data;
	if (idx != length) {
		fprintf(stderr,
			PROGNAME ": length doesn't match: %d != %d\n", idx,
			length);
		fclose(fp);
		exit(EXIT_FAILURE);
	}
	fclose(fp);
	return imgbuf;
}

usb_dev_handle *usb_find_device(char *devpath, int configuration, int interface,
				int altsetting)
{
	struct usb_bus *bus;
	struct usb_device *dev;
	struct usb_device *found = NULL;
	usb_dev_handle *usbdev = NULL;
	char filename[64];
	char lastpath[64];
	char *tok;
	char *pos;
	int i, ret;

	// initialize libusb
	usb_init();
	// search for USB busses
	if ((usb_find_busses()) < 0) {
		fprintf(stderr, PROGNAME ": USB Error %s\n",
			usb_strerror());
		exit(EXIT_FAILURE);
	}
	// search for devices on USB busses
	if ((ret = usb_find_devices()) < 0) {
		fprintf(stderr, PROGNAME ": USB Error %s\n",
			usb_strerror());
		exit(EXIT_FAILURE);
	}
	pos = devpath;
	memset(&lastpath, 0, sizeof(lastpath));
	memset(&filename, 0, sizeof(filename));
	while ((tok = strsep(&pos, "/"))) {
		strncpy(lastpath, filename, sizeof(lastpath));
		strncpy(filename, tok, sizeof(filename));
	}
	// go through all busses and devices
	for (bus = usb_busses; bus; bus = bus->next) {
		for (dev = bus->devices; dev; dev = dev->next) {
			if (!strcmp(filename, dev->filename)
			    && !(strcmp(lastpath, bus->dirname))) {
				found = dev;
				break;
			}
		}
	}
	// check if we found the device
	if (!found) {
		fprintf(stderr, PROGNAME ": Device %s not found\n",
			devpath);
		exit(EXIT_FAILURE);
	}
	// set errno to zero here. this is necessary, because above each USB-device
	// is opend and then ioctl'd, which results in EPERM errors on some. These
	// are remembered in errno, which is _not_ updated on success in usb_open.
	// So the if clause afterwards will return, despite the error was earlier
	// and harmless.
	errno = 0;
	// open a connection to this device
	usbdev = usb_open(found);
	if ((!usbdev) | (errno)) {
		fprintf(stderr, PROGNAME ": USB Error %s\n",
			usb_strerror());
		exit(EXIT_FAILURE);
	}
/* 	{ */
/* 		char			buf[] = "der puffer"; */
/* 		struct usbdevfs_ioctl	io = {0,IOCTL_USx2x_OUT_PIPE2(sizeof(buf)),buf}; */

/* 		if(ioctl( *(int*)usbdev, USBDEVFS_IOCTL, &io)) */
/* 			perror( "ioctl!"); */
/* 	} */
/* 	{ */
/* 		struct usbdevfs_ioctl io = {0,IOCTL_USx2x_START_ALSA,NULL}; */
/* 		if(ioctl( *(int*)usbdev, USBDEVFS_IOCTL, &io)) */
/* 			perror( "ioctl!"); */
/* 	} */
/* 	{ */
/* 		struct usbdevfs_ioctl io = {0,IOCTL_USx2x_STOP_ALSA,NULL}; */
/* 		if(ioctl( *(int*)usbdev, USBDEVFS_IOCTL, &io)) */
/* 			perror( "ioctl!"); */
/* 	} */

/* 	// set configuration */
/* 	i = usb_device(usbdev)->config[configuration].bConfigurationValue; */
/* 	if (usb_set_configuration(usbdev, i) < 0) { */
/* 		fprintf(stderr, PROGNAME ": USB Error %s\n", */
/* 			usb_strerror()); */
/* 		exit(EXIT_FAILURE); */
/* 	} */
/* 	// set interface */
/* 	i = usb_device(usbdev)->config[configuration].interface[interface]. */
/* 	    altsetting[altsetting].bInterfaceNumber; */
/* 	if (usb_claim_interface(usbdev, i) < 0) { */
/* 		fprintf(stderr, PROGNAME ": USB Error %s\n", */
/* 			usb_strerror()); */
/* 		exit(EXIT_FAILURE); */
/* 	} */
/* 	// set alternate interface */
/* 	i = usb_device(usbdev)->config[configuration].interface[interface]. */
/* 	    altsetting[altsetting].bAlternateSetting; */
/* 	if (usb_set_altinterface(usbdev, i) < 0) { */
/* 		fprintf(stderr, PROGNAME ": USB Error %s\n", */
/* 			usb_strerror()); */
/* 		exit(EXIT_FAILURE); */
/* 	} */
	return usbdev;
}

void download_image(usb_dev_handle * usbdev, int pipe, byte * imgbuf,
		    int length, int timeout)
{
	int ret;
	int todo;
	int pos = 0;

	if (!imgbuf || !length) {
		fprintf(stderr,
			PROGNAME ": invalid download_image params\n");
		exit(EXIT_FAILURE);
	}
	while (pos < length) {
		todo = 0x1000;
		if ((length - pos) < todo)
			todo = (length - pos);
		{
			struct usbdevfs_ioctl	io = {0,IOCTL_USx2x_OUT_PIPE2(todo), imgbuf+pos};
			
			ret = ioctl( *(int*)usbdev, USBDEVFS_IOCTL, &io);
		}

		if (ret) {
			perror( PROGNAME ": USB Error");
			exit(EXIT_FAILURE);
		}
		//printf("."); fflush(stdout);
		pos += todo;
	}
	//printf("\n");
}

int main(int argc, char *argv[])
{
	char *firmware_path = 0;
	char *device_path = getenv("DEVICE");
	int prepadding = 0;
	int endpoint = 2;
	int interface = 0;
	int configuration = 0;
	int altsetting = 0;
	int timeout = 1000;
	int configuration_length = 0;
	int opt;
	byte *configuration_data;
	byte *pad_data;
	usb_dev_handle *usbdev = NULL;

	while ((opt = getopt(argc, argv, "hvVD:I:i:c:a:e:p:t:")) != EOF)
		switch (opt) {
		case 'D':
			device_path = optarg;
			break;

		case 'I':
			firmware_path = optarg;
			break;

		case 'V':
			puts(RBTLOAD_VERSION);
			return 0;

		case 'v':
			verbose++;
			break;

		case 'i':
			interface = atoi(optarg);
			break;

		case 'c':
			configuration = atoi(optarg);
			break;

		case 'a':
			altsetting = atoi(optarg);
			break;

		case 'e':
			endpoint = atoi(optarg);
			break;

		case 'p':
			prepadding = atoi(optarg);
			break;

		case 't':
			timeout = atoi(optarg);
			break;

		case 'h':
		default:
			usage();
		}

	if (!device_path) {
		fputs(PROGNAME ": no device specified!\n", stderr);
		usage();
	}

	if (!firmware_path) {
		fputs(PROGNAME ": missing firmware file name!\n", stderr);
		usage();
	}

	configuration_data =
	    read_xilinx_image(firmware_path, &configuration_length);
	usbdev =
	    usb_find_device(device_path, configuration, interface,
			    altsetting);

	if (prepadding) {
		pad_data = malloc(prepadding);
		memset(pad_data, 0, prepadding);
		download_image(usbdev, endpoint, pad_data, prepadding,
			       timeout);
	}
	download_image(usbdev, endpoint, configuration_data,
		       configuration_length, timeout);
	{
		struct usbdevfs_ioctl io = {0,IOCTL_USx2x_START_ALSA, NULL};
		if(ioctl( *(int*)usbdev, USBDEVFS_IOCTL, &io));
		perror( "ioctl!");
	}

	// close USB handle
	if (usb_close(usbdev) < 0) {
		fprintf(stderr, PROGNAME ": USB Error %s\n",
			usb_strerror());
		return EXIT_FAILURE;
	}
	return EXIT_SUCCESS;
}
#ifndef USBUS428IOCTL_H
#define USBUS428IOCTL_H
#include <linux/ioctl.h>

#define IOCTL_USx2x_OUT_PIPE2(len)	_IOC(_IOC_WRITE,'U',0,len)
#define IOCTL_USx2x_START_ALSA		_IO('U', 1)
#define IOCTL_USx2x_STOP_ALSA		_IO('U', 2)

#endif
/*
 * usbus428.c - ALSA USB US-428 Driver
 *
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 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 <sound/core.h>
#include <sound/seq_device.h>
#define SNDRV_GET_ID
#include <sound/initval.h>
#include <sound/pcm.h>

#include <sound/rawmidi.h>
#include <linux/usb.h>
#include "usbus428.h"
#include "usbus428ctls.h" 
#include "usbus428ctldefs.h" 
#include "usbus428ioctl.h" 



MODULE_AUTHOR("Karsten Wiese <[EMAIL PROTECTED]>");
MODULE_DESCRIPTION("USB TASCAM (0x1604) US-428 (0x8001) Version 0.0.8");
MODULE_LICENSE("GPL");
MODULE_CLASSES("{sound}");

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_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
MODULE_PARM_DESC(index, "Index value for USB MIDI.");
MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC);
MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
MODULE_PARM_DESC(id, "ID string for USB MIDI.");
MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC);
MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
MODULE_PARM_DESC(enable, "Enable USB MIDI.");
MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC);


static int snd_us428_card_used[SNDRV_CARDS];
DECLARE_MUTEX(snd_us428_open_mutex);
static void snd_us428_Out04Int(urb_t* urb);

static void* us428_usb_probe(struct usb_device* device,
				   unsigned int ifnum,
				   const struct usb_device_id* device_id);
static void snd_us428_usb_disconnect(struct usb_device* usb_device, void* ptr);

struct us428_control{
	unsigned short TransferBuLe;
	unsigned char  TransferBuffer[16];
	unsigned char  Request;
	unsigned short Value;
};


static void snd_us428_In04Int(urb_t* urb){
	int		err = 0;
	us428dev_t*	us428 = urb->context;
	
	us428->In04IntCalls++;

	if (urb->status){
		snd_printk( "Interrupt Pipe 4 came back with status=%i\n", urb->status);
		return;
	}

        {
		int diff = -1, i, j, v;
	//	printk("%i:0x%02X ", 8, (int)((unsigned char*)us428->In04Buf)[8]); Master volume shows 0 here if fader is at max during boot ?!?
		for (i = 0; i < 21; i++){
			if (us428->In04Last[i] != ((char*)us428->In04Buf)[i]){
/* 				printk("%i:0x%02X ", i, (int)((unsigned char*)us428->In04Buf)[i]); */
				diff = i;
				us428->In04Last[i] = ((char*)us428->In04Buf)[i];
				switch(i){
				case eFaderM:		// Master Volume
					v = 0x40 * ((unsigned char*)us428->In04Last)[i];
					for (j = 0; j < URBS_AsyncSeq; ++j)
						if (0 == us428->AS04.urb[j]->status){
							usb_fill_bulk_urb(	us428->AS04.urb[j], us428->chip.dev,
										usb_sndbulkpipe(us428->chip.dev, 0x04),
										us428->AS04.urb[j]->transfer_buffer, 5,
										snd_us428_Out04Int, us428
										);
							((char*)us428->AS04.urb[j]->transfer_buffer)[0] = 4;
							((char*)us428->AS04.urb[j]->transfer_buffer)[1] =
							((char*)us428->AS04.urb[j]->transfer_buffer)[3] = (v & 0xff00) >> 8;
							((char*)us428->AS04.urb[j]->transfer_buffer)[2] =
							((char*)us428->AS04.urb[j]->transfer_buffer)[4] = v & 0xff;
							//printk(" %i ", v);
							us428->AS04.urb[j]->transfer_flags = USB_QUEUE_BULK;
							/*if ((err =*/ usb_submit_urb(us428->AS04.urb[j]);//))
							break;
						}
				}
			}
		}
		if (diff >= 0  &&  P_us428ctls_sharedmem_u){
			us428ctls_sharedmem_t* us428ctls = &(P_us428ctls_sharedmem_u->us428ctls);
			int n = us428ctls->CtlSnapShotLast + 1;

			if (n >= N_us428_ctl_BUFS  ||  n < 0)
				n = 0;
			memcpy(us428ctls->CtlSnapShot + n, us428->In04Buf, sizeof(us428ctls->CtlSnapShot[0]));
			us428ctls->CtlSnapShotDiffersAt[n] = diff;
			us428ctls->CtlSnapShotLast = n;
			wake_up(&us428ctls_wait_queue_head);
		}
	}
	
	
	if (us428->US04){
		if (0 == us428->US04->submitted){
			do{
				err = usb_submit_urb(us428->US04->urb[us428->US04->submitted++]);
			}while (! err && us428->US04->submitted < us428->US04->len);
			snd_printd("us428->US04->submitted=%i\n", us428->US04->submitted);
		}
	}else
		if (P_us428ctls_sharedmem_u){
			us428ctls_sharedmem_t* us428ctls = &(P_us428ctls_sharedmem_u->us428ctls);
			if (us428ctls->MakeLightLast != us428ctls->MakeLightSent
			       && us428ctls->MakeLightLast >= 0
			       && us428ctls->MakeLightLast < N_us428_light_BUFS
				){
				int j;
				for (j = 0; j < URBS_AsyncSeq; ++j)
					if (0 == us428->AS04.urb[j]->status){
						usb_fill_bulk_urb(	us428->AS04.urb[j], us428->chip.dev,
									usb_sndbulkpipe(us428->chip.dev, 0x04),
									us428ctls->MakeLight + us428ctls->MakeLightLast, sizeof(us428ctls->MakeLight[0]),
									snd_us428_Out04Int, us428
							);
						us428->AS04.urb[j]->transfer_flags = USB_QUEUE_BULK;
						/*if ((err =*/ usb_submit_urb(us428->AS04.urb[j]);//))
						us428ctls->MakeLightSent = us428ctls->MakeLightLast;
						break;
					}

				;
			}
		}


	if (err){
		snd_printk("In04Int() usb_submit_urb err=%i\n", err);
	}
}





static int snd_us428_In04_init(us428dev_t* us428)
{
	int	err = 0;
	if (! (us428->In04urb = usb_alloc_urb(0)))
		return -ENOMEM;

	if (! (us428->In04Buf = kmalloc(21, GFP_KERNEL))){
		usb_free_urb(us428->In04urb);
		return -ENOMEM;
	}
	 
	init_waitqueue_head(&us428->In04WaitQueue);
	usb_fill_int_urb(	us428->In04urb, us428->chip.dev, usb_rcvintpipe(us428->chip.dev, 0x4),
				us428->In04Buf, 21,
				snd_us428_In04Int, us428,
				10);
	us428->In04urb->transfer_flags = USB_QUEUE_BULK;
	err = usb_submit_urb(us428->In04urb);
	return err;
}



/* 
 * pipe 4 is used for switching the lamps, setting samplerate, volumes ....   
 */
static void snd_us428_Out04Int(urb_t* urb){
	if (urb->status){
		int		i;
		us428dev_t*	us428 = urb->context;
		for (i = 0; i < 10 && us428->AS04.urb[i] != urb; i++);
		snd_printd("snd_us428_Out04Int() us428->Seq04=%i urb %i status=%i\n", us428->Seq04, i, urb->status);
	}
}
/*
 * Prepare some urbs
 */
static int snd_us428_AsyncSeq04_init(us428dev_t* us428)
{
	int	err = 0,
		i;
	us428->Seq04 = 0;

	if (NULL == (us428->AS04.buffer = kmalloc(URB_DataLen_AsyncSeq*URBS_AsyncSeq, GFP_KERNEL))){
		err = -ENOMEM;
	}else
		for (i = 0; i < URBS_AsyncSeq; ++i){
			if (NULL == (us428->AS04.urb[i] = usb_alloc_urb(0))){
				err = -ENOMEM;
				break;
			}
			usb_fill_bulk_urb(	us428->AS04.urb[i], us428->chip.dev,
						usb_sndbulkpipe(us428->chip.dev, 0x04),
						us428->AS04.buffer + URB_DataLen_AsyncSeq*i, 0,
						snd_us428_Out04Int, us428
				);
		}
	return err;
}


static void snd_us428_unlinkSeq(snd_us428_AsyncSeq_t* S)
{
	int	i;
	for (i = 0; i < URBS_AsyncSeq; ++i){
		if (S[i].urb){
			usb_unlink_urb(S->urb[i]);
			usb_free_urb(S->urb[i]);
			S->urb[i] = NULL;
		}
	}
	kfree(S->buffer);
}



static int snd_us428_create_usbmidi(snd_card_t* card )
{
	static snd_usb_midi_endpoint_info_t quirk_data = {
		.out_ep =0x06,
		.in_ep = 0x06,
		.out_cables =	0x003,
		.in_cables =	0x003
	};
	static snd_usb_audio_quirk_t quirk = {
		.vendor_name =	"TASCAM",
		.product_name =	"US-428",
		.ifnum = 	0,
       		.type = QUIRK_MIDI_FIXED_ENDPOINT,
		.data = &quirk_data
	};

	snd_printd("snd_us428_create_usbmidi \n");

        INIT_LIST_HEAD(&us428(card)->chip.midi_list);

	return snd_usb_create_midi_interface(&us428(card)->chip, us428(card)->chip.dev->actconfig->interface , &quirk);
}




static int snd_us428_create_alsa_devices(snd_card_t* card)
{
	int err;

	do {
		if ((err = snd_us428_create_usbmidi(card)) < 0){
			snd_printk("snd_us428_create_alsa_devices: snd_us428_create_usbmidi error %i \n", err);
			break;
		}
		err = snd_us428_audio_create(card);
		if (err < 0) 
			break;
		err = snd_card_register(card);
		if (err < 0) 
			break;
	} while (0);

	return err;
} 

static void snd_us428_card_private_free(snd_card_t *card)
{
	if (us428(card)->chip.index >= 0  &&  us428(card)->chip.index < SNDRV_CARDS){
		snd_us428_card_used[us428(card)->chip.index] = 0;
	}
}

static struct usb_device_id snd_us428_usb_id_table[] = {
	{
		.match_flags =	USB_DEVICE_ID_MATCH_DEVICE,
		.idVendor =	0x1604,
		.idProduct =	0x8001 
	},
	{ /* terminator */ }
};

static snd_card_t* snd_us428_create_card(struct usb_device* device)
{
	int		err = 0,	
			dev;
	snd_card_t*	card = NULL;

	do{
		for (dev = 0; dev < SNDRV_CARDS; ++dev){
			if (enable[dev] && !snd_us428_card_used[dev])
				break;
		}
		if (dev >= SNDRV_CARDS){
			err = -ENOENT;
			break;
		}
		card = snd_card_new(index[dev], id[dev], THIS_MODULE, sizeof(us428dev_t));
		if (! card) {
			err = -ENOMEM;
			break;
		}
		snd_us428_card_used[us428(card)->chip.index = dev] = 1;
		card->private_free = snd_us428_card_private_free;
		us428(card)->chip.dev = device;
		us428(card)->chip.card = card;
		us428(card)->stride = 4;		// 16 Bit 
		strcpy(card->driver, "USB US-428");
		sprintf(card->shortname, "TASCAM US-428");
		sprintf(card->longname, "%s (%x:%x if %d at %03d/%03d)",
			card->shortname, 
			snd_us428_usb_id_table[0].idVendor, snd_us428_usb_id_table[0].idProduct,
			0,//us428(card)->usbmidi.ifnum,
			us428(card)->chip.dev->bus->busnum, us428(card)->chip.dev->devnum
			);
	} while (0);
	return card;
}


/* int snd_us428_initPipe2(struct usb_device* dev) */
/* { */
/* 	char *argv [3], **envp, *buf, *scratch; */
/* 	int i = 0, value; */

/* 	if (in_interrupt ()) { */
/* 		dbg ("In_interrupt"); */
/* 		return -1; */
/* 	} */
/* 	if (dev->devnum < 0) { */
/* 		dbg ("device already deleted ??"); */
/* 		return -1; */
/* 	} */
/* 	if (!(envp = (char **) kmalloc (20 * sizeof (char *), GFP_KERNEL))) { */
/* 		dbg ("enomem"); */
/* 		return -1; */
/* 	} */
/* 	if (!(buf = kmalloc (256, GFP_KERNEL))) { */
/* 		kfree (envp); */
/* 		dbg ("enomem2"); */
/* 		return -1; */
/* 	} */

/* 	/\* only one standardized param to hotplug command: type *\/ */
/* 	argv [0] = "/etc/hotplug/usb/tascam_fpga"; */
/* 	argv [1] = "-a1"; */
/* 	argv [2] = 0; */

/* 	/\* minimal command environment *\/ */
/* 	envp [i++] = "HOME=/"; */
/* 	envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; */

/* #ifdef	DEBUG */
/* 	/\* hint that policy agent should enter no-stdout debug mode *\/ */
/* 	envp [i++] = "DEBUG=kernel"; */
/* #endif */
/* 	/\* extensible set of named bus-specific parameters, */
/* 	 * supporting multiple driver selection algorithms. */
/* 	 *\/ */
/* 	scratch = buf; */

/* 	/\* action:  add, remove *\/ */
/* 	envp [i++] = "PRODUCT=1604/8001/"; */
/* 	envp [i++] = scratch; */
/* 	scratch += sprintf (scratch, "DEVICE=/proc/bus/usb/%03d/%03d", */
/* 		dev->bus->busnum, dev->devnum) + 1; */
/* 	envp [i++] = 0; */
/* 	/\* assert: (scratch - buf) < sizeof buf *\/ */

/* 	/\* NOTE: user mode daemons can call the agents too *\/ */

/* 	dbg ("kusbd: %s %s %d", argv [0], verb, dev->devnum); */
/* 	value = call_usermodehelper (argv [0], argv, envp); */
/* 	kfree (buf); */
/* 	kfree (envp); */
/* 	if (value != 0) */
/* 		dbg ("kusbd policy returned 0x%x", value); */
/* 	return value; */
/* } */


static int us428_start_alsa(struct usb_device* device)
{
	snd_card_t*		card = NULL;
	int			err = 0;
	struct usb_interface*	interface = device->actconfig->interface + 0;
	
	down(&snd_us428_open_mutex);
	do {
			/* See if the device offered us matches what we can accept */
		if (device->descriptor.idVendor != 0x1604 || device->descriptor.idProduct != 0x8001)
			break;
		
		snd_printd("us428_start_alsa(interface->act_altsetting=%i)\n", (int)interface->act_altsetting);

				// altsetting 1: 16 Bit;  2: 24 Bit FIXME for 24 bit
		if ((err = usb_set_interface(device, 0, 1))){
			snd_printd("usb_set_interface error \n");
			break;
		}
		if (1 != interface->act_altsetting){
			snd_printd("act_altsetting != 1\n");
			break;
		}

		if (err)
			break;

		if (NULL == (card = snd_us428_create_card(device)))
			break;

		us428(card)->Seq04Complete = 1;

		if ((err = snd_us428_AsyncSeq04_init(us428(card)))){
			snd_printd("snd_us428_AsyncSeq04_init error \n");
			break;
		}
		if ((err = snd_us428_create_alsa_devices(card))){
			snd_printd("snd_us428_create_alsa_devices error %i \n", err);
			snd_card_free(card);
			break;
		}
		if ((err = snd_us428_In04_init(us428(card)))){
			snd_printd("snd_us428_In04_init error \n");
			break;
		}
		if ((err = init_us428ctls(us428(card)))){
			snd_printd("init_us428ctls error \n");
			break;
		}
		snd_printd(" us428_start_alsa succeeded :-)\n");
	} while (0);

	if (err){
		snd_card_free(card);
		card = NULL;
	} else
		usb_ifnum_to_if(device, 0)->private_data = card;

	up(&snd_us428_open_mutex);
	return err;
}


/*
 * Probes for an us428 in hot boot state
 */
static void* us428_usb_probe(struct usb_device* device, unsigned int ifnum, const struct usb_device_id* device_id)
{
	struct usb_interface*	interface = device->actconfig->interface + ifnum;
	
			/* See if the device offered us matches what we can accept */
	if (device->descriptor.idVendor != 0x1604 || device->descriptor.idProduct != 0x8001)
		return 0;
		
	snd_printd("us428_usb_probe(ifnum=%i, d_id=%i, interface->act_altsetting=%i)\n", ifnum, (int)device_id, (int)interface->act_altsetting);
		
	return (void*)-1;	// Marker: we govern this device, no alsa yet.
}


MODULE_DEVICE_TABLE(usb, snd_us428_usb_id_table);
static int us428_ioctl(struct usb_device *dev, unsigned int code, void *buf);
static struct usb_driver snd_us428_usb_driver = {
	.name =		"snd-usb-us428",
	.probe =	us428_usb_probe,
	.disconnect =	snd_us428_usb_disconnect,
	.id_table =	snd_us428_usb_id_table,
	.driver_list =	LIST_HEAD_INIT(snd_us428_usb_driver.driver_list),
	.ioctl =        us428_ioctl
};


static int us428_ioctl(struct usb_device *dev, unsigned int code, void *buf)
{
	if ('U' != _IOC_TYPE(code))
		return -EBADRQC;

	if ((void*)-1 != usb_ifnum_to_if(dev, 0)->private_data)
		return -EBUSY;		// ALSA Interface activ. nobody may disturb.

	switch (_IOC_NR(code)) {
	case _IOC_NR(IOCTL_USx2x_OUT_PIPE2(0)):
	{
		int lret;
		return usb_bulk_msg(dev, usb_sndbulkpipe(dev, 2), buf, _IOC_SIZE(code), &lret,  HZ);
	}
	case _IOC_NR(IOCTL_USx2x_START_ALSA):
		us428_start_alsa(dev);
		break;
	case _IOC_NR(IOCTL_USx2x_STOP_ALSA):
		return -ENOSYS;
		break;
	default:
		return -EBADRQC;
	}

	snd_printk( "us428_ioctl(usb_device * %p, code %X, buf %p)\n", dev, code, buf);
	return 0;
}

/*
 * Frees the device.
 */
static void snd_us428_usb_disconnect(struct usb_device* device, void* ptr)
{
	if (ptr && (void*)-1 != ptr) {
		snd_card_t*		card = ptr;
		struct list_head*	p;

		down(&snd_us428_open_mutex);
		cleanup_us428ctls();
		/* release the midi resources */
		list_for_each(p, &us428(card)->chip.midi_list) {
			snd_usbmidi_disconnect(p, &snd_us428_usb_driver);
		}

		snd_printd("snd_us428_usb_disconnect(struct usb_device* =0x%X, void* ptr)\n", (int)device);

		snd_us428_unlinkSeq(&(us428(card)->AS04));

		usb_unlink_urb(us428(card)->In04urb);
		kfree(us428(card)->In04Buf);
		usb_free_urb(us428(card)->In04urb);

		snd_card_free(card);
		up(&snd_us428_open_mutex);
	}
	snd_printd("snd_us428_usb_disconnect() end\n");
}

static int __init snd_us428_module_init(void)
{
	int err;

	err = usb_register(&snd_us428_usb_driver);
	if (err < 0) {
		return err;
	}
	return 0;
}

static void __exit snd_us428_module_exit(void)
{
	usb_deregister(&snd_us428_usb_driver);
}

module_init(snd_us428_module_init)
module_exit(snd_us428_module_exit)

#ifndef MODULE

/* format is: snd-usb-us428=enable,index,id */

static int __init snd_us428_setup(char* str)
{
	static unsigned __initdata nr_dev = 0;

	if (nr_dev >= SNDRV_CARDS)
		return 0;
	(void)(get_option(&str, &enable[nr_dev]) == 2 &&
	       get_option(&str, &index[nr_dev]) == 2 &&
	       get_id(&str, &id[nr_dev]) == 2);
	nr_dev++;
	return 1;
}

__setup("snd-usb-us428=", snd_us428_setup);

#endif /* !MODULE */

--- End Message ---

Reply via email to