1 /* $NetBSD: hdaudioctl.c,v 1.5 2020/07/01 12:19:45 sborrill Exp $ */ 2 3 /* 4 * Copyright (c) 2009 Precedence Technologies Ltd <support@precedence.co.uk> 5 * Copyright (c) 2009 Jared D. McNeill <jmcneill@invisible.ca> 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Precedence Technologies Ltd 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. The name of the author may not be used to endorse or promote products 17 * derived from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/types.h> 33 #include <sys/ioctl.h> 34 35 #include <prop/proplib.h> 36 37 #include <errno.h> 38 #include <fcntl.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <unistd.h> 43 #include <ctype.h> 44 45 #include <dev/hdaudio/hdaudioio.h> 46 #include <dev/hdaudio/hdaudioreg.h> 47 48 #include "hdaudioctl.h" 49 50 #define DEVPATH_HDAUDIO "/dev/hdaudio0" 51 52 const char *pin_devices[16] = { 53 "Line out", "Speaker", "Headphones", "CD", 54 "SPDIF Out", "Digital Out", "Modem Line", "Modem Handset", 55 "Line In", "AUX", "Mic In", "Telephony", 56 "SPDIF In", "Digital In", "Reserved", "Other" 57 }; 58 static const char *pin_jacks[16] = { 59 "Unknown", "1/8\"", "1/4\"", "ATAPI", 60 "RCA", "Optic", "Digital", "Analog", 61 "DIN", "XLR", "RJ-11", "Combo", 62 "0xC", "0xD", "0xE", "Other" 63 }; 64 static const char *pin_connections[4] = { 65 "Jack", "None", "Fixed", "Both" 66 }; 67 static const char *pin_colors[16] = { 68 "Unknown", "Black", "Grey", "Blue", 69 "Green", "Red", "Orange", "Yellow", 70 "Purple", "Pink", "Res. A", "Res. B", 71 "Res. C", "Res. D", "White", "Other" 72 }; 73 static const char *pin_locations[64] = { 74 "0x00", "Rear", "Front", "Left", 75 "Right", "Top", "Bottom", "Rear-panel", 76 "Drive-bay", "0x09", "0x0a", "0x0b", 77 "0x0c", "0x0d", "0x0e", "0x0f", 78 "Internal", "0x11", "0x12", "0x13", 79 "0x14", "0x15", "0x16", "Riser", 80 "0x18", "Onboard", "0x1a", "0x1b", 81 "0x1c", "0x1d", "0x1e", "0x1f", 82 "External", "Ext-Rear", "Ext-Front", "Ext-Left", 83 "Ext-Right", "Ext-Top", "Ext-Bottom", "0x07", 84 "0x28", "0x29", "0x2a", "0x2b", 85 "0x2c", "0x2d", "0x2e", "0x2f", 86 "Other", "0x31", "0x32", "0x33", 87 "0x34", "0x35", "Other-Bott", "Lid-In", 88 "Lid-Out", "0x39", "0x3a", "0x3b", 89 "0x3c", "0x3d", "0x3e", "0x3f" 90 }; 91 92 void 93 usage(void) 94 { 95 const char *prog; 96 prog = getprogname(); 97 98 fprintf(stderr, "usage: %s [-f dev] list\n", prog); 99 fprintf(stderr, " %s [-f dev] show <codecid> <nid>\n", prog); 100 fprintf(stderr, " %s [-f dev] get <codecid> <nid>\n", prog); 101 fprintf(stderr, " %s [-f dev] set <codecid> <nid> [plist]\n", 102 prog); 103 fprintf(stderr, " %s [-f dev] graph <codecid> <nid>\n", prog); 104 exit(EXIT_FAILURE); 105 } 106 107 static int 108 hdaudioctl_list(int fd) 109 { 110 prop_dictionary_t request, response; 111 prop_dictionary_t dict; 112 prop_object_iterator_t iter; 113 prop_object_t obj; 114 prop_array_t array; 115 uint16_t nid, codecid; 116 uint16_t vendor, product; 117 uint32_t subsystem; 118 const char *device = NULL; 119 int error; 120 121 request = prop_dictionary_create(); 122 if (request == NULL) { 123 fprintf(stderr, "out of memory\n"); 124 return ENOMEM; 125 } 126 127 error = prop_dictionary_sendrecv_ioctl(request, fd, 128 HDAUDIO_FGRP_INFO, &response); 129 if (error != 0) { 130 perror("HDAUDIO_FGRP_INFO failed"); 131 return error; 132 } 133 134 array = prop_dictionary_get(response, "function-group-info"); 135 iter = prop_array_iterator(array); 136 prop_object_iterator_reset(iter); 137 while ((obj = prop_object_iterator_next(iter)) != NULL) { 138 dict = (prop_dictionary_t)obj; 139 prop_dictionary_get_uint16(dict, "codecid", &codecid); 140 prop_dictionary_get_uint16(dict, "nid", &nid); 141 prop_dictionary_get_uint16(dict, "vendor-id", &vendor); 142 prop_dictionary_get_uint16(dict, "product-id", &product); 143 prop_dictionary_get_uint32(dict, "subsystem-id", &subsystem); 144 prop_dictionary_get_cstring_nocopy(dict, "device", &device); 145 146 printf("codecid 0x%02X nid 0x%02X vendor 0x%04X " 147 "product 0x%04X subsystem 0x%08X device %s\n", 148 codecid, nid, vendor, product, subsystem, 149 device ? device : "<none>"); 150 } 151 152 prop_object_release(array); 153 prop_object_release(response); 154 prop_object_release(request); 155 156 return 0; 157 } 158 159 static int 160 hdaudioctl_get(int fd, int argc, char *argv[]) 161 { 162 prop_dictionary_t request, response; 163 prop_array_t config; 164 uint16_t nid, codecid; 165 const char *xml; 166 int error; 167 168 if (argc != 2) 169 usage(); 170 171 codecid = strtol(argv[0], NULL, 0); 172 nid = strtol(argv[1], NULL, 0); 173 174 request = prop_dictionary_create(); 175 if (request == NULL) { 176 fprintf(stderr, "out of memory\n"); 177 return ENOMEM; 178 } 179 180 prop_dictionary_set_uint16(request, "codecid", codecid); 181 prop_dictionary_set_uint16(request, "nid", nid); 182 183 error = prop_dictionary_sendrecv_ioctl(request, fd, 184 HDAUDIO_FGRP_GETCONFIG, &response); 185 if (error != 0) { 186 perror("HDAUDIO_FGRP_GETCONFIG failed"); 187 return error; 188 } 189 190 config = prop_dictionary_get(response, "pin-config"); 191 xml = prop_array_externalize(config); 192 193 printf("%s\n", xml); 194 195 prop_object_release(response); 196 prop_object_release(request); 197 198 return 0; 199 } 200 201 static int 202 hdaudioctl_set(int fd, int argc, char *argv[]) 203 { 204 prop_dictionary_t request, response; 205 prop_array_t config = NULL; 206 uint16_t nid, codecid; 207 int error; 208 209 if (argc < 2 || argc > 3) 210 usage(); 211 212 codecid = strtol(argv[0], NULL, 0); 213 nid = strtol(argv[1], NULL, 0); 214 if (argc == 3) { 215 config = prop_array_internalize_from_file(argv[2]); 216 if (config == NULL) { 217 fprintf(stderr, 218 "couldn't load configuration from %s\n", argv[2]); 219 return EIO; 220 } 221 } 222 223 request = prop_dictionary_create(); 224 if (request == NULL) { 225 fprintf(stderr, "out of memory\n"); 226 return ENOMEM; 227 } 228 229 prop_dictionary_set_uint16(request, "codecid", codecid); 230 prop_dictionary_set_uint16(request, "nid", nid); 231 if (config) 232 prop_dictionary_set(request, "pin-config", config); 233 234 error = prop_dictionary_sendrecv_ioctl(request, fd, 235 HDAUDIO_FGRP_SETCONFIG, &response); 236 if (error != 0) { 237 perror("HDAUDIO_FGRP_SETCONFIG failed"); 238 return error; 239 } 240 241 prop_object_release(response); 242 prop_object_release(request); 243 244 return 0; 245 } 246 247 /* Based on page 178 onwards: 248 * https://www.intel.com/content/dam/www/public/us/en/documents/product-specifications/high-definition-audio-specification.pdf 249 * 31:30 = Port connectivity 250 * 29:24 = Location 251 * 23:20 = Default device 252 * 19:16 = Connection type 253 * 15:12 = Color 254 * 11:8 = Misc 255 * 7:4 = Default association 256 * 3:0 = Sequence 257 */ 258 259 static int 260 hdaudioctl_show(int fd, int argc, char *argv[]) 261 { 262 prop_dictionary_t request, response, dict; 263 prop_array_t array; 264 prop_object_iterator_t iter; 265 prop_object_t obj; 266 uint16_t nid, codecid; 267 uint32_t config; 268 int error; 269 const char *device, *conn, *jack, *loc, *color; 270 if (argc != 2) 271 usage(); 272 273 codecid = strtol(argv[0], NULL, 0); 274 nid = strtol(argv[1], NULL, 0); 275 276 request = prop_dictionary_create(); 277 if (request == NULL) { 278 fprintf(stderr, "out of memory\n"); 279 return ENOMEM; 280 } 281 282 prop_dictionary_set_uint16(request, "codecid", codecid); 283 prop_dictionary_set_uint16(request, "nid", nid); 284 285 error = prop_dictionary_sendrecv_ioctl(request, fd, 286 HDAUDIO_FGRP_GETCONFIG, &response); 287 if (error != 0) { 288 perror("HDAUDIO_FGRP_GETCONFIG failed"); 289 return error; 290 } 291 292 array = prop_dictionary_get(response, "pin-config"); 293 iter = prop_array_iterator(array); 294 prop_object_iterator_reset(iter); 295 printf("nid Data As Seq Device Conn Jack " 296 "Location Color Misc\n"); 297 printf("==================================================" 298 "=======================\n"); 299 while ((obj = prop_object_iterator_next(iter)) != NULL) { 300 dict = (prop_dictionary_t)obj; 301 prop_dictionary_get_uint32(dict, "config", &config); 302 prop_dictionary_get_uint16(dict, "nid", &nid); 303 device = pin_devices[(config >> 20U) & 0xf]; 304 conn = pin_connections[(config >> 30U) & 0x3]; 305 jack = pin_jacks[(config >> 16) & 0xf]; 306 loc = pin_locations[(config >> 24) & 0x3f]; 307 color = pin_colors[(config >> 12) & 0xf]; 308 printf("0x%2X %08X %2d %3d %-14s %-5s %-7s %-10s %-7s %4X\n", 309 nid, config, ((config >> 4U) & 0xf), (config & 0xf), 310 device, conn, jack, loc, color, ((config >> 8U) & 0xf)); 311 } 312 prop_object_release(array); 313 prop_object_release(response); 314 prop_object_release(request); 315 316 return 0; 317 } 318 319 320 int 321 main(int argc, char *argv[]) 322 { 323 int fd, error; 324 int ch; 325 const char *devpath = DEVPATH_HDAUDIO; 326 327 while ((ch = getopt(argc, argv, "f:h")) != -1) { 328 switch (ch) { 329 case 'f': 330 devpath = strdup(optarg); 331 break; 332 case 'h': 333 default: 334 usage(); 335 /* NOTREACHED */ 336 } 337 } 338 argc -= optind; 339 argv += optind; 340 341 if (argc < 1) 342 usage(); 343 344 fd = open(devpath, O_RDWR); 345 if (fd < 0) { 346 fprintf(stderr, "Error opening %s: %s\n", devpath, 347 strerror(errno)); 348 return EXIT_FAILURE; 349 } 350 351 error = 0; 352 if (strcmp(argv[0], "list") == 0) 353 error = hdaudioctl_list(fd); 354 else if (strcmp(argv[0], "get") == 0) 355 error = hdaudioctl_get(fd, argc - 1, argv + 1); 356 else if (strcmp(argv[0], "set") == 0) 357 error = hdaudioctl_set(fd, argc - 1, argv + 1); 358 else if (strcmp(argv[0], "graph") == 0) 359 error = hdaudioctl_graph(fd, argc - 1, argv + 1); 360 else if (strcmp(argv[0], "show") == 0) 361 error = hdaudioctl_show(fd, argc - 1, argv + 1); 362 else 363 usage(); 364 365 close(fd); 366 367 if (error) 368 return EXIT_FAILURE; 369 return EXIT_SUCCESS; 370 } 371