diff options
author | Tomas Winkler <tomas.winkler@intel.com> | 2008-05-29 04:35:05 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-06-03 15:00:23 -0400 |
commit | 7c616cba240cd0d579c996be3f3603456acfb0ad (patch) | |
tree | 9c0a4594423b12138c1c430dac8063b4a437e1cd /drivers | |
parent | c135475439f75e6eb29e7586d33f3e22a61c1bb4 (diff) |
iwlwifi-5000: implement initial calibration for 5000
This patch adds initial calibration framework for 5000 HW faimily.
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: Gregory Greenman <gregory.greenman@intel.com>
Signed-off-by: Ron Rindjunsky <ron.rindjunsky@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-5000.c | 127 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-commands.h | 50 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-core.c | 16 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-core.h | 1 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-dev.h | 12 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-eeprom.h | 1 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-hcmd.c | 4 |
7 files changed, 211 insertions, 0 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index a94cd362fef5..eb6141e6edbc 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c | |||
@@ -395,6 +395,8 @@ static struct iwl_sensitivity_ranges iwl5000_sensitivity = { | |||
395 | 395 | ||
396 | #endif /* CONFIG_IWL5000_RUN_TIME_CALIB */ | 396 | #endif /* CONFIG_IWL5000_RUN_TIME_CALIB */ |
397 | 397 | ||
398 | |||
399 | |||
398 | static const u8 *iwl5000_eeprom_query_addr(const struct iwl_priv *priv, | 400 | static const u8 *iwl5000_eeprom_query_addr(const struct iwl_priv *priv, |
399 | size_t offset) | 401 | size_t offset) |
400 | { | 402 | { |
@@ -404,6 +406,118 @@ static const u8 *iwl5000_eeprom_query_addr(const struct iwl_priv *priv, | |||
404 | } | 406 | } |
405 | 407 | ||
406 | /* | 408 | /* |
409 | * Calibration | ||
410 | */ | ||
411 | static int iwl5000_send_Xtal_calib(struct iwl_priv *priv) | ||
412 | { | ||
413 | u16 *xtal_calib = (u16 *)iwl_eeprom_query_addr(priv, EEPROM_5000_XTAL); | ||
414 | |||
415 | struct iwl5000_calibration cal_cmd = { | ||
416 | .op_code = IWL5000_PHY_CALIBRATE_CRYSTAL_FRQ_CMD, | ||
417 | .data = { | ||
418 | (u8)xtal_calib[0], | ||
419 | (u8)xtal_calib[1], | ||
420 | } | ||
421 | }; | ||
422 | |||
423 | return iwl_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD, | ||
424 | sizeof(cal_cmd), &cal_cmd); | ||
425 | } | ||
426 | |||
427 | static int iwl5000_send_calib_results(struct iwl_priv *priv) | ||
428 | { | ||
429 | int ret = 0; | ||
430 | |||
431 | if (priv->calib_results.lo_res) | ||
432 | ret = iwl_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD, | ||
433 | priv->calib_results.lo_res_len, | ||
434 | priv->calib_results.lo_res); | ||
435 | if (ret) | ||
436 | goto err; | ||
437 | |||
438 | |||
439 | if (priv->calib_results.tx_iq_res) | ||
440 | ret = iwl_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD, | ||
441 | priv->calib_results.tx_iq_res_len, | ||
442 | priv->calib_results.tx_iq_res); | ||
443 | |||
444 | if (ret) | ||
445 | goto err; | ||
446 | |||
447 | if (priv->calib_results.tx_iq_perd_res) | ||
448 | ret = iwl_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD, | ||
449 | priv->calib_results.tx_iq_perd_res_len, | ||
450 | priv->calib_results.tx_iq_perd_res); | ||
451 | if (ret) | ||
452 | goto err; | ||
453 | |||
454 | return 0; | ||
455 | err: | ||
456 | IWL_ERROR("Error %d\n", ret); | ||
457 | return ret; | ||
458 | } | ||
459 | |||
460 | static int iwl5000_send_calib_cfg(struct iwl_priv *priv) | ||
461 | { | ||
462 | struct iwl5000_calib_cfg_cmd calib_cfg_cmd; | ||
463 | struct iwl_host_cmd cmd = { | ||
464 | .id = CALIBRATION_CFG_CMD, | ||
465 | .len = sizeof(struct iwl5000_calib_cfg_cmd), | ||
466 | .data = &calib_cfg_cmd, | ||
467 | }; | ||
468 | |||
469 | memset(&calib_cfg_cmd, 0, sizeof(calib_cfg_cmd)); | ||
470 | calib_cfg_cmd.ucd_calib_cfg.once.is_enable = IWL_CALIB_INIT_CFG_ALL; | ||
471 | calib_cfg_cmd.ucd_calib_cfg.once.start = IWL_CALIB_INIT_CFG_ALL; | ||
472 | calib_cfg_cmd.ucd_calib_cfg.once.send_res = IWL_CALIB_INIT_CFG_ALL; | ||
473 | calib_cfg_cmd.ucd_calib_cfg.flags = IWL_CALIB_INIT_CFG_ALL; | ||
474 | |||
475 | return iwl_send_cmd(priv, &cmd); | ||
476 | } | ||
477 | |||
478 | static void iwl5000_rx_calib_result(struct iwl_priv *priv, | ||
479 | struct iwl_rx_mem_buffer *rxb) | ||
480 | { | ||
481 | struct iwl_rx_packet *pkt = (void *)rxb->skb->data; | ||
482 | struct iwl5000_calib_hdr *hdr = (struct iwl5000_calib_hdr *)pkt->u.raw; | ||
483 | int len = le32_to_cpu(pkt->len) & FH_RSCSR_FRAME_SIZE_MSK; | ||
484 | |||
485 | iwl_free_calib_results(priv); | ||
486 | |||
487 | /* reduce the size of the length field itself */ | ||
488 | len -= 4; | ||
489 | |||
490 | switch (hdr->op_code) { | ||
491 | case IWL5000_PHY_CALIBRATE_LO_CMD: | ||
492 | priv->calib_results.lo_res = kzalloc(len, GFP_ATOMIC); | ||
493 | priv->calib_results.lo_res_len = len; | ||
494 | memcpy(priv->calib_results.lo_res, pkt->u.raw, len); | ||
495 | break; | ||
496 | case IWL5000_PHY_CALIBRATE_TX_IQ_CMD: | ||
497 | priv->calib_results.tx_iq_res = kzalloc(len, GFP_ATOMIC); | ||
498 | priv->calib_results.tx_iq_res_len = len; | ||
499 | memcpy(priv->calib_results.tx_iq_res, pkt->u.raw, len); | ||
500 | break; | ||
501 | case IWL5000_PHY_CALIBRATE_TX_IQ_PERD_CMD: | ||
502 | priv->calib_results.tx_iq_perd_res = kzalloc(len, GFP_ATOMIC); | ||
503 | priv->calib_results.tx_iq_perd_res_len = len; | ||
504 | memcpy(priv->calib_results.tx_iq_perd_res, pkt->u.raw, len); | ||
505 | break; | ||
506 | default: | ||
507 | IWL_ERROR("Unknown calibration notification %d\n", | ||
508 | hdr->op_code); | ||
509 | return; | ||
510 | } | ||
511 | } | ||
512 | |||
513 | static void iwl5000_rx_calib_complete(struct iwl_priv *priv, | ||
514 | struct iwl_rx_mem_buffer *rxb) | ||
515 | { | ||
516 | IWL_DEBUG_INFO("Init. calibration is completed, restarting fw.\n"); | ||
517 | queue_work(priv->workqueue, &priv->restart); | ||
518 | } | ||
519 | |||
520 | /* | ||
407 | * ucode | 521 | * ucode |
408 | */ | 522 | */ |
409 | static int iwl5000_load_section(struct iwl_priv *priv, | 523 | static int iwl5000_load_section(struct iwl_priv *priv, |
@@ -565,6 +679,7 @@ static void iwl5000_init_alive_start(struct iwl_priv *priv) | |||
565 | goto restart; | 679 | goto restart; |
566 | } | 680 | } |
567 | 681 | ||
682 | iwl5000_send_calib_cfg(priv); | ||
568 | return; | 683 | return; |
569 | 684 | ||
570 | restart: | 685 | restart: |
@@ -684,8 +799,14 @@ static int iwl5000_alive_notify(struct iwl_priv *priv) | |||
684 | iwl_release_nic_access(priv); | 799 | iwl_release_nic_access(priv); |
685 | spin_unlock_irqrestore(&priv->lock, flags); | 800 | spin_unlock_irqrestore(&priv->lock, flags); |
686 | 801 | ||
802 | |||
687 | iwl5000_send_wimax_coex(priv); | 803 | iwl5000_send_wimax_coex(priv); |
688 | 804 | ||
805 | iwl5000_send_Xtal_calib(priv); | ||
806 | |||
807 | if (priv->ucode_type == UCODE_RT) | ||
808 | iwl5000_send_calib_results(priv); | ||
809 | |||
689 | return 0; | 810 | return 0; |
690 | } | 811 | } |
691 | 812 | ||
@@ -856,8 +977,14 @@ static u16 iwl5000_get_hcmd_size(u8 cmd_id, u16 len) | |||
856 | 977 | ||
857 | static void iwl5000_rx_handler_setup(struct iwl_priv *priv) | 978 | static void iwl5000_rx_handler_setup(struct iwl_priv *priv) |
858 | { | 979 | { |
980 | /* init calibration handlers */ | ||
981 | priv->rx_handlers[CALIBRATION_RES_NOTIFICATION] = | ||
982 | iwl5000_rx_calib_result; | ||
983 | priv->rx_handlers[CALIBRATION_COMPLETE_NOTIFICATION] = | ||
984 | iwl5000_rx_calib_complete; | ||
859 | } | 985 | } |
860 | 986 | ||
987 | |||
861 | static int iwl5000_hw_valid_rtc_data_addr(u32 addr) | 988 | static int iwl5000_hw_valid_rtc_data_addr(u32 addr) |
862 | { | 989 | { |
863 | return (addr >= RTC_DATA_LOWER_BOUND) && | 990 | return (addr >= RTC_DATA_LOWER_BOUND) && |
diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h index a637abe6efef..6f62beb1e4bb 100644 --- a/drivers/net/wireless/iwlwifi/iwl-commands.h +++ b/drivers/net/wireless/iwlwifi/iwl-commands.h | |||
@@ -2778,10 +2778,59 @@ enum { | |||
2778 | IWL5000_PHY_CALIBRATE_AGC_TABLE_CMD = 14, | 2778 | IWL5000_PHY_CALIBRATE_AGC_TABLE_CMD = 14, |
2779 | IWL5000_PHY_CALIBRATE_CRYSTAL_FRQ_CMD = 15, | 2779 | IWL5000_PHY_CALIBRATE_CRYSTAL_FRQ_CMD = 15, |
2780 | IWL5000_PHY_CALIBRATE_BASE_BAND_CMD = 16, | 2780 | IWL5000_PHY_CALIBRATE_BASE_BAND_CMD = 16, |
2781 | IWL5000_PHY_CALIBRATE_TX_IQ_PERD_CMD = 17, | ||
2781 | IWL5000_PHY_CALIBRATE_CHAIN_NOISE_RESET_CMD = 18, | 2782 | IWL5000_PHY_CALIBRATE_CHAIN_NOISE_RESET_CMD = 18, |
2782 | IWL5000_PHY_CALIBRATE_CHAIN_NOISE_GAIN_CMD = 19, | 2783 | IWL5000_PHY_CALIBRATE_CHAIN_NOISE_GAIN_CMD = 19, |
2783 | }; | 2784 | }; |
2784 | 2785 | ||
2786 | enum { | ||
2787 | CALIBRATION_CFG_CMD = 0x65, | ||
2788 | CALIBRATION_RES_NOTIFICATION = 0x66, | ||
2789 | CALIBRATION_COMPLETE_NOTIFICATION = 0x67 | ||
2790 | }; | ||
2791 | |||
2792 | struct iwl_cal_crystal_freq_cmd { | ||
2793 | u8 cap_pin1; | ||
2794 | u8 cap_pin2; | ||
2795 | } __attribute__ ((packed)); | ||
2796 | |||
2797 | struct iwl5000_calibration { | ||
2798 | u8 op_code; | ||
2799 | u8 first_group; | ||
2800 | u8 num_groups; | ||
2801 | u8 all_data_valid; | ||
2802 | struct iwl_cal_crystal_freq_cmd data; | ||
2803 | } __attribute__ ((packed)); | ||
2804 | |||
2805 | #define IWL_CALIB_INIT_CFG_ALL __constant_cpu_to_le32(0xffffffff) | ||
2806 | |||
2807 | struct iwl_calib_cfg_elmnt_s { | ||
2808 | __le32 is_enable; | ||
2809 | __le32 start; | ||
2810 | __le32 send_res; | ||
2811 | __le32 apply_res; | ||
2812 | __le32 reserved; | ||
2813 | } __attribute__ ((packed)); | ||
2814 | |||
2815 | struct iwl_calib_cfg_status_s { | ||
2816 | struct iwl_calib_cfg_elmnt_s once; | ||
2817 | struct iwl_calib_cfg_elmnt_s perd; | ||
2818 | __le32 flags; | ||
2819 | } __attribute__ ((packed)); | ||
2820 | |||
2821 | struct iwl5000_calib_cfg_cmd { | ||
2822 | struct iwl_calib_cfg_status_s ucd_calib_cfg; | ||
2823 | struct iwl_calib_cfg_status_s drv_calib_cfg; | ||
2824 | __le32 reserved1; | ||
2825 | } __attribute__ ((packed)); | ||
2826 | |||
2827 | struct iwl5000_calib_hdr { | ||
2828 | u8 op_code; | ||
2829 | u8 first_group; | ||
2830 | u8 groups_num; | ||
2831 | u8 data_valid; | ||
2832 | } __attribute__ ((packed)); | ||
2833 | |||
2785 | struct iwl5000_calibration_chain_noise_reset_cmd { | 2834 | struct iwl5000_calibration_chain_noise_reset_cmd { |
2786 | u8 op_code; /* IWL5000_PHY_CALIBRATE_CHAIN_NOISE_RESET_CMD */ | 2835 | u8 op_code; /* IWL5000_PHY_CALIBRATE_CHAIN_NOISE_RESET_CMD */ |
2787 | u8 flags; /* not used */ | 2836 | u8 flags; /* not used */ |
@@ -2894,6 +2943,7 @@ struct iwl_rx_packet { | |||
2894 | struct iwl4965_notif_statistics stats; | 2943 | struct iwl4965_notif_statistics stats; |
2895 | struct iwl4965_compressed_ba_resp compressed_ba; | 2944 | struct iwl4965_compressed_ba_resp compressed_ba; |
2896 | struct iwl4965_missed_beacon_notif missed_beacon; | 2945 | struct iwl4965_missed_beacon_notif missed_beacon; |
2946 | struct iwl5000_calibration calib; | ||
2897 | __le32 status; | 2947 | __le32 status; |
2898 | u8 raw[0]; | 2948 | u8 raw[0]; |
2899 | } u; | 2949 | } u; |
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index 373e9847c378..010085aee5d4 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c | |||
@@ -871,9 +871,25 @@ err: | |||
871 | } | 871 | } |
872 | EXPORT_SYMBOL(iwl_init_drv); | 872 | EXPORT_SYMBOL(iwl_init_drv); |
873 | 873 | ||
874 | void iwl_free_calib_results(struct iwl_priv *priv) | ||
875 | { | ||
876 | kfree(priv->calib_results.lo_res); | ||
877 | priv->calib_results.lo_res = NULL; | ||
878 | priv->calib_results.lo_res_len = 0; | ||
879 | |||
880 | kfree(priv->calib_results.tx_iq_res); | ||
881 | priv->calib_results.tx_iq_res = NULL; | ||
882 | priv->calib_results.tx_iq_res_len = 0; | ||
883 | |||
884 | kfree(priv->calib_results.tx_iq_perd_res); | ||
885 | priv->calib_results.tx_iq_perd_res = NULL; | ||
886 | priv->calib_results.tx_iq_perd_res_len = 0; | ||
887 | } | ||
888 | EXPORT_SYMBOL(iwl_free_calib_results); | ||
874 | 889 | ||
875 | void iwl_uninit_drv(struct iwl_priv *priv) | 890 | void iwl_uninit_drv(struct iwl_priv *priv) |
876 | { | 891 | { |
892 | iwl_free_calib_results(priv); | ||
877 | iwlcore_free_geos(priv); | 893 | iwlcore_free_geos(priv); |
878 | iwl_free_channel_map(priv); | 894 | iwl_free_channel_map(priv); |
879 | } | 895 | } |
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h index 050549131c47..9d43085ead9c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.h +++ b/drivers/net/wireless/iwlwifi/iwl-core.h | |||
@@ -172,6 +172,7 @@ struct ieee80211_hw *iwl_alloc_all(struct iwl_cfg *cfg, | |||
172 | void iwl_hw_detect(struct iwl_priv *priv); | 172 | void iwl_hw_detect(struct iwl_priv *priv); |
173 | 173 | ||
174 | void iwlcore_clear_stations_table(struct iwl_priv *priv); | 174 | void iwlcore_clear_stations_table(struct iwl_priv *priv); |
175 | void iwl_free_calib_results(struct iwl_priv *priv); | ||
175 | void iwl_reset_qos(struct iwl_priv *priv); | 176 | void iwl_reset_qos(struct iwl_priv *priv); |
176 | void iwl_set_rxon_chain(struct iwl_priv *priv); | 177 | void iwl_set_rxon_chain(struct iwl_priv *priv); |
177 | int iwl_set_rxon_channel(struct iwl_priv *priv, | 178 | int iwl_set_rxon_channel(struct iwl_priv *priv, |
diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index 2c92e55850c5..291c1ec9b0de 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h | |||
@@ -876,6 +876,15 @@ struct statistics_general_data { | |||
876 | u32 beacon_energy_c; | 876 | u32 beacon_energy_c; |
877 | }; | 877 | }; |
878 | 878 | ||
879 | struct iwl_calib_results { | ||
880 | void *tx_iq_res; | ||
881 | void *tx_iq_perd_res; | ||
882 | void *lo_res; | ||
883 | u32 tx_iq_res_len; | ||
884 | u32 tx_iq_perd_res_len; | ||
885 | u32 lo_res_len; | ||
886 | }; | ||
887 | |||
879 | enum ucode_type { | 888 | enum ucode_type { |
880 | UCODE_NONE = 0, | 889 | UCODE_NONE = 0, |
881 | UCODE_INIT, | 890 | UCODE_INIT, |
@@ -983,6 +992,9 @@ struct iwl_priv { | |||
983 | s32 temperature; /* degrees Kelvin */ | 992 | s32 temperature; /* degrees Kelvin */ |
984 | s32 last_temperature; | 993 | s32 last_temperature; |
985 | 994 | ||
995 | /* init calibration results */ | ||
996 | struct iwl_calib_results calib_results; | ||
997 | |||
986 | /* Scan related variables */ | 998 | /* Scan related variables */ |
987 | unsigned long last_scan_jiffies; | 999 | unsigned long last_scan_jiffies; |
988 | unsigned long next_scan_jiffies; | 1000 | unsigned long next_scan_jiffies; |
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.h b/drivers/net/wireless/iwlwifi/iwl-eeprom.h index dc1f027c66a0..d3a2a5b4ac56 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom.h +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.h | |||
@@ -146,6 +146,7 @@ struct iwl_eeprom_channel { | |||
146 | 146 | ||
147 | /*5000 calibrations */ | 147 | /*5000 calibrations */ |
148 | #define EEPROM_5000_CALIB_ALL (INDIRECT_ADDRESS | INDIRECT_CALIBRATION) | 148 | #define EEPROM_5000_CALIB_ALL (INDIRECT_ADDRESS | INDIRECT_CALIBRATION) |
149 | #define EEPROM_5000_XTAL ((2*0x128) | EEPROM_5000_CALIB_ALL) | ||
149 | 150 | ||
150 | /* 5000 links */ | 151 | /* 5000 links */ |
151 | #define EEPROM_5000_LINK_HOST (2*0x64) | 152 | #define EEPROM_5000_LINK_HOST (2*0x64) |
diff --git a/drivers/net/wireless/iwlwifi/iwl-hcmd.c b/drivers/net/wireless/iwlwifi/iwl-hcmd.c index dab4a0eff961..6c537360820b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-hcmd.c +++ b/drivers/net/wireless/iwlwifi/iwl-hcmd.c | |||
@@ -56,6 +56,7 @@ const char *get_cmd_string(u8 cmd) | |||
56 | IWL_CMD(REPLY_RATE_SCALE); | 56 | IWL_CMD(REPLY_RATE_SCALE); |
57 | IWL_CMD(REPLY_LEDS_CMD); | 57 | IWL_CMD(REPLY_LEDS_CMD); |
58 | IWL_CMD(REPLY_TX_LINK_QUALITY_CMD); | 58 | IWL_CMD(REPLY_TX_LINK_QUALITY_CMD); |
59 | IWL_CMD(COEX_PRIORITY_TABLE_CMD); | ||
59 | IWL_CMD(RADAR_NOTIFICATION); | 60 | IWL_CMD(RADAR_NOTIFICATION); |
60 | IWL_CMD(REPLY_QUIET_CMD); | 61 | IWL_CMD(REPLY_QUIET_CMD); |
61 | IWL_CMD(REPLY_CHANNEL_SWITCH); | 62 | IWL_CMD(REPLY_CHANNEL_SWITCH); |
@@ -89,6 +90,9 @@ const char *get_cmd_string(u8 cmd) | |||
89 | IWL_CMD(REPLY_RX_MPDU_CMD); | 90 | IWL_CMD(REPLY_RX_MPDU_CMD); |
90 | IWL_CMD(REPLY_RX); | 91 | IWL_CMD(REPLY_RX); |
91 | IWL_CMD(REPLY_COMPRESSED_BA); | 92 | IWL_CMD(REPLY_COMPRESSED_BA); |
93 | IWL_CMD(CALIBRATION_CFG_CMD); | ||
94 | IWL_CMD(CALIBRATION_RES_NOTIFICATION); | ||
95 | IWL_CMD(CALIBRATION_COMPLETE_NOTIFICATION); | ||
92 | default: | 96 | default: |
93 | return "UNKNOWN"; | 97 | return "UNKNOWN"; |
94 | 98 | ||