]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/devaudio.c
devsd: fix possible sdbio() race with inquiry data changing (due to ahci hotplug)
[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[1024+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 882",    /* 20 ms */
150                         "master 100",
151                         "audio 100",
152                         "head 100",
153                 };
154
155                 attached |= i;
156                 for(i=0; i<nelem(settings) && adev->volwrite; i++){
157                         strcpy(ac->buf, settings[i]);
158                         if(!waserror()){
159                                 adev->volwrite(adev, ac->buf, strlen(ac->buf), 0);
160                                 poperror();
161                         }
162                 }
163         }
164
165         return c;
166 }
167
168 static Chan*
169 audioopen(Chan *c, int omode)
170 {
171         Audiochan *ac;
172         Audio *adev;
173
174         ac = c->aux;
175         adev = ac->adev;
176         if((c->qid.path == Qaudio) && (incref(&adev->audioopen) != 1)){
177                 decref(&adev->audioopen);
178                 error(Ebusy);
179         }
180         return devopen(c, omode, audiodir, nelem(audiodir), devgen);
181 }
182
183 static long
184 audioread(Chan *c, void *a, long n, vlong off)
185 {
186         Audiochan *ac;
187         Audio *adev;
188         long (*fn)(Audio *, void *, long, vlong);
189
190         ac = c->aux;
191         adev = ac->adev;
192
193         fn = nil;
194         switch((ulong)c->qid.path){
195         case Qdir:
196                 audiodir[Qaudio].length = adev->buffered ? adev->buffered(adev) : 0;
197                 return devdirread(c, a, n, audiodir, nelem(audiodir), devgen);
198         case Qaudio:
199                 fn = adev->read;
200                 break;
201         case Qaudiostat:
202                 fn = adev->status;
203                 break;
204         case Qvolume:
205                 fn = adev->volread;
206                 break;
207         }
208         if(fn == nil)
209                 error(Egreg);
210
211         eqlock(ac);
212         if(waserror()){
213                 qunlock(ac);
214                 nexterror();
215         }
216         switch((ulong)c->qid.path){
217         case Qaudiostat:
218         case Qvolume:
219                 /* generate the text on first read */
220                 if(ac->data == nil || off == 0){
221                         long l;
222
223                         ac->data = nil;
224                         l = fn(adev, ac->buf, sizeof(ac->buf)-1, 0);
225                         if(l < 0)
226                                 l = 0;
227                         ac->buf[l] = 0;
228                         ac->data = ac->buf;
229                 }
230                 /* then serve all requests from buffer */
231                 n = readstr(off, a, n, ac->data);
232                 break;
233
234         default:
235                 n = fn(adev, a, n, off);
236         }
237         qunlock(ac);
238         poperror();
239         return n;
240 }
241
242 static long
243 audiowrite(Chan *c, void *a, long n, vlong off)
244 {
245         Audiochan *ac;
246         Audio *adev;
247         long (*fn)(Audio *, void *, long, vlong);
248
249         ac = c->aux;
250         adev = ac->adev;
251
252         fn = nil;
253         switch((ulong)c->qid.path){
254         case Qaudio:
255                 fn = adev->write;
256                 break;
257         case Qaudioctl:
258                 fn = adev->ctl;
259                 break;
260         case Qvolume:
261                 fn = adev->volwrite;
262                 break;
263         }
264         if(fn == nil)
265                 error(Egreg);
266
267         eqlock(ac);
268         if(waserror()){
269                 qunlock(ac);
270                 nexterror();
271         }
272         switch((ulong)c->qid.path){
273         case Qaudioctl:
274         case Qvolume:
275                 if(n >= sizeof(ac->buf))
276                         error(Etoobig);
277
278                 /* copy data to audiochan buffer so it can be modified */
279                 ac->data = nil;
280                 memmove(ac->buf, a, n);
281                 ac->buf[n] = 0;
282                 a = ac->buf;
283                 off = 0;
284         }
285         n = fn(adev, a, n, off);
286         qunlock(ac);
287         poperror();
288         return n;
289 }
290
291 static void
292 audioclose(Chan *c)
293 {
294         Audiochan *ac;
295         Audio *adev;
296
297         ac = c->aux;
298         adev = ac->adev;
299         if((c->qid.path == Qaudio) && (c->flag & COPEN)){
300                 if(adev->close){
301                         if(!waserror()){
302                                 adev->close(adev);
303                                 poperror();
304                         }
305                 }
306                 decref(&adev->audioopen);
307         }
308         if(ac->owner == c){
309                 ac->owner = nil;
310                 c->aux = nil;
311                 free(ac);
312         }
313 }
314
315 static Walkqid*
316 audiowalk(Chan *c, Chan *nc, char **name, int nname)
317 {
318         Audiochan *ac;
319         Audio *adev;
320         Walkqid *wq;
321
322         ac = c->aux;
323         adev = ac->adev;
324         wq = devwalk(c, nc, name, nname, audiodir, nelem(audiodir), devgen);
325         if(wq && wq->clone){
326                 if(audioclone(wq->clone, adev) == nil){
327                         free(wq);
328                         wq = nil;
329                 }
330         }
331         return wq;
332 }
333
334 static int
335 audiostat(Chan *c, uchar *dp, int n)
336 {
337         Audiochan *ac;
338         Audio *adev;
339
340         ac = c->aux;
341         adev = ac->adev;
342         if((ulong)c->qid.path == Qaudio)
343                 audiodir[Qaudio].length = adev->buffered ? adev->buffered(adev) : 0;
344         return devstat(c, dp, n, audiodir, nelem(audiodir), devgen);
345 }
346
347 /*
348  * audioread() made sure the buffer is big enougth so a full volume
349  * table can be serialized in one pass.
350  */
351 long
352 genaudiovolread(Audio *adev, void *a, long n, vlong,
353         Volume *vol, int (*volget)(Audio *, int, int *), ulong caps)
354 {
355         int i, j, v[2];
356         char *p, *e;
357
358         p = a;
359         e = p + n;
360         for(i = 0; vol[i].name != 0; ++i){
361                 if(vol[i].cap && (vol[i].cap & caps) == 0)
362                         continue;
363                 v[0] = 0;
364                 v[1] = 0;
365                 if((*volget)(adev, i, v) != 0)
366                         continue;
367                 if(vol[i].type == Absolute)
368                         p += snprint(p, e - p, "%s %d\n", vol[i].name, v[0]);
369                 else {
370                         if(vol[i].range == 0)
371                                 continue;
372                         for(j=0; j<2; j++){
373                                 if(v[j] < 0)
374                                         v[j] = 0;
375                                 if(v[j] > vol[i].range)
376                                         v[j] = vol[i].range;
377                                 v[j] = (v[j]*100)/vol[i].range;
378                         }
379                         switch(vol[i].type){
380                         case Left:
381                                 p += snprint(p, e - p, "%s %d\n", vol[i].name, v[0]);
382                                 break;
383                         case Right:
384                                 p += snprint(p, e - p, "%s %d\n", vol[i].name, v[1]);
385                                 break;
386                         case Stereo:
387                                 p += snprint(p, e - p, "%s %d %d\n", vol[i].name, v[0], v[1]);
388                                 break;
389                         }
390                 }
391         }
392
393         return p - (char*)a;
394 }
395
396 /*
397  * genaudiovolwrite modifies the buffer that gets passed to it. this
398  * is ok as long as it is called from inside Audio.volwrite() because
399  * audiowrite() copies the data to Audiochan.buf[] and inserts a
400  * terminating \0 byte before calling Audio.volwrite().
401  */
402 long
403 genaudiovolwrite(Audio *adev, void *a, long n, vlong,
404         Volume *vol, int (*volset)(Audio *, int, int *), ulong caps)
405 {
406         int ntok, i, j, v[2];
407         char *p, *e, *x, *tok[4];
408
409         p = a;
410         e = p + n;
411
412         for(;p < e; p = x){
413                 if(x = strchr(p, '\n'))
414                         *x++ = 0;
415                 else
416                         x = e;
417                 ntok = tokenize(p, tok, 4);
418                 if(ntok <= 0)
419                         continue;
420                 if(ntok == 1){
421                         tok[1] = tok[0];
422                         tok[0] = "master";
423                         ntok = 2;
424                 }
425                 for(i = 0; vol[i].name != 0; i++){
426                         if(vol[i].cap && (vol[i].cap & caps) == 0)
427                                 continue;
428                         if(cistrcmp(vol[i].name, tok[0]))
429                                 continue;
430         
431                         if((ntok>2) && (!cistrcmp(tok[1], "out") || !cistrcmp(tok[1], "in")))
432                                 memmove(tok+1, tok+2, --ntok);
433
434                         v[0] = 0;
435                         v[1] = 0;
436                         if(ntok > 1)
437                                 v[0] = v[1] = atoi(tok[1]);
438                         if(ntok > 2)
439                                 v[1] = atoi(tok[2]);
440                         if(vol[i].type == Absolute)
441                                 (*volset)(adev, i, v);
442                         else {
443                                 for(j=0; j<2; j++){
444                                         v[j] = (50+(v[j]*vol[i].range))/100;
445                                         if(v[j] < 0)
446                                                 v[j] = 0;
447                                         if(v[j] > vol[i].range)
448                                                 v[j] = vol[i].range;
449                                 }
450                                 (*volset)(adev, i, v);
451                         }
452                         break;
453                 }
454                 if(vol[i].name == nil)
455                         error(Evolume);
456         }
457
458         return n;
459 }
460
461 Dev audiodevtab = {
462         'A',
463         "audio",
464         audioreset,
465         devinit,
466         devshutdown,
467         audioattach,
468         audiowalk,
469         audiostat,
470         audioopen,
471         devcreate,
472         audioclose,
473         audioread,
474         devbread,
475         audiowrite,
476         devbwrite,
477         devremove,
478         devwstat,
479 };