1*74a4d8c2SCharles.Forsyth /*
2*74a4d8c2SCharles.Forsyth * SAC/UDA 1341 Audio driver for the Bitsy
3*74a4d8c2SCharles.Forsyth *
4*74a4d8c2SCharles.Forsyth * This code is covered by the Lucent Public Licence 1.02 (http://plan9.bell-labs.com/plan9dist/license.html);
5*74a4d8c2SCharles.Forsyth * see the file NOTICE in the current directory. Modifications for the Inferno environment by Vita Nuova.
6*74a4d8c2SCharles.Forsyth *
7*74a4d8c2SCharles.Forsyth * The Philips UDA 1341 sound chip is accessed through the Serial Audio
8*74a4d8c2SCharles.Forsyth * Controller (SAC) of the StrongARM SA-1110.
9*74a4d8c2SCharles.Forsyth *
10*74a4d8c2SCharles.Forsyth * The code morphs Nicolas Pitre's <nico@cam.org> Linux controller
11*74a4d8c2SCharles.Forsyth * and Ken's Soundblaster controller.
12*74a4d8c2SCharles.Forsyth *
13*74a4d8c2SCharles.Forsyth * The interface should be identical to that of devaudio.c
14*74a4d8c2SCharles.Forsyth */
15*74a4d8c2SCharles.Forsyth #include "u.h"
16*74a4d8c2SCharles.Forsyth #include "../port/lib.h"
17*74a4d8c2SCharles.Forsyth #include "mem.h"
18*74a4d8c2SCharles.Forsyth #include "dat.h"
19*74a4d8c2SCharles.Forsyth #include "fns.h"
20*74a4d8c2SCharles.Forsyth #include "../port/error.h"
21*74a4d8c2SCharles.Forsyth #include "io.h"
22*74a4d8c2SCharles.Forsyth
23*74a4d8c2SCharles.Forsyth static int debug = 0;
24*74a4d8c2SCharles.Forsyth
25*74a4d8c2SCharles.Forsyth /* UDA 1341 Registers */
26*74a4d8c2SCharles.Forsyth enum {
27*74a4d8c2SCharles.Forsyth /* Status0 register */
28*74a4d8c2SCharles.Forsyth UdaStatusDC = 0, /* 1 bit */
29*74a4d8c2SCharles.Forsyth UdaStatusIF = 1, /* 3 bits */
30*74a4d8c2SCharles.Forsyth UdaStatusSC = 4, /* 2 bits */
31*74a4d8c2SCharles.Forsyth UdaStatusRST = 6, /* 1 bit */
32*74a4d8c2SCharles.Forsyth };
33*74a4d8c2SCharles.Forsyth
34*74a4d8c2SCharles.Forsyth enum {
35*74a4d8c2SCharles.Forsyth /* Status1 register */
36*74a4d8c2SCharles.Forsyth UdaStatusPC = 0, /* 2 bits */
37*74a4d8c2SCharles.Forsyth UdaStatusDS = 2, /* 1 bit */
38*74a4d8c2SCharles.Forsyth UdaStatusPDA = 3, /* 1 bit */
39*74a4d8c2SCharles.Forsyth UdaStatusPAD = 4, /* 1 bit */
40*74a4d8c2SCharles.Forsyth UdaStatusIGS = 5, /* 1 bit */
41*74a4d8c2SCharles.Forsyth UdaStatusOGS = 6, /* 1 bit */
42*74a4d8c2SCharles.Forsyth };
43*74a4d8c2SCharles.Forsyth
44*74a4d8c2SCharles.Forsyth /*
45*74a4d8c2SCharles.Forsyth * UDA1341 L3 address and command types
46*74a4d8c2SCharles.Forsyth */
47*74a4d8c2SCharles.Forsyth
48*74a4d8c2SCharles.Forsyth enum {
49*74a4d8c2SCharles.Forsyth UDA1341_DATA0 = 0,
50*74a4d8c2SCharles.Forsyth UDA1341_DATA1,
51*74a4d8c2SCharles.Forsyth UDA1341_STATUS,
52*74a4d8c2SCharles.Forsyth UDA1341_L3Addr = 0x14,
53*74a4d8c2SCharles.Forsyth };
54*74a4d8c2SCharles.Forsyth
55*74a4d8c2SCharles.Forsyth typedef struct AQueue AQueue;
56*74a4d8c2SCharles.Forsyth typedef struct Buf Buf;
57*74a4d8c2SCharles.Forsyth typedef struct IOstate IOstate;
58*74a4d8c2SCharles.Forsyth
59*74a4d8c2SCharles.Forsyth enum
60*74a4d8c2SCharles.Forsyth {
61*74a4d8c2SCharles.Forsyth Qdir = 0,
62*74a4d8c2SCharles.Forsyth Qaudio,
63*74a4d8c2SCharles.Forsyth Qvolume,
64*74a4d8c2SCharles.Forsyth Qstatus,
65*74a4d8c2SCharles.Forsyth Qaudioctl,
66*74a4d8c2SCharles.Forsyth
67*74a4d8c2SCharles.Forsyth Fmono = 1,
68*74a4d8c2SCharles.Forsyth Fin = 2,
69*74a4d8c2SCharles.Forsyth Fout = 4,
70*74a4d8c2SCharles.Forsyth
71*74a4d8c2SCharles.Forsyth Aclosed = 0,
72*74a4d8c2SCharles.Forsyth Aread,
73*74a4d8c2SCharles.Forsyth Awrite,
74*74a4d8c2SCharles.Forsyth
75*74a4d8c2SCharles.Forsyth Vaudio = 0,
76*74a4d8c2SCharles.Forsyth Vmic,
77*74a4d8c2SCharles.Forsyth Vtreb,
78*74a4d8c2SCharles.Forsyth Vbass,
79*74a4d8c2SCharles.Forsyth Vspeed,
80*74a4d8c2SCharles.Forsyth Vfilter,
81*74a4d8c2SCharles.Forsyth Vinvert,
82*74a4d8c2SCharles.Forsyth Nvol,
83*74a4d8c2SCharles.Forsyth
84*74a4d8c2SCharles.Forsyth Bufsize = 4*1024, /* 46 ms each */
85*74a4d8c2SCharles.Forsyth Nbuf = 32, /* 1.5 seconds total */
86*74a4d8c2SCharles.Forsyth
87*74a4d8c2SCharles.Forsyth Speed = 44100,
88*74a4d8c2SCharles.Forsyth Ncmd = 50, /* max volume command words */
89*74a4d8c2SCharles.Forsyth };
90*74a4d8c2SCharles.Forsyth
91*74a4d8c2SCharles.Forsyth Dirtab
92*74a4d8c2SCharles.Forsyth audiodir[] =
93*74a4d8c2SCharles.Forsyth {
94*74a4d8c2SCharles.Forsyth ".", {Qdir, 0, QTDIR}, 0, 0555,
95*74a4d8c2SCharles.Forsyth "audio", {Qaudio}, 0, 0666,
96*74a4d8c2SCharles.Forsyth "volume", {Qvolume}, 0, 0666,
97*74a4d8c2SCharles.Forsyth "audioctl", {Qaudioctl}, 0, 0666,
98*74a4d8c2SCharles.Forsyth "audiostat",{Qstatus}, 0, 0444,
99*74a4d8c2SCharles.Forsyth };
100*74a4d8c2SCharles.Forsyth
101*74a4d8c2SCharles.Forsyth struct Buf
102*74a4d8c2SCharles.Forsyth {
103*74a4d8c2SCharles.Forsyth uchar* virt;
104*74a4d8c2SCharles.Forsyth ulong phys;
105*74a4d8c2SCharles.Forsyth uint nbytes;
106*74a4d8c2SCharles.Forsyth };
107*74a4d8c2SCharles.Forsyth
108*74a4d8c2SCharles.Forsyth struct IOstate
109*74a4d8c2SCharles.Forsyth {
110*74a4d8c2SCharles.Forsyth QLock;
111*74a4d8c2SCharles.Forsyth Lock ilock;
112*74a4d8c2SCharles.Forsyth Rendez vous;
113*74a4d8c2SCharles.Forsyth Chan *chan; /* chan of open */
114*74a4d8c2SCharles.Forsyth Dma* dma; /* dma chan, alloc on open, free on close */
115*74a4d8c2SCharles.Forsyth int bufinit; /* boolean, if buffers allocated */
116*74a4d8c2SCharles.Forsyth Buf buf[Nbuf]; /* buffers and queues */
117*74a4d8c2SCharles.Forsyth volatile Buf *current; /* next dma to finish */
118*74a4d8c2SCharles.Forsyth volatile Buf *next; /* next candidate for dma */
119*74a4d8c2SCharles.Forsyth volatile Buf *filling; /* buffer being filled */
120*74a4d8c2SCharles.Forsyth /* just be be cute (and to have defines like linux, a real operating system) */
121*74a4d8c2SCharles.Forsyth #define emptying filling
122*74a4d8c2SCharles.Forsyth };
123*74a4d8c2SCharles.Forsyth
124*74a4d8c2SCharles.Forsyth static struct
125*74a4d8c2SCharles.Forsyth {
126*74a4d8c2SCharles.Forsyth QLock;
127*74a4d8c2SCharles.Forsyth int amode; /* Aclosed/Aread/Awrite for /audio */
128*74a4d8c2SCharles.Forsyth int intr; /* boolean an interrupt has happened */
129*74a4d8c2SCharles.Forsyth int rivol[Nvol]; /* right/left input/output volumes */
130*74a4d8c2SCharles.Forsyth int livol[Nvol];
131*74a4d8c2SCharles.Forsyth int rovol[Nvol];
132*74a4d8c2SCharles.Forsyth int lovol[Nvol];
133*74a4d8c2SCharles.Forsyth uvlong totcount; /* how many bytes processed since open */
134*74a4d8c2SCharles.Forsyth vlong tottime; /* time at which totcount bytes were processed */
135*74a4d8c2SCharles.Forsyth int clockout; /* need steady output to provide input clock */
136*74a4d8c2SCharles.Forsyth IOstate i;
137*74a4d8c2SCharles.Forsyth IOstate o;
138*74a4d8c2SCharles.Forsyth } audio;
139*74a4d8c2SCharles.Forsyth
140*74a4d8c2SCharles.Forsyth static struct
141*74a4d8c2SCharles.Forsyth {
142*74a4d8c2SCharles.Forsyth ulong bytes;
143*74a4d8c2SCharles.Forsyth ulong totaldma;
144*74a4d8c2SCharles.Forsyth ulong idledma;
145*74a4d8c2SCharles.Forsyth ulong faildma;
146*74a4d8c2SCharles.Forsyth ulong samedma;
147*74a4d8c2SCharles.Forsyth } iostats;
148*74a4d8c2SCharles.Forsyth
149*74a4d8c2SCharles.Forsyth static struct
150*74a4d8c2SCharles.Forsyth {
151*74a4d8c2SCharles.Forsyth char* name;
152*74a4d8c2SCharles.Forsyth int flag;
153*74a4d8c2SCharles.Forsyth int ilval; /* initial values */
154*74a4d8c2SCharles.Forsyth int irval;
155*74a4d8c2SCharles.Forsyth } volumes[] =
156*74a4d8c2SCharles.Forsyth {
157*74a4d8c2SCharles.Forsyth [Vaudio] {"audio", Fout|Fmono, 80, 80},
158*74a4d8c2SCharles.Forsyth [Vmic] {"mic", Fin|Fmono, 0, 0},
159*74a4d8c2SCharles.Forsyth [Vtreb] {"treb", Fout|Fmono, 50, 50},
160*74a4d8c2SCharles.Forsyth [Vbass] {"bass", Fout|Fmono, 50, 50},
161*74a4d8c2SCharles.Forsyth [Vspeed] {"speed", Fin|Fout|Fmono, Speed, Speed},
162*74a4d8c2SCharles.Forsyth [Vfilter] {"filter", Fout|Fmono, 0, 0},
163*74a4d8c2SCharles.Forsyth [Vinvert] {"invert", Fin|Fout|Fmono, 0, 0},
164*74a4d8c2SCharles.Forsyth [Nvol] {0}
165*74a4d8c2SCharles.Forsyth };
166*74a4d8c2SCharles.Forsyth
167*74a4d8c2SCharles.Forsyth static void setreg(char *name, int val, int n);
168*74a4d8c2SCharles.Forsyth
169*74a4d8c2SCharles.Forsyth static char Emode[] = "illegal open mode";
170*74a4d8c2SCharles.Forsyth static char Evolume[] = "illegal volume specifier";
171*74a4d8c2SCharles.Forsyth
172*74a4d8c2SCharles.Forsyth static void
bufinit(IOstate * b)173*74a4d8c2SCharles.Forsyth bufinit(IOstate *b)
174*74a4d8c2SCharles.Forsyth {
175*74a4d8c2SCharles.Forsyth int i;
176*74a4d8c2SCharles.Forsyth
177*74a4d8c2SCharles.Forsyth if (debug) print("bufinit\n");
178*74a4d8c2SCharles.Forsyth for (i = 0; i < Nbuf; i++) {
179*74a4d8c2SCharles.Forsyth b->buf[i].virt = xspanalloc(Bufsize, CACHELINESZ, 0);
180*74a4d8c2SCharles.Forsyth b->buf[i].phys = PADDR(b->buf[i].virt);
181*74a4d8c2SCharles.Forsyth }
182*74a4d8c2SCharles.Forsyth b->bufinit = 1;
183*74a4d8c2SCharles.Forsyth };
184*74a4d8c2SCharles.Forsyth
185*74a4d8c2SCharles.Forsyth static void
setempty(IOstate * b)186*74a4d8c2SCharles.Forsyth setempty(IOstate *b)
187*74a4d8c2SCharles.Forsyth {
188*74a4d8c2SCharles.Forsyth int i;
189*74a4d8c2SCharles.Forsyth
190*74a4d8c2SCharles.Forsyth if (debug) print("setempty\n");
191*74a4d8c2SCharles.Forsyth for (i = 0; i < Nbuf; i++) {
192*74a4d8c2SCharles.Forsyth b->buf[i].nbytes = 0;
193*74a4d8c2SCharles.Forsyth }
194*74a4d8c2SCharles.Forsyth b->filling = b->buf;
195*74a4d8c2SCharles.Forsyth b->current = b->buf;
196*74a4d8c2SCharles.Forsyth b->next = b->buf;
197*74a4d8c2SCharles.Forsyth }
198*74a4d8c2SCharles.Forsyth
199*74a4d8c2SCharles.Forsyth static int
audioqnotempty(void * x)200*74a4d8c2SCharles.Forsyth audioqnotempty(void *x)
201*74a4d8c2SCharles.Forsyth {
202*74a4d8c2SCharles.Forsyth IOstate *s = x;
203*74a4d8c2SCharles.Forsyth
204*74a4d8c2SCharles.Forsyth return dmaidle(s->dma) || s->emptying != s->current;
205*74a4d8c2SCharles.Forsyth }
206*74a4d8c2SCharles.Forsyth
207*74a4d8c2SCharles.Forsyth static int
audioqnotfull(void * x)208*74a4d8c2SCharles.Forsyth audioqnotfull(void *x)
209*74a4d8c2SCharles.Forsyth {
210*74a4d8c2SCharles.Forsyth IOstate *s = x;
211*74a4d8c2SCharles.Forsyth
212*74a4d8c2SCharles.Forsyth return dmaidle(s->dma) || s->filling != s->current;
213*74a4d8c2SCharles.Forsyth }
214*74a4d8c2SCharles.Forsyth
215*74a4d8c2SCharles.Forsyth static void
audioreset(void)216*74a4d8c2SCharles.Forsyth audioreset(void)
217*74a4d8c2SCharles.Forsyth {
218*74a4d8c2SCharles.Forsyth /* Turn MCP operations off */
219*74a4d8c2SCharles.Forsyth MCPREG->mccr = 0;
220*74a4d8c2SCharles.Forsyth }
221*74a4d8c2SCharles.Forsyth
222*74a4d8c2SCharles.Forsyth uchar status0[1] = {0x22};
223*74a4d8c2SCharles.Forsyth uchar status1[1] = {0x80};
224*74a4d8c2SCharles.Forsyth uchar data00[1] = {0x00}; /* volume control, bits 0 – 5 */
225*74a4d8c2SCharles.Forsyth uchar data01[1] = {0x40};
226*74a4d8c2SCharles.Forsyth uchar data02[1] = {0x80};
227*74a4d8c2SCharles.Forsyth uchar data0e0[2] = {0xc0, 0xe0};
228*74a4d8c2SCharles.Forsyth uchar data0e1[2] = {0xc1, 0xe0};
229*74a4d8c2SCharles.Forsyth uchar data0e2[2] = {0xc2, 0xf2};
230*74a4d8c2SCharles.Forsyth /* there is no data0e3 */
231*74a4d8c2SCharles.Forsyth uchar data0e4[2] = {0xc4, 0xe0};
232*74a4d8c2SCharles.Forsyth uchar data0e5[2] = {0xc5, 0xe0};
233*74a4d8c2SCharles.Forsyth uchar data0e6[2] = {0xc6, 0xe3};
234*74a4d8c2SCharles.Forsyth
235*74a4d8c2SCharles.Forsyth static void
enable(void)236*74a4d8c2SCharles.Forsyth enable(void)
237*74a4d8c2SCharles.Forsyth {
238*74a4d8c2SCharles.Forsyth uchar data[1];
239*74a4d8c2SCharles.Forsyth int cs;
240*74a4d8c2SCharles.Forsyth
241*74a4d8c2SCharles.Forsyth L3init();
242*74a4d8c2SCharles.Forsyth
243*74a4d8c2SCharles.Forsyth PPCREG->ppar &= ~PPAR_SPR;
244*74a4d8c2SCharles.Forsyth
245*74a4d8c2SCharles.Forsyth /* external clock and ssp configured for current samples/sec */
246*74a4d8c2SCharles.Forsyth cs = archaudiospeed(audio.livol[Vspeed], 1);
247*74a4d8c2SCharles.Forsyth status0[0] = (status0[0] & ~(3<<4)) | (cs<<4);
248*74a4d8c2SCharles.Forsyth
249*74a4d8c2SCharles.Forsyth /* Enable the audio power */
250*74a4d8c2SCharles.Forsyth archaudiopower(1);
251*74a4d8c2SCharles.Forsyth // egpiobits(EGPIO_audio_ic_power | EGPIO_codec_reset, 1);
252*74a4d8c2SCharles.Forsyth
253*74a4d8c2SCharles.Forsyth /* Wait for the UDA1341 to wake up */
254*74a4d8c2SCharles.Forsyth delay(100);
255*74a4d8c2SCharles.Forsyth
256*74a4d8c2SCharles.Forsyth /* Reset the chip */
257*74a4d8c2SCharles.Forsyth data[0] = status0[0] | 1<<UdaStatusRST;
258*74a4d8c2SCharles.Forsyth L3write(UDA1341_L3Addr | UDA1341_STATUS, data, 1 );
259*74a4d8c2SCharles.Forsyth archcodecreset();
260*74a4d8c2SCharles.Forsyth
261*74a4d8c2SCharles.Forsyth /* write uda 1341 status[0] */
262*74a4d8c2SCharles.Forsyth L3write(UDA1341_L3Addr | UDA1341_STATUS, status0, 1 );
263*74a4d8c2SCharles.Forsyth L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
264*74a4d8c2SCharles.Forsyth L3write(UDA1341_L3Addr | UDA1341_DATA0, data02, 1);
265*74a4d8c2SCharles.Forsyth L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e2, 2);
266*74a4d8c2SCharles.Forsyth L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e6, 2 );
267*74a4d8c2SCharles.Forsyth
268*74a4d8c2SCharles.Forsyth if (debug) {
269*74a4d8c2SCharles.Forsyth print("enable: status0 = 0x%2.2ux\n", status0[0]);
270*74a4d8c2SCharles.Forsyth print("enable: status1 = 0x%2.2ux\n", status1[0]);
271*74a4d8c2SCharles.Forsyth print("enable: data02 = 0x%2.2ux\n", data02[0]);
272*74a4d8c2SCharles.Forsyth print("enable: data0e2 = 0x%4.4ux\n", data0e2[0] | data0e2[1]<<8);
273*74a4d8c2SCharles.Forsyth print("enable: data0e4 = 0x%4.4ux\n", data0e4[0] | data0e4[1]<<8);
274*74a4d8c2SCharles.Forsyth print("enable: data0e6 = 0x%4.4ux\n", data0e6[0] | data0e6[1]<<8);
275*74a4d8c2SCharles.Forsyth }
276*74a4d8c2SCharles.Forsyth }
277*74a4d8c2SCharles.Forsyth
278*74a4d8c2SCharles.Forsyth static void
disable(void)279*74a4d8c2SCharles.Forsyth disable(void)
280*74a4d8c2SCharles.Forsyth {
281*74a4d8c2SCharles.Forsyth SSPREG->sscr0 = 0x031f; /* disable */
282*74a4d8c2SCharles.Forsyth }
283*74a4d8c2SCharles.Forsyth
284*74a4d8c2SCharles.Forsyth static void
resetlevel(void)285*74a4d8c2SCharles.Forsyth resetlevel(void)
286*74a4d8c2SCharles.Forsyth {
287*74a4d8c2SCharles.Forsyth int i;
288*74a4d8c2SCharles.Forsyth
289*74a4d8c2SCharles.Forsyth for(i=0; volumes[i].name; i++) {
290*74a4d8c2SCharles.Forsyth audio.lovol[i] = volumes[i].ilval;
291*74a4d8c2SCharles.Forsyth audio.rovol[i] = volumes[i].irval;
292*74a4d8c2SCharles.Forsyth audio.livol[i] = volumes[i].ilval;
293*74a4d8c2SCharles.Forsyth audio.rivol[i] = volumes[i].irval;
294*74a4d8c2SCharles.Forsyth }
295*74a4d8c2SCharles.Forsyth }
296*74a4d8c2SCharles.Forsyth
297*74a4d8c2SCharles.Forsyth static void
mxvolume(void)298*74a4d8c2SCharles.Forsyth mxvolume(void) {
299*74a4d8c2SCharles.Forsyth int *left, *right;
300*74a4d8c2SCharles.Forsyth int cs;
301*74a4d8c2SCharles.Forsyth
302*74a4d8c2SCharles.Forsyth cs = archaudiospeed(audio.livol[Vspeed], 1);
303*74a4d8c2SCharles.Forsyth status0[0] = (status0[0] & ~(3<<4)) | (cs<<4);
304*74a4d8c2SCharles.Forsyth L3write(UDA1341_L3Addr | UDA1341_STATUS, status0, 1);
305*74a4d8c2SCharles.Forsyth if(debug)
306*74a4d8c2SCharles.Forsyth print("mxvolume: status0 = %2.2ux\n", status0[0]);
307*74a4d8c2SCharles.Forsyth if(audio.amode & Aread){
308*74a4d8c2SCharles.Forsyth left = audio.livol;
309*74a4d8c2SCharles.Forsyth right = audio.rivol;
310*74a4d8c2SCharles.Forsyth if (left[Vmic]+right[Vmic] == 0) {
311*74a4d8c2SCharles.Forsyth /* Turn on automatic gain control (AGC) */
312*74a4d8c2SCharles.Forsyth data0e4[1] |= 0x10;
313*74a4d8c2SCharles.Forsyth L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e4, 2 );
314*74a4d8c2SCharles.Forsyth } else {
315*74a4d8c2SCharles.Forsyth int v;
316*74a4d8c2SCharles.Forsyth /* Turn on manual gain control */
317*74a4d8c2SCharles.Forsyth v = ((left[Vmic]+right[Vmic])*0x7f/200)&0x7f;
318*74a4d8c2SCharles.Forsyth data0e4[1] &= ~0x13;
319*74a4d8c2SCharles.Forsyth data0e5[1] &= ~0x1f;
320*74a4d8c2SCharles.Forsyth data0e4[1] |= v & 0x3;
321*74a4d8c2SCharles.Forsyth data0e5[0] |= (v & 0x7c)<<6;
322*74a4d8c2SCharles.Forsyth data0e5[1] |= (v & 0x7c)>>2;
323*74a4d8c2SCharles.Forsyth L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e4, 2 );
324*74a4d8c2SCharles.Forsyth L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e5, 2 );
325*74a4d8c2SCharles.Forsyth }
326*74a4d8c2SCharles.Forsyth if (left[Vinvert]+right[Vinvert] == 0)
327*74a4d8c2SCharles.Forsyth status1[0] &= ~0x10;
328*74a4d8c2SCharles.Forsyth else
329*74a4d8c2SCharles.Forsyth status1[0] |= 0x10;
330*74a4d8c2SCharles.Forsyth L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
331*74a4d8c2SCharles.Forsyth if (debug) {
332*74a4d8c2SCharles.Forsyth print("mxvolume: status1 = 0x%2.2ux\n", status1[0]);
333*74a4d8c2SCharles.Forsyth print("mxvolume: data0e4 = 0x%4.4ux\n", data0e4[0]|data0e4[0]<<8);
334*74a4d8c2SCharles.Forsyth print("mxvolume: data0e5 = 0x%4.4ux\n", data0e5[0]|data0e5[0]<<8);
335*74a4d8c2SCharles.Forsyth }
336*74a4d8c2SCharles.Forsyth }
337*74a4d8c2SCharles.Forsyth if(audio.amode & Awrite){
338*74a4d8c2SCharles.Forsyth left = audio.lovol;
339*74a4d8c2SCharles.Forsyth right = audio.rovol;
340*74a4d8c2SCharles.Forsyth data00[0] &= ~0x3f;
341*74a4d8c2SCharles.Forsyth data00[0] |= ((200-left[Vaudio]-right[Vaudio])*0x3f/200)&0x3f;
342*74a4d8c2SCharles.Forsyth if (left[Vtreb]+right[Vtreb] <= 100
343*74a4d8c2SCharles.Forsyth && left[Vbass]+right[Vbass] <= 100)
344*74a4d8c2SCharles.Forsyth /* settings neutral */
345*74a4d8c2SCharles.Forsyth data02[0] &= ~0x03;
346*74a4d8c2SCharles.Forsyth else {
347*74a4d8c2SCharles.Forsyth data02[0] |= 0x03;
348*74a4d8c2SCharles.Forsyth data01[0] &= ~0x3f;
349*74a4d8c2SCharles.Forsyth data01[0] |= ((left[Vtreb]+right[Vtreb]-100)*0x3/100)&0x03;
350*74a4d8c2SCharles.Forsyth data01[0] |= (((left[Vbass]+right[Vbass]-100)*0xf/100)&0xf)<<2;
351*74a4d8c2SCharles.Forsyth }
352*74a4d8c2SCharles.Forsyth if (left[Vfilter]+right[Vfilter] == 0)
353*74a4d8c2SCharles.Forsyth data02[0] &= ~0x10;
354*74a4d8c2SCharles.Forsyth else
355*74a4d8c2SCharles.Forsyth data02[0]|= 0x10;
356*74a4d8c2SCharles.Forsyth if (left[Vinvert]+right[Vinvert] == 0)
357*74a4d8c2SCharles.Forsyth status1[0] &= ~0x8;
358*74a4d8c2SCharles.Forsyth else
359*74a4d8c2SCharles.Forsyth status1[0] |= 0x8;
360*74a4d8c2SCharles.Forsyth L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
361*74a4d8c2SCharles.Forsyth L3write(UDA1341_L3Addr | UDA1341_DATA0, data00, 1);
362*74a4d8c2SCharles.Forsyth L3write(UDA1341_L3Addr | UDA1341_DATA0, data01, 1);
363*74a4d8c2SCharles.Forsyth L3write(UDA1341_L3Addr | UDA1341_DATA0, data02, 1);
364*74a4d8c2SCharles.Forsyth if (debug) {
365*74a4d8c2SCharles.Forsyth print("mxvolume: status1 = 0x%2.2ux\n", status1[0]);
366*74a4d8c2SCharles.Forsyth print("mxvolume: data00 = 0x%2.2ux\n", data00[0]);
367*74a4d8c2SCharles.Forsyth print("mxvolume: data01 = 0x%2.2ux\n", data01[0]);
368*74a4d8c2SCharles.Forsyth print("mxvolume: data02 = 0x%2.2ux\n", data02[0]);
369*74a4d8c2SCharles.Forsyth }
370*74a4d8c2SCharles.Forsyth }
371*74a4d8c2SCharles.Forsyth }
372*74a4d8c2SCharles.Forsyth
373*74a4d8c2SCharles.Forsyth static void
setreg(char * name,int val,int n)374*74a4d8c2SCharles.Forsyth setreg(char *name, int val, int n)
375*74a4d8c2SCharles.Forsyth {
376*74a4d8c2SCharles.Forsyth uchar x[2];
377*74a4d8c2SCharles.Forsyth int i;
378*74a4d8c2SCharles.Forsyth
379*74a4d8c2SCharles.Forsyth if(strcmp(name, "pause") == 0){
380*74a4d8c2SCharles.Forsyth for(i = 0; i < n; i++)
381*74a4d8c2SCharles.Forsyth microdelay(val);
382*74a4d8c2SCharles.Forsyth return;
383*74a4d8c2SCharles.Forsyth }
384*74a4d8c2SCharles.Forsyth
385*74a4d8c2SCharles.Forsyth x[0] = val;
386*74a4d8c2SCharles.Forsyth x[1] = val>>8;
387*74a4d8c2SCharles.Forsyth
388*74a4d8c2SCharles.Forsyth switch(n){
389*74a4d8c2SCharles.Forsyth case 1:
390*74a4d8c2SCharles.Forsyth case 2:
391*74a4d8c2SCharles.Forsyth break;
392*74a4d8c2SCharles.Forsyth default:
393*74a4d8c2SCharles.Forsyth error("setreg");
394*74a4d8c2SCharles.Forsyth }
395*74a4d8c2SCharles.Forsyth
396*74a4d8c2SCharles.Forsyth if(strcmp(name, "status") == 0){
397*74a4d8c2SCharles.Forsyth L3write(UDA1341_L3Addr | UDA1341_STATUS, x, n);
398*74a4d8c2SCharles.Forsyth } else if(strcmp(name, "data0") == 0){
399*74a4d8c2SCharles.Forsyth L3write(UDA1341_L3Addr | UDA1341_DATA0, x, n);
400*74a4d8c2SCharles.Forsyth } else if(strcmp(name, "data1") == 0){
401*74a4d8c2SCharles.Forsyth L3write(UDA1341_L3Addr | UDA1341_DATA1, x, n);
402*74a4d8c2SCharles.Forsyth } else
403*74a4d8c2SCharles.Forsyth error("setreg");
404*74a4d8c2SCharles.Forsyth }
405*74a4d8c2SCharles.Forsyth
406*74a4d8c2SCharles.Forsyth static void
outenable(void)407*74a4d8c2SCharles.Forsyth outenable(void) {
408*74a4d8c2SCharles.Forsyth /* turn on DAC, set output gain switch */
409*74a4d8c2SCharles.Forsyth archaudioamp(1);
410*74a4d8c2SCharles.Forsyth archaudiomute(0);
411*74a4d8c2SCharles.Forsyth status1[0] |= 0x41;
412*74a4d8c2SCharles.Forsyth L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
413*74a4d8c2SCharles.Forsyth /* set volume */
414*74a4d8c2SCharles.Forsyth data00[0] |= 0xf;
415*74a4d8c2SCharles.Forsyth L3write(UDA1341_L3Addr | UDA1341_DATA0, data00, 1);
416*74a4d8c2SCharles.Forsyth if (debug) {
417*74a4d8c2SCharles.Forsyth print("outenable: status1 = 0x%2.2ux\n", status1[0]);
418*74a4d8c2SCharles.Forsyth print("outenable: data00 = 0x%2.2ux\n", data00[0]);
419*74a4d8c2SCharles.Forsyth }
420*74a4d8c2SCharles.Forsyth }
421*74a4d8c2SCharles.Forsyth
422*74a4d8c2SCharles.Forsyth static void
outdisable(void)423*74a4d8c2SCharles.Forsyth outdisable(void) {
424*74a4d8c2SCharles.Forsyth archaudiomute(1);
425*74a4d8c2SCharles.Forsyth dmastop(audio.o.dma);
426*74a4d8c2SCharles.Forsyth /* turn off DAC, clear output gain switch */
427*74a4d8c2SCharles.Forsyth archaudioamp(0);
428*74a4d8c2SCharles.Forsyth status1[0] &= ~0x41;
429*74a4d8c2SCharles.Forsyth L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
430*74a4d8c2SCharles.Forsyth if (debug) {
431*74a4d8c2SCharles.Forsyth print("outdisable: status1 = 0x%2.2ux\n", status1[0]);
432*74a4d8c2SCharles.Forsyth }
433*74a4d8c2SCharles.Forsyth // egpiobits(EGPIO_audio_power, 0);
434*74a4d8c2SCharles.Forsyth }
435*74a4d8c2SCharles.Forsyth
436*74a4d8c2SCharles.Forsyth static void
inenable(void)437*74a4d8c2SCharles.Forsyth inenable(void) {
438*74a4d8c2SCharles.Forsyth /* turn on ADC, set input gain switch */
439*74a4d8c2SCharles.Forsyth status1[0] |= 0x22;
440*74a4d8c2SCharles.Forsyth L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
441*74a4d8c2SCharles.Forsyth if (debug) {
442*74a4d8c2SCharles.Forsyth print("inenable: status1 = 0x%2.2ux\n", status1[0]);
443*74a4d8c2SCharles.Forsyth }
444*74a4d8c2SCharles.Forsyth }
445*74a4d8c2SCharles.Forsyth
446*74a4d8c2SCharles.Forsyth static void
indisable(void)447*74a4d8c2SCharles.Forsyth indisable(void) {
448*74a4d8c2SCharles.Forsyth dmastop(audio.i.dma);
449*74a4d8c2SCharles.Forsyth /* turn off ADC, clear input gain switch */
450*74a4d8c2SCharles.Forsyth status1[0] &= ~0x22;
451*74a4d8c2SCharles.Forsyth L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
452*74a4d8c2SCharles.Forsyth if (debug) {
453*74a4d8c2SCharles.Forsyth print("indisable: status1 = 0x%2.2ux\n", status1[0]);
454*74a4d8c2SCharles.Forsyth }
455*74a4d8c2SCharles.Forsyth }
456*74a4d8c2SCharles.Forsyth
457*74a4d8c2SCharles.Forsyth static void
sendaudio(IOstate * s)458*74a4d8c2SCharles.Forsyth sendaudio(IOstate *s) {
459*74a4d8c2SCharles.Forsyth /* interrupt routine calls this too */
460*74a4d8c2SCharles.Forsyth int n;
461*74a4d8c2SCharles.Forsyth
462*74a4d8c2SCharles.Forsyth if (debug > 1) print("#A: sendaudio\n");
463*74a4d8c2SCharles.Forsyth ilock(&s->ilock);
464*74a4d8c2SCharles.Forsyth while (s->next != s->filling) {
465*74a4d8c2SCharles.Forsyth assert(s->next->nbytes);
466*74a4d8c2SCharles.Forsyth if ((n = dmastart(s->dma, (void*)s->next->phys, s->next->nbytes)) == 0) {
467*74a4d8c2SCharles.Forsyth iostats.faildma++;
468*74a4d8c2SCharles.Forsyth break;
469*74a4d8c2SCharles.Forsyth }
470*74a4d8c2SCharles.Forsyth iostats.totaldma++;
471*74a4d8c2SCharles.Forsyth switch (n) {
472*74a4d8c2SCharles.Forsyth case 1:
473*74a4d8c2SCharles.Forsyth iostats.idledma++;
474*74a4d8c2SCharles.Forsyth break;
475*74a4d8c2SCharles.Forsyth case 3:
476*74a4d8c2SCharles.Forsyth iostats.faildma++;
477*74a4d8c2SCharles.Forsyth break;
478*74a4d8c2SCharles.Forsyth }
479*74a4d8c2SCharles.Forsyth if (debug) {
480*74a4d8c2SCharles.Forsyth if (debug > 1)
481*74a4d8c2SCharles.Forsyth print("dmastart @%p\n", s->next);
482*74a4d8c2SCharles.Forsyth else
483*74a4d8c2SCharles.Forsyth iprint("+");
484*74a4d8c2SCharles.Forsyth }
485*74a4d8c2SCharles.Forsyth s->next->nbytes = 0;
486*74a4d8c2SCharles.Forsyth s->next++;
487*74a4d8c2SCharles.Forsyth if (s->next == &s->buf[Nbuf])
488*74a4d8c2SCharles.Forsyth s->next = &s->buf[0];
489*74a4d8c2SCharles.Forsyth }
490*74a4d8c2SCharles.Forsyth iunlock(&s->ilock);
491*74a4d8c2SCharles.Forsyth }
492*74a4d8c2SCharles.Forsyth
493*74a4d8c2SCharles.Forsyth static void
recvaudio(IOstate * s)494*74a4d8c2SCharles.Forsyth recvaudio(IOstate *s) {
495*74a4d8c2SCharles.Forsyth /* interrupt routine calls this too */
496*74a4d8c2SCharles.Forsyth int n;
497*74a4d8c2SCharles.Forsyth
498*74a4d8c2SCharles.Forsyth if (debug > 1) print("#A: recvaudio\n");
499*74a4d8c2SCharles.Forsyth ilock(&s->ilock);
500*74a4d8c2SCharles.Forsyth while (s->next != s->emptying) {
501*74a4d8c2SCharles.Forsyth assert(s->next->nbytes == 0);
502*74a4d8c2SCharles.Forsyth if ((n = dmastart(s->dma, (void*)s->next->phys, Bufsize)) == 0) {
503*74a4d8c2SCharles.Forsyth iostats.faildma++;
504*74a4d8c2SCharles.Forsyth break;
505*74a4d8c2SCharles.Forsyth }
506*74a4d8c2SCharles.Forsyth iostats.totaldma++;
507*74a4d8c2SCharles.Forsyth switch (n) {
508*74a4d8c2SCharles.Forsyth case 1:
509*74a4d8c2SCharles.Forsyth iostats.idledma++;
510*74a4d8c2SCharles.Forsyth break;
511*74a4d8c2SCharles.Forsyth case 3:
512*74a4d8c2SCharles.Forsyth iostats.faildma++;
513*74a4d8c2SCharles.Forsyth break;
514*74a4d8c2SCharles.Forsyth }
515*74a4d8c2SCharles.Forsyth if (debug) {
516*74a4d8c2SCharles.Forsyth if (debug > 1)
517*74a4d8c2SCharles.Forsyth print("dmastart @%p\n", s->next);
518*74a4d8c2SCharles.Forsyth else
519*74a4d8c2SCharles.Forsyth iprint("+");
520*74a4d8c2SCharles.Forsyth }
521*74a4d8c2SCharles.Forsyth s->next++;
522*74a4d8c2SCharles.Forsyth if (s->next == &s->buf[Nbuf])
523*74a4d8c2SCharles.Forsyth s->next = &s->buf[0];
524*74a4d8c2SCharles.Forsyth }
525*74a4d8c2SCharles.Forsyth iunlock(&s->ilock);
526*74a4d8c2SCharles.Forsyth }
527*74a4d8c2SCharles.Forsyth
528*74a4d8c2SCharles.Forsyth static void
audiopower(int flag)529*74a4d8c2SCharles.Forsyth audiopower(int flag) {
530*74a4d8c2SCharles.Forsyth IOstate *s;
531*74a4d8c2SCharles.Forsyth
532*74a4d8c2SCharles.Forsyth if (debug) {
533*74a4d8c2SCharles.Forsyth iprint("audiopower %d\n", flag);
534*74a4d8c2SCharles.Forsyth }
535*74a4d8c2SCharles.Forsyth if (flag) {
536*74a4d8c2SCharles.Forsyth /* power on only when necessary */
537*74a4d8c2SCharles.Forsyth if (audio.amode) {
538*74a4d8c2SCharles.Forsyth archaudiopower(1);
539*74a4d8c2SCharles.Forsyth enable();
540*74a4d8c2SCharles.Forsyth if (audio.amode & Aread) {
541*74a4d8c2SCharles.Forsyth inenable();
542*74a4d8c2SCharles.Forsyth s = &audio.i;
543*74a4d8c2SCharles.Forsyth dmastop(s->dma);
544*74a4d8c2SCharles.Forsyth recvaudio(s);
545*74a4d8c2SCharles.Forsyth }
546*74a4d8c2SCharles.Forsyth if (audio.amode & Awrite) {
547*74a4d8c2SCharles.Forsyth outenable();
548*74a4d8c2SCharles.Forsyth s = &audio.o;
549*74a4d8c2SCharles.Forsyth dmastop(s->dma);
550*74a4d8c2SCharles.Forsyth sendaudio(s);
551*74a4d8c2SCharles.Forsyth }
552*74a4d8c2SCharles.Forsyth mxvolume();
553*74a4d8c2SCharles.Forsyth }
554*74a4d8c2SCharles.Forsyth } else {
555*74a4d8c2SCharles.Forsyth /* power off */
556*74a4d8c2SCharles.Forsyth if (audio.amode & Aread)
557*74a4d8c2SCharles.Forsyth indisable();
558*74a4d8c2SCharles.Forsyth if (audio.amode & Awrite)
559*74a4d8c2SCharles.Forsyth outdisable();
560*74a4d8c2SCharles.Forsyth disable();
561*74a4d8c2SCharles.Forsyth archaudiopower(0);
562*74a4d8c2SCharles.Forsyth }
563*74a4d8c2SCharles.Forsyth }
564*74a4d8c2SCharles.Forsyth
565*74a4d8c2SCharles.Forsyth static void
audiointr(void * x,ulong ndma)566*74a4d8c2SCharles.Forsyth audiointr(void *x, ulong ndma) {
567*74a4d8c2SCharles.Forsyth IOstate *s = x;
568*74a4d8c2SCharles.Forsyth
569*74a4d8c2SCharles.Forsyth if (debug) {
570*74a4d8c2SCharles.Forsyth if (debug > 1)
571*74a4d8c2SCharles.Forsyth iprint("#A: audio interrupt @%p\n", s->current);
572*74a4d8c2SCharles.Forsyth else
573*74a4d8c2SCharles.Forsyth iprint("-");
574*74a4d8c2SCharles.Forsyth }
575*74a4d8c2SCharles.Forsyth /* Only interrupt routine touches s->current */
576*74a4d8c2SCharles.Forsyth s->current++;
577*74a4d8c2SCharles.Forsyth if (s->current == &s->buf[Nbuf])
578*74a4d8c2SCharles.Forsyth s->current = &s->buf[0];
579*74a4d8c2SCharles.Forsyth if (ndma > 0) {
580*74a4d8c2SCharles.Forsyth if (s == &audio.o)
581*74a4d8c2SCharles.Forsyth sendaudio(s);
582*74a4d8c2SCharles.Forsyth else if (s == &audio.i)
583*74a4d8c2SCharles.Forsyth recvaudio(s);
584*74a4d8c2SCharles.Forsyth }
585*74a4d8c2SCharles.Forsyth wakeup(&s->vous);
586*74a4d8c2SCharles.Forsyth }
587*74a4d8c2SCharles.Forsyth
588*74a4d8c2SCharles.Forsyth static void
audioinit(void)589*74a4d8c2SCharles.Forsyth audioinit(void)
590*74a4d8c2SCharles.Forsyth {
591*74a4d8c2SCharles.Forsyth audio.amode = Aclosed;
592*74a4d8c2SCharles.Forsyth resetlevel();
593*74a4d8c2SCharles.Forsyth // powerenable(audiopower);
594*74a4d8c2SCharles.Forsyth }
595*74a4d8c2SCharles.Forsyth
596*74a4d8c2SCharles.Forsyth static Chan*
audioattach(char * param)597*74a4d8c2SCharles.Forsyth audioattach(char *param)
598*74a4d8c2SCharles.Forsyth {
599*74a4d8c2SCharles.Forsyth return devattach('A', param);
600*74a4d8c2SCharles.Forsyth }
601*74a4d8c2SCharles.Forsyth
602*74a4d8c2SCharles.Forsyth static Walkqid*
audiowalk(Chan * c,Chan * nc,char ** name,int nname)603*74a4d8c2SCharles.Forsyth audiowalk(Chan *c, Chan *nc, char **name, int nname)
604*74a4d8c2SCharles.Forsyth {
605*74a4d8c2SCharles.Forsyth return devwalk(c, nc, name, nname, audiodir, nelem(audiodir), devgen);
606*74a4d8c2SCharles.Forsyth }
607*74a4d8c2SCharles.Forsyth
608*74a4d8c2SCharles.Forsyth static int
audiostat(Chan * c,uchar * db,int n)609*74a4d8c2SCharles.Forsyth audiostat(Chan *c, uchar *db, int n)
610*74a4d8c2SCharles.Forsyth {
611*74a4d8c2SCharles.Forsyth return devstat(c, db, n, audiodir, nelem(audiodir), devgen);
612*74a4d8c2SCharles.Forsyth }
613*74a4d8c2SCharles.Forsyth
614*74a4d8c2SCharles.Forsyth static Chan*
audioopen(Chan * c,int mode)615*74a4d8c2SCharles.Forsyth audioopen(Chan *c, int mode)
616*74a4d8c2SCharles.Forsyth {
617*74a4d8c2SCharles.Forsyth IOstate *s;
618*74a4d8c2SCharles.Forsyth int omode = mode;
619*74a4d8c2SCharles.Forsyth
620*74a4d8c2SCharles.Forsyth switch((ulong)c->qid.path) {
621*74a4d8c2SCharles.Forsyth default:
622*74a4d8c2SCharles.Forsyth error(Eperm);
623*74a4d8c2SCharles.Forsyth break;
624*74a4d8c2SCharles.Forsyth
625*74a4d8c2SCharles.Forsyth case Qstatus:
626*74a4d8c2SCharles.Forsyth if((omode&7) != OREAD)
627*74a4d8c2SCharles.Forsyth error(Eperm);
628*74a4d8c2SCharles.Forsyth case Qvolume:
629*74a4d8c2SCharles.Forsyth case Qaudioctl:
630*74a4d8c2SCharles.Forsyth case Qdir:
631*74a4d8c2SCharles.Forsyth break;
632*74a4d8c2SCharles.Forsyth
633*74a4d8c2SCharles.Forsyth case Qaudio:
634*74a4d8c2SCharles.Forsyth omode = (omode & 0x7) + 1;
635*74a4d8c2SCharles.Forsyth if (omode & ~(Aread | Awrite))
636*74a4d8c2SCharles.Forsyth error(Ebadarg);
637*74a4d8c2SCharles.Forsyth qlock(&audio);
638*74a4d8c2SCharles.Forsyth if(audio.amode & omode){
639*74a4d8c2SCharles.Forsyth qunlock(&audio);
640*74a4d8c2SCharles.Forsyth error(Einuse);
641*74a4d8c2SCharles.Forsyth }
642*74a4d8c2SCharles.Forsyth enable();
643*74a4d8c2SCharles.Forsyth memset(&iostats, 0, sizeof(iostats));
644*74a4d8c2SCharles.Forsyth if (omode & Aread) {
645*74a4d8c2SCharles.Forsyth inenable();
646*74a4d8c2SCharles.Forsyth s = &audio.i;
647*74a4d8c2SCharles.Forsyth if(s->bufinit == 0)
648*74a4d8c2SCharles.Forsyth bufinit(s);
649*74a4d8c2SCharles.Forsyth setempty(s);
650*74a4d8c2SCharles.Forsyth s->emptying = &s->buf[Nbuf-1];
651*74a4d8c2SCharles.Forsyth s->chan = c;
652*74a4d8c2SCharles.Forsyth s->dma = dmasetup(DmaSSP, 1, 0, audiointr, (void*)s);
653*74a4d8c2SCharles.Forsyth audio.amode |= Aread;
654*74a4d8c2SCharles.Forsyth audio.clockout = 1;
655*74a4d8c2SCharles.Forsyth }
656*74a4d8c2SCharles.Forsyth if (omode & Awrite) {
657*74a4d8c2SCharles.Forsyth outenable();
658*74a4d8c2SCharles.Forsyth s = &audio.o;
659*74a4d8c2SCharles.Forsyth audio.amode |= Awrite;
660*74a4d8c2SCharles.Forsyth if(s->bufinit == 0)
661*74a4d8c2SCharles.Forsyth bufinit(s);
662*74a4d8c2SCharles.Forsyth setempty(s);
663*74a4d8c2SCharles.Forsyth s->chan = c;
664*74a4d8c2SCharles.Forsyth s->dma = dmasetup(DmaSSP, 0, 0, audiointr, (void*)s);
665*74a4d8c2SCharles.Forsyth audio.amode |= Awrite;
666*74a4d8c2SCharles.Forsyth }
667*74a4d8c2SCharles.Forsyth mxvolume();
668*74a4d8c2SCharles.Forsyth qunlock(&audio);
669*74a4d8c2SCharles.Forsyth if (debug) print("open done\n");
670*74a4d8c2SCharles.Forsyth break;
671*74a4d8c2SCharles.Forsyth }
672*74a4d8c2SCharles.Forsyth c = devopen(c, mode, audiodir, nelem(audiodir), devgen);
673*74a4d8c2SCharles.Forsyth c->mode = openmode(mode);
674*74a4d8c2SCharles.Forsyth c->flag |= COPEN;
675*74a4d8c2SCharles.Forsyth c->offset = 0;
676*74a4d8c2SCharles.Forsyth
677*74a4d8c2SCharles.Forsyth return c;
678*74a4d8c2SCharles.Forsyth }
679*74a4d8c2SCharles.Forsyth
680*74a4d8c2SCharles.Forsyth static void
audioclose(Chan * c)681*74a4d8c2SCharles.Forsyth audioclose(Chan *c)
682*74a4d8c2SCharles.Forsyth {
683*74a4d8c2SCharles.Forsyth IOstate *s;
684*74a4d8c2SCharles.Forsyth
685*74a4d8c2SCharles.Forsyth switch((ulong)c->qid.path) {
686*74a4d8c2SCharles.Forsyth default:
687*74a4d8c2SCharles.Forsyth error(Eperm);
688*74a4d8c2SCharles.Forsyth break;
689*74a4d8c2SCharles.Forsyth
690*74a4d8c2SCharles.Forsyth case Qdir:
691*74a4d8c2SCharles.Forsyth case Qvolume:
692*74a4d8c2SCharles.Forsyth case Qaudioctl:
693*74a4d8c2SCharles.Forsyth case Qstatus:
694*74a4d8c2SCharles.Forsyth break;
695*74a4d8c2SCharles.Forsyth
696*74a4d8c2SCharles.Forsyth case Qaudio:
697*74a4d8c2SCharles.Forsyth if (debug > 1) print("#A: close\n");
698*74a4d8c2SCharles.Forsyth if(c->flag & COPEN) {
699*74a4d8c2SCharles.Forsyth qlock(&audio);
700*74a4d8c2SCharles.Forsyth if(waserror()){
701*74a4d8c2SCharles.Forsyth qunlock(&audio);
702*74a4d8c2SCharles.Forsyth nexterror();
703*74a4d8c2SCharles.Forsyth }
704*74a4d8c2SCharles.Forsyth if (audio.o.chan == c) {
705*74a4d8c2SCharles.Forsyth /* closing the write end */
706*74a4d8c2SCharles.Forsyth audio.amode &= ~Awrite;
707*74a4d8c2SCharles.Forsyth s = &audio.o;
708*74a4d8c2SCharles.Forsyth qlock(s);
709*74a4d8c2SCharles.Forsyth if(waserror()){
710*74a4d8c2SCharles.Forsyth qunlock(s);
711*74a4d8c2SCharles.Forsyth nexterror();
712*74a4d8c2SCharles.Forsyth }
713*74a4d8c2SCharles.Forsyth if (s->filling->nbytes) {
714*74a4d8c2SCharles.Forsyth /* send remaining partial buffer */
715*74a4d8c2SCharles.Forsyth s->filling++;
716*74a4d8c2SCharles.Forsyth if (s->filling == &s->buf[Nbuf])
717*74a4d8c2SCharles.Forsyth s->filling = &s->buf[0];
718*74a4d8c2SCharles.Forsyth sendaudio(s);
719*74a4d8c2SCharles.Forsyth }
720*74a4d8c2SCharles.Forsyth dmawait(s->dma);
721*74a4d8c2SCharles.Forsyth outdisable();
722*74a4d8c2SCharles.Forsyth setempty(s);
723*74a4d8c2SCharles.Forsyth dmafree(s->dma);
724*74a4d8c2SCharles.Forsyth qunlock(s);
725*74a4d8c2SCharles.Forsyth poperror();
726*74a4d8c2SCharles.Forsyth }
727*74a4d8c2SCharles.Forsyth if (audio.i.chan == c) {
728*74a4d8c2SCharles.Forsyth /* closing the read end */
729*74a4d8c2SCharles.Forsyth audio.amode &= ~Aread;
730*74a4d8c2SCharles.Forsyth s = &audio.i;
731*74a4d8c2SCharles.Forsyth qlock(s);
732*74a4d8c2SCharles.Forsyth if(waserror()){
733*74a4d8c2SCharles.Forsyth qunlock(s);
734*74a4d8c2SCharles.Forsyth nexterror();
735*74a4d8c2SCharles.Forsyth }
736*74a4d8c2SCharles.Forsyth indisable();
737*74a4d8c2SCharles.Forsyth setempty(s);
738*74a4d8c2SCharles.Forsyth dmafree(s->dma);
739*74a4d8c2SCharles.Forsyth qunlock(s);
740*74a4d8c2SCharles.Forsyth poperror();
741*74a4d8c2SCharles.Forsyth }
742*74a4d8c2SCharles.Forsyth if (audio.amode == 0) {
743*74a4d8c2SCharles.Forsyth /* turn audio off */
744*74a4d8c2SCharles.Forsyth archaudiopower(0);
745*74a4d8c2SCharles.Forsyth }
746*74a4d8c2SCharles.Forsyth qunlock(&audio);
747*74a4d8c2SCharles.Forsyth poperror();
748*74a4d8c2SCharles.Forsyth if (debug) {
749*74a4d8c2SCharles.Forsyth print("total dmas: %lud\n", iostats.totaldma);
750*74a4d8c2SCharles.Forsyth print("dmas while idle: %lud\n", iostats.idledma);
751*74a4d8c2SCharles.Forsyth print("dmas while busy: %lud\n", iostats.faildma);
752*74a4d8c2SCharles.Forsyth print("out of order dma: %lud\n", iostats.samedma);
753*74a4d8c2SCharles.Forsyth }
754*74a4d8c2SCharles.Forsyth }
755*74a4d8c2SCharles.Forsyth break;
756*74a4d8c2SCharles.Forsyth }
757*74a4d8c2SCharles.Forsyth }
758*74a4d8c2SCharles.Forsyth
759*74a4d8c2SCharles.Forsyth static long
audioread(Chan * c,void * v,long n,vlong off)760*74a4d8c2SCharles.Forsyth audioread(Chan *c, void *v, long n, vlong off)
761*74a4d8c2SCharles.Forsyth {
762*74a4d8c2SCharles.Forsyth int liv, riv, lov, rov;
763*74a4d8c2SCharles.Forsyth long m, n0;
764*74a4d8c2SCharles.Forsyth char buf[300];
765*74a4d8c2SCharles.Forsyth int j;
766*74a4d8c2SCharles.Forsyth ulong offset = off;
767*74a4d8c2SCharles.Forsyth char *p;
768*74a4d8c2SCharles.Forsyth IOstate *s;
769*74a4d8c2SCharles.Forsyth
770*74a4d8c2SCharles.Forsyth n0 = n;
771*74a4d8c2SCharles.Forsyth p = v;
772*74a4d8c2SCharles.Forsyth switch((ulong)c->qid.path) {
773*74a4d8c2SCharles.Forsyth default:
774*74a4d8c2SCharles.Forsyth error(Eperm);
775*74a4d8c2SCharles.Forsyth break;
776*74a4d8c2SCharles.Forsyth
777*74a4d8c2SCharles.Forsyth case Qdir:
778*74a4d8c2SCharles.Forsyth return devdirread(c, p, n, audiodir, nelem(audiodir), devgen);
779*74a4d8c2SCharles.Forsyth
780*74a4d8c2SCharles.Forsyth case Qaudio:
781*74a4d8c2SCharles.Forsyth if (debug > 1) print("#A: read %ld\n", n);
782*74a4d8c2SCharles.Forsyth if((audio.amode & Aread) == 0)
783*74a4d8c2SCharles.Forsyth error(Emode);
784*74a4d8c2SCharles.Forsyth s = &audio.i;
785*74a4d8c2SCharles.Forsyth qlock(s);
786*74a4d8c2SCharles.Forsyth if(waserror()){
787*74a4d8c2SCharles.Forsyth qunlock(s);
788*74a4d8c2SCharles.Forsyth nexterror();
789*74a4d8c2SCharles.Forsyth }
790*74a4d8c2SCharles.Forsyth while(n > 0) {
791*74a4d8c2SCharles.Forsyth if(s->emptying->nbytes == 0) {
792*74a4d8c2SCharles.Forsyth if (debug > 1) print("#A: emptied @%p\n", s->emptying);
793*74a4d8c2SCharles.Forsyth recvaudio(s);
794*74a4d8c2SCharles.Forsyth s->emptying++;
795*74a4d8c2SCharles.Forsyth if (s->emptying == &s->buf[Nbuf])
796*74a4d8c2SCharles.Forsyth s->emptying = s->buf;
797*74a4d8c2SCharles.Forsyth }
798*74a4d8c2SCharles.Forsyth /* wait if dma in progress */
799*74a4d8c2SCharles.Forsyth while (!dmaidle(s->dma) && s->emptying == s->current) {
800*74a4d8c2SCharles.Forsyth if (debug > 1) print("#A: sleep\n");
801*74a4d8c2SCharles.Forsyth sleep(&s->vous, audioqnotempty, s);
802*74a4d8c2SCharles.Forsyth }
803*74a4d8c2SCharles.Forsyth
804*74a4d8c2SCharles.Forsyth m = Bufsize - s->emptying->nbytes;
805*74a4d8c2SCharles.Forsyth if(m > n)
806*74a4d8c2SCharles.Forsyth m = n;
807*74a4d8c2SCharles.Forsyth memmove(p, s->emptying->virt + s->emptying->nbytes, m);
808*74a4d8c2SCharles.Forsyth
809*74a4d8c2SCharles.Forsyth s->emptying->nbytes -= m;
810*74a4d8c2SCharles.Forsyth n -= m;
811*74a4d8c2SCharles.Forsyth p += m;
812*74a4d8c2SCharles.Forsyth }
813*74a4d8c2SCharles.Forsyth poperror();
814*74a4d8c2SCharles.Forsyth qunlock(s);
815*74a4d8c2SCharles.Forsyth break;
816*74a4d8c2SCharles.Forsyth break;
817*74a4d8c2SCharles.Forsyth
818*74a4d8c2SCharles.Forsyth case Qstatus:
819*74a4d8c2SCharles.Forsyth buf[0] = 0;
820*74a4d8c2SCharles.Forsyth snprint(buf, sizeof(buf), "bytes %llud\ntime %lld\n",
821*74a4d8c2SCharles.Forsyth audio.totcount, audio.tottime);
822*74a4d8c2SCharles.Forsyth return readstr(offset, p, n, buf);
823*74a4d8c2SCharles.Forsyth
824*74a4d8c2SCharles.Forsyth case Qvolume:
825*74a4d8c2SCharles.Forsyth case Qaudioctl:
826*74a4d8c2SCharles.Forsyth j = 0;
827*74a4d8c2SCharles.Forsyth buf[0] = 0;
828*74a4d8c2SCharles.Forsyth for(m=0; volumes[m].name; m++){
829*74a4d8c2SCharles.Forsyth liv = audio.livol[m];
830*74a4d8c2SCharles.Forsyth riv = audio.rivol[m];
831*74a4d8c2SCharles.Forsyth lov = audio.lovol[m];
832*74a4d8c2SCharles.Forsyth rov = audio.rovol[m];
833*74a4d8c2SCharles.Forsyth j += snprint(buf+j, sizeof(buf)-j, "%s", volumes[m].name);
834*74a4d8c2SCharles.Forsyth if((volumes[m].flag & Fmono) || liv==riv && lov==rov){
835*74a4d8c2SCharles.Forsyth if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov)
836*74a4d8c2SCharles.Forsyth j += snprint(buf+j, sizeof(buf)-j, " %d", liv);
837*74a4d8c2SCharles.Forsyth else{
838*74a4d8c2SCharles.Forsyth if(volumes[m].flag & Fin)
839*74a4d8c2SCharles.Forsyth j += snprint(buf+j, sizeof(buf)-j,
840*74a4d8c2SCharles.Forsyth " in %d", liv);
841*74a4d8c2SCharles.Forsyth if(volumes[m].flag & Fout)
842*74a4d8c2SCharles.Forsyth j += snprint(buf+j, sizeof(buf)-j,
843*74a4d8c2SCharles.Forsyth " out %d", lov);
844*74a4d8c2SCharles.Forsyth }
845*74a4d8c2SCharles.Forsyth }else{
846*74a4d8c2SCharles.Forsyth if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) &&
847*74a4d8c2SCharles.Forsyth liv==lov && riv==rov)
848*74a4d8c2SCharles.Forsyth j += snprint(buf+j, sizeof(buf)-j,
849*74a4d8c2SCharles.Forsyth " left %d right %d",
850*74a4d8c2SCharles.Forsyth liv, riv);
851*74a4d8c2SCharles.Forsyth else{
852*74a4d8c2SCharles.Forsyth if(volumes[m].flag & Fin)
853*74a4d8c2SCharles.Forsyth j += snprint(buf+j, sizeof(buf)-j,
854*74a4d8c2SCharles.Forsyth " in left %d right %d",
855*74a4d8c2SCharles.Forsyth liv, riv);
856*74a4d8c2SCharles.Forsyth if(volumes[m].flag & Fout)
857*74a4d8c2SCharles.Forsyth j += snprint(buf+j, sizeof(buf)-j,
858*74a4d8c2SCharles.Forsyth " out left %d right %d",
859*74a4d8c2SCharles.Forsyth lov, rov);
860*74a4d8c2SCharles.Forsyth }
861*74a4d8c2SCharles.Forsyth }
862*74a4d8c2SCharles.Forsyth j += snprint(buf+j, sizeof(buf)-j, "\n");
863*74a4d8c2SCharles.Forsyth }
864*74a4d8c2SCharles.Forsyth return readstr(offset, p, n, buf);
865*74a4d8c2SCharles.Forsyth }
866*74a4d8c2SCharles.Forsyth return n0-n;
867*74a4d8c2SCharles.Forsyth }
868*74a4d8c2SCharles.Forsyth
869*74a4d8c2SCharles.Forsyth static long
audiowrite(Chan * c,void * vp,long n,vlong)870*74a4d8c2SCharles.Forsyth audiowrite(Chan *c, void *vp, long n, vlong)
871*74a4d8c2SCharles.Forsyth {
872*74a4d8c2SCharles.Forsyth long m, n0;
873*74a4d8c2SCharles.Forsyth int i, nf, v, left, right, in, out;
874*74a4d8c2SCharles.Forsyth char buf[255], *field[Ncmd];
875*74a4d8c2SCharles.Forsyth char *p;
876*74a4d8c2SCharles.Forsyth IOstate *a;
877*74a4d8c2SCharles.Forsyth
878*74a4d8c2SCharles.Forsyth p = vp;
879*74a4d8c2SCharles.Forsyth n0 = n;
880*74a4d8c2SCharles.Forsyth switch((ulong)c->qid.path) {
881*74a4d8c2SCharles.Forsyth default:
882*74a4d8c2SCharles.Forsyth error(Eperm);
883*74a4d8c2SCharles.Forsyth break;
884*74a4d8c2SCharles.Forsyth
885*74a4d8c2SCharles.Forsyth case Qvolume:
886*74a4d8c2SCharles.Forsyth case Qaudioctl:
887*74a4d8c2SCharles.Forsyth v = Vaudio;
888*74a4d8c2SCharles.Forsyth left = 1;
889*74a4d8c2SCharles.Forsyth right = 1;
890*74a4d8c2SCharles.Forsyth in = 1;
891*74a4d8c2SCharles.Forsyth out = 1;
892*74a4d8c2SCharles.Forsyth if(n > sizeof(buf)-1)
893*74a4d8c2SCharles.Forsyth n = sizeof(buf)-1;
894*74a4d8c2SCharles.Forsyth memmove(buf, p, n);
895*74a4d8c2SCharles.Forsyth buf[n] = '\0';
896*74a4d8c2SCharles.Forsyth n = 0;
897*74a4d8c2SCharles.Forsyth
898*74a4d8c2SCharles.Forsyth nf = getfields(buf, field, Ncmd, 1, " \t\n");
899*74a4d8c2SCharles.Forsyth for(i = 0; i < nf; i++){
900*74a4d8c2SCharles.Forsyth /*
901*74a4d8c2SCharles.Forsyth * a number is volume
902*74a4d8c2SCharles.Forsyth */
903*74a4d8c2SCharles.Forsyth if(field[i][0] >= '0' && field[i][0] <= '9') {
904*74a4d8c2SCharles.Forsyth m = strtoul(field[i], 0, 10);
905*74a4d8c2SCharles.Forsyth if(v == Vspeed){
906*74a4d8c2SCharles.Forsyth if(archaudiospeed(m, 0) < 0)
907*74a4d8c2SCharles.Forsyth error(Evolume);
908*74a4d8c2SCharles.Forsyth }else
909*74a4d8c2SCharles.Forsyth if(m < 0 || m > 100)
910*74a4d8c2SCharles.Forsyth error(Evolume);
911*74a4d8c2SCharles.Forsyth if(left && out)
912*74a4d8c2SCharles.Forsyth audio.lovol[v] = m;
913*74a4d8c2SCharles.Forsyth if(left && in)
914*74a4d8c2SCharles.Forsyth audio.livol[v] = m;
915*74a4d8c2SCharles.Forsyth if(right && out)
916*74a4d8c2SCharles.Forsyth audio.rovol[v] = m;
917*74a4d8c2SCharles.Forsyth if(right && in)
918*74a4d8c2SCharles.Forsyth audio.rivol[v] = m;
919*74a4d8c2SCharles.Forsyth goto cont0;
920*74a4d8c2SCharles.Forsyth }
921*74a4d8c2SCharles.Forsyth if(strcmp(field[i], "rate") == 0)
922*74a4d8c2SCharles.Forsyth field[i] = "speed"; /* honestly ... */
923*74a4d8c2SCharles.Forsyth
924*74a4d8c2SCharles.Forsyth for(m=0; volumes[m].name; m++) {
925*74a4d8c2SCharles.Forsyth if(strcmp(field[i], volumes[m].name) == 0) {
926*74a4d8c2SCharles.Forsyth v = m;
927*74a4d8c2SCharles.Forsyth in = 1;
928*74a4d8c2SCharles.Forsyth out = 1;
929*74a4d8c2SCharles.Forsyth left = 1;
930*74a4d8c2SCharles.Forsyth right = 1;
931*74a4d8c2SCharles.Forsyth goto cont0;
932*74a4d8c2SCharles.Forsyth }
933*74a4d8c2SCharles.Forsyth }
934*74a4d8c2SCharles.Forsyth if(strcmp(field[i], "enc") == 0) {
935*74a4d8c2SCharles.Forsyth if(++i >= nf)
936*74a4d8c2SCharles.Forsyth error(Evolume);
937*74a4d8c2SCharles.Forsyth if(strcmp(field[i], "pcm") != 0)
938*74a4d8c2SCharles.Forsyth error(Evolume);
939*74a4d8c2SCharles.Forsyth goto cont0;
940*74a4d8c2SCharles.Forsyth }
941*74a4d8c2SCharles.Forsyth if(strcmp(field[i], "bits") == 0) {
942*74a4d8c2SCharles.Forsyth if(++i >= nf)
943*74a4d8c2SCharles.Forsyth error(Evolume);
944*74a4d8c2SCharles.Forsyth if(strtol(field[i], 0, 0) != 16)
945*74a4d8c2SCharles.Forsyth error(Evolume);
946*74a4d8c2SCharles.Forsyth goto cont0;
947*74a4d8c2SCharles.Forsyth }
948*74a4d8c2SCharles.Forsyth if(strcmp(field[i], "chans") == 0) {
949*74a4d8c2SCharles.Forsyth if(++i >= nf)
950*74a4d8c2SCharles.Forsyth error(Evolume);
951*74a4d8c2SCharles.Forsyth if(strtol(field[i], 0, 0) != 2)
952*74a4d8c2SCharles.Forsyth error(Evolume);
953*74a4d8c2SCharles.Forsyth goto cont0;
954*74a4d8c2SCharles.Forsyth }
955*74a4d8c2SCharles.Forsyth if(strcmp(field[i], "reset") == 0) {
956*74a4d8c2SCharles.Forsyth resetlevel();
957*74a4d8c2SCharles.Forsyth goto cont0;
958*74a4d8c2SCharles.Forsyth }
959*74a4d8c2SCharles.Forsyth if(strcmp(field[i], "debug") == 0) {
960*74a4d8c2SCharles.Forsyth debug = debug?0:1;
961*74a4d8c2SCharles.Forsyth goto cont0;
962*74a4d8c2SCharles.Forsyth }
963*74a4d8c2SCharles.Forsyth if(strcmp(field[i], "in") == 0) {
964*74a4d8c2SCharles.Forsyth in = 1;
965*74a4d8c2SCharles.Forsyth out = 0;
966*74a4d8c2SCharles.Forsyth goto cont0;
967*74a4d8c2SCharles.Forsyth }
968*74a4d8c2SCharles.Forsyth if(strcmp(field[i], "out") == 0) {
969*74a4d8c2SCharles.Forsyth in = 0;
970*74a4d8c2SCharles.Forsyth out = 1;
971*74a4d8c2SCharles.Forsyth goto cont0;
972*74a4d8c2SCharles.Forsyth }
973*74a4d8c2SCharles.Forsyth if(strcmp(field[i], "left") == 0) {
974*74a4d8c2SCharles.Forsyth left = 1;
975*74a4d8c2SCharles.Forsyth right = 0;
976*74a4d8c2SCharles.Forsyth goto cont0;
977*74a4d8c2SCharles.Forsyth }
978*74a4d8c2SCharles.Forsyth if(strcmp(field[i], "right") == 0) {
979*74a4d8c2SCharles.Forsyth left = 0;
980*74a4d8c2SCharles.Forsyth right = 1;
981*74a4d8c2SCharles.Forsyth goto cont0;
982*74a4d8c2SCharles.Forsyth }
983*74a4d8c2SCharles.Forsyth if(strcmp(field[i], "reg") == 0) {
984*74a4d8c2SCharles.Forsyth if(nf < 3)
985*74a4d8c2SCharles.Forsyth error(Evolume);
986*74a4d8c2SCharles.Forsyth setreg(field[1], atoi(field[2]), nf == 4 ? atoi(field[3]):1);
987*74a4d8c2SCharles.Forsyth return n0;
988*74a4d8c2SCharles.Forsyth }
989*74a4d8c2SCharles.Forsyth error(Evolume);
990*74a4d8c2SCharles.Forsyth break;
991*74a4d8c2SCharles.Forsyth cont0:;
992*74a4d8c2SCharles.Forsyth }
993*74a4d8c2SCharles.Forsyth mxvolume();
994*74a4d8c2SCharles.Forsyth break;
995*74a4d8c2SCharles.Forsyth
996*74a4d8c2SCharles.Forsyth case Qaudio:
997*74a4d8c2SCharles.Forsyth if (debug > 1) print("#A: write %ld\n", n);
998*74a4d8c2SCharles.Forsyth if((audio.amode & Awrite) == 0)
999*74a4d8c2SCharles.Forsyth error(Emode);
1000*74a4d8c2SCharles.Forsyth a = &audio.o;
1001*74a4d8c2SCharles.Forsyth qlock(a);
1002*74a4d8c2SCharles.Forsyth if(waserror()){
1003*74a4d8c2SCharles.Forsyth qunlock(a);
1004*74a4d8c2SCharles.Forsyth nexterror();
1005*74a4d8c2SCharles.Forsyth }
1006*74a4d8c2SCharles.Forsyth while(n > 0) {
1007*74a4d8c2SCharles.Forsyth /* wait if dma in progress */
1008*74a4d8c2SCharles.Forsyth while (!dmaidle(a->dma) && a->filling == a->current) {
1009*74a4d8c2SCharles.Forsyth if (debug > 1) print("#A: sleep\n");
1010*74a4d8c2SCharles.Forsyth sleep(&a->vous, audioqnotfull, a);
1011*74a4d8c2SCharles.Forsyth }
1012*74a4d8c2SCharles.Forsyth
1013*74a4d8c2SCharles.Forsyth m = Bufsize - a->filling->nbytes;
1014*74a4d8c2SCharles.Forsyth if(m > n)
1015*74a4d8c2SCharles.Forsyth m = n;
1016*74a4d8c2SCharles.Forsyth memmove(a->filling->virt + a->filling->nbytes, p, m);
1017*74a4d8c2SCharles.Forsyth
1018*74a4d8c2SCharles.Forsyth a->filling->nbytes += m;
1019*74a4d8c2SCharles.Forsyth n -= m;
1020*74a4d8c2SCharles.Forsyth p += m;
1021*74a4d8c2SCharles.Forsyth if(a->filling->nbytes >= Bufsize) {
1022*74a4d8c2SCharles.Forsyth if (debug > 1) print("#A: filled @%p\n", a->filling);
1023*74a4d8c2SCharles.Forsyth a->filling++;
1024*74a4d8c2SCharles.Forsyth if (a->filling == &a->buf[Nbuf])
1025*74a4d8c2SCharles.Forsyth a->filling = a->buf;
1026*74a4d8c2SCharles.Forsyth sendaudio(a);
1027*74a4d8c2SCharles.Forsyth }
1028*74a4d8c2SCharles.Forsyth }
1029*74a4d8c2SCharles.Forsyth poperror();
1030*74a4d8c2SCharles.Forsyth qunlock(a);
1031*74a4d8c2SCharles.Forsyth break;
1032*74a4d8c2SCharles.Forsyth }
1033*74a4d8c2SCharles.Forsyth return n0 - n;
1034*74a4d8c2SCharles.Forsyth }
1035*74a4d8c2SCharles.Forsyth
1036*74a4d8c2SCharles.Forsyth Dev audiodevtab = {
1037*74a4d8c2SCharles.Forsyth 'A',
1038*74a4d8c2SCharles.Forsyth "audio",
1039*74a4d8c2SCharles.Forsyth
1040*74a4d8c2SCharles.Forsyth audioreset,
1041*74a4d8c2SCharles.Forsyth audioinit,
1042*74a4d8c2SCharles.Forsyth devshutdown,
1043*74a4d8c2SCharles.Forsyth audioattach,
1044*74a4d8c2SCharles.Forsyth audiowalk,
1045*74a4d8c2SCharles.Forsyth audiostat,
1046*74a4d8c2SCharles.Forsyth audioopen,
1047*74a4d8c2SCharles.Forsyth devcreate,
1048*74a4d8c2SCharles.Forsyth audioclose,
1049*74a4d8c2SCharles.Forsyth audioread,
1050*74a4d8c2SCharles.Forsyth devbread,
1051*74a4d8c2SCharles.Forsyth audiowrite,
1052*74a4d8c2SCharles.Forsyth devbwrite,
1053*74a4d8c2SCharles.Forsyth devremove,
1054*74a4d8c2SCharles.Forsyth devwstat,
1055*74a4d8c2SCharles.Forsyth audiopower,
1056*74a4d8c2SCharles.Forsyth };
1057