summaryrefslogtreecommitdiff
path: root/hid/pico/src/ph_ps2_mouse.c
blob: 279a06d6993c0317b42d1e485636ffe773ecccb1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
#include "ph_outputs.h"
#include "ph_ps2_phy.h"

extern bool ph_g_ps2_mouse_online;

ph_ps2_phy ph_ps2_mouse;
u8 ms_type = 0;
u8 ms_mode = 0;
u8 ms_input_mode = 0;
u8 ms_rate = 100;
u32 ms_magic_seq = 0x00;

u8 buttons = 0;

#define MS_TYPE_STANDARD  0x00
#define MS_TYPE_WHEEL_3   0x03
#define MS_TYPE_WHEEL_5   0x04

#define MS_MODE_IDLE      0
#define MS_MODE_STREAMING 1

#define MS_INPUT_CMD      0
#define MS_INPUT_SET_RATE 1

void ph_ps2_mouse_send(u8 byte) {
  queue_try_add(&ph_ps2_mouse.qbytes, &byte);
}

void ph_ps2_mouse_packet(u8 button, u8 x1, u8 y1) {
  if(ms_mode == MS_MODE_STREAMING) {
    u8 s = (button & 7) + 8;
    u8 x = x1 & 0x7f;
    u8 y = y1 & 0x7f;
    u8 z = 0;
    
    if(x1 >> 7) {
      s += 0x10;
      x += 0x80;
    }
    
    if(y1 >> 7) {
      y = 0x80 - y;
    } else if(y) {
      s += 0x20;
      y = 0x100 - y;
    }
    
    ph_ps2_mouse_send(s);
    ph_ps2_mouse_send(x);
    ph_ps2_mouse_send(y);
    
    if (ms_type == MS_TYPE_WHEEL_3 || ms_type == MS_TYPE_WHEEL_5) {
      /*if(report[3] >> 7) {
        z = 0x8 - z;
      } else if(z) {
        z = 0x10 - z;
      }
    
      if (ms_type == MS_TYPE_WHEEL_5) {
        if (report[0] & 0x8) {
          z += 0x10;
        }
    
        if (report[0] & 0x10) {
          z += 0x20;
        }
      }*/
    
      ph_ps2_mouse_send(z);
    }
  }
}

void ph_ps2_mouse_send_button(u8 button, bool state) {
  // TODO: PS2: Send mouse button
  //   @button - USB button code
  //   @state - true if pressed, false if released
  // The function should take care not to send duplicate events (if needed for PS/2)
  // If the PS2 keyboard is not used (PH_O_IS_MOUSE_PS2 is false), the function should do nothing.
  (void)button; // Remove this
  (void)state; // Remove this
  
  u8 bitval = 1;
  
  button--;
  
  if(state) {
    buttons = buttons | (bitval << button);
  } else {
    buttons = buttons & ~(bitval << button);
  }
  
  ph_ps2_mouse_packet(buttons, 0, 0);
}

void ph_ps2_mouse_send_rel(s8 x1, s8 y1) {
  // TODO: PS2: Send relative move event
  // If the PS2 keyboard is not used (PH_O_IS_MOUSE_PS2 is false), the function should do nothing.
  ph_ps2_mouse_packet(buttons, x1, y1);
}

void ph_ps2_mouse_send_wheel(s8 h, s8 v) {
  (void)h;
  // TODO: PS2: Send wheel. As I understand, PS/2 has no horizontal scrolling, so @h just can be ignored.
  //   @v - vertical scrolling like on USB
  // If the PS2 keyboard is not used (PH_O_IS_MOUSE_PS2 is false), the function should do nothing.
  (void)v; // Remove this
}

void ph_ps2_mouse_receive(u8 byte, u8 prev_byte) {
  
  if(ms_input_mode == MS_INPUT_SET_RATE) {
    ms_rate = byte;
    ms_input_mode = MS_INPUT_CMD;
    ph_ps2_mouse_send(0xfa);
  
    ms_magic_seq = (ms_magic_seq << 8) | byte;
    if(ms_type == MS_TYPE_STANDARD && ms_magic_seq == 0xc86450) {
      ms_type = MS_TYPE_WHEEL_3;
    } else if (ms_type == MS_TYPE_WHEEL_3 && ms_magic_seq == 0xc8c850) {
      ms_type = MS_TYPE_WHEEL_5;
    }
    return;
  }
  
  if(byte != 0xf3) {
    ms_magic_seq = 0x00;
  }
  
  switch(byte) {
    case 0xff: // Reset
      ms_type = MS_TYPE_STANDARD;
      ms_mode = MS_MODE_IDLE;
      ms_rate = 100;
      
      ph_ps2_mouse_send(0xfa);
      ph_ps2_mouse_send(0xaa);
      ph_ps2_mouse_send(ms_type);
    return;
  
    case 0xf6: // Set Defaults
      ms_type = MS_TYPE_STANDARD;
      ms_rate = 100;
    case 0xf5: // Disable Data Reporting
    case 0xea: // Set Stream Mode
      ms_mode = MS_MODE_IDLE;
      ph_ps2_mouse_send(0xfa);
    return;
  
    case 0xf4: // Enable Data Reporting
      ms_mode = MS_MODE_STREAMING;
      ph_ps2_mouse_send(0xfa);
    return;
  
    case 0xf3: // Set Sample Rate
      ms_input_mode = MS_INPUT_SET_RATE;
      ph_ps2_mouse_send(0xfa);
    return;
  
    case 0xf2: // Get Device ID
      ph_ps2_mouse_send(0xfa);
      ph_ps2_mouse_send(ms_type);
    return;
  
    case 0xe9: // Status Request
      ph_ps2_mouse_send(0xfa);
      ph_ps2_mouse_send(0x00); // Bit6: Mode, Bit 5: Enable, Bit 4: Scaling, Bits[2,1,0] = Buttons[L,M,R]
      ph_ps2_mouse_send(0x02); // Resolution
      ph_ps2_mouse_send(ms_rate); // Sample Rate
    return;
  
  // TODO: Implement (more of) these?
  //    case 0xf0: // Set Remote Mode
  //    case 0xee: // Set Wrap Mode
  //    case 0xec: // Reset Wrap Mode
  //    case 0xeb: // Read Data
  //    case 0xe8: // Set Resolution
  //    case 0xe7: // Set Scaling 2:1
  //    case 0xe6: // Set Scaling 1:1
  }
  
  ph_ps2_mouse_send(0xfa);
}

void ph_ps2_mouse_task(void) {
  ph_ps2_phy_task(&ph_ps2_mouse);
}

void ph_ps2_mouse_init(u8 gpio) {
  ph_ps2_phy_init(&ph_ps2_mouse, pio0, gpio, &ph_ps2_mouse_receive);
}