Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ]
Version: [ 2.6.11.8 ] [ 2.6.25 ] [ 2.6.25.8 ] [ 2.6.31.13 ] Architecture: [ i386 ]
  1 #include "config.h"
  2 
  3 #include <stdio.h>
  4 #include <stdlib.h>
  5 #include <unistd.h>
  6 #include <string.h>
  7 #include <errno.h>
  8 #include <fcntl.h>
  9 #include <pthread.h>
 10 #include <sys/ioctl.h>
 11 #ifdef HAVE_SOUNDCARD_H
 12 # include <soundcard.h>
 13 #endif
 14 #ifdef HAVE_SYS_SOUNDCARD_H
 15 # include <sys/soundcard.h>
 16 #endif
 17 
 18 #include "grab-ng.h"
 19 
 20 /* -------------------------------------------------------------------- */
 21 
 22 extern int  debug;
 23 static const char *names[] = SOUND_DEVICE_NAMES;
 24 
 25 static int mixer_read_attr(struct ng_attribute *attr);
 26 static void mixer_write_attr(struct ng_attribute *attr, int val);
 27 
 28 struct mixer_handle {
 29     int  mix;
 30     int  volctl;
 31     int  volume;
 32     int  muted;
 33 };
 34 
 35 static struct ng_attribute mixer_attrs[] = {
 36     {
 37         id:       ATTR_ID_MUTE,
 38         name:     "mute",
 39         type:     ATTR_TYPE_BOOL,
 40         read:     mixer_read_attr,
 41         write:    mixer_write_attr,
 42     },{
 43         id:       ATTR_ID_VOLUME,
 44         name:     "volume",
 45         type:     ATTR_TYPE_INTEGER,
 46         min:      0,
 47         max:      100,
 48         read:     mixer_read_attr,
 49         write:    mixer_write_attr,
 50     },{
 51         /* end of list */
 52     }
 53 };
 54 
 55 static void
 56 mixer_close(void *handle)
 57 {
 58     struct mixer_handle *h = handle;
 59 
 60     if (-1 != h->mix)
 61         close(h->mix);
 62     free(h);
 63 }
 64 
 65 static void*
 66 mixer_open(char *device)
 67 {
 68     struct mixer_handle *h;
 69 
 70     h = malloc(sizeof(*h));
 71     memset(h,0,sizeof(*h));
 72     h->mix    = -1;
 73     h->volctl = -1;
 74 
 75     if (-1 == (h->mix = open(device,O_RDONLY))) {
 76         fprintf(stderr,"open %s: %s\n",device,strerror(errno));
 77         goto err;
 78     }
 79     fcntl(h->mix,F_SETFD,FD_CLOEXEC);
 80     return h;
 81 
 82  err:
 83     mixer_close(h);
 84     return NULL;
 85 }
 86 
 87 static struct ng_attribute*
 88 mixer_volctl(void *handle, char *channel)
 89 {
 90     struct mixer_handle *h = handle;
 91     struct ng_attribute *attrs;
 92     int i, devmask;
 93 
 94     if (-1 == ioctl(h->mix,MIXER_READ(SOUND_MIXER_DEVMASK),&devmask)) {
 95         fprintf(stderr,"oss mixer read devmask: %s",strerror(errno));
 96         return NULL;
 97     }
 98     for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
 99         if ((1<<i) & devmask && strcasecmp(names[i],channel) == 0) {
100             if (-1 == ioctl(h->mix,MIXER_READ(i),&h->volume)) {
101                 fprintf(stderr,"oss mixer  read volume: %s",strerror(errno));
102                 return NULL;
103             } else {
104                 h->volctl = i;
105             }
106         }
107     }
108 
109     if (-1 == h->volctl) {
110         fprintf(stderr,"oss mixer: '%s' not found, available:", channel);
111         for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
112             if ((1<<i) & devmask)
113                 fprintf(stderr," '%s'",names[i]);
114         fprintf(stderr,"\n");
115         return NULL;
116     }
117 
118     attrs = malloc(sizeof(mixer_attrs));
119     memcpy(attrs,mixer_attrs,sizeof(mixer_attrs));
120     for (i = 0; attrs[i].name != NULL; i++)
121         attrs[i].handle = h;
122     
123     return attrs;
124 }
125 
126 static int
127 mixer_read_attr(struct ng_attribute *attr)
128 {
129     struct mixer_handle *h = attr->handle;
130     int vol;
131 
132     switch (attr->id) {
133     case ATTR_ID_VOLUME:
134         if (-1 == ioctl(h->mix,MIXER_READ(h->volctl),&h->volume))
135             perror("oss mixer read volume");
136         vol = h->volume & 0x7f;
137         return vol;
138     case ATTR_ID_MUTE:
139         return h->muted;
140     default:
141         return -1;
142     }
143 }
144 
145 static void
146 mixer_write_attr(struct ng_attribute *attr, int val)
147 {
148     struct mixer_handle *h = attr->handle;
149 
150     switch (attr->id) {
151     case ATTR_ID_VOLUME:
152         val &= 0x7f;
153         h->volume = val | (val << 8);
154         if (-1 == ioctl(h->mix,MIXER_WRITE(h->volctl),&h->volume))
155             perror("oss mixer write volume");
156         h->muted = 0;
157         break;
158     case ATTR_ID_MUTE:
159         h->muted = val;
160         if (h->muted) {
161             int zero = 0;
162             if (-1 == ioctl(h->mix,MIXER_READ(h->volctl),&h->volume))
163                 perror("oss mixer read volume");
164             if (-1 == ioctl(h->mix,MIXER_WRITE(h->volctl),&zero))
165                 perror("oss mixer write volume");
166         } else {
167             if (-1 == ioctl(h->mix,MIXER_WRITE(h->volctl),&h->volume))
168                 perror("oss mixer write volume");
169         }
170         break;
171     }
172 }
173 
174 static struct ng_devinfo* mixer_probe(void)
175 {
176     struct ng_devinfo *info = NULL;
177     int i,n,fd;
178 #ifdef SOUND_MIXER_INFO
179     mixer_info minfo;
180 #endif
181 
182     n = 0;
183     for (i = 0; NULL != ng_dev.mixer_scan[i]; i++) {
184         fd = open(ng_dev.mixer_scan[i],O_RDONLY);
185         if (-1 == fd)
186             continue;
187         info = realloc(info,sizeof(*info) * (n+2));
188         memset(info+n,0,sizeof(*info)*2);
189         strcpy(info[n].device,ng_dev.mixer_scan[i]);
190 #ifdef SOUND_MIXER_INFO
191         ioctl(fd,SOUND_MIXER_INFO,&minfo);
192         strcpy(info[n].name,minfo.name);
193 #else
194         strcpy(info[n].name,ng_dev.mixer_scan[i]);
195 #endif
196         close(fd);
197         n++;
198     }
199     return info;
200 }
201 
202 static struct ng_devinfo*
203 mixer_channels(char *device)
204 {
205     struct ng_devinfo *info = NULL;
206     static char *names[]  = SOUND_DEVICE_NAMES;
207     static char *labels[] = SOUND_DEVICE_LABELS;
208     int fd,i,n,devmask;
209 
210     if (-1 == (fd = open(device,O_RDONLY))) {
211         fprintf(stderr,"open %s: %s\n",device,strerror(errno));
212         return NULL;
213     }
214     n = 0;
215     ioctl(fd,MIXER_READ(SOUND_MIXER_DEVMASK),&devmask);
216     for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
217         if (!((1<<i) & devmask))
218             continue;
219         info = realloc(info,sizeof(*info) * (n+2));
220         memset(info+n,0,sizeof(*info)*2);
221         strcpy(info[n].device,names[i]);
222         strcpy(info[n].name,labels[i]);
223         n++;
224     }
225     close(fd);
226     return info;
227 }
228 
229 struct ng_mix_driver oss_mixer = {
230     name:      "oss",
231     probe:     mixer_probe,
232     channels:  mixer_channels,
233     open:      mixer_open,
234     volctl:    mixer_volctl,
235     close:     mixer_close,
236 };
237 
238 /* ------------------------------------------------------------------- */
239 
240 struct oss_handle {
241     int    fd;
242 
243     /* oss */
244     struct ng_audio_fmt  ifmt;
245     unsigned int         afmt,channels,rate;
246     unsigned int         blocksize;
247 
248     /* me */
249     struct     ng_audio_fmt ofmt;
250     int        byteswap;
251     int        bytes;
252     int        bytes_per_sec;
253 };
254 
255 static const unsigned int afmt_to_oss[AUDIO_FMT_COUNT] = {
256     0,
257     AFMT_U8,
258     AFMT_U8,
259     AFMT_S16_LE,
260     AFMT_S16_LE,
261     AFMT_S16_BE,
262     AFMT_S16_BE
263 };
264 
265 static int
266 oss_setformat(struct oss_handle *h, struct ng_audio_fmt *fmt)
267 {
268     int frag;
269 
270     if (0 == afmt_to_oss[fmt->fmtid])
271         return -1;
272 
273     h->afmt     = afmt_to_oss[fmt->fmtid];
274     h->channels = ng_afmt_to_channels[fmt->fmtid];
275     frag        = 0x7fff000c; /* 4k */
276 
277     /* format */
278     ioctl(h->fd, SNDCTL_DSP_SETFMT, &h->afmt);
279     if (h->afmt != afmt_to_oss[fmt->fmtid]) {
280         if (ng_debug)
281             fprintf(stderr,"oss: SNDCTL_DSP_SETFMT(%d): %s\n",
282                     afmt_to_oss[fmt->fmtid],strerror(errno));
283         goto err;
284     }
285 
286     /* channels */
287     ioctl(h->fd, SNDCTL_DSP_CHANNELS, &h->channels);
288     if (h->channels != ng_afmt_to_channels[fmt->fmtid]) {
289         if (ng_debug)
290             fprintf(stderr,"oss: SNDCTL_DSP_CHANNELS(%d): %s\n",
291                     ng_afmt_to_channels[fmt->fmtid],strerror(errno));
292         goto err;
293     }
294 
295     /* sample rate */
296     h->rate = fmt->rate;
297     ioctl(h->fd, SNDCTL_DSP_SPEED, &h->rate);
298     ioctl(h->fd, SNDCTL_DSP_SETFRAGMENT, &frag);
299     if (h->rate != fmt->rate) {
300         fprintf(stderr, "oss: warning: got sample rate %d (asked for %d)\n",
301                 h->rate,fmt->rate);
302         if (h->rate < fmt->rate * 1001 / 1000 &&
303             h->rate > fmt->rate *  999 / 1000) {
304             /* ignore very small differences ... */
305             h->rate = fmt->rate;
306         }
307     }
308 
309     if (-1 == ioctl(h->fd, SNDCTL_DSP_GETBLKSIZE,  &h->blocksize)) {
310         if (ng_debug)
311             perror("SNDCTL_DSP_GETBLKSIZE");
312         goto err;
313     }
314     if (0 == h->blocksize)
315         /* dmasound bug compatibility */
316         h->blocksize = 4096;
317 
318     if (ng_debug)
319         fprintf(stderr,"oss: bs=%d rate=%d channels=%d bits=%d (%s)\n",
320                 h->blocksize,h->rate,
321                 ng_afmt_to_channels[fmt->fmtid],
322                 ng_afmt_to_bits[fmt->fmtid],
323                 ng_afmt_to_desc[fmt->fmtid]);
324     return 0;
325     
326  err:
327     if (ng_debug)
328         fprintf(stderr,"oss: sound format not supported [%s]\n",
329                 ng_afmt_to_desc[fmt->fmtid]);
330     return -1;
331 }
332 
333 static void*
334 oss_open(char *device, struct ng_audio_fmt *fmt, int record)
335 {
336     struct oss_handle *h;
337     struct ng_audio_fmt ifmt;
338 
339     h = malloc(sizeof(*h));
340     if (NULL == h)
341         return NULL;
342     memset(h,0,sizeof(*h));
343 
344     if (-1 == (h->fd = open(device ? device : ng_dev.dsp,
345                             record ? O_RDONLY : O_WRONLY | O_NONBLOCK))) {
346         fprintf(stderr,"oss: open %s: %s\n",
347                 device ? device : ng_dev.dsp,
348                 strerror(errno));
349         goto err;
350     }
351     fcntl(h->fd,F_SETFD,FD_CLOEXEC);
352 
353     if (0 == oss_setformat(h,fmt)) {
354         /* fine, native format works */
355         fmt->rate = h->rate;
356         h->ifmt = *fmt;
357         h->ofmt = *fmt;
358         h->bytes_per_sec = ng_afmt_to_bits[h->ifmt.fmtid] *
359             ng_afmt_to_channels[h->ifmt.fmtid] * h->ifmt.rate / 8;
360         return h;
361     }
362 
363     /* try byteswapping */
364     ifmt = *fmt;
365     switch (fmt->fmtid) {
366     case AUDIO_S16_LE_MONO:   ifmt.fmtid = AUDIO_S16_BE_MONO;   break;
367     case AUDIO_S16_LE_STEREO: ifmt.fmtid = AUDIO_S16_BE_STEREO; break;
368     case AUDIO_S16_BE_MONO:   ifmt.fmtid = AUDIO_S16_LE_MONO;   break;
369     case AUDIO_S16_BE_STEREO: ifmt.fmtid = AUDIO_S16_LE_STEREO; break;
370     }
371     if (0 == oss_setformat(h,&ifmt)) {
372         if (ng_debug)
373             fprintf(stderr,"oss: byteswapping pcm data\n");
374         h->byteswap = 1;
375         ifmt.rate = h->rate;
376         fmt->rate = h->rate;
377         h->ifmt = ifmt;
378         h->ofmt = *fmt;
379         h->bytes_per_sec = ng_afmt_to_bits[h->ifmt.fmtid] *
380             ng_afmt_to_channels[h->ifmt.fmtid] * h->ifmt.rate / 8;
381         return h;
382     }
383 
384     fprintf(stderr,"oss: can't use format %s\n",
385             ng_afmt_to_desc[fmt->fmtid]);
386     
387  err:
388     fmt->rate  = 0;
389     fmt->fmtid = AUDIO_NONE;
390     if (h->fd)
391         close(h->fd);
392     free(h);
393     return NULL;
394 }
395 
396 static int
397 oss_startrec(void *handle)
398 {
399     struct oss_handle *h = handle;
400     int trigger;
401 
402     if (ng_debug)
403         fprintf(stderr,"oss: startrec\n");
404     trigger = 0;
405     ioctl(h->fd,SNDCTL_DSP_SETTRIGGER,&trigger);
406 
407 #if 1
408     /*
409      * Try to clear the sound driver buffers.  IMHO this shouldn't
410      * be needed, but looks like it is with some drivers ...
411      */
412     {
413         int oflags,flags,rc;
414         unsigned char buf[4096];
415 
416         oflags = fcntl(h->fd,F_GETFL);
417         flags = oflags | O_NONBLOCK;
418         fcntl(h->fd,F_SETFL,flags);
419         for (;;) {
420             rc = read(h->fd,buf,sizeof(buf));
421             if (ng_debug)
422                 fprintf(stderr,"oss: clearbuf rc=%d errno=%s\n",rc,strerror(errno));
423             if (rc != sizeof(buf))
424                 break;
425         }
426         fcntl(h->fd,F_SETFL,oflags);
427     }
428 #endif
429 
430     trigger = PCM_ENABLE_INPUT;
431     ioctl(h->fd,SNDCTL_DSP_SETTRIGGER,&trigger);
432     return 0;
433 }
434 
435 static void
436 oss_bufread(int fd,char *buffer,int blocksize)
437 {
438     int rc,count=0;
439 
440     /* why FreeBSD returns chunks smaller than blocksize? */
441     for (;;) {
442         rc = read(fd,buffer+count,blocksize-count);
443         if (rc < 0) {
444             if (EINTR == errno)
445                 continue;
446             perror("oss: read");
447             exit(1);
448         }
449         count += rc;
450         if (count == blocksize)
451             return;
452     }
453     fprintf(stderr,"#");
454 }
455 
456 static void
457 oss_bufswap(void *ptr, int size)
458 {
459     unsigned short *buf = ptr;
460     int i;
461 
462     size = size >> 1;
463     for (i = 0; i < size; i++)
464         buf[i] = ((buf[i] >> 8) & 0xff) | ((buf[i] << 8) & 0xff00);
465 }
466 
467 static struct ng_audio_buf*
468 oss_read(void *handle, int64_t stopby)
469 {
470     struct oss_handle *h = handle;
471     struct ng_audio_buf* buf;
472     int bytes;
473 
474     if (stopby) {
475         bytes = stopby * h->bytes_per_sec / 1000000000 - h->bytes;
476         if (ng_debug)
477             fprintf(stderr,"oss: left: %d bytes (%.3fs)\n",
478                     bytes,(float)bytes/h->bytes_per_sec);
479         if (bytes <= 0)
480             return NULL;
481         bytes = (bytes + 3) & ~3;
482         if (bytes > (int)h->blocksize)
483             bytes = h->blocksize;
484     } else {
485         bytes = h->blocksize;
486     }
487     buf = ng_malloc_audio_buf(&h->ofmt,bytes);
488     oss_bufread(h->fd,buf->data,bytes);
489     if (h->byteswap)
490         oss_bufswap(buf->data,bytes);
491     h->bytes += bytes;
492     buf->info.ts = (long long)h->bytes * 1000000000 / h->bytes_per_sec;
493     return buf;
494 }
495 
496 static struct ng_audio_buf*
497 oss_write(void *handle, struct ng_audio_buf *buf)
498 {
499     struct oss_handle *h = handle;
500     int rc;
501 
502     if (0 == buf->written && h->byteswap)
503         oss_bufswap(buf->data,buf->size);
504     rc = write(h->fd, buf->data+buf->written, buf->size-buf->written);
505     switch (rc) {
506     case -1:
507         perror("write dsp");
508         free(buf);
509         buf = NULL;
510     case 0:
511         fprintf(stderr,"write dsp: Huh? no data written?\n");
512         free(buf);
513         buf = NULL;
514     default:
515         buf->written += rc;
516         if (buf->written == buf->size) {
517             free(buf);
518             buf = NULL;
519         }
520     }
521     return buf;
522 }
523 
524 static int64_t
525 oss_latency(void *handle)
526 {
527     struct oss_handle *h = handle;
528     audio_buf_info info;
529     int bytes,samples;
530     long long latency;
531 
532     if (-1 == ioctl(h->fd, SNDCTL_DSP_GETOSPACE, &info))
533         return 0;
534     bytes   = info.fragsize * info.fragstotal;
535     samples = bytes * 8 / ng_afmt_to_bits[h->ifmt.fmtid] / h->channels;
536     latency = (long long)samples * 1000000000 / h->rate;
537     if (ng_debug)
538         fprintf(stderr,"oss: bytes: %d  / samples: %d => latency: %lld\n",
539                 bytes,samples,latency);
540     return latency;
541 }
542 
543 static int
544 oss_fd(void *handle)
545 {
546     struct oss_handle *h = handle;
547     return h->fd;
548 }
549 
550 static void
551 oss_close(void *handle)
552 {
553     struct oss_handle *h = handle;
554 
555     close(h->fd);
556     free(h);
557 }
558 
559 /* ------------------------------------------------------------------- */
560 
561 static struct ng_dsp_driver oss_dsp = {
562     name:      "oss",
563     open:      oss_open,
564     close:     oss_close,
565     fd:        oss_fd,
566     startrec:  oss_startrec,
567     read:      oss_read,
568     write:     oss_write,
569     latency:   oss_latency,
570 };
571 
572 extern void ng_plugin_init(void);
573 void ng_plugin_init(void)
574 {
575     ng_dsp_driver_register(NG_PLUGIN_MAGIC,__FILE__,&oss_dsp);
576     ng_mix_driver_register(NG_PLUGIN_MAGIC,__FILE__,&oss_mixer);
577 }
578 
  This page was automatically generated by the LXR engine.