]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/devaudio.c
pc kernel: fix wrong simd exception mask (fixes go bootstrap)
[plan9front.git] / sys / src / 9 / port / devaudio.c
1 #include        "u.h"
2 #include        "../port/lib.h"
3 #include        "mem.h"
4 #include        "dat.h"
5 #include        "fns.h"
6 #include        "io.h"
7 #include        "../port/error.h"
8 #include        "../port/audioif.h"
9
10 typedef struct Audioprobe Audioprobe;
11 typedef struct Audiochan Audiochan;
12
13 struct Audioprobe
14 {
15         char *name;
16         int (*probe)(Audio*);
17 };
18
19 struct Audiochan
20 {
21         QLock;
22
23         Chan *owner;
24         Audio *adev;
25
26         char *data;
27         char buf[4000+1];
28 };
29
30 enum {
31         Qdir = 0,
32         Qaudio,
33         Qaudioctl,
34         Qaudiostat,
35         Qvolume,
36 };
37
38 static Dirtab audiodir[] = {
39         ".",    {Qdir, 0, QTDIR},       0,      DMDIR|0555,
40         "audio",        {Qaudio},       0,      0666,
41         "audioctl",     {Qaudioctl},    0,      0222,
42         "audiostat",    {Qaudiostat},   0,      0444,
43         "volume",       {Qvolume},      0,      0666,
44 };
45
46
47 static int naudioprobes;
48 static Audioprobe audioprobes[16];
49 static Audio *audiodevs;
50
51 static char Evolume[] = "illegal volume specifier";
52 static char Ebusy[] = "device is busy";
53
54 void
55 addaudiocard(char *name, int (*probefn)(Audio *))
56 {
57         Audioprobe *probe;
58
59         if(naudioprobes >= nelem(audioprobes))
60                 return;
61
62         probe = &audioprobes[naudioprobes++];
63         probe->name = name;
64         probe->probe = probefn;
65 }
66
67 static void
68 audioreset(void)
69 {
70         int i, ctlrno = 0;
71         Audio **pp;
72         Audioprobe *probe;
73
74         pp = &audiodevs;
75         *pp = malloc(sizeof(Audio));
76
77         for(i=0; i<naudioprobes; i++){
78                 probe = &audioprobes[i];
79
80                 for(;;){
81                         if(*pp == nil){
82                                 print("audio: no memory\n");
83                                 break;
84                         }
85                         memset(*pp, 0, sizeof(Audio));
86                         (*pp)->ctlrno = ctlrno;
87                         (*pp)->name = probe->name;
88                         if(probe->probe(*pp))
89                                 break;
90
91                         ctlrno++;
92                         pp = &(*pp)->next;
93                         *pp = malloc(sizeof(Audio));
94                 }
95         }
96
97         free(*pp);
98         *pp = nil;
99 }
100
101 static Audiochan*
102 audioclone(Chan *c, Audio *adev)
103 {
104         Audiochan *ac;
105
106         ac = malloc(sizeof(Audiochan));
107         if(ac == nil){
108                 cclose(c);
109                 return nil;
110         }
111
112         c->aux = ac;
113         ac->owner = c;
114         ac->adev = adev;
115         ac->data = nil;
116
117         return ac;
118 }
119
120 static Chan*
121 audioattach(char *spec)
122 {
123         static int attached = 0;
124         Audiochan *ac;
125         Audio *adev;
126         Chan *c;
127         int i;
128
129         if(spec != nil && *spec != '\0')
130                 i = strtol(spec, 0, 10);
131         else
132                 i = 0;
133         for(adev = audiodevs; adev; adev = adev->next)
134                 if(adev->ctlrno == i)
135                         break;
136         if(adev == nil)
137                 error(Enodev);
138
139         c = devattach('A', spec);
140         c->qid.path = Qdir;
141
142         if((ac = audioclone(c, adev)) == nil)
143                 error(Enomem);
144
145         i = 1<<adev->ctlrno;
146         if((attached & i) == 0){
147                 static char *settings[] = {
148                         "speed 44100",
149                         "delay 1764",   /* 40 ms */
150                         "master 100",
151                         "audio 100",
152                         "head 100",
153                         "recgain 0",
154                 };
155
156                 attached |= i;
157                 for(i=0; i<nelem(settings) && adev->volwrite; i++){
158                         strcpy(ac->buf, settings[i]);
159                         if(!waserror()){
160                                 adev->volwrite(adev, ac->buf, strlen(ac->buf), 0);
161                                 poperror();
162                         }
163                 }
164         }
165
166         return c;
167 }
168
169 static Chan*
170 audioopen(Chan *c, int omode)
171 {
172         Audiochan *ac;
173         Audio *adev;
174         int mode;
175
176         ac = c->aux;
177         adev = ac->adev;
178         if(c->qid.path == Qaudio){
179                 mode = openmode(omode);
180                 if(waserror()){
181                         if(mode == OREAD || mode == ORDWR)
182                                 decref(&adev->audioopenr);
183                         nexterror();
184                 }
185                 if(mode == OREAD || mode == ORDWR)
186                         if(incref(&adev->audioopenr) != 1)
187                                 error(Ebusy);
188
189                 if(waserror()){
190                         if(mode == OWRITE || mode == ORDWR)
191                                 decref(&adev->audioopenw);
192                         nexterror();
193                 }
194                 if(mode == OWRITE || mode == ORDWR)
195                         if(incref(&adev->audioopenw) != 1)
196                                 error(Ebusy);
197
198                 c = devopen(c, omode, audiodir, nelem(audiodir), devgen);
199                 poperror();
200                 poperror();
201                 return c;
202         }
203         return devopen(c, omode, audiodir, nelem(audiodir), devgen);
204 }
205
206 static long
207 audioread(Chan *c, void *a, long n, vlong off)
208 {
209         Audiochan *ac;
210         Audio *adev;
211         long (*fn)(Audio *, void *, long, vlong);
212
213         ac = c->aux;
214         adev = ac->adev;
215
216         fn = nil;
217         switch((ulong)c->qid.path){
218         case Qdir:
219                 audiodir[Qaudio].length = adev->buffered ? adev->buffered(adev) : 0;
220                 return devdirread(c, a, n, audiodir, nelem(audiodir), devgen);
221         case Qaudio:
222                 fn = adev->read;
223                 break;
224         case Qaudiostat:
225                 fn = adev->status;
226                 break;
227         case Qvolume:
228                 fn = adev->volread;
229                 break;
230         }
231         if(fn == nil)
232                 error(Egreg);
233
234         eqlock(ac);
235         if(waserror()){
236                 qunlock(ac);
237                 nexterror();
238         }
239         switch((ulong)c->qid.path){
240         case Qaudiostat:
241         case Qvolume:
242                 /* generate the text on first read */
243                 if(ac->data == nil || off == 0){
244                         long l;
245
246                         ac->data = nil;
247                         l = fn(adev, ac->buf, sizeof(ac->buf)-1, 0);
248                         if(l < 0)
249                                 l = 0;
250                         ac->buf[l] = 0;
251                         ac->data = ac->buf;
252                 }
253                 /* then serve all requests from buffer */
254                 n = readstr(off, a, n, ac->data);
255                 break;
256
257         default:
258                 n = fn(adev, a, n, off);
259         }
260         qunlock(ac);
261         poperror();
262         return n;
263 }
264
265 static long
266 audiowrite(Chan *c, void *a, long n, vlong off)
267 {
268         Audiochan *ac;
269         Audio *adev;
270         long (*fn)(Audio *, void *, long, vlong);
271
272         ac = c->aux;
273         adev = ac->adev;
274
275         fn = nil;
276         switch((ulong)c->qid.path){
277         case Qaudio:
278                 fn = adev->write;
279                 break;
280         case Qaudioctl:
281                 fn = adev->ctl;
282                 break;
283         case Qvolume:
284                 fn = adev->volwrite;
285                 break;
286         }
287         if(fn == nil)
288                 error(Egreg);
289
290         eqlock(ac);
291         if(waserror()){
292                 qunlock(ac);
293                 nexterror();
294         }
295         switch((ulong)c->qid.path){
296         case Qaudioctl:
297         case Qvolume:
298                 if(n >= sizeof(ac->buf))
299                         error(Etoobig);
300
301                 /* copy data to audiochan buffer so it can be modified */
302                 ac->data = nil;
303                 memmove(ac->buf, a, n);
304                 ac->buf[n] = 0;
305                 a = ac->buf;
306                 off = 0;
307         }
308         n = fn(adev, a, n, off);
309         qunlock(ac);
310         poperror();
311         return n;
312 }
313
314 static void
315 audioclose(Chan *c)
316 {
317         Audiochan *ac;
318         Audio *adev;
319
320         ac = c->aux;
321         adev = ac->adev;
322         if((c->qid.path == Qaudio) && (c->flag & COPEN)){
323                 if(adev->close){
324                         if(!waserror()){
325                                 adev->close(adev, c->mode);
326                                 poperror();
327                         }
328                 }
329                 if(c->mode == OWRITE || c->mode == ORDWR)
330                         decref(&adev->audioopenw);
331                 if(c->mode == OREAD || c->mode == ORDWR)
332                         decref(&adev->audioopenr);
333         }
334         if(ac->owner == c){
335                 ac->owner = nil;
336                 c->aux = nil;
337                 free(ac);
338         }
339 }
340
341 static Walkqid*
342 audiowalk(Chan *c, Chan *nc, char **name, int nname)
343 {
344         Audiochan *ac;
345         Audio *adev;
346         Walkqid *wq;
347
348         ac = c->aux;
349         adev = ac->adev;
350         wq = devwalk(c, nc, name, nname, audiodir, nelem(audiodir), devgen);
351         if(wq && wq->clone){
352                 if(audioclone(wq->clone, adev) == nil){
353                         free(wq);
354                         wq = nil;
355                 }
356         }
357         return wq;
358 }
359
360 static int
361 audiostat(Chan *c, uchar *dp, int n)
362 {
363         Audiochan *ac;
364         Audio *adev;
365
366         ac = c->aux;
367         adev = ac->adev;
368         if((ulong)c->qid.path == Qaudio)
369                 audiodir[Qaudio].length = adev->buffered ? adev->buffered(adev) : 0;
370         return devstat(c, dp, n, audiodir, nelem(audiodir), devgen);
371 }
372
373 /*
374  * audioread() made sure the buffer is big enougth so a full volume
375  * table can be serialized in one pass.
376  */
377 long
378 genaudiovolread(Audio *adev, void *a, long n, vlong,
379         Volume *vol, int (*volget)(Audio *, int, int *), ulong caps)
380 {
381         int i, j, r, v[2];
382         char *p, *e;
383
384         p = a;
385         e = p + n;
386         for(i = 0; vol[i].name != 0; ++i){
387                 if(vol[i].cap && (vol[i].cap & caps) == 0)
388                         continue;
389                 v[0] = 0;
390                 v[1] = 0;
391                 if((*volget)(adev, i, v) != 0)
392                         continue;
393                 if(vol[i].type == Absolute)
394                         p += snprint(p, e - p, "%s %d\n", vol[i].name, v[0]);
395                 else {
396                         r = abs(vol[i].range);
397                         if(r == 0)
398                                 continue;
399                         for(j=0; j<2; j++){
400                                 if(v[j] < 0)
401                                         v[j] = 0;
402                                 if(v[j] > r)
403                                         v[j] = r;
404                                 if(vol[i].range < 0)
405                                         v[j] = r - v[j];
406                                 v[j] = (v[j]*100)/r;
407                         }
408                         switch(vol[i].type){
409                         case Left:
410                                 p += snprint(p, e - p, "%s %d\n", vol[i].name, v[0]);
411                                 break;
412                         case Right:
413                                 p += snprint(p, e - p, "%s %d\n", vol[i].name, v[1]);
414                                 break;
415                         case Stereo:
416                                 p += snprint(p, e - p, "%s %d %d\n", vol[i].name, v[0], v[1]);
417                                 break;
418                         }
419                 }
420         }
421
422         return p - (char*)a;
423 }
424
425 /*
426  * genaudiovolwrite modifies the buffer that gets passed to it. this
427  * is ok as long as it is called from inside Audio.volwrite() because
428  * audiowrite() copies the data to Audiochan.buf[] and inserts a
429  * terminating \0 byte before calling Audio.volwrite().
430  */
431 long
432 genaudiovolwrite(Audio *adev, void *a, long n, vlong,
433         Volume *vol, int (*volset)(Audio *, int, int *), ulong caps)
434 {
435         int ntok, i, j, r, v[2];
436         char *p, *e, *x, *tok[4];
437
438         p = a;
439         e = p + n;
440
441         for(;p < e; p = x){
442                 if(x = strchr(p, '\n'))
443                         *x++ = 0;
444                 else
445                         x = e;
446                 ntok = tokenize(p, tok, 4);
447                 if(ntok <= 0)
448                         continue;
449                 if(ntok == 1){
450                         tok[1] = tok[0];
451                         tok[0] = "master";
452                         ntok = 2;
453                 }
454                 for(i = 0; vol[i].name != 0; i++){
455                         if(vol[i].cap && (vol[i].cap & caps) == 0)
456                                 continue;
457                         if(cistrcmp(vol[i].name, tok[0]))
458                                 continue;
459         
460                         if((ntok>2) && (!cistrcmp(tok[1], "out") || !cistrcmp(tok[1], "in")))
461                                 memmove(tok+1, tok+2, --ntok);
462
463                         v[0] = 0;
464                         v[1] = 0;
465                         if(ntok > 1)
466                                 v[0] = v[1] = atoi(tok[1]);
467                         if(ntok > 2)
468                                 v[1] = atoi(tok[2]);
469                         if(vol[i].type == Absolute)
470                                 (*volset)(adev, i, v);
471                         else {
472                                 r = abs(vol[i].range);
473                                 for(j=0; j<2; j++){
474                                         v[j] = (50+(v[j]*r))/100;
475                                         if(v[j] < 0)
476                                                 v[j] = 0;
477                                         if(v[j] > r)
478                                                 v[j] = r;
479                                         if(vol[i].range < 0)
480                                                 v[j] = r - v[j];
481                                 }
482                                 (*volset)(adev, i, v);
483                         }
484                         break;
485                 }
486                 if(vol[i].name == nil)
487                         error(Evolume);
488         }
489
490         return n;
491 }
492
493 Dev audiodevtab = {
494         'A',
495         "audio",
496         audioreset,
497         devinit,
498         devshutdown,
499         audioattach,
500         audiowalk,
501         audiostat,
502         audioopen,
503         devcreate,
504         audioclose,
505         audioread,
506         devbread,
507         audiowrite,
508         devbwrite,
509         devremove,
510         devwstat,
511 };