]>
git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blob - drivers/usb/intel_ulpss/usb_stub.c
1 // SPDX-License-Identifier: GPL-2.0-only
3 * Intel LPSS USB driver
5 * Copyright (c) 2020, Intel Corporation.
8 #include <linux/acpi.h>
9 #include <linux/errno.h>
10 #include <linux/slab.h>
12 #include "protocol_intel_ulpss.h"
13 #include "ulpss_bridge.h"
15 #define MAKE_CMD_ID(type, cmd) ((type << 8) | cmd)
16 static bool usb_stub_validate(u8
*data
, u32 data_len
)
19 struct cmd_header
*header
= (struct cmd_header
*)data
;
21 /* verify the respone flag */
22 if (header
->cmd
!= GPIO_INTR_NOTIFY
&&
23 ((header
->flags
& RESP_FLAG
) == 0))
26 /* verify the payload len */
27 is_valid
= is_valid
&& (header
->len
+ sizeof(*header
) == data_len
);
32 static int usb_stub_parse(struct usb_stub
*stub
, struct cmd_header
*header
)
36 if (!stub
|| !header
|| (header
->len
< 0))
39 stub
->len
= header
->len
;
44 memcpy(stub
->buf
, header
->data
, header
->len
);
46 ret
= stub
->parse(stub
, header
->cmd
, header
->flags
,
47 header
->data
, header
->len
);
53 * Bottom half processing work function (instead of thread handler)
54 * for processing fw messages
56 static void event_work_cb(struct work_struct
*work
)
58 struct usb_bridge
*intel_ulpss_dev
;
59 struct bridge_msg msg_in_proc
= { 0 };
60 struct usb_stub
*stub
;
61 struct cmd_header
*header
;
65 intel_ulpss_dev
= container_of(work
, struct usb_bridge
, event_work
);
66 BUG_ON(!intel_ulpss_dev
);
68 while (kfifo_get(&intel_ulpss_dev
->msg_fifo
, &msg_in_proc
)) {
72 header
= (struct cmd_header
*)msg_in_proc
.buf
;
74 dev_dbg(&intel_ulpss_dev
->intf
->dev
,
75 "receive: type:%d cmd:%d flags:%d len:%d\n",
76 header
->type
, header
->cmd
, header
->flags
, header
->len
);
79 if (!usb_stub_validate(msg_in_proc
.buf
, msg_in_proc
.len
)) {
80 dev_err(&intel_ulpss_dev
->intf
->dev
,
81 "%s header->len:%d payload_len:%ld\n ",
82 __func__
, header
->len
, msg_in_proc
.len
);
86 stub
= usb_stub_find(intel_ulpss_dev
->intf
, header
->type
);
87 ret
= usb_stub_parse(stub
, header
);
89 dev_err(&intel_ulpss_dev
->intf
->dev
,
90 "%s failed to parse data: ret:%d type:%d len: %d",
91 __func__
, ret
, header
->type
, header
->len
);
95 rcv_cmd_id
= MAKE_CMD_ID(stub
->type
, header
->cmd
);
96 if (rcv_cmd_id
== stub
->cmd_id
) {
98 wake_up_interruptible(&intel_ulpss_dev
->bulk_out_ack
);
101 dev_warn(&intel_ulpss_dev
->intf
->dev
,
102 "%s rcv_cmd_id:%x != stub->cmd_id:%x",
103 __func__
, rcv_cmd_id
, stub
->cmd_id
);
108 struct usb_stub
*usb_stub_alloc(struct usb_interface
*intf
)
110 struct usb_bridge
*intel_ulpss_dev
= usb_get_intfdata(intf
);
111 struct usb_stub
*cur_stub
;
113 cur_stub
= kzalloc(sizeof(struct usb_stub
), GFP_KERNEL
);
115 dev_err(&intf
->dev
, "%s no memory for new stub", __func__
);
119 mutex_init(&cur_stub
->stub_mutex
);
120 INIT_LIST_HEAD(&cur_stub
->list
);
122 list_add_tail(&cur_stub
->list
, &intel_ulpss_dev
->stubs_list
);
123 intel_ulpss_dev
->stub_count
++;
125 "%s enuming stub intel_ulpss_dev->stub_count:%ld type:%d success\n",
126 __func__
, intel_ulpss_dev
->stub_count
, cur_stub
->type
);
131 int usb_stub_init(struct usb_interface
*intf
)
133 struct usb_bridge
*intel_ulpss_dev
= usb_get_intfdata(intf
);
135 if (!intel_ulpss_dev
)
138 INIT_WORK(&intel_ulpss_dev
->event_work
, event_work_cb
);
143 struct usb_stub
*usb_stub_find(struct usb_interface
*intf
, u8 type
)
145 struct usb_bridge
*intel_ulpss_dev
= usb_get_intfdata(intf
);
146 struct usb_stub
*cur_stub
;
148 if (!intel_ulpss_dev
)
151 list_for_each_entry (cur_stub
, &intel_ulpss_dev
->stubs_list
, list
) {
152 if (cur_stub
->type
== type
)
156 dev_err(&intf
->dev
, "%s usb stub not find, type: %d", __func__
, type
);
160 int usb_stub_write(struct usb_stub
*stub
, u8 cmd
, u8
*data
, u8 len
,
161 bool wait_ack
, long timeout
)
165 u8 buff
[MAX_PACKET_SIZE
] = { 0 };
167 struct cmd_header
*header
;
168 struct usb_bridge
*intel_ulpss_dev
= usb_get_intfdata(stub
->intf
);
170 if (!stub
|| !intel_ulpss_dev
)
173 header
= (struct cmd_header
*)buff
;
174 if (len
>= MAX_PAYLOAD_SIZE
)
182 header
->type
= stub
->type
;
184 header
->flags
= flags
;
187 memcpy(header
->data
, data
, len
);
188 dev_dbg(&intel_ulpss_dev
->intf
->dev
,
189 "send: type:%d cmd:%d flags:%d len:%d\n", header
->type
,
190 header
->cmd
, header
->flags
, header
->len
);
192 stub
->cmd_id
= MAKE_CMD_ID(stub
->type
, cmd
);
194 ret
= intel_ulpss_bridge_write(
195 stub
->intf
, header
, sizeof(struct cmd_header
) + len
, timeout
);
197 if (ret
!= sizeof(struct cmd_header
) + len
) {
198 dev_err(&intel_ulpss_dev
->intf
->dev
,
199 "%s bridge write failed ret:%d total_len:%ld\n ",
200 __func__
, ret
, sizeof(struct cmd_header
) + len
);
204 if (flags
& ACK_FLAG
) {
205 ret
= wait_event_interruptible_timeout(
206 intel_ulpss_dev
->bulk_out_ack
, (stub
->acked
), timeout
);
210 dev_err(&intel_ulpss_dev
->intf
->dev
,
211 "acked wait interrupted ret:%d timeout:%ld ack:%d\n",
212 ret
, timeout
, stub
->acked
);
215 } else if (ret
== 0) {
216 dev_err(&intel_ulpss_dev
->intf
->dev
,
217 "acked sem wait timed out ret:%d timeout:%ld ack:%d\n",
218 ret
, timeout
, stub
->acked
);
226 void usb_stub_broadcast(struct usb_interface
*intf
, long event
,
230 struct usb_stub
*cur_stub
= NULL
;
231 struct usb_bridge
*intel_ulpss_dev
= usb_get_intfdata(intf
);
233 if (!intel_ulpss_dev
)
236 list_for_each_entry (cur_stub
, &intel_ulpss_dev
->stubs_list
, list
) {
237 if (cur_stub
&& cur_stub
->notify
) {
238 ret
= cur_stub
->notify(cur_stub
, event
, event_data
);
245 void usb_stub_cleanup(struct usb_interface
*intf
)
247 struct usb_stub
*cur_stub
= NULL
;
248 struct usb_stub
*next
= NULL
;
249 struct usb_bridge
*intel_ulpss_dev
= usb_get_intfdata(intf
);
251 if (!intel_ulpss_dev
)
254 list_for_each_entry_safe (cur_stub
, next
, &intel_ulpss_dev
->stubs_list
,
259 list_del_init(&cur_stub
->list
);
260 dev_dbg(&intf
->dev
, "%s type:%d\n ", __func__
, cur_stub
->type
);
261 if (cur_stub
->cleanup
)
262 cur_stub
->cleanup(cur_stub
);
264 mutex_destroy(&cur_stub
->stub_mutex
);
267 intel_ulpss_dev
->stub_count
--;
271 struct acpi_device
*find_adev_by_hid(struct acpi_device
*parent
,
274 struct acpi_device
*adev
;
279 list_for_each_entry (adev
, &parent
->children
, node
) {
280 if (!strcmp(hid
, acpi_device_hid(adev
)))