1#include <asm.h>
2#include <compiler/core.h>
3#include <drivers/pci.h>
4#include <drivers/usb/xhci.h>
5#include <mem/alloc.h>
6#include <mem/page.h>
7#include <mem/vmm.h>
8#include <sch/sched.h>
9#include <stdbool.h>
10#include <stdint.h>
11#include <time/spin_sleep.h>
12
13#include "internal.h"
14
15void xhci_emit_singular(struct xhci_command *cmd, struct xhci_ring *ring) {
16 struct xhci_trb *src = cmd->private;
17 struct xhci_trb *dst = xhci_ring_next_trb(ring);
18
19 dst->parameter = src->parameter;
20 dst->status = src->status;
21 dst->control |= src->control;
22
23 /* Track completion TRB */
24 cmd->request->trb_phys = xhci_get_trb_phys(ring, trb: dst);
25 cmd->request->last_trb = dst;
26}
27
28bool xhci_send_command(struct xhci_device *dev, struct xhci_command *cmd) {
29 struct xhci_ring *ring = cmd->ring;
30 struct xhci_request *rq = cmd->request;
31
32 enum irql irql = spin_lock_high(&dev->lock);
33
34 if (!xhci_ring_can_reserve(ring, ntrbs: cmd->num_trbs)) {
35 xhci_request_move(dev, req: rq, new_list: XHCI_REQ_LIST_WAITING);
36 spin_unlock(&dev->lock, irql);
37 return true;
38 }
39
40 if (cmd->slot) {
41 if (!xhci_slot_getref(obj: cmd->slot)) {
42 spin_unlock(&dev->lock, irql);
43 return false;
44 }
45 }
46
47 xhci_ring_reserve(ring, ntrbs: cmd->num_trbs);
48
49 /* Emit TRBs */
50 cmd->emit(cmd, ring);
51
52 if (rq->port)
53 rq->generation = dev->port_info[rq->port - 1].generation;
54
55 xhci_request_move(dev, req: rq, new_list: XHCI_REQ_LIST_OUTGOING);
56
57 xhci_ring_doorbell(dev, slot_id: cmd->slot ? cmd->slot->slot_id : 0, ep_id: cmd->ep_id);
58 spin_unlock(&dev->lock, irql);
59 return true;
60}
61
62/* Submit a single interrupt IN transfer, blocking until completion */
63enum usb_error xhci_submit_interrupt_transfer(struct usb_request *req) {
64 struct usb_device *dev = req->dev;
65 struct xhci_device *xhci = dev->host->driver_data;
66 struct xhci_slot *slot = dev->slot;
67 enum usb_error return_status = USB_OK;
68
69 /* we drop this ref in the callback to the urb */
70 if (!usb_device_getref(obj: dev)) {
71 return USB_ERR_NO_DEVICE;
72 }
73
74 if (!xhci_slot_getref(obj: slot)) {
75 usb_device_putref(dev);
76 return USB_ERR_NO_DEVICE;
77 }
78
79 struct usb_endpoint *ep = req->ep;
80
81 uint8_t ep_id = get_ep_index(ep);
82
83 struct xhci_ring *ring = slot->ep_rings[ep_id];
84 if (!ring || !req->buffer || req->length == 0) {
85 xhci_warn("Invalid parameters for interrupt transfer");
86 return_status = USB_ERR_INVALID_ARGUMENT;
87 goto out;
88 }
89
90 uint64_t parameter = vmm_get_phys((vaddr_t) req->buffer);
91 uint32_t status = req->length;
92 status |= TRB_SET_INTERRUPTER_TARGET(0);
93
94 uint32_t control = TRB_SET_TYPE(TRB_TYPE_NORMAL);
95 control |= TRB_IOC_BIT;
96
97 struct xhci_request *xreq = vnew(xreq, ALLOC_ZERO);
98 if (!xreq) {
99 return_status = USB_ERR_OOM;
100 goto out;
101 }
102
103 struct xhci_command *cmd = vnew(cmd, ALLOC_ZERO);
104
105 if (!cmd) {
106 vfree(xreq);
107 return_status = USB_ERR_OOM;
108 goto out;
109 }
110
111 xhci_request_init(req: xreq, cmd, rq: req, t: XHCI_CMD_TYPE_INTERRUPT_TRANSFER);
112
113 struct xhci_trb outgoing = {
114 .parameter = parameter,
115 .control = control,
116 .status = status,
117 };
118
119 *cmd = (struct xhci_command){
120 .ring = ring,
121 .private = &outgoing,
122 .ep_id = ep_id,
123 .slot = slot,
124 .request = xreq,
125 .emit = xhci_emit_singular,
126 .num_trbs = 1,
127 };
128
129 if (!xhci_send_command(dev: xhci, cmd))
130 return_status = USB_ERR_NO_DEVICE;
131
132out:
133 xhci_slot_putref(slot);
134 return return_status;
135}
136
137struct xhci_ctrl_emit {
138 struct usb_setup_packet *setup;
139 uint64_t buffer_phys;
140 uint16_t length;
141};
142
143static void xhci_emit_control(struct xhci_command *cmd,
144 struct xhci_ring *ring) {
145 struct xhci_ctrl_emit *c = cmd->private;
146 struct xhci_trb *trb;
147
148 /* Setup stage */
149 trb = xhci_ring_next_trb(ring);
150 trb->parameter = ((uint64_t) c->setup->bitmap_request_type) |
151 ((uint64_t) c->setup->request << 8) |
152 ((uint64_t) c->setup->value << 16) |
153 ((uint64_t) c->setup->index << 32) |
154 ((uint64_t) c->setup->length << 48);
155
156 trb->status = 8;
157 trb->control |= TRB_IDT_BIT | TRB_SET_TYPE(TRB_TYPE_SETUP_STAGE);
158 trb->control |= (XHCI_SETUP_TRANSFER_TYPE_OUT << 16);
159
160 if (c->length) {
161 trb = xhci_ring_next_trb(ring);
162 trb->parameter = c->buffer_phys;
163 trb->status = c->length;
164 trb->control |= TRB_SET_TYPE(TRB_TYPE_DATA_STAGE);
165 trb->control |= (XHCI_SETUP_TRANSFER_TYPE_IN << 16);
166 }
167
168 trb = xhci_ring_next_trb(ring);
169 trb->parameter = 0;
170 trb->status = 0;
171 trb->control |= TRB_SET_TYPE(TRB_TYPE_STATUS_STAGE) | TRB_IOC_BIT;
172
173 /* Completion on status stage */
174 cmd->request->last_trb = trb;
175 cmd->request->trb_phys = xhci_get_trb_phys(ring, trb);
176}
177
178static inline enum usb_error fail_control_transfer(enum usb_error err) {
179 xhci_warn("control transfer failed with %s", usb_error_str(err));
180 return err;
181}
182
183static enum usb_error xhci_send_control_transfer(struct xhci_device *dev,
184 struct xhci_slot *slot,
185 struct usb_request *req) {
186 if (!req->setup)
187 return fail_control_transfer(err: USB_ERR_INVALID_ARGUMENT);
188
189 /* request is responsible for dropping this */
190 if (!usb_device_getref(obj: req->dev))
191 return fail_control_transfer(err: USB_ERR_NO_DEVICE);
192
193 if (!xhci_slot_getref(obj: slot)) {
194 usb_device_putref(dev: req->dev);
195 return fail_control_transfer(err: USB_ERR_NO_DEVICE);
196 }
197
198 struct xhci_request *xreq = vnew(xreq, ALLOC_ZERO);
199 struct xhci_command *cmd = vnew(cmd, ALLOC_ZERO);
200 struct xhci_ctrl_emit *emit = vnew(emit, ALLOC_ZERO);
201
202 if (!xreq || !cmd || !emit) {
203 /* drop USB dev ref, drop slot ref, dealloc, bye bye */
204 usb_device_putref(dev: req->dev);
205 xhci_slot_putref(slot);
206 vfree(xreq);
207 vfree(cmd);
208 vfree(emit);
209 return fail_control_transfer(err: USB_ERR_OOM);
210 }
211
212 emit->setup = req->setup;
213 emit->length = req->setup->length;
214 emit->buffer_phys = emit->length ? vmm_get_phys((vaddr_t) req->buffer) : 0;
215
216 xhci_request_init(req: xreq, cmd, rq: req, t: XHCI_CMD_TYPE_CONTROL_TRANSFER);
217
218 *cmd = (struct xhci_command){
219 .ring = slot->ep_rings[0],
220 .slot = slot,
221 .ep_id = 1,
222 .request = xreq,
223 .private = emit,
224 .emit = xhci_emit_control,
225 .num_trbs = 2 + (emit->length ? 1 : 0),
226 };
227
228 if (!xhci_send_command(dev, cmd)) {
229 xhci_slot_putref(slot);
230 return fail_control_transfer(err: USB_ERR_NO_DEVICE);
231 }
232
233 xhci_slot_putref(slot);
234 return USB_OK;
235}
236
237enum usb_error xhci_control_transfer(struct usb_request *request) {
238 struct xhci_device *xhci = request->dev->host->driver_data;
239 struct xhci_slot *slot = request->dev->slot;
240
241 return xhci_send_control_transfer(dev: xhci, slot, req: request);
242}
243