initial import
[ps3freebsd_ps3vuart.git] / ps3sm.c
blobf13f6999bf2475675f48fcfd7e3326a8cd8c2eaf
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/conf.h>
37 #include <sys/bus.h>
38 #include <sys/kthread.h>
39 #include <sys/malloc.h>
40 #include <sys/lock.h>
41 #include <sys/mutex.h>
42 #include <sys/uio.h>
43 #include <sys/resource.h>
44 #include <sys/rman.h>
45 #include <sys/reboot.h>
47 #include <vm/vm.h>
48 #include <vm/pmap.h>
50 #include <machine/bus.h>
51 #include <machine/platform.h>
52 #include <machine/pmap.h>
53 #include <machine/resource.h>
55 #include "ps3vuart_bus.h"
56 #include "ps3vuart_port.h"
57 #include "ps3sm_msg.h"
58 #include "ps3sm.h"
60 #define PS3SM_LOCK_INIT(_sc) \
61 mtx_init(&(_sc)->sc_mtx, device_get_nameunit((_sc)->sc_dev), \
62 "ps3sm", MTX_DEF)
63 #define PS3SM_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx)
64 #define PS3SM_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
65 #define PS3SM_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
66 #define PS3SM_ASSERT_LOCKED(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
67 #define PS3SM_ASSERT_UNLOCKED(_sc) mtx_assert(&(_sc)->sc_mtx, MA_NOTOWNED)
69 struct ps3sm_packet {
70 TAILQ_ENTRY(ps3sm_packet) sp_queue;
72 struct ps3sm_header sp_hdr;
73 char sp_data[0];
76 TAILQ_HEAD(ps3sm_packetq, ps3sm_packet);
78 struct ps3sm_softc {
79 device_t sc_dev;
81 struct mtx sc_mtx;
83 int sc_irq_id;
84 struct resource *sc_irq;
85 void *sc_irq_ctx;
87 struct ps3vuart_port sc_port;
89 struct cdev *sc_cdev;
91 struct ps3sm_header sc_pkthdr;
92 struct ps3sm_packetq sc_pktq;
94 int sc_async_read;
95 struct proc *sc_async_task;
97 int sc_running;
100 static void ps3sm_intr(void *arg);
101 static void ps3sm_async_task(void *arg);
102 static int ps3sm_read_packet(struct ps3sm_softc *sc,
103 struct ps3sm_packet **pkt);
104 static int ps3sm_init(struct ps3sm_softc *sc);
105 static int ps3sm_fini(struct ps3sm_softc *sc);
107 static int ps3sm_dev_open(struct cdev *dev, int flags, int mode,
108 struct thread *td);
109 static int ps3sm_dev_close(struct cdev *dev, int flags, int mode,
110 struct thread *td);
111 static int ps3sm_dev_read(struct cdev *dev, struct uio *uio, int ioflag);
112 static int ps3sm_dev_write(struct cdev *dev, struct uio *uio, int ioflag);
113 static int ps3sm_dev_poll(struct cdev *dev, int events, struct thread *td);
115 static MALLOC_DEFINE(M_PS3SM, "ps3sm", "PS3 SM");
117 static struct cdevsw ps3sm_cdevsw = {
118 .d_version = D_VERSION,
119 .d_open = ps3sm_dev_open,
120 .d_close = ps3sm_dev_close,
121 .d_read = ps3sm_dev_read,
122 .d_write = ps3sm_dev_write,
123 .d_poll = ps3sm_dev_poll,
124 .d_name = "ps3sm",
127 static device_t ps3sm_dev = NULL;
129 static int
130 ps3sm_probe(device_t dev)
132 if (ps3vuart_bus_get_type(dev) != PS3VUART_BUS_TYPE_SM)
133 return (ENXIO);
135 device_set_desc(dev, "Playstation 3 SM");
137 return (BUS_PROBE_SPECIFIC);
140 static int
141 ps3sm_attach(device_t dev)
143 struct ps3sm_softc *sc = device_get_softc(dev);
144 int err;
146 sc->sc_dev = dev;
148 PS3SM_LOCK_INIT(sc);
150 PS3SM_LOCK(sc);
152 /* Setup IRQ */
154 sc->sc_irq_id = 0;
155 sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
156 &sc->sc_irq_id, RF_ACTIVE);
157 if (!sc->sc_irq) {
158 device_printf(dev, "Could not allocate IRQ\n");
159 err = ENXIO;
160 goto destroy_lock;
163 err = bus_setup_intr(dev, sc->sc_irq,
164 INTR_TYPE_MISC | INTR_MPSAFE,
165 NULL, ps3sm_intr, sc, &sc->sc_irq_ctx);
166 if (err) {
167 device_printf(dev, "Could not setup IRQ (%d)\n", err);
168 goto release_irq;
171 /* Setup VUART port */
173 err = ps3vuart_port_init(&sc->sc_port, ps3vuart_bus_get_port(dev));
174 if (err) {
175 device_printf(dev, "Could not setup VUART port (%d)\n", err);
176 goto teardown_irq;
179 /* Setup SM */
181 err = ps3sm_init(sc);
182 if (err) {
183 device_printf(dev, "Could not setup SM (%d)\n", err);
184 goto fini_vuart_port;
187 /* Create char device */
189 sc->sc_cdev = make_dev(&ps3sm_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
190 "%s", "ps3sm");
191 if (!sc->sc_cdev) {
192 device_printf(dev, "Could not create char device\n");
193 err = ENOMEM;
194 goto fini_sm;
197 sc->sc_cdev->si_drv1 = sc;
199 TAILQ_INIT(&sc->sc_pktq);
201 sc->sc_async_read = 1;
203 /* Create async task */
205 kproc_create(&ps3sm_async_task, sc, &sc->sc_async_task, 0, 0,
206 "PS3 SM async task");
208 sc->sc_running = 1;
210 ps3sm_dev = dev;
212 PS3SM_UNLOCK(sc);
214 return (0);
216 fini_sm:
218 ps3sm_fini(sc);
220 fini_vuart_port:
222 ps3vuart_port_fini(&sc->sc_port);
224 teardown_irq:
226 bus_teardown_intr(dev, sc->sc_irq, sc->sc_irq_ctx);
228 release_irq:
230 bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_id, sc->sc_irq);
232 destroy_lock:
234 PS3SM_UNLOCK(sc);
236 PS3SM_LOCK_DESTROY(sc);
238 return (err);
241 static int
242 ps3sm_detach(device_t dev)
244 struct ps3sm_softc *sc = device_get_softc(dev);
245 struct ps3sm_packet *pkt;
247 PS3SM_LOCK(sc);
249 ps3sm_dev = NULL;
251 /* Stop async task */
253 sc->sc_running = 0;
254 wakeup(sc);
255 while (sc->sc_running != -1)
256 msleep(sc, &sc->sc_mtx, 0, "detach", 0);
258 /* Free packet queue */
260 while ((pkt = TAILQ_FIRST(&sc->sc_pktq))) {
261 TAILQ_REMOVE(&sc->sc_pktq, pkt, sp_queue);
262 free(pkt, M_PS3SM);
265 /* Destroy char device */
267 destroy_dev(sc->sc_cdev);
269 /* Shutdown SM */
271 ps3sm_fini(sc);
273 /* Free IRQ */
275 bus_teardown_intr(dev, sc->sc_irq, sc->sc_irq_ctx);
276 bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_id, sc->sc_irq);
278 /* Destroy VUART port */
280 ps3vuart_port_fini(&sc->sc_port);
282 PS3SM_UNLOCK(sc);
284 PS3SM_LOCK_DESTROY(sc);
286 return (0);
289 static void
290 ps3sm_intr(void *arg)
292 struct ps3sm_softc *sc = arg;
293 struct ps3sm_packet *pkt = NULL;
294 int err;
296 PS3SM_LOCK(sc);
298 ps3vuart_port_intr(&sc->sc_port);
300 if (sc->sc_async_read) {
301 err = ps3sm_read_packet(sc, &pkt);
302 if (!err) {
303 TAILQ_INSERT_TAIL(&sc->sc_pktq, pkt, sp_queue);
304 wakeup(sc);
308 PS3SM_UNLOCK(sc);
311 static void
312 ps3sm_async_task(void *arg)
314 struct ps3sm_softc *sc = (struct ps3sm_softc *) arg;
315 struct ps3sm_packet *pkt;
317 while (sc->sc_running) {
318 PS3SM_LOCK(sc);
320 do {
321 pkt = TAILQ_FIRST(&sc->sc_pktq);
322 if (!pkt)
323 msleep(sc, &sc->sc_mtx, 0, "packet queue", 0);
324 } while (!pkt && sc->sc_running);
326 if (pkt)
327 TAILQ_REMOVE(&sc->sc_pktq, pkt, sp_queue);
329 PS3SM_UNLOCK(sc);
331 if (!sc->sc_running) {
332 if (pkt)
333 free(pkt, M_PS3SM);
334 break;
337 switch (pkt->sp_hdr.sid) {
338 case PS3SM_SID_EXT_EVENT:
340 struct ps3sm_ext_event *ext_event =
341 (struct ps3sm_ext_event *) &pkt->sp_hdr;
343 switch (ext_event->type) {
344 case PS3SM_EXT_EVENT_POWER_PRESSED:
345 device_printf(sc->sc_dev,
346 "power button pressed\n");
348 shutdown_nice(0);
349 break;
350 case PS3SM_EXT_EVENT_POWER_RELEASED:
351 device_printf(sc->sc_dev,
352 "power button released\n");
353 break;
354 case PS3SM_EXT_EVENT_THERMAL_ALERT:
355 device_printf(sc->sc_dev,
356 "thermal alert\n");
357 break;
358 case PS3SM_EXT_EVENT_THERMAL_CLEARED:
359 device_printf(sc->sc_dev,
360 "thermal cleared\n");
361 break;
364 break;
365 case PS3SM_SID_REQ_ERROR:
367 struct ps3sm_req_error *req_error =
368 (struct ps3sm_req_error *) &pkt->sp_hdr;
370 device_printf(sc->sc_dev,
371 "request error: payload length %d "
372 "sid 0x%04x tag 0x%08x\n",
373 PS3SM_HDR(req_error)->payload_length,
374 PS3SM_HDR(req_error)->sid,
375 PS3SM_HDR(req_error)->tag);
377 break;
378 default:
379 device_printf(sc->sc_dev,
380 "async packet: payload length %d "
381 "sid 0x%04x tag 0x%08x\n",
382 pkt->sp_hdr.payload_length,
383 pkt->sp_hdr.sid,
384 pkt->sp_hdr.tag);
385 break;
388 free(pkt, M_PS3SM);
391 PS3SM_LOCK(sc);
392 sc->sc_running = -1;
393 wakeup(sc);
394 PS3SM_UNLOCK(sc);
396 kproc_exit(1);
399 static int
400 ps3sm_read_packet(struct ps3sm_softc *sc, struct ps3sm_packet **pkt)
402 uint64_t bytes;
403 int read;
404 int err;
406 PS3SM_ASSERT_LOCKED(sc);
408 /* Read packet header */
410 if (!sc->sc_pkthdr.length) {
411 err = ps3vuart_port_get_param(&sc->sc_port,
412 PS3VUART_PORT_PARAM_RX_BYTES, &bytes);
413 if (err)
414 return (err);
416 if (bytes < sizeof(struct ps3sm_header))
417 return (EAGAIN);
419 err = ps3vuart_port_read(&sc->sc_port,
420 (char *) &sc->sc_pkthdr, sizeof(sc->sc_pkthdr), &read);
421 if (err)
422 return (err);
425 /* Read packet body */
427 if (sc->sc_pkthdr.length) {
428 err = ps3vuart_port_get_param(&sc->sc_port,
429 PS3VUART_PORT_PARAM_RX_BYTES, &bytes);
430 if (err)
431 return (err);
433 if (bytes < sc->sc_pkthdr.length)
434 return (EAGAIN);
436 *pkt = malloc(sizeof(struct ps3sm_packet) +
437 sc->sc_pkthdr.length, M_PS3SM, M_WAITOK);
438 if (!(*pkt))
439 return (ENOMEM);
441 err = ps3vuart_port_read(&sc->sc_port,
442 (char *) (*pkt)->sp_data, sc->sc_pkthdr.length, &read);
443 if (err) {
444 free(*pkt, M_PS3SM);
445 *pkt = NULL;
446 return (err);
449 bcopy(&sc->sc_pkthdr, &(*pkt)->sp_hdr, sizeof(sc->sc_pkthdr));
451 sc->sc_pkthdr.length = 0;
453 return (0);
456 return (EAGAIN);
459 static int
460 ps3sm_init(struct ps3sm_softc *sc)
462 struct ps3sm_set_attr set_attr;
463 int written;
464 int err;
466 PS3SM_ASSERT_LOCKED(sc);
468 /* Set attribute */
470 ps3sm_init_header(PS3SM_HDR(&set_attr),
471 sizeof(set_attr) - sizeof(struct ps3sm_header),
472 PS3SM_SID_SET_ATTR, 0);
473 set_attr.version = PS3SM_SET_ATTR_VERSION;
474 set_attr.attrs = PS3SM_ATTR_POWER | PS3SM_ATTR_THERMAL;
476 err = ps3vuart_port_write(&sc->sc_port, (char *) &set_attr,
477 sizeof(set_attr), &written);
478 if (err)
479 return (err);
481 return (0);
484 static int
485 ps3sm_fini(struct ps3sm_softc *sc)
487 struct ps3sm_set_attr set_attr;
488 int written;
489 int err;
491 PS3SM_ASSERT_LOCKED(sc);
493 /* Set attribute */
495 ps3sm_init_header(PS3SM_HDR(&set_attr),
496 sizeof(set_attr) - sizeof(struct ps3sm_header),
497 PS3SM_SID_SET_ATTR, 0);
498 set_attr.version = PS3SM_SET_ATTR_VERSION;
499 set_attr.attrs = 0;
501 err = ps3vuart_port_write(&sc->sc_port, (char *) &set_attr,
502 sizeof(set_attr), &written);
503 if (err)
504 return (err);
506 return (0);
510 ps3sm_ctl_led(uint8_t arg1, uint8_t arg2, uint8_t arg3, uint8_t arg4)
512 struct ps3sm_softc *sc;
513 struct ps3sm_ctl_led ctl_led;
514 int written;
515 int err;
517 if (!ps3sm_dev)
518 return (ENODEV);
520 sc = device_get_softc(ps3sm_dev);
522 PS3SM_LOCK(sc);
524 ps3sm_init_header(PS3SM_HDR(&ctl_led),
525 sizeof(ctl_led) - sizeof(struct ps3sm_header),
526 PS3SM_SID_CTL_LED, 0);
527 ctl_led.version = PS3SM_CTL_LED_VERSION;
528 ctl_led.arg1 = arg1;
529 ctl_led.arg2 = arg2;
530 ctl_led.arg3 = arg3;
531 ctl_led.arg4 = arg4;
533 err = ps3vuart_port_write(&sc->sc_port, (char *) &ctl_led,
534 sizeof(ctl_led), &written);
536 PS3SM_UNLOCK(sc);
538 return (err);
542 ps3sm_ring_buzzer(uint8_t arg1, uint8_t arg2, uint32_t arg3)
544 struct ps3sm_softc *sc;
545 struct ps3sm_ring_buzzer ring_buzzer;
546 int written;
547 int err;
549 if (!ps3sm_dev)
550 return (ENODEV);
552 sc = device_get_softc(ps3sm_dev);
554 PS3SM_LOCK(sc);
556 ps3sm_init_header(PS3SM_HDR(&ring_buzzer),
557 sizeof(ring_buzzer) - sizeof(struct ps3sm_header),
558 PS3SM_SID_RING_BUZZER, 0);
559 ring_buzzer.version = PS3SM_RING_BUZZER_VERSION;
560 ring_buzzer.arg1 = arg1;
561 ring_buzzer.arg2 = arg2;
562 ring_buzzer.arg3 = arg3;
564 err = ps3vuart_port_write(&sc->sc_port, (char *) &ring_buzzer,
565 sizeof(ring_buzzer), &written);
567 PS3SM_UNLOCK(sc);
569 return (err);
572 static int
573 ps3sm_dev_open(struct cdev *dev, int flags, int mode,
574 struct thread *td)
576 struct ps3sm_softc *sc = dev->si_drv1;
578 PS3SM_LOCK(sc);
580 /* Disable async packet reading in kernel */
582 sc->sc_async_read = 0;
584 PS3SM_UNLOCK(sc);
586 return (0);
589 static int
590 ps3sm_dev_close(struct cdev *dev, int flags, int mode,
591 struct thread *td)
593 struct ps3sm_softc *sc = dev->si_drv1;
595 PS3SM_LOCK(sc);
597 /* Enable async packet reading in kernel */
599 sc->sc_async_read = 1;
601 PS3SM_UNLOCK(sc);
603 return (0);
606 static int
607 ps3sm_dev_read(struct cdev *dev, struct uio *uio, int ioflag)
609 #define BUFSZ 4096
611 struct ps3sm_softc *sc = dev->si_drv1;
612 unsigned char *buf;
613 int read;
614 int err = 0;
616 PS3SM_LOCK(sc);
618 if (!uio->uio_resid)
619 goto out;
621 buf = malloc(BUFSZ, M_PS3SM, M_WAITOK);
622 if (!buf) {
623 err = ENOMEM;
624 goto out;
627 while (uio->uio_resid) {
628 err = ps3vuart_port_read(&sc->sc_port, buf,
629 min(uio->uio_resid, BUFSZ), &read);
630 if (err || !read)
631 break;
633 err = uiomove(buf, read, uio);
634 if (!err)
635 break;
638 free(buf, M_PS3SM);
640 out:
642 PS3SM_UNLOCK(sc);
644 return (err);
646 #undef BUFSZ
649 static int
650 ps3sm_dev_write(struct cdev *dev, struct uio *uio, int ioflag)
652 #define BUFSZ 4096
654 struct ps3sm_softc *sc = dev->si_drv1;
655 unsigned char *buf;
656 int bytes, written;
657 int err = 0;
659 PS3SM_LOCK(sc);
661 if (!uio->uio_resid)
662 goto out;
664 buf = malloc(BUFSZ, M_PS3SM, M_WAITOK);
665 if (!buf) {
666 err = ENOMEM;
667 goto out;
670 while (uio->uio_resid) {
671 bytes = min(uio->uio_resid, BUFSZ);
673 err = uiomove(buf, bytes, uio);
674 if (err)
675 break;
677 err = ps3vuart_port_write(&sc->sc_port, buf, bytes, &written);
678 if (err || !written)
679 break;
682 free(buf, M_PS3SM);
684 out:
686 PS3SM_UNLOCK(sc);
688 return (err);
690 #undef BUFSZ
693 static int
694 ps3sm_dev_poll(struct cdev *dev, int events, struct thread *td)
696 struct ps3sm_softc *sc = dev->si_drv1;
698 PS3SM_LOCK(sc);
700 /* XXX: implement */
702 PS3SM_UNLOCK(sc);
704 return (0);
707 static device_method_t ps3sm_methods[] = {
708 /* Device interface */
709 DEVMETHOD(device_probe, ps3sm_probe),
710 DEVMETHOD(device_attach, ps3sm_attach),
711 DEVMETHOD(device_detach, ps3sm_detach),
713 { 0, 0 }
716 static driver_t ps3sm_driver = {
717 "ps3sm",
718 ps3sm_methods,
719 sizeof(struct ps3sm_softc)
722 static devclass_t ps3sm_devclass;
724 DRIVER_MODULE(ps3sm, ps3vuart_bus, ps3sm_driver, ps3sm_devclass, 0, 0);
725 MODULE_DEPEND(ps3sm, ps3vuart_port, 1, 1, 1);
726 MODULE_VERSION(ps3sm, 1);