All of lore.kernel.org
 help / color / mirror / Atom feed
* Fwd: [usb-midi-fw-devel] proposal concerning cooperation of snd-usb-usx2x.o and rbtload
@ 2003-08-29 11:29 Karsten Wiese
  2003-08-29 16:28 ` Takashi Iwai
  0 siblings, 1 reply; 2+ messages in thread
From: Karsten Wiese @ 2003-08-29 11:29 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel

[-- Attachment #1: Type: text/plain, Size: 152 bytes --]

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

[-- Attachment #2: Karsten Wiese <annabellesgarden@yahoo.de>: [usb-midi-fw-devel] proposal concerning cooperation of snd-usb-usx2x.o and rbtload --]
[-- Type: message/rfc822, Size: 36394 bytes --]

[-- Attachment #2.1.1: Type: text/plain, Size: 1753 bytes --]

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

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2.1.2: rbtload.c --]
[-- Type: text/x-csrc; charset="us-ascii"; name="rbtload.c", Size: 11816 bytes --]

/*
 * rbtload.c
 * Loads a rbt file (fpga configuration) into a tascam usb device
 *
 * Copyright (c) 2003 Pedro López-Cabanillas <plcl@bigfoot.com>
 * 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;
}

[-- Attachment #2.1.3: usbusx2xioctl.h --]
[-- Type: text/x-chdr, Size: 231 bytes --]

#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

[-- Attachment #2.1.4: usbus428.c --]
[-- Type: text/x-csrc, Size: 17864 bytes --]

/*
 * 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 <annabellesgarden@yahoo.de>");
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 */

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: Fwd: [usb-midi-fw-devel] proposal concerning cooperation of snd-usb-usx2x.o and rbtload
  2003-08-29 11:29 Fwd: [usb-midi-fw-devel] proposal concerning cooperation of snd-usb-usx2x.o and rbtload Karsten Wiese
@ 2003-08-29 16:28 ` Takashi Iwai
  0 siblings, 0 replies; 2+ messages in thread
From: Takashi Iwai @ 2003-08-29 16:28 UTC (permalink / raw)
  To: Karsten Wiese; +Cc: alsa-devel

Hi Karsten,

At Fri, 29 Aug 2003 13:29:17 +0200,
Karsten Wiese wrote:
> 
> [1  <text/plain; us-ascii (7bit)>]
> 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.
(snip)
> 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).

hmm, what would be a gain by using ioctl?


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

you can create a hwdep device in the probe callback of snd-usb-us428,
and later create pcm and mixer devices dynamically after the firmware
is loaded.
this method is used in some other drivers like vx drivers.
you can use own mmap and ioctl for this device, too, if you want.

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

this is ok, we can merge later.

> - us428 and us224 (don't know about us122) have those knobs & sliders. they 
> are also special.

can't you implement these as standard mixer elements?
then all mixer programs can access them.


thanks,

Takashi


-------------------------------------------------------
This sf.net email is sponsored by:ThinkGeek
Welcome to geek heaven.
http://thinkgeek.com/sf

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2003-08-29 16:28 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2003-08-29 11:29 Fwd: [usb-midi-fw-devel] proposal concerning cooperation of snd-usb-usx2x.o and rbtload Karsten Wiese
2003-08-29 16:28 ` Takashi Iwai

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.