Merge branch 'master' into develop
[jack2.git] / linux / alsa / usx2y.c
blob3eed12d0972ee09d1195bcd35cd18a915c778c43
1 /*
2 Copyright (C) 2001 Paul Davis
3 Copyright (C) 2005 Karsten Wiese, Rui Nuno Capela
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "hardware.h"
22 #include "alsa_driver.h"
23 #include "usx2y.h"
24 #include <sys/mman.h>
26 #ifndef ARRAY_SIZE
27 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
28 #endif
30 //#define DBGHWDEP
32 #ifdef DBGHWDEP
33 int dbg_offset;
34 char dbg_buffer[8096];
35 #endif
36 static
37 int usx2y_set_input_monitor_mask (jack_hardware_t *hw, unsigned long mask)
39 return -1;
42 static
43 int usx2y_change_sample_clock (jack_hardware_t *hw, SampleClockMode mode)
45 return -1;
48 static void
49 usx2y_release (jack_hardware_t *hw)
51 usx2y_t *h = (usx2y_t *) hw->private_hw;
53 if (h == 0)
54 return;
56 if (h->hwdep_handle)
57 snd_hwdep_close(h->hwdep_handle);
59 free(h);
62 static int
63 usx2y_driver_get_channel_addresses_playback (alsa_driver_t *driver,
64 snd_pcm_uframes_t *playback_avail)
66 channel_t chn;
67 int iso;
68 snd_pcm_uframes_t playback_iso_avail;
69 char *playback;
71 usx2y_t *h = (usx2y_t *) driver->hw->private_hw;
73 if (0 > h->playback_iso_start) {
74 int bytes = driver->playback_sample_bytes * 2 * driver->frames_per_cycle *
75 driver->user_nperiods;
76 iso = h->hwdep_pcm_shm->playback_iso_start;
77 if (0 > iso)
78 return 0; /* FIXME: return -1; */
79 if (++iso >= ARRAY_SIZE(h->hwdep_pcm_shm->captured_iso))
80 iso = 0;
81 while((bytes -= h->hwdep_pcm_shm->captured_iso[iso].length) > 0)
82 if (++iso >= ARRAY_SIZE(h->hwdep_pcm_shm->captured_iso))
83 iso = 0;
84 h->playback_iso_bytes_done = h->hwdep_pcm_shm->captured_iso[iso].length + bytes;
85 #ifdef DBGHWDEP
86 dbg_offset = sprintf(dbg_buffer, "first iso = %i %i@%p:%i\n",
87 iso, h->hwdep_pcm_shm->captured_iso[iso].length,
88 h->hwdep_pcm_shm->playback,
89 h->hwdep_pcm_shm->captured_iso[iso].offset);
90 #endif
91 } else {
92 iso = h->playback_iso_start;
94 #ifdef DBGHWDEP
95 dbg_offset += sprintf(dbg_buffer + dbg_offset, "iso = %i(%i;%i); ", iso,
96 h->hwdep_pcm_shm->captured_iso[iso].offset,
97 h->hwdep_pcm_shm->captured_iso[iso].frame);
98 #endif
99 playback = h->hwdep_pcm_shm->playback +
100 h->hwdep_pcm_shm->captured_iso[iso].offset +
101 h->playback_iso_bytes_done;
102 playback_iso_avail = (h->hwdep_pcm_shm->captured_iso[iso].length -
103 h->playback_iso_bytes_done) /
104 (driver->playback_sample_bytes * 2);
105 if (*playback_avail >= playback_iso_avail) {
106 *playback_avail = playback_iso_avail;
107 if (++iso >= ARRAY_SIZE(h->hwdep_pcm_shm->captured_iso))
108 iso = 0;
109 h->playback_iso_bytes_done = 0;
110 } else
111 h->playback_iso_bytes_done =
112 *playback_avail * (driver->playback_sample_bytes * 2);
113 h->playback_iso_start = iso;
114 for (chn = 0; chn < driver->playback_nchannels; chn++) {
115 const snd_pcm_channel_area_t *a = &driver->playback_areas[chn];
116 driver->playback_addr[chn] = playback + a->first / 8;
118 #ifdef DBGHWDEP
119 if (dbg_offset < (sizeof(dbg_buffer) - 256))
120 dbg_offset += sprintf(dbg_buffer + dbg_offset, "avail %li@%p\n", *playback_avail, driver->playback_addr[0]);
121 else {
122 printf(dbg_buffer);
123 return -1;
125 #endif
127 return 0;
130 static int
131 usx2y_driver_get_channel_addresses_capture (alsa_driver_t *driver,
132 snd_pcm_uframes_t *capture_avail)
134 channel_t chn;
135 int iso;
136 snd_pcm_uframes_t capture_iso_avail;
137 int capture_offset;
139 usx2y_t *h = (usx2y_t *) driver->hw->private_hw;
141 if (0 > h->capture_iso_start) {
142 iso = h->hwdep_pcm_shm->capture_iso_start;
143 if (0 > iso)
144 return 0; /* FIXME: return -1; */
145 h->capture_iso_bytes_done = 0;
146 #ifdef DBGHWDEP
147 dbg_offset = sprintf(dbg_buffer, "cfirst iso = %i %i@%p:%i\n",
148 iso, h->hwdep_pcm_shm->captured_iso[iso].length,
149 h->hwdep_pcm_shm->capture0x8,
150 h->hwdep_pcm_shm->captured_iso[iso].offset);
151 #endif
152 } else {
153 iso = h->capture_iso_start;
155 #ifdef DBGHWDEP
156 dbg_offset += sprintf(dbg_buffer + dbg_offset, "ciso = %i(%i;%i); ", iso,
157 h->hwdep_pcm_shm->captured_iso[iso].offset,
158 h->hwdep_pcm_shm->captured_iso[iso].frame);
159 #endif
160 capture_offset =
161 h->hwdep_pcm_shm->captured_iso[iso].offset +
162 h->capture_iso_bytes_done;
163 capture_iso_avail = (h->hwdep_pcm_shm->captured_iso[iso].length -
164 h->capture_iso_bytes_done) /
165 (driver->capture_sample_bytes * 2);
166 if (*capture_avail >= capture_iso_avail) {
167 *capture_avail = capture_iso_avail;
168 if (++iso >= ARRAY_SIZE(h->hwdep_pcm_shm->captured_iso))
169 iso = 0;
170 h->capture_iso_bytes_done = 0;
171 } else
172 h->capture_iso_bytes_done =
173 *capture_avail * (driver->capture_sample_bytes * 2);
174 h->capture_iso_start = iso;
175 for (chn = 0; chn < driver->capture_nchannels; chn++) {
176 driver->capture_addr[chn] =
177 (chn < 2 ? h->hwdep_pcm_shm->capture0x8 : h->hwdep_pcm_shm->capture0xA)
178 + capture_offset +
179 ((chn & 1) ? driver->capture_sample_bytes : 0);
181 #ifdef DBGHWDEP
183 int f = 0;
184 unsigned *u = driver->capture_addr[0];
185 static unsigned last;
186 dbg_offset += sprintf(dbg_buffer + dbg_offset, "\nvon %6u bis %6u\n", last, u[0]);
187 while (f < *capture_avail && dbg_offset < (sizeof(dbg_buffer) - 256)) {
188 if (u[f] != last + 1)
189 dbg_offset += sprintf(dbg_buffer + dbg_offset, "\nooops %6u %6u\n", last, u[f]);
190 last = u[f++];
193 if (dbg_offset < (sizeof(dbg_buffer) - 256))
194 dbg_offset += sprintf(dbg_buffer + dbg_offset, "avail %li@%p\n", *capture_avail, driver->capture_addr[0]);
195 else {
196 printf(dbg_buffer);
197 return -1;
199 #endif
201 return 0;
204 static int
205 usx2y_driver_start (alsa_driver_t *driver)
207 int err, i;
208 snd_pcm_uframes_t poffset, pavail;
210 usx2y_t *h = (usx2y_t *) driver->hw->private_hw;
212 for (i = 0; i < driver->capture_nchannels; i++)
213 // US428 channels 3+4 are on a separate 2 channel stream.
214 // ALSA thinks its 1 stream with 4 channels.
215 driver->capture_interleave_skip[i] = 2 * driver->capture_sample_bytes;
218 driver->playback_interleave_skip[0] = 2 * driver->playback_sample_bytes;
219 driver->playback_interleave_skip[1] = 2 * driver->playback_sample_bytes;
221 driver->poll_last = 0;
222 driver->poll_next = 0;
224 if ((err = snd_pcm_prepare (driver->playback_handle)) < 0) {
225 jack_error ("ALSA/USX2Y: prepare error for playback: %s", snd_strerror(err));
226 return -1;
229 if (driver->midi && !driver->xrun_recovery)
230 (driver->midi->start)(driver->midi);
232 if (driver->playback_handle) {
233 /* int i, j; */
234 /* char buffer[2000]; */
235 h->playback_iso_start =
236 h->capture_iso_start = -1;
237 snd_hwdep_poll_descriptors(h->hwdep_handle, &h->pfds, 1);
238 h->hwdep_pcm_shm = (snd_usX2Y_hwdep_pcm_shm_t*)
239 mmap(NULL, sizeof(snd_usX2Y_hwdep_pcm_shm_t),
240 PROT_READ,
241 MAP_SHARED, h->pfds.fd,
243 if (MAP_FAILED == h->hwdep_pcm_shm) {
244 perror("ALSA/USX2Y: mmap");
245 return -1;
247 if (mprotect(h->hwdep_pcm_shm->playback,
248 sizeof(h->hwdep_pcm_shm->playback),
249 PROT_READ|PROT_WRITE)) {
250 perror("ALSA/USX2Y: mprotect");
251 return -1;
253 memset(h->hwdep_pcm_shm->playback, 0, sizeof(h->hwdep_pcm_shm->playback));
254 /* for (i = 0, j = 0; i < 2000;) { */
255 /* j += sprintf(buffer + j, "%04hX ", */
256 /* *(unsigned short*)(h->hwdep_pcm_shm->capture + i)); */
257 /* if (((i += 2) % 32) == 0) { */
258 /* jack_error(buffer); */
259 /* j = 0; */
260 /* } */
261 /* } */
264 if (driver->hw_monitoring) {
265 driver->hw->set_input_monitor_mask (driver->hw,
266 driver->input_monitor_mask);
269 if (driver->playback_handle) {
270 /* fill playback buffer with zeroes, and mark
271 all fragments as having data.
274 pavail = snd_pcm_avail_update (driver->playback_handle);
276 if (pavail != driver->frames_per_cycle * driver->playback_nperiods) {
277 jack_error ("ALSA/USX2Y: full buffer not available at start");
278 return -1;
281 if (snd_pcm_mmap_begin(
282 driver->playback_handle,
283 &driver->playback_areas,
284 &poffset, &pavail) < 0) {
285 return -1;
288 /* XXX this is cheating. ALSA offers no guarantee that
289 we can access the entire buffer at any one time. It
290 works on most hardware tested so far, however, buts
291 its a liability in the long run. I think that
292 alsa-lib may have a better function for doing this
293 here, where the goal is to silence the entire
294 buffer.
297 /* snd_pcm_uframes_t frag, nframes = driver->buffer_frames; */
298 /* while (nframes) { */
299 /* frag = nframes; */
300 /* if (usx2y_driver_get_channel_addresses_playback(driver, &frag) < 0) */
301 /* return -1; */
303 /* for (chn = 0; chn < driver->playback_nchannels; chn++) */
304 /* alsa_driver_silence_on_channel (driver, chn, frag); */
305 /* nframes -= frag; */
306 /* } */
309 snd_pcm_mmap_commit (driver->playback_handle, poffset,
310 driver->user_nperiods * driver->frames_per_cycle);
312 if ((err = snd_pcm_start (driver->playback_handle)) < 0) {
313 jack_error ("ALSA/USX2Y: could not start playback (%s)",
314 snd_strerror (err));
315 return -1;
319 if (driver->hw_monitoring &&
320 (driver->input_monitor_mask || driver->all_monitor_in)) {
321 if (driver->all_monitor_in) {
322 driver->hw->set_input_monitor_mask (driver->hw, ~0U);
323 } else {
324 driver->hw->set_input_monitor_mask (
325 driver->hw, driver->input_monitor_mask);
329 driver->playback_nfds = snd_pcm_poll_descriptors_count (driver->playback_handle);
330 driver->capture_nfds = snd_pcm_poll_descriptors_count (driver->capture_handle);
332 if (driver->pfd) {
333 free (driver->pfd);
336 driver->pfd = (struct pollfd *)
337 malloc (sizeof (struct pollfd) *
338 (driver->playback_nfds + driver->capture_nfds + 2));
340 return 0;
343 static int
344 usx2y_driver_stop (alsa_driver_t *driver)
346 int err;
347 JSList* node;
348 int chn;
350 usx2y_t *h = (usx2y_t *) driver->hw->private_hw;
352 /* silence all capture port buffers, because we might
353 be entering offline mode.
356 for (chn = 0, node = driver->capture_ports; node;
357 node = jack_slist_next (node), chn++) {
359 jack_port_t* port;
360 char* buf;
361 jack_nframes_t nframes = driver->engine->control->buffer_size;
363 port = (jack_port_t *) node->data;
364 buf = jack_port_get_buffer (port, nframes);
365 memset (buf, 0, sizeof (jack_default_audio_sample_t) * nframes);
368 if (driver->playback_handle) {
369 if ((err = snd_pcm_drop (driver->playback_handle)) < 0) {
370 jack_error ("ALSA/USX2Y: channel flush for playback "
371 "failed (%s)", snd_strerror (err));
372 return -1;
376 if (driver->hw_monitoring) {
377 driver->hw->set_input_monitor_mask (driver->hw, 0);
380 munmap(h->hwdep_pcm_shm, sizeof(snd_usX2Y_hwdep_pcm_shm_t));
382 if (driver->midi && !driver->xrun_recovery)
383 (driver->midi->stop)(driver->midi);
385 return 0;
388 static int
389 usx2y_driver_null_cycle (alsa_driver_t* driver, jack_nframes_t nframes)
391 jack_nframes_t nf;
392 snd_pcm_uframes_t offset;
393 snd_pcm_uframes_t contiguous, contiguous_;
394 int chn;
396 VERBOSE(driver->engine,
397 "usx2y_driver_null_cycle (%p, %i)", driver, nframes);
399 if (driver->capture_handle) {
400 nf = nframes;
401 offset = 0;
402 while (nf) {
404 contiguous = (nf > driver->frames_per_cycle) ?
405 driver->frames_per_cycle : nf;
407 if (snd_pcm_mmap_begin (
408 driver->capture_handle,
409 &driver->capture_areas,
410 (snd_pcm_uframes_t *) &offset,
411 (snd_pcm_uframes_t *) &contiguous)) {
412 return -1;
414 contiguous_ = contiguous;
415 while (contiguous_) {
416 snd_pcm_uframes_t frag = contiguous_;
417 if (usx2y_driver_get_channel_addresses_capture(driver, &frag) < 0)
418 return -1;
419 contiguous_ -= frag;
422 if (snd_pcm_mmap_commit (driver->capture_handle,
423 offset, contiguous) < 0) {
424 return -1;
427 nf -= contiguous;
431 if (driver->playback_handle) {
432 nf = nframes;
433 offset = 0;
434 while (nf) {
435 contiguous = (nf > driver->frames_per_cycle) ?
436 driver->frames_per_cycle : nf;
438 if (snd_pcm_mmap_begin (
439 driver->playback_handle,
440 &driver->playback_areas,
441 (snd_pcm_uframes_t *) &offset,
442 (snd_pcm_uframes_t *) &contiguous)) {
443 return -1;
447 snd_pcm_uframes_t frag, nframes = contiguous;
448 while (nframes) {
449 frag = nframes;
450 if (usx2y_driver_get_channel_addresses_playback(driver, &frag) < 0)
451 return -1;
452 for (chn = 0; chn < driver->playback_nchannels; chn++)
453 alsa_driver_silence_on_channel (driver, chn, frag);
454 nframes -= frag;
458 if (snd_pcm_mmap_commit (driver->playback_handle,
459 offset, contiguous) < 0) {
460 return -1;
463 nf -= contiguous;
467 return 0;
470 static int
471 usx2y_driver_read (alsa_driver_t *driver, jack_nframes_t nframes)
473 snd_pcm_uframes_t contiguous;
474 snd_pcm_sframes_t nread;
475 snd_pcm_uframes_t offset;
476 jack_default_audio_sample_t* buf[4];
477 channel_t chn;
478 JSList *node;
479 jack_port_t* port;
480 int err;
481 snd_pcm_uframes_t nframes_ = nframes;
483 if (!driver->capture_handle || driver->engine->freewheeling) {
484 return 0;
487 if (driver->midi)
488 (driver->midi->read)(driver->midi, nframes);
490 nread = 0;
492 if (snd_pcm_mmap_begin (driver->capture_handle,
493 &driver->capture_areas,
494 &offset, &nframes_) < 0) {
495 jack_error ("ALSA/USX2Y: %s: mmap areas info error",
496 driver->alsa_name_capture);
497 return -1;
500 for (chn = 0, node = driver->capture_ports;
501 node; node = jack_slist_next (node), chn++) {
502 port = (jack_port_t *) node->data;
503 if (!jack_port_connected (port)) {
504 continue;
506 buf[chn] = jack_port_get_buffer (port, nframes_);
509 while (nframes) {
511 contiguous = nframes;
512 if (usx2y_driver_get_channel_addresses_capture (
513 driver, &contiguous) < 0) {
514 return -1;
516 for (chn = 0, node = driver->capture_ports;
517 node; node = jack_slist_next (node), chn++) {
518 port = (jack_port_t *) node->data;
519 if (!jack_port_connected (port)) {
520 /* no-copy optimization */
521 continue;
523 alsa_driver_read_from_channel (driver, chn,
524 buf[chn] + nread,
525 contiguous);
526 /* sample_move_dS_s24(buf[chn] + nread, */
527 /* driver->capture_addr[chn], */
528 /* contiguous, */
529 /* driver->capture_interleave_skip); */
531 nread += contiguous;
532 nframes -= contiguous;
535 if ((err = snd_pcm_mmap_commit (driver->capture_handle,
536 offset, nframes_)) < 0) {
537 jack_error ("ALSA/USX2Y: could not complete read of %"
538 PRIu32 " frames: error = %d", nframes_, err);
539 return -1;
542 return 0;
545 static int
546 usx2y_driver_write (alsa_driver_t* driver, jack_nframes_t nframes)
548 channel_t chn;
549 JSList *node;
550 jack_default_audio_sample_t* buf[2];
551 snd_pcm_sframes_t nwritten;
552 snd_pcm_uframes_t contiguous;
553 snd_pcm_uframes_t offset;
554 jack_port_t *port;
555 int err;
556 snd_pcm_uframes_t nframes_ = nframes;
558 driver->process_count++;
560 if (!driver->playback_handle || driver->engine->freewheeling) {
561 return 0;
564 if (driver->midi)
565 (driver->midi->write)(driver->midi, nframes);
567 nwritten = 0;
569 /* check current input monitor request status */
571 driver->input_monitor_mask = 0;
573 for (chn = 0, node = driver->capture_ports; node;
574 node = jack_slist_next (node), chn++) {
575 if (((jack_port_t *) node->data)->shared->monitor_requests) {
576 driver->input_monitor_mask |= (1<<chn);
580 if (driver->hw_monitoring) {
581 if ((driver->hw->input_monitor_mask
582 != driver->input_monitor_mask)
583 && !driver->all_monitor_in) {
584 driver->hw->set_input_monitor_mask (
585 driver->hw, driver->input_monitor_mask);
589 if (snd_pcm_mmap_begin(driver->playback_handle,
590 &driver->playback_areas,
591 &offset, &nframes_) < 0) {
592 jack_error ("ALSA/USX2Y: %s: mmap areas info error",
593 driver->alsa_name_capture);
594 return -1;
597 for (chn = 0, node = driver->playback_ports;
598 node; node = jack_slist_next (node), chn++) {
599 port = (jack_port_t *) node->data;
600 buf[chn] = jack_port_get_buffer (port, nframes_);
603 while (nframes) {
605 contiguous = nframes;
606 if (usx2y_driver_get_channel_addresses_playback (
607 driver, &contiguous) < 0) {
608 return -1;
610 for (chn = 0, node = driver->playback_ports;
611 node; node = jack_slist_next (node), chn++) {
612 port = (jack_port_t *) node->data;
613 alsa_driver_write_to_channel (driver, chn,
614 buf[chn] + nwritten,
615 contiguous);
617 nwritten += contiguous;
618 nframes -= contiguous;
621 if ((err = snd_pcm_mmap_commit (driver->playback_handle,
622 offset, nframes_)) < 0) {
623 jack_error ("ALSA/USX2Y: could not complete playback of %"
624 PRIu32 " frames: error = %d", nframes_, err);
625 if (err != -EPIPE && err != -ESTRPIPE)
626 return -1;
629 return 0;
632 static void
633 usx2y_driver_setup (alsa_driver_t *driver)
635 driver->nt_start = (JackDriverNTStartFunction) usx2y_driver_start;
636 driver->nt_stop = (JackDriverNTStopFunction) usx2y_driver_stop;
637 driver->read = (JackDriverReadFunction) usx2y_driver_read;
638 driver->write = (JackDriverReadFunction) usx2y_driver_write;
639 driver->null_cycle =
640 (JackDriverNullCycleFunction) usx2y_driver_null_cycle;
643 jack_hardware_t *
644 jack_alsa_usx2y_hw_new (alsa_driver_t *driver)
646 jack_hardware_t *hw;
647 usx2y_t *h;
649 int hwdep_cardno;
650 int hwdep_devno;
651 char *hwdep_colon;
652 char hwdep_name[9];
653 snd_hwdep_t *hwdep_handle;
655 hw = (jack_hardware_t *) malloc (sizeof (jack_hardware_t));
657 hw->capabilities = 0;
658 hw->input_monitor_mask = 0;
659 hw->private_hw = 0;
661 hw->set_input_monitor_mask = usx2y_set_input_monitor_mask;
662 hw->change_sample_clock = usx2y_change_sample_clock;
663 hw->release = usx2y_release;
665 /* Derive the special USB US-X2Y hwdep pcm device name from
666 * the playback one, thus allowing the use of the "rawusb"
667 * experimental stuff if, and only if, the "hw:n,2" device
668 * name is specified. Otherwise, fallback to generic backend.
670 hwdep_handle = NULL;
671 hwdep_cardno = hwdep_devno = 0;
672 if ((hwdep_colon = strrchr(driver->alsa_name_playback, ':')) != NULL)
673 sscanf(hwdep_colon, ":%d,%d", &hwdep_cardno, &hwdep_devno);
674 if (hwdep_devno == 2) {
675 snprintf(hwdep_name, sizeof(hwdep_name), "hw:%d,1", hwdep_cardno);
676 if (snd_hwdep_open (&hwdep_handle, hwdep_name, O_RDWR) < 0) {
677 jack_error ("ALSA/USX2Y: Cannot open hwdep device \"%s\"", hwdep_name);
678 } else {
679 /* Allocate specific USX2Y hwdep pcm struct. */
680 h = (usx2y_t *) malloc (sizeof (usx2y_t));
681 h->driver = driver;
682 h->hwdep_handle = hwdep_handle;
683 hw->private_hw = h;
684 /* Set our own operational function pointers. */
685 usx2y_driver_setup(driver);
686 jack_info("ALSA/USX2Y: EXPERIMENTAL hwdep pcm device %s"
687 " (aka \"rawusb\")", driver->alsa_name_playback);
691 return hw;