initial import
[ps3freebsd_ps3vuart.git] / ps3vuart_port.c
blob549922b2f645e8efbbdb613a91f6b2531073ea8d
1 /*-
2 * Copyright (C) 2011, 2012 glevand <geoffrey.levand@mail.ru>
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer,
10 * without modification, immediately at the beginning of the file.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 * $FreeBSD$
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
32 #include <sys/param.h>
33 #include <sys/module.h>
34 #include <sys/kernel.h>
35 #include <sys/systm.h>
36 #include <sys/malloc.h>
37 #include <sys/lock.h>
38 #include <sys/mutex.h>
40 #include <vm/vm.h>
41 #include <vm/pmap.h>
43 #include <powerpc/ps3/ps3-hvcall.h>
45 #include "ps3vuart_port.h"
47 #define PS3VUART_PORT_LOCK_INIT(_vp) \
48 mtx_init(&(_vp)->vp_mtx, "ps3vuart_port", NULL, MTX_DEF)
49 #define PS3VUART_PORT_LOCK_DESTROY(_vp) mtx_destroy(&(_vp)->vp_mtx)
50 #define PS3VUART_PORT_LOCK(_vp) mtx_lock(&(_vp)->vp_mtx)
51 #define PS3VUART_PORT_UNLOCK(_vp) mtx_unlock(&(_vp)->vp_mtx)
52 #define PS3VUART_PORT_ASSERT_LOCKED(_vp) mtx_assert(&(_vp)->vp_mtx, MA_OWNED)
53 #define PS3VUART_PORT_ASSERT_UNLOCKED(_vp) mtx_assert(&(_vp)->vp_mtx, MA_NOTOWNED)
55 static MALLOC_DEFINE(M_PS3VUART_PORT, "ps3vuart_port", "PS3 VUART Port");
57 static int
58 ps3vuart_port_set_intr_mask(struct ps3vuart_port *vp, uint64_t mask)
60 int err;
62 PS3VUART_PORT_ASSERT_LOCKED(vp);
64 if (vp->vp_intr_mask == mask)
65 return (0);
67 err = lv1_set_virtual_uart_param(vp->vp_port,
68 PS3VUART_PORT_PARAM_INTR_MASK, mask);
69 if (err)
70 return (err);
72 vp->vp_intr_mask = mask;
74 return (0);
77 static int
78 p3vuart_port_discard_rx_bytes(struct ps3vuart_port *vp, int bytes,
79 int *discarded)
81 uint64_t waiting, read;
82 void *buf;
83 int err;
85 PS3VUART_PORT_ASSERT_LOCKED(vp);
87 if (!bytes) {
88 err = lv1_get_virtual_uart_param(vp->vp_port,
89 PS3VUART_PORT_PARAM_RX_BYTES, &waiting);
90 if (err)
91 return (err);
93 bytes = waiting;
96 if (!bytes) {
97 if (discarded)
98 *discarded = 0;
99 return (0);
102 bytes += 128;
104 buf = malloc(bytes, M_PS3VUART_PORT, M_WAITOK);
105 if (!buf)
106 return (ENOMEM);
108 err = lv1_read_virtual_uart(vp->vp_port, vtophys(buf),
109 bytes, &read);
111 free(buf, M_PS3VUART_PORT);
113 if (err)
114 return (err);
116 if (discarded)
117 *discarded = read;
119 return (0);
122 static int
123 ps3vuart_port_push(struct ps3vuart_port *vp, int *pushed)
125 struct ps3vuart_port_buf *vb, *tmp;
126 uint64_t written;
127 int bytes, err;
129 PS3VUART_PORT_ASSERT_LOCKED(vp);
131 if (pushed)
132 *pushed = 0;
134 TAILQ_FOREACH_SAFE(vb, &vp->vp_tx_bufq, vb_queue, tmp) {
135 bytes = vb->vb_tail - vb->vb_head;
137 err = lv1_write_virtual_uart(vp->vp_port,
138 vtophys(vb->vb_head), bytes, &written);
139 if (err)
140 return (err);
142 vp->vp_tx_bufq_bytes -= written;
143 vp->vp_stat.vs_tx_bytes += written;
145 if (pushed)
146 *pushed += written;
148 if (written < bytes) {
149 /* VUART Tx buffer is full */
150 vb->vb_head += written;
151 return (0);
154 TAILQ_REMOVE(&vp->vp_tx_bufq, vb, vb_queue);
155 free(vb, M_PS3VUART_PORT);
158 /* Tx queue is empty, disable Tx interrupts */
160 err = ps3vuart_port_set_intr_mask(vp,
161 vp->vp_intr_mask & ~PS3VUART_PORT_INTR_TX);
163 return (err);
166 static int
167 ps3vuart_port_pull(struct ps3vuart_port *vp, int *pulled)
169 struct ps3vuart_port_buf *vb;
170 uint64_t waiting, read;
171 int err;
173 PS3VUART_PORT_ASSERT_LOCKED(vp);
175 err = lv1_get_virtual_uart_param(vp->vp_port,
176 PS3VUART_PORT_PARAM_RX_BYTES, &waiting);
177 if (err)
178 return (err);
180 if (!waiting) {
181 if (pulled)
182 *pulled = 0;
183 return (0);
186 waiting += 128;
188 vb = malloc(sizeof(*vb) + waiting, M_PS3VUART_PORT, M_WAITOK);
189 if (!vb)
190 return (ENOMEM);
192 err = lv1_read_virtual_uart(vp->vp_port,
193 vtophys(vb->vb_data), waiting, &read);
194 if (err)
195 return (err);
197 vb->vb_head = vb->vb_data;
198 vb->vb_tail = vb->vb_head + read;
200 TAILQ_INSERT_TAIL(&vp->vp_rx_bufq, vb, vb_queue);
202 vp->vp_rx_bufq_bytes += read;
204 if (pulled)
205 *pulled = read;
207 return (0);
211 ps3vuart_port_init(struct ps3vuart_port *vp, int port)
213 uint64_t status, bufsize;
214 int err;
216 vp->vp_port = port;
218 PS3VUART_PORT_LOCK_INIT(vp);
220 PS3VUART_PORT_LOCK(vp);
222 /* Assert that VUART is connected */
224 err = lv1_get_virtual_uart_param(vp->vp_port,
225 PS3VUART_PORT_PARAM_INTR_STAT, &status);
226 if (err)
227 goto out;
229 if (!(status & PS3VUART_PORT_INTR_CONNECT)) {
230 err = ENODEV;
231 goto out;
234 /* Disable all interrupts */
236 vp->vp_intr_mask = 0;
238 err = lv1_set_virtual_uart_param(vp->vp_port,
239 PS3VUART_PORT_PARAM_INTR_MASK, vp->vp_intr_mask);
240 if (err)
241 goto out;
243 /* Init Tx/Rx queues */
245 TAILQ_INIT(&vp->vp_rx_bufq);
246 vp->vp_rx_bufq_bytes = 0;
248 TAILQ_INIT(&vp->vp_tx_bufq);
249 vp->vp_tx_bufq_bytes = 0;
251 /* Reset statistic counters */
253 memset(&vp->vp_stat, 0, sizeof(struct ps3vuart_port_stat));
255 /* Discard stale Rx bytes */
257 err = p3vuart_port_discard_rx_bytes(vp, 0, NULL);
258 if (err)
259 goto out;
261 /* Set Tx trigger level */
264 * An Tx interrupt is generated whenever the amount of
265 * occupied space in VUART Tx buffer becomes less than or
266 * equal the Tx trigger level.
269 err = lv1_set_virtual_uart_param(vp->vp_port,
270 PS3VUART_PORT_PARAM_TX_TRIG, 1);
271 if (err)
272 goto out;
274 /* Set Rx trigger level */
277 * An Rx interrupt is generated whenever the amount of
278 * free space in VUART Rx buffer becomes less than or
279 * equal the Rx trigger level.
282 err = lv1_get_virtual_uart_param(vp->vp_port,
283 PS3VUART_PORT_PARAM_RX_BUFSIZE, &bufsize);
284 if (err)
285 goto out;
287 err = lv1_set_virtual_uart_param(vp->vp_port,
288 PS3VUART_PORT_PARAM_RX_TRIG, bufsize - 1);
289 if (err)
290 goto out;
292 /* Enable Rx and disconnect interrupts */
294 err = ps3vuart_port_set_intr_mask(vp,
295 PS3VUART_PORT_INTR_RX | PS3VUART_PORT_INTR_DISCONNECT);
297 out:
299 PS3VUART_PORT_UNLOCK(vp);
301 if (err)
302 PS3VUART_PORT_LOCK_DESTROY(vp);
304 return (err);
307 void
308 ps3vuart_port_fini(struct ps3vuart_port *vp)
310 struct ps3vuart_port_buf *vb;
312 PS3VUART_PORT_LOCK(vp);
314 /* Disable interrupts */
316 ps3vuart_port_set_intr_mask(vp, 0);
318 /* Free Rx queue */
320 while ((vb = TAILQ_FIRST(&vp->vp_rx_bufq))) {
321 TAILQ_REMOVE(&vp->vp_rx_bufq, vb, vb_queue);
322 free(vb, M_PS3VUART_PORT);
325 /* Free Tx queue */
327 while ((vb = TAILQ_FIRST(&vp->vp_tx_bufq))) {
328 TAILQ_REMOVE(&vp->vp_tx_bufq, vb, vb_queue);
329 free(vb, M_PS3VUART_PORT);
332 PS3VUART_PORT_UNLOCK(vp);
334 PS3VUART_PORT_LOCK_DESTROY(vp);
338 ps3vuart_port_write(struct ps3vuart_port *vp, const char *buf, int size,
339 int *written)
341 struct ps3vuart_port_buf *vb;
342 uint64_t bytes;
343 int err = 0;
345 PS3VUART_PORT_LOCK(vp);
347 *written = 0;
349 /* Try to send as many bytes as possible to VUART */
351 if (TAILQ_EMPTY(&vp->vp_tx_bufq)) {
352 err = lv1_write_virtual_uart(vp->vp_port,
353 vtophys(buf), size, &bytes);
354 if (err) {
355 err = ENXIO;
356 goto out;
359 buf += bytes;
360 size -= bytes;
361 *written += bytes;
362 vp->vp_stat.vs_tx_bytes += bytes;
365 if (!size)
366 goto out;
368 /* Enqueue remaining bytes in Tx queue */
370 vb = malloc(sizeof(*vb) + size, M_PS3VUART_PORT, M_WAITOK);
371 if (!vb) {
372 err = ENOMEM;
373 goto out;
376 bcopy(buf, vb->vb_data, size);
378 vb->vb_head = vb->vb_data;
379 vb->vb_tail = vb->vb_head + size;
381 TAILQ_INSERT_TAIL(&vp->vp_tx_bufq, vb, vb_queue);
383 *written += size;
384 vp->vp_tx_bufq_bytes += size;
386 /* Enable Tx interrupts */
388 err = ps3vuart_port_set_intr_mask(vp,
389 vp->vp_intr_mask | PS3VUART_PORT_INTR_TX);
391 out:
393 PS3VUART_PORT_UNLOCK(vp);
395 return (err);
399 ps3vuart_port_read(struct ps3vuart_port *vp, char *buf, int size,
400 int *read)
402 struct ps3vuart_port_buf *vb, *tmp;
403 int bytes;
404 int err = 0;
406 PS3VUART_PORT_LOCK(vp);
408 err = ps3vuart_port_pull(vp, NULL);
409 if (err) {
410 err = ENXIO;
411 goto out;
414 *read = 0;
416 TAILQ_FOREACH_SAFE(vb, &vp->vp_rx_bufq, vb_queue, tmp) {
417 bytes = min(vb->vb_tail - vb->vb_head, size);
419 bcopy(vb->vb_head, buf, bytes);
421 buf += bytes;
422 size -= bytes;
423 *read += bytes;
424 vp->vp_rx_bufq_bytes -= bytes;
426 if (bytes < (vb->vb_tail - vb->vb_head)) {
427 vb->vb_head += bytes;
428 goto out;
431 TAILQ_REMOVE(&vp->vp_rx_bufq, vb, vb_queue);
432 free(vb, M_PS3VUART_PORT);
435 out:
437 PS3VUART_PORT_UNLOCK(vp);
439 return (err);
443 ps3vuart_port_set_param(struct ps3vuart_port *vp, int param, uint64_t val)
445 int err;
447 PS3VUART_PORT_LOCK(vp);
449 if (param == PS3VUART_PORT_PARAM_INTR_MASK)
450 err = ps3vuart_port_set_intr_mask(vp, val);
451 else
452 err = lv1_set_virtual_uart_param(vp->vp_port, param, val);
454 PS3VUART_PORT_UNLOCK(vp);
456 return (err);
460 ps3vuart_port_get_param(struct ps3vuart_port *vp, int param, uint64_t *val)
462 int err;
464 PS3VUART_PORT_LOCK(vp);
466 err = lv1_get_virtual_uart_param(vp->vp_port, param, val);
467 if (err)
468 goto out;
470 switch (param) {
471 case PS3VUART_PORT_PARAM_RX_BYTES:
472 *val += vp->vp_rx_bufq_bytes;
473 break;
474 case PS3VUART_PORT_PARAM_INTR_STAT:
475 *val &= vp->vp_intr_mask;
476 break;
479 out:
481 PS3VUART_PORT_UNLOCK(vp);
483 return (err);
486 void
487 ps3vuart_port_intr(struct ps3vuart_port *vp)
489 uint64_t status;
490 int err;
492 PS3VUART_PORT_LOCK(vp);
494 err = lv1_get_virtual_uart_param(vp->vp_port,
495 PS3VUART_PORT_PARAM_INTR_STAT, &status);
496 if (err)
497 goto out;
499 KASSERT(status & PS3VUART_PORT_INTR_CONNECT,
500 ("port %d disconnected", vp->vp_port));
501 KASSERT(!(status & PS3VUART_PORT_INTR_DISCONNECT),
502 ("port %d disconnected", vp->vp_port));
504 status &= vp->vp_intr_mask;
506 if (status & PS3VUART_PORT_INTR_RX) {
507 vp->vp_stat.vs_rx_intrs++;
508 ps3vuart_port_pull(vp, NULL);
511 if (status & PS3VUART_PORT_INTR_TX) {
512 vp->vp_stat.vs_tx_intrs++;
513 ps3vuart_port_push(vp, NULL);
516 out:
518 PS3VUART_PORT_UNLOCK(vp);
521 void
522 ps3vuart_port_get_stat(struct ps3vuart_port *vp,
523 struct ps3vuart_port_stat *stat)
525 PS3VUART_PORT_LOCK(vp);
527 *stat = vp->vp_stat;
529 PS3VUART_PORT_UNLOCK(vp);
532 static int
533 ps3vuart_port_modevent(module_t mod, int type, void *data)
535 int err = 0;
537 switch (type) {
538 case MOD_LOAD:
539 case MOD_UNLOAD:
540 break;
541 default:
542 err = EOPNOTSUPP;
545 return (err);
548 static moduledata_t ps3vuart_port_mod = {
549 "ps3vuart_port",
550 ps3vuart_port_modevent,
551 NULL
554 DECLARE_MODULE(ps3vuart_port, ps3vuart_port_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
555 MODULE_VERSION(ps3vuart_port, 1);