Logo Search packages:      
Sourcecode: alsa-tools version File versions  Download package

dl10k1.c

/*
 *  EMU10k1 dump loader
 *
 *  Copyright (c) 2003,2004 by Peter Zubaj
 *
 *   Hwdep usage based on sb16_csp
 *
 *   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 */

/* TODO - kontrola dat, ktore nahravam */

#include <getopt.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdint.h>

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <sys/ioctl.h>
#include <alsa/asoundlib.h>
#include <alsa/sound/emu10k1.h>

#include <getopt.h>
#include <bitops.h>

#include "ld10k1_dump_file.h"

#define DL10K1_SIGNATURE "DUMP Image (dl10k1)"
int card = 0;
snd_hwdep_t *handle;
const char *card_proc_id;

void error(const char *fmt,...)
{
      va_list va;

      va_start(va, fmt);
      fprintf(stderr, "Error: ");
      vfprintf(stderr, fmt, va);
      fprintf(stderr, "\n");
      va_end(va);
}

static void help(char *command)
{
      fprintf(stderr,
            "Usage: %s [-options]\n"
            "\nAvailable options:\n"
            "  -h, --help        this help\n"
            "  -c, --card        select card number, default = 0\n"
            "  -d, --dump        file with dump\n"
            , command);
}

int driver_set_tram_size(int tram_size)
{
      if (snd_hwdep_ioctl(handle, SNDRV_EMU10K1_IOCTL_TRAM_SETUP, &tram_size) < 0) {
            error("unable to setup tram");
            return 1;
      }
      return 0;
}

void free_code_struct(emu10k1_fx8010_code_t *code)
{
      if (code->gpr_map)
            free(code->gpr_map);
      if (code->tram_data_map)
            free(code->tram_data_map);
      if (code->tram_addr_map)
            free(code->tram_addr_map);
      if (code->code)
            free(code->code);
}

int alloc_code_struct(emu10k1_fx8010_code_t *code)
{
      /* alloc code structure */
      code->gpr_map = NULL;
      code->tram_data_map = NULL;
      code->tram_addr_map = NULL;
      code->code = NULL;
      
      code->gpr_map = (uint32_t *)malloc(sizeof(uint32_t) * 0x200);
      if (!code->gpr_map)
            goto err;
      memset(code->gpr_map, 0, sizeof(uint32_t) * 0x200);

      code->tram_data_map = (uint32_t *)malloc(sizeof(uint32_t) * 0x100);
      if (!code->tram_data_map)
            goto err;
      memset(code->tram_data_map, 0, sizeof(uint32_t) * 0x100);

      code->tram_addr_map = (uint32_t *)malloc(sizeof(uint32_t) * 0x100);
      if (!code->tram_addr_map)
            goto err;
      memset(code->tram_addr_map, 0, sizeof(uint32_t) * 0x100);

      code->code = (uint32_t *)malloc(sizeof(uint32_t) * 1024 * 2);
      if (!code->code)
            goto err;
      memset(code->code, 0, sizeof(uint32_t) * 1024 * 2);

        return 0;
err:
      free_code_struct(code);
      return -1;
}

int driver_init_dsp(int audigy)
{
      int i;
      emu10k1_fx8010_code_t code;
      emu10k1_fx8010_control_gpr_t *ctrl;
      emu10k1_ctl_elem_id_t *ids;
      emu10k1_fx8010_pcm_t ipcm;
      unsigned int *iptr;

      if (alloc_code_struct(&code) < 0) {
            error("no mem");
            return 1;
      }

      /* get count of controls */
      code.gpr_list_control_count = 0;
      if (snd_hwdep_ioctl(handle, SNDRV_EMU10K1_IOCTL_CODE_PEEK, &code) < 0) {
            error("unable to peek code");
            free_code_struct(&code);
            return 1;
      }

      ctrl = (emu10k1_fx8010_control_gpr_t *)malloc(sizeof(emu10k1_fx8010_control_gpr_t) * code.gpr_list_control_total);
      if (!ctrl) {
            error("no mem");
            free_code_struct(&code);
            return 1;
      }

      code.gpr_list_control_count = code.gpr_list_control_total;
      code.gpr_list_controls = ctrl;

      for (i = 0; i < sizeof(code.gpr_valid) / sizeof(unsigned long); i++)
            code.gpr_valid[i] = 0x0;
      for (i = 0; i < sizeof(code.tram_valid) / sizeof(unsigned long); i++)
            code.tram_valid[i] = 0x0;
      for (i = 0; i < sizeof(code.code_valid) / sizeof(unsigned long); i++)
            code.code_valid[i] = 0x0;;

      if (snd_hwdep_ioctl(handle, SNDRV_EMU10K1_IOCTL_CODE_PEEK, &code) < 0) {
            error("unable to peek code");
            free_code_struct(&code);
            free(ctrl);
            return 1;
      }

      
      /* new name */
      strcpy(code.name, DL10K1_SIGNATURE);
      for (i = 0; i < sizeof(code.gpr_valid) / sizeof(unsigned long); i++)
            code.gpr_valid[i] = ~0;

      for (i = 0; i < sizeof(code.gpr_valid) * 8; i++)
            code.gpr_map[i] = 0;

      ids = (emu10k1_ctl_elem_id_t *)malloc(sizeof(emu10k1_ctl_elem_id_t) * code.gpr_list_control_total);
      if (!ids) {
            free_code_struct(&code);
            free(ctrl);
            error("no mem");
            return 1;
      }

      code.gpr_del_control_count = code.gpr_list_control_total;
      if (code.gpr_del_control_count) {
            for (i = 0; i < code.gpr_del_control_count; i++) {
                  memcpy(&(ids[i]), &(ctrl[i].id), sizeof(emu10k1_ctl_elem_id_t));
            }
      }

      free(ctrl);

      code.gpr_del_controls = ids;
      code.gpr_list_control_count = 0;
      code.gpr_add_control_count = 0;
      code.gpr_list_control_count = 0;

      for (i = 0; i < sizeof(code.tram_valid) / sizeof(unsigned long); i++)
            code.tram_valid[i] = ~0;
      for (i = 0; i < sizeof(code.code_valid) / sizeof(unsigned long); i++)
            code.code_valid[i] = ~0;

      for (i = 0; i < sizeof(code.tram_valid) * 8; i++) {
            code.tram_addr_map[i] = 0;
            code.tram_data_map[i] = 0;
      }

      for (iptr = code.code, i = 0; i < sizeof(code.code_valid) * 8; i++, iptr += 2)
            if (audigy) {
                  *iptr = ((0xcf & 0x7ff) << 12) | (0xc0 & 0x7ff);
                  *(iptr + 1) = ((0x0f & 0x0f) << 24) | ((0xc0 & 0x7ff) << 12) | (0xc0 & 0x7ff);
            } else {
                  *iptr = ((0x40 & 0x3ff) << 10) | (0x40 & 0x3ff);
                  *(iptr + 1) = ((0x06 & 0x0f) << 20) | ((0x40 & 0x3ff) << 10) | (0x40 & 0x3ff);
            }

      if (snd_hwdep_ioctl(handle, SNDRV_EMU10K1_IOCTL_CODE_POKE, &code) < 0) {
            error("unable to poke code");
            free_code_struct(&code);
            free(ids);
            return 1;
      }

      free(ids);

      /* delete tram pcm dsp part */
      if (!audigy) {
            for (i = 0; i < EMU10K1_FX8010_PCM_COUNT; i++) {
                  ipcm.substream = i;
                  ipcm.channels = 0;
                  if (snd_hwdep_ioctl(handle, SNDRV_EMU10K1_IOCTL_PCM_POKE, &ipcm) < 0) {
                        error("unable to poke code");
                        free_code_struct(&code);
                        return 1;
                  }
            }
      }
      return 0;
}

int dump_load(int audigy, char *file_name)
{
      struct stat dump_stat;
      void *dump_data, *ptr;
      FILE *dump_file;
      emu10k1_fx8010_control_gpr_t *ctrl = NULL;
      ld10k1_ctl_dump_t *fctrl = NULL;
      unsigned int *fgpr = NULL;
      ld10k1_tram_dump_t *ftram = NULL;
      ld10k1_instr_dump_t *finstr = NULL;
      int i, j;
      unsigned int vaddr, addr;
      int op;
      
      unsigned int *iptr;

      emu10k1_fx8010_code_t code;

      ld10k1_dump_t *header = NULL;

      /* first load patch to mem */
      if (stat(file_name, &dump_stat)) {
            error("unable to load patch %s", file_name);
            return 1;
      }

      /* minimal dump len is size of header */
      if (dump_stat.st_size < sizeof(ld10k1_dump_t)) {
            error("unable to load dump %s (wrong file size)", file_name);
            return 1;
      }


      dump_data = malloc(dump_stat.st_size);
      if (!dump_data) {
            error("no mem");
            return 1;
      }

      dump_file = fopen(file_name, "r");
      if (!dump_file) {
            error("unable to open file %s", file_name);
            goto err;
      }

      if (fread(dump_data, dump_stat.st_size, 1, dump_file) != 1) {
            error("unable to read data from file %s", file_name);
            goto err;
      } else
            fclose(dump_file);

      /* signature check */

      header = (ld10k1_dump_t *)dump_data;
      if (strncmp(header->signature, "LD10K1 DUMP 001", 16) != 0) {
            error("wrong dump file %s (wrong signature)", file_name);
            goto err;
      }
      
      /*printf("Size header%d\nctc %d %d\ngpr %d %d\ntram %d %d\ninstr %d %d\n", sizeof(ld10k1_dump_t),
            header->ctl_count, sizeof(ld10k1_ctl_dump_t),
            header->gpr_count, sizeof(unsigned int),
            header->tram_count, sizeof(ld10k1_tram_dump_t),
            header->instr_count, sizeof(ld10k1_instr_dump_t));*/

      /*check size */
      if (sizeof(ld10k1_dump_t) +
            header->ctl_count * sizeof(ld10k1_ctl_dump_t) +
            header->gpr_count * sizeof(unsigned int) +
            header->tram_count * sizeof(ld10k1_tram_dump_t) +
            header->instr_count * sizeof(ld10k1_instr_dump_t) != dump_stat.st_size)
            goto err;

      /* check dump type */
      if (header->dump_type == DUMP_TYPE_LIVE && audigy) {
            error("can't load dump from Live to Audigy");
            goto err1;
      } else if ((header->dump_type == DUMP_TYPE_AUDIGY_OLD ||
                  header->dump_type == DUMP_TYPE_AUDIGY) &&
                  !audigy) {
            error("can't load dump from Audigy to Live");
            goto err1;
      } else if (header->dump_type == DUMP_TYPE_AUDIGY_OLD) {
            error("can't load dump from Audigy (not patched drivers) to Audigy (current drivers)");
            goto err1;
      }

      ptr = dump_data;
      ptr += sizeof(ld10k1_dump_t);

      ctrl = (emu10k1_fx8010_control_gpr_t *)malloc(sizeof(emu10k1_fx8010_control_gpr_t) * header->ctl_count);
      if (!ctrl) {
            error("no mem");
            goto err1;
      }

      if (alloc_code_struct(&code) < 0) {
            error("no mem");
            return 1;
      }
      

      strcpy(code.name, DL10K1_SIGNATURE);

      /* copy ctls */
      fctrl = (ld10k1_ctl_dump_t *)ptr;
      memset(ctrl, 0, sizeof(emu10k1_fx8010_control_gpr_t) * header->ctl_count);
      for (i = 0; i < header->ctl_count; i++) {
            strcpy(ctrl[i].id.name, fctrl[i].name);
            ctrl[i].id.iface = EMU10K1_CTL_ELEM_IFACE_MIXER;
            ctrl[i].id.index = fctrl[i].index;
            ctrl[i].vcount = fctrl[i].vcount;
            ctrl[i].count = fctrl[i].count;
            for (j = 0; j < 32; j++) {
                  ctrl[i].gpr[j] = fctrl[i].gpr_idx[j];
                  ctrl[i].value[j] = fctrl[i].value[j];
            }
            ctrl[i].min = fctrl[i].min;
            ctrl[i].max = fctrl[i].max;
            ctrl[i].translation = fctrl[i].translation;
      }
      code.gpr_add_control_count = header->ctl_count;
      code.gpr_add_controls = ctrl;

      code.gpr_del_control_count = 0;
      code.gpr_del_controls = NULL;

      code.gpr_list_control_count = 0;
      code.gpr_list_controls = NULL;

      /* copy gprs */
      ptr += sizeof(ld10k1_ctl_dump_t) * header->ctl_count;
      fgpr = (unsigned int *)ptr;

      for (i = 0; i < sizeof(code.gpr_valid) / sizeof(unsigned long); i++)
            code.gpr_valid[i] = ~0;

      for (i = 0; i < header->gpr_count; i++)
            code.gpr_map[i] = fgpr[i];

      ptr += sizeof(unsigned int) * header->gpr_count;
      ftram = (ld10k1_tram_dump_t *)ptr;
      /* tram addr + data */
      for (i = 0; i < header->tram_count; i++) {
            addr = ftram[i].addr;
            vaddr = addr & 0xFFFFF;
            op = ftram[i].type;

            set_bit(i, code.tram_valid);
            switch(op) {
                  case DUMP_TRAM_READ:
                        if (audigy)
                              vaddr = vaddr | 0x2 << 20;
                        else
                              vaddr = vaddr | TANKMEMADDRREG_READ | TANKMEMADDRREG_ALIGN;
                        break;
                  case DUMP_TRAM_WRITE:
                        if (audigy)
                              vaddr = vaddr | 0x6 << 20;
                        else
                              vaddr = vaddr | TANKMEMADDRREG_WRITE | TANKMEMADDRREG_ALIGN;
                        break;
                  case DUMP_TRAM_NULL:
                  default:
                        vaddr = 0;
                        break;
            }

            code.tram_addr_map[i] = vaddr;
            code.tram_data_map[i] = ftram[i].data;
      }

      ptr += sizeof(ld10k1_tram_dump_t) * header->tram_count;
      finstr = (ld10k1_instr_dump_t *)ptr;
      for (iptr = code.code, i = 0; i < header->instr_count; i++, iptr += 2) {
            set_bit(i, code.code_valid);
            if (finstr[i].used) {
                  if (audigy) {
                        *iptr = ((finstr[i].arg[2] & 0x7ff) << 12) | (finstr[i].arg[3] & 0x7ff);
                        *(iptr + 1) = ((finstr[i].op & 0x0f) << 24) | ((finstr[i].arg[0] & 0x7ff) << 12) | (finstr[i].arg[1] & 0x7ff);
                  } else {
                        if (i < 0x200) {
                              *iptr = ((finstr[i].arg[2] & 0x3ff) << 10) | (finstr[i].arg[3] & 0x3ff);
                              *(iptr + 1) = ((finstr[i].op & 0x0f) << 20) | ((finstr[i].arg[0] & 0x3ff) << 10) | (finstr[i].arg[1] & 0x3ff);
                        }
                  }
            } else {
                  if (audigy) {
                        *iptr = ((0xcf & 0x7ff) << 12) | (0xc0 & 0x7ff);
                        *(iptr + 1) = ((0x0f & 0x0f) << 24) | ((0xc0 & 0x7ff) << 12) | (0xc0 & 0x7ff);
                  } else {
                        if (i < 0x200) {
                              *iptr = ((0x40 & 0x3ff) << 10) | (0x40 & 0x3ff);
                              *(iptr + 1) = ((0x06 & 0x0f) << 20) | ((0x40 & 0x3ff) << 10) | (0x40 & 0x3ff);
                        }
                  }
            }
      }

      if (header->dump_type != DUMP_TYPE_AUDIGY_OLD && 
            driver_set_tram_size(header->tram_size))
            goto err1;

      if (driver_init_dsp(audigy))
            goto err1;

      if (snd_hwdep_ioctl(handle, SNDRV_EMU10K1_IOCTL_CODE_POKE, &code) < 0) {
            error("unable to poke code");
            goto err1;
      }

      if (dump_data)
            free(dump_data);

      if (ctrl)
            free(ctrl);

      return 0;

err:
      error("wrong dump file format %s", file_name);
err1:
      free_code_struct(&code);
      if (dump_data)
            free(dump_data);
      if (ctrl)
            free(ctrl);

      return 1;
}

int main(int argc, char *argv[])
{
      int dev;
      int c;
      int err;
      int audigy;
      
      int opt_help = 0;
      char *opt_dump_file = NULL;

      char card_id[32];
      snd_ctl_t *ctl_handle;
      snd_ctl_card_info_t *card_info;
      snd_hwdep_info_t *hwdep_info;

      char name[16];

      snd_ctl_card_info_alloca(&card_info);
      snd_hwdep_info_alloca(&hwdep_info);

      static struct option long_options[] = {
                           {"help", 0, 0, 'h'},
                           {"card", 1, 0, 'c'},
                           {"dump", 1, 0, 'd'},
                           {0, 0, 0, 0}
               };

      int option_index = 0;
      while ((c = getopt_long(argc, argv, "hc:d:",
              long_options, &option_index)) != EOF) {
            switch (c) {
/*          case 0: */
/*                break; */
            case 'h':
                  opt_help = 1;
                  break;
            case 'd':
                  opt_dump_file = optarg;
                  break;
            case 'c':
                  card = snd_card_get_index(optarg);
                  if (card < 0 || card > 31) {
                        error("wrong -c argument '%s'\n", optarg);
                        return 1;
                  }
                  break;
            default:
                  return 1;
            }
      }

      if (opt_help) {
            help(argv[0]);
            return 0;
      }

      if (!opt_dump_file) {
            error("dump file not specified");
            return 1;
      }

      if (getuid() != 0 )
      {
            error("You are not running dl10k1 as root.");
            return 1;
      }
      
      /* Get control handle for selected card */
      sprintf(card_id, "hw:%i", card);
      if ((err = snd_ctl_open(&ctl_handle, card_id, 0)) < 0) {
            error("control open (%s): %s", card_id, snd_strerror(err));
            return 1;
      }

      /* Read control hardware info from card */
      if ((err = snd_ctl_card_info(ctl_handle, card_info)) < 0) {
            error("control hardware info (%s): %s", card_id, snd_strerror(err));
            exit(1);
      }

      if (!(card_proc_id = snd_ctl_card_info_get_id (card_info))) {
            error("card id (%s): %s", card_id, snd_strerror(err));
            exit(1);
      }


      /* EMU10k1/EMU10k2 chip is present only on SB Live, Audigy, Audigy 2, E-mu APS cards */
      if (strcmp(snd_ctl_card_info_get_driver(card_info), "EMU10K1") != 0 &&
          strcmp(snd_ctl_card_info_get_driver(card_info), "Audigy") != 0 &&
            strcmp(snd_ctl_card_info_get_driver(card_info), "Audigy2") != 0 &&
            strcmp(snd_ctl_card_info_get_driver(card_info), "E-mu APS") != 0) {
            error("not a EMU10K1/EMU10K2 based card");
            exit(1);
      }

      if (strcmp(snd_ctl_card_info_get_driver(card_info), "Audigy") == 0 ||
            strcmp(snd_ctl_card_info_get_driver(card_info), "Audigy2") == 0)
            audigy = 1;
      else
            audigy = 0;

      /* find EMU10k1 hardware dependant device and execute command */
      dev = -1;
      err = 1;
      while (1) {
            if (snd_ctl_hwdep_next_device(ctl_handle, &dev) < 0)
                  error("hwdep next device (%s): %s", card_id, snd_strerror(err));
            if (dev < 0)
                  break;
            snd_hwdep_info_set_device(hwdep_info, dev);
            if (snd_ctl_hwdep_info(ctl_handle, hwdep_info) < 0) {
                  if (err != -ENOENT)
                        error("control hwdep info (%s): %s", card_id, snd_strerror(err));
                  continue;
            }
            if (snd_hwdep_info_get_iface(hwdep_info) == SND_HWDEP_IFACE_EMU10K1) {
                  sprintf(name, "hw:%i,%i", card, dev);

                  /* open EMU10k1 hwdep device */
                  if ((err = snd_hwdep_open(&handle, name, O_WRONLY)) < 0) {
                        error("EMU10k1 open (%i-%i): %s", card, dev, snd_strerror(err));
                        exit(1);
                  }
                  
                  err = dump_load(audigy, opt_dump_file);

                  snd_hwdep_close(handle);

                  break;
            }
      }

      snd_ctl_close(ctl_handle);

      return 0;
}

Generated by  Doxygen 1.6.0   Back to index