sal.c (18452B)
1 /* 2 * 3 * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file. 4 * 5 * Copyright 2007-2019 Broadcom Inc. All rights reserved. 6 * 7 * File: sal.c 8 * Purpose: Defines sal routines for user-mode Unix targets. 9 */ 10 11 #include <sys/types.h> 12 #include <sys/time.h> 13 #include <string.h> 14 #include <stdlib.h> 15 #include <errno.h> 16 #include <unistd.h> 17 #include <signal.h> 18 #include <pthread.h> 19 #include <assert.h> 20 21 #include <sal/core/time.h> 22 #include <sal/core/thread.h> 23 #include <sal/core/sync.h> 24 #include <sal/core/boot.h> 25 #include <sal/core/spl.h> 26 #include <sal/core/alloc.h> 27 28 #include <sal/appl/sal.h> 29 #include <sal/appl/io.h> 30 #include <sal/appl/config.h> 31 #include <shared/error.h> 32 33 #ifdef BROADCOM_DEBUG 34 /* { */ 35 #ifdef INCLUDE_BCM_SAL_PROFILE 36 /* { */ 37 #if AGGRESSIVE_ALLOC_DEBUG_TESTING 38 /* { */ 39 #include <sal/core/alloc.h> 40 /* } */ 41 #endif 42 /* } */ 43 #endif 44 /* } */ 45 #endif /* BROADCOM_DEBUG */ 46 47 #if defined (__STRICT_ANSI__) 48 #define RANDOM rand 49 #define SRANDOM srand 50 #else 51 #define RANDOM random 52 #define SRANDOM srandom 53 #endif 54 55 #if (defined(__DUNE_GTO_BCM_CPU__) || defined(__DUNE_WRX_BCM_CPU__) || defined(__DUNE_SLK_BCM_CPU__)) && !defined(INCLUDE_CPU_I2C) 56 #define INCLUDE_CPU_I2C 57 #endif 58 59 #ifdef INCLUDE_CPU_I2C 60 #include <fcntl.h> 61 #include <linux/i2c.h> 62 #include <linux/i2c-dev.h> 63 #ifndef I2C_SMBUS_QUICK 64 #include <linux/i2c.h> 65 #endif 66 #include <sys/ioctl.h> /* ioctl */ 67 68 #define SAL_I2C_DEVICE_FILE_NAME_PREFIX "/dev/i2c-" /* i2c device is SAL_I2C_DEVICE_FILE_NAME_PREFIX + device number */ 69 #define SAL_I2C_MAX_NAME_LENGTH 64 70 #define SAL_I2C_MAX_NOF_SLAVE_DEVS 128 /* Maximum number of slave devices (Addresses) on an I2C bus, numbered from 0 */ 71 #define SAL_I2C_MAX_ADDRESS_SIZE 4 /* Maximum number of bytes of an address in an I2C device (register address) */ 72 73 /* flags for sal_i2c_operation() */ 74 #define SAL_I2C_FLAGS_WRITE 1 75 #define SAL_I2C_FLAGS_WRITE_SPLIT 2 /* Split the address and data writes */ 76 #define SAL_I2C_FLAGS_IGNORE_UNKNOWN_DEV_ERR_ON_WRITE 4 77 #define SAL_I2C_MAX_NOF_BUSES 8 /* Maximum number of I2C buses supported which are consecutively numbered from 0 */ 78 #endif /* INCLUDE_CPU_I2C */ 79 80 81 /* 82 * Function: 83 * sal_flash_sync 84 * Purpose: 85 * For compatibility only. 86 * Parameters: 87 * None 88 * Returns: 89 * 0 - success 90 * -1 - failed 91 * Notes: 92 * Not supported for Unix. 93 */ 94 95 int 96 sal_flash_sync(void) 97 { 98 return(-1); 99 } 100 101 /* 102 * Function: 103 * sal_flash_init 104 * Purpose: 105 * For compatibility only. 106 * Parameters: 107 * None 108 * Returns: 109 * 0 - success 110 * -1 - failed 111 * Notes: 112 * Not supported for Unix. 113 */ 114 115 int 116 sal_flash_init(int format) 117 { 118 return(-1); 119 } 120 121 /* 122 * Function: 123 * sal_flash_boot 124 * Purpose: 125 * For compatibility only. 126 * Parameters: 127 * None 128 * Returns: 129 * 0 - success 130 * -1 - failed 131 * Notes: 132 * Not supported for Unix. 133 */ 134 135 int 136 sal_flash_boot(char *f) 137 { 138 return(-1); 139 } 140 141 /* 142 * Function: 143 * sal_appl_init 144 * Purpose: 145 * Initialize the SAL abstraction layer for Unix 146 * Parameters: 147 * None 148 * Returns: 149 * 0 - success 150 * -1 - failed 151 */ 152 153 int 154 sal_appl_init(void) 155 { 156 #ifndef NO_FILEIO 157 char start_cwd[256]; 158 159 sal_getcwd(start_cwd, sizeof (start_cwd)); 160 sal_homedir_set(start_cwd); 161 #endif 162 163 sal_console_init(); 164 sal_config_refresh(); 165 166 DISPLAY_MEM ; 167 return(0); 168 } 169 170 /* 171 * Function: 172 * sal_reboot 173 * Purpose: 174 * For compatibility only. 175 * Parameters: 176 * None 177 * Notes: 178 * Causes the Unix application to exit. 179 */ 180 181 void 182 sal_reboot(void) 183 { 184 exit(0); 185 } 186 187 /* 188 * Function: 189 * sal_shell 190 * Purpose: 191 * Fork a standard O/S shell. 192 * Parameters: 193 * None 194 */ 195 196 void 197 sal_shell(void) 198 { 199 char *s = getenv("SHELL"); 200 /* coverity[tainted_string] */ 201 system(s ? s : "/bin/sh"); 202 } 203 204 /* 205 * Function: 206 * sal_led 207 * Purpose: 208 * For compatibility only. 209 * Parameters: 210 * v - pattern to show on LED display 211 * Returns: 212 * Previous state of LEDs. 213 * Notes: 214 * Not supported for Unix. 215 */ 216 217 uint32 218 sal_led(uint32 v) 219 { 220 static uint32 led = 0; 221 uint32 led_prev; 222 223 led_prev = led; 224 led = v; 225 226 return led_prev; 227 } 228 229 /* 230 * Function: 231 * sal_led_string 232 * Purpose: 233 * For compatibility only. 234 * Parameters: 235 * s - string to show on LED display 236 * Notes: 237 * Not supported for Unix. 238 */ 239 240 void 241 sal_led_string(const char *s) 242 { 243 COMPILER_REFERENCE(s); 244 } 245 246 /* 247 * Function: 248 * sal_date_set 249 * Purpose: 250 * For compatibility only. 251 * Parameters: 252 * val - new system time 253 * Returns: 254 * 0 - success 255 * -1 - failure 256 * Notes: 257 * Not supported for Unix. 258 */ 259 260 int 261 sal_date_set(sal_time_t *val) 262 { 263 sal_printf("Don't know how to set date on this platform (%lu)\n", *val); 264 return -1; 265 } 266 267 /* 268 * Function: 269 * sal_date_get 270 * Purpose: 271 * For compatibility only. 272 * Parameters: 273 * None 274 * Returns: 275 * 0 - success 276 * -1 - failure 277 */ 278 279 int 280 sal_date_get(sal_time_t *val) 281 { 282 time((time_t *) val); 283 return 0; 284 } 285 286 #if !(defined(UNIX) && !defined(__STRICT_ANSI__)) 287 /* 288 * Function: 289 * sal_strcasecmp 290 * Purpose: 291 * Compare two strings ignoring the case of the characters. 292 * Parameters: 293 * s1 - first string to compare 294 * s2 - second string to compare 295 * Returns: 296 * 0 if s1 and s2 are identical. 297 * negative integer if s1 < s2. 298 * positive integer if s1 > s2. 299 * Notes 300 * See man page of strcasecmp for more info. 301 */ 302 303 int 304 sal_strcasecmp(const char *s1, const char *s2) 305 { 306 unsigned char c1, c2; 307 308 do { 309 c1 = tolower(*s1++); 310 c2 = tolower(*s2++); 311 } while (c1 == c2 && c1 != 0); 312 313 return c1 - c2; 314 } 315 316 /* 317 * Function: 318 * sal_strncasecmp 319 * Purpose: 320 * Compare two strings ignoring the case of the characters. 321 * Parameters: 322 * s1 - first string to compare 323 * s2 - second string to compare 324 * n - maximum number of characters to compare 325 * Returns: 326 * 0 if s1 and s2 are identical up to n characters. 327 * negative integer if s1 < s2 up to n characters. 328 * positive integer if s1 > s2 up to n characters. 329 * Notes 330 * See man page of strncasecmp for more info. 331 */ 332 333 int 334 sal_strncasecmp(const char *s1, const char *s2, size_t n) 335 { 336 unsigned char c1, c2; 337 338 if (n == 0) { 339 return 0; 340 } 341 do { 342 c1 = tolower(*s1++); 343 c2 = tolower(*s2++); 344 } while (--n && c1 == c2 && c1 != 0); 345 346 return c1 - c2; 347 } 348 349 #endif /* !(defined(UNIX) && !defined(__STRICT_ANSI__)) */ 350 /* 351 * Function: 352 * sal_strcasestr 353 * Purpose: 354 * Finds the first occurrence of the substring needle in the string haystack 355 * Parameters: 356 * haystack - string to be searched in 357 * needle - substring to be searched for in haystack 358 * Returns: 359 * These functions return a pointer to the beginning of the substring 360 * or NULL if the substring is not found. 361 * Notes 362 * See man page of strncasestr for more info. 363 */ 364 365 char * 366 sal_strcasestr(const char *haystack, const char *needle) 367 { 368 char c, sc; 369 size_t len; 370 371 if ((c = *needle++) != 0) { 372 c = tolower((unsigned char)c); 373 len = strlen(needle); 374 do { 375 do { 376 if ((sc = *haystack++) == 0) 377 return (NULL); 378 } while ((char)tolower((unsigned char)sc) != c); 379 } while (sal_strncasecmp(haystack, needle, len) != 0); 380 haystack--; 381 } 382 return ((char *)haystack); 383 } 384 385 386 /* Code for supporting I2C operations on I2C devices whose master is the CPU */ 387 388 #ifdef INCLUDE_CPU_I2C 389 /* File descriptor for each I2C bus the CPU is connected to, a positive value means it is open */ 390 static int i2c_bus_filedesc[SAL_I2C_MAX_NOF_BUSES] = {0}; 391 392 /* open a Linux device file for an I2C bus */ 393 static int sal_i2c_init_fd(int* p_fd, uint8 bus_num) 394 { 395 char dev_file_name[SAL_I2C_MAX_NAME_LENGTH]; 396 397 sal_snprintf(dev_file_name, SAL_I2C_MAX_NAME_LENGTH, SAL_I2C_DEVICE_FILE_NAME_PREFIX"%d", (int)bus_num); 398 399 /* Try to open the given i2c dev num */ 400 if ( ((*p_fd) = open(dev_file_name, O_RDWR)) <= 0) 401 { 402 sal_printf("ERROR in %s: Failed to open() I2C device file for bus number %u\n", 403 FUNCTION_NAME(), (unsigned)bus_num); 404 return _SHR_E_FAIL; 405 } 406 return _SHR_E_NONE; 407 } 408 409 /* close a Linux device file for an I2C bus */ 410 static int sal_i2c_deinit_fd(int *fd) 411 { 412 /* close the I2C device */ 413 if (*fd > 0) { 414 if (close(*fd)) { 415 return _SHR_E_NOT_FOUND; 416 } 417 *fd = -1; 418 } 419 420 return _SHR_E_NONE; 421 } 422 423 /* Init CPU I2C */ 424 int sal_i2c_init() 425 { 426 return _SHR_E_NONE; 427 } 428 429 /* De-init CPU I2C */ 430 int sal_i2c_deinit() 431 { 432 int i, ret = _SHR_E_NONE; 433 for (i = 0; i < SAL_I2C_MAX_NOF_BUSES; ++i) { 434 ret |= sal_i2c_deinit_fd(i2c_bus_filedesc + i); 435 } 436 return ret ? _SHR_E_NOT_FOUND : _SHR_E_NONE; 437 } 438 439 440 /* 441 * Perform an I2C access operation on an I2C device connected to the CPU. 442 * The device file for the I2C bus needs to already be open. 443 */ 444 static int sal_i2c_operation( 445 int i2c_bus, /* The number of the I2C bus to access on */ 446 uint16 i2c_dev, /* the device on the I2C bus (slave device ID/address) to be accessed */ 447 uint8 alen, /* the length of the address in bytes, should be between 0 and 4. */ 448 uint8 dlen, /* the length of the data to read. */ 449 uint32 addr, /* The address in the device to be accessed (register address). Used if alen>0 */ 450 uint8 *data, /* the buffer for the data to be read. */ 451 uint32 flags) /* flags affecting function operation specifically SAL_I2C_FLAGS_WRITE indicated this is a write operation */ 452 { 453 int i; 454 unsigned bus = i2c_bus; 455 uint32 value; 456 struct i2c_rdwr_ioctl_data rw_data; 457 struct i2c_msg i2c_rw_msg[2]; 458 uint8 addr_n_data[SAL_I2C_MAX_ADDRESS_SIZE + 256]; /* buffer to hold the address and for writes also the write data */ 459 460 if (bus >= SAL_I2C_MAX_NOF_BUSES || i2c_dev >= SAL_I2C_MAX_NOF_SLAVE_DEVS || alen > SAL_I2C_MAX_ADDRESS_SIZE) { 461 sal_printf("I2c bus %u or I2C device 0x%x or address length %d out of range.\n", 462 bus, (unsigned)i2c_dev, (unsigned)alen); 463 return _SHR_E_PARAM; 464 } 465 466 /* open the given i2c bus device file if it is not open */ 467 if (i2c_bus_filedesc[bus] <= 0) { 468 if (sal_i2c_init_fd(i2c_bus_filedesc + bus, bus) != _SHR_E_NONE || i2c_bus_filedesc[bus] <= 0) { 469 sal_printf("Failed to auto-open the device file.\n"); 470 return _SHR_E_FAIL; 471 } 472 } 473 474 /* copy the address to addr_n_data as big endian */ 475 value = addr; 476 for (i = alen ; i > 0 ; ) { 477 addr_n_data[--i] = value & 0xff; 478 value >>= 8; 479 } 480 481 /* init the i2c access struct */ 482 rw_data.msgs = &i2c_rw_msg[0]; 483 i2c_rw_msg[0].addr = i2c_dev; 484 i2c_rw_msg[0].flags = 0; /* I2C_M_WR */ 485 i2c_rw_msg[0].buf = &addr_n_data[0]; 486 if (flags & SAL_I2C_FLAGS_WRITE) { 487 if (flags & SAL_I2C_FLAGS_WRITE_SPLIT) { /* block write */ 488 rw_data.nmsgs = 2; /* two WRITE operations */ 489 i2c_rw_msg[0].len = alen; 490 i2c_rw_msg[1].addr = i2c_dev; 491 i2c_rw_msg[1].flags = I2C_M_NOSTART; 492 i2c_rw_msg[1].buf = data; /* write from input buffer */ 493 i2c_rw_msg[1].len = dlen; 494 } else { 495 rw_data.nmsgs = 1; /* one WRITE operation */ 496 i2c_rw_msg[0].len = alen + dlen; 497 sal_memcpy(addr_n_data + alen, data, dlen); /* copy write data after the address */ 498 } 499 } else { /* read */ 500 if (alen) { /* We have an address so we need to first write it and then read */ 501 rw_data.nmsgs = 2; /* WRITE + READ operations */ 502 i2c_rw_msg[0].len = alen; 503 i2c_rw_msg[1].addr = i2c_dev; 504 i2c_rw_msg[1].flags = I2C_M_RD; 505 i2c_rw_msg[1].buf = data; /* read to input buffer */ 506 i2c_rw_msg[1].len = dlen; 507 } else { /* No address, only read */ 508 rw_data.nmsgs = 1; /* one READ operation */ 509 i2c_rw_msg[0].flags = I2C_M_RD; 510 i2c_rw_msg[0].buf = data; /* read to input buffer */ 511 i2c_rw_msg[0].len = dlen; 512 } 513 } 514 515 /* Perform the I2C messages/operations */ 516 while (ioctl(i2c_bus_filedesc[bus], I2C_RDWR, &rw_data) < 0) { 517 if (errno == ENXIO && 518 (flags & (SAL_I2C_FLAGS_WRITE | SAL_I2C_FLAGS_IGNORE_UNKNOWN_DEV_ERR_ON_WRITE)) == 519 (SAL_I2C_FLAGS_WRITE | SAL_I2C_FLAGS_IGNORE_UNKNOWN_DEV_ERR_ON_WRITE)) { 520 break; 521 } 522 /* sal_printf("ioctl I2C_RDWR failed in %s, %d, %s " 523 "- error reading from bus %u slave device 0x%x, address 0x%x, error = %d\n", 524 __FILE__, __LINE__, FUNCTION_NAME(), bus, (unsigned)i2c_dev, addr, errno); */ 525 if (errno != EINTR) { 526 return _SHR_E_FAIL; 527 } 528 } 529 #ifdef CPU_I2C_DUMP_MESSAGES 530 { 531 unsigned j, k; 532 struct i2c_msg *p = rw_data.msgs; 533 sal_printf("I2C %s ioctl(%d,", flags & SAL_I2C_FLAGS_WRITE ? "Write":"Read", i2c_bus_filedesc[bus]); 534 for (j = rw_data.nmsgs; j > 0; --j, ++p) { 535 sal_printf(" [addr=0x%x flags=0x%x len=%u, buf=0x", (unsigned)p->addr, (unsigned)p->flags, (unsigned)p->len); 536 for (k = 0; k < p->len; ++k ) { 537 sal_printf("%.2x", (unsigned)p->buf[k]); 538 } 539 sal_printf("]"); 540 } 541 sal_printf("\n"); 542 } 543 #endif /* CPU_I2C_DUMP_MESSAGES */ 544 545 /* In read operations the output is now in the data buffer */ 546 return _SHR_E_NONE; 547 } 548 549 #endif /* INCLUDE_CPU_I2C */ 550 551 /* 552 * Perform an I2C operations on an I2C device connected to the CPU's I2C controller. 553 * The CPU is the I2C master. 554 */ 555 556 /* 557 * Function : sal_i2c_read 558 * Purpose : Provide i2c bus read functionality using CPU i2c controller 559 * Returns : 0 - success, otherwise - failure 560 */ 561 int 562 sal_i2c_read( 563 int i2c_bus, /* The number of the I2C bus to access on */ 564 uint16 i2c_dev, /* the device on the I2C bus (slave device ID/address) to be accessed */ 565 uint32 addr, /* The address in the device to be accessed (register address). Used if alen>0 */ 566 uint8 addr_len, /* the length of the address in bytes, should be between 0 and 4. */ 567 uint8 *data, /* the buffer for the data to be read. */ 568 uint8 data_len) /* the length of the data to read. */ 569 { 570 #ifdef INCLUDE_CPU_I2C 571 return sal_i2c_operation(i2c_bus, i2c_dev, addr_len, data_len, addr, data, 0); 572 #else /* INCLUDE_CPU_I2C */ 573 return _SHR_E_UNAVAIL; 574 #endif /* INCLUDE_CPU_I2C */ 575 } 576 577 /* 578 * Function : sal_i2c_write 579 * Purpose : Provide i2c bus write functionality using CPU i2c controller 580 * Returns : 0 - success, otherwise - failure 581 */ 582 int 583 sal_i2c_write( 584 int i2c_bus, /* The number of the I2C bus to access on */ 585 uint16 i2c_dev, /* the device on the I2C bus (slave device ID/address) to be accessed */ 586 uint32 addr, /* The address in the device to be accessed (register address). Used if addr_len>0 */ 587 uint8 addr_len, /* the length of the address in bytes, should be between 0 and 4. */ 588 uint8 *data, /* the buffer of the data to be written. */ 589 uint8 data_len) /* the length of the data to write. */ 590 { 591 #ifdef INCLUDE_CPU_I2C 592 return sal_i2c_operation(i2c_bus, i2c_dev, addr_len, data_len, addr, data, SAL_I2C_FLAGS_WRITE); 593 #else /* INCLUDE_CPU_I2C */ 594 return _SHR_E_UNAVAIL; 595 #endif /* INCLUDE_CPU_I2C */ 596 } 597 598 /* 599 * Function : sal_i2c_write_split 600 * Purpose : Provide i2c bus write functionality using CPU i2c controller 601 * Uses two Linux messages for the write instead of one. 602 * Returns : 0 - success, otherwise - failure 603 */ 604 int 605 sal_i2c_write_split( 606 int i2c_bus, /* The number of the I2C bus to access on */ 607 uint16 i2c_dev, /* the device on the I2C bus (slave device ID/address) to be accessed */ 608 uint32 addr, /* The address in the device to be accessed (register address). Used if addr_len>0 */ 609 uint8 addr_len, /* the length of the address in bytes, should be between 0 and 4. */ 610 uint8 *data, /* the buffer of the data to be written. */ 611 uint8 data_len) /* the length of the data to write. */ 612 { 613 #ifdef INCLUDE_CPU_I2C 614 return sal_i2c_operation(i2c_bus, i2c_dev, addr_len, data_len, addr, data, 615 SAL_I2C_FLAGS_WRITE | SAL_I2C_FLAGS_WRITE_SPLIT); 616 #else /* INCLUDE_CPU_I2C */ 617 return _SHR_E_UNAVAIL; 618 #endif /* INCLUDE_CPU_I2C */ 619 } 620 621 /* 622 * Function: cpu_i2c_device_present 623 * Purpose: Probe the I2C bus using i2c_dev, report if a device responds. 624 * The I2C bus is released upon return. 625 * No further action is taken. 626 * 627 * Parameters: 628 * i2c_dev - I2C slave address 629 * 630 * Return: 631 * _SHR_E_NONE - An I2C device responds at the indicated slave address i2c_dev. 632 * otherwise - No I2C response at the indicated saddr, or I/O error. 633 */ 634 int 635 sal_i2c_device_present( 636 int i2c_bus, /* The number of the I2C bus to access on */ 637 uint16 i2c_dev) /* the device on the I2C bus (slave device ID/address) to be accessed */ 638 { 639 #ifdef INCLUDE_CPU_I2C 640 struct i2c_smbus_ioctl_data smbus_data; 641 unsigned bus = i2c_bus; 642 643 if (bus >= SAL_I2C_MAX_NOF_BUSES || i2c_dev >= SAL_I2C_MAX_NOF_SLAVE_DEVS) { 644 sal_printf("I2c bus %u or I2C device 0x%x out of range.\n", 645 bus, (unsigned)i2c_dev); 646 return _SHR_E_PARAM; 647 } 648 649 /* open the given i2c bus device file if it is not open */ 650 if (i2c_bus_filedesc[bus] <= 0) { 651 if (sal_i2c_init_fd(i2c_bus_filedesc + bus, bus) != _SHR_E_NONE || i2c_bus_filedesc[bus] <= 0) { 652 sal_printf("Failed to auto-open the device file.\n"); 653 return _SHR_E_FAIL; 654 } 655 } 656 657 658 if (ioctl(i2c_bus_filedesc[bus], I2C_SLAVE, i2c_dev) < 0) { 659 /* ERROR HANDLING; you can check errno to see what went wrong */ 660 return _SHR_E_NOT_FOUND; 661 } 662 663 sal_memset(&smbus_data, 0, sizeof(smbus_data)); 664 smbus_data.read_write = I2C_SMBUS_QUICK; 665 smbus_data.size = I2C_SMBUS_QUICK; 666 667 if (ioctl(i2c_bus_filedesc[bus], I2C_SMBUS, &smbus_data) < 0) { 668 /* ERROR HANDLING; you can check errno to see what went wrong */ 669 return _SHR_E_NOT_FOUND; 670 } 671 return _SHR_E_NONE; 672 #else /* INCLUDE_CPU_I2C */ 673 return _SHR_E_UNAVAIL; 674 #endif /* INCLUDE_CPU_I2C */ 675 } 676 677 /* 678 * Function : sal_i2c_config_get 679 * Purpose : Retrieve configurable i2c bus properties 680 * unit : I2C controller 681 * flags : SAL_I2C_* flags 682 */ 683 int 684 sal_i2c_config_get(int unit, uint32 *flags) 685 { 686 return(-1); /* Not supported */ 687 } 688 689 /* 690 * Function : sal_i2c_config_set 691 * Purpose : Configure i2c bus properties 692 * unit : I2C controller 693 * flags : SAL_I2C_* flags 694 */ 695 int 696 sal_i2c_config_set(int unit, uint32 flags) 697 { 698 return(-1); /* Not supported */ 699 }