/*****************************************************************************
   MMUSBAUDIO: USB kernel module for the Roland UA-100, UA-5, SC-8850 & UM-1

   (c) 2001-2011 by Michael Minn - see www.michaelminn.com for contact info

   printk messages are written to /var/log/messages

   See /usr/src/linux/Documentation/usb for more information about USB programming

   This program is free software; you can redistribute it and/or modify
   it under the terms of version 2 of the GNU General Public License as 
   published by the Free Software Foundation.


   NOTES ON AUDIO PROTOCOL FOR THE ROLAND UA-100 and UA-5 ----

   For writes, UA-100 data is four interleaved 16-bit channels (wave 1 and wave 2)
   for a total of 8 bytes per sample. UA-5 has 2 interleaved 24-bit channels
   for a total of 6 bytes per sample.

   At 44.1 Khz sampling rate, to time isosynchronous writes correctly each URB 
   contain 1 ms of data. Each frame is 44 samples except for every 10th URB, 
   which is 45 samples. 

   For the UA-100, the URBs are 360 and 352 bytes, respectively. Accordingly, the
   UA-5 URBs are 270 and 263 (246) bytes, respectively.

   For Reads, however, each frame must be sent at full length (184 bytes) or you 
   will get a nasty kernel message in /var/log/messages: 
   "usb-uhci.c: interrupt, status 2, frame# xxx"

   At 48 Khz and 96 Khz, this uneven frame size is not needed since 1ms
   equals 48 and 96 samples, respectively.


****************************************************************************/

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/fs.h>
#include <linux/usb.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include "sound.h"
#include "soundcard.h"
#include "mmusbaudio.h"

//#include <linux/vermagic.h>
//MODULE_INFO(vermagic, VERMAGIC_STRING);

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
#error "This version of MMUSBAUDIO can only be used with 2.6.24 kernels or later."
#endif

/************************* Version Info *****************************/

#define DRIVER_AUTHOR "(c) 2001-2011 by Michael Minn"
#define DRIVER_DESC "MMUSBAUDIO: Linux USB Driver for Roland MIDI/Audio interfaces"
#define DRIVER_VERSION "v2011.08.23"

#define MMUSBAUDIO_MAX_MIDI_DEVICES 8
#define MMUSBAUDIO_MAX_AUDIO_DEVICES 16

// Queued URBs serve as buffers. Increasing this number will reduce dropouts
// but increase latency. This number should be a multiple of 10 to facilitate
// the 9+1 variable length URB size used for 44.1Khz audio. 60 seems to be
// a lower limit to keep Flash Player video from freezing or having ticks.

#define MMUSBAUDIO_AUDIO_URBS 100

#ifndef USB_ISO_ASAP
#define USB_ISO_ASAP URB_ISO_ASAP
#endif
 

/******************************* USB Audio *******************************/

struct mmusbaudio_audio_device
{
	/* User audio format */
	int channels;
	int resolution; /* in bytes */
	int sample_rate;

	/* Device audio format */
	int device_rate;
	int device_read_channels;
	int device_write_channels;
	int device_resolution;

	int read_interface;
	int write_interface;
	int read_alternate;
	int write_alternate;
	int read_endpoint;
	int write_endpoint;
	int read_packet_size;
	int write_packet_size;

	int mixer; // mixer device number allocated by OSS - mixers not actually implemented
	int oss_minor_number;
	char *device_name;

	struct urb *read_urb[MMUSBAUDIO_AUDIO_URBS];
	struct urb *write_urb[MMUSBAUDIO_AUDIO_URBS];

	int current_read_urb;
	int current_read_offset;
	int current_write_urb;
	int current_write_offset;
	int current_write_total;

	wait_queue_head_t read_wait_queue;
	wait_queue_head_t write_wait_queue;

	int in_use;
	struct usb_device *usb_device;
} mmusbaudio_audio_devices[MMUSBAUDIO_MAX_AUDIO_DEVICES];

struct file_operations mmusbaudio_audio_operations;
struct file_operations mmusbaudio_mixer_operations;

static struct mmusbaudio_audio_device *find_available_audio_device(
	int rate, int resolution, int is_input)
{
	int scan = 0;
	int status = 0;
	int device_number = 0;
	struct mmusbaudio_audio_device *device = NULL;

	// Search existing devices for unused read/write side
	for (scan = 0; scan < MMUSBAUDIO_MAX_AUDIO_DEVICES; ++scan)
	{
		// pr_info("Scan for %d %d %d = %d %d 0x%x", rate, resolution, is_input,
		// mmusbaudio_audio_devices[scan].device_rate,
		// mmusbaudio_audio_devices[scan].device_resolution,
		// mmusbaudio_audio_devices[scan].read_endpoint);

		if (mmusbaudio_audio_devices[scan].usb_device &&
		    (mmusbaudio_audio_devices[scan].device_rate == rate) &&
		    (mmusbaudio_audio_devices[scan].device_resolution == resolution) &&
		    ((is_input && (mmusbaudio_audio_devices[scan].read_endpoint <= 0)) ||
		     ((!is_input) && (mmusbaudio_audio_devices[scan].write_endpoint <= 0))))
			return mmusbaudio_audio_devices + scan;
	}

	// Register with soundcore to get next /dev/dspX number
	if ((status = register_sound_dsp(&mmusbaudio_audio_operations, -1)) < 0)
	{
		pr_err("Failure %d registering DSP device with OSS", status);
		return NULL;
	}

	// Limited to size of static array
	device_number = status >> 4;
	if (device_number >= MMUSBAUDIO_MAX_AUDIO_DEVICES)
	{
		unregister_sound_dsp(status);
		pr_err("Out of USB device buffers (next 0x%x)", status);
		return NULL;
	}

	device = mmusbaudio_audio_devices + device_number;
	if (device->usb_device)
	{
		unregister_sound_dsp(device_number);
		pr_err("Device number %d already allocated?!", status);
		return NULL;
	}

	memset(device, 0, sizeof(*device));

	// USB mixers are not supported.
	// Every device gets a fake mixer to prevent player problems with inaccessible mixer
	device->mixer = register_sound_mixer(&mmusbaudio_mixer_operations, -1);

	device->oss_minor_number = status;
	device->device_rate = rate;
	device->device_resolution = resolution;

	return device;
}

int mmusbaudio_assign_audio_class_device(struct usb_device *usb_device, int probe_interface)
{
	int scan = 0;
	unsigned char *interface = NULL;
	unsigned char *format = NULL;
	unsigned char buffer[512 + 256] = "";

	// The supported devices only have one configuration.
	// USB_REQ_GET_DESCRIPTOR reads the configuration descriptor and
	// all interface, audio and endpoint descriptors as well.

	int length = usb_control_msg(usb_device, usb_rcvctrlpipe(usb_device, 0),
		USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, (USB_DT_CONFIG << 8), 0, buffer, 
		sizeof(buffer), 100);

	if (length < 0)
	{
		pr_err("Failure %d reading configuration descriptors from the device", length);
		return length;
	}

	// Scan interfaces and endpoints
	while (scan < length)
	{
		int descriptor_length = buffer[scan];
		unsigned char descriptor_type = buffer[scan + 1]; 
		unsigned char descriptor_subtype = buffer[scan + 2]; 

		//pr_info("   Scan descriptor 0x%x (length 0x%x @ 0x%x of 0x%x)", 
		//	descriptor_type, descriptor_length, scan, length);

		// usb_interface_descriptor
		if (descriptor_type == 4) 
		{
			interface = buffer + scan;
			format = NULL;
		}

		// AudioStreaming Interface Descriptor - CS_INTERFACE.FORMAT_TYPE (pp 111)
		else if ((descriptor_type == 0x24) && (descriptor_subtype == 0x02))
			format = buffer + scan;

		// usb_endpoint_descriptor
		else if ((descriptor_type == 5) && interface && format && (interface[2] == probe_interface))
		{
			int interface_number = interface[2];
			int alternate_setting = interface[3];

			unsigned char *endpoint = buffer + scan;
			int endpoint_address = endpoint[2];
			// 0x82 = input, 0x01 = output
			int is_input = endpoint_address & USB_ENDPOINT_DIR_MASK; 
			char *dir_string = "out";

			// FORMAT_TYPE (pp 111)
			int channels = format[4];
			int bits = format[6];
			int supported_rates = format[7];
			int sampling_rate = 0;
			int rate_scan = 0;
			int max_packet_size = (int)endpoint[4] + ((int)endpoint[5] << 8);
			int packet_size = channels * (bits / 8) * sampling_rate / 1000;

			// Some output devices (like the Zoom B2.1U) support multiple 
			// sample rates with the size of written URB data determining 
			// the output sample rate. This probably should be available 
			// for ioctl() selection, but for now if one of the rates is 
			// the standard CD rate of 44,100, use that. Otherwise use the 
			// last rate in the list.

			while (rate_scan < supported_rates)
			{
				sampling_rate = (int) format[8 + (rate_scan * 3)] + 
						(((int) format[9 + (rate_scan * 3)]) << 8) + 
						(((int) format[10 + (rate_scan * 3)]) << 16);

 				if (sampling_rate == 44100)
					rate_scan = supported_rates;	
				else
					++rate_scan;
			}

			// Some devices have separate endpoints for low sampling rates and resolutions
			// Ignore them to avoid clogging device list
			if ((sampling_rate < 44100) || (bits < 16) || (channels < 2))
				pr_info("mmusbaudio standard audio class: "
					"Ignoring %d Hz / %d bit / %d ch interface (IF %02x, Alt %02x, EP %02x)",
					sampling_rate, bits, channels,
					interface_number, alternate_setting, endpoint_address);

			else
			{
				struct mmusbaudio_audio_device *device = 
					find_available_audio_device(sampling_rate, bits / 8, is_input);
				if (!device)
				{
					pr_err("No USB devices available");
					return -ENOMEM;
				}

				if (is_input)
					dir_string = "in";

				pr_info("mmusbaudio standard audio class: /dev/dsp%d %s: %d Hz / "
					"%d-bit / %d chan (IF %02x, ALT %02x, EP %02x)", 
					device->oss_minor_number >> 4, dir_string, sampling_rate, bits, 
					channels, interface_number, alternate_setting, endpoint_address);

				// 44.1 Khz devices sequence 9 frames of 44 samples and 1 frame of 45 
				// This is 9 * 176 bytes URBs + one additional 180 byte URB
				// mmusbaudio_audio_open() handles tweaking the size when allocating the URBs
				// Setting this to maxpacket (192 bytes) causes the device to play at a higher speed.
				if ((sampling_rate == 44100) && (bits == 16))
					packet_size = 180;
				else if ((sampling_rate == 44100) && (bits == 24))
					packet_size = 270;

				if (packet_size > max_packet_size)
					packet_size = max_packet_size;

				if (is_input)
				{
					device->read_interface = interface_number;
					device->read_alternate = alternate_setting;
					device->read_endpoint = endpoint_address;
					device->read_packet_size = packet_size;
					device->device_read_channels = channels;
				}
				else
				{
					device->write_interface = interface_number;
					device->write_alternate = alternate_setting;
					device->write_endpoint = endpoint_address;
					device->write_packet_size = packet_size;
					device->device_write_channels = channels;
				}

				device->usb_device = usb_device;
				device->device_name = "USB Audio Class Device";
				device->device_rate = sampling_rate;
				device->device_resolution = bits / 8;

				//pr_info("  %d bytes, if %d/%d, alt %d/%d, ep %d/%d, channels %d/%d, packet %d/%d", 
				//	device->device_resolution, 
				//	device->read_interface, device->write_interface,
				//	device->read_alternate, device->write_alternate,
				//	device->read_endpoint, device->write_alternate,
				//	device->device_read_channels, device->device_write_channels,
				//	device->read_packet_size, device->write_packet_size);
			}
		}

		if (descriptor_length <= 0)
			scan = length;
		else
			scan += descriptor_length;
	}

	return 0;
}

static int mmusbaudio_assign_vendor_audio_device(struct usb_device *usb_device, int probe_interface, char *device_name)
{
	int scan = 0;
	int count = 0;
	int device_number = -1;
	int interface_number = 0;
	int alternate_setting = 0;
	unsigned char buffer[256];

	// The supported devices only have one configuration
	// Reading the configuration descriptor reads in all 
	// interface and endpoint descriptors as well

	int length = usb_control_msg(usb_device, usb_rcvctrlpipe(usb_device, 0),
		USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, (USB_DT_CONFIG << 8), 0, buffer, 256, 100);

	if (length < 0)
	{
		pr_err("Failure %d reading configuration descriptors from the device", length);
		return length;
	}

	// Scan interfaces and endpoints:
	// Vendor-specific sampling rate / resolution uses
	// max packet size of endpoints as a signature 

	for (count = 0, scan = 0; (count < 50) && (scan < length); ++count)
	{
		int descriptor_length = buffer[scan];
		unsigned char descriptor_type = buffer[scan + 1]; // type 4 = interface, type 5 = endpoint
		unsigned char endpoint_address = buffer[scan + 2];
		unsigned char is_input = endpoint_address & USB_ENDPOINT_DIR_MASK; // 0x82 = input, 0x01 = output
		short packet_size = *((short*) &buffer[scan + 4]);

		// pr_info("   Scan descriptor 0x%x, if 0x%x, alt 0x%x, endpoint 0x%x, size 0x%x (%d)", 
		//	descriptor_type, interface_number, alternate_setting,
		//	endpoint_address, packet_size, packet_size);

		if (descriptor_type == 4)
		{
			interface_number = buffer[scan + 2];
			alternate_setting = buffer[scan + 3];
		}

		else if ((descriptor_type == 5) && (interface_number != probe_interface))
			; // Skip interfaces other than the one currently being probed

		else if ((descriptor_type == 5) && (packet_size == 192) && !is_input)
		{
			struct mmusbaudio_audio_device *device = 
				find_available_audio_device(44100, 2, is_input);
			if (!device)
			{
				pr_err("No USB devices available");
				return -ENOMEM;
			}

			pr_info("44.1 KHz / 16-bit output interface 0x%x, alternate 0x%x, endpoint 0x%x", 
				interface_number, alternate_setting, endpoint_address);

			device->usb_device = usb_device;
			device->device_name = device_name;
			device->write_interface = interface_number;
			device->write_alternate = alternate_setting;
			device->write_endpoint = endpoint_address;

			// 44.1 Khz devices (UA-1/5) sequence 9 frames of 44 samples and 1 frame of 45 
			// This is 9 180 bytes URBs + one additional 176 byte URB
			// mmusbaudio_audio_open() handles tweaking the size when allocating the URBs
			// Setting this to maxpacket (192 bytes) causes the device to play at a higher speed.
			device->write_packet_size = 180;
			device->device_rate = 44100;
			device->device_write_channels = 2;
			device->device_resolution = 2;

			device_number = device - mmusbaudio_audio_devices;
		}

		else if ((descriptor_type == 5) && (packet_size == 192) && is_input)
		{
			struct mmusbaudio_audio_device *device = 
				find_available_audio_device(44100, 2, is_input);
			if (!device)
			{
				pr_err("No USB devices available");
				return -ENOMEM;
			}

			pr_info("44.1 KHz / 16-bit input interface 0x%x, alternate 0x%x, endpoint 0x%x", 
				interface_number, alternate_setting, endpoint_address);
			device->usb_device = usb_device;
			device->device_name = device_name;
			device->read_interface = interface_number;
			device->read_alternate = alternate_setting;
			device->read_endpoint = endpoint_address;
			device->read_packet_size = 180; // 1ms per URB = (9 x 176) + (1 * 180)
			device->device_rate = 44100;
			device->device_read_channels = 2;
			device->device_resolution = 2;

			device_number = device - mmusbaudio_audio_devices;
		}

		else if ((descriptor_type == 5) && (packet_size == 208) && !is_input)
		{
			struct mmusbaudio_audio_device *device = 
				find_available_audio_device(48000, 2, is_input);
			if (!device)
			{
				pr_err("No USB devices available");
				return -ENOMEM;
			}

			pr_info("48 KHz / 16-bit output interface 0x%x, alternate 0x%x, endpoint 0x%x", 
				interface_number, alternate_setting, endpoint_address);
			device->usb_device = usb_device;
			device->device_name = device_name;
			device->write_interface = interface_number;
			device->write_alternate = alternate_setting;
			device->write_endpoint = endpoint_address;
			device->write_packet_size = 192; // 1ms / URB
			device->device_rate = 48000;
			device->device_write_channels = 2;
			device->device_resolution = 2;

			device_number = device - mmusbaudio_audio_devices;
		}

		else if ((descriptor_type == 5) && (packet_size == 208) && is_input)
		{
			struct mmusbaudio_audio_device *device = 
				find_available_audio_device(48000, 2, is_input);
			if (!device)
			{
				pr_err("No USB devices available");
				return -ENOMEM;
			}

			pr_info("48 KHz / 16-bit input interface 0x%x, alternate 0x%x, endpoint 0x%x", 
				interface_number, alternate_setting, endpoint_address);
			device->usb_device = usb_device;
			device->device_name = device_name;
			device->read_interface = interface_number;
			device->read_alternate = alternate_setting;
			device->read_endpoint = endpoint_address;
			device->read_packet_size = 192; // 1ms / URB
			device->device_rate = 48000;
			device->device_read_channels = 2;
			device->device_resolution = 2;

			device_number = device - mmusbaudio_audio_devices;
		}

		else if ((descriptor_type == 5) && (packet_size == 240) && !is_input)
		{
			struct mmusbaudio_audio_device *device = 
				find_available_audio_device(48000, 3, is_input);
			if (!device)
			{
				pr_err("No USB devices available");
				return -ENOMEM;
			}

			pr_info("44.1 KHz / 24-bit output interface 0x%x, alternate 0x%x, endpoint 0x%x", 
				interface_number, alternate_setting, endpoint_address);
			device->usb_device = usb_device;
			device->device_name = device_name;
			device->write_interface = interface_number;
			device->write_alternate = alternate_setting;
			device->write_endpoint = endpoint_address;
			device->write_packet_size = 240;
			device->device_rate = 44100;
			device->device_write_channels = 2;
			device->device_resolution = 3;

			device_number = device - mmusbaudio_audio_devices;
		}

		else if ((descriptor_type == 5) && (packet_size == 252) && is_input)
		{
			struct mmusbaudio_audio_device *device = 
				find_available_audio_device(48000, 3, is_input);
			if (!device)
			{
				pr_err("No USB devices available");
				return -ENOMEM;
			}

			pr_info("44.1 KHz / 24-bit input interface 0x%x, alternate 0x%x, endpoint 0x%x", 
				interface_number, alternate_setting, endpoint_address);
			device->usb_device = usb_device;
			device->device_name = device_name;
			device->read_interface = interface_number;
			device->read_alternate = alternate_setting;
			device->read_endpoint = endpoint_address;
			device->read_packet_size = 252;
			device->device_rate = 44100;
			device->device_read_channels = 2;
			device->device_resolution = 3;

			device_number = device - mmusbaudio_audio_devices;
		}

		else if ((descriptor_type == 5) && (packet_size == 288) && !is_input)
		{
			struct mmusbaudio_audio_device *device = 
				find_available_audio_device(44100, 3, is_input);
			if (!device)
			{
				pr_err("No USB devices available");
				return -ENOMEM;
			}

			pr_info("44.1 KHz / 24-bit output interface 0x%x, alternate 0x%x, endpoint 0x%x", 
				interface_number, alternate_setting, endpoint_address);
			device->usb_device = usb_device;
			device->device_name = device_name;
			device->write_interface = interface_number;
			device->write_alternate = alternate_setting;
			device->write_endpoint = endpoint_address;
			device->write_packet_size = 270;
			device->device_rate = 44100;
			device->device_write_channels = 2;
			device->device_resolution = 3;

			device_number = device - mmusbaudio_audio_devices;
		}

		else if ((descriptor_type == 5) && (packet_size == 288) && is_input)
		{
			struct mmusbaudio_audio_device *device = 
				find_available_audio_device(44100, 3, is_input);
			if (!device)
			{
				pr_err("No USB devices available");
				return -ENOMEM;
			}

			pr_info("44.1 KHz / 24-bit input interface 0x%x, alternate 0x%x, endpoint 0x%x", 
				interface_number, alternate_setting, endpoint_address);
			device->usb_device = usb_device;
			device->device_name = device_name;
			device->read_interface = interface_number;
			device->read_alternate = alternate_setting;
			device->read_endpoint = endpoint_address;
			device->read_packet_size = 270;
			device->device_rate = 44100;
			device->device_read_channels = 2;
			device->device_resolution = 3;

			device_number = device - mmusbaudio_audio_devices;
		}

		else if ((descriptor_type == 5) && (packet_size == 294) && is_input)
		{
			struct mmusbaudio_audio_device *device = 
				find_available_audio_device(48000, 3, is_input);
			if (!device)
			{
				pr_err("No USB devices available");
				return -ENOMEM;
			}

			pr_info("48 KHz / 24-bit input interface 0x%x, alternate 0x%x, endpoint 0x%x", 
				interface_number, alternate_setting, endpoint_address);
			device->usb_device = usb_device;
			device->device_name = device_name;
			device->read_interface = interface_number;
			device->read_alternate = alternate_setting;
			device->read_endpoint = endpoint_address;
			device->read_packet_size = 288;
			device->device_rate = 48000;
			device->device_read_channels = 2;
			device->device_resolution = 3;

			device_number = device - mmusbaudio_audio_devices;
		}

		else if ((descriptor_type == 5) && (packet_size == 320) && !is_input)
		{
			struct mmusbaudio_audio_device *device = 
				find_available_audio_device(48000, 3, is_input);
			if (!device)
			{
				pr_err("No USB devices available");
				return -ENOMEM;
			}

			pr_info("48 KHz / 24-bit output interface 0x%x, alternate 0x%x, endpoint 0x%x", 
				interface_number, alternate_setting, endpoint_address);
			device->usb_device = usb_device;
			device->device_name = device_name;
			device->write_interface = interface_number;
			device->write_alternate = alternate_setting;
			device->write_endpoint = endpoint_address;
			device->write_packet_size = 288;
			device->device_rate = 48000;
			device->device_write_channels = 2;
			device->device_resolution = 3;

			device_number = device - mmusbaudio_audio_devices;
		}

		else if ((descriptor_type == 5) && (packet_size == 320) && is_input)
		{
			struct mmusbaudio_audio_device *device = 
				find_available_audio_device(48000, 3, is_input);
			if (!device)
			{
				pr_err("No USB devices available");
				return -ENOMEM;
			}

			pr_info("48 KHz / 24-bit input interface 0x%x, alternate 0x%x, endpoint 0x%x", 
				interface_number, alternate_setting, endpoint_address);
			device->usb_device = usb_device;
			device->device_name = device_name;
			device->read_interface = interface_number;
			device->read_alternate = alternate_setting;
			device->read_endpoint = endpoint_address;
			device->read_packet_size = 288;
			device->device_rate = 48000;
			device->device_read_channels = 2;
			device->device_resolution = 3;
			//device->read_buffer_length = 4800 * 6;
			//device->read_buffer = kmalloc(device->read_buffer_length + 16, GFP_KERNEL);

			device_number = device - mmusbaudio_audio_devices;
		}

		else if ((descriptor_type == 5) && (packet_size == 360) && !is_input)
		{
			struct mmusbaudio_audio_device *device = 
				find_available_audio_device(44100, 2, is_input);
			if (!device)
			{
				pr_err("No USB devices available");
				return -ENOMEM;
			}

			pr_info("44.1 KHz / 16-bit input interface 0x%x, alternate 0x%x, endpoint 0x%x", 
				interface_number, alternate_setting, endpoint_address);
			device->usb_device = usb_device;
			device->device_name = device_name;
			device->write_interface = interface_number;
			device->write_alternate = alternate_setting;
			device->write_endpoint = endpoint_address;
			device->write_packet_size = 360;
			device->device_rate = 44100;
			device->device_resolution = 2;
			device->device_write_channels = 4;

			device_number = device - mmusbaudio_audio_devices;
		}

		else if ((descriptor_type == 5) && (packet_size == 360) && !is_input)
		{
			struct mmusbaudio_audio_device *device = 
				find_available_audio_device(44100, 2, is_input);
			if (!device)
			{
				pr_err("No USB devices available");
				return -ENOMEM;
			}

			pr_info("44.1 KHz / 16-bit input interface 0x%x, alternate 0x%x, endpoint 0x%x", 
				interface_number, alternate_setting, endpoint_address);
			device->usb_device = usb_device;
			device->device_name = device_name;
			device->read_interface = interface_number;
			device->read_alternate = alternate_setting;
			device->read_endpoint = endpoint_address;
			device->read_packet_size = 184;
			device->device_rate = 44100;
			device->device_read_channels = 2;
			device->device_resolution = 2;

			device_number = device - mmusbaudio_audio_devices;
		}

		else if ((descriptor_type == 5) && (packet_size == 588) && !is_input)
		{
			struct mmusbaudio_audio_device *device = 
				find_available_audio_device(48000, 3, is_input);
			if (!device)
			{
				pr_err("No USB devices available");
				return -ENOMEM;
			}

			pr_info("48 KHz / 24-bit output interface 0x%x, alternate 0x%x, endpoint 0x%x", 
				interface_number, alternate_setting, endpoint_address);
			device->usb_device = usb_device;
			device->device_name = device_name;
			device->write_interface = interface_number;
			device->write_alternate = alternate_setting;
			device->write_endpoint = endpoint_address;
			device->write_packet_size = 576;
			device->device_rate = 48000;
			device->device_write_channels = 4;
			device->device_resolution = 3;

			device_number = device - mmusbaudio_audio_devices;
		}

		else if ((descriptor_type == 5) && (packet_size == 608) && !is_input)
		{
			struct mmusbaudio_audio_device *device = 
				find_available_audio_device(96000, 3, is_input);
			if (!device)
			{
				pr_err("No USB devices available");
				return -ENOMEM;
			}

			pr_info("96 KHz / 24-bit output interface 0x%x, alternate 0x%x, endpoint 0x%x", 
				interface_number, alternate_setting, endpoint_address);
			device->usb_device = usb_device;
			device->device_name = device_name;
			device->write_interface = interface_number;
			device->write_alternate = alternate_setting;
			device->write_endpoint = endpoint_address;
			device->read_interface = 0;
			device->read_alternate = 0;
			device->read_endpoint = 0;

			device->write_packet_size = 576;
			device->device_rate = 96000;
			device->device_write_channels = 2;
			device->device_resolution = 3;

			device_number = device - mmusbaudio_audio_devices;
		}

		else if ((descriptor_type == 5) && (packet_size == 608) && is_input)
		{
			struct mmusbaudio_audio_device *device = 
				find_available_audio_device(96000, 3, is_input);
			if (!device)
			{
				pr_err("No USB devices available");
				return -ENOMEM;
			}

			pr_info("96 KHz / 24-bit input interface 0x%x, alternate 0x%x, endpoint 0x%x", 
				interface_number, alternate_setting, endpoint_address);
			device->usb_device = usb_device;
			device->device_name = device_name;
			device->write_interface = 0;
			device->write_alternate = 0;
			device->write_endpoint = 0;
			device->write_packet_size = 0;
			device->read_interface = interface_number;
			device->read_alternate = alternate_setting;
			device->read_endpoint = endpoint_address;
			device->read_packet_size = 576;
			device->device_rate = 96000;
			device->device_read_channels = 2;
			device->device_write_channels = 2;
			device->device_resolution = 3;

			device_number = device - mmusbaudio_audio_devices;
		}

		if (descriptor_length <= 0)
			scan = length;
		else
			scan += descriptor_length;
	}

	if (device_number >= 0)
	{
		if (device_number == 0)
			pr_info("Registered %s as /dev/dsp", device_name);
		else
			pr_info("Registered %s as /dev/dsp%d", device_name, device_number);
	}

	return device_number;
}

static int mmusbaudio_release_audio_device(struct mmusbaudio_audio_device *device)
{
	pr_info("Disconnecting AUDIO device 0x%02x", device->oss_minor_number >> 4);

	device->channels = 0;
	device->usb_device = NULL;
	unregister_sound_dsp(device->oss_minor_number);
	unregister_sound_mixer(device->mixer);

	return 0;
}


// Old kernel: static void mmusbaudio_audio_write_callback(struct urb *urb, struct pt_regs *registers)

static void mmusbaudio_audio_write_callback(struct urb *urb)
{
	struct mmusbaudio_audio_device *device = 
		(struct mmusbaudio_audio_device *) urb->context;

	if (device && urb->context && waitqueue_active(&device->write_wait_queue))
		wake_up_interruptible(&device->write_wait_queue);

	//mmusbaudio_audio_submit_write_urb(urb);
}

static int mmusbaudio_audio_submit_write_urb(struct urb *urb)
{
	int status = 0;
	struct mmusbaudio_audio_device *device = 
		(struct mmusbaudio_audio_device*) urb->context;

	if (!device)
		return -EHOSTDOWN;

	urb->dev = device->usb_device;
	urb->complete = mmusbaudio_audio_write_callback;
	urb->pipe = usb_sndisocpipe(device->usb_device, device->write_endpoint);

	//pr_info("Before submit %d, usb_maxpacket() %d", urb->transfer_buffer_length, 
	//usb_maxpacket(device->usb_device, urb->pipe, 1));

	urb->actual_length = urb->transfer_buffer_length;
	urb->status = 0;
	urb->transfer_flags = USB_ISO_ASAP;
	urb->number_of_packets = 1;
	urb->iso_frame_desc[0].offset = 0;
	urb->iso_frame_desc[0].status = 0;
	urb->iso_frame_desc[0].length = urb->transfer_buffer_length;
	urb->iso_frame_desc[0].actual_length = urb->transfer_buffer_length;

	if ((status = usb_submit_urb(urb, GFP_ATOMIC)) < 0)
	{
		urb->context = NULL;
		pr_err("failure %d submitting write_urb", status);
	}

	return status;
}

//static void mmusbaudio_audio_read_callback(struct urb *urb, struct pt_regs *registers)
static void mmusbaudio_audio_read_callback(struct urb *urb)
{
	struct mmusbaudio_audio_device *device = (struct mmusbaudio_audio_device *) urb->context;

	/* Wake read() if waiting */
	if (device && waitqueue_active(&device->read_wait_queue))
		wake_up_interruptible(&device->read_wait_queue);
}


static int mmusbaudio_audio_submit_read_urb(struct urb *urb)
{
	// int retry = 40;
	int status = 0;
	struct mmusbaudio_audio_device *device = 
		(struct mmusbaudio_audio_device *) urb->context;

	if (!device)
		return -EHOSTDOWN;

	urb->dev = device->usb_device;
	urb->complete = mmusbaudio_audio_read_callback;
	urb->pipe = usb_rcvisocpipe(device->usb_device, device->read_endpoint);
	urb->actual_length = urb->transfer_buffer_length;
	urb->status = 0;
	urb->transfer_flags = USB_ISO_ASAP;
	urb->number_of_packets = 1;
	urb->iso_frame_desc[0].offset = 0;
	urb->iso_frame_desc[0].status = 0;
	urb->iso_frame_desc[0].length = urb->transfer_buffer_length;
	urb->iso_frame_desc[0].actual_length = urb->transfer_buffer_length;

	if ((status = usb_submit_urb(urb, GFP_ATOMIC)) < 0)
	{
		urb->context = NULL;
		pr_err("failure %d submitting read_urb", status);
	}

	return status;
}

static int mmusbaudio_audio_open (struct inode *inode, struct file *file)
{
	int scan = 0;
	int status = 0;
	int device_number = (MINOR(inode->i_rdev) >> 4) & 0x7;
	struct mmusbaudio_audio_device *device = &mmusbaudio_audio_devices[device_number];

	// pr_info("audio open(device minor number %x)", MINOR(inode->i_rdev));

	if (!device || !device->usb_device)
	{
		pr_err("No device for audio open() device %d", device_number);
		return -EHOSTDOWN;
	}

	if (device->in_use)
	{
		pr_err("Open request for busy DSP device %d", device_number);
		return -EBUSY;
	}
	++device->in_use;
	
	//if ((status = usb_reset_device(device->usb_device)) < 0)
	//{
	//	pr_err("Failure %d resetting device", status);
	//	return status;
	//}

	// Initialize them early to prevent kernel oops if there are other problems
	init_waitqueue_head(&device->read_wait_queue);
	init_waitqueue_head(&device->write_wait_queue);

	// These calls used to sporadically return -110 (ETIMEDOUT)
	if (device->read_packet_size > 0)
		if ((status = usb_set_interface(device->usb_device, device->read_interface, 
			device->read_alternate)) < 0)
		{
			pr_err("Failure %d setting read interface (interface %x, alternate %x", 
				status, device->write_interface, device->write_alternate);
			return status;
		}

	if (device->write_packet_size > 0)
		if ((status = usb_set_interface(device->usb_device, device->write_interface, 
			device->write_alternate)) < 0)
		{
			pr_err("Failure %d setting write interface (interface 0x%x, alternate 0x%x", 
				status, device->write_interface, device->write_alternate);
			return status;
		}

	file->private_data = (void*) device;
	device->channels = 2;
	device->resolution = 2;
	device->sample_rate = 44100;
	device->current_read_urb = 0;
	device->current_read_offset = 0;
	device->current_write_urb = 0;
	device->current_write_offset = 0;
	device->current_write_total = 0;
	memset(device->write_urb, 0, sizeof(struct urb*) * MMUSBAUDIO_AUDIO_URBS);
	memset(device->read_urb, 0, sizeof(struct urb*) * MMUSBAUDIO_AUDIO_URBS);

	/* Allocate the URBs */
	for (scan = 0; scan < MMUSBAUDIO_AUDIO_URBS; ++scan)
		if (device->write_packet_size > 0)
		{
			// Don't do a memset() to clear the URB structure after allocating. This 
			// was causing URBs to behave strangely at shutdown time (11/16/04)
			device->write_urb[scan] = usb_alloc_urb(1, GFP_KERNEL);
			// pr_err("alloc write %d (%x)", scan, (int) device->write_urb[scan]);
			device->write_urb[scan]->context = device;
			device->write_urb[scan]->interval = 1;
			device->write_urb[scan]->transfer_buffer = kmalloc(device->write_packet_size, GFP_KERNEL);
			memset(device->write_urb[scan]->transfer_buffer, 0, device->write_packet_size);
			device->write_urb[scan]->transfer_buffer_length = device->write_packet_size;
			device->write_urb[scan]->actual_length = device->write_packet_size;
			device->write_urb[scan]->number_of_packets = 1;
			device->write_urb[scan]->iso_frame_desc[0].offset = 0;
			device->write_urb[scan]->iso_frame_desc[0].length = device->write_packet_size;
			device->write_urb[scan]->iso_frame_desc[0].actual_length = device->write_packet_size;
			device->write_urb[scan]->iso_frame_desc[0].status = 0;

			// 44.1 Khz devices sequence 9 frames of 44 samples and 1 frame of 45 
			if ((device->device_rate == 44100) && ((scan % 10) != 9))
				device->write_urb[scan]->transfer_buffer_length -= 
					(device->device_resolution * device->device_write_channels);
		}

	for (scan = 0; scan < MMUSBAUDIO_AUDIO_URBS; ++scan)
		if (device->read_packet_size > 0) 
		{
			device->read_urb[scan] = usb_alloc_urb(1, GFP_KERNEL);
			// pr_err("alloc read %d (%x)", scan, (int) device->read_urb[scan]);
			device->read_urb[scan]->context = device;
			device->read_urb[scan]->interval = 1;
			device->read_urb[scan]->transfer_buffer = kmalloc(device->read_packet_size, GFP_KERNEL);
			memset(device->read_urb[scan]->transfer_buffer, 0, device->read_packet_size);

			device->read_urb[scan]->transfer_buffer_length = device->read_packet_size;
			device->read_urb[scan]->actual_length = device->read_packet_size;
			device->read_urb[scan]->number_of_packets = 1;
			device->read_urb[scan]->iso_frame_desc[0].offset = 0;
			device->read_urb[scan]->iso_frame_desc[0].length = device->read_packet_size;
			device->read_urb[scan]->iso_frame_desc[0].actual_length = device->read_packet_size;
			device->read_urb[scan]->iso_frame_desc[0].status = 0;
		}

	try_module_get(THIS_MODULE);

	return status;
}

static int mmusbaudio_audio_release (struct inode *inode, struct file *file)
{
	int scan;
	//int status = 0;
	//int timeout = HZ / 2;
	struct mmusbaudio_audio_device *device = (struct mmusbaudio_audio_device*) file->private_data;
	int device_number = device - mmusbaudio_audio_devices;

	// pr_info("audio close(context %x)", (int) device);
	if (!device || (device_number < 0) || (device_number >= MMUSBAUDIO_MAX_AUDIO_DEVICES))
	{
		pr_err("Invalid audio device 0x%x in file structure given to close()", device_number);
		return -EINVAL;
	}

	//device->is_running = 0;
	//pr_info("Giving URBs time to cleanup?");
	//set_current_state(TASK_UNINTERRUPTIBLE);
	//schedule_timeout(HZ / 4);

	// pr_info("Unlinking");
	for (scan = 0; device->usb_device && 
		(scan < MMUSBAUDIO_AUDIO_URBS); ++scan)
	{
		if (device->write_urb[scan])
			usb_kill_urb(device->write_urb[scan]);

		if (device->read_urb[scan])
			usb_kill_urb(device->read_urb[scan]);
	}

	// pr_info("Freeing URBs");
	for (scan = 0; device->usb_device && 
		(scan < MMUSBAUDIO_AUDIO_URBS); ++scan)
	{
		if (device->write_urb[scan])
		{
			if (device->write_urb[scan]->transfer_buffer)
				kfree(device->write_urb[scan]->transfer_buffer);
			usb_free_urb(device->write_urb[scan]);
			device->write_urb[scan] = NULL;
		}

		if (device->read_urb[scan])
		{
			if (device->read_urb[scan]->transfer_buffer)
				kfree(device->read_urb[scan]->transfer_buffer);
			usb_free_urb(device->read_urb[scan]);
			device->read_urb[scan] = NULL;
		}
	}

	device->in_use = 0;

	module_put(THIS_MODULE);

	//pr_info("Audio device closed");

	return 0;
}

static ssize_t mmusbaudio_audio_write (struct file *file, const char *data, size_t data_size, loff_t *offset)
{
	int status = 0;
	struct mmusbaudio_audio_device *device = (struct mmusbaudio_audio_device*) file->private_data;
	int device_number = device - mmusbaudio_audio_devices;
	int source_samples = 0;
	int destination_sample = 0;
	int destination_samples = 0;
	int source_bytes_per_sample = 0;
	int resolution_offset = 0;
	unsigned long copy_status = 0; // needed to prevent compile warnings - unused
	struct urb *write_urb = NULL;

	//pr_info("audio write(%d, src chan %d, dev chan %d)", data_size, device->channels, device->device_write_channels);
	
	if (!device || (device_number < 0) || (device_number >= MMUSBAUDIO_MAX_AUDIO_DEVICES))
	{
		pr_err("Invalid audio device 0x%x in file structure given to write()", device_number);
		return -EINVAL;
	}

	if (!device->usb_device)
	{
		pr_err("No USB device pointer for audio write()");
		return -EHOSTDOWN;
	}

	if (!device->in_use)
	{
		pr_err("Write request for unopened DSP device %d", device_number);
		return -EHOSTDOWN;
	}

	if (device->write_packet_size <= 0) 
	{
		pr_err("write() on read-only device");
		return -EROFS;
	}

	source_samples = data_size / device->resolution / device->channels;
	destination_samples = source_samples * device->device_rate / device->sample_rate;
	source_bytes_per_sample = device->resolution * device->channels;
	resolution_offset = device->device_resolution - device->resolution;

	if (resolution_offset < 0)
	{
		pr_err("Invalid %d bytes/sample for device %d bytes/sample", 
			device->device_resolution, device->resolution);
		return -EINVAL;
	}

	// Wait if current URB hasn't returned yet
	write_urb = device->write_urb[device->current_write_urb];
	if ((status = wait_event_interruptible(device->write_wait_queue, 
			(write_urb->status != -EINPROGRESS))) < 0)
	{
		if (status == -ERESTARTSYS)
			return 0;

		pr_err("write() failure %d waiting for audio URB to complete", status);
		return status;
	}

	for (destination_sample = 0; destination_sample < destination_samples; ++destination_sample)
	{
		int channel = 0;
		int source_sample = destination_sample * device->sample_rate / device->device_rate;

		// copy data into URB transfer buffer channel-by-channel
		for (channel = 0; channel < device->device_write_channels; ++channel, 
			device->current_write_offset += device->device_resolution)
		{
			int source_index = (source_sample * source_bytes_per_sample) +
				((channel % device->channels) * device->resolution);

			// Copy data into the buffer 
			if (device->resolution != 1)
				copy_status = copy_from_user(write_urb->transfer_buffer + 
					device->current_write_offset + resolution_offset,
					data + source_index, device->device_resolution);

			// 8-bit input is unsigned char
			else
			{
				unsigned char value = 0;
				copy_status = copy_from_user(&value, data + source_index, 1);

				if (value & 0x80)
					value &= 0x7f;
				else
					value = (value & 0x7f) - 128;

				((char*)write_urb->transfer_buffer) 
					[device->current_write_offset + resolution_offset] = value;
			}

			// Bug with ticks in mono conversion was here 1/15/05
			if (channel < (device->channels - 1))
				source_index += device->resolution;
		}

		// If URB is full, submit it and move on to the next URB
		if (device->current_write_offset >= write_urb->transfer_buffer_length)
		{
			if ((status = mmusbaudio_audio_submit_write_urb(write_urb)) < 0)
				return status;

			device->current_write_offset = 0;
			device->current_write_urb = (device->current_write_urb + 1) % MMUSBAUDIO_AUDIO_URBS;
			write_urb = device->write_urb[device->current_write_urb];
			if ((status = wait_event_interruptible(device->write_wait_queue, 
					(write_urb->status != -EINPROGRESS))) < 0)
			{
				if (status == -ERESTARTSYS)
					return 0;

				pr_err("write() failure %d waiting for audio URB to complete", status);
				return status;
			}
		}
	}

	device->current_write_total += data_size;

	return data_size;
}

static ssize_t mmusbaudio_audio_read (struct file *file, char *data, size_t size, loff_t *offset)
{
	int status = 0;
	struct mmusbaudio_audio_device *device = (struct mmusbaudio_audio_device*) file->private_data;
	int device_number = device - mmusbaudio_audio_devices;
	int resolution_offset = 0;
	int destination_samples = 0;
	int source_samples = 0;
	int source_sample = 0;
	unsigned long copy_status = 0; // needed to prevent compile warnings - unused
	struct urb *read_urb = NULL;

	// pr_info("audio read %d bytes", size);

	// pr_info("  Device channels %d, user channels %d", device->device_read_channels, device->channels);

	if (!device || (device_number < 0) || (device_number >= MMUSBAUDIO_MAX_AUDIO_DEVICES))
	{
		pr_err("Invalid audio device 0x%x in file structure given to read()", device_number);
		return -EINVAL;
	}

	if (!device->usb_device)
	{
		pr_err("No USB device pointer for audio read()");
		return -EHOSTDOWN;
	}

	if (!device->in_use)
	{
		pr_err("Open request for unopened DSP device %d", device_number);
		return -EHOSTDOWN;
	}

	if (device->read_packet_size <= 0)
	{
		pr_err("Read request for write-only device %d", device_number);
		return -EINVAL;
	}

	resolution_offset = device->device_resolution - device->resolution;
	destination_samples = size / device->channels / device->resolution;
	source_samples = destination_samples * device->device_rate / device->sample_rate;

	if (resolution_offset < 0)
	{
		pr_err("Invalid %d bytes/sample for device %d bytes/sample", 
			device->device_resolution, device->resolution);
		return -EINVAL;
	}

	// Wait if current URB hasn't returned yet
	read_urb = device->read_urb[device->current_read_urb];
	if ((status = wait_event_interruptible(device->read_wait_queue, 
			(read_urb->status != -EINPROGRESS))) < 0)
	{
		if (status == -ERESTARTSYS)
			return 0;

		pr_err("read() failure %d waiting for audio URB to complete", status);
		return status;
	}

	// Copy data from URBs sample-by-sample
	for (source_sample = 0; source_sample < source_samples; ++source_sample)
	{
		int channel = 0;
		int destination_sample = source_sample * 
			device->sample_rate / device->device_rate;
		int destination_index = destination_sample * 
			device->resolution * device->channels;
		int source_index = device->current_read_offset + resolution_offset;

		//for (channel = 0; channel < device->channels; ++channel)

		for (channel = 0; channel < device->channels; ++channel)
		{
			if (device->resolution != 1)
				copy_status = copy_to_user(data + destination_index, 
					read_urb->transfer_buffer + source_index, device->resolution);

			else // 8-bit unsigned
			{
				unsigned char value = ((unsigned char*)read_urb->transfer_buffer)[source_index];
				value += 128;
				copy_status = copy_to_user(data + destination_index, &value, 1);
			}

			destination_index += device->resolution;
			source_index += device->device_resolution;
		}

		device->current_read_offset += (device->device_read_channels * device->device_resolution);

		if (device->current_read_offset >= read_urb->actual_length)
		{
			if ((status = mmusbaudio_audio_submit_read_urb(read_urb)) < 0)
				return status;

			device->current_read_offset = 0;
			device->current_read_urb = (device->current_read_urb + 1) % MMUSBAUDIO_AUDIO_URBS;
			read_urb = device->read_urb[device->current_read_urb];

			if ((status = wait_event_interruptible(device->read_wait_queue, 
					(read_urb->status != -EINPROGRESS))) < 0)
			{
				if (status == -ERESTARTSYS)
					return 0;

				pr_err("read() failure %d waiting for audio URB to complete", status);
				return status;
			}
		}
			
	}

	return size;
}

#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
static int mmusbaudio_audio_ioctl (struct inode *inode, struct file *file, unsigned int command, unsigned long data)
#else
static long mmusbaudio_audio_ioctl (struct file *file, unsigned int command, unsigned long data)
#endif
{
	int data_value = 0;
	struct mmusbaudio_audio_device *device = (struct mmusbaudio_audio_device*) file->private_data;
	int device_number = device - mmusbaudio_audio_devices;
	unsigned long copy_status; // needed to prevent compile warnings with copy_to_user() - value ignored

	//pr_info("audio ioctl(%x)", command);

	if (!device || (device_number < 0) || (device_number >= MMUSBAUDIO_MAX_AUDIO_DEVICES))
	{
		pr_err("Invalid audio device 0x%x in file structure given to ioctl()", device_number);
		return -EINVAL;
	}

	if (!device->usb_device)
	{
		pr_err("No USB device pointer for audio ioctl()");
		return -ENODEV;
	}

	if (!device->in_use)
		return -ENODEV;
		
	if (data)
		copy_status = copy_from_user(&data_value, (void*) data, 4);
		//data_value = *((int*)data);

	if (command == SNDCTL_DSP_SETFMT) // c0045005
	{
		if (data_value == AFMT_U8)
			device->resolution = 1;
		else if (data_value == AFMT_S16_LE)
			device->resolution = 2;
		else if (data_value == AFMT_S24_LE)
			device->resolution = 3;
		else
		{
			pr_err("Invalid DSP format %x", data_value);
			return -EINVAL;
		}

		//pr_info("%d bit device", device->resolution * 8);
		return 0;
	}

	else if (command == SOUND_PCM_WRITE_CHANNELS)
	{
		//pr_info("%d channels", data_value);
		device->channels = data_value;
		return 0;
	}

	else if ((command == SNDCTL_DSP_SPEED) // c0045002
		 || (command == SOUND_PCM_WRITE_RATE))
	{
		//pr_info("Bit rate %d", data_value);
		device->sample_rate = data_value;
		return 0;
	}

	// else if (command == 0x5401)
	//	return 0;

	else if (command == SOUND_PCM_READ_RATE)
	{
		return (data = 44100);
	}

	else if (command == SOUND_PCM_READ_CHANNELS)
	{
		//pr_info("%d channels", data_value);
		if (data > device->device_read_channels)
			return -EINVAL;
		else
			return 0;
	}

	else if (command == SOUND_PCM_READ_BITS)
	{
		return (data = 16);
	}

	else if (command == SNDCTL_DSP_GETFMTS)
	{
		/* This is a mask needed by the shell play command */
		int format = AFMT_S16_LE; 
		copy_status = copy_to_user((void*) data, &format, 4);
		return 0;
	}

	else if (command == SNDCTL_DSP_RESET)
	{
		return 0; /* don't do anything, but acknowledge so sox will work */
	}

	else if (command == SNDCTL_DSP_SYNC)
	{
		return 0; /* don't do anything, but acknowledge so sox will work */
	}

	else if (command == SNDCTL_DSP_STEREO) // c0045003
	{
		if (data_value)
		{
			//pr_info("Stereo");
			device->channels = 2;
		}
		else
		{
			//pr_info("Mono");
			device->channels = 1;
		}
		return 0; 
	}

	else if (command == SNDCTL_DSP_SETFRAGMENT) // c004500a
	{
		return 0; /* don't do anything, but acknowledge so standard audio applications will work */
	}

	else if (command == SNDCTL_DSP_GETBLKSIZE)
	{
		int block_size = device->write_packet_size * MMUSBAUDIO_AUDIO_URBS;
		copy_status = copy_to_user((void*) data, &block_size, 4);
		return 0;
	}

	else if (command == SNDCTL_DSP_GETODELAY)
	{
		// Used by RealPlayer - just ignore and still works
		// int *delay = (int*)data;
		// pr_info("GETODELAY %d", *delay);
		// *delay = 1000 * MMUSBAUDIO_AUDIO_WRITE_URB_SHORT / (44100 * 4);
		return 0;
	}

	else if (command == SNDCTL_DSP_GETOPTR)
	{
		// Needed for Flash Player streaming video
		count_info info;
		memset(&info, 0, sizeof(info));
		info.bytes = device->current_write_total;
		info.blocks = device->current_write_total / device->channels * device->resolution;
		info.ptr = info.bytes;

		//info.bytes = device->current_write_total;
                //info.blocks = info.bytes;
                //info.ptr = 0; 		

		//pr_info("SNDCTL_DSP_GETOPTR %d bytes, %d blocks", info.bytes, info.blocks);
		copy_status = copy_to_user((void*) data, &info, sizeof(info));
		return 0;
	}

	else if (command == SNDCTL_DSP_GETOSPACE)
	{
		// Needed for Flash Player 7 streaming video
		int scan = 0;
		int free_bytes = 0;
		int total_bytes = 0;
		//int device_frame_size = device->device_write_channels * device->device_resolution;
		audio_buf_info info;
		memset(&info, 0, sizeof(info));

		for (scan = 0; scan < MMUSBAUDIO_AUDIO_URBS; ++scan)
		{
			total_bytes += device->write_urb[scan]->transfer_buffer_length;
			if (device && device->write_urb[scan] && device->write_urb[scan]->status != -EINPROGRESS)
				free_bytes += device->write_urb[scan]->transfer_buffer_length;
		}

		// 4/20/2007 - fragsize must be greater than one or new mplayer crashes with divzero error.
		// info.fragstotal and info.bytes are in bytes, not fragsize or Flash 7 will freeze.

		info.fragsize = device->channels * device->resolution;
		info.bytes = free_bytes * device->channels * device->resolution / 
			device->device_write_channels / device->device_resolution;
		info.fragments = info.bytes / info.fragsize;
		info.fragstotal = total_bytes * device->channels * device->resolution / 
			device->device_write_channels / device->device_resolution;

		//pr_info("SNDCTL_DSP_GETOSPACE: %d fragments, %d bytes/fragment, %d total (device %d bytes, %d total)", 
		//	info.fragments, info.fragsize, info.fragstotal, free_bytes, total_bytes);

		copy_status = copy_to_user((void*) data, &info, sizeof(info));

		return 0;
	}

	else if (command == SNDCTL_MIDI_INFO)
	{
		struct midi_info midi_info;
		memset(&midi_info, 0, sizeof(struct midi_info));
		strcpy(midi_info.name, device->device_name);
		copy_status = copy_to_user((void*)data, &midi_info, sizeof(struct midi_info));
		return 0;
	}

	else if (command == SNDCTL_DSP_SETDUPLEX) /* 0x5016 */
		return -1; // Full duplex not supported

	else
	{
		pr_info("Undefined audio IOCTL %x", command);
		return -1;
	}

}

unsigned int mmusbaudio_audio_poll(struct file *file, struct poll_table_struct *poll_table)
{
	int mask = 0;
	int scan = 0;
	int free_read_bytes = 0;
	int free_write_bytes = 0;
	int total_read_bytes = 0;
	int total_write_bytes = 0;
	struct mmusbaudio_audio_device *device = (struct mmusbaudio_audio_device*) file->private_data;
	int device_number = device - mmusbaudio_audio_devices;

	if (!device || (device_number < 0) || (device_number >= MMUSBAUDIO_MAX_AUDIO_DEVICES))
	{
		pr_err("Invalid audio device 0x%x in file structure given to poll()", device_number);
		return -EINVAL;
	}

	if (!device->usb_device)
	{
		pr_err("No USB device pointer for audio poll()");
		return -ENODEV;
	}

	for (scan = 0; scan < MMUSBAUDIO_AUDIO_URBS; ++scan)
	{
		total_read_bytes += device->read_urb[scan]->transfer_buffer_length;
		total_write_bytes += device->write_urb[scan]->transfer_buffer_length;

		if (device && device->read_urb[scan] && (device->read_urb[scan]->status != -EINPROGRESS))
			free_read_bytes += device->read_urb[scan]->transfer_buffer_length;

		if (device && device->write_urb[scan] && (device->write_urb[scan]->status != -EINPROGRESS))
			free_write_bytes += device->write_urb[scan]->transfer_buffer_length;
	}

	//poll_wait(file, &device->read_wait_queue, poll_table);
	//poll_wait(file, &device->write_wait_queue, poll_table);

	if (free_write_bytes < total_write_bytes)
		mask |= (POLLOUT | POLLWRNORM);

	//if (free_read_bytes < total_read_bytes)
		mask |= (POLLIN | POLLRDNORM);

	// pr_info("mmusbaudio_audio_poll() = 0x%x", mask);

	return mask;
}

#if 0
#ifndef __user
#define __user
#endif

struct file_operations mmusbaudio_audio_operations =
{
	NULL, /* struct module *owner; */
	NULL, /* loff_t (*llseek) (struct file *, loff_t, int); */
	mmusbaudio_audio_read, /* ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); */
	NULL, /* aio_read */
	mmusbaudio_audio_write, /* ssize_t (*write) (struct file *, char __user *, size_t, loff_t *); */
	NULL, /* aio_write */
	NULL, /* int (*readdir) (struct file *, void *, filldir_t); */
	NULL, /* unsigned int (*poll) (struct file *, struct poll_table_struct *); */
	mmusbaudio_audio_ioctl, /* int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); */
/* See http://lwn.net/Articles/2.6-kernel-api/ */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11)
	NULL, /* long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); */
	NULL, /* long (*compat_ioctl) (struct file *, unsigned int, unsigned long); */
#endif
	NULL, /* int (*mmap) (struct file *, struct vm_area_struct *); */
	mmusbaudio_audio_open, /* int (*open) (struct inode *, struct file *); */
	NULL, /* int (*flush) (struct file *); */
	mmusbaudio_audio_release, /* int (*release) (struct inode *, struct file *); */
	NULL, /* int (*fsync) (struct file *, struct dentry *); */
	NULL, /* aio_fsync */
	NULL, /* int (*fasync) (int, struct file *, int); */
	NULL, /* int (*lock) (struct file *, int, struct file_lock *); */
	NULL, /* ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *); */
	NULL, /* ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *); */
	NULL, /* ssize_t (*writepage) (struct file *, struct page *, int, size_t, loff_t *, int); */
	NULL, /* sendpage */
	NULL, /* get_unmapped_area */
	NULL, /* fcntl */
};
#endif
// https://bbs.archlinux.org/viewtopic.php?id=109662
// http://unix.stackexchange.com/questions/4711/what-is-the-difference-between-ioctl-unlocked-ioctl-and-compat-ioctl

struct file_operations mmusbaudio_audio_operations =
{
	.read = mmusbaudio_audio_read, /* ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); */
	.write = mmusbaudio_audio_write, /* ssize_t (*write) (struct file *, char __user *, size_t, loff_t *); */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
	.ioctl = mmusbaudio_audio_ioctl, /* int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); */
#else
	.unlocked_ioctl = mmusbaudio_audio_ioctl,
#endif
	.open = mmusbaudio_audio_open, /* int (*open) (struct inode *, struct file *); */
	.release = mmusbaudio_audio_release, /* int (*release) (struct inode *, struct file *); */
	.poll = mmusbaudio_audio_poll, /* unsigned int (*poll) (struct file *, struct poll_table_struct *); */
};

// MMUSBAUDIO doesn't actually implement mixers with any device, but
// a mixer must be allocated to prevent ALSA from writing a noxious
// stream of error messages to the log file when using XMMS

static ssize_t mmusbaudio_mixer_read(struct file *file, char __user *data, size_t size, loff_t *offset)
{
	//pr_info("mixer read()");
	return size;
}

static ssize_t mmusbaudio_mixer_write(struct file *file, const char *data, size_t size, loff_t *offset)
{
	//pr_info("mixer write()");
	return size;
}

#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
static int mmusbaudio_mixer_ioctl(struct inode *node, struct file *file, unsigned int number, unsigned long data)
#else
static long mmusbaudio_mixer_ioctl(struct file *file, unsigned int number, unsigned long data)
#endif
{
	unsigned long copy_status; // needed to prevent compile warnings with copy_to_user() - value ignored

	//pr_info("mixer ioctl(0x%x)", number);

	if ((number == 0x80044dfe) && data) // SOUND_MIXER_READ_DEVMASK
	{
		int mask = 0;
		copy_status = copy_to_user(((int*) data), &mask, sizeof(int));
		return 0;
	}

	else if ((number == 0x80044d04) && data) // SOUND_MIXER_READ_VOLUME
	{
		int volume = 0;
		copy_status = copy_to_user(((int*) data), &volume, sizeof(int));
		return 0;
	}

	//else
	//	pr_info("ioctl(%x, %lx)", number, data);

	return -1;
}

#if 0
struct file_operations mmusbaudio_mixer_operations =
{
	NULL, /* struct module *owner; */
	NULL, /* loff_t (*llseek) (struct file *, loff_t, int); */
	mmusbaudio_mixer_read, /* ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); */
	mmusbaudio_mixer_write, /* ssize_t (*write) (struct file *, char __user *, size_t, loff_t *); */
	NULL, /* aio_read */
	NULL, /* aio_write */
	NULL, /* int (*readdir) (struct file *, void *, filldir_t); */
	NULL, /* unsigned int (*poll) (struct file *, struct poll_table_struct *); */
	mmusbaudio_mixer_ioctl, /* int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); */
/* See http://lwn.net/Articles/2.6-kernel-api/ */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11)
	NULL, /* long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); */
	NULL, /* long (*compat_ioctl) (struct file *, unsigned int, unsigned long); */
#endif
	NULL, /* int (*mmap) (struct file *, struct vm_area_struct *); */
	NULL, /* int (*open) (struct inode *, struct file *); */
	NULL, /* int (*flush) (struct file *); */
	NULL, /* int (*release) (struct inode *, struct file *); */
	NULL, /* int (*fsync) (struct file *, struct dentry *); */
	NULL, /* aio_fsync */
	NULL, /* int (*fasync) (int, struct file *, int); */
	NULL, /* int (*lock) (struct file *, int, struct file_lock *); */
	NULL, /* ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *); */
	NULL, /* ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *); */
	NULL, /* ssize_t (*writepage) (struct file *, struct page *, int, size_t, loff_t *, int); */
	NULL, /* sendpage */
	NULL, /* get_unmapped_area */
	NULL, /* fcntl */
};
#endif

struct file_operations mmusbaudio_mixer_operations =
{
	.read = mmusbaudio_mixer_read, /* ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); */
	.write = mmusbaudio_mixer_write, /* ssize_t (*write) (struct file *, char __user *, size_t, loff_t *); */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
	.ioctl = mmusbaudio_mixer_ioctl, /* int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); */
#else
	.unlocked_ioctl = mmusbaudio_mixer_ioctl,
#endif
};



/************************* MIDI Event Functions *****************************/

#define MMUSBAUDIO_MIDI_NOTE_OFF 0x80
#define MMUSBAUDIO_MIDI_NOTE_ON  0x90
#define MMUSBAUDIO_MIDI_POLYPHONIC_KEY_PRESSURE 0xA0
#define MMUSBAUDIO_MIDI_CONTROL_CHANGE 0xB0
#define MMUSBAUDIO_MIDI_PROGRAM_CHANGE 0xC0
#define MMUSBAUDIO_MIDI_AFTERTOUCH 0xD0
#define MMUSBAUDIO_MIDI_PITCH_BEND 0xE0
#define MMUSBAUDIO_MIDI_SYSTEM_EXCLUSIVE 0xF0
#define MMUSBAUDIO_MIDI_TIME_CODE_QUARTER_FRAME 0xF1
#define MMUSBAUDIO_MIDI_SONG_POSITION 0xF2
#define MMUSBAUDIO_MIDI_SONG_SELECT 0xF3
#define MMUSBAUDIO_MIDI_TUNE_REQUEST 0xF6
#define MMUSBAUDIO_MIDI_END_OF_EXCLUSIVE 0xF7
#define MMUSBAUDIO_MIDI_TIMING_CLOCK 0xF8
#define MMUSBAUDIO_MIDI_START 0xFA
#define MMUSBAUDIO_MIDI_CONTINUE 0xFB
#define MMUSBAUDIO_MIDI_STOP 0xFC
#define MMUSBAUDIO_MIDI_ACTIVE_SENSING 0xFE
#define MMUSBAUDIO_MIDI_META_EVENT 0xFF

static int mmusbaudio_midi_message_length(unsigned char *message, int message_length)
{
	/* if the message does not begin with a status byte, assume it's incomplete and needs to be sent immediately */
	if (!(message[0] & 0x80))
		return message_length; 

	else switch (message[0] & 0xF0)
	{
		case MMUSBAUDIO_MIDI_NOTE_OFF:
		case MMUSBAUDIO_MIDI_NOTE_ON:
		case MMUSBAUDIO_MIDI_POLYPHONIC_KEY_PRESSURE:
		case MMUSBAUDIO_MIDI_CONTROL_CHANGE:
		case MMUSBAUDIO_MIDI_PITCH_BEND:
			return 3;

		case MMUSBAUDIO_MIDI_PROGRAM_CHANGE:
		case MMUSBAUDIO_MIDI_AFTERTOUCH:
			return 2;

		default:
			switch (message[0])
			{
				case MMUSBAUDIO_MIDI_SONG_POSITION:
					return 3;

				case MMUSBAUDIO_MIDI_TIME_CODE_QUARTER_FRAME:
				case MMUSBAUDIO_MIDI_SONG_SELECT:
					return 2;

				case MMUSBAUDIO_MIDI_TUNE_REQUEST:
				case MMUSBAUDIO_MIDI_END_OF_EXCLUSIVE:
				case MMUSBAUDIO_MIDI_TIMING_CLOCK:
				case MMUSBAUDIO_MIDI_START:
				case MMUSBAUDIO_MIDI_CONTINUE:
				case MMUSBAUDIO_MIDI_STOP:
				case MMUSBAUDIO_MIDI_ACTIVE_SENSING:
					return 1;

				case MMUSBAUDIO_MIDI_META_EVENT:
					if (message_length <= 2)
						return 3; /* a meta event is at least three bytes long */
					else
						return message[2] + 3;

				case MMUSBAUDIO_MIDI_SYSTEM_EXCLUSIVE:
					if (message_length <= 0)
						return 2; /* Sys Ex is at least two bytes long */
					else if (message[message_length - 1] == MMUSBAUDIO_MIDI_END_OF_EXCLUSIVE)
						return message_length;
					else
						return message_length + 1;
			}
	}

	return message_length;
}


/************************* USB Debug Functions *****************************

static void mmusbaudio_urb_dump(char *message, struct urb *urb)
{
	unsigned char *char_data = (unsigned char*) urb->transfer_buffer;
	int scan = 0;

	pr_info("%s", message);
	if (!urb)
	{
		pr_info("NULL URB");
		return;
	}

	pr_info(" pipe %x, transfer_flags %x", urb->pipe, urb->transfer_flags);
	printk(" status %d, length %d, actual %d", urb->status, urb->transfer_buffer_length, urb->actual_length);

	for (scan = 0; scan < urb->transfer_buffer_length; ++scan)
	{
		printk (" %02x", (int) char_data[scan]);
		if ((scan & 0xf) == 0xf)
			printk("");
  	}

	printk("");
}


static void mmusbaudio_device_dump(char *message, struct usb_device *device)
{
	pr_info("%s", message);
	pr_info("  Device #%d, slow %d", device->devnum, device->slow);
	pr_info("  Alternate #%d, endpoints %d",
		device->actconfig->interface[2].act_altsetting,
		device->actconfig->interface[2].altsetting[device->actconfig->interface->act_altsetting].bNumEndpoints);
}

int mmusbaudio_scan_descriptors(struct usb_device *device)
{
	int scan = 0;
	int dump = 0;
	int count = 0;
	int length = 0;
	unsigned char buffer[256];
	char string[256];

	length = usb_control_msg(device, usb_rcvctrlpipe(device, 0),
	USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, (USB_DT_CONFIG << 8), 0, buffer, 256, 100);

	pr_info("Descriptors = %d bytes", length);

	for (count = 0; (count < 50) && (scan < length); ++count)
	{
		pr_info("  %d) Descriptor: length %d, type %02x", scan, (int) buffer[scan], (int) buffer[scan + 1]);

		for (dump = 0, string[0] = 0; (dump < buffer[scan]) && (dump < 16); ++dump)
			sprintf(&string[strlen(string)], "%02x ", buffer[scan + dump]);

		pr_info("  %s", string);

		if (buffer[scan] <= 0)
			scan = length;
		else
			scan += buffer[scan];
	}

	return 0;
}

**********************************************************************************/


/************************ Roland MIDI Protocol ***********************************
*
*	Although the endpoints do not indicate it, the supported Roland MIDI
*	devices use the USB MIDI class protocol.
*
*	MIDI data is sent and received in 4-byte chunks with a preceeding "burst"
*	byte that indicates the destination/source port for following 3 bytes of
*	data. All MIDI messages under status 0xf0 (SYSEX) are two or three bytes long
*	and fit in a single 4-byte chunk. Messages over status byte 0xf0 are split into
*	multiple consecutive chunks with the appropriate burst bytes specified below.
*
*	For MIDI sends, the upper 4 bits of the "burst" byte are the source/destination
*	port (described below). The lower 4 bits are either the upper 4 bits of the MIDI
*	message status byte, 4 for the beginning or middle of a SYSEX message, 6 if there
*	are two bytes in the last SYSEX chunk, and 5 if there is one byte in the last
*	chunk of a SYSEX message. For MIDI receives the protocol is basically
*	the same with the upper four bits representing the input port.
*
*		Example: 19 90 30 70 is a note on message sent to port 2.
*
*	For UA-100 sends, the "burst byte" is 0x00 for MIDI OUT 1, 0x10 for MIDI OUT 2
*	and 0x20 for the UA-100 control port which receives SYSEX messages to change
*	the internal mixer and effect settings. MIDI receives are basically the same:
*	0x00 for MIDI IN 1 and 0x00 for MIDI IN 2.
*
*	For the UM-1, there is only one input and output port, so the upper 4 bits of
*	the "burst byte" are always 0x00.
*
*	For the SC-8850, the "burst byte" indicates which of the four "parts" (i.e.
*	16-channel banks) should receive the MIDI output: 0x00 for Part A, 0x10
*	for Part B, 0x20 for Part C and 0x30 for Part D.  However, unlike the other
*	devices, for MIDI input: 0x40 for MIDI IN 1 and 0x50 for MIDI IN 2
*
*	Chunks smaller than 4 bytes (such as two-byte program change messages) are
*	padded with trailing NULLs to a 4-byte length.
*
***************************************************************************************/

struct mmusbaudio_midi_device
{
	int is_open;
	int submitted_read_urbs;
	int read_endpoint_address;
	int write_endpoint_address;
	int urb_buffer_length;
	int oss_minor_number;
	struct usb_device *usb_device;
	unsigned char burst_byte;
	struct urb *read_urb;
	struct urb *write_urb;
	wait_queue_head_t read_wait_queue;
	wait_queue_head_t write_wait_queue;
	char *device_name;
} mmusbaudio_midi_devices[MMUSBAUDIO_MAX_MIDI_DEVICES];

static int mmusbaudio_release_midi_device(struct mmusbaudio_midi_device *device)
{
	pr_info("Disconnecting MIDI device 0x%02x", device->oss_minor_number >> 4);

	device->usb_device = NULL;
	device->is_open = 0;
	unregister_sound_midi(device->oss_minor_number);

	if (device->write_urb)
	{
		usb_unlink_urb(device->write_urb);
		kfree(device->write_urb->transfer_buffer);
		usb_free_urb(device->write_urb);
	}

	if (device->read_urb)
	{
		usb_unlink_urb(device->read_urb);
		kfree(device->read_urb->transfer_buffer);
		usb_free_urb(device->read_urb);
	}

	return 0;
}

static int mmusbaudio_midi_file_open (struct inode *inode, struct file *file)
{
	// int scan = 0;
	int status = 0;
	int device_number = MINOR(inode->i_rdev) >> 4;
	struct mmusbaudio_midi_device *device = &mmusbaudio_midi_devices[device_number];

	/*******
	for (scan = 0; (scan < MMUSBAUDIO_MAX_MIDI_DEVICES) && (!device); ++scan)
		if (mmusbaudio_midi_devices[scan].oss_minor_number == MINOR(inode->i_rdev))
			device = &mmusbaudio_midi_devices[scan];

	if (!device)
	{
		pr_err("Invalid MIDI device minor number %d", MINOR(inode->i_rdev));
		return -EINVAL;
	}
	********/

	if (!device->usb_device)
	{
		pr_err("Disconnected MIDI device minor number %d", MINOR(inode->i_rdev));
		return -EHOSTDOWN; /* device unplugged? */
	}

	if (device->is_open)
		return -EBUSY;

	//pr_info("%s opened /dev/midi%02d", device->device_name, MINOR(inode->i_rdev) >> 4);

	init_waitqueue_head(&device->read_wait_queue);
	init_waitqueue_head(&device->write_wait_queue);

	file->private_data = (void*) device;
	device->submitted_read_urbs = 0;
	device->read_urb->status = 0;
	device->write_urb->status = 0;
	++device->is_open;

	try_module_get(THIS_MODULE);

	return status;
}

static int mmusbaudio_midi_file_release (struct inode *inode, struct file *file)
{
	struct mmusbaudio_midi_device *device = (struct mmusbaudio_midi_device*) file->private_data;
	int device_number = device - mmusbaudio_midi_devices;

	if ((device_number < 0) || (device_number >= MMUSBAUDIO_MAX_MIDI_DEVICES))
	{
		pr_err("Invalid MIDI device 0x%x in file structure given to release()", device_number);
		return -EINVAL;
	}

	if (!device->usb_device)
	{
		pr_err("No USB device pointer for MIDI release()");
		return -ENODEV;
	}

	//pr_info("%s closed /dev/midi%02d", device->device_name, device->oss_minor_number >> 4);

	usb_unlink_urb(device->read_urb);
	usb_unlink_urb(device->write_urb);
	--device->is_open;

	module_put(THIS_MODULE);

	return 0;
}

static void mmusbaudio_midi_write_callback(struct urb *urb, struct pt_regs *registers)
{
	struct mmusbaudio_midi_device *device = (struct mmusbaudio_midi_device *) urb->context;

	if (waitqueue_active(&device->write_wait_queue))
		wake_up_interruptible(&device->write_wait_queue);
}

static ssize_t mmusbaudio_midi_file_write (struct file *file, const char *user_data, size_t length, loff_t *offset)
{
	int status = 0;
	int copy_status = 0;
	int message_length = 0;
	int block_start = 0;
	unsigned char data[256];
	struct mmusbaudio_midi_device *device = (struct mmusbaudio_midi_device*) file->private_data;
	int device_number = device - mmusbaudio_midi_devices;

	if (length > 256)
		length = 256;

	// Need to capture the return value for gcc v4. 
	// Returned value is not length
	copy_status = copy_from_user(data, user_data, length);
	message_length = mmusbaudio_midi_message_length(data, length);

	//pr_info("midi write() 0x%x 0x%x 0x%x 0x%x (length %d)", 
	//	data[0], data[1], data[2], data[3], message_length);


	if ((device_number < 0) || (device_number >= MMUSBAUDIO_MAX_MIDI_DEVICES))
	{
		pr_err("Invalid device 0x%x in file structure given to MIDI write()", device_number);
		return -EINVAL;
	}

	if (!device->usb_device)
	{
		pr_err("No USB device pointer for MIDI write()");
		return -ENODEV;
	}

	if ((!device) || (!device->usb_device) || (!device->is_open) || 
	    (!device->write_urb) || (!device->write_urb->transfer_buffer))
		return -EHOSTDOWN; /* device unplugged? */

	/* Ignore MIDI messages without status byte or status */
	/* greater than SYSEX or Roland part select F4 */
	else if ((data[0] < 0x80) || (data[0] > 0xf4))
		return -EINVAL; 

	else if (length < message_length)
		return -EINVAL; /* Ignore incomplete MIDI messages */

	//pr_info("  before midi submit %02x to device %02x", (int) data, device_number);

	/* The UA-100 limits bulk transfers to 32 bytes per URB (i.e. 24 bytes of data + the burst bytes) */
	/* Therefore, long SYSEX messages (such as the 40 byte effect parameter messages) must be broken */
	/* into multiple successive URBs */
	for (block_start = 0; block_start < length; block_start += (device->urb_buffer_length * 3 / 4))
	{
		int chunk_start = 0;
		int block_end = block_start + (device->urb_buffer_length * 3 / 4);

		if (block_end > length)
			block_end = length;

		if ((status = wait_event_interruptible(device->write_wait_queue, 
				(device->write_urb->status != -EINPROGRESS))) < 0)
		{
			pr_err("Failure %d waiting for MIDI URB to complete", status);
			return status;
		}

		device->write_urb->transfer_buffer_length = 0;
		for (chunk_start = block_start; chunk_start < block_end; chunk_start += 3)
		{
			int chunk_length = 3;
			if (chunk_length > (length - chunk_start))
				chunk_length = length - chunk_start;

			if (data[0] != 0xf0)
				((unsigned char*)device->write_urb->transfer_buffer) 
					[device->write_urb->transfer_buffer_length] = 
					((data[chunk_start] >> 4) & 0x0f) | device->burst_byte;
			else if ((chunk_start + 3) < length)
				((unsigned char*)device->write_urb->transfer_buffer) 
					[device->write_urb->transfer_buffer_length] = 
					0x04 | device->burst_byte;
			else if ((length - chunk_start) == 1)
				((unsigned char*)device->write_urb->transfer_buffer) 
					[device->write_urb->transfer_buffer_length] = 
					0x05 | device->burst_byte;
			else
				((unsigned char*)device->write_urb->transfer_buffer) 
					[device->write_urb->transfer_buffer_length] = 
					0x06 | device->burst_byte;

			/* chunk length must be multiple of 4? */
			memset(&((unsigned char*)device->write_urb->transfer_buffer)
					[device->write_urb->transfer_buffer_length + 1], 0, 3); 
			memcpy(&((unsigned char*)device->write_urb->transfer_buffer)
					[device->write_urb->transfer_buffer_length + 1], 
					&data[chunk_start], chunk_length);
			device->write_urb->transfer_buffer_length += 4;
		}

		//spin_lock_init(&device->write_urb->lock);
		device->write_urb->dev = device->usb_device;
		device->write_urb->pipe = usb_sndbulkpipe(device->usb_device, device->write_endpoint_address);
		device->write_urb->complete = (usb_complete_t)mmusbaudio_midi_write_callback;
		device->write_urb->context = device;

		/*mmusbaudio_urb_dump("MIDI write", device->write_urb);*/

		if ((status = usb_submit_urb(device->write_urb, GFP_ATOMIC)) != 0)
		{
			pr_err("failure %d submitting write_urb (%d bytes)", status, device->write_urb->transfer_buffer_length);
			return status;
		}
		else
			status = length;

		//pr_info("  after midi submit %02x to device %02x", (int) data, device_number);
	}

	return status;
}

static void mmusbaudio_midi_read_callback(struct urb *urb, struct pt_regs *registers)
{
	struct mmusbaudio_midi_device *device = (struct mmusbaudio_midi_device *) urb->context;

	if (waitqueue_active(&device->read_wait_queue))
		wake_up_interruptible(&device->read_wait_queue);
}

static ssize_t mmusbaudio_midi_file_read (struct file *file, char *data, size_t length, loff_t *offset)
{
	int scan = 0;
	int status = 0;
	int actual_length = 0;
	// needed to prevent compile warnings for copy_to_user() - value ignored
	unsigned long copy_status; 
	struct mmusbaudio_midi_device *device = (struct mmusbaudio_midi_device*) file->private_data;
	int device_number = device - mmusbaudio_midi_devices;

	if ((device_number < 0) || (device_number >= MMUSBAUDIO_MAX_MIDI_DEVICES))
	{
		pr_err("Invalid device 0x%x in file structure given to MIDI read()", device_number);
		return -EINVAL;
	}

	if (!device->usb_device)
	{
		pr_err("No USB device pointer for MIDI read()");
		return -ENODEV;
	}

	if ((!device) || (!device->usb_device) || (!device->is_open) || 
	    (!device->read_urb) || (!device->read_urb->transfer_buffer))
		return -EHOSTDOWN; /* device unplugged? */

	if (device->submitted_read_urbs <= 0)
	{
		//spin_lock_init(&device->read_urb->lock);
		device->read_urb->dev = device->usb_device;
		device->read_urb->pipe = usb_rcvbulkpipe(device->usb_device, device->read_endpoint_address);
		((unsigned char*)device->read_urb->transfer_buffer)[0] = 0;
		device->read_urb->complete = (usb_complete_t)mmusbaudio_midi_read_callback;
		device->read_urb->context = device;
		device->submitted_read_urbs = 1;

		if ((status = usb_submit_urb(device->read_urb, GFP_ATOMIC)) < 0)
		{
			pr_err("failure %d submitting read_urb to endpoint %02x", status, device->read_endpoint_address);
			return status;
		}

		// interruptible_sleep_on(&device->read_wait_queue);
		// interruptible_sleep_on_timeout(&device->read_wait_queue, 1);
		// mmusbaudio_urb_dump("Read URB -----------", device->read_urb);
	}


	if ((!device) || (!device->usb_device) || (!device->is_open))
		return -EHOSTDOWN; /* device unplugged? */

	// MMUSBAUDIO only implements nonblocking MIDI read
	// if the device does not return the URB by the timeout given above, return
	else if (device->read_urb->status == -EINPROGRESS)
		return -EAGAIN;

	// UA-100 regularly returns empty buffers
	else if (((unsigned char*)device->read_urb->transfer_buffer)[0] == 0)
		actual_length = -EAGAIN;

	/*
	else if ((((unsigned char*)device->read_urb->transfer_buffer)[0] & 0xf0) != device->burst_byte)
		length = 0; data from a different port - doesn't work with SC-8850
	*/

	else for (scan = 0; (scan < device->read_urb->actual_length) && (((scan / 4) * 3) < length) 
			&& (((unsigned char*)device->read_urb->transfer_buffer) [scan] > 0); scan += 4)
		switch (((unsigned char*)device->read_urb->transfer_buffer) [scan] & 0x0f)
		{
			case 0x05: /* 1 byte end of sysex */
			case 0x0F: /* single byte ? */
				copy_status = copy_to_user(data + ((scan / 4) * 3), 
					((unsigned char*)device->read_urb->transfer_buffer) + scan + 1, 1);
				actual_length += 1;
				break;

			case 0x02: /* 2 byte system common message */
			case 0x06: /* 2 byte end of sysex */
			case 0x0C: /* Program Change*/
			case 0x0D: /* Aftertouch */
				copy_status = copy_to_user(data + ((scan / 4) * 3), 
					((unsigned char*)device->read_urb->transfer_buffer) + scan + 1, 2);
				actual_length += 2;
				break;

			default: /* everything else is 3-byte packet */
				copy_status = copy_to_user(data + ((scan / 4) * 3), 
					((unsigned char*)device->read_urb->transfer_buffer) + scan + 1, 3);
				actual_length += 3;
				break;
		}

	device->submitted_read_urbs = 0;

	return actual_length;
}

#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
static int mmusbaudio_midi_file_ioctl (struct inode *inode, struct file *file, unsigned int command, unsigned long data)
#else
static long mmusbaudio_midi_file_ioctl (struct file *file, unsigned int command, unsigned long data)
#endif
{
	// needed to prevent compile warnings on copy_to_user() - value ignored
	unsigned long copy_status; 
	struct midi_info midi_info;
	struct mmusbaudio_midi_device *device = (struct mmusbaudio_midi_device*) file->private_data;
	int device_number = device - mmusbaudio_midi_devices;

	if ((device_number < 0) || (device_number >= MMUSBAUDIO_MAX_MIDI_DEVICES))
	{
		pr_err("Invalid device 0x%x in file structure given to MIDI ioctl()", device_number);
		return -EINVAL;
	}

	if (!device->usb_device)
	{
		pr_err("No USB device pointer for MIDI ioctl()");
		return -ENODEV;
	}

	if (command == SNDCTL_MIDI_INFO)
	{
		memset(&midi_info, 0, sizeof(struct midi_info));
		strcpy(midi_info.name, device->device_name);
		copy_status = copy_to_user(((struct midi_info*) data), &midi_info, sizeof(struct midi_info));
		return 0;
	}

	else
	{
		pr_info("Undefined MIDI IOCTL %x", command);
		return -1;
	}
}

struct file_operations mmusbaudio_midi_operations =
{
	.read = mmusbaudio_midi_file_read, /* ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); */
	.write = mmusbaudio_midi_file_write, /* ssize_t (*write) (struct file *, char __user *, size_t, loff_t *); */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
	.ioctl = mmusbaudio_midi_file_ioctl, /* int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); */
#else
	.unlocked_ioctl = mmusbaudio_midi_file_ioctl,
#endif
	.open = mmusbaudio_midi_file_open, /* int (*open) (struct inode *, struct file *); */
	.release = mmusbaudio_midi_file_release, /* int (*release) (struct inode *, struct file *); */
};

static struct mmusbaudio_midi_device *mmusbaudio_assign_midi_device(struct usb_device *usb_device, char *device_name, 
		int in_endpoint, int out_endpoint, unsigned char burst_byte, int buffer_length)
{
	int status = 0;
	int device_number = 0;
	struct mmusbaudio_midi_device *device = NULL;

	if ((status = register_sound_midi(&mmusbaudio_midi_operations, -1)) < 0)
	{
		pr_err("Failure %d registering MIDI device with OSS", status);
		return NULL;
	}

	device_number = status >> 4;
	if (device_number >= MMUSBAUDIO_MAX_MIDI_DEVICES)
	{
		pr_err("MIDI device number %d out of range", device_number);
		unregister_sound_midi(status);
		return NULL;
	}

	device = &mmusbaudio_midi_devices[device_number];
	if (device->usb_device)
	{
		pr_err("MIDI device number %d in use", device_number);
		unregister_sound_midi(status);
		return NULL;
	}
	
	pr_info("Registered %s as /dev/midi%02d", device_name, device_number);

	device->oss_minor_number = status;
	device->device_name = device_name;
	device->usb_device = usb_device;
	device->read_endpoint_address = in_endpoint;
	device->write_endpoint_address = out_endpoint;
	device->burst_byte = burst_byte;
	device->urb_buffer_length = buffer_length;
	device->is_open = 0;
	device->submitted_read_urbs = 0;

	/* The buffer sent with a URB must be allocated with kmalloc */
	/* or you get an interrupt error from usb-uhci - page alignment perhaps? */
	/* For reads, the buffer must be the EXACT length specified by the */
	/* device descriptor */

	device->read_urb = usb_alloc_urb(0, GFP_KERNEL);
	device->read_urb->transfer_buffer = (unsigned char *)kmalloc(buffer_length, GFP_KERNEL);
	device->read_urb->context = device;
	device->read_urb->transfer_buffer_length = buffer_length;

	device->write_urb = usb_alloc_urb(0, GFP_KERNEL);
	device->write_urb->transfer_buffer = (unsigned char *)kmalloc(buffer_length, GFP_KERNEL);
	device->write_urb->context = device;
	device->write_urb->transfer_buffer_length = buffer_length;

	// For some reason, sporadically returns -110 ETIMEDOUT
	if ((status = usb_set_interface(usb_device, 2, 0)) < 0)
	{
		pr_err("Failure %d setting MIDI interface", status);
		return NULL;
	}

	return device;
}



/************************* USB Driver Registration *****************************/

static int mmusbaudio_probe(struct usb_interface *probe_interface, const struct usb_device_id *id)
{
	struct usb_device *device = interface_to_usbdev(probe_interface);
	unsigned char interface = probe_interface->cur_altsetting->desc.bInterfaceNumber;
	unsigned char interface_class = probe_interface->cur_altsetting->desc.bInterfaceClass;
	//void *driver_data = NULL;

	//pr_info("mmusbaudio_probe(%d)", interface);

	if (interface_class == 0x01) // USB Standard Audio Class
		mmusbaudio_assign_audio_class_device(device, interface);

	else if ((device->descriptor.idVendor == 0x582) && (device->descriptor.idProduct == 0))
		mmusbaudio_assign_vendor_audio_device(device, interface, "UA-100 Audio");

	else if ((device->descriptor.idVendor == 0x582) && (device->descriptor.idProduct == 0x10))
		mmusbaudio_assign_vendor_audio_device(device, interface, "UA-5 (Advanced Mode)");

	else if ((device->descriptor.idVendor == 0x582) && (device->descriptor.idProduct == 0x35))
		mmusbaudio_assign_vendor_audio_device(device, interface, "M-1000 (Advanced Mode)");

	else if ((device->descriptor.idVendor == 0x582) && (device->descriptor.idProduct == 0x96))
		mmusbaudio_assign_vendor_audio_device(device, interface, "UA-1EX (Advanced Mode)");

	else if ((device->descriptor.idVendor == 0x582) && (device->descriptor.idProduct == 0xE6))
		mmusbaudio_assign_vendor_audio_device(device, interface, "UA-25 EX (Advanced Mode)");

	else if ((device->descriptor.idVendor == 0x582) && (device->descriptor.idProduct == 0xE9))
		mmusbaudio_assign_vendor_audio_device(device, interface, "UA-1G (Advanced Mode)");

	else if ((device->descriptor.idVendor == 0x1235) && (device->descriptor.idProduct == 0x10))
		mmusbaudio_assign_vendor_audio_device(device, interface, "Focusrite Sapphire 6");

	else if ((device->descriptor.idVendor == 0x582) && 
			(device->descriptor.idProduct == 0) && (interface == 2))
	{
		mmusbaudio_assign_midi_device(device, "UA-100 A", 0x82, 0x02, 0x00, 32);
		mmusbaudio_assign_midi_device(device, "UA-100 B", 0x82, 0x02, 0x10, 32);
		mmusbaudio_assign_midi_device(device, "UA-100 CTRL", 0x82, 0x02, 0x20, 32);
	}

	else if ((device->descriptor.idVendor == 0x582) && (device->descriptor.idProduct == 3) && (interface == 2))
	{
		mmusbaudio_assign_midi_device(device, "SC-8850 A", 0x81, 0x01, 0x00, 64);
		mmusbaudio_assign_midi_device(device, "SC-8850 B", 0x81, 0x01, 0x10, 64);
		mmusbaudio_assign_midi_device(device, "SC-8850 C", 0x81, 0x01, 0x20, 64);
		mmusbaudio_assign_midi_device(device, "SC-8850 D", 0x81, 0x01, 0x30, 64);
	}

	else if ((device->descriptor.idVendor == 0x582) && (device->descriptor.idProduct == 9) && (interface == 2))
		mmusbaudio_assign_midi_device(device, "UM-1", 0x81, 0x01, 0x00, 64);

	else
		pr_info("Unsupported interface %d (class 0x%x, vendor 0x%x, product 0x%x)", 
			interface, interface_class, device->descriptor.idVendor, 
			device->descriptor.idProduct);

	//if (driver_data)
	//	dev_set_drvdata(&probe_interface->dev, driver_data);

	return 0;
}

static void mmusbaudio_disconnect(struct usb_interface *interface)
{
	struct usb_device *device = interface_to_usbdev(interface);
	struct mmusbaudio_midi_device *midi_device = mmusbaudio_midi_devices;
	struct mmusbaudio_audio_device *audio_device = mmusbaudio_audio_devices;
	
	while (audio_device < &mmusbaudio_audio_devices[MMUSBAUDIO_MAX_AUDIO_DEVICES])
	{
		if (audio_device->usb_device == device)
			mmusbaudio_release_audio_device(audio_device);
		++audio_device;
	}

	while (midi_device < &mmusbaudio_midi_devices[MMUSBAUDIO_MAX_AUDIO_DEVICES])
	{
		if (midi_device->usb_device == device)
			mmusbaudio_release_midi_device(midi_device);
		++midi_device;
	}
}

/****************** Modprobe/Hotplugging Identification Info ******************/

static struct usb_device_id mmusbaudio_ids [] =
{
	{ USB_DEVICE_VER(0x0582, 0x0000, 0x0, 0xffff) }, /* UA-100  */
	{ USB_DEVICE_VER(0x0582, 0x0003, 0x0, 0xffff) }, /* SC-8850 */
	{ USB_DEVICE_VER(0x0582, 0x0009, 0x0, 0xffff) }, /* UM-1    */
	{ USB_DEVICE_VER(0x0582, 0x0010, 0x0, 0xffff) }, /* UA-5 (Advanced Mode) */
	{ USB_DEVICE_VER(0x0582, 0x0011, 0x0, 0xffff) }, /* UA-5 (Basic Mode)    */
	{ USB_DEVICE_VER(0x0582, 0x0035, 0x0, 0xffff) }, /* M-1000 (Advanced Mode) */
	{ USB_DEVICE_VER(0x0582, 0x0036, 0x0, 0xffff) }, /* M-1000 (Basic Mode)    */
	{ USB_DEVICE_VER(0x0582, 0x0096, 0x0, 0xffff) }, /* UA-1EX (Advanced Mode) */
	{ USB_DEVICE_VER(0x0582, 0x0097, 0x0, 0xffff) }, /* UA-1EX (Basic Mode) */
	{ USB_DEVICE_VER(0x0582, 0x00EA, 0x0, 0xffff) }, /* UA-1G (Basic Mode) */
	{ USB_DEVICE_VER(0x0582, 0x00E6, 0x0, 0xffff) }, /* UA-25EX (Advanced Mode) */
	{ USB_DEVICE_VER(0x0582, 0x00E7, 0x0, 0xffff) }, /* UA-25EX (Basic Mode) */
	{ USB_DEVICE_VER(0x0582, 0x00E9, 0x0, 0xffff) }, /* UA-1G (Advanced Mode) */
	{ USB_DEVICE_VER(0x1210, 0x0011, 0x0, 0xffff) }, /* DigiTech GSP 1101 */
	{ USB_DEVICE_VER(0x1235, 0x0010, 0x0, 0xffff) }, /* Focusrite Sapphire 6 - Novation EMS */
	{ USB_DEVICE_VER(0x08bb, 0x2900, 0x0, 0xffff) }, /* Ion USB Turntable - TI Japan PCM2900 Audio Codec */
	{ USB_DEVICE_VER(0x08bb, 0x2904, 0x0, 0xffff) }, /* Zoom B2.1U Bass Pedal - TI Japan PCM2900 Audio Codec */
	{ } /* Terminating entry */
};

MODULE_DEVICE_TABLE (usb, mmusbaudio_ids);

MODULE_AUTHOR(DRIVER_AUTHOR);

MODULE_DESCRIPTION(DRIVER_DESC);

#ifdef MODULE_LICENSE
MODULE_LICENSE("GPL");
#endif

static struct usb_driver mmusbaudio_driver_registration =
{
	name: "mmusbaudio",
	probe: mmusbaudio_probe,
	disconnect: mmusbaudio_disconnect,
	id_table:	mmusbaudio_ids,
	//driver_list: {NULL, NULL},
	//fops: NULL
};


/************************* Module Entry and Exit *****************************/

static int __init mmusbaudio_init(void)
{
	int status = 0;

	pr_info(DRIVER_DESC " " DRIVER_AUTHOR " " DRIVER_VERSION);

	memset(&mmusbaudio_midi_devices, 0, sizeof(mmusbaudio_midi_devices));
	memset(&mmusbaudio_audio_devices, 0, sizeof(mmusbaudio_midi_devices));
	
	if ((status = usb_register(&mmusbaudio_driver_registration)) < 0)
		pr_err("MMUSBAUDIO failed usb_register with code %d", status);

	return status;
}

static void __exit mmusbaudio_cleanup(void)
{
	usb_deregister(&mmusbaudio_driver_registration);

	pr_info("MMUSBAUDIO module deregistered and unloaded");
}

module_init(mmusbaudio_init);
module_exit(mmusbaudio_cleanup);



