1 /****************************************************************************** 2 3 AudioScience HPI driver 4 Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com> 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of version 2 of the GNU General Public License as 8 published by the Free Software Foundation; 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, write to the Free Software 17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 19 \file hpicmn.c 20 21 Common functions used by hpixxxx.c modules 22 23 (C) Copyright AudioScience Inc. 1998-2003 24 *******************************************************************************/ 25 #define SOURCEFILE_NAME "hpicmn.c" 26 27 #include "hpi_internal.h" 28 #include "hpidebug.h" 29 #include "hpimsginit.h" 30 31 #include "hpicmn.h" 32 33 struct hpi_adapters_list { 34 struct hpios_spinlock list_lock; 35 struct hpi_adapter_obj adapter[HPI_MAX_ADAPTERS]; 36 u16 gw_num_adapters; 37 }; 38 39 static struct hpi_adapters_list adapters; 40 41 /** 42 * Given an HPI Message that was sent out and a response that was received, 43 * validate that the response has the correct fields filled in, 44 * i.e ObjectType, Function etc 45 **/ 46 u16 hpi_validate_response(struct hpi_message *phm, struct hpi_response *phr) 47 { 48 if (phr->type != HPI_TYPE_RESPONSE) { 49 HPI_DEBUG_LOG(ERROR, "header type %d invalid\n", phr->type); 50 return HPI_ERROR_INVALID_RESPONSE; 51 } 52 53 if (phr->object != phm->object) { 54 HPI_DEBUG_LOG(ERROR, "header object %d invalid\n", 55 phr->object); 56 return HPI_ERROR_INVALID_RESPONSE; 57 } 58 59 if (phr->function != phm->function) { 60 HPI_DEBUG_LOG(ERROR, "header type %d invalid\n", 61 phr->function); 62 return HPI_ERROR_INVALID_RESPONSE; 63 } 64 65 return 0; 66 } 67 68 u16 hpi_add_adapter(struct hpi_adapter_obj *pao) 69 { 70 u16 retval = 0; 71 /*HPI_ASSERT(pao->wAdapterType); */ 72 73 hpios_alistlock_lock(&adapters); 74 75 if (pao->index >= HPI_MAX_ADAPTERS) { 76 retval = HPI_ERROR_BAD_ADAPTER_NUMBER; 77 goto unlock; 78 } 79 80 if (adapters.adapter[pao->index].adapter_type) { 81 { 82 retval = HPI_DUPLICATE_ADAPTER_NUMBER; 83 goto unlock; 84 } 85 } 86 adapters.adapter[pao->index] = *pao; 87 hpios_dsplock_init(&adapters.adapter[pao->index]); 88 adapters.gw_num_adapters++; 89 90 unlock: 91 hpios_alistlock_unlock(&adapters); 92 return retval; 93 } 94 95 void hpi_delete_adapter(struct hpi_adapter_obj *pao) 96 { 97 if (!pao->adapter_type) { 98 HPI_DEBUG_LOG(ERROR, "removing null adapter?\n"); 99 return; 100 } 101 102 hpios_alistlock_lock(&adapters); 103 if (adapters.adapter[pao->index].adapter_type) 104 adapters.gw_num_adapters--; 105 memset(&adapters.adapter[pao->index], 0, sizeof(adapters.adapter[0])); 106 hpios_alistlock_unlock(&adapters); 107 } 108 109 /** 110 * FindAdapter returns a pointer to the struct hpi_adapter_obj with 111 * index wAdapterIndex in an HPI_ADAPTERS_LIST structure. 112 * 113 */ 114 struct hpi_adapter_obj *hpi_find_adapter(u16 adapter_index) 115 { 116 struct hpi_adapter_obj *pao = NULL; 117 118 if (adapter_index >= HPI_MAX_ADAPTERS) { 119 HPI_DEBUG_LOG(VERBOSE, "find_adapter invalid index %d\n", 120 adapter_index); 121 return NULL; 122 } 123 124 pao = &adapters.adapter[adapter_index]; 125 if (pao->adapter_type != 0) { 126 /* 127 HPI_DEBUG_LOG(VERBOSE, "Found adapter index %d\n", 128 wAdapterIndex); 129 */ 130 return pao; 131 } else { 132 /* 133 HPI_DEBUG_LOG(VERBOSE, "No adapter index %d\n", 134 wAdapterIndex); 135 */ 136 return NULL; 137 } 138 } 139 140 /** 141 * 142 * wipe an HPI_ADAPTERS_LIST structure. 143 * 144 **/ 145 static void wipe_adapter_list(void) 146 { 147 memset(&adapters, 0, sizeof(adapters)); 148 } 149 150 static void subsys_get_adapter(struct hpi_message *phm, 151 struct hpi_response *phr) 152 { 153 int count = phm->obj_index; 154 u16 index = 0; 155 156 /* find the nCount'th nonzero adapter in array */ 157 for (index = 0; index < HPI_MAX_ADAPTERS; index++) { 158 if (adapters.adapter[index].adapter_type) { 159 if (!count) 160 break; 161 count--; 162 } 163 } 164 165 if (index < HPI_MAX_ADAPTERS) { 166 phr->u.s.adapter_index = adapters.adapter[index].index; 167 phr->u.s.adapter_type = adapters.adapter[index].adapter_type; 168 } else { 169 phr->u.s.adapter_index = 0; 170 phr->u.s.adapter_type = 0; 171 phr->error = HPI_ERROR_BAD_ADAPTER_NUMBER; 172 } 173 } 174 175 static unsigned int control_cache_alloc_check(struct hpi_control_cache *pC) 176 { 177 unsigned int i; 178 int cached = 0; 179 if (!pC) 180 return 0; 181 182 if (pC->init) 183 return pC->init; 184 185 if (!pC->p_cache) 186 return 0; 187 188 if (pC->control_count && pC->cache_size_in_bytes) { 189 char *p_master_cache; 190 unsigned int byte_count = 0; 191 192 p_master_cache = (char *)pC->p_cache; 193 HPI_DEBUG_LOG(DEBUG, "check %d controls\n", 194 pC->control_count); 195 for (i = 0; i < pC->control_count; i++) { 196 struct hpi_control_cache_info *info = 197 (struct hpi_control_cache_info *) 198 &p_master_cache[byte_count]; 199 200 if (!info->size_in32bit_words) { 201 if (!i) { 202 HPI_DEBUG_LOG(INFO, 203 "adap %d cache not ready?\n", 204 pC->adap_idx); 205 return 0; 206 } 207 /* The cache is invalid. 208 * Minimum valid entry size is 209 * sizeof(struct hpi_control_cache_info) 210 */ 211 HPI_DEBUG_LOG(ERROR, 212 "adap %d zero size cache entry %d\n", 213 pC->adap_idx, i); 214 break; 215 } 216 217 if (info->control_type) { 218 pC->p_info[info->control_index] = info; 219 cached++; 220 } else /* dummy cache entry */ 221 pC->p_info[info->control_index] = NULL; 222 223 byte_count += info->size_in32bit_words * 4; 224 225 HPI_DEBUG_LOG(VERBOSE, 226 "cached %d, pinfo %p index %d type %d size %d\n", 227 cached, pC->p_info[info->control_index], 228 info->control_index, info->control_type, 229 info->size_in32bit_words); 230 231 /* quit loop early if whole cache has been scanned. 232 * dwControlCount is the maximum possible entries 233 * but some may be absent from the cache 234 */ 235 if (byte_count >= pC->cache_size_in_bytes) 236 break; 237 /* have seen last control index */ 238 if (info->control_index == pC->control_count - 1) 239 break; 240 } 241 242 if (byte_count != pC->cache_size_in_bytes) 243 HPI_DEBUG_LOG(WARNING, 244 "adap %d bytecount %d != cache size %d\n", 245 pC->adap_idx, byte_count, 246 pC->cache_size_in_bytes); 247 else 248 HPI_DEBUG_LOG(DEBUG, 249 "adap %d cache good, bytecount == cache size = %d\n", 250 pC->adap_idx, byte_count); 251 252 pC->init = (u16)cached; 253 } 254 return pC->init; 255 } 256 257 /** Find a control. 258 */ 259 static short find_control(u16 control_index, 260 struct hpi_control_cache *p_cache, struct hpi_control_cache_info **pI) 261 { 262 if (!control_cache_alloc_check(p_cache)) { 263 HPI_DEBUG_LOG(VERBOSE, 264 "control_cache_alloc_check() failed %d\n", 265 control_index); 266 return 0; 267 } 268 269 *pI = p_cache->p_info[control_index]; 270 if (!*pI) { 271 HPI_DEBUG_LOG(VERBOSE, "Uncached Control %d\n", 272 control_index); 273 return 0; 274 } else { 275 HPI_DEBUG_LOG(VERBOSE, "find_control() type %d\n", 276 (*pI)->control_type); 277 } 278 return 1; 279 } 280 281 /* allow unified treatment of several string fields within struct */ 282 #define HPICMN_PAD_OFS_AND_SIZE(m) {\ 283 offsetof(struct hpi_control_cache_pad, m), \ 284 sizeof(((struct hpi_control_cache_pad *)(NULL))->m) } 285 286 struct pad_ofs_size { 287 unsigned int offset; 288 unsigned int field_size; 289 }; 290 291 static struct pad_ofs_size pad_desc[] = { 292 HPICMN_PAD_OFS_AND_SIZE(c_channel), /* HPI_PAD_CHANNEL_NAME */ 293 HPICMN_PAD_OFS_AND_SIZE(c_artist), /* HPI_PAD_ARTIST */ 294 HPICMN_PAD_OFS_AND_SIZE(c_title), /* HPI_PAD_TITLE */ 295 HPICMN_PAD_OFS_AND_SIZE(c_comment), /* HPI_PAD_COMMENT */ 296 }; 297 298 /** CheckControlCache checks the cache and fills the struct hpi_response 299 * accordingly. It returns one if a cache hit occurred, zero otherwise. 300 */ 301 short hpi_check_control_cache(struct hpi_control_cache *p_cache, 302 struct hpi_message *phm, struct hpi_response *phr) 303 { 304 short found = 1; 305 struct hpi_control_cache_info *pI; 306 struct hpi_control_cache_single *pC; 307 struct hpi_control_cache_pad *p_pad; 308 309 if (!find_control(phm->obj_index, p_cache, &pI)) { 310 HPI_DEBUG_LOG(VERBOSE, 311 "HPICMN find_control() failed for adap %d\n", 312 phm->adapter_index); 313 return 0; 314 } 315 316 phr->error = 0; 317 318 /* pC is the default cached control strucure. May be cast to 319 something else in the following switch statement. 320 */ 321 pC = (struct hpi_control_cache_single *)pI; 322 p_pad = (struct hpi_control_cache_pad *)pI; 323 324 switch (pI->control_type) { 325 326 case HPI_CONTROL_METER: 327 if (phm->u.c.attribute == HPI_METER_PEAK) { 328 phr->u.c.an_log_value[0] = pC->u.meter.an_log_peak[0]; 329 phr->u.c.an_log_value[1] = pC->u.meter.an_log_peak[1]; 330 } else if (phm->u.c.attribute == HPI_METER_RMS) { 331 if (pC->u.meter.an_logRMS[0] == 332 HPI_CACHE_INVALID_SHORT) { 333 phr->error = 334 HPI_ERROR_INVALID_CONTROL_ATTRIBUTE; 335 phr->u.c.an_log_value[0] = HPI_METER_MINIMUM; 336 phr->u.c.an_log_value[1] = HPI_METER_MINIMUM; 337 } else { 338 phr->u.c.an_log_value[0] = 339 pC->u.meter.an_logRMS[0]; 340 phr->u.c.an_log_value[1] = 341 pC->u.meter.an_logRMS[1]; 342 } 343 } else 344 found = 0; 345 break; 346 case HPI_CONTROL_VOLUME: 347 if (phm->u.c.attribute == HPI_VOLUME_GAIN) { 348 phr->u.c.an_log_value[0] = pC->u.vol.an_log[0]; 349 phr->u.c.an_log_value[1] = pC->u.vol.an_log[1]; 350 } else 351 found = 0; 352 break; 353 case HPI_CONTROL_MULTIPLEXER: 354 if (phm->u.c.attribute == HPI_MULTIPLEXER_SOURCE) { 355 phr->u.c.param1 = pC->u.mux.source_node_type; 356 phr->u.c.param2 = pC->u.mux.source_node_index; 357 } else { 358 found = 0; 359 } 360 break; 361 case HPI_CONTROL_CHANNEL_MODE: 362 if (phm->u.c.attribute == HPI_CHANNEL_MODE_MODE) 363 phr->u.c.param1 = pC->u.mode.mode; 364 else 365 found = 0; 366 break; 367 case HPI_CONTROL_LEVEL: 368 if (phm->u.c.attribute == HPI_LEVEL_GAIN) { 369 phr->u.c.an_log_value[0] = pC->u.level.an_log[0]; 370 phr->u.c.an_log_value[1] = pC->u.level.an_log[1]; 371 } else 372 found = 0; 373 break; 374 case HPI_CONTROL_TUNER: 375 if (phm->u.c.attribute == HPI_TUNER_FREQ) 376 phr->u.c.param1 = pC->u.tuner.freq_ink_hz; 377 else if (phm->u.c.attribute == HPI_TUNER_BAND) 378 phr->u.c.param1 = pC->u.tuner.band; 379 else if (phm->u.c.attribute == HPI_TUNER_LEVEL_AVG) 380 if (pC->u.tuner.s_level_avg == 381 HPI_CACHE_INVALID_SHORT) { 382 phr->u.cu.tuner.s_level = 0; 383 phr->error = 384 HPI_ERROR_INVALID_CONTROL_ATTRIBUTE; 385 } else 386 phr->u.cu.tuner.s_level = 387 pC->u.tuner.s_level_avg; 388 else 389 found = 0; 390 break; 391 case HPI_CONTROL_AESEBU_RECEIVER: 392 if (phm->u.c.attribute == HPI_AESEBURX_ERRORSTATUS) 393 phr->u.c.param1 = pC->u.aes3rx.error_status; 394 else if (phm->u.c.attribute == HPI_AESEBURX_FORMAT) 395 phr->u.c.param1 = pC->u.aes3rx.format; 396 else 397 found = 0; 398 break; 399 case HPI_CONTROL_AESEBU_TRANSMITTER: 400 if (phm->u.c.attribute == HPI_AESEBUTX_FORMAT) 401 phr->u.c.param1 = pC->u.aes3tx.format; 402 else 403 found = 0; 404 break; 405 case HPI_CONTROL_TONEDETECTOR: 406 if (phm->u.c.attribute == HPI_TONEDETECTOR_STATE) 407 phr->u.c.param1 = pC->u.tone.state; 408 else 409 found = 0; 410 break; 411 case HPI_CONTROL_SILENCEDETECTOR: 412 if (phm->u.c.attribute == HPI_SILENCEDETECTOR_STATE) { 413 phr->u.c.param1 = pC->u.silence.state; 414 } else 415 found = 0; 416 break; 417 case HPI_CONTROL_MICROPHONE: 418 if (phm->u.c.attribute == HPI_MICROPHONE_PHANTOM_POWER) 419 phr->u.c.param1 = pC->u.microphone.phantom_state; 420 else 421 found = 0; 422 break; 423 case HPI_CONTROL_SAMPLECLOCK: 424 if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE) 425 phr->u.c.param1 = pC->u.clk.source; 426 else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE_INDEX) { 427 if (pC->u.clk.source_index == 428 HPI_CACHE_INVALID_UINT16) { 429 phr->u.c.param1 = 0; 430 phr->error = 431 HPI_ERROR_INVALID_CONTROL_ATTRIBUTE; 432 } else 433 phr->u.c.param1 = pC->u.clk.source_index; 434 } else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SAMPLERATE) 435 phr->u.c.param1 = pC->u.clk.sample_rate; 436 else 437 found = 0; 438 break; 439 case HPI_CONTROL_PAD:{ 440 struct hpi_control_cache_pad *p_pad; 441 p_pad = (struct hpi_control_cache_pad *)pI; 442 443 if (!(p_pad->field_valid_flags & (1 << 444 HPI_CTL_ATTR_INDEX(phm->u.c. 445 attribute)))) { 446 phr->error = 447 HPI_ERROR_INVALID_CONTROL_ATTRIBUTE; 448 break; 449 } 450 451 if (phm->u.c.attribute == HPI_PAD_PROGRAM_ID) 452 phr->u.c.param1 = p_pad->pI; 453 else if (phm->u.c.attribute == HPI_PAD_PROGRAM_TYPE) 454 phr->u.c.param1 = p_pad->pTY; 455 else { 456 unsigned int index = 457 HPI_CTL_ATTR_INDEX(phm->u.c. 458 attribute) - 1; 459 unsigned int offset = phm->u.c.param1; 460 unsigned int pad_string_len, field_size; 461 char *pad_string; 462 unsigned int tocopy; 463 464 if (index > ARRAY_SIZE(pad_desc) - 1) { 465 phr->error = 466 HPI_ERROR_INVALID_CONTROL_ATTRIBUTE; 467 break; 468 } 469 470 pad_string = 471 ((char *)p_pad) + 472 pad_desc[index].offset; 473 field_size = pad_desc[index].field_size; 474 /* Ensure null terminator */ 475 pad_string[field_size - 1] = 0; 476 477 pad_string_len = strlen(pad_string) + 1; 478 479 if (offset > pad_string_len) { 480 phr->error = 481 HPI_ERROR_INVALID_CONTROL_VALUE; 482 break; 483 } 484 485 tocopy = pad_string_len - offset; 486 if (tocopy > sizeof(phr->u.cu.chars8.sz_data)) 487 tocopy = sizeof(phr->u.cu.chars8. 488 sz_data); 489 490 memcpy(phr->u.cu.chars8.sz_data, 491 &pad_string[offset], tocopy); 492 493 phr->u.cu.chars8.remaining_chars = 494 pad_string_len - offset - tocopy; 495 } 496 } 497 break; 498 default: 499 found = 0; 500 break; 501 } 502 503 HPI_DEBUG_LOG(VERBOSE, "%s Adap %d, Ctl %d, Type %d, Attr %d\n", 504 found ? "Cached" : "Uncached", phm->adapter_index, 505 pI->control_index, pI->control_type, phm->u.c.attribute); 506 507 if (found) 508 phr->size = 509 sizeof(struct hpi_response_header) + 510 sizeof(struct hpi_control_res); 511 512 return found; 513 } 514 515 /** Updates the cache with Set values. 516 517 Only update if no error. 518 Volume and Level return the limited values in the response, so use these 519 Multiplexer does so use sent values 520 */ 521 void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *p_cache, 522 struct hpi_message *phm, struct hpi_response *phr) 523 { 524 struct hpi_control_cache_single *pC; 525 struct hpi_control_cache_info *pI; 526 527 if (phr->error) 528 return; 529 530 if (!find_control(phm->obj_index, p_cache, &pI)) { 531 HPI_DEBUG_LOG(VERBOSE, 532 "HPICMN find_control() failed for adap %d\n", 533 phm->adapter_index); 534 return; 535 } 536 537 /* pC is the default cached control strucure. 538 May be cast to something else in the following switch statement. 539 */ 540 pC = (struct hpi_control_cache_single *)pI; 541 542 switch (pI->control_type) { 543 case HPI_CONTROL_VOLUME: 544 if (phm->u.c.attribute == HPI_VOLUME_GAIN) { 545 pC->u.vol.an_log[0] = phr->u.c.an_log_value[0]; 546 pC->u.vol.an_log[1] = phr->u.c.an_log_value[1]; 547 } 548 break; 549 case HPI_CONTROL_MULTIPLEXER: 550 /* mux does not return its setting on Set command. */ 551 if (phm->u.c.attribute == HPI_MULTIPLEXER_SOURCE) { 552 pC->u.mux.source_node_type = (u16)phm->u.c.param1; 553 pC->u.mux.source_node_index = (u16)phm->u.c.param2; 554 } 555 break; 556 case HPI_CONTROL_CHANNEL_MODE: 557 /* mode does not return its setting on Set command. */ 558 if (phm->u.c.attribute == HPI_CHANNEL_MODE_MODE) 559 pC->u.mode.mode = (u16)phm->u.c.param1; 560 break; 561 case HPI_CONTROL_LEVEL: 562 if (phm->u.c.attribute == HPI_LEVEL_GAIN) { 563 pC->u.vol.an_log[0] = phr->u.c.an_log_value[0]; 564 pC->u.vol.an_log[1] = phr->u.c.an_log_value[1]; 565 } 566 break; 567 case HPI_CONTROL_MICROPHONE: 568 if (phm->u.c.attribute == HPI_MICROPHONE_PHANTOM_POWER) 569 pC->u.microphone.phantom_state = (u16)phm->u.c.param1; 570 break; 571 case HPI_CONTROL_AESEBU_TRANSMITTER: 572 if (phm->u.c.attribute == HPI_AESEBUTX_FORMAT) 573 pC->u.aes3tx.format = phm->u.c.param1; 574 break; 575 case HPI_CONTROL_AESEBU_RECEIVER: 576 if (phm->u.c.attribute == HPI_AESEBURX_FORMAT) 577 pC->u.aes3rx.format = phm->u.c.param1; 578 break; 579 case HPI_CONTROL_SAMPLECLOCK: 580 if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE) 581 pC->u.clk.source = (u16)phm->u.c.param1; 582 else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE_INDEX) 583 pC->u.clk.source_index = (u16)phm->u.c.param1; 584 else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SAMPLERATE) 585 pC->u.clk.sample_rate = phm->u.c.param1; 586 break; 587 default: 588 break; 589 } 590 } 591 592 struct hpi_control_cache *hpi_alloc_control_cache(const u32 control_count, 593 const u32 size_in_bytes, u8 *p_dsp_control_buffer) 594 { 595 struct hpi_control_cache *p_cache = 596 kmalloc(sizeof(*p_cache), GFP_KERNEL); 597 if (!p_cache) 598 return NULL; 599 600 p_cache->p_info = 601 kmalloc(sizeof(*p_cache->p_info) * control_count, GFP_KERNEL); 602 if (!p_cache->p_info) { 603 kfree(p_cache); 604 return NULL; 605 } 606 memset(p_cache->p_info, 0, sizeof(*p_cache->p_info) * control_count); 607 p_cache->cache_size_in_bytes = size_in_bytes; 608 p_cache->control_count = control_count; 609 p_cache->p_cache = p_dsp_control_buffer; 610 p_cache->init = 0; 611 return p_cache; 612 } 613 614 void hpi_free_control_cache(struct hpi_control_cache *p_cache) 615 { 616 if (p_cache) { 617 kfree(p_cache->p_info); 618 kfree(p_cache); 619 } 620 } 621 622 static void subsys_message(struct hpi_message *phm, struct hpi_response *phr) 623 { 624 hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, phm->function, 0); 625 626 switch (phm->function) { 627 case HPI_SUBSYS_OPEN: 628 case HPI_SUBSYS_CLOSE: 629 case HPI_SUBSYS_DRIVER_UNLOAD: 630 break; 631 case HPI_SUBSYS_DRIVER_LOAD: 632 wipe_adapter_list(); 633 hpios_alistlock_init(&adapters); 634 break; 635 case HPI_SUBSYS_GET_ADAPTER: 636 subsys_get_adapter(phm, phr); 637 break; 638 case HPI_SUBSYS_GET_NUM_ADAPTERS: 639 phr->u.s.num_adapters = adapters.gw_num_adapters; 640 break; 641 case HPI_SUBSYS_CREATE_ADAPTER: 642 case HPI_SUBSYS_DELETE_ADAPTER: 643 break; 644 default: 645 phr->error = HPI_ERROR_INVALID_FUNC; 646 break; 647 } 648 } 649 650 void HPI_COMMON(struct hpi_message *phm, struct hpi_response *phr) 651 { 652 switch (phm->type) { 653 case HPI_TYPE_MESSAGE: 654 switch (phm->object) { 655 case HPI_OBJ_SUBSYSTEM: 656 subsys_message(phm, phr); 657 break; 658 } 659 break; 660 661 default: 662 phr->error = HPI_ERROR_INVALID_TYPE; 663 break; 664 } 665 } 666