hci_send_acl(conn->hchan, skb, flags);
 }
 
+static bool __chan_is_moving(struct l2cap_chan *chan)
+{
+       return chan->move_state != L2CAP_MOVE_STABLE &&
+              chan->move_state != L2CAP_MOVE_WAIT_PREPARE;
+}
+
 static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
 {
        struct hci_conn *hcon = chan->conn->hcon;
        l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req);
 }
 
+static void l2cap_move_setup(struct l2cap_chan *chan)
+{
+       struct sk_buff *skb;
+
+       BT_DBG("chan %p", chan);
+
+       if (chan->mode != L2CAP_MODE_ERTM)
+               return;
+
+       __clear_retrans_timer(chan);
+       __clear_monitor_timer(chan);
+       __clear_ack_timer(chan);
+
+       chan->retry_count = 0;
+       skb_queue_walk(&chan->tx_q, skb) {
+               if (bt_cb(skb)->control.retries)
+                       bt_cb(skb)->control.retries = 1;
+               else
+                       break;
+       }
+
+       chan->expected_tx_seq = chan->buffer_seq;
+
+       clear_bit(CONN_REJ_ACT, &chan->conn_state);
+       clear_bit(CONN_SREJ_ACT, &chan->conn_state);
+       l2cap_seq_list_clear(&chan->retrans_list);
+       l2cap_seq_list_clear(&chan->srej_list);
+       skb_queue_purge(&chan->srej_q);
+
+       chan->tx_state = L2CAP_TX_STATE_XMIT;
+       chan->rx_state = L2CAP_RX_STATE_MOVE;
+
+       set_bit(CONN_REMOTE_BUSY, &chan->conn_state);
+}
+
 static void l2cap_chan_ready(struct l2cap_chan *chan)
 {
        /* This clears all conf flags, including CONF_NOT_COMPLETE */
                                         u16 cmd_len, void *data)
 {
        struct l2cap_move_chan_req *req = data;
+       struct l2cap_chan *chan;
        u16 icid = 0;
        u16 result = L2CAP_MR_NOT_ALLOWED;
 
        if (!enable_hs)
                return -EINVAL;
 
-       /* Placeholder: Always refuse */
+       chan = l2cap_get_chan_by_dcid(conn, icid);
+       if (!chan) {
+               l2cap_send_move_chan_rsp(conn, cmd->ident, icid,
+                                        L2CAP_MR_NOT_ALLOWED);
+               return 0;
+       }
+
+       if (chan->scid < L2CAP_CID_DYN_START ||
+           chan->chan_policy == BT_CHANNEL_POLICY_BREDR_ONLY ||
+           (chan->mode != L2CAP_MODE_ERTM &&
+            chan->mode != L2CAP_MODE_STREAMING)) {
+               result = L2CAP_MR_NOT_ALLOWED;
+               goto send_move_response;
+       }
+
+       if (chan->local_amp_id == req->dest_amp_id) {
+               result = L2CAP_MR_SAME_ID;
+               goto send_move_response;
+       }
+
+       if (req->dest_amp_id) {
+               struct hci_dev *hdev;
+               hdev = hci_dev_get(req->dest_amp_id);
+               if (!hdev || hdev->dev_type != HCI_AMP ||
+                   !test_bit(HCI_UP, &hdev->flags)) {
+                       if (hdev)
+                               hci_dev_put(hdev);
+
+                       result = L2CAP_MR_BAD_ID;
+                       goto send_move_response;
+               }
+               hci_dev_put(hdev);
+       }
+
+       /* Detect a move collision.  Only send a collision response
+        * if this side has "lost", otherwise proceed with the move.
+        * The winner has the larger bd_addr.
+        */
+       if ((__chan_is_moving(chan) ||
+            chan->move_role != L2CAP_MOVE_ROLE_NONE) &&
+           bacmp(conn->src, conn->dst) > 0) {
+               result = L2CAP_MR_COLLISION;
+               goto send_move_response;
+       }
+
+       chan->ident = cmd->ident;
+       chan->move_role = L2CAP_MOVE_ROLE_RESPONDER;
+       l2cap_move_setup(chan);
+       chan->move_id = req->dest_amp_id;
+       icid = chan->dcid;
+
+       if (!req->dest_amp_id) {
+               /* Moving to BR/EDR */
+               if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
+                       chan->move_state = L2CAP_MOVE_WAIT_LOCAL_BUSY;
+                       result = L2CAP_MR_PEND;
+               } else {
+                       chan->move_state = L2CAP_MOVE_WAIT_CONFIRM;
+                       result = L2CAP_MR_SUCCESS;
+               }
+       } else {
+               chan->move_state = L2CAP_MOVE_WAIT_PREPARE;
+               /* Placeholder - uncomment when amp functions are available */
+               /*amp_accept_physical(chan, req->dest_amp_id);*/
+               result = L2CAP_MR_PEND;
+       }
+
+send_move_response:
        l2cap_send_move_chan_rsp(conn, cmd->ident, icid, result);
 
+       l2cap_chan_unlock(chan);
+
        return 0;
 }