/* * Temporary utility used to develop the next hdaudio driver * version. Not usable. */ /* * * This file is part of Open Sound System. * * Copyright (C) 4Front Technologies 1996-2008. * * This this source file is released under GPL v2 license (no other versions). * See the COPYING file included in the main directory of this source * distribution for the license terms and conditions. * */ #include #include #include #include #include #include #include #include #include #define DRIVER_NICK "hdaudio" #define CE_CONT 0 #define CE_NOTE 1 #define CE_WARN 2 #define DDB(x) #define PMALLOC(osdev, s) malloc(s) typedef int sound_os_info, mixer_create_controls_t; typedef struct { char *hw_info; } oss_device_t; #include #define ioctl_arg int #define mixer_ext_init_fn int #include //#include int fd; #undef corb_read #undef corb_write typedef struct { int association, sequence; int nwidgets; int jack_count; #define PATH_MAXWID 8 widget_t *widgets[PATH_MAXWID]; } path_t; #define MAX_PATHS 32 path_t *paths[MAX_PATHS]; int npaths=0; int corb_write (void *dc, unsigned int cad, unsigned int nid, unsigned int d, unsigned int verb, unsigned int parm) { unsigned int tmp; tmp = (cad << 28) | (d << 27) | (nid << 20) | (verb << 8) | parm; if (ioctl (fd, HDA_IOCTL_WRITE, &tmp) == -1) { perror ("HDA_IOCTL_WRITE"); return 0; } return 1; } static int corb_read (void *dc, unsigned int cad, unsigned int nid, unsigned int d, unsigned int verb, unsigned int parm, unsigned int *upper, unsigned int *lower) { unsigned int tmp; tmp = (cad << 28) | (d << 27) | (nid << 20) | (verb << 8) | parm; if (ioctl (fd, HDA_IOCTL_READ, &tmp) == -1) { /* perror("HDA_IOCTL_READ"); */ if (errno == EINVAL) { fprintf (stderr, "hdaudio_snoopy mode is not available\n"); exit (-1); } return 0; } *upper = tmp; *lower = 0; return 1; } void cmn_err (int level, char *s, ...) { char tmp[1024], *a[6]; va_list ap; int i, n = 0; va_start (ap, s); for (i = 0; i < strlen (s); i++) if (s[i] == '%') n++; for (i = 0; i < n && i < 6; i++) { a[i] = va_arg (ap, char *); } for (i = n; i < 6; i++) a[i] = NULL; if (level == CE_CONT) { sprintf (tmp, s, a[0], a[1], a[2], a[3], a[4], a[5], NULL, NULL, NULL, NULL); printf ("%s", tmp); } else { strcpy (tmp, DRIVER_NICK ": "); sprintf (tmp + strlen (tmp), s, a[0], a[1], a[2], a[3], a[4], a[5], NULL, NULL, NULL, NULL); printf ("%s", tmp); } va_end (ap); } /* ******************************** */ static const char *widget_id[16] = { "pcm", "rec", "mix", "select", "jack", "power", "vol", "beep", "unkn", "unkn", "unkn", "unkn", "unkn", "unkn", "unkn", "vendor" }; static int attach_pin_widget (hdaudio_mixer_t * mixer, codec_t * codec, widget_t * widget, int cad, int wid, int group_type) { unsigned int conf; int num = codec->jack_number++; unsigned int pincaps, b; int association, sequence; int default_device; int default_loc; int conn; int color; int no_color = 0; char *name = NULL, *loc = "", *color_name = NULL; if (!corb_read (mixer, cad, wid, 0, GET_CONFIG_DEFAULT, 0, &conf, &b)) return 0; default_device = (conf >> 20) & 0x0f; default_loc = (conf >> 24) & 0x3f; conn = (conf >> 30) & 0x03; if (!corb_read (mixer, cad, wid, 0, GET_PARAMETER, HDA_PIN_CAPS, &pincaps, &b)) { cmn_err (CE_WARN, "GET_PARAMETER HDA_PIN_CAPS failed\n"); return 0; } widget->pincaps = pincaps; if (conf == 0) { cmn_err (CE_WARN, "CONFIG_DEFAULT information not provided by BIOS for cad=%d, wid=%02x\n", cad, wid); cmn_err (CE_CONT, "Cannot work in this system.\n"); return 0; } color = (conf >> 12) & 0x0f; association = (conf>>4) & 0xf; sequence = conf & 0xf; if (pincaps & (1 << 6)) cmn_err (CE_WARN, "Balanced I/O not supported\n"); if (!(pincaps & (1 << 5))) /* No input */ widget->pin_type = PIN_OUT; if (!(pincaps & (1 << 4))) widget->pin_type = PIN_IN; DDB (cmn_err (CE_CONT, "\tConfig default %08x\n", conf)); if ((default_loc & 0x0f) == 0x1) /* Rear panel - default */ loc = ""; if ((default_loc & 0x0f) == 0x2) /* Front panel */ loc = "fp-"; if ((default_loc & 0xf0) == 0x10) /* Internal func - eg cd/tad/spk */ loc = "int-"; if (conn == 1) /* Pin not connected to anything */ { widget->skip = 1; widget->skip_output = 1; } widget->association = association; widget->sequence = sequence; switch (default_device) { case 0x0: name = "lineout"; widget->pin_type = PIN_OUT; break; case 0x1: name = "speaker"; no_color = 1; widget->pin_type = PIN_OUT; break; case 0x2: name = "headphone"; widget->pin_type = PIN_OUT; break; case 0x3: name = "cd"; no_color = 1; widget->pin_type = PIN_IN; break; case 0x4: name = "spdifout"; no_color = 1; widget->pin_type = PIN_OUT; break; case 0x5: name = "digout"; no_color = 1; widget->pin_type = PIN_OUT; break; case 0x6: name = "modem"; no_color = 1; break; case 0x7: name = "phone"; no_color = 1; break; case 0x8: name = "linein"; widget->pin_type = PIN_IN; break; case 0x9: name = "aux"; break; case 0xa: name = "mic"; widget->pin_type = PIN_MIC; break; case 0xb: name = "telephony"; no_color = 1; break; case 0xc: name = "spdifin"; no_color = 1; break; case 0xd: name = "digin"; no_color = 1; break; case 0xe: name = "reserved"; no_color = 1; break; case 0xf: /* Unused pin widget */ widget->skip = 1; widget->skip_output = 1; break; } /* process only colored jacks and skip fixed function jacks */ switch (color) { case 0x1: color_name = "black"; widget->rgbcolor = OSS_RGB_BLACK; break; case 0x2: color_name = "gray"; widget->rgbcolor = OSS_RGB_GRAY; break; case 0x3: color_name = "blue"; widget->rgbcolor = OSS_RGB_BLUE; break; case 0x4: color_name = "green"; widget->rgbcolor = OSS_RGB_GREEN; break; case 0x5: color_name = "red"; widget->rgbcolor = OSS_RGB_RED; break; case 0x6: color_name = "orange"; widget->rgbcolor = OSS_RGB_ORANGE; break; case 0x7: color_name = "yellow"; widget->rgbcolor = OSS_RGB_YELLOW; break; case 0x8: color_name = "purple"; widget->rgbcolor = OSS_RGB_PURPLE; break; case 0x9: color_name = "pink"; widget->rgbcolor = OSS_RGB_PINK; break; case 0xe: color_name = "white"; widget->rgbcolor = OSS_RGB_WHITE; break; default: if (name != NULL) color_name = name; else color_name = "internal"; } if (no_color) widget->rgbcolor = 0; if (default_device == 0xf) /* Not present */ { widget->rgbcolor = 0; color_name = "internal"; } sprintf (widget->color, "%s%s", loc, color_name); if (name == NULL || default_device == 0x00) name = color_name; sprintf (widget->name, "%s%s", loc, name); DDB(cmn_err(CE_CONT, "\tJack name %s, color %s (%06x)\n", widget->name, widget->color, widget->rgbcolor)); DDB(cmn_err(CE_CONT, "\tAssociation %x, sequence %x\n", association, sequence)); printf("Widget %02x: default_device=%d, name=%s, color=%s\n", wid, default_device, widget->name, widget->color); return 1; } static int attach_widget (hdaudio_mixer_t * mixer, int cad, int wid, int group_type) { static const char *widget_types[16] = { "Audio output", "Audio input", "Audio mixer", "Audio selector", "Pin complex", "Power widget", "Volume knob", "Beep generator", "Reserved8", "Reserved9", "ReservedA", "ReservedB", "ReservedC", "ReservedD", "ReservedE", "Vendor defined audio" }; static const int bit_sizes[] = { 8, 16, 20, 24, 32 }; static const unsigned int bit_fmts[] = { AFMT_U8, AFMT_S16_LE, AFMT_S32_LE, AFMT_S32_LE, AFMT_S32_LE }; static const int srates[] = { 8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 384000 }; unsigned int widget_caps, b, pstate; unsigned int inamp_caps, outamp_caps; int wid_type; int i; codec_t *codec = mixer->codecs[cad]; widget_t *widget; if (codec == NULL) return 0; if (wid >= MAX_WIDGETS) { cmn_err (CE_WARN, "Too many widgets for codec %d (%d/%d)\n", cad, wid, MAX_WIDGETS); return 0; } mixer->ncontrols++; codec->nwidgets = wid + 1; widget = &codec->widgets[wid]; widget->cad = cad; widget->wid = wid; widget->rgbcolor = 0; widget->group_type = group_type; DDB (cmn_err (CE_CONT, " * Widget %02x, type %d\n", wid, group_type)); if (!corb_read (mixer, cad, wid, 0, GET_PARAMETER, HDA_WIDGET_CAPS, &widget_caps, &b)) { cmn_err (CE_WARN, "GET_PARAMETER HDA_WIDGET_CAPS failed\n"); return 0; } if (widget_caps & WCAP_AMP_CAP_OVERRIDE) /* Amp param override? */ { if (!corb_read (mixer, widget->cad, widget->wid, 0, GET_PARAMETER, HDA_OUTPUTAMP_CAPS, &outamp_caps, &b)) { cmn_err (CE_WARN, "GET_PARAMETER HDA_OUTPUTAMP_CAPS failed\n"); return -EIO; } widget->outamp_caps = outamp_caps; if (!corb_read (mixer, widget->cad, widget->wid, 0, GET_PARAMETER, HDA_INPUTAMP_CAPS, &inamp_caps, &b)) { cmn_err (CE_WARN, "GET_PARAMETER HDA_INPUTAMP_CAPS failed\n"); return -EIO; } widget->inamp_caps = inamp_caps; } else { widget->outamp_caps = outamp_caps = codec->default_outamp_caps; widget->inamp_caps = inamp_caps = codec->default_inamp_caps; } if (!corb_read (mixer, cad, wid, 0, GET_POWER_STATE, 0, &pstate, &b)) return 0; /* power up each of the widgets if there is a Power Capability (1<<10) */ if (widget_caps & WCAP_POWER_CTL) corb_write (mixer, cad, wid, 0, SET_POWER_STATE, 0); widget->widget_caps = widget_caps; wid_type = (widget_caps >> 20) & 0x0f; DDB (cmn_err (CE_CONT, "\tWidget type %d (%s)(%s)\n", wid_type, widget_types[wid_type], widget_id[wid_type])); DDB (cmn_err (CE_CONT, "\tPower State %d\n", pstate)); if (widget_caps & WCAP_CONN_LIST) { unsigned int clen; /* Handle connection list */ if (!corb_read (mixer, cad, wid, 0, GET_PARAMETER, HDA_CONNLIST_LEN, &clen, &b)) { cmn_err (CE_WARN, "GET_PARAMETER HDA_CONNLIST_LEN failed\n"); return 0; } if (clen & 0x80) { cmn_err (CE_WARN, "Long form connection list not supported\n"); return 0; } if (clen > 0) { if (clen > MAX_CONN) { cmn_err (CE_WARN, "Too many connections\n"); return 0; } DDB (cmn_err (CE_CONT, "\tConn list (%d): ", clen)); for (i = 0; i < clen; i += 4) { int j; unsigned int a; if (!corb_read (mixer, cad, wid, 0, GET_CONNECTION_LIST_ENTRY, i, &a, &b)) { cmn_err (CE_WARN, "GET_CONNECTION_LIST_ENTRY failed\n"); return 0; } for (j = 0; j < 4 && (i + j) < clen; j++) { int v, is_range = 0; if (widget->nconn >= MAX_CONN) { cmn_err (CE_WARN, "Too many connections for widget %d (%d)\n", widget->wid, widget->nconn); break; } v = (a >> (j * 8)) & 0xff; DDB (cmn_err (CE_CONT, "%d ", v)); if (v & 0x80) { is_range = 1; v &= ~0x80; } if (v < 0 || v >= MAX_WIDGETS) { cmn_err (CE_NOTE, "Connection %d for widget %d is out of range (%d) - Skipped\n", j, widget->wid, v); continue; } if (is_range) /* Range: prev...v */ { int x; codec->widgets[v].references[codec->widgets[v]. refcount++] = wid; if (widget->nconn < 1) { cmn_err (CE_CONT, "Bad range connection for widget %d\n", widget->wid); continue; } for (x = widget->connections[widget->nconn - 1] + 1; x <= v; x++) { if (widget->nconn >= MAX_CONN) { cmn_err (CE_WARN, "Too many connectionsi(B) for widget %d (%d)\n", widget->wid, widget->nconn); break; } widget->connections[widget->nconn++] = x; codec->widgets[x].references[codec->widgets[x]. refcount++] = wid; } } else { widget->connections[widget->nconn++] = v; codec->widgets[v].references[codec->widgets[v]. refcount++] = wid; } } } DDB (cmn_err (CE_CONT, "\n")); } } widget->wid_type = wid_type; strcpy (widget->name, widget_id[wid_type]); /* * Handle widget based on its type. */ switch (wid_type) { case NT_PIN: if (!attach_pin_widget (mixer, codec, widget, cad, wid, group_type)) return 0; break; } return 1; } /*ARGSUSED*/ static int attach_function_group (hdaudio_mixer_t * mixer, int cad, int wid, int group_type) { unsigned int a, b, gt; int i, first_node, num_nodes; codec_t *codec = mixer->codecs[cad]; if (codec == NULL) return 0; cmn_err (CE_CONT, "Attach function group, cad=%02x, wid=%02x\n", cad, wid); if (!corb_read (mixer, cad, wid, 0, GET_PARAMETER, HDA_OUTPUTAMP_CAPS, &codec->default_outamp_caps, &b)) { cmn_err (CE_WARN, "GET_PARAMETER HDA_OUTPUTAMP_CAPS failed\n"); return 0; } if (!corb_read (mixer, cad, wid, 0, GET_PARAMETER, HDA_INPUTAMP_CAPS, &codec->default_inamp_caps, &b)) { cmn_err (CE_WARN, "GET_PARAMETER HDA_INPUTTAMP_CAPS failed\n"); return 0; } if (!corb_read (mixer, cad, wid, 0, GET_PARAMETER, HDA_NODE_COUNT, &a, &b)) { cmn_err (CE_WARN, "GET_PARAMETER HDA_NODE_COUNT2 failed\n"); return 0; } first_node = (a >> 16) & 0xff; num_nodes = a & 0xff; corb_read (mixer, cad, wid, 0, GET_PARAMETER, HDA_GROUP_TYPE, >, &b); gt &= 0xff; DDB (cmn_err (CE_CONT, " * Function group %d First node %d, num nodes %d, group type %x\n", wid, first_node, num_nodes, gt)); /* * Ignore other than audio function groups. Codecs probably allocate * higher widget number for the modem group than the audio group. So in this * way we can have smaller MAX_WIDGETS which in turn conserves memory. */ if (gt != group_type) return 0; if (corb_read (mixer, cad, wid, 0, GET_PARAMETER, HDA_PCM_SIZES, &a, &b)) { codec->sizes = a; } if (first_node > 0) for (i = first_node; i < first_node + num_nodes; i++) if (!attach_widget (mixer, cad, i, gt)) return 0; if (num_nodes >= 1) codec->active = 1; return 1; } static int attach_codec (hdaudio_mixer_t * mixer, int cad, char *hw_info, unsigned int pci_subdevice, int group_type) { unsigned int a, b, x; int i; int first_node, num_nodes; int has_audio_group = 0; codec_t *codec; if (cad >= MAX_CODECS) { cmn_err (CE_WARN, "attach_codec: Too many codecs %d\n", cad); return -EIO; } mixer->ncodecs = cad + 1; if (mixer->codecs[cad] == NULL) { if ((codec = PMALLOC (mixer->osdev, sizeof (*codec))) == NULL) { cmn_err (CE_CONT, "Cannot allocate codec descriptor\n"); return -ENOMEM; } memset (codec, 0, sizeof (*codec)); mixer->codecs[cad] = codec; } else { codec = mixer->codecs[cad]; } corb_write (mixer, cad, 0, 0, SET_POWER_STATE, 0); /* Power up everything */ if (!corb_read (mixer, cad, 0, 0, GET_PARAMETER, HDA_VENDOR, &a, &b)) { if (group_type == 1) { sprintf (hw_info, " Codec %2d: Not present\n", cad); cmn_err (CE_NOTE, "attach_codec: Codec #%d is not physically present\n", cad); } return -EIO; } codec->vendor_id = a; /* * Find out the primary group list */ if (!corb_read (mixer, cad, 0, 0, GET_PARAMETER, HDA_NODE_COUNT, &x, &b)) { cmn_err (CE_WARN, "GET_PARAMETER HDA_NODE_COUNT3 failed\n"); return -EIO; } codec->first_node = first_node = (x >> 16) & 0xff; num_nodes = x & 0xff; /* * Check if this one is an audio codec (has an audio function group) */ for (i = first_node; i < first_node + num_nodes; i++) { unsigned int gt; if (corb_read (mixer, cad, i, 0, GET_PARAMETER, HDA_GROUP_TYPE, >, &b)); if ((gt & 0xff) != 1) /* Audio function group */ continue; has_audio_group = 1; } #if 0 /* * Find codec specific settings */ for (ix = 0; codecs[ix].id != 0; ix++) if (codecs[ix].id == a) break; DDB (cmn_err (CE_CONT, "HD audio Codec ID: %08x (%s)\n", a, codecs[ix].name)); if (codecs[ix].id == 0) /* Unknown codec */ { if (group_type == 1) sprintf (hw_info, " Codec %2d: Unknown (0x%08x", cad, a); cmn_err (CE_NOTE, "Unknown HDA codec 0x%08x\n", a); /* * Create hexadecimal codec ID */ if (has_audio_group && mixer->chip_name == NULL) if ((mixer->chip_name = PMALLOC (mixer->osdev, 32)) != NULL) { sprintf (mixer->chip_name, "0x%08x", a); } } else { if (group_type == 1) sprintf (hw_info, " Codec %2d: %s (0x%08x", cad, codecs[ix].name, a); } if (has_audio_group && mixer->chip_name == NULL) { mixer->chip_name = codecs[ix].name; } if (codecs[ix].remap != NULL) codec->remap = codecs[ix].remap; if (codecs[ix].flags != 0) codec->vendor_flags = codecs[ix].flags; if (codecs[ix].mixer_init != NULL) codec->mixer_init = codecs[ix].mixer_init; codec->multich_map = codecs[ix].multich_map; codec->codec_desc = &codecs[ix]; #endif if (corb_read (mixer, cad, 0, 0, GET_PARAMETER, HDA_REVISION, &a, &b)) { DDB (cmn_err (CE_CONT, "HDA codec revision %d.%d (%d.%d) (0x%08x)\n", (a >> 20) & 0xf, (a >> 16) & 0xf, (a >> 8) & 0xff, a & 0xff, a)); } else DDB (cmn_err (CE_CONT, "Can't get codec revision\n")); DDB (cmn_err (CE_CONT, "**** Codec %d ****\n", cad)); DDB (cmn_err (CE_CONT, "First group node %d, num nodes %d\n", first_node, num_nodes)); for (i = first_node; i < first_node + num_nodes; i++) { corb_read (mixer, cad, i, 0, GET_PARAMETER, HDA_GROUP_TYPE, &a, &b); if ((a & 0xff) == group_type) /* Proper function group type */ { codec->afg = i; if (corb_read (mixer, cad, i, 0, GET_SUBSYSTEM_ID, 0, &a, &b)) { DDB (cmn_err (CE_CONT, "Subsystem ID = 0x%08x\n", a)); if (group_type == 1) { /* Append subvendor ID to hw_info */ hw_info += strlen (hw_info); sprintf (hw_info, "/0x%08x", a); } codec->subvendor_id = a; #if 0 for (subix = 0; subdevices[subix].id != 0; subix++) if (subdevices[subix].id == a) { if (subdevices[subix].main_id != 0) if (subdevices[subix].main_id != codec->vendor_id) continue; if (subdevices[subix].pci_subdevice != 0) if (subdevices[subix].pci_subdevice != pci_subdevice) continue; DDB (cmn_err (CE_CONT, "Subdevice known as %s\n", subdevices[subix].name)); if (group_type == 1) { hw_info += strlen (hw_info); sprintf (hw_info, " %s", subdevices[subix].name); } if (subdevices[subix].remap != NULL) { codec->remap = subdevices[subix].remap; } if (subdevices[subix].multich_map != 0) codec->multich_map = subdevices[subix].multich_map; if (subdevices[subix].flags != 0) codec->vendor_flags = subdevices[subix].flags; if (subdevices[subix].mixer_init != NULL) { codec->mixer_init = subdevices[subix].mixer_init; } } #endif } break; } } hw_info += strlen (hw_info); if (group_type == 1) strcpy (hw_info, ")\n"); for (i = first_node; i < first_node + num_nodes; i++) { if (!attach_function_group (mixer, cad, i, group_type)) continue; /* power up the AFG! */ corb_write (mixer, cad, i, 0, SET_POWER_STATE, 0); } #if 0 if (has_audio_group) { polish_widget_list (mixer, cad); } copy_endpoints (mixer, codec, 0); /* Copy analog endpoints from codec to mixer */ #endif return (has_audio_group) ? 0 : -EIO; } static void follow_path(hdaudio_mixer_t *mixer, codec_t *codec, widget_t *widget, path_t *path, int use_force) { int i; if (widget->used) return; widget->used = 1; path->widgets[path->nwidgets++] = widget; if (widget->wid_type == NT_PIN) { path->jack_count++; path->association=widget->association; path->sequence=widget->sequence; /* * Stop at a pin widget that is not the first widget of the path. * In this way we don't walk back the input chain from the pin. */ if (path->nwidgets > 1) return; } if (use_force || path->nwidgets == 1 || widget->nconn == 1 || (widget->nconn>0 && widget->wid_type == NT_SELECT)) { for (i=0;inconn;i++) if (!codec->widgets[widget->connections[i]].used) //if (codec->widgets[widget->connections[i]].refcount < 2) { follow_path(mixer, codec, &codec->widgets[widget->connections[i]], path, use_force); return; } } } static void dump_path(hdaudio_mixer_t *mixer, path_t *path) { int i, j; if (path->nwidgets == 0) return; printf("Path (a=%x, s=%x, jc=%d):\n ", path->association, path->sequence, path->jack_count); for (i=0;inwidgets;i++) { widget_t *widget = path->widgets[i]; printf("\t%02x(%s/%s/nc=%d/rc=%d", widget->wid, widget_id[widget->wid_type], widget->name, widget->nconn, widget->refcount); printf(")\n"); if (widget->widget_caps & WCAP_OUTPUT_AMP_PRESENT) { printf("\t\tOutput amp\n"); } if (widget->widget_caps & WCAP_INPUT_AMP_PRESENT) { printf("\t\t%d input amp(s)\n", widget->nconn); for (j=0;jnconn;j++) { widget_t *in_widget; in_widget = &mixer->codecs[widget->cad]-> widgets[widget->connections[j]]; printf("\t\t\t%02x(%s/%s/nc=%d/rc=%d)\n", in_widget->wid, widget_id[in_widget->wid_type], in_widget->name, in_widget->nconn, in_widget->refcount); in_widget->used=1; } } } printf("\n"); } static void store_path(hdaudio_mixer_t *mixer, path_t *path) { if (npaths>=MAX_PATHS) { cmn_err(CE_WARN, "Too many paths\n"); return; } paths[npaths++]=path; } static void create_path_list(hdaudio_mixer_t *mixer, int cad, int node_type, int use_force) { codec_t *codec; int wid; path_t *path; cmn_err(CE_CONT, "Create path list %d (%s)\n", node_type, widget_id[node_type]); codec=mixer->codecs[cad]; for (wid=0;widnwidgets;wid++) if (codec->widgets[wid].wid_type == node_type) if (!codec->widgets[wid].skip) if (codec->widgets[wid].wid == wid) { path=malloc(sizeof(*path)); follow_path(mixer, codec, &codec->widgets[wid], path, use_force); if (path->nwidgets>1) store_path(mixer, path); else if (path->nwidgets>0) path->widgets[0]->used=0; } } static void create_path_list_for_pin(hdaudio_mixer_t *mixer, int cad, int association, int sequence) { codec_t *codec; int wid; path_t *path; codec=mixer->codecs[cad]; for (wid=0;widnwidgets;wid++) if (codec->widgets[wid].wid_type == NT_PIN && !codec->widgets[wid].skip) if (codec->widgets[wid].pin_type == PIN_HEADPHONE || codec->widgets[wid].pin_type == PIN_OUT) if (codec->widgets[wid].wid == wid) if (codec->widgets[wid].nconn > 0) if (!codec->widgets[wid].skip) if (codec->widgets[wid].sequence == sequence) if (codec->widgets[wid].association == association) { path=malloc(sizeof(*path)); follow_path(mixer, codec, &codec->widgets[wid], path, 0); if (path->nwidgets>1) store_path(mixer, path); else if (path->nwidgets>0) path->widgets[0]->used=0; } } hdaudio_mixer_t * hdaudio_mixer_create (char *name, void *devc, oss_device_t * osdev, hdmixer_write_t writefunc, hdmixer_read_t readfunc, unsigned int codecmask, unsigned int vendor_id, unsigned int subvendor_id) { hdaudio_mixer_t *mixer; int i, func; int ncodecs = 0; char tmp[128]; int mixer_dev; char *hw_info = osdev->hw_info; /* For printing hardware information */ if ((mixer = PMALLOC (osdev, sizeof (*mixer))) == NULL) { cmn_err (CE_WARN, "hdaudio_mixer_create: Out of memory\n"); return NULL; } memset (mixer, 0, sizeof (*mixer)); mixer->devc = devc; mixer->osdev = osdev; mixer->mixer_dev = 0; strncpy (mixer->name, name, sizeof (mixer->name) - 1); mixer->name[sizeof (mixer->name) - 1] = 0; for (i = 0; i < MAX_CODECS; i++) mixer->codecs[i] = NULL; #ifdef _KERNEL mixer->read = readfunc; mixer->write = writefunc; #endif mixer->codecmask = codecmask; sprintf (hw_info, "HD Audio controller %s\n" "Vendor ID 0x%08x\n" "Subvendor ID 0x%08x\n", name, vendor_id, subvendor_id); hw_info += strlen (hw_info); /* * Search first all audio function groups for all codecs and then * handle modem function groups. */ for (func = 1; func <= 2; func++) for (i = 0; i < 16; i++) if (mixer->codecmask & (1 << i)) { if (attach_codec (mixer, i, hw_info, subvendor_id, func) >= 0) ncodecs++; hw_info += strlen (hw_info); } for (i = 0; i < 16; i++) if (mixer->codecmask & (1 << i)) { int association, sequence; printf("*** Codec %d\n", i); for (association=1;association<16;association++) for (sequence=0;sequence<16;sequence++) create_path_list_for_pin(mixer, i, association, sequence); create_path_list(mixer, i, NT_ADC, 0); create_path_list(mixer, i, NT_MIXER, 1); create_path_list(mixer, i, NT_SELECT, 1); } for (i=0;icodecmask & (1 << i)) { int wid; for (wid=0;widcodecs[i]->nwidgets;wid++) { widget_t *widget = &mixer->codecs[i]->widgets[wid]; if (widget->wid != wid) continue; if (widget->used || widget->skip) continue; printf("Codec %d, Widget %02x %s/%s/%d\n", widget->cad, widget->wid, widget_id[widget->wid_type], widget->name, widget->nconn); } } #endif return mixer; } int main (int argc, char *argv[]) { unsigned int rev, b; int first_node, num_nodes; int i; int cad = 0; int codecmask = 0; oss_device_t osdev = { 0 }; osdev.hw_info = malloc (256); if ((fd = open ("/dev/oss/oss_hdaudio0/pcm0", O_RDWR | O_EXCL, 0)) == -1) { perror ("/dev/oss/oss_hdaudio0/pcm0"); exit (-1); } for (cad = 0; cad < 15; cad++) if (corb_read (NULL, cad, 0, 0, GET_PARAMETER, HDA_REVISION, &rev, &b)) { #if 1 printf ("Codec %2d: HD codec revision %d.%d (%d.%d) (0x%08x)\n", cad, (rev >> 20) & 0xf, (rev >> 16) & 0xf, (rev >> 8) & 0xff, rev & 0xff, rev); #endif codecmask |= (1 << cad); } hdaudio_mixer_create ("ACME hdaudio", NULL, &osdev, NULL, NULL, codecmask, 0x12345678, 0x8754321); //printf("HW Info: %s\n", osdev.hw_info); exit (0); }