diff options
author | Avinash Patil <patila@marvell.com> | 2014-09-12 10:38:59 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2014-09-15 15:00:52 -0400 |
commit | 6e251174c77a28a4ebaad9be963843898b658039 (patch) | |
tree | 2f52e5110018cb9189f5149ad50893485d6cd947 /drivers/net/wireless/mwifiex | |
parent | d8d91253ba11bb6e98105c2bc88a40d08fac06ff (diff) |
mwifiex: add rx workqueue support
This patch adds RX work queue support to mwifiex.
Packets received are queued to internal queue which are then
processed by scheduling a work item for RX process.
RX work is enabled only on SMP systems.
Reviewed-by: James Cameron <quozl@laptop.org>
Signed-off-by: Avinash Patil <patila@marvell.com>
Signed-off-by: Marc Yang <yangyang@marvell.com>
Signed-off-by: Cathy Luo <cluo@marvell.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/mwifiex')
-rw-r--r-- | drivers/net/wireless/mwifiex/11n_rxreorder.c | 14 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/init.c | 19 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/main.c | 91 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/main.h | 14 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/pcie.c | 12 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/sdio.c | 11 |
6 files changed, 159 insertions, 2 deletions
diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.c b/drivers/net/wireless/mwifiex/11n_rxreorder.c index 06a2c215ef5e..40057079ffb9 100644 --- a/drivers/net/wireless/mwifiex/11n_rxreorder.c +++ b/drivers/net/wireless/mwifiex/11n_rxreorder.c | |||
@@ -183,6 +183,15 @@ mwifiex_del_rx_reorder_entry(struct mwifiex_private *priv, | |||
183 | if (!tbl) | 183 | if (!tbl) |
184 | return; | 184 | return; |
185 | 185 | ||
186 | spin_lock_irqsave(&priv->adapter->rx_proc_lock, flags); | ||
187 | priv->adapter->rx_locked = true; | ||
188 | if (priv->adapter->rx_processing) { | ||
189 | spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags); | ||
190 | flush_workqueue(priv->adapter->rx_workqueue); | ||
191 | } else { | ||
192 | spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags); | ||
193 | } | ||
194 | |||
186 | start_win = (tbl->start_win + tbl->win_size) & (MAX_TID_VALUE - 1); | 195 | start_win = (tbl->start_win + tbl->win_size) & (MAX_TID_VALUE - 1); |
187 | mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win); | 196 | mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win); |
188 | 197 | ||
@@ -194,6 +203,11 @@ mwifiex_del_rx_reorder_entry(struct mwifiex_private *priv, | |||
194 | 203 | ||
195 | kfree(tbl->rx_reorder_ptr); | 204 | kfree(tbl->rx_reorder_ptr); |
196 | kfree(tbl); | 205 | kfree(tbl); |
206 | |||
207 | spin_lock_irqsave(&priv->adapter->rx_proc_lock, flags); | ||
208 | priv->adapter->rx_locked = false; | ||
209 | spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags); | ||
210 | |||
197 | } | 211 | } |
198 | 212 | ||
199 | /* | 213 | /* |
diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index cd9baad60595..f7c97cf3840b 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c | |||
@@ -447,8 +447,11 @@ int mwifiex_init_lock_list(struct mwifiex_adapter *adapter) | |||
447 | spin_lock_init(&adapter->cmd_free_q_lock); | 447 | spin_lock_init(&adapter->cmd_free_q_lock); |
448 | spin_lock_init(&adapter->cmd_pending_q_lock); | 448 | spin_lock_init(&adapter->cmd_pending_q_lock); |
449 | spin_lock_init(&adapter->scan_pending_q_lock); | 449 | spin_lock_init(&adapter->scan_pending_q_lock); |
450 | spin_lock_init(&adapter->rx_q_lock); | ||
451 | spin_lock_init(&adapter->rx_proc_lock); | ||
450 | 452 | ||
451 | skb_queue_head_init(&adapter->usb_rx_data_q); | 453 | skb_queue_head_init(&adapter->usb_rx_data_q); |
454 | skb_queue_head_init(&adapter->rx_data_q); | ||
452 | 455 | ||
453 | for (i = 0; i < adapter->priv_num; ++i) { | 456 | for (i = 0; i < adapter->priv_num; ++i) { |
454 | INIT_LIST_HEAD(&adapter->bss_prio_tbl[i].bss_prio_head); | 457 | INIT_LIST_HEAD(&adapter->bss_prio_tbl[i].bss_prio_head); |
@@ -614,6 +617,7 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter) | |||
614 | int ret = -EINPROGRESS; | 617 | int ret = -EINPROGRESS; |
615 | struct mwifiex_private *priv; | 618 | struct mwifiex_private *priv; |
616 | s32 i; | 619 | s32 i; |
620 | unsigned long flags; | ||
617 | struct sk_buff *skb; | 621 | struct sk_buff *skb; |
618 | 622 | ||
619 | /* mwifiex already shutdown */ | 623 | /* mwifiex already shutdown */ |
@@ -648,6 +652,21 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter) | |||
648 | } | 652 | } |
649 | } | 653 | } |
650 | 654 | ||
655 | spin_lock_irqsave(&adapter->rx_proc_lock, flags); | ||
656 | |||
657 | while ((skb = skb_dequeue(&adapter->rx_data_q))) { | ||
658 | struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb); | ||
659 | |||
660 | atomic_dec(&adapter->rx_pending); | ||
661 | priv = adapter->priv[rx_info->bss_num]; | ||
662 | if (priv) | ||
663 | priv->stats.rx_dropped++; | ||
664 | |||
665 | dev_kfree_skb_any(skb); | ||
666 | } | ||
667 | |||
668 | spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); | ||
669 | |||
651 | spin_lock(&adapter->mwifiex_lock); | 670 | spin_lock(&adapter->mwifiex_lock); |
652 | 671 | ||
653 | if (adapter->if_ops.data_complete) { | 672 | if (adapter->if_ops.data_complete) { |
diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index f1393868d04c..b522f7c36901 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c | |||
@@ -126,6 +126,42 @@ static int mwifiex_unregister(struct mwifiex_adapter *adapter) | |||
126 | return 0; | 126 | return 0; |
127 | } | 127 | } |
128 | 128 | ||
129 | static int mwifiex_process_rx(struct mwifiex_adapter *adapter) | ||
130 | { | ||
131 | unsigned long flags; | ||
132 | struct sk_buff *skb; | ||
133 | bool delay_main_work = adapter->delay_main_work; | ||
134 | |||
135 | spin_lock_irqsave(&adapter->rx_proc_lock, flags); | ||
136 | if (adapter->rx_processing || adapter->rx_locked) { | ||
137 | spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); | ||
138 | goto exit_rx_proc; | ||
139 | } else { | ||
140 | adapter->rx_processing = true; | ||
141 | spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); | ||
142 | } | ||
143 | |||
144 | /* Check for Rx data */ | ||
145 | while ((skb = skb_dequeue(&adapter->rx_data_q))) { | ||
146 | atomic_dec(&adapter->rx_pending); | ||
147 | if (adapter->delay_main_work && | ||
148 | (atomic_dec_return(&adapter->rx_pending) < | ||
149 | LOW_RX_PENDING)) { | ||
150 | adapter->delay_main_work = false; | ||
151 | queue_work(adapter->rx_workqueue, &adapter->rx_work); | ||
152 | } | ||
153 | mwifiex_handle_rx_packet(adapter, skb); | ||
154 | } | ||
155 | spin_lock_irqsave(&adapter->rx_proc_lock, flags); | ||
156 | adapter->rx_processing = false; | ||
157 | spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); | ||
158 | |||
159 | if (delay_main_work) | ||
160 | queue_work(adapter->workqueue, &adapter->main_work); | ||
161 | exit_rx_proc: | ||
162 | return 0; | ||
163 | } | ||
164 | |||
129 | /* | 165 | /* |
130 | * The main process. | 166 | * The main process. |
131 | * | 167 | * |
@@ -163,6 +199,19 @@ process_start: | |||
163 | (adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY)) | 199 | (adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY)) |
164 | break; | 200 | break; |
165 | 201 | ||
202 | /* If we process interrupts first, it would increase RX pending | ||
203 | * even further. Avoid this by checking if rx_pending has | ||
204 | * crossed high threshold and schedule rx work queue | ||
205 | * and then process interrupts | ||
206 | */ | ||
207 | if (atomic_read(&adapter->rx_pending) >= HIGH_RX_PENDING) { | ||
208 | adapter->delay_main_work = true; | ||
209 | if (!adapter->rx_processing) | ||
210 | queue_work(adapter->rx_workqueue, | ||
211 | &adapter->rx_work); | ||
212 | break; | ||
213 | } | ||
214 | |||
166 | /* Handle pending interrupt if any */ | 215 | /* Handle pending interrupt if any */ |
167 | if (adapter->int_status) { | 216 | if (adapter->int_status) { |
168 | if (adapter->hs_activated) | 217 | if (adapter->hs_activated) |
@@ -171,6 +220,9 @@ process_start: | |||
171 | adapter->if_ops.process_int_status(adapter); | 220 | adapter->if_ops.process_int_status(adapter); |
172 | } | 221 | } |
173 | 222 | ||
223 | if (adapter->rx_work_enabled && adapter->data_received) | ||
224 | queue_work(adapter->rx_workqueue, &adapter->rx_work); | ||
225 | |||
174 | /* Need to wake up the card ? */ | 226 | /* Need to wake up the card ? */ |
175 | if ((adapter->ps_state == PS_STATE_SLEEP) && | 227 | if ((adapter->ps_state == PS_STATE_SLEEP) && |
176 | (adapter->pm_wakeup_card_req && | 228 | (adapter->pm_wakeup_card_req && |
@@ -183,6 +235,7 @@ process_start: | |||
183 | } | 235 | } |
184 | 236 | ||
185 | if (IS_CARD_RX_RCVD(adapter)) { | 237 | if (IS_CARD_RX_RCVD(adapter)) { |
238 | adapter->data_received = false; | ||
186 | adapter->pm_wakeup_fw_try = false; | 239 | adapter->pm_wakeup_fw_try = false; |
187 | if (adapter->ps_state == PS_STATE_SLEEP) | 240 | if (adapter->ps_state == PS_STATE_SLEEP) |
188 | adapter->ps_state = PS_STATE_AWAKE; | 241 | adapter->ps_state = PS_STATE_AWAKE; |
@@ -318,6 +371,12 @@ static void mwifiex_terminate_workqueue(struct mwifiex_adapter *adapter) | |||
318 | flush_workqueue(adapter->workqueue); | 371 | flush_workqueue(adapter->workqueue); |
319 | destroy_workqueue(adapter->workqueue); | 372 | destroy_workqueue(adapter->workqueue); |
320 | adapter->workqueue = NULL; | 373 | adapter->workqueue = NULL; |
374 | |||
375 | if (adapter->rx_workqueue) { | ||
376 | flush_workqueue(adapter->rx_workqueue); | ||
377 | destroy_workqueue(adapter->rx_workqueue); | ||
378 | adapter->rx_workqueue = NULL; | ||
379 | } | ||
321 | } | 380 | } |
322 | 381 | ||
323 | /* | 382 | /* |
@@ -732,6 +791,21 @@ int is_command_pending(struct mwifiex_adapter *adapter) | |||
732 | } | 791 | } |
733 | 792 | ||
734 | /* | 793 | /* |
794 | * This is the RX work queue function. | ||
795 | * | ||
796 | * It handles the RX operations. | ||
797 | */ | ||
798 | static void mwifiex_rx_work_queue(struct work_struct *work) | ||
799 | { | ||
800 | struct mwifiex_adapter *adapter = | ||
801 | container_of(work, struct mwifiex_adapter, rx_work); | ||
802 | |||
803 | if (adapter->surprise_removed) | ||
804 | return; | ||
805 | mwifiex_process_rx(adapter); | ||
806 | } | ||
807 | |||
808 | /* | ||
735 | * This is the main work queue function. | 809 | * This is the main work queue function. |
736 | * | 810 | * |
737 | * It handles the main process, which in turn handles the complete | 811 | * It handles the main process, which in turn handles the complete |
@@ -787,6 +861,11 @@ mwifiex_add_card(void *card, struct semaphore *sem, | |||
787 | adapter->cmd_wait_q.status = 0; | 861 | adapter->cmd_wait_q.status = 0; |
788 | adapter->scan_wait_q_woken = false; | 862 | adapter->scan_wait_q_woken = false; |
789 | 863 | ||
864 | if (num_possible_cpus() > 1) { | ||
865 | adapter->rx_work_enabled = true; | ||
866 | pr_notice("rx work enabled, cpus %d\n", num_possible_cpus()); | ||
867 | } | ||
868 | |||
790 | adapter->workqueue = | 869 | adapter->workqueue = |
791 | alloc_workqueue("MWIFIEX_WORK_QUEUE", | 870 | alloc_workqueue("MWIFIEX_WORK_QUEUE", |
792 | WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1); | 871 | WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1); |
@@ -794,6 +873,18 @@ mwifiex_add_card(void *card, struct semaphore *sem, | |||
794 | goto err_kmalloc; | 873 | goto err_kmalloc; |
795 | 874 | ||
796 | INIT_WORK(&adapter->main_work, mwifiex_main_work_queue); | 875 | INIT_WORK(&adapter->main_work, mwifiex_main_work_queue); |
876 | |||
877 | if (adapter->rx_work_enabled) { | ||
878 | adapter->rx_workqueue = alloc_workqueue("MWIFIEX_RX_WORK_QUEUE", | ||
879 | WQ_HIGHPRI | | ||
880 | WQ_MEM_RECLAIM | | ||
881 | WQ_UNBOUND, 1); | ||
882 | if (!adapter->rx_workqueue) | ||
883 | goto err_kmalloc; | ||
884 | |||
885 | INIT_WORK(&adapter->rx_work, mwifiex_rx_work_queue); | ||
886 | } | ||
887 | |||
797 | if (adapter->if_ops.iface_work) | 888 | if (adapter->if_ops.iface_work) |
798 | INIT_WORK(&adapter->iface_work, adapter->if_ops.iface_work); | 889 | INIT_WORK(&adapter->iface_work, adapter->if_ops.iface_work); |
799 | 890 | ||
diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index e9cd4ab4902d..1a999999b391 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h | |||
@@ -58,6 +58,9 @@ enum { | |||
58 | #define MAX_TX_PENDING 100 | 58 | #define MAX_TX_PENDING 100 |
59 | #define LOW_TX_PENDING 80 | 59 | #define LOW_TX_PENDING 80 |
60 | 60 | ||
61 | #define HIGH_RX_PENDING 50 | ||
62 | #define LOW_RX_PENDING 20 | ||
63 | |||
61 | #define MWIFIEX_UPLD_SIZE (2312) | 64 | #define MWIFIEX_UPLD_SIZE (2312) |
62 | 65 | ||
63 | #define MAX_EVENT_SIZE 2048 | 66 | #define MAX_EVENT_SIZE 2048 |
@@ -714,6 +717,12 @@ struct mwifiex_adapter { | |||
714 | atomic_t cmd_pending; | 717 | atomic_t cmd_pending; |
715 | struct workqueue_struct *workqueue; | 718 | struct workqueue_struct *workqueue; |
716 | struct work_struct main_work; | 719 | struct work_struct main_work; |
720 | struct workqueue_struct *rx_workqueue; | ||
721 | struct work_struct rx_work; | ||
722 | bool rx_work_enabled; | ||
723 | bool rx_processing; | ||
724 | bool delay_main_work; | ||
725 | bool rx_locked; | ||
717 | struct mwifiex_bss_prio_tbl bss_prio_tbl[MWIFIEX_MAX_BSS_NUM]; | 726 | struct mwifiex_bss_prio_tbl bss_prio_tbl[MWIFIEX_MAX_BSS_NUM]; |
718 | /* spin lock for init/shutdown */ | 727 | /* spin lock for init/shutdown */ |
719 | spinlock_t mwifiex_lock; | 728 | spinlock_t mwifiex_lock; |
@@ -754,6 +763,10 @@ struct mwifiex_adapter { | |||
754 | struct list_head scan_pending_q; | 763 | struct list_head scan_pending_q; |
755 | /* spin lock for scan_pending_q */ | 764 | /* spin lock for scan_pending_q */ |
756 | spinlock_t scan_pending_q_lock; | 765 | spinlock_t scan_pending_q_lock; |
766 | /* spin lock for RX queue */ | ||
767 | spinlock_t rx_q_lock; | ||
768 | /* spin lock for RX processing routine */ | ||
769 | spinlock_t rx_proc_lock; | ||
757 | struct sk_buff_head usb_rx_data_q; | 770 | struct sk_buff_head usb_rx_data_q; |
758 | u32 scan_processing; | 771 | u32 scan_processing; |
759 | u16 region_code; | 772 | u16 region_code; |
@@ -831,6 +844,7 @@ struct mwifiex_adapter { | |||
831 | u8 num_mem_types; | 844 | u8 num_mem_types; |
832 | u8 curr_mem_idx; | 845 | u8 curr_mem_idx; |
833 | bool scan_chan_gap_enabled; | 846 | bool scan_chan_gap_enabled; |
847 | struct sk_buff_head rx_data_q; | ||
834 | }; | 848 | }; |
835 | 849 | ||
836 | int mwifiex_init_lock_list(struct mwifiex_adapter *adapter); | 850 | int mwifiex_init_lock_list(struct mwifiex_adapter *adapter); |
diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c index 2ada1b709778..1504b16e248e 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c | |||
@@ -1233,6 +1233,7 @@ static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter) | |||
1233 | struct sk_buff *skb_tmp = NULL; | 1233 | struct sk_buff *skb_tmp = NULL; |
1234 | struct mwifiex_pcie_buf_desc *desc; | 1234 | struct mwifiex_pcie_buf_desc *desc; |
1235 | struct mwifiex_pfu_buf_desc *desc2; | 1235 | struct mwifiex_pfu_buf_desc *desc2; |
1236 | unsigned long flags; | ||
1236 | 1237 | ||
1237 | if (!mwifiex_pcie_ok_to_access_hw(adapter)) | 1238 | if (!mwifiex_pcie_ok_to_access_hw(adapter)) |
1238 | mwifiex_pm_wakeup_card(adapter); | 1239 | mwifiex_pm_wakeup_card(adapter); |
@@ -1283,7 +1284,16 @@ static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter) | |||
1283 | "info: RECV DATA: Rd=%#x, Wr=%#x, Len=%d\n", | 1284 | "info: RECV DATA: Rd=%#x, Wr=%#x, Len=%d\n", |
1284 | card->rxbd_rdptr, wrptr, rx_len); | 1285 | card->rxbd_rdptr, wrptr, rx_len); |
1285 | skb_pull(skb_data, INTF_HEADER_LEN); | 1286 | skb_pull(skb_data, INTF_HEADER_LEN); |
1286 | mwifiex_handle_rx_packet(adapter, skb_data); | 1287 | if (adapter->rx_work_enabled) { |
1288 | spin_lock_irqsave(&adapter->rx_q_lock, flags); | ||
1289 | skb_queue_tail(&adapter->rx_data_q, skb_data); | ||
1290 | spin_unlock_irqrestore(&adapter->rx_q_lock, | ||
1291 | flags); | ||
1292 | adapter->data_received = true; | ||
1293 | atomic_inc(&adapter->rx_pending); | ||
1294 | } else { | ||
1295 | mwifiex_handle_rx_packet(adapter, skb_data); | ||
1296 | } | ||
1287 | } | 1297 | } |
1288 | 1298 | ||
1289 | skb_tmp = dev_alloc_skb(MWIFIEX_RX_DATA_BUF_SIZE); | 1299 | skb_tmp = dev_alloc_skb(MWIFIEX_RX_DATA_BUF_SIZE); |
diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index bdab122db5cf..ea8fc587e90f 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c | |||
@@ -1039,6 +1039,7 @@ static int mwifiex_decode_rx_packet(struct mwifiex_adapter *adapter, | |||
1039 | struct sk_buff *skb, u32 upld_typ) | 1039 | struct sk_buff *skb, u32 upld_typ) |
1040 | { | 1040 | { |
1041 | u8 *cmd_buf; | 1041 | u8 *cmd_buf; |
1042 | unsigned long flags; | ||
1042 | __le16 *curr_ptr = (__le16 *)skb->data; | 1043 | __le16 *curr_ptr = (__le16 *)skb->data; |
1043 | u16 pkt_len = le16_to_cpu(*curr_ptr); | 1044 | u16 pkt_len = le16_to_cpu(*curr_ptr); |
1044 | 1045 | ||
@@ -1048,7 +1049,15 @@ static int mwifiex_decode_rx_packet(struct mwifiex_adapter *adapter, | |||
1048 | switch (upld_typ) { | 1049 | switch (upld_typ) { |
1049 | case MWIFIEX_TYPE_DATA: | 1050 | case MWIFIEX_TYPE_DATA: |
1050 | dev_dbg(adapter->dev, "info: --- Rx: Data packet ---\n"); | 1051 | dev_dbg(adapter->dev, "info: --- Rx: Data packet ---\n"); |
1051 | mwifiex_handle_rx_packet(adapter, skb); | 1052 | if (adapter->rx_work_enabled) { |
1053 | spin_lock_irqsave(&adapter->rx_q_lock, flags); | ||
1054 | skb_queue_tail(&adapter->rx_data_q, skb); | ||
1055 | spin_unlock_irqrestore(&adapter->rx_q_lock, flags); | ||
1056 | adapter->data_received = true; | ||
1057 | atomic_inc(&adapter->rx_pending); | ||
1058 | } else { | ||
1059 | mwifiex_handle_rx_packet(adapter, skb); | ||
1060 | } | ||
1052 | break; | 1061 | break; |
1053 | 1062 | ||
1054 | case MWIFIEX_TYPE_CMD: | 1063 | case MWIFIEX_TYPE_CMD: |