xref: /plan9/sys/src/cmd/usb/audio/audiosub.c (revision 906943f9f6b8411972abb5e3a03ed19f74be7ccc)
17f0337cdSDavid du Colombier #include <u.h>
27f0337cdSDavid du Colombier #include <libc.h>
37f0337cdSDavid du Colombier #include <thread.h>
47f0337cdSDavid du Colombier #include "usb.h"
5*906943f9SDavid du Colombier #include "audio.h"
6*906943f9SDavid du Colombier #include "audioctl.h"
7*906943f9SDavid du Colombier 
8*906943f9SDavid du Colombier typedef struct Namelist Namelist;
9*906943f9SDavid du Colombier 
10*906943f9SDavid du Colombier struct Namelist
11*906943f9SDavid du Colombier {
12*906943f9SDavid du Colombier 	short	index;
13*906943f9SDavid du Colombier 	char		*name;
14*906943f9SDavid du Colombier };
157f0337cdSDavid du Colombier 
167f0337cdSDavid du Colombier Namelist terminal_types[] = {
177f0337cdSDavid du Colombier 	{	0x100, "USB Terminal, undefined type"},
187f0337cdSDavid du Colombier 	{	0x101, "USB Streaming"},
197c70c028SDavid du Colombier 	{	0x201, "Microphone"},
207f0337cdSDavid du Colombier 	{	0x301, "Speaker"},
21ce31847cSDavid du Colombier 	{	0x603, "Line connector"},
227c70c028SDavid du Colombier 	{	0x605, "S/PDIF"},
237f0337cdSDavid du Colombier 	{	0, nil }
247f0337cdSDavid du Colombier };
257f0337cdSDavid du Colombier 
26ce31847cSDavid du Colombier units[2][8];	/* rec and play units */
27ce31847cSDavid du Colombier nunits[2];		/* number in use */
287f0337cdSDavid du Colombier 
29*906943f9SDavid du Colombier char *
namefor(Namelist * list,int item)30*906943f9SDavid du Colombier namefor(Namelist *list, int item)
31*906943f9SDavid du Colombier {
32*906943f9SDavid du Colombier 	while(list->name){
33*906943f9SDavid du Colombier 		if(list->index == item)
34*906943f9SDavid du Colombier 			return list->name;
35*906943f9SDavid du Colombier 		list++;
36*906943f9SDavid du Colombier 	}
37*906943f9SDavid du Colombier 	return "<unnamed>";
38*906943f9SDavid du Colombier }
39*906943f9SDavid du Colombier 
407f0337cdSDavid du Colombier static int
findunit(int nr)417f0337cdSDavid du Colombier findunit(int nr)
427f0337cdSDavid du Colombier {
437f0337cdSDavid du Colombier 	int rec, i;
447f0337cdSDavid du Colombier 	for(rec = 0; rec < 2; rec++)
457f0337cdSDavid du Colombier 		for(i = 0; i < nunits[rec]; i++)
46ce31847cSDavid du Colombier 			if(units[rec][i] == nr)
477f0337cdSDavid du Colombier 				return rec;
487f0337cdSDavid du Colombier 	return -1;
497f0337cdSDavid du Colombier }
507f0337cdSDavid du Colombier 
517f0337cdSDavid du Colombier void
audio_interface(Dev *,Desc * dd)52*906943f9SDavid du Colombier audio_interface(Dev *, Desc *dd)
53*906943f9SDavid du Colombier {
54*906943f9SDavid du Colombier 	byte *b = (uchar*)&dd->data;
55*906943f9SDavid du Colombier 	byte *bb = b;
56*906943f9SDavid du Colombier 	int nb = dd->data.bLength;
57ce31847cSDavid du Colombier 	int ctl, ch, u, x;
587f0337cdSDavid du Colombier 	byte *p;
59*906943f9SDavid du Colombier 	int class, subclass;
607f0337cdSDavid du Colombier 	Audioalt *aa;
61*906943f9SDavid du Colombier 	char *hd;
627f0337cdSDavid du Colombier 
63*906943f9SDavid du Colombier 	class = Class(dd->iface->csp);
64*906943f9SDavid du Colombier 	subclass = Subclass(dd->iface->csp);
657f0337cdSDavid du Colombier 
66*906943f9SDavid du Colombier 	dprint(2, "%d.%d: ", class, subclass);
677f0337cdSDavid du Colombier 	switch (subclass){
687f0337cdSDavid du Colombier 	case 1:	// control
697f0337cdSDavid du Colombier 		switch (b[2]){
70ce31847cSDavid du Colombier 		case 0x01:
71*906943f9SDavid du Colombier 			dprint(2, "Class-Specific AC Interface Header Descriptor\n");
72*906943f9SDavid du Colombier 			dprint(2, "\tAudioDevClass release (bcd)%c%c%c%c, "
73*906943f9SDavid du Colombier 				"TotalLength %d, InCollection %d aInterfaceNr %d\n",
747f0337cdSDavid du Colombier 					'0'+((b[4]>>4)&0xf), '0'+(b[4]&0xf),
757f0337cdSDavid du Colombier 					'0'+((b[3]>>4)&0xf), '0'+(b[3]&0xf),
76ce31847cSDavid du Colombier 					b[5]|(b[6]<<8), b[7], b[8]);
777f0337cdSDavid du Colombier 			break;
787f0337cdSDavid du Colombier 		case 0x02:	// input
79*906943f9SDavid du Colombier 			dprint(2, "Audio Input Terminal Descriptor\n");
80*906943f9SDavid du Colombier 			dprint(2, "\tbTerminalId %d, wTerminalType "
81*906943f9SDavid du Colombier 				"0x%x (%s), bAssocTerminal %d bNrChannels %d, "
82*906943f9SDavid du Colombier 				"wChannelConfig %d, iChannelNames %d iTerminal %d\n",
837f0337cdSDavid du Colombier 					b[3], b[4]|(b[5]<<8),
847f0337cdSDavid du Colombier 					namefor(terminal_types, b[4]|(b[5]<<8)),
857f0337cdSDavid du Colombier 					b[6], b[7], b[8]|(b[9]<<8), b[10], b[11]);
86ce31847cSDavid du Colombier 			if((b[4]|b[5]<<8) == 0x101){
877f0337cdSDavid du Colombier 				if(verbose)
887f0337cdSDavid du Colombier 					fprint(2, "Audio output unit %d\n", b[3]);
897f0337cdSDavid du Colombier 				/* USB streaming input: play interface */
90ce31847cSDavid du Colombier 				units[Play][nunits[Play]++] = b[3];
917f0337cdSDavid du Colombier 			}else{
927f0337cdSDavid du Colombier 				if(verbose)
93*906943f9SDavid du Colombier 					fprint(2, "Dev can record from %s\n",
947f0337cdSDavid du Colombier 						namefor(terminal_types, b[4]|(b[5]<<8)));
957f0337cdSDavid du Colombier 				/* Non-USB input: record interface */
96ce31847cSDavid du Colombier 				units[Record][nunits[Record]++] = b[3];
977f0337cdSDavid du Colombier 			}
987f0337cdSDavid du Colombier 			break;
997f0337cdSDavid du Colombier 		case 0x03:	// output
100*906943f9SDavid du Colombier 			if(usbdebug){
1017f0337cdSDavid du Colombier 				fprint(2, "Audio Output Terminal Descriptor\n");
1027f0337cdSDavid du Colombier 				fprint(2, "\tbTerminalId %d, wTerminalType 0x%x (%s), bAssocTerminal %d bSourceId %d, iTerminal %d\n",
1037f0337cdSDavid du Colombier 					b[3], b[4]|(b[5]<<8),
1047f0337cdSDavid du Colombier 					namefor(terminal_types, b[4]|(b[5]<<8)),
1057f0337cdSDavid du Colombier 					b[6], b[7], b[8]);
1067f0337cdSDavid du Colombier 			}
1077f0337cdSDavid du Colombier 			if((b[4]|b[5]<<8) == 0x101){
1087f0337cdSDavid du Colombier 				if(verbose)
1097f0337cdSDavid du Colombier 					fprint(2, "Audio input unit %d\n", b[3]);
1107f0337cdSDavid du Colombier 				/* USB streaming output: record interface */
111ce31847cSDavid du Colombier 				units[Record][nunits[Record]++] = b[3];
1127f0337cdSDavid du Colombier 				if(verbose)
113*906943f9SDavid du Colombier 					fprint(2, "Dev can play to %s\n",
1147f0337cdSDavid du Colombier 						namefor(terminal_types, b[4]|(b[5]<<8)));
1157f0337cdSDavid du Colombier 				/* Non-USB output: play interface */
116ce31847cSDavid du Colombier 				units[Play][nunits[Play]++] = b[3];
1177f0337cdSDavid du Colombier 			}
1187f0337cdSDavid du Colombier 			break;
119ce31847cSDavid du Colombier 		case 0x04:
1207f0337cdSDavid du Colombier 			if(verbose)
1217f0337cdSDavid du Colombier 				fprint(2, "Audio Mixer Unit %d\n", b[3]);
122*906943f9SDavid du Colombier 			if(usbdebug){
1235ab4dd4cSDavid du Colombier 				fprint(2, "\t%d bytes:", nb);
1245ab4dd4cSDavid du Colombier 				for(ctl = 0; ctl < nb; ctl++)
1255ab4dd4cSDavid du Colombier 					fprint(2, " 0x%2.2x", b[ctl]);
1265ab4dd4cSDavid du Colombier 				fprint(2, "\n\tbUnitId %d, bNrInPins %d", b[3], b[4]);
1275ab4dd4cSDavid du Colombier 			}
1285ab4dd4cSDavid du Colombier 			if(b[4]){
129*906943f9SDavid du Colombier 				dprint(2, ", baSourceIDs: [%d", b[5]);
1305ab4dd4cSDavid du Colombier 				u = findunit(b[5]);
1315ab4dd4cSDavid du Colombier 				for(ctl = 1; ctl < b[4]; ctl++){
1325ab4dd4cSDavid du Colombier 					if(u < 0)
1335ab4dd4cSDavid du Colombier 						u = findunit(b[5+ctl]);
1345ab4dd4cSDavid du Colombier 					else if((x = findunit(b[5+ctl])) >= 0 && u != x && verbose)
135*906943f9SDavid du Colombier 						fprint(2, "\tMixer %d for I & O \n", b[3]);
136*906943f9SDavid du Colombier 					dprint(2, ", %d", b[5+ctl]);
1375ab4dd4cSDavid du Colombier 				}
138*906943f9SDavid du Colombier 				dprint(2, "]\n");
1395ab4dd4cSDavid du Colombier 				if(u >= 0){
1405ab4dd4cSDavid du Colombier 					units[u][nunits[u]++] = b[3];
1415ab4dd4cSDavid du Colombier 					if(mixerid[u] >= 0)
142*906943f9SDavid du Colombier 						fprint(2, "Second mixer (%d, %d) on %s\n",
143*906943f9SDavid du Colombier 						 mixerid[u], b[3], u?"record":"playback");
1445ab4dd4cSDavid du Colombier 					mixerid[u] = b[3];
1455ab4dd4cSDavid du Colombier 				}
146*906943f9SDavid du Colombier 				if(usbdebug){
1475ab4dd4cSDavid du Colombier 					fprint(2, "Channels %d, config %d, ",
1485ab4dd4cSDavid du Colombier 						b[ctl+5], b[ctl+5+1] | b[ctl+5+2] << 8);
1495ab4dd4cSDavid du Colombier 					x = b[ctl+5] * b[4];
1505ab4dd4cSDavid du Colombier 					fprint(2, "programmable: %d bits, 0x", x);
1515ab4dd4cSDavid du Colombier 					x = (x + 7) >> 3;
1525ab4dd4cSDavid du Colombier 					while(x--)
1535ab4dd4cSDavid du Colombier 						fprint(2, "%2.2x", b[ctl+x+5+4]);
1545ab4dd4cSDavid du Colombier 				}
1555ab4dd4cSDavid du Colombier 			}
1567f0337cdSDavid du Colombier 			break;
157ce31847cSDavid du Colombier 		case 0x05:
1587f0337cdSDavid du Colombier 			if(verbose)
1597f0337cdSDavid du Colombier 				fprint(2, "Audio Selector Unit %d\n", b[3]);
160*906943f9SDavid du Colombier 			dprint(2, "\tbUnitId %d, bNrInPins %d", b[3], b[4]);
161ce31847cSDavid du Colombier 			if(b[4]){
1627f0337cdSDavid du Colombier 				u = findunit(b[5]);
163*906943f9SDavid du Colombier 				dprint(2, ", baSourceIDs: %s [%d",
1645ab4dd4cSDavid du Colombier 					u?"record":"playback", b[5]);
1657f0337cdSDavid du Colombier 				for(ctl = 1; ctl < b[4]; ctl++){
1667f0337cdSDavid du Colombier 					if(u < 0)
1677f0337cdSDavid du Colombier 						u = findunit(b[5+ctl]);
168*906943f9SDavid du Colombier 					else if((x = findunit(b[5+ctl])) >= 0 &&
169*906943f9SDavid du Colombier 						u != x && verbose)
170*906943f9SDavid du Colombier 						fprint(2, "\tSelector %d for I & O\n", b[3]);
171*906943f9SDavid du Colombier 					dprint(2, ", %d", b[5+ctl]);
1727f0337cdSDavid du Colombier 				}
173*906943f9SDavid du Colombier 				dprint(2, "]\n");
1747f0337cdSDavid du Colombier 				if(u >= 0){
175ce31847cSDavid du Colombier 					units[u][nunits[u]++] = b[3];
1767f0337cdSDavid du Colombier 					if(selectorid[u] >= 0)
1777f0337cdSDavid du Colombier 						fprint(2, "Second selector (%d, %d) on %s\n", selectorid[u], b[3], u?"record":"playback");
1787f0337cdSDavid du Colombier 					selectorid[u] = b[3];
1797c70c028SDavid du Colombier 					controls[u][Selector_control].readable = 1;
1807c70c028SDavid du Colombier 					controls[u][Selector_control].settable = 1;
1817c70c028SDavid du Colombier 					controls[u][Selector_control].chans = 0;
1827f0337cdSDavid du Colombier 				}
1837f0337cdSDavid du Colombier 			}
1847f0337cdSDavid du Colombier 			break;
185ce31847cSDavid du Colombier 		case 0x06:	// feature
1867f0337cdSDavid du Colombier 			if(verbose) fprint(2, "Audio Feature Unit %d", b[3]);
187*906943f9SDavid du Colombier 			dprint(2, "\tbUnitId %d, bSourceId %d, bControlSize %d\n",
1887f0337cdSDavid du Colombier 				b[3], b[4], b[5]);
1897f0337cdSDavid du Colombier 			u = findunit(b[4]);
1907f0337cdSDavid du Colombier 			if(u >= 0){
1917f0337cdSDavid du Colombier 				if(verbose) fprint(2, " for %s\n", u?"Record":"Playback");
192ce31847cSDavid du Colombier 				units[u][nunits[u]++] = b[3];
1937f0337cdSDavid du Colombier 				if(featureid[u] >= 0)
194*906943f9SDavid du Colombier 					if(verbose)
195*906943f9SDavid du Colombier 						fprint(2, "Second feature unit (%d, %d)"
196*906943f9SDavid du Colombier 							" on %s\n", featureid[u], b[3],
197*906943f9SDavid du Colombier 							u?"record":"playback");
1987f0337cdSDavid du Colombier 				featureid[u] = b[3];
1997f0337cdSDavid du Colombier 			}else
2007f0337cdSDavid du Colombier 				if(verbose) fprint(2, ", not known what for\n");
201ce31847cSDavid du Colombier 			p = b + 6;
202ce31847cSDavid du Colombier 			for(ctl = 1; ctl < 0x0b; ctl++)
2037f0337cdSDavid du Colombier 				if((1<<(ctl-1)) & (b[6] | ((b[5]>1)?(b[7]<<8):0))){
20415174232SDavid du Colombier 					if(verbose)
20515174232SDavid du Colombier 						fprint(2, "\t%s control on master channel\n",
20615174232SDavid du Colombier 							controls[0][ctl].name);
207ce31847cSDavid du Colombier 					if(u >= 0){
20815174232SDavid du Colombier 						controls[u][ctl].readable = 1;
20915174232SDavid du Colombier 						controls[u][ctl].settable = 1;
210ce31847cSDavid du Colombier 						controls[u][ctl].chans = 0;
211ce31847cSDavid du Colombier 					}
212ce31847cSDavid du Colombier 				}
213ce31847cSDavid du Colombier 			p += (b[5]>1)?2:1;
214ce31847cSDavid du Colombier 			for(ch = 0; ch < (nb - 8)/b[5]; ch++){
215ce31847cSDavid du Colombier 				for(ctl = 1; ctl < 0x0b; ctl++)
216ce31847cSDavid du Colombier 					if((1<<(ctl-1)) & (p[0] | ((b[5]>1)?(p[1]<<8):0))){
2177f0337cdSDavid du Colombier 						if(verbose)
2187f0337cdSDavid du Colombier 						  fprint(2, "\t%s control on channel %d\n",
2197f0337cdSDavid du Colombier 							controls[0][ctl].name, ch+1);
220ce31847cSDavid du Colombier 						if(u >= 0){
221ce31847cSDavid du Colombier 							controls[u][ctl].readable = 1;
222ce31847cSDavid du Colombier 							controls[u][ctl].settable = 1;
223ce31847cSDavid du Colombier 							controls[u][ctl].chans |= 1 <<(ch+1);
2247f0337cdSDavid du Colombier 						}
2257f0337cdSDavid du Colombier 					}
2267f0337cdSDavid du Colombier 				p += (b[5]>1)?2:1;
2277f0337cdSDavid du Colombier 			}
2287f0337cdSDavid du Colombier 			break;
2297f0337cdSDavid du Colombier 		default:
230*906943f9SDavid du Colombier 			hd = hexstr(bb, nb);
231*906943f9SDavid du Colombier 			fprint(2, "audio control unknown: %s\n", hd);
232*906943f9SDavid du Colombier 			free(hd);
2337f0337cdSDavid du Colombier 		}
2347f0337cdSDavid du Colombier 		break;
2357f0337cdSDavid du Colombier 	case 2: // stream
2367f0337cdSDavid du Colombier 		switch (b[2]){
2377f0337cdSDavid du Colombier 		case 0x01:
238*906943f9SDavid du Colombier 			dprint(2, "Audio stream for TerminalID %d, delay %d, format_tag %#ux\n",
2397f0337cdSDavid du Colombier 				b[3], b[4], b[5] | (b[6]<<8));
2407f0337cdSDavid du Colombier 			break;
2417f0337cdSDavid du Colombier 		case 0x02:
242*906943f9SDavid du Colombier 			aa = (Audioalt *)dd->altc->aux;
2437f0337cdSDavid du Colombier 			if(aa == nil){
2447f0337cdSDavid du Colombier 				aa = mallocz(sizeof(Audioalt), 1);
245*906943f9SDavid du Colombier 				dd->altc->aux = aa;
2467f0337cdSDavid du Colombier 			}
2477f0337cdSDavid du Colombier 			if(verbose){
2487f0337cdSDavid du Colombier 				if(b[4] <= 2)
249*906943f9SDavid du Colombier 					fprint(2, "Interface %d: %s, %d bits, ",
250*906943f9SDavid du Colombier 						dd->iface->id, (b[4] == 1)?"mono":"stereo", b[6]);
2517f0337cdSDavid du Colombier 				else
252*906943f9SDavid du Colombier 					fprint(2, "Interface %d, %d channels, %d bits, ",
253*906943f9SDavid du Colombier 						dd->iface->id, b[4], b[6]);
2547f0337cdSDavid du Colombier 			}
2557f0337cdSDavid du Colombier 			if(b[7] == 0){
2567f0337cdSDavid du Colombier 				if(verbose)
2577f0337cdSDavid du Colombier 					fprint(2, "frequency variable between %d and %d\n",
2587f0337cdSDavid du Colombier 						b[8] | b[9]<<8 | b[10]<<16, b[11] | b[12]<<8 | b[13]<<16);
2597f0337cdSDavid du Colombier 				aa->minfreq = b[8] | b[9]<<8 | b[10]<<16;
2607f0337cdSDavid du Colombier 				aa->maxfreq = b[11] | b[12]<<8 | b[13]<<16;
2617f0337cdSDavid du Colombier 				aa->caps |= has_contfreq;
2627f0337cdSDavid du Colombier 			}else{
2637f0337cdSDavid du Colombier 				if(verbose)
2647f0337cdSDavid du Colombier 					fprint(2, "discrete frequencies are:");
2657f0337cdSDavid du Colombier 				for(ch = 0; ch < b[7] && ch < 8; ch++){
2667f0337cdSDavid du Colombier 					aa->freqs[ch] = b[8+3*ch] | b[9+3*ch]<<8 | b[10+3*ch]<<16;
2677f0337cdSDavid du Colombier 					if(verbose)
2687f0337cdSDavid du Colombier 						fprint(2, " %d", b[8+3*ch] | b[9+3*ch]<<8 | b[10+3*ch]<<16);
2697f0337cdSDavid du Colombier 				}
2707f0337cdSDavid du Colombier 				if(ch < 8)
2717f0337cdSDavid du Colombier 					aa->freqs[ch] = -1;
2727f0337cdSDavid du Colombier 				if(verbose)
2737f0337cdSDavid du Colombier 					fprint(2, "\n");
2747f0337cdSDavid du Colombier 				if(ch > 1)
2757f0337cdSDavid du Colombier 					aa->caps |= has_discfreq;	/* more than one frequency */
2767f0337cdSDavid du Colombier 				else
2777f0337cdSDavid du Colombier 					aa->caps |= onefreq;		/* only one frequency */
2787f0337cdSDavid du Colombier 			}
2797f0337cdSDavid du Colombier 			aa->nchan = b[4];
2807f0337cdSDavid du Colombier 			aa->res = b[6];
2817f0337cdSDavid du Colombier 			aa->subframesize = b[5];
2827f0337cdSDavid du Colombier 			break;
2837f0337cdSDavid du Colombier 		default:
284*906943f9SDavid du Colombier 			if(usbdebug){
285*906943f9SDavid du Colombier 				hd = hexstr(bb, nb);
286*906943f9SDavid du Colombier 				fprint(2, "audio stream unknown: %s\n", hd);
287*906943f9SDavid du Colombier 				free(hd);
288*906943f9SDavid du Colombier 			}
2897f0337cdSDavid du Colombier 		}
2907f0337cdSDavid du Colombier 		break;
2917f0337cdSDavid du Colombier 	case 3: // midi
2927f0337cdSDavid du Colombier 	default:
293*906943f9SDavid du Colombier 		if(usbdebug){
294*906943f9SDavid du Colombier 			hd = hexstr(bb, nb);
295*906943f9SDavid du Colombier 			fprint(2, "Unknown audio stream type: CS_INTERFACE: %s\n", hd);
296*906943f9SDavid du Colombier 			free(hd);
2977f0337cdSDavid du Colombier 		}
2987f0337cdSDavid du Colombier 	}
2997f0337cdSDavid du Colombier }
300*906943f9SDavid du Colombier 
301