1#include <asm.h>
2#include <compiler/core.h>
3#include <drivers/pci.h>
4#include <drivers/usb/xhci.h>
5#include <math/bit.h>
6#include <mem/alloc.h>
7#include <mem/page.h>
8#include <mem/vmm.h>
9#include <stdbool.h>
10#include <stdint.h>
11#include <string.h>
12#include <time/spin_sleep.h>
13
14#include "internal.h"
15
16bool xhci_controller_stop(struct xhci_device *dev) {
17 struct xhci_op_regs cc_mem_io *op = dev->op_regs;
18
19 struct xhci_usbcmd usbcmd = {.raw = mmio_read_32(address: &op->usbcmd)};
20 usbcmd.run_stop = 0;
21 mmio_write_32(address: &op->usbcmd, value: usbcmd.raw);
22 uint64_t timeout = XHCI_DEVICE_TIMEOUT * 1000;
23 while (!BIT_TEST(mmio_read_32(&op->usbsts), 0) && timeout--) {
24 sleep_spin_us(us: 10);
25 if (timeout == 0)
26 return false;
27 }
28 return true;
29}
30
31bool xhci_controller_reset(struct xhci_device *dev) {
32 struct xhci_op_regs cc_mem_io *op = dev->op_regs;
33
34 struct xhci_usbcmd usbcmd = {.raw = mmio_read_32(address: &op->usbcmd)};
35 usbcmd.host_controller_reset = 1;
36 mmio_write_32(address: &op->usbcmd, value: usbcmd.raw);
37 uint64_t timeout = XHCI_DEVICE_TIMEOUT * 1000;
38
39 while (BIT_TEST(mmio_read_32(&op->usbcmd), 1) && timeout--) {
40 sleep_spin_us(us: 10);
41 if (timeout == 0)
42 return false;
43 }
44 return true;
45}
46
47bool xhci_controller_start(struct xhci_device *dev) {
48 struct xhci_op_regs cc_mem_io *op = dev->op_regs;
49
50 struct xhci_usbcmd usbcmd = {.raw = mmio_read_32(address: &op->usbcmd)};
51 usbcmd.run_stop = 1;
52 mmio_write_32(address: &op->usbcmd, value: usbcmd.raw);
53 uint64_t timeout = XHCI_DEVICE_TIMEOUT * 1000;
54 while (BIT_TEST(mmio_read_32(&op->usbsts), 0) && timeout--) {
55 sleep_spin_us(us: 10);
56 if (timeout == 0)
57 return false;
58 }
59
60 return true;
61}
62
63void xhci_controller_enable_ints(struct xhci_device *dev) {
64 struct xhci_op_regs cc_mem_io *op = dev->op_regs;
65 struct xhci_usbcmd usbcmd = {.raw = mmio_read_32(address: &op->usbcmd)};
66 usbcmd.interrupter_enable = 1;
67 mmio_write_32(address: &op->usbcmd, value: usbcmd.raw);
68}
69
70void xhci_wake_waiter(struct xhci_device *dev, struct xhci_request *req) {
71 xhci_trace("wake waiter for %s", xhci_request_command_type_str(req->type));
72 cc_ignore(dev);
73 event_set_full(e: &req->event, reason: THREAD_WAKE_REASON_BLOCKING_IO, NULL);
74}
75
76void xhci_cleanup(struct xhci_device *dev, struct xhci_request *req) {
77 cc_ignore(dev);
78
79 struct usb_request *urb = req->urb;
80 struct usb_device *udev = urb->dev;
81 urb->status = xhci_rq_to_usb_status(req);
82 urb->complete(urb);
83
84 vfree(req->command);
85 vfree(req);
86
87 usb_device_putref(dev: udev);
88}
89
90struct xhci_ring *xhci_allocate_ring(void) {
91 struct xhci_trb *trbs = valloc_aligned(PAGE_SIZE, PAGE_SIZE, ALLOC_ZERO);
92 if (!trbs)
93 return NULL;
94
95 paddr_t phys = vmm_get_phys((vaddr_t) trbs);
96 struct xhci_ring *ring = vnew(ring, ALLOC_ZERO);
97 if (!ring) {
98 vfree_aligned(trbs);
99 return NULL;
100 }
101
102 ring->phys = phys;
103 ring->cycle = 1;
104 ring->size = TRB_RING_SIZE;
105 ring->trbs = trbs;
106 ring->enqueue_index = 0;
107 ring->dequeue_index = 0;
108
109 struct xhci_trb *link = &trbs[TRB_RING_SIZE - 1];
110
111 link->parameter = phys;
112 link->status = 0;
113 link->control =
114 TRB_SET_TYPE(TRB_TYPE_LINK) | TRB_TOGGLE_CYCLE_BIT | ring->cycle;
115
116 return ring;
117}
118
119struct xhci_ring *xhci_allocate_event_ring(void) {
120 struct xhci_ring *er = vnew(er, ALLOC_ZERO);
121 if (!er)
122 return NULL;
123
124 er->trbs = valloc_aligned(PAGE_SIZE, PAGE_SIZE, ALLOC_ZERO);
125 if (!er->trbs) {
126 vfree(er);
127 return NULL;
128 }
129 er->phys = vmm_get_phys((vaddr_t) er->trbs);
130
131 er->size = TRB_RING_SIZE;
132 er->dequeue_index = 0;
133 er->cycle = 1;
134 return er;
135}
136
137void xhci_free_ring(struct xhci_ring *ring) {
138 if (!ring)
139 return;
140
141 vfree_aligned(ring->trbs);
142 vfree(ring);
143}
144
145void xhci_teardown_slot(struct xhci_slot *me) {
146 enum irql irql = spin_lock_high(&me->dev->lock);
147 struct xhci_ring *copy_into[32];
148 me->udev = NULL;
149 me->port = NULL;
150 memcpy(copy_into, me->ep_rings, sizeof(struct xhci_ring *) * 32);
151 memset(me->ep_rings, 0, sizeof(struct xhci_ring *) * 32);
152 xhci_slot_set_state(slot: me, new: XHCI_SLOT_STATE_DISCONNECTED);
153 spin_unlock(&me->dev->lock, irql);
154
155 /* tear down the rings */
156 for (size_t i = 0; i < 32; i++) {
157 struct xhci_ring *ring = copy_into[i];
158 xhci_free_ring(ring);
159 }
160
161 xhci_trace(ANSI_RED "teardown" ANSI_RESET);
162 xhci_disable_slot(dev: me->dev, slot_id: me->slot_id);
163}
164
165void xhci_reset_slot(struct usb_device *dev) {
166 struct xhci_device *xdev = dev->host->driver_data;
167 uint8_t slot_id = ((struct xhci_slot *) dev->slot)->slot_id;
168 struct xhci_request request = {0};
169 struct xhci_command cmd = {0};
170
171 xhci_request_init_blocking(req: &request, cmd: &cmd, /* port = */ 0,
172 t: XHCI_CMD_TYPE_RESET_DEVICE);
173
174 struct xhci_trb outgoing = {
175 .parameter = 0,
176 .status = 0,
177 .control =
178 TRB_SET_TYPE(TRB_TYPE_RESET_DEVICE) | TRB_SET_SLOT_ID(slot_id),
179 };
180
181 cmd = (struct xhci_command){
182 .private = &outgoing,
183 .emit = xhci_emit_singular,
184 .ep_id = 0,
185 .slot = NULL,
186 .ring = xdev->cmd_ring,
187 .request = &request,
188 .num_trbs = 1,
189 };
190
191 xhci_send_command_and_block(dev: xdev, cmd: &cmd);
192
193 xhci_trace("our status was %u", request.status);
194}
195