2 * Copyright (C) 2011, 2012 glevand <geoffrey.levand@mail.ru>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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>
38 #include <sys/kthread.h>
39 #include <sys/malloc.h>
41 #include <sys/mutex.h>
43 #include <sys/resource.h>
45 #include <sys/reboot.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"
60 #define PS3SM_LOCK_INIT(_sc) \
61 mtx_init(&(_sc)->sc_mtx, device_get_nameunit((_sc)->sc_dev), \
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)
70 TAILQ_ENTRY(ps3sm_packet
) sp_queue
;
72 struct ps3sm_header sp_hdr
;
76 TAILQ_HEAD(ps3sm_packetq
, ps3sm_packet
);
84 struct resource
*sc_irq
;
87 struct ps3vuart_port sc_port
;
91 struct ps3sm_header sc_pkthdr
;
92 struct ps3sm_packetq sc_pktq
;
95 struct proc
*sc_async_task
;
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
,
109 static int ps3sm_dev_close(struct cdev
*dev
, int flags
, int mode
,
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
,
127 static device_t ps3sm_dev
= NULL
;
130 ps3sm_probe(device_t dev
)
132 if (ps3vuart_bus_get_type(dev
) != PS3VUART_BUS_TYPE_SM
)
135 device_set_desc(dev
, "Playstation 3 SM");
137 return (BUS_PROBE_SPECIFIC
);
141 ps3sm_attach(device_t dev
)
143 struct ps3sm_softc
*sc
= device_get_softc(dev
);
155 sc
->sc_irq
= bus_alloc_resource_any(dev
, SYS_RES_IRQ
,
156 &sc
->sc_irq_id
, RF_ACTIVE
);
158 device_printf(dev
, "Could not allocate IRQ\n");
163 err
= bus_setup_intr(dev
, sc
->sc_irq
,
164 INTR_TYPE_MISC
| INTR_MPSAFE
,
165 NULL
, ps3sm_intr
, sc
, &sc
->sc_irq_ctx
);
167 device_printf(dev
, "Could not setup IRQ (%d)\n", err
);
171 /* Setup VUART port */
173 err
= ps3vuart_port_init(&sc
->sc_port
, ps3vuart_bus_get_port(dev
));
175 device_printf(dev
, "Could not setup VUART port (%d)\n", err
);
181 err
= ps3sm_init(sc
);
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,
192 device_printf(dev
, "Could not create char device\n");
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");
222 ps3vuart_port_fini(&sc
->sc_port
);
226 bus_teardown_intr(dev
, sc
->sc_irq
, sc
->sc_irq_ctx
);
230 bus_release_resource(dev
, SYS_RES_IRQ
, sc
->sc_irq_id
, sc
->sc_irq
);
236 PS3SM_LOCK_DESTROY(sc
);
242 ps3sm_detach(device_t dev
)
244 struct ps3sm_softc
*sc
= device_get_softc(dev
);
245 struct ps3sm_packet
*pkt
;
251 /* Stop async task */
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
);
265 /* Destroy char device */
267 destroy_dev(sc
->sc_cdev
);
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
);
284 PS3SM_LOCK_DESTROY(sc
);
290 ps3sm_intr(void *arg
)
292 struct ps3sm_softc
*sc
= arg
;
293 struct ps3sm_packet
*pkt
= NULL
;
298 ps3vuart_port_intr(&sc
->sc_port
);
300 if (sc
->sc_async_read
) {
301 err
= ps3sm_read_packet(sc
, &pkt
);
303 TAILQ_INSERT_TAIL(&sc
->sc_pktq
, pkt
, sp_queue
);
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
) {
321 pkt
= TAILQ_FIRST(&sc
->sc_pktq
);
323 msleep(sc
, &sc
->sc_mtx
, 0, "packet queue", 0);
324 } while (!pkt
&& sc
->sc_running
);
327 TAILQ_REMOVE(&sc
->sc_pktq
, pkt
, sp_queue
);
331 if (!sc
->sc_running
) {
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");
350 case PS3SM_EXT_EVENT_POWER_RELEASED
:
351 device_printf(sc
->sc_dev
,
352 "power button released\n");
354 case PS3SM_EXT_EVENT_THERMAL_ALERT
:
355 device_printf(sc
->sc_dev
,
358 case PS3SM_EXT_EVENT_THERMAL_CLEARED
:
359 device_printf(sc
->sc_dev
,
360 "thermal cleared\n");
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
);
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
,
400 ps3sm_read_packet(struct ps3sm_softc
*sc
, struct ps3sm_packet
**pkt
)
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
);
416 if (bytes
< sizeof(struct ps3sm_header
))
419 err
= ps3vuart_port_read(&sc
->sc_port
,
420 (char *) &sc
->sc_pkthdr
, sizeof(sc
->sc_pkthdr
), &read
);
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
);
433 if (bytes
< sc
->sc_pkthdr
.length
)
436 *pkt
= malloc(sizeof(struct ps3sm_packet
) +
437 sc
->sc_pkthdr
.length
, M_PS3SM
, M_WAITOK
);
441 err
= ps3vuart_port_read(&sc
->sc_port
,
442 (char *) (*pkt
)->sp_data
, sc
->sc_pkthdr
.length
, &read
);
449 bcopy(&sc
->sc_pkthdr
, &(*pkt
)->sp_hdr
, sizeof(sc
->sc_pkthdr
));
451 sc
->sc_pkthdr
.length
= 0;
460 ps3sm_init(struct ps3sm_softc
*sc
)
462 struct ps3sm_set_attr set_attr
;
466 PS3SM_ASSERT_LOCKED(sc
);
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
);
485 ps3sm_fini(struct ps3sm_softc
*sc
)
487 struct ps3sm_set_attr set_attr
;
491 PS3SM_ASSERT_LOCKED(sc
);
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
;
501 err
= ps3vuart_port_write(&sc
->sc_port
, (char *) &set_attr
,
502 sizeof(set_attr
), &written
);
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
;
520 sc
= device_get_softc(ps3sm_dev
);
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
;
533 err
= ps3vuart_port_write(&sc
->sc_port
, (char *) &ctl_led
,
534 sizeof(ctl_led
), &written
);
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
;
552 sc
= device_get_softc(ps3sm_dev
);
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
);
573 ps3sm_dev_open(struct cdev
*dev
, int flags
, int mode
,
576 struct ps3sm_softc
*sc
= dev
->si_drv1
;
580 /* Disable async packet reading in kernel */
582 sc
->sc_async_read
= 0;
590 ps3sm_dev_close(struct cdev
*dev
, int flags
, int mode
,
593 struct ps3sm_softc
*sc
= dev
->si_drv1
;
597 /* Enable async packet reading in kernel */
599 sc
->sc_async_read
= 1;
607 ps3sm_dev_read(struct cdev
*dev
, struct uio
*uio
, int ioflag
)
611 struct ps3sm_softc
*sc
= dev
->si_drv1
;
621 buf
= malloc(BUFSZ
, M_PS3SM
, M_WAITOK
);
627 while (uio
->uio_resid
) {
628 err
= ps3vuart_port_read(&sc
->sc_port
, buf
,
629 min(uio
->uio_resid
, BUFSZ
), &read
);
633 err
= uiomove(buf
, read
, uio
);
650 ps3sm_dev_write(struct cdev
*dev
, struct uio
*uio
, int ioflag
)
654 struct ps3sm_softc
*sc
= dev
->si_drv1
;
664 buf
= malloc(BUFSZ
, M_PS3SM
, M_WAITOK
);
670 while (uio
->uio_resid
) {
671 bytes
= min(uio
->uio_resid
, BUFSZ
);
673 err
= uiomove(buf
, bytes
, uio
);
677 err
= ps3vuart_port_write(&sc
->sc_port
, buf
, bytes
, &written
);
694 ps3sm_dev_poll(struct cdev
*dev
, int events
, struct thread
*td
)
696 struct ps3sm_softc
*sc
= dev
->si_drv1
;
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
),
716 static driver_t ps3sm_driver
= {
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);