xref: /linux/net/bluetooth/hci_drv.c (revision 1b98f357dadd6ea613a435fbaef1a5dd7b35fd21)
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