1#include <asm.h>
2#include <compiler.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_irq_disable(lock: &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(lock: &dev->lock, old: irql);
37 return true;
38 }
39
40 if (cmd->slot) {
41 if (!xhci_slot_get(obj: cmd->slot)) {
42 spin_unlock(lock: &dev->lock, old: 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(lock: &dev->lock, old: 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_get(obj: dev)) {
71 return USB_ERR_NO_DEVICE;
72 }
73
74 if (!xhci_slot_get(obj: slot)) {
75 usb_device_put(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, VMM_FLAG_NONE);
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 =
98 kmalloc(sizeof(struct xhci_request), ALLOC_FLAGS_ZERO);
99 if (!xreq) {
100 return_status = USB_ERR_OOM;
101 goto out;
102 }
103
104 struct xhci_command *cmd =
105 kmalloc(sizeof(struct xhci_command), ALLOC_FLAGS_ZERO);
106
107 if (!cmd) {
108 return_status = USB_ERR_OOM;
109 goto out;
110 }
111
112 xhci_request_init(req: xreq, cmd, rq: req, t: XHCI_CMD_TYPE_INTERRUPT_TRANSFER);
113
114 struct xhci_trb outgoing = {
115 .parameter = parameter,
116 .control = control,
117 .status = status,
118 };
119
120 *cmd = (struct xhci_command){
121 .ring = ring,
122 .private = &outgoing,
123 .ep_id = ep_id,
124 .slot = slot,
125 .request = xreq,
126 .emit = xhci_emit_singular,
127 .num_trbs = 1,
128 };
129
130 if (!xhci_send_command(dev: xhci, cmd))
131 return_status = USB_ERR_NO_DEVICE;
132
133out:
134 xhci_slot_put(slot);
135 return return_status;
136}
137
138struct xhci_ctrl_emit {
139 struct usb_setup_packet *setup;
140 uint64_t buffer_phys;
141 uint16_t length;
142};
143
144void xhci_emit_control(struct xhci_command *cmd, 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
183enum 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_get(obj: req->dev))
191 return fail_control_transfer(err: USB_ERR_NO_DEVICE);
192
193 if (!xhci_slot_get(obj: slot)) {
194 usb_device_put(dev: req->dev);
195 return fail_control_transfer(err: USB_ERR_NO_DEVICE);
196 }
197
198 struct xhci_request *xreq = kmalloc(sizeof(*xreq), ALLOC_FLAGS_ZERO);
199 struct xhci_command *cmd = kmalloc(sizeof(*cmd), ALLOC_FLAGS_ZERO);
200 struct xhci_ctrl_emit *emit = kmalloc(sizeof(*emit), ALLOC_FLAGS_ZERO);
201
202 if (!xreq || !cmd || !emit) {
203 /* drop USB dev ref, drop slot ref, dealloc, bye bye */
204 usb_device_put(dev: req->dev);
205 xhci_slot_put(slot);
206 kfree(xreq);
207 kfree(cmd);
208 kfree(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 =
215 emit->length ? vmm_get_phys((vaddr_t) req->buffer, VMM_FLAG_NONE) : 0;
216
217 xhci_request_init(req: xreq, cmd, rq: req, t: XHCI_CMD_TYPE_CONTROL_TRANSFER);
218
219 *cmd = (struct xhci_command){
220 .ring = slot->ep_rings[0],
221 .slot = slot,
222 .ep_id = 1,
223 .request = xreq,
224 .private = emit,
225 .emit = xhci_emit_control,
226 .num_trbs = 2 + (emit->length ? 1 : 0),
227 };
228
229 if (!xhci_send_command(dev, cmd)) {
230 xhci_slot_put(slot);
231 return fail_control_transfer(err: USB_ERR_NO_DEVICE);
232 }
233
234 xhci_slot_put(slot);
235 return USB_OK;
236}
237
238enum usb_error xhci_control_transfer(struct usb_request *request) {
239 struct xhci_device *xhci = request->dev->host->driver_data;
240 struct xhci_slot *slot = request->dev->slot;
241
242 return xhci_send_control_transfer(dev: xhci, slot, req: request);
243}
244