diff --git a/src/alsa.c b/src/alsa.c index 3022f92..becfd3b 100644 --- a/src/alsa.c +++ b/src/alsa.c @@ -5,10 +5,15 @@ #include #include "alsa.h" +#include "scarlett2.h" #include "scarlett2-firmware.h" +#include "scarlett2-ioctls.h" #include "stringhelper.h" #include "window-iface.h" +#define MAJOR_HWDEP_VERSION_SCARLETT2 1 +#define MAJOR_HWDEP_VERSION_FCP 2 + #define MAX_TLV_RANGE_SIZE 1024 // TLV type for channel labels @@ -1067,7 +1072,81 @@ static void alsa_get_serial_number(struct alsa_card *card) { card->serial = strdup(serial); } +// return true if the Firmware Version control exists and is writable +// and locked (i.e. the FCP server is running) +static int check_firmware_version_locked(struct alsa_card *card) { + snd_ctl_elem_id_t *id; + snd_ctl_elem_info_t *info; + + snd_ctl_elem_id_alloca(&id); + snd_ctl_elem_info_alloca(&info); + + // look for the Firmware Version control + snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_CARD); + snd_ctl_elem_id_set_name(id, "Firmware Version"); + snd_ctl_elem_info_set_id(info, id); + + // no Firmware Version control found + int err = snd_ctl_elem_info(card->handle, info); + if (err < 0) + return 0; + + return snd_ctl_elem_info_is_writable(info) && + snd_ctl_elem_info_is_locked(info); +} + +// return the driver type for this card +// DRIVER_TYPE_NONE: no driver +// DRIVER_TYPE_HWDEP: Scarlett2 driver +// DRIVER_TYPE_SOCKET: FCP driver +// DRIVER_TYPE_SOCKET_UNINIT: FCP driver, but not initialised +static int get_driver_type(struct alsa_card *card) { + snd_hwdep_t *hwdep; + + int err = scarlett2_open_card(card->device, &hwdep); + + // no hwdep for this card - driver type none + if (err == -ENOENT) + return DRIVER_TYPE_NONE; + + // if we get EPERM, it's FCP but no server running + if (err == -EPERM) + return DRIVER_TYPE_SOCKET_UNINIT; + + // if we get EBUSY, it's FCP + if (err == -EBUSY) + // fcp-server locks the Firmware Version control when it has + // finished starting up + return check_firmware_version_locked(card) ? + DRIVER_TYPE_SOCKET : DRIVER_TYPE_SOCKET_UNINIT; + + // failed to open hwdep + if (err < 0) + return DRIVER_TYPE_NONE; + + // we can open hwdep, so now check the protocol version + int ver = scarlett2_get_protocol_version(hwdep); + scarlett2_close(hwdep); + + // failed to get protocol version + if (ver < 0) + return DRIVER_TYPE_NONE; + + // hwdep protocol version 1.x.x is Scarlett2 driver + if (SCARLETT2_HWDEP_VERSION_MAJOR(ver) == MAJOR_HWDEP_VERSION_SCARLETT2) + return DRIVER_TYPE_HWDEP; + + // hwdep protocol version 2.x.x is FCP driver (but not initialised, + // because we were able to open the hwdep) + if (SCARLETT2_HWDEP_VERSION_MAJOR(ver) == MAJOR_HWDEP_VERSION_FCP) + return DRIVER_TYPE_SOCKET_UNINIT; + + return DRIVER_TYPE_NONE; +} + static void card_init(struct alsa_card *card) { + card->driver_type = get_driver_type(card); + alsa_get_elem_list(card); alsa_set_lr_nums(card); alsa_get_routing_controls(card); diff --git a/src/alsa.h b/src/alsa.h index 9693e39..31fbcfc 100644 --- a/src/alsa.h +++ b/src/alsa.h @@ -41,6 +41,19 @@ enum { HW_TYPE_COUNT }; +// driver types +// NONE is 1st Gen or Scarlett2 before hwdep support was added +// (no erase config or firmware update support) +// HWDEP is the Scarlett2 driver after hwdep support was added +// SOCKET is the FCP driver +enum { + DRIVER_TYPE_NONE, + DRIVER_TYPE_HWDEP, + DRIVER_TYPE_SOCKET, + DRIVER_TYPE_SOCKET_UNINIT, + DRIVER_TYPE_COUNT +}; + // names for the hardware types extern const char *hw_type_names[HW_TYPE_COUNT]; @@ -162,6 +175,7 @@ struct alsa_card { uint32_t pid; char *serial; char *name; + int driver_type; char *fcp_socket; int best_firmware_version; snd_ctl_t *handle;