Relocate PS2 code (#14895)
* Relocate ps2 protocol code * clang * Move makefile logic
This commit is contained in:
		
							parent
							
								
									5500c428dd
								
							
						
					
					
						commit
						d4be4b67a2
					
				
					 11 changed files with 38 additions and 32 deletions
				
			
		
							
								
								
									
										139
									
								
								drivers/ps2/ps2.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								drivers/ps2/ps2.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,139 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright 2010,2011,2012,2013 Jun WAKO <wakojun@gmail.com>
 | 
			
		||||
 | 
			
		||||
This software is licensed with a Modified BSD License.
 | 
			
		||||
All of this is supposed to be Free Software, Open Source, DFSG-free,
 | 
			
		||||
GPL-compatible, and OK to use in both free and proprietary applications.
 | 
			
		||||
Additions and corrections to this file are welcome.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Redistribution and use in source and binary forms, with or without
 | 
			
		||||
modification, are permitted provided that the following conditions are met:
 | 
			
		||||
 | 
			
		||||
* Redistributions of source code must retain the above copyright
 | 
			
		||||
  notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 | 
			
		||||
* Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
  notice, this list of conditions and the following disclaimer in
 | 
			
		||||
  the documentation and/or other materials provided with the
 | 
			
		||||
  distribution.
 | 
			
		||||
 | 
			
		||||
* Neither the name of the copyright holders nor the names of
 | 
			
		||||
  contributors may be used to endorse or promote products derived
 | 
			
		||||
  from this software without specific prior written permission.
 | 
			
		||||
 | 
			
		||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | 
			
		||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
			
		||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
			
		||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 | 
			
		||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | 
			
		||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | 
			
		||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
			
		||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | 
			
		||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | 
			
		||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 | 
			
		||||
POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include "wait.h"
 | 
			
		||||
#include "ps2_io.h"
 | 
			
		||||
#include "print.h"
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Primitive PS/2 Library for AVR
 | 
			
		||||
 *
 | 
			
		||||
 * PS/2 Resources
 | 
			
		||||
 * --------------
 | 
			
		||||
 * [1] The PS/2 Mouse/Keyboard Protocol
 | 
			
		||||
 * http://www.computer-engineering.org/ps2protocol/
 | 
			
		||||
 * Concise and thorough primer of PS/2 protocol.
 | 
			
		||||
 *
 | 
			
		||||
 * [2] Keyboard and Auxiliary Device Controller
 | 
			
		||||
 * http://www.mcamafia.de/pdf/ibm_hitrc07.pdf
 | 
			
		||||
 * Signal Timing and Format
 | 
			
		||||
 *
 | 
			
		||||
 * [3] Keyboards(101- and 102-key)
 | 
			
		||||
 * http://www.mcamafia.de/pdf/ibm_hitrc11.pdf
 | 
			
		||||
 * Keyboard Layout, Scan Code Set, POR, and Commands.
 | 
			
		||||
 *
 | 
			
		||||
 * [4] PS/2 Reference Manuals
 | 
			
		||||
 * http://www.mcamafia.de/pdf/ibm_hitrc07.pdf
 | 
			
		||||
 * Collection of IBM Personal System/2 documents.
 | 
			
		||||
 *
 | 
			
		||||
 * [5] TrackPoint Engineering Specifications for version 3E
 | 
			
		||||
 * https://web.archive.org/web/20100526161812/http://wwwcssrv.almaden.ibm.com/trackpoint/download.html
 | 
			
		||||
 */
 | 
			
		||||
#define PS2_ACK 0xFA
 | 
			
		||||
#define PS2_RESEND 0xFE
 | 
			
		||||
#define PS2_SET_LED 0xED
 | 
			
		||||
 | 
			
		||||
// TODO: error numbers
 | 
			
		||||
#define PS2_ERR_NONE 0
 | 
			
		||||
#define PS2_ERR_STARTBIT1 1
 | 
			
		||||
#define PS2_ERR_STARTBIT2 2
 | 
			
		||||
#define PS2_ERR_STARTBIT3 3
 | 
			
		||||
#define PS2_ERR_PARITY 0x10
 | 
			
		||||
#define PS2_ERR_NODATA 0x20
 | 
			
		||||
 | 
			
		||||
#define PS2_LED_SCROLL_LOCK 0
 | 
			
		||||
#define PS2_LED_NUM_LOCK 1
 | 
			
		||||
#define PS2_LED_CAPS_LOCK 2
 | 
			
		||||
 | 
			
		||||
extern uint8_t ps2_error;
 | 
			
		||||
 | 
			
		||||
void    ps2_host_init(void);
 | 
			
		||||
uint8_t ps2_host_send(uint8_t data);
 | 
			
		||||
uint8_t ps2_host_recv_response(void);
 | 
			
		||||
uint8_t ps2_host_recv(void);
 | 
			
		||||
void    ps2_host_set_led(uint8_t usb_led);
 | 
			
		||||
 | 
			
		||||
/*--------------------------------------------------------------------
 | 
			
		||||
 * static functions
 | 
			
		||||
 *------------------------------------------------------------------*/
 | 
			
		||||
static inline uint16_t wait_clock_lo(uint16_t us) {
 | 
			
		||||
    while (clock_in() && us) {
 | 
			
		||||
        asm("");
 | 
			
		||||
        wait_us(1);
 | 
			
		||||
        us--;
 | 
			
		||||
    }
 | 
			
		||||
    return us;
 | 
			
		||||
}
 | 
			
		||||
static inline uint16_t wait_clock_hi(uint16_t us) {
 | 
			
		||||
    while (!clock_in() && us) {
 | 
			
		||||
        asm("");
 | 
			
		||||
        wait_us(1);
 | 
			
		||||
        us--;
 | 
			
		||||
    }
 | 
			
		||||
    return us;
 | 
			
		||||
}
 | 
			
		||||
static inline uint16_t wait_data_lo(uint16_t us) {
 | 
			
		||||
    while (data_in() && us) {
 | 
			
		||||
        asm("");
 | 
			
		||||
        wait_us(1);
 | 
			
		||||
        us--;
 | 
			
		||||
    }
 | 
			
		||||
    return us;
 | 
			
		||||
}
 | 
			
		||||
static inline uint16_t wait_data_hi(uint16_t us) {
 | 
			
		||||
    while (!data_in() && us) {
 | 
			
		||||
        asm("");
 | 
			
		||||
        wait_us(1);
 | 
			
		||||
        us--;
 | 
			
		||||
    }
 | 
			
		||||
    return us;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* idle state that device can send */
 | 
			
		||||
static inline void idle(void) {
 | 
			
		||||
    clock_hi();
 | 
			
		||||
    data_hi();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* inhibit device to send */
 | 
			
		||||
static inline void inhibit(void) {
 | 
			
		||||
    clock_lo();
 | 
			
		||||
    data_hi();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										187
									
								
								drivers/ps2/ps2_busywait.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								drivers/ps2/ps2_busywait.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,187 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright 2010,2011,2012,2013 Jun WAKO <wakojun@gmail.com>
 | 
			
		||||
 | 
			
		||||
This software is licensed with a Modified BSD License.
 | 
			
		||||
All of this is supposed to be Free Software, Open Source, DFSG-free,
 | 
			
		||||
GPL-compatible, and OK to use in both free and proprietary applications.
 | 
			
		||||
Additions and corrections to this file are welcome.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Redistribution and use in source and binary forms, with or without
 | 
			
		||||
modification, are permitted provided that the following conditions are met:
 | 
			
		||||
 | 
			
		||||
* Redistributions of source code must retain the above copyright
 | 
			
		||||
  notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 | 
			
		||||
* Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
  notice, this list of conditions and the following disclaimer in
 | 
			
		||||
  the documentation and/or other materials provided with the
 | 
			
		||||
  distribution.
 | 
			
		||||
 | 
			
		||||
* Neither the name of the copyright holders nor the names of
 | 
			
		||||
  contributors may be used to endorse or promote products derived
 | 
			
		||||
  from this software without specific prior written permission.
 | 
			
		||||
 | 
			
		||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | 
			
		||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
			
		||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
			
		||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 | 
			
		||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | 
			
		||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | 
			
		||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
			
		||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | 
			
		||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | 
			
		||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 | 
			
		||||
POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * PS/2 protocol busywait version
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include "wait.h"
 | 
			
		||||
#include "ps2.h"
 | 
			
		||||
#include "ps2_io.h"
 | 
			
		||||
#include "debug.h"
 | 
			
		||||
 | 
			
		||||
#define WAIT(stat, us, err)     \
 | 
			
		||||
    do {                        \
 | 
			
		||||
        if (!wait_##stat(us)) { \
 | 
			
		||||
            ps2_error = err;    \
 | 
			
		||||
            goto ERROR;         \
 | 
			
		||||
        }                       \
 | 
			
		||||
    } while (0)
 | 
			
		||||
 | 
			
		||||
uint8_t ps2_error = PS2_ERR_NONE;
 | 
			
		||||
 | 
			
		||||
void ps2_host_init(void) {
 | 
			
		||||
    clock_init();
 | 
			
		||||
    data_init();
 | 
			
		||||
 | 
			
		||||
    // POR(150-2000ms) plus BAT(300-500ms) may take 2.5sec([3]p.20)
 | 
			
		||||
    wait_ms(2500);
 | 
			
		||||
 | 
			
		||||
    inhibit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t ps2_host_send(uint8_t data) {
 | 
			
		||||
    bool parity = true;
 | 
			
		||||
    ps2_error   = PS2_ERR_NONE;
 | 
			
		||||
 | 
			
		||||
    /* terminate a transmission if we have */
 | 
			
		||||
    inhibit();
 | 
			
		||||
    wait_us(100);  // 100us [4]p.13, [5]p.50
 | 
			
		||||
 | 
			
		||||
    /* 'Request to Send' and Start bit */
 | 
			
		||||
    data_lo();
 | 
			
		||||
    clock_hi();
 | 
			
		||||
    WAIT(clock_lo, 10000, 10);  // 10ms [5]p.50
 | 
			
		||||
 | 
			
		||||
    /* Data bit */
 | 
			
		||||
    for (uint8_t i = 0; i < 8; i++) {
 | 
			
		||||
        wait_us(15);
 | 
			
		||||
        if (data & (1 << i)) {
 | 
			
		||||
            parity = !parity;
 | 
			
		||||
            data_hi();
 | 
			
		||||
        } else {
 | 
			
		||||
            data_lo();
 | 
			
		||||
        }
 | 
			
		||||
        WAIT(clock_hi, 50, 2);
 | 
			
		||||
        WAIT(clock_lo, 50, 3);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Parity bit */
 | 
			
		||||
    wait_us(15);
 | 
			
		||||
    if (parity) {
 | 
			
		||||
        data_hi();
 | 
			
		||||
    } else {
 | 
			
		||||
        data_lo();
 | 
			
		||||
    }
 | 
			
		||||
    WAIT(clock_hi, 50, 4);
 | 
			
		||||
    WAIT(clock_lo, 50, 5);
 | 
			
		||||
 | 
			
		||||
    /* Stop bit */
 | 
			
		||||
    wait_us(15);
 | 
			
		||||
    data_hi();
 | 
			
		||||
 | 
			
		||||
    /* Ack */
 | 
			
		||||
    WAIT(data_lo, 50, 6);
 | 
			
		||||
    WAIT(clock_lo, 50, 7);
 | 
			
		||||
 | 
			
		||||
    /* wait for idle state */
 | 
			
		||||
    WAIT(clock_hi, 50, 8);
 | 
			
		||||
    WAIT(data_hi, 50, 9);
 | 
			
		||||
 | 
			
		||||
    inhibit();
 | 
			
		||||
    return ps2_host_recv_response();
 | 
			
		||||
ERROR:
 | 
			
		||||
    inhibit();
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* receive data when host want else inhibit communication */
 | 
			
		||||
uint8_t ps2_host_recv_response(void) {
 | 
			
		||||
    // Command may take 25ms/20ms at most([5]p.46, [3]p.21)
 | 
			
		||||
    // 250 * 100us(wait for start bit in ps2_host_recv)
 | 
			
		||||
    uint8_t data = 0;
 | 
			
		||||
    uint8_t try
 | 
			
		||||
        = 250;
 | 
			
		||||
    do {
 | 
			
		||||
        data = ps2_host_recv();
 | 
			
		||||
    } while (try --&&ps2_error);
 | 
			
		||||
    return data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* called after start bit comes */
 | 
			
		||||
uint8_t ps2_host_recv(void) {
 | 
			
		||||
    uint8_t data   = 0;
 | 
			
		||||
    bool    parity = true;
 | 
			
		||||
    ps2_error      = PS2_ERR_NONE;
 | 
			
		||||
 | 
			
		||||
    /* release lines(idle state) */
 | 
			
		||||
    idle();
 | 
			
		||||
 | 
			
		||||
    /* start bit [1] */
 | 
			
		||||
    WAIT(clock_lo, 100, 1);  // TODO: this is enough?
 | 
			
		||||
    WAIT(data_lo, 1, 2);
 | 
			
		||||
    WAIT(clock_hi, 50, 3);
 | 
			
		||||
 | 
			
		||||
    /* data [2-9] */
 | 
			
		||||
    for (uint8_t i = 0; i < 8; i++) {
 | 
			
		||||
        WAIT(clock_lo, 50, 4);
 | 
			
		||||
        if (data_in()) {
 | 
			
		||||
            parity = !parity;
 | 
			
		||||
            data |= (1 << i);
 | 
			
		||||
        }
 | 
			
		||||
        WAIT(clock_hi, 50, 5);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* parity [10] */
 | 
			
		||||
    WAIT(clock_lo, 50, 6);
 | 
			
		||||
    if (data_in() != parity) {
 | 
			
		||||
        ps2_error = PS2_ERR_PARITY;
 | 
			
		||||
        goto ERROR;
 | 
			
		||||
    }
 | 
			
		||||
    WAIT(clock_hi, 50, 7);
 | 
			
		||||
 | 
			
		||||
    /* stop bit [11] */
 | 
			
		||||
    WAIT(clock_lo, 50, 8);
 | 
			
		||||
    WAIT(data_hi, 1, 9);
 | 
			
		||||
    WAIT(clock_hi, 50, 10);
 | 
			
		||||
 | 
			
		||||
    inhibit();
 | 
			
		||||
    return data;
 | 
			
		||||
ERROR:
 | 
			
		||||
    if (ps2_error > PS2_ERR_STARTBIT3) {
 | 
			
		||||
        xprintf("x%02X\n", ps2_error);
 | 
			
		||||
    }
 | 
			
		||||
    inhibit();
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* send LED state to keyboard */
 | 
			
		||||
void ps2_host_set_led(uint8_t led) {
 | 
			
		||||
    ps2_host_send(0xED);
 | 
			
		||||
    ps2_host_send(led);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										340
									
								
								drivers/ps2/ps2_interrupt.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										340
									
								
								drivers/ps2/ps2_interrupt.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,340 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright 2010,2011,2012,2013 Jun WAKO <wakojun@gmail.com>
 | 
			
		||||
 | 
			
		||||
This software is licensed with a Modified BSD License.
 | 
			
		||||
All of this is supposed to be Free Software, Open Source, DFSG-free,
 | 
			
		||||
GPL-compatible, and OK to use in both free and proprietary applications.
 | 
			
		||||
Additions and corrections to this file are welcome.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Redistribution and use in source and binary forms, with or without
 | 
			
		||||
modification, are permitted provided that the following conditions are met:
 | 
			
		||||
 | 
			
		||||
* Redistributions of source code must retain the above copyright
 | 
			
		||||
  notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 | 
			
		||||
* Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
  notice, this list of conditions and the following disclaimer in
 | 
			
		||||
  the documentation and/or other materials provided with the
 | 
			
		||||
  distribution.
 | 
			
		||||
 | 
			
		||||
* Neither the name of the copyright holders nor the names of
 | 
			
		||||
  contributors may be used to endorse or promote products derived
 | 
			
		||||
  from this software without specific prior written permission.
 | 
			
		||||
 | 
			
		||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | 
			
		||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
			
		||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
			
		||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 | 
			
		||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | 
			
		||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | 
			
		||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
			
		||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | 
			
		||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | 
			
		||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 | 
			
		||||
POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * PS/2 protocol Pin interrupt version
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
#if defined(__AVR__)
 | 
			
		||||
#    include <avr/interrupt.h>
 | 
			
		||||
#elif defined(PROTOCOL_CHIBIOS)  // TODO: or STM32 ?
 | 
			
		||||
// chibiOS headers
 | 
			
		||||
#    include "ch.h"
 | 
			
		||||
#    include "hal.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "ps2.h"
 | 
			
		||||
#include "ps2_io.h"
 | 
			
		||||
#include "print.h"
 | 
			
		||||
#include "wait.h"
 | 
			
		||||
 | 
			
		||||
#define WAIT(stat, us, err)     \
 | 
			
		||||
    do {                        \
 | 
			
		||||
        if (!wait_##stat(us)) { \
 | 
			
		||||
            ps2_error = err;    \
 | 
			
		||||
            goto ERROR;         \
 | 
			
		||||
        }                       \
 | 
			
		||||
    } while (0)
 | 
			
		||||
 | 
			
		||||
uint8_t ps2_error = PS2_ERR_NONE;
 | 
			
		||||
 | 
			
		||||
static inline uint8_t pbuf_dequeue(void);
 | 
			
		||||
static inline void    pbuf_enqueue(uint8_t data);
 | 
			
		||||
static inline bool    pbuf_has_data(void);
 | 
			
		||||
static inline void    pbuf_clear(void);
 | 
			
		||||
 | 
			
		||||
#if defined(PROTOCOL_CHIBIOS)
 | 
			
		||||
void ps2_interrupt_service_routine(void);
 | 
			
		||||
void palCallback(void *arg) { ps2_interrupt_service_routine(); }
 | 
			
		||||
 | 
			
		||||
#    define PS2_INT_INIT()                                 \
 | 
			
		||||
        { palSetLineMode(PS2_CLOCK_PIN, PAL_MODE_INPUT); } \
 | 
			
		||||
        while (0)
 | 
			
		||||
#    define PS2_INT_ON()                                                    \
 | 
			
		||||
        {                                                                   \
 | 
			
		||||
            palEnableLineEvent(PS2_CLOCK_PIN, PAL_EVENT_MODE_FALLING_EDGE); \
 | 
			
		||||
            palSetLineCallback(PS2_CLOCK_PIN, palCallback, NULL);           \
 | 
			
		||||
        }                                                                   \
 | 
			
		||||
        while (0)
 | 
			
		||||
#    define PS2_INT_OFF()                       \
 | 
			
		||||
        { palDisableLineEvent(PS2_CLOCK_PIN); } \
 | 
			
		||||
        while (0)
 | 
			
		||||
#endif  // PROTOCOL_CHIBIOS
 | 
			
		||||
 | 
			
		||||
void ps2_host_init(void) {
 | 
			
		||||
    idle();
 | 
			
		||||
    PS2_INT_INIT();
 | 
			
		||||
    PS2_INT_ON();
 | 
			
		||||
    // POR(150-2000ms) plus BAT(300-500ms) may take 2.5sec([3]p.20)
 | 
			
		||||
    // wait_ms(2500);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t ps2_host_send(uint8_t data) {
 | 
			
		||||
    bool parity = true;
 | 
			
		||||
    ps2_error   = PS2_ERR_NONE;
 | 
			
		||||
 | 
			
		||||
    PS2_INT_OFF();
 | 
			
		||||
 | 
			
		||||
    /* terminate a transmission if we have */
 | 
			
		||||
    inhibit();
 | 
			
		||||
    wait_us(100);  // 100us [4]p.13, [5]p.50
 | 
			
		||||
 | 
			
		||||
    /* 'Request to Send' and Start bit */
 | 
			
		||||
    data_lo();
 | 
			
		||||
    clock_hi();
 | 
			
		||||
    WAIT(clock_lo, 10000, 10);  // 10ms [5]p.50
 | 
			
		||||
 | 
			
		||||
    /* Data bit[2-9] */
 | 
			
		||||
    for (uint8_t i = 0; i < 8; i++) {
 | 
			
		||||
        if (data & (1 << i)) {
 | 
			
		||||
            parity = !parity;
 | 
			
		||||
            data_hi();
 | 
			
		||||
        } else {
 | 
			
		||||
            data_lo();
 | 
			
		||||
        }
 | 
			
		||||
        WAIT(clock_hi, 50, 2);
 | 
			
		||||
        WAIT(clock_lo, 50, 3);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Parity bit */
 | 
			
		||||
    wait_us(15);
 | 
			
		||||
    if (parity) {
 | 
			
		||||
        data_hi();
 | 
			
		||||
    } else {
 | 
			
		||||
        data_lo();
 | 
			
		||||
    }
 | 
			
		||||
    WAIT(clock_hi, 50, 4);
 | 
			
		||||
    WAIT(clock_lo, 50, 5);
 | 
			
		||||
 | 
			
		||||
    /* Stop bit */
 | 
			
		||||
    wait_us(15);
 | 
			
		||||
    data_hi();
 | 
			
		||||
 | 
			
		||||
    /* Ack */
 | 
			
		||||
    WAIT(data_lo, 50, 6);
 | 
			
		||||
    WAIT(clock_lo, 50, 7);
 | 
			
		||||
 | 
			
		||||
    /* wait for idle state */
 | 
			
		||||
    WAIT(clock_hi, 50, 8);
 | 
			
		||||
    WAIT(data_hi, 50, 9);
 | 
			
		||||
 | 
			
		||||
    idle();
 | 
			
		||||
    PS2_INT_ON();
 | 
			
		||||
    return ps2_host_recv_response();
 | 
			
		||||
ERROR:
 | 
			
		||||
    idle();
 | 
			
		||||
    PS2_INT_ON();
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t ps2_host_recv_response(void) {
 | 
			
		||||
    // Command may take 25ms/20ms at most([5]p.46, [3]p.21)
 | 
			
		||||
    uint8_t retry = 25;
 | 
			
		||||
    while (retry-- && !pbuf_has_data()) {
 | 
			
		||||
        wait_ms(1);
 | 
			
		||||
    }
 | 
			
		||||
    return pbuf_dequeue();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* get data received by interrupt */
 | 
			
		||||
uint8_t ps2_host_recv(void) {
 | 
			
		||||
    if (pbuf_has_data()) {
 | 
			
		||||
        ps2_error = PS2_ERR_NONE;
 | 
			
		||||
        return pbuf_dequeue();
 | 
			
		||||
    } else {
 | 
			
		||||
        ps2_error = PS2_ERR_NODATA;
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ps2_interrupt_service_routine(void) {
 | 
			
		||||
    static enum {
 | 
			
		||||
        INIT,
 | 
			
		||||
        START,
 | 
			
		||||
        BIT0,
 | 
			
		||||
        BIT1,
 | 
			
		||||
        BIT2,
 | 
			
		||||
        BIT3,
 | 
			
		||||
        BIT4,
 | 
			
		||||
        BIT5,
 | 
			
		||||
        BIT6,
 | 
			
		||||
        BIT7,
 | 
			
		||||
        PARITY,
 | 
			
		||||
        STOP,
 | 
			
		||||
    } state               = INIT;
 | 
			
		||||
    static uint8_t data   = 0;
 | 
			
		||||
    static uint8_t parity = 1;
 | 
			
		||||
 | 
			
		||||
    // TODO: abort if elapse 100us from previous interrupt
 | 
			
		||||
 | 
			
		||||
    // return unless falling edge
 | 
			
		||||
    if (clock_in()) {
 | 
			
		||||
        goto RETURN;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    state++;
 | 
			
		||||
    switch (state) {
 | 
			
		||||
        case START:
 | 
			
		||||
            if (data_in()) goto ERROR;
 | 
			
		||||
            break;
 | 
			
		||||
        case BIT0:
 | 
			
		||||
        case BIT1:
 | 
			
		||||
        case BIT2:
 | 
			
		||||
        case BIT3:
 | 
			
		||||
        case BIT4:
 | 
			
		||||
        case BIT5:
 | 
			
		||||
        case BIT6:
 | 
			
		||||
        case BIT7:
 | 
			
		||||
            data >>= 1;
 | 
			
		||||
            if (data_in()) {
 | 
			
		||||
                data |= 0x80;
 | 
			
		||||
                parity++;
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        case PARITY:
 | 
			
		||||
            if (data_in()) {
 | 
			
		||||
                if (!(parity & 0x01)) goto ERROR;
 | 
			
		||||
            } else {
 | 
			
		||||
                if (parity & 0x01) goto ERROR;
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        case STOP:
 | 
			
		||||
            if (!data_in()) goto ERROR;
 | 
			
		||||
            pbuf_enqueue(data);
 | 
			
		||||
            goto DONE;
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            goto ERROR;
 | 
			
		||||
    }
 | 
			
		||||
    goto RETURN;
 | 
			
		||||
ERROR:
 | 
			
		||||
    ps2_error = state;
 | 
			
		||||
DONE:
 | 
			
		||||
    state  = INIT;
 | 
			
		||||
    data   = 0;
 | 
			
		||||
    parity = 1;
 | 
			
		||||
RETURN:
 | 
			
		||||
    return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if defined(__AVR__)
 | 
			
		||||
ISR(PS2_INT_VECT) { ps2_interrupt_service_routine(); }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* send LED state to keyboard */
 | 
			
		||||
void ps2_host_set_led(uint8_t led) {
 | 
			
		||||
    ps2_host_send(0xED);
 | 
			
		||||
    ps2_host_send(led);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*--------------------------------------------------------------------
 | 
			
		||||
 * Ring buffer to store scan codes from keyboard
 | 
			
		||||
 *------------------------------------------------------------------*/
 | 
			
		||||
#define PBUF_SIZE 32
 | 
			
		||||
static uint8_t     pbuf[PBUF_SIZE];
 | 
			
		||||
static uint8_t     pbuf_head = 0;
 | 
			
		||||
static uint8_t     pbuf_tail = 0;
 | 
			
		||||
static inline void pbuf_enqueue(uint8_t data) {
 | 
			
		||||
#if defined(__AVR__)
 | 
			
		||||
    uint8_t sreg = SREG;
 | 
			
		||||
    cli();
 | 
			
		||||
#elif defined(PROTOCOL_CHIBIOS)
 | 
			
		||||
    chSysLockFromISR();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    uint8_t next = (pbuf_head + 1) % PBUF_SIZE;
 | 
			
		||||
    if (next != pbuf_tail) {
 | 
			
		||||
        pbuf[pbuf_head] = data;
 | 
			
		||||
        pbuf_head       = next;
 | 
			
		||||
    } else {
 | 
			
		||||
        print("pbuf: full\n");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#if defined(__AVR__)
 | 
			
		||||
    SREG = sreg;
 | 
			
		||||
#elif defined(PROTOCOL_CHIBIOS)
 | 
			
		||||
    chSysUnlockFromISR();
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
static inline uint8_t pbuf_dequeue(void) {
 | 
			
		||||
    uint8_t val = 0;
 | 
			
		||||
 | 
			
		||||
#if defined(__AVR__)
 | 
			
		||||
    uint8_t sreg = SREG;
 | 
			
		||||
    cli();
 | 
			
		||||
#elif defined(PROTOCOL_CHIBIOS)
 | 
			
		||||
    chSysLock();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    if (pbuf_head != pbuf_tail) {
 | 
			
		||||
        val       = pbuf[pbuf_tail];
 | 
			
		||||
        pbuf_tail = (pbuf_tail + 1) % PBUF_SIZE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#if defined(__AVR__)
 | 
			
		||||
    SREG = sreg;
 | 
			
		||||
#elif defined(PROTOCOL_CHIBIOS)
 | 
			
		||||
    chSysUnlock();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    return val;
 | 
			
		||||
}
 | 
			
		||||
static inline bool pbuf_has_data(void) {
 | 
			
		||||
#if defined(__AVR__)
 | 
			
		||||
    uint8_t sreg = SREG;
 | 
			
		||||
    cli();
 | 
			
		||||
#elif defined(PROTOCOL_CHIBIOS)
 | 
			
		||||
    chSysLock();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    bool has_data = (pbuf_head != pbuf_tail);
 | 
			
		||||
 | 
			
		||||
#if defined(__AVR__)
 | 
			
		||||
    SREG = sreg;
 | 
			
		||||
#elif defined(PROTOCOL_CHIBIOS)
 | 
			
		||||
    chSysUnlock();
 | 
			
		||||
#endif
 | 
			
		||||
    return has_data;
 | 
			
		||||
}
 | 
			
		||||
static inline void pbuf_clear(void) {
 | 
			
		||||
#if defined(__AVR__)
 | 
			
		||||
    uint8_t sreg = SREG;
 | 
			
		||||
    cli();
 | 
			
		||||
#elif defined(PROTOCOL_CHIBIOS)
 | 
			
		||||
    chSysLock();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    pbuf_head = pbuf_tail = 0;
 | 
			
		||||
 | 
			
		||||
#if defined(__AVR__)
 | 
			
		||||
    SREG = sreg;
 | 
			
		||||
#elif defined(PROTOCOL_CHIBIOS)
 | 
			
		||||
    chSysUnlock();
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								drivers/ps2/ps2_io.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								drivers/ps2/ps2_io.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
void clock_init(void);
 | 
			
		||||
void clock_lo(void);
 | 
			
		||||
void clock_hi(void);
 | 
			
		||||
bool clock_in(void);
 | 
			
		||||
 | 
			
		||||
void data_init(void);
 | 
			
		||||
void data_lo(void);
 | 
			
		||||
void data_hi(void);
 | 
			
		||||
bool data_in(void);
 | 
			
		||||
							
								
								
									
										270
									
								
								drivers/ps2/ps2_mouse.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										270
									
								
								drivers/ps2/ps2_mouse.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,270 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright 2011,2013 Jun Wako <wakojun@gmail.com>
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, either version 2 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include "ps2_mouse.h"
 | 
			
		||||
#include "wait.h"
 | 
			
		||||
#include "gpio.h"
 | 
			
		||||
#include "host.h"
 | 
			
		||||
#include "timer.h"
 | 
			
		||||
#include "print.h"
 | 
			
		||||
#include "report.h"
 | 
			
		||||
#include "debug.h"
 | 
			
		||||
#include "ps2.h"
 | 
			
		||||
 | 
			
		||||
/* ============================= MACROS ============================ */
 | 
			
		||||
 | 
			
		||||
static report_mouse_t mouse_report = {};
 | 
			
		||||
 | 
			
		||||
static inline void ps2_mouse_print_report(report_mouse_t *mouse_report);
 | 
			
		||||
static inline void ps2_mouse_convert_report_to_hid(report_mouse_t *mouse_report);
 | 
			
		||||
static inline void ps2_mouse_clear_report(report_mouse_t *mouse_report);
 | 
			
		||||
static inline void ps2_mouse_enable_scrolling(void);
 | 
			
		||||
static inline void ps2_mouse_scroll_button_task(report_mouse_t *mouse_report);
 | 
			
		||||
 | 
			
		||||
/* ============================= IMPLEMENTATION ============================ */
 | 
			
		||||
 | 
			
		||||
/* supports only 3 button mouse at this time */
 | 
			
		||||
void ps2_mouse_init(void) {
 | 
			
		||||
    ps2_host_init();
 | 
			
		||||
 | 
			
		||||
    wait_ms(PS2_MOUSE_INIT_DELAY);  // wait for powering up
 | 
			
		||||
 | 
			
		||||
    PS2_MOUSE_SEND(PS2_MOUSE_RESET, "ps2_mouse_init: sending reset");
 | 
			
		||||
 | 
			
		||||
    PS2_MOUSE_RECEIVE("ps2_mouse_init: read BAT");
 | 
			
		||||
    PS2_MOUSE_RECEIVE("ps2_mouse_init: read DevID");
 | 
			
		||||
 | 
			
		||||
#ifdef PS2_MOUSE_USE_REMOTE_MODE
 | 
			
		||||
    ps2_mouse_set_remote_mode();
 | 
			
		||||
#else
 | 
			
		||||
    ps2_mouse_enable_data_reporting();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef PS2_MOUSE_ENABLE_SCROLLING
 | 
			
		||||
    ps2_mouse_enable_scrolling();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef PS2_MOUSE_USE_2_1_SCALING
 | 
			
		||||
    ps2_mouse_set_scaling_2_1();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    ps2_mouse_init_user();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__attribute__((weak)) void ps2_mouse_init_user(void) {}
 | 
			
		||||
 | 
			
		||||
__attribute__((weak)) void ps2_mouse_moved_user(report_mouse_t *mouse_report) {}
 | 
			
		||||
 | 
			
		||||
void ps2_mouse_task(void) {
 | 
			
		||||
    static uint8_t buttons_prev = 0;
 | 
			
		||||
    extern int     tp_buttons;
 | 
			
		||||
 | 
			
		||||
    /* receives packet from mouse */
 | 
			
		||||
    uint8_t rcv;
 | 
			
		||||
    rcv = ps2_host_send(PS2_MOUSE_READ_DATA);
 | 
			
		||||
    if (rcv == PS2_ACK) {
 | 
			
		||||
        mouse_report.buttons = ps2_host_recv_response() | tp_buttons;
 | 
			
		||||
        mouse_report.x       = ps2_host_recv_response() * PS2_MOUSE_X_MULTIPLIER;
 | 
			
		||||
        mouse_report.y       = ps2_host_recv_response() * PS2_MOUSE_Y_MULTIPLIER;
 | 
			
		||||
#ifdef PS2_MOUSE_ENABLE_SCROLLING
 | 
			
		||||
        mouse_report.v = -(ps2_host_recv_response() & PS2_MOUSE_SCROLL_MASK) * PS2_MOUSE_V_MULTIPLIER;
 | 
			
		||||
#endif
 | 
			
		||||
    } else {
 | 
			
		||||
        if (debug_mouse) print("ps2_mouse: fail to get mouse packet\n");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* if mouse moves or buttons state changes */
 | 
			
		||||
    if (mouse_report.x || mouse_report.y || mouse_report.v || ((mouse_report.buttons ^ buttons_prev) & PS2_MOUSE_BTN_MASK)) {
 | 
			
		||||
#ifdef PS2_MOUSE_DEBUG_RAW
 | 
			
		||||
        // Used to debug raw ps2 bytes from mouse
 | 
			
		||||
        ps2_mouse_print_report(&mouse_report);
 | 
			
		||||
#endif
 | 
			
		||||
        buttons_prev = mouse_report.buttons;
 | 
			
		||||
        ps2_mouse_convert_report_to_hid(&mouse_report);
 | 
			
		||||
#if PS2_MOUSE_SCROLL_BTN_MASK
 | 
			
		||||
        ps2_mouse_scroll_button_task(&mouse_report);
 | 
			
		||||
#endif
 | 
			
		||||
        if (mouse_report.x || mouse_report.y || mouse_report.v) {
 | 
			
		||||
            ps2_mouse_moved_user(&mouse_report);
 | 
			
		||||
        }
 | 
			
		||||
#ifdef PS2_MOUSE_DEBUG_HID
 | 
			
		||||
        // Used to debug the bytes sent to the host
 | 
			
		||||
        ps2_mouse_print_report(&mouse_report);
 | 
			
		||||
#endif
 | 
			
		||||
        host_mouse_send(&mouse_report);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ps2_mouse_clear_report(&mouse_report);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ps2_mouse_disable_data_reporting(void) { PS2_MOUSE_SEND(PS2_MOUSE_DISABLE_DATA_REPORTING, "ps2 mouse disable data reporting"); }
 | 
			
		||||
 | 
			
		||||
void ps2_mouse_enable_data_reporting(void) { PS2_MOUSE_SEND(PS2_MOUSE_ENABLE_DATA_REPORTING, "ps2 mouse enable data reporting"); }
 | 
			
		||||
 | 
			
		||||
void ps2_mouse_set_remote_mode(void) {
 | 
			
		||||
    PS2_MOUSE_SEND_SAFE(PS2_MOUSE_SET_REMOTE_MODE, "ps2 mouse set remote mode");
 | 
			
		||||
    ps2_mouse_mode = PS2_MOUSE_REMOTE_MODE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ps2_mouse_set_stream_mode(void) {
 | 
			
		||||
    PS2_MOUSE_SEND_SAFE(PS2_MOUSE_SET_STREAM_MODE, "ps2 mouse set stream mode");
 | 
			
		||||
    ps2_mouse_mode = PS2_MOUSE_STREAM_MODE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ps2_mouse_set_scaling_2_1(void) { PS2_MOUSE_SEND_SAFE(PS2_MOUSE_SET_SCALING_2_1, "ps2 mouse set scaling 2:1"); }
 | 
			
		||||
 | 
			
		||||
void ps2_mouse_set_scaling_1_1(void) { PS2_MOUSE_SEND_SAFE(PS2_MOUSE_SET_SCALING_1_1, "ps2 mouse set scaling 1:1"); }
 | 
			
		||||
 | 
			
		||||
void ps2_mouse_set_resolution(ps2_mouse_resolution_t resolution) { PS2_MOUSE_SET_SAFE(PS2_MOUSE_SET_RESOLUTION, resolution, "ps2 mouse set resolution"); }
 | 
			
		||||
 | 
			
		||||
void ps2_mouse_set_sample_rate(ps2_mouse_sample_rate_t sample_rate) { PS2_MOUSE_SET_SAFE(PS2_MOUSE_SET_SAMPLE_RATE, sample_rate, "ps2 mouse set sample rate"); }
 | 
			
		||||
 | 
			
		||||
/* ============================= HELPERS ============================ */
 | 
			
		||||
 | 
			
		||||
#define X_IS_NEG (mouse_report->buttons & (1 << PS2_MOUSE_X_SIGN))
 | 
			
		||||
#define Y_IS_NEG (mouse_report->buttons & (1 << PS2_MOUSE_Y_SIGN))
 | 
			
		||||
#define X_IS_OVF (mouse_report->buttons & (1 << PS2_MOUSE_X_OVFLW))
 | 
			
		||||
#define Y_IS_OVF (mouse_report->buttons & (1 << PS2_MOUSE_Y_OVFLW))
 | 
			
		||||
static inline void ps2_mouse_convert_report_to_hid(report_mouse_t *mouse_report) {
 | 
			
		||||
    // PS/2 mouse data is '9-bit integer'(-256 to 255) which is comprised of sign-bit and 8-bit value.
 | 
			
		||||
    // bit: 8    7 ... 0
 | 
			
		||||
    //      sign \8-bit/
 | 
			
		||||
    //
 | 
			
		||||
    // Meanwhile USB HID mouse indicates 8bit data(-127 to 127), note that -128 is not used.
 | 
			
		||||
    //
 | 
			
		||||
    // This converts PS/2 data into HID value. Use only -127-127 out of PS/2 9-bit.
 | 
			
		||||
    mouse_report->x = X_IS_NEG ? ((!X_IS_OVF && -127 <= mouse_report->x && mouse_report->x <= -1) ? mouse_report->x : -127) : ((!X_IS_OVF && 0 <= mouse_report->x && mouse_report->x <= 127) ? mouse_report->x : 127);
 | 
			
		||||
    mouse_report->y = Y_IS_NEG ? ((!Y_IS_OVF && -127 <= mouse_report->y && mouse_report->y <= -1) ? mouse_report->y : -127) : ((!Y_IS_OVF && 0 <= mouse_report->y && mouse_report->y <= 127) ? mouse_report->y : 127);
 | 
			
		||||
 | 
			
		||||
#ifdef PS2_MOUSE_INVERT_BUTTONS
 | 
			
		||||
    // swap left & right buttons
 | 
			
		||||
    uint8_t needs_left    = mouse_report->buttons & PS2_MOUSE_BTN_RIGHT;
 | 
			
		||||
    uint8_t needs_right   = mouse_report->buttons & PS2_MOUSE_BTN_LEFT;
 | 
			
		||||
    mouse_report->buttons = (mouse_report->buttons & ~(PS2_MOUSE_BTN_MASK)) | (needs_left ? PS2_MOUSE_BTN_LEFT : 0) | (needs_right ? PS2_MOUSE_BTN_RIGHT : 0);
 | 
			
		||||
#else
 | 
			
		||||
    // remove sign and overflow flags
 | 
			
		||||
    mouse_report->buttons &= PS2_MOUSE_BTN_MASK;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef PS2_MOUSE_INVERT_X
 | 
			
		||||
    mouse_report->x = -mouse_report->x;
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef PS2_MOUSE_INVERT_Y  // NOTE if not!
 | 
			
		||||
    // invert coordinate of y to conform to USB HID mouse
 | 
			
		||||
    mouse_report->y = -mouse_report->y;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef PS2_MOUSE_ROTATE
 | 
			
		||||
    int8_t x = mouse_report->x;
 | 
			
		||||
    int8_t y = mouse_report->y;
 | 
			
		||||
#    if PS2_MOUSE_ROTATE == 90
 | 
			
		||||
    mouse_report->x = y;
 | 
			
		||||
    mouse_report->y = -x;
 | 
			
		||||
#    elif PS2_MOUSE_ROTATE == 180
 | 
			
		||||
    mouse_report->x = -x;
 | 
			
		||||
    mouse_report->y = -y;
 | 
			
		||||
#    elif PS2_MOUSE_ROTATE == 270
 | 
			
		||||
    mouse_report->x = -y;
 | 
			
		||||
    mouse_report->y = x;
 | 
			
		||||
#    endif
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void ps2_mouse_clear_report(report_mouse_t *mouse_report) {
 | 
			
		||||
    mouse_report->x       = 0;
 | 
			
		||||
    mouse_report->y       = 0;
 | 
			
		||||
    mouse_report->v       = 0;
 | 
			
		||||
    mouse_report->h       = 0;
 | 
			
		||||
    mouse_report->buttons = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void ps2_mouse_print_report(report_mouse_t *mouse_report) {
 | 
			
		||||
    if (!debug_mouse) return;
 | 
			
		||||
    print("ps2_mouse: [");
 | 
			
		||||
    print_hex8(mouse_report->buttons);
 | 
			
		||||
    print("|");
 | 
			
		||||
    print_hex8((uint8_t)mouse_report->x);
 | 
			
		||||
    print(" ");
 | 
			
		||||
    print_hex8((uint8_t)mouse_report->y);
 | 
			
		||||
    print(" ");
 | 
			
		||||
    print_hex8((uint8_t)mouse_report->v);
 | 
			
		||||
    print(" ");
 | 
			
		||||
    print_hex8((uint8_t)mouse_report->h);
 | 
			
		||||
    print("]\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void ps2_mouse_enable_scrolling(void) {
 | 
			
		||||
    PS2_MOUSE_SEND(PS2_MOUSE_SET_SAMPLE_RATE, "Initiaing scroll wheel enable: Set sample rate");
 | 
			
		||||
    PS2_MOUSE_SEND(200, "200");
 | 
			
		||||
    PS2_MOUSE_SEND(PS2_MOUSE_SET_SAMPLE_RATE, "Set sample rate");
 | 
			
		||||
    PS2_MOUSE_SEND(100, "100");
 | 
			
		||||
    PS2_MOUSE_SEND(PS2_MOUSE_SET_SAMPLE_RATE, "Set sample rate");
 | 
			
		||||
    PS2_MOUSE_SEND(80, "80");
 | 
			
		||||
    PS2_MOUSE_SEND(PS2_MOUSE_GET_DEVICE_ID, "Finished enabling scroll wheel");
 | 
			
		||||
    wait_ms(20);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define PRESS_SCROLL_BUTTONS mouse_report->buttons |= (PS2_MOUSE_SCROLL_BTN_MASK)
 | 
			
		||||
#define RELEASE_SCROLL_BUTTONS mouse_report->buttons &= ~(PS2_MOUSE_SCROLL_BTN_MASK)
 | 
			
		||||
static inline void ps2_mouse_scroll_button_task(report_mouse_t *mouse_report) {
 | 
			
		||||
    static enum {
 | 
			
		||||
        SCROLL_NONE,
 | 
			
		||||
        SCROLL_BTN,
 | 
			
		||||
        SCROLL_SENT,
 | 
			
		||||
    } scroll_state                     = SCROLL_NONE;
 | 
			
		||||
    static uint16_t scroll_button_time = 0;
 | 
			
		||||
 | 
			
		||||
    if (PS2_MOUSE_SCROLL_BTN_MASK == (mouse_report->buttons & (PS2_MOUSE_SCROLL_BTN_MASK))) {
 | 
			
		||||
        // All scroll buttons are pressed
 | 
			
		||||
 | 
			
		||||
        if (scroll_state == SCROLL_NONE) {
 | 
			
		||||
            scroll_button_time = timer_read();
 | 
			
		||||
            scroll_state       = SCROLL_BTN;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // If the mouse has moved, update the report to scroll instead of move the mouse
 | 
			
		||||
        if (mouse_report->x || mouse_report->y) {
 | 
			
		||||
            scroll_state    = SCROLL_SENT;
 | 
			
		||||
            mouse_report->v = -mouse_report->y / (PS2_MOUSE_SCROLL_DIVISOR_V);
 | 
			
		||||
            mouse_report->h = mouse_report->x / (PS2_MOUSE_SCROLL_DIVISOR_H);
 | 
			
		||||
            mouse_report->x = 0;
 | 
			
		||||
            mouse_report->y = 0;
 | 
			
		||||
#ifdef PS2_MOUSE_INVERT_H
 | 
			
		||||
            mouse_report->h = -mouse_report->h;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef PS2_MOUSE_INVERT_V
 | 
			
		||||
            mouse_report->v = -mouse_report->v;
 | 
			
		||||
#endif
 | 
			
		||||
        }
 | 
			
		||||
    } else if (0 == (PS2_MOUSE_SCROLL_BTN_MASK & mouse_report->buttons)) {
 | 
			
		||||
        // None of the scroll buttons are pressed
 | 
			
		||||
 | 
			
		||||
#if PS2_MOUSE_SCROLL_BTN_SEND
 | 
			
		||||
        if (scroll_state == SCROLL_BTN && timer_elapsed(scroll_button_time) < PS2_MOUSE_SCROLL_BTN_SEND) {
 | 
			
		||||
            PRESS_SCROLL_BUTTONS;
 | 
			
		||||
            host_mouse_send(mouse_report);
 | 
			
		||||
            wait_ms(100);
 | 
			
		||||
            RELEASE_SCROLL_BUTTONS;
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
        scroll_state = SCROLL_NONE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    RELEASE_SCROLL_BUTTONS;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										177
									
								
								drivers/ps2/ps2_mouse.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								drivers/ps2/ps2_mouse.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,177 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright 2011 Jun Wako <wakojun@gmail.com>
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, either version 2 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include "debug.h"
 | 
			
		||||
#include "report.h"
 | 
			
		||||
 | 
			
		||||
#define PS2_MOUSE_SEND(command, message)                                                \
 | 
			
		||||
    do {                                                                                \
 | 
			
		||||
        __attribute__((unused)) uint8_t rcv = ps2_host_send(command);                   \
 | 
			
		||||
        if (debug_mouse) {                                                              \
 | 
			
		||||
            print((message));                                                           \
 | 
			
		||||
            xprintf(" command: %X, result: %X, error: %X \n", command, rcv, ps2_error); \
 | 
			
		||||
        }                                                                               \
 | 
			
		||||
    } while (0)
 | 
			
		||||
 | 
			
		||||
#define PS2_MOUSE_SEND_SAFE(command, message)          \
 | 
			
		||||
    do {                                               \
 | 
			
		||||
        if (PS2_MOUSE_STREAM_MODE == ps2_mouse_mode) { \
 | 
			
		||||
            ps2_mouse_disable_data_reporting();        \
 | 
			
		||||
        }                                              \
 | 
			
		||||
        PS2_MOUSE_SEND(command, message);              \
 | 
			
		||||
        if (PS2_MOUSE_STREAM_MODE == ps2_mouse_mode) { \
 | 
			
		||||
            ps2_mouse_enable_data_reporting();         \
 | 
			
		||||
        }                                              \
 | 
			
		||||
    } while (0)
 | 
			
		||||
 | 
			
		||||
#define PS2_MOUSE_SET_SAFE(command, value, message)    \
 | 
			
		||||
    do {                                               \
 | 
			
		||||
        if (PS2_MOUSE_STREAM_MODE == ps2_mouse_mode) { \
 | 
			
		||||
            ps2_mouse_disable_data_reporting();        \
 | 
			
		||||
        }                                              \
 | 
			
		||||
        PS2_MOUSE_SEND(command, message);              \
 | 
			
		||||
        PS2_MOUSE_SEND(value, "Sending value");        \
 | 
			
		||||
        if (PS2_MOUSE_STREAM_MODE == ps2_mouse_mode) { \
 | 
			
		||||
            ps2_mouse_enable_data_reporting();         \
 | 
			
		||||
        }                                              \
 | 
			
		||||
    } while (0)
 | 
			
		||||
 | 
			
		||||
#define PS2_MOUSE_RECEIVE(message)                                      \
 | 
			
		||||
    do {                                                                \
 | 
			
		||||
        __attribute__((unused)) uint8_t rcv = ps2_host_recv_response(); \
 | 
			
		||||
        if (debug_mouse) {                                              \
 | 
			
		||||
            print((message));                                           \
 | 
			
		||||
            xprintf(" result: %X, error: %X \n", rcv, ps2_error);       \
 | 
			
		||||
        }                                                               \
 | 
			
		||||
    } while (0)
 | 
			
		||||
 | 
			
		||||
__attribute__((unused)) static enum ps2_mouse_mode_e {
 | 
			
		||||
    PS2_MOUSE_STREAM_MODE,
 | 
			
		||||
    PS2_MOUSE_REMOTE_MODE,
 | 
			
		||||
} ps2_mouse_mode = PS2_MOUSE_STREAM_MODE;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Data format:
 | 
			
		||||
 * byte|7       6       5       4       3       2       1       0
 | 
			
		||||
 * ----+----------------------------------------------------------------
 | 
			
		||||
 *    0|[Yovflw][Xovflw][Ysign ][Xsign ][ 1    ][Middle][Right ][Left  ]
 | 
			
		||||
 *    1|[                    X movement(0-255)                         ]
 | 
			
		||||
 *    2|[                    Y movement(0-255)                         ]
 | 
			
		||||
 */
 | 
			
		||||
#define PS2_MOUSE_BTN_MASK 0x07
 | 
			
		||||
#define PS2_MOUSE_BTN_LEFT 0
 | 
			
		||||
#define PS2_MOUSE_BTN_RIGHT 1
 | 
			
		||||
#define PS2_MOUSE_BTN_MIDDLE 2
 | 
			
		||||
#define PS2_MOUSE_X_SIGN 4
 | 
			
		||||
#define PS2_MOUSE_Y_SIGN 5
 | 
			
		||||
#define PS2_MOUSE_X_OVFLW 6
 | 
			
		||||
#define PS2_MOUSE_Y_OVFLW 7
 | 
			
		||||
 | 
			
		||||
/* mouse button to start scrolling; set 0 to disable scroll */
 | 
			
		||||
#ifndef PS2_MOUSE_SCROLL_BTN_MASK
 | 
			
		||||
#    define PS2_MOUSE_SCROLL_BTN_MASK (1 << PS2_MOUSE_BTN_MIDDLE)
 | 
			
		||||
#endif
 | 
			
		||||
/* send button event when button is released within this value(ms); set 0 to disable  */
 | 
			
		||||
#ifndef PS2_MOUSE_SCROLL_BTN_SEND
 | 
			
		||||
#    define PS2_MOUSE_SCROLL_BTN_SEND 300
 | 
			
		||||
#endif
 | 
			
		||||
/* divide virtical and horizontal mouse move by this to convert to scroll move */
 | 
			
		||||
#ifndef PS2_MOUSE_SCROLL_DIVISOR_V
 | 
			
		||||
#    define PS2_MOUSE_SCROLL_DIVISOR_V 2
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef PS2_MOUSE_SCROLL_DIVISOR_H
 | 
			
		||||
#    define PS2_MOUSE_SCROLL_DIVISOR_H 2
 | 
			
		||||
#endif
 | 
			
		||||
/* multiply reported mouse values by these */
 | 
			
		||||
#ifndef PS2_MOUSE_X_MULTIPLIER
 | 
			
		||||
#    define PS2_MOUSE_X_MULTIPLIER 1
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef PS2_MOUSE_Y_MULTIPLIER
 | 
			
		||||
#    define PS2_MOUSE_Y_MULTIPLIER 1
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef PS2_MOUSE_V_MULTIPLIER
 | 
			
		||||
#    define PS2_MOUSE_V_MULTIPLIER 1
 | 
			
		||||
#endif
 | 
			
		||||
/* For some mice this will need to be 0x0F */
 | 
			
		||||
#ifndef PS2_MOUSE_SCROLL_MASK
 | 
			
		||||
#    define PS2_MOUSE_SCROLL_MASK 0xFF
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef PS2_MOUSE_INIT_DELAY
 | 
			
		||||
#    define PS2_MOUSE_INIT_DELAY 1000
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
enum ps2_mouse_command_e {
 | 
			
		||||
    PS2_MOUSE_RESET                  = 0xFF,
 | 
			
		||||
    PS2_MOUSE_RESEND                 = 0xFE,
 | 
			
		||||
    PS2_MOSUE_SET_DEFAULTS           = 0xF6,
 | 
			
		||||
    PS2_MOUSE_DISABLE_DATA_REPORTING = 0xF5,
 | 
			
		||||
    PS2_MOUSE_ENABLE_DATA_REPORTING  = 0xF4,
 | 
			
		||||
    PS2_MOUSE_SET_SAMPLE_RATE        = 0xF3,
 | 
			
		||||
    PS2_MOUSE_GET_DEVICE_ID          = 0xF2,
 | 
			
		||||
    PS2_MOUSE_SET_REMOTE_MODE        = 0xF0,
 | 
			
		||||
    PS2_MOUSE_SET_WRAP_MODE          = 0xEC,
 | 
			
		||||
    PS2_MOUSE_READ_DATA              = 0xEB,
 | 
			
		||||
    PS2_MOUSE_SET_STREAM_MODE        = 0xEA,
 | 
			
		||||
    PS2_MOUSE_STATUS_REQUEST         = 0xE9,
 | 
			
		||||
    PS2_MOUSE_SET_RESOLUTION         = 0xE8,
 | 
			
		||||
    PS2_MOUSE_SET_SCALING_2_1        = 0xE7,
 | 
			
		||||
    PS2_MOUSE_SET_SCALING_1_1        = 0xE6,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef enum ps2_mouse_resolution_e {
 | 
			
		||||
    PS2_MOUSE_1_COUNT_MM,
 | 
			
		||||
    PS2_MOUSE_2_COUNT_MM,
 | 
			
		||||
    PS2_MOUSE_4_COUNT_MM,
 | 
			
		||||
    PS2_MOUSE_8_COUNT_MM,
 | 
			
		||||
} ps2_mouse_resolution_t;
 | 
			
		||||
 | 
			
		||||
typedef enum ps2_mouse_sample_rate_e {
 | 
			
		||||
    PS2_MOUSE_10_SAMPLES_SEC  = 10,
 | 
			
		||||
    PS2_MOUSE_20_SAMPLES_SEC  = 20,
 | 
			
		||||
    PS2_MOUSE_40_SAMPLES_SEC  = 40,
 | 
			
		||||
    PS2_MOUSE_60_SAMPLES_SEC  = 60,
 | 
			
		||||
    PS2_MOUSE_80_SAMPLES_SEC  = 80,
 | 
			
		||||
    PS2_MOUSE_100_SAMPLES_SEC = 100,
 | 
			
		||||
    PS2_MOUSE_200_SAMPLES_SEC = 200,
 | 
			
		||||
} ps2_mouse_sample_rate_t;
 | 
			
		||||
 | 
			
		||||
void ps2_mouse_init(void);
 | 
			
		||||
 | 
			
		||||
void ps2_mouse_init_user(void);
 | 
			
		||||
 | 
			
		||||
void ps2_mouse_task(void);
 | 
			
		||||
 | 
			
		||||
void ps2_mouse_disable_data_reporting(void);
 | 
			
		||||
 | 
			
		||||
void ps2_mouse_enable_data_reporting(void);
 | 
			
		||||
 | 
			
		||||
void ps2_mouse_set_remote_mode(void);
 | 
			
		||||
 | 
			
		||||
void ps2_mouse_set_stream_mode(void);
 | 
			
		||||
 | 
			
		||||
void ps2_mouse_set_scaling_2_1(void);
 | 
			
		||||
 | 
			
		||||
void ps2_mouse_set_scaling_1_1(void);
 | 
			
		||||
 | 
			
		||||
void ps2_mouse_set_resolution(ps2_mouse_resolution_t resolution);
 | 
			
		||||
 | 
			
		||||
void ps2_mouse_set_sample_rate(ps2_mouse_sample_rate_t sample_rate);
 | 
			
		||||
 | 
			
		||||
void ps2_mouse_moved_user(report_mouse_t *mouse_report);
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue