/*-
 * Copyright (c) 1999, 2000 MAEKAWA Masahide <maekawa@math.sci.kobe-u.ac.jp>
 * 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.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 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.
 *
 *	$Maekawa: audio.c,v 1.12 2000/03/19 16:14:43 maekawa Exp $
 */

#include <stdio.h>
#include <unistd.h>

#include <dev/usb/usb.h>
#include <uaudioreg.h>

#include "main.h"
#include "audio.h"

/*
 * Class: Audio
 */
void
audio_interface(desc, subclass)
	usb_descriptor_t *desc;
	u_int16_t subclass;
{
	switch (subclass) {
	case UISUBCLASS_AUDIOCONTROL:
		audio_control_interface(desc);
		break;
	case UISUBCLASS_AUDIOSTREAM:
		audio_streaming_interface(desc);
		break;
	case UISUBCLASS_MIDISTREAM:
		audio_midi_interface(desc);
		break;
	default:
		dump_descriptor(desc, "\t\t");
		dump_raw((u_int8_t *)desc, "\t\t");
		break;
	}

	return;
}

void
audio_endpoint(desc, subclass)
	usb_descriptor_t *desc;
	u_int16_t subclass;
{
	switch (subclass) {
	case UISUBCLASS_AUDIOSTREAM:
		audio_streaming_endpoint(desc);
		break;
	case UISUBCLASS_MIDISTREAM:
		audio_midi_endpoint(desc);
		break;
	default:
		dump_descriptor(desc, "\t\t");
		dump_raw((u_int8_t *)desc, "\t\t");
		break;
	}

	return;
}

/*
 * Subclass: Audio Control
 */
void
audio_control_interface(desc)
	usb_descriptor_t *desc;
{
	u_int8_t *buf = (u_int8_t *)desc;

	switch (desc->bDescriptorSubtype) {
	case UDESCSUB_AC_HEADER:
		audio_control_header(buf);
		break;
	case UDESCSUB_AC_INPUT:
		audio_control_input_terminal(buf);
		break;
	case UDESCSUB_AC_OUTPUT:
		audio_control_output_terminal(buf);
		break;
	case UDESCSUB_AC_MIXER:
		audio_control_mixer_unit(buf);
		break;
	case UDESCSUB_AC_SELECTOR:
		audio_control_selector_unit(buf);
		break;
	case UDESCSUB_AC_FEATURE:
		audio_control_feature_unit(buf);
		break;
	case UDESCSUB_AC_PROCESSING:
		audio_control_processing_unit(buf);
		break;
	case UDESCSUB_AC_EXTENSION:
		audio_control_extension_unit(buf);
		break;
	default:
		dump_descriptor(buf, "\t\t");
		dump_raw(buf, "\t\t");
		break;
	}

	return;
}

void
audio_control_header(buf)
	u_int8_t *buf;
{
	int n, i;

	printf("\t\tClass-Specific AC Interface Header Descriptor:\n"
	       "\t\t  bLength            %d\n"
	       "\t\t  bDescriptorType    %02x\n"
	       "\t\t  bDescriptorSubtype %02x\n"
	       "\t\t  bcdADC             %04x\n"
	       "\t\t  wTotalLength       %d\n"
	       "\t\t  bInCollection      %d\n",
	       buf[0], buf[1], buf[2], UGETW(&buf[3]), UGETW(&buf[5]), buf[7]);

	n = buf[7];

	for (i = 0 ; i < n ; i++) {
		printf("\t\t  baInterfaceNr(%d)   %d\n", i+1, buf[8+i]);
	}

	printf("\n");

	return;
}

void
audio_control_input_terminal(buf)
	u_int8_t *buf;
{
	printf("\t\tInput Terminal Descriptor:\n"
	       "\t\t  bLength            %d\n"
	       "\t\t  bDescriptorType    %02x\n"
	       "\t\t  bDescriptorSubtype %02x\n"
	       "\t\t  bTerminalID        %d\n"
	       "\t\t  wTerminalType      %04x\n"
	       "\t\t  bAssocTerminal     %d\n"
	       "\t\t  bNrChannels        %d\n"
	       "\t\t  wChannelConfig     %04x\n"
	       "\t\t  iChannelNames      %d\n"
	       "\t\t  iTerminal          %d\n\n",
	       buf[0], buf[1], buf[2], buf[3], UGETW(&buf[4]), buf[6],
	       buf[7], UGETW(&buf[8]), buf[10], buf[11]);

	return;
}

void
audio_control_output_terminal(buf)
	u_int8_t *buf;
{
	printf("\t\tOutput Terminal Descriptor:\n"
	       "\t\t  bLength            %d\n"
	       "\t\t  bDescriptorType    %02x\n"
	       "\t\t  bDescriptorSubtype %02x\n"
	       "\t\t  bTerminalID        %d\n"
	       "\t\t  wTerminalType      %04x\n"
	       "\t\t  bAssocTerminal     %d\n"
	       "\t\t  bSourceID          %d\n"
	       "\t\t  iTerminal          %d\n\n",
	       buf[0], buf[1], buf[2], buf[3], UGETW(&buf[4]), buf[6],
	       buf[7], buf[8]);

	return;
}

void
audio_control_mixer_unit(buf)
	u_int8_t *buf;
{
	int p, N, i;

	printf("\t\tMixer Unit Descriptor:\n"
	       "\t\t  bLength            %d\n"
	       "\t\t  bDescriptorType    %02x\n"
	       "\t\t  bDescriptorSubtype %02x\n"
	       "\t\t  bUnitID            %d\n"
	       "\t\t  bNrInPins          %d\n",
	       buf[0], buf[1], buf[2], buf[3], buf[4]);

	p = buf[4];

	for (i = 0 ; i < p ; i++) {
		printf("\t\t  baSourceID(%d)      %d\n", i+1, buf[5+i]);
	}

	printf("\t\t  bNrChannels        %d\n"
	       "\t\t  wChannelConfig     %d\n"
	       "\t\t  iChannelNames      %d\n"
	       "\t\t  bmControls         %d\n",
	       buf[5+p], UGETW(&buf[6+p]), buf[8+p], buf[9+p]);

	N = buf[9+p];

	printf("\t\t  iMixer             %d\n\n", buf[9+p+N]);

	return;
}

void
audio_control_selector_unit(buf)
	u_int8_t *buf;
{
	int p, i;

	printf("\t\tSelector Unit Descriptor:\n"
	       "\t\t  bLength            %d\n"
	       "\t\t  bDescriptorType    %02x\n"
	       "\t\t  bDescriptorSubtype %02x\n"
	       "\t\t  bUnitID            %d\n"
	       "\t\t  bNrInPins          %d\n",
	       buf[0], buf[1], buf[2], buf[3], buf[4]);

	p = buf[4];

	for (i = 0 ; i < p ; i++) {
		printf("\t\t  baSourceID(%d)     %d\n", i+1, buf[5+p]);
	}

	printf("\n");

	return;
}

void
audio_control_feature_unit(buf)
	u_int8_t *buf;
{
	int n, ch, i, j;

	printf("\t\tFeature Unit Descriptor:\n"
	       "\t\t  bLength            %d\n"
	       "\t\t  bDescriptorType    %02x\n"
	       "\t\t  bDescriptorSubtype %02x\n"
	       "\t\t  bUnitID            %d\n"
	       "\t\t  bSourceID          %d\n"
	       "\t\t  bControlSize       %d\n",
	       buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);

	n = buf[5];
	ch = (buf[0] - 7) / n;

	for (i = 0 ; i < ch ; i++) {
		for (j = 0 ; j < n ; j++) {
			printf("\t\t  bmaControls(%d)     %02x\n",
			       i, buf[6+(i+1)*j]);
		}
	}

	printf("\t\t  iFeature           %d\n\n", buf[6+ch*n]);

	return;
}

void
audio_control_processing_unit(buf)
	u_int8_t *buf;
{
	u_int16_t wProcessType;
	int n, m = 0, mode = 0, i;

	wProcessType = UGETW(&buf[4]);

	switch (wProcessType) {
	case 0x01:
		/* Up/Down-mix Processing Unit Descriptor */
		printf("\t\tUp/Down-mix Processing Unit Descriptor:\n");
		mode = 1;
		break;
	case 0x02:
		/* Dolby Prologic Processing Unit Descriptor */
		printf("\t\tDolby Prologic Processing Unit Descriptor:\n");
		mode = 1;
		break;
	case 0x03:
		/* 3D-Stereo Extender Processing Unit Descriptor */
		printf("\t\t3D-Stereo Extender Processing Unit Descriptor:\n");
		break;
	case 0x04:
		/* Reverberation Processing Unit Descriptor */
		printf("\t\tReverberation Processing Unit Descriptor:\n");
		break;
	case 0x05:
		/* Chorus Processing Unit Descriptor */
		printf("\t\tChorus Processing Unit Descriptor:\n");
		break;
	case 0x06:
		/* Dynamic Range Compressor Processing Unit Descriptor */
		printf("\t\tDynamic Range Compressor"
		       " Processing Unit Descriptor:\n");
		break;
	default:
		dump_descriptor(buf, "\t\t");
		dump_raw(buf, "\t\t");
		return;
	}

	printf("\t\t  bLength            %d\n"
	       "\t\t  bDescriptorType    %02x\n"
	       "\t\t  bDescriptorSubtype %02x\n"
	       "\t\t  bUnitID            %d\n"
	       "\t\t  wProcessType       %04x\n"
	       "\t\t  bNrInPins          %d\n"
	       "\t\t  bSourceID          %d\n"
	       "\t\t  bNrChannels        %d\n"
	       "\t\t  wChannelConfig     %04x\n"
	       "\t\t  iChannelNames      %d\n"
	       "\t\t  bControlSize       %d\n",
	       buf[0], buf[1], buf[2], buf[3], UGETW(&buf[4]), buf[6], buf[7],
	       buf[8], UGETW(&buf[9]), buf[11], buf[12]);

	n = buf[12];

	for (i = 0 ; i < n ; i++) {
		printf("\t\t  bmControls         %02x\n", buf[13+i]);
	}

	printf("\t\t  iProcessing        %d\n", buf[13+n]);

	if (mode) {
		printf("\t\t  bNrModes           %d\n", buf[14+n]);

		m = buf[14+n];

		for (i = 0 ; i < m ; i++) {
			printf("\t\t  waModes(%d)         %02x\n",
			       i+1, UGETW(&buf[15+n+i*2]));
		}
	}

	printf("\n");

	return;
}

void
audio_control_extension_unit(buf)
	u_int8_t *buf;
{
	int p, n, i;

	printf("\t\tExtension Unit Descriptor:\n"
	       "\t\t  bLength            %d\n"
	       "\t\t  bDescriptorType    %02x\n"
	       "\t\t  bDescriptorSubtype %02x\n"
	       "\t\t  bUnitID            %d\n"
	       "\t\t  wExtensionCode     %04x\n"
	       "\t\t  bNrInPins          %d\n",
	       buf[0], buf[1], buf[2], buf[3], UGETW(&buf[4]), buf[6]);

	p = buf[6];

	for (i = 0 ; i < p ; i++) {
		printf("\t\t  baSourceID(%d)      %d\n", i+1, buf[7+i]);
	}

	printf("\t\t  bNrChannels        %d\n"
	       "\t\t  wChannelConfig     %04x\n"
	       "\t\t  iChannelNames      %d\n"
	       "\t\t  bControlSize       %d\n",
	       buf[7+p], UGETW(&buf[8+p]), buf[10+p], buf[11+p]);

	n = buf[11+p];

	for (i = 0 ; i < n ; i++) {
		printf("\t\t  bmControls         %02x\n", buf[12+p+i]);
	}

	printf("\t\t  iExtension         %d\n\n", buf[12+p+n]);

	return;
}

/*
 * Subclass: Audio Streaming
 */

void
audio_streaming_interface(desc)
	usb_descriptor_t *desc;
{
	u_int8_t *buf = (u_int8_t *)desc;

	switch (desc->bDescriptorSubtype) {
	case AS_GENERAL:
		audio_streaming_general(buf);
		break;
	case FORMAT_TYPE:
		audio_streaming_format(buf);
		break;
	case FORMAT_SPECIFIC:
		audio_streaming_format_specific(buf);
		break;
	default:
		dump_descriptor(buf, "\t\t");
		dump_raw(buf, "\t\t");
		break;
	}

	return;
}

void
audio_streaming_endpoint(desc)
	usb_descriptor_t *desc;
{
	u_int8_t *buf = (u_int8_t *)desc;

	printf("\t\tClass-Specific AS Endpoint Descriptor:\n"
	       "\t\t  bLength            %d\n"
	       "\t\t  bDescriptorType    %02x\n"
	       "\t\t  bDescriptorSubtype %02x\n"
	       "\t\t  bmAttributes       %02x\n"
	       "\t\t  bLockDelayUnits    %d\n"
	       "\t\t  wLockDelay         %d\n\n",
	       buf[0], buf[1], buf[2], buf[3], buf[4], UGETW(&buf[5]));

	return;
}

void
audio_streaming_general(buf)
	u_int8_t *buf;
{
	printf("\t\tClass-Specific AS Interface Descriptor:\n"
	       "\t\t  bLength            %d\n"
	       "\t\t  bDescriptorType    %02x\n"
	       "\t\t  bDescriptorSubtype %02x\n"
	       "\t\t  bTerminalLink      %d\n"
	       "\t\t  bDecay             %d\n"
	       "\t\t  wFormatTag         %d\n\n",
	       buf[0], buf[1], buf[2], buf[3], buf[4], UGETW(&buf[5]));

	return;
}

void
audio_streaming_format(buf)
	u_int8_t *buf;
{
	int bFormatType;

	bFormatType = buf[3];

	switch (bFormatType) {
	case FORMAT_TYPE_I:
		audio_streaming_format_type1(buf);
		break;
	case FORMAT_TYPE_II:
		audio_streaming_format_type2(buf);
		break;
	case FORMAT_TYPE_III:
		audio_streaming_format_type3(buf);
		break;
	default:
		dump_descriptor(buf, "\t\t");
		dump_raw(buf, "\t\t");
		break;
	}

	return;
}

void
audio_streaming_format_type1(buf)
	u_int8_t *buf;
{
	int bSamFreqType, i;

	printf("\t\tType I Format Descriptor:\n"
	       "\t\t  bLength            %d\n"
	       "\t\t  bDescriptorType    %02x\n"
	       "\t\t  bDescriptorSubtype %02x\n"
	       "\t\t  bFormatType        %02x\n"
	       "\t\t  bNrChannels        %d\n"
	       "\t\t  bSubframeSize      %d\n"
	       "\t\t  bBitResolution     %d\n"
	       "\t\t  bSamFreqType       %d\n",
	       buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);

	bSamFreqType = buf[7];

	if (bSamFreqType == 0) {
		printf("\t\t  tLowerSamFreq      %d\n"
		       "\t\t  tUpperSamFreq      %d\n",
		       buf[8] | buf[9] << 8 | buf[10] << 16,
		       buf[11] | buf[12] << 8 | buf[13] << 16);
	} else if (bSamFreqType > 0) {
		for (i = 0 ; i < bSamFreqType ; i++) {
			printf("\t\t  tSamFreq[%d]        %d\n", i+1,
			       buf[8+i*3]
			       | buf[9+i*3] << 8
			       | buf[10+i*3] << 16);
		}
	}

	printf("\n");

	return;
}

void
audio_streaming_format_type2(buf)
	u_int8_t *buf;
{
	int bSamFreqType, i;

	printf("\t\tType II Format Descriptor:\n"
	       "\t\t  bLength            %d\n"
	       "\t\t  bDescriptorType    %02x\n"
	       "\t\t  bDescriptorSubtype %02x\n"
	       "\t\t  bFormatType        %02x\n"
	       "\t\t  wMaxBitRate        %d\n"
	       "\t\t  wSamplesPerFrame   %d\n"
	       "\t\t  bSamFreqType       %d\n",
	       buf[0], buf[1], buf[2], buf[3],
	       UGETW(&buf[4]), UGETW(&buf[6]), buf[8]);

	bSamFreqType = buf[8];

	if (bSamFreqType == 0) {
		printf("\t\t  tLowerSamFreq      %d\n"
		       "\t\t  tUpperSamFreq      %d\n",
		       buf[9] | buf[10] << 8 | buf[11] << 16,
		       buf[12] | buf[13] << 8 | buf[14] << 16);
	} else if (bSamFreqType > 0) {
		for (i = 0 ; i < bSamFreqType ; i++) {
			printf("\t\t  tSamFreq[%d]        %d\n", i+1,
			       buf[9+i*3]
			       | buf[10+i*3] << 8
			       | buf[11+i*3] << 16);
		}
	}

	printf("\n");

	return;
}

void
audio_streaming_format_type3(buf)
	u_int8_t *buf;
{
	int bSamFreqType, i;

	printf("\t\tType III Format Descriptor:\n"
	       "\t\t  bLength            %d\n"
	       "\t\t  bDescriptorType    %02x\n"
	       "\t\t  bDescriptorSubtype %02x\n"
	       "\t\t  bFormatType        %02x\n"
	       "\t\t  bNrChannels        %d\n"
	       "\t\t  bSubframeSize      %d\n"
	       "\t\t  bBitResolution     %d\n"
	       "\t\t  bSamFreqType       %d\n",
	       buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);

	bSamFreqType = buf[7];

	if (bSamFreqType == 0) {
		printf("\t\t  tLowerSamFreq      %d\n"
		       "\t\t  tUpperSamFreq      %d\n",
		       buf[8] | buf[9] << 8 | buf[10] << 16,
		       buf[11] | buf[12] << 8 | buf[13] << 16);
	} else if (bSamFreqType > 0) {
		for (i = 0 ; i < bSamFreqType ; i++) {
			printf("\t\t  tSamFreq[%d]        %d\n", i+1,
			       buf[8+i*3]
			       | buf[9+i*3] << 8
			       | buf[10+i*3] << 16);
		}
	}

	printf("\n");

	return;
}

void
audio_streaming_format_specific(buf)
	u_int8_t *buf;
{
	int wFormatTag;

	wFormatTag = UGETW(&buf[3]);

	switch (wFormatTag) {
	case 0x1001:
		/* MPEG Format-Specific Descriptor */
		audio_streaming_format_mpeg(buf);
		break;
	case 0x1002:
		/* AC-3 Format-Specific Descriptor */
		audio_streaming_format_ac3(buf);
		break;
	default:
		dump_descriptor(buf, "\t\t");
		dump_raw(buf, "\t\t");
		break;
	}

	return;
}

void
audio_streaming_format_mpeg(buf)
	u_int8_t *buf;
{
	printf("\t\tMPEG Format Specific Descriptor:\n"
	       "\t\t  bLength            %d\n"
	       "\t\t  bDescriptorType    %02x\n"
	       "\t\t  bDescriptorSubtype %02x\n"
	       "\t\t  wFormatTag         %04x\n"
	       "\t\t  bmMPEGCapabilities %04x\n"
	       "\t\t  bmMPEGFeatures     %02x\n\n",
	       buf[0], buf[1], buf[2], UGETW(&buf[3]), UGETW(&buf[5]), buf[7]);

	return;
}

void
audio_streaming_format_ac3(buf)
	u_int8_t *buf;
{
	printf("\t\tAC-3 Format Specific Descriptor:\n"
	       "\t\t  bLength            %d\n"
	       "\t\t  bDescriptorType    %02x\n"
	       "\t\t  bDescriptorSubtype %02x\n"
	       "\t\t  wFormatTag         %04x\n"
	       "\t\t  bmBSID             %08x\n"
	       "\t\t  bmAC3Features      %02x\n\n",
	       buf[0], buf[1], buf[2], UGETW(&buf[3]), UGETDW(&buf[5]), buf[9]);

	return;
}

/*
 * Subclass: MIDI Streaming
 */

void
audio_midi_interface(desc)
	usb_descriptor_t *desc;
{
	u_int8_t *buf = (u_int8_t *)desc;

	switch (desc->bDescriptorSubtype) {
	case 0x01:
		/* MS Interface Header Descriptor */
		audio_midi_header(buf);
		break;
	case 0x02:
		/* MIDI IN Jack Descriptor */
		audio_midi_midi_in_jack(buf);
		break;
	case 0x03:
		/* MIDI OUT Jack Descriptor */
		audio_midi_midi_out_jack(buf);
		break;
	case 0x04:
		/* Element Descriptor */
		audio_midi_element(buf);
		break;
	default:
		dump_descriptor(buf, "\t\t");
		dump_raw(buf, "\t\t");
		break;
	}

	return;
}

void
audio_midi_endpoint(desc)
	usb_descriptor_t *desc;
{
	u_int8_t *buf = (u_int8_t *)desc;
	int n, i;

	printf("\t\tMS Bulk Data Endpoint Descriptor:\n"
	       "\t\t  bLength            %d\n"
	       "\t\t  bDescriptorType    %02x\n"
	       "\t\t  bDescriptorSubtype %02x\n"
	       "\t\t  bNumEmbMIDIJack    %d\n",
	       buf[0], buf[1], buf[2], buf[3]);

	n = buf[3];

	for (i = 1 ; i <= n ; i++) {
		printf("\t\t  baAssocJackID(%d)   %d\n", i, buf[4+(i-1)]);
	}

	printf("\n");

	return;
}

void
audio_midi_header(buf)
	u_int8_t *buf;
{
	printf("\t\tMS Interface Header Descriptor:\n"
	       "\t\t  bLength            %d\n"
	       "\t\t  bDescriptorType    %02x\n"
	       "\t\t  bDescriptorSubtype %02x\n"
	       "\t\t  bcdMSC             %04x\n"
	       "\t\t  wTotalLength       %d\n\n",
	       buf[0], buf[1], buf[2], UGETW(&buf[3]), UGETW(&buf[5]));

	return;
}

void
audio_midi_midi_in_jack(buf)
	u_int8_t *buf;
{
	printf("\t\tMIDI IN Jack Descriptor:\n"
	       "\t\t  bLength            %d\n"
	       "\t\t  bDescriptorType    %02x\n"
	       "\t\t  bDescriptorSubtype %02x\n"
	       "\t\t  bJackType          %02x",
	       buf[0], buf[1], buf[2], buf[3]);

	if (buf[3] == 0x01) {
		printf(" (Embedded)\n");
	} else if (buf[3] == 0x02) {
		printf(" (External)\n");
	} else {
		printf("\n");
	}

	printf("\t\t  bJackID            %02x\n"
	       "\t\t  iJack              %d\n\n",
	       buf[4], buf[5]);

	return;
}

void
audio_midi_midi_out_jack(buf)
	u_int8_t *buf;
{
	int p, i;

	printf("\t\tMIDI OUT Jack Descriptor:\n"
	       "\t\t  bLength            %d\n"
	       "\t\t  bDescriptorType    %02x\n"
	       "\t\t  bDescriptorSubtype %02x\n"
	       "\t\t  bJackType          %02x",
	       buf[0], buf[1], buf[2], buf[3]);

	if (buf[3] == 0x01) {
		printf(" (Embedded)\n");
	} else if (buf[3] == 0x02) {
		printf(" (External)\n");
	} else {
		printf("\n");
	}

	printf("\t\t  bJackID            %02x\n"
	       "\t\t  bNrInputPins       %d\n",
	       buf[4], buf[5]);

	p = buf[5];

	for (i = 1 ; i <= p ; i++) {
		printf("\t\t  baSourceID(%d)      %d\n"
		       "\t\t  BaSourcePin(%d)     %d\n",
		       i, buf[6+2*(i-1)], i, buf[6+2*(i-1)+1]);
	}

	printf("\t\t  iJack              %d\n\n", buf[5+2*p]);

	return;
}

void
audio_midi_element(buf)
	u_int8_t *buf;
{
	int p, n, i;

	printf("\t\tElement Descriptor:\n"
	       "\t\t  bLength            %d\n"
	       "\t\t  bDescriptorType    %02x\n"
	       "\t\t  bDescriptorSubType %02x\n"
	       "\t\t  bElementID         %02x\n"
	       "\t\t  bNrInputPins       %d\n",
	       buf[0], buf[1], buf[2], buf[3], buf[4]);

	p = buf[4];

	for (i = 1 ; i <= p ; i++) {
		printf("\t\t  baSourceID(%d)      %d\n"
		       "\t\t  BaSourcePin(%d)     %d\n",
		       i, buf[5+2*(i-1)], i, buf[5+2*(i-1)+1]);
	}

	printf("\t\t  bNrOutputPins      %d\n"
	       "\t\t  bInTerminalLink    %02x\n"
	       "\t\t  bOutTerminalLink   %02x\n"
	       "\t\t  bElCapsSize        %d\n",
	       buf[5+2*p], buf[6+2*p], buf[7+2*p], buf[8+2*p]);

	n = buf[0] - 2 * p;

	for (i = 0 ; i < n ; i++) {
		printf("\t\t  bmElementCaps      %02x\n", buf[9+2*p+i]);
	}

	printf("\t\t  iElement           %d\n\n", buf[9+2*p+n]);

	return;
}
