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