/* Copyright (C) 2001 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. $Id: hammerfall.c,v 1.3 2005/09/29 14:51:59 letz Exp $ */ #include "hardware.h" #include "alsa_driver.h" #include "hammerfall.h" #include "JackError.h" #define FALSE 0 #define TRUE 1 /* Set this to 1 if you want this compile error: * warning: `hammerfall_monitor_controls' defined but not used */ #define HAMMERFALL_MONITOR_CONTROLS 0 static void set_control_id (snd_ctl_elem_id_t *ctl, const char *name) { snd_ctl_elem_id_set_name (ctl, name); snd_ctl_elem_id_set_numid (ctl, 0); snd_ctl_elem_id_set_interface (ctl, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_id_set_device (ctl, 0); snd_ctl_elem_id_set_subdevice (ctl, 0); snd_ctl_elem_id_set_index (ctl, 0); } #if HAMMERFALL_MONITOR_CONTROLS static void hammerfall_broadcast_channel_status_change (hammerfall_t *h, int lock, int sync, channel_t lowchn, channel_t highchn) { channel_t chn; ClockSyncStatus status = 0; if (lock) { status |= Lock; } else { status |= NoLock; } if (sync) { status |= Sync; } else { status |= NoSync; } for (chn = lowchn; chn < highchn; chn++) { alsa_driver_set_clock_sync_status (h->driver, chn, status); } } static void hammerfall_check_sync_state (hammerfall_t *h, int val, int adat_id) { int lock; int sync; /* S/PDIF channel is always locked and synced, but we only need tell people once that this is TRUE. XXX - maybe need to make sure that the rate matches our idea of the current rate ? */ if (!h->said_that_spdif_is_fine) { ClockSyncStatus status; status = Lock|Sync; /* XXX broken! fix for hammerfall light ! */ alsa_driver_set_clock_sync_status (h->driver, 24, status); alsa_driver_set_clock_sync_status (h->driver, 25, status); h->said_that_spdif_is_fine = TRUE; } lock = (val & 0x1) ? TRUE : FALSE; sync = (val & 0x2) ? TRUE : FALSE; if (h->lock_status[adat_id] != lock || h->sync_status[adat_id] != sync) { hammerfall_broadcast_channel_status_change (h, lock, sync, adat_id*8, (adat_id*8)+8); } h->lock_status[adat_id] = lock; h->sync_status[adat_id] = sync; } static void hammerfall_check_sync (hammerfall_t *h, snd_ctl_elem_value_t *ctl) { const char *name; int val; snd_ctl_elem_id_t *ctl_id; jack_info ("check sync"); snd_ctl_elem_id_alloca (&ctl_id); snd_ctl_elem_value_get_id (ctl, ctl_id); name = snd_ctl_elem_id_get_name (ctl_id); if (strcmp (name, "ADAT1 Sync Check") == 0) { val = snd_ctl_elem_value_get_enumerated (ctl, 0); hammerfall_check_sync_state (h, val, 0); } else if (strcmp (name, "ADAT2 Sync Check") == 0) { val = snd_ctl_elem_value_get_enumerated (ctl, 0); hammerfall_check_sync_state (h, val, 1); } else if (strcmp (name, "ADAT3 Sync Check") == 0) { val = snd_ctl_elem_value_get_enumerated (ctl, 0); hammerfall_check_sync_state (h, val, 2); } else { jack_error ("Hammerfall: unknown control \"%s\"", name); } } #endif /* HAMMERFALL_MONITOR_CONTROLS */ static int hammerfall_set_input_monitor_mask (jack_hardware_t *hw, unsigned long mask) { hammerfall_t *h = (hammerfall_t *) hw->private_hw; snd_ctl_elem_value_t *ctl; snd_ctl_elem_id_t *ctl_id; int err; int i; snd_ctl_elem_value_alloca (&ctl); snd_ctl_elem_id_alloca (&ctl_id); set_control_id (ctl_id, "Channels Thru"); snd_ctl_elem_value_set_id (ctl, ctl_id); for (i = 0; i < 26; i++) { snd_ctl_elem_value_set_integer (ctl, i, (mask & (1<driver->ctl_handle, ctl)) != 0) { jack_error ("ALSA/Hammerfall: cannot set input monitoring (%s)", snd_strerror (err)); return -1; } hw->input_monitor_mask = mask; return 0; } static int hammerfall_change_sample_clock (jack_hardware_t *hw, SampleClockMode mode) { hammerfall_t *h = (hammerfall_t *) hw->private_hw; snd_ctl_elem_value_t *ctl; snd_ctl_elem_id_t *ctl_id; int err; snd_ctl_elem_value_alloca (&ctl); snd_ctl_elem_id_alloca (&ctl_id); set_control_id (ctl_id, "Sync Mode"); snd_ctl_elem_value_set_id (ctl, ctl_id); switch (mode) { case AutoSync: snd_ctl_elem_value_set_enumerated (ctl, 0, 0); break; case ClockMaster: snd_ctl_elem_value_set_enumerated (ctl, 0, 1); break; case WordClock: snd_ctl_elem_value_set_enumerated (ctl, 0, 2); break; } if ((err = snd_ctl_elem_write (h->driver->ctl_handle, ctl)) < 0) { jack_error ("ALSA-Hammerfall: cannot set clock mode"); } return 0; } static void hammerfall_release (jack_hardware_t *hw) { hammerfall_t *h = (hammerfall_t *) hw->private_hw; void *status; if (h == 0) { return; } #ifndef __ANDROID__ if (h->monitor_thread) { pthread_cancel (h->monitor_thread); pthread_join (h->monitor_thread, &status); } #endif free (h); } #if HAMMERFALL_MONITOR_CONTROLS static void * hammerfall_monitor_controls (void *arg) { jack_hardware_t *hw = (jack_hardware_t *) arg; hammerfall_t *h = (hammerfall_t *) hw->private_hw; snd_ctl_elem_id_t *switch_id[3]; snd_ctl_elem_value_t *sw[3]; pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL); snd_ctl_elem_id_malloc (&switch_id[0]); snd_ctl_elem_id_malloc (&switch_id[1]); snd_ctl_elem_id_malloc (&switch_id[2]); snd_ctl_elem_value_malloc (&sw[0]); snd_ctl_elem_value_malloc (&sw[1]); snd_ctl_elem_value_malloc (&sw[2]); set_control_id (switch_id[0], "ADAT1 Sync Check"); set_control_id (switch_id[1], "ADAT2 Sync Check"); set_control_id (switch_id[2], "ADAT3 Sync Check"); snd_ctl_elem_value_set_id (sw[0], switch_id[0]); snd_ctl_elem_value_set_id (sw[1], switch_id[1]); snd_ctl_elem_value_set_id (sw[2], switch_id[2]); while (1) { if (snd_ctl_elem_read (h->driver->ctl_handle, sw[0])) { jack_error ("cannot read control switch 0 ..."); } hammerfall_check_sync (h, sw[0]); if (snd_ctl_elem_read (h->driver->ctl_handle, sw[1])) { jack_error ("cannot read control switch 0 ..."); } hammerfall_check_sync (h, sw[1]); if (snd_ctl_elem_read (h->driver->ctl_handle, sw[2])) { jack_error ("cannot read control switch 0 ..."); } hammerfall_check_sync (h, sw[2]); if (nanosleep (&h->monitor_interval, 0)) { break; } } pthread_exit (0); } #endif /* HAMMERFALL_MONITOR_CONTROLS */ jack_hardware_t * jack_alsa_hammerfall_hw_new (alsa_driver_t *driver) { jack_hardware_t *hw; hammerfall_t *h; hw = (jack_hardware_t *) malloc (sizeof (jack_hardware_t)); hw->capabilities = Cap_HardwareMonitoring|Cap_AutoSync|Cap_WordClock|Cap_ClockMaster|Cap_ClockLockReporting; hw->input_monitor_mask = 0; hw->private_hw = 0; hw->set_input_monitor_mask = hammerfall_set_input_monitor_mask; hw->change_sample_clock = hammerfall_change_sample_clock; hw->release = hammerfall_release; h = (hammerfall_t *) malloc (sizeof (hammerfall_t)); h->lock_status[0] = FALSE; h->sync_status[0] = FALSE; h->lock_status[1] = FALSE; h->sync_status[1] = FALSE; h->lock_status[2] = FALSE; h->sync_status[2] = FALSE; h->said_that_spdif_is_fine = FALSE; h->driver = driver; h->monitor_interval.tv_sec = 1; h->monitor_interval.tv_nsec = 0; hw->private_hw = h; #if 0 if (pthread_create (&h->monitor_thread, 0, hammerfall_monitor_controls, hw)) { jack_error ("ALSA/Hammerfall: cannot create sync monitor thread"); } #endif return hw; }