1*04425292SHsin-chen Chuang // SPDX-License-Identifier: GPL-2.0-only 2*04425292SHsin-chen Chuang /* 3*04425292SHsin-chen Chuang * Copyright (C) 2025 Google Corporation 4*04425292SHsin-chen Chuang */ 5*04425292SHsin-chen Chuang 6*04425292SHsin-chen Chuang #include <linux/skbuff.h> 7*04425292SHsin-chen Chuang #include <linux/types.h> 8*04425292SHsin-chen Chuang 9*04425292SHsin-chen Chuang #include <net/bluetooth/bluetooth.h> 10*04425292SHsin-chen Chuang #include <net/bluetooth/hci.h> 11*04425292SHsin-chen Chuang #include <net/bluetooth/hci_core.h> 12*04425292SHsin-chen Chuang #include <net/bluetooth/hci_drv.h> 13*04425292SHsin-chen Chuang 14*04425292SHsin-chen Chuang int hci_drv_cmd_status(struct hci_dev *hdev, u16 cmd, u8 status) 15*04425292SHsin-chen Chuang { 16*04425292SHsin-chen Chuang struct hci_drv_ev_hdr *hdr; 17*04425292SHsin-chen Chuang struct hci_drv_ev_cmd_status *ev; 18*04425292SHsin-chen Chuang struct sk_buff *skb; 19*04425292SHsin-chen Chuang 20*04425292SHsin-chen Chuang skb = bt_skb_alloc(sizeof(*hdr) + sizeof(*ev), GFP_KERNEL); 21*04425292SHsin-chen Chuang if (!skb) 22*04425292SHsin-chen Chuang return -ENOMEM; 23*04425292SHsin-chen Chuang 24*04425292SHsin-chen Chuang hdr = skb_put(skb, sizeof(*hdr)); 25*04425292SHsin-chen Chuang hdr->opcode = __cpu_to_le16(HCI_DRV_EV_CMD_STATUS); 26*04425292SHsin-chen Chuang hdr->len = __cpu_to_le16(sizeof(*ev)); 27*04425292SHsin-chen Chuang 28*04425292SHsin-chen Chuang ev = skb_put(skb, sizeof(*ev)); 29*04425292SHsin-chen Chuang ev->opcode = __cpu_to_le16(cmd); 30*04425292SHsin-chen Chuang ev->status = status; 31*04425292SHsin-chen Chuang 32*04425292SHsin-chen Chuang hci_skb_pkt_type(skb) = HCI_DRV_PKT; 33*04425292SHsin-chen Chuang 34*04425292SHsin-chen Chuang return hci_recv_frame(hdev, skb); 35*04425292SHsin-chen Chuang } 36*04425292SHsin-chen Chuang EXPORT_SYMBOL(hci_drv_cmd_status); 37*04425292SHsin-chen Chuang 38*04425292SHsin-chen Chuang int hci_drv_cmd_complete(struct hci_dev *hdev, u16 cmd, u8 status, void *rp, 39*04425292SHsin-chen Chuang size_t rp_len) 40*04425292SHsin-chen Chuang { 41*04425292SHsin-chen Chuang struct hci_drv_ev_hdr *hdr; 42*04425292SHsin-chen Chuang struct hci_drv_ev_cmd_complete *ev; 43*04425292SHsin-chen Chuang struct sk_buff *skb; 44*04425292SHsin-chen Chuang 45*04425292SHsin-chen Chuang skb = bt_skb_alloc(sizeof(*hdr) + sizeof(*ev) + rp_len, GFP_KERNEL); 46*04425292SHsin-chen Chuang if (!skb) 47*04425292SHsin-chen Chuang return -ENOMEM; 48*04425292SHsin-chen Chuang 49*04425292SHsin-chen Chuang hdr = skb_put(skb, sizeof(*hdr)); 50*04425292SHsin-chen Chuang hdr->opcode = __cpu_to_le16(HCI_DRV_EV_CMD_COMPLETE); 51*04425292SHsin-chen Chuang hdr->len = __cpu_to_le16(sizeof(*ev) + rp_len); 52*04425292SHsin-chen Chuang 53*04425292SHsin-chen Chuang ev = skb_put(skb, sizeof(*ev)); 54*04425292SHsin-chen Chuang ev->opcode = __cpu_to_le16(cmd); 55*04425292SHsin-chen Chuang ev->status = status; 56*04425292SHsin-chen Chuang 57*04425292SHsin-chen Chuang skb_put_data(skb, rp, rp_len); 58*04425292SHsin-chen Chuang 59*04425292SHsin-chen Chuang hci_skb_pkt_type(skb) = HCI_DRV_PKT; 60*04425292SHsin-chen Chuang 61*04425292SHsin-chen Chuang return hci_recv_frame(hdev, skb); 62*04425292SHsin-chen Chuang } 63*04425292SHsin-chen Chuang EXPORT_SYMBOL(hci_drv_cmd_complete); 64*04425292SHsin-chen Chuang 65*04425292SHsin-chen Chuang int hci_drv_process_cmd(struct hci_dev *hdev, struct sk_buff *skb) 66*04425292SHsin-chen Chuang { 67*04425292SHsin-chen Chuang struct hci_drv_cmd_hdr *hdr; 68*04425292SHsin-chen Chuang const struct hci_drv_handler *handler = NULL; 69*04425292SHsin-chen Chuang u16 opcode, len, ogf, ocf; 70*04425292SHsin-chen Chuang 71*04425292SHsin-chen Chuang hdr = skb_pull_data(skb, sizeof(*hdr)); 72*04425292SHsin-chen Chuang if (!hdr) 73*04425292SHsin-chen Chuang return -EILSEQ; 74*04425292SHsin-chen Chuang 75*04425292SHsin-chen Chuang opcode = __le16_to_cpu(hdr->opcode); 76*04425292SHsin-chen Chuang len = __le16_to_cpu(hdr->len); 77*04425292SHsin-chen Chuang if (len != skb->len) 78*04425292SHsin-chen Chuang return -EILSEQ; 79*04425292SHsin-chen Chuang 80*04425292SHsin-chen Chuang ogf = hci_opcode_ogf(opcode); 81*04425292SHsin-chen Chuang ocf = hci_opcode_ocf(opcode); 82*04425292SHsin-chen Chuang 83*04425292SHsin-chen Chuang if (!hdev->hci_drv) 84*04425292SHsin-chen Chuang return hci_drv_cmd_status(hdev, opcode, 85*04425292SHsin-chen Chuang HCI_DRV_STATUS_UNKNOWN_COMMAND); 86*04425292SHsin-chen Chuang 87*04425292SHsin-chen Chuang if (ogf != HCI_DRV_OGF_DRIVER_SPECIFIC) { 88*04425292SHsin-chen Chuang if (opcode < hdev->hci_drv->common_handler_count) 89*04425292SHsin-chen Chuang handler = &hdev->hci_drv->common_handlers[opcode]; 90*04425292SHsin-chen Chuang } else { 91*04425292SHsin-chen Chuang if (ocf < hdev->hci_drv->specific_handler_count) 92*04425292SHsin-chen Chuang handler = &hdev->hci_drv->specific_handlers[ocf]; 93*04425292SHsin-chen Chuang } 94*04425292SHsin-chen Chuang 95*04425292SHsin-chen Chuang if (!handler || !handler->func) 96*04425292SHsin-chen Chuang return hci_drv_cmd_status(hdev, opcode, 97*04425292SHsin-chen Chuang HCI_DRV_STATUS_UNKNOWN_COMMAND); 98*04425292SHsin-chen Chuang 99*04425292SHsin-chen Chuang if (len != handler->data_len) 100*04425292SHsin-chen Chuang return hci_drv_cmd_status(hdev, opcode, 101*04425292SHsin-chen Chuang HCI_DRV_STATUS_INVALID_PARAMETERS); 102*04425292SHsin-chen Chuang 103*04425292SHsin-chen Chuang return handler->func(hdev, skb->data, len); 104*04425292SHsin-chen Chuang } 105*04425292SHsin-chen Chuang EXPORT_SYMBOL(hci_drv_process_cmd); 106