| 1 | /* Copyright (C) 2007 L. Donnie Smith <donnie.smith@gatech.edu> |
|---|
| 2 | * |
|---|
| 3 | * This program is free software; you can redistribute it and/or modify |
|---|
| 4 | * it under the terms of the GNU General Public License as published by |
|---|
| 5 | * the Free Software Foundation; either version 2 of the License, or |
|---|
| 6 | * (at your option) any later version. |
|---|
| 7 | * |
|---|
| 8 | * This program is distributed in the hope that it will be useful, |
|---|
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 11 | * GNU General Public License for more details. |
|---|
| 12 | * |
|---|
| 13 | * You should have received a copy of the GNU General Public License |
|---|
| 14 | * along with this program; if not, write to the Free Software |
|---|
| 15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|---|
| 16 | * |
|---|
| 17 | */ |
|---|
| 18 | |
|---|
| 19 | #include <stdint.h> |
|---|
| 20 | #include <stdlib.h> |
|---|
| 21 | #include <string.h> |
|---|
| 22 | #include <pthread.h> |
|---|
| 23 | #include <time.h> |
|---|
| 24 | #include <unistd.h> |
|---|
| 25 | #include "cwiid_internal.h" |
|---|
| 26 | |
|---|
| 27 | #define READ_BUF_LEN 23 |
|---|
| 28 | void *router_thread(struct wiimote *wiimote) |
|---|
| 29 | { |
|---|
| 30 | unsigned char buf[READ_BUF_LEN]; |
|---|
| 31 | ssize_t len; |
|---|
| 32 | struct mesg_array ma; |
|---|
| 33 | char err, print_clock_err = 1; |
|---|
| 34 | |
|---|
| 35 | while (1) { |
|---|
| 36 | /* Read packet */ |
|---|
| 37 | len = read(wiimote->int_socket, buf, READ_BUF_LEN); |
|---|
| 38 | ma.count = 0; |
|---|
| 39 | if (clock_gettime(CLOCK_REALTIME, &ma.timestamp)) { |
|---|
| 40 | if (print_clock_err) { |
|---|
| 41 | cwiid_err(wiimote, "clock_gettime error"); |
|---|
| 42 | print_clock_err = 0; |
|---|
| 43 | } |
|---|
| 44 | } |
|---|
| 45 | err = 0; |
|---|
| 46 | if ((len == -1) || (len == 0)) { |
|---|
| 47 | process_error(wiimote, len, &ma); |
|---|
| 48 | write_mesg_array(wiimote, &ma); |
|---|
| 49 | /* Quit! */ |
|---|
| 50 | break; |
|---|
| 51 | } |
|---|
| 52 | else { |
|---|
| 53 | /* Verify first byte (DATA/INPUT) */ |
|---|
| 54 | if (buf[0] != (BT_TRANS_DATA | BT_PARAM_INPUT)) { |
|---|
| 55 | cwiid_err(wiimote, "Invalid packet type"); |
|---|
| 56 | } |
|---|
| 57 | |
|---|
| 58 | /* Main switch */ |
|---|
| 59 | /* printf("%.2X %.2X %.2X %.2X %.2X %.2X %.2X %.2X\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); |
|---|
| 60 | printf("%.2X %.2X %.2X %.2X %.2X %.2X %.2X %.2X\n", buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); |
|---|
| 61 | printf("%.2X %.2X %.2X %.2X %.2X %.2X %.2X %.2X\n", buf[16], buf[17], buf[18], buf[19], buf[20], buf[21], buf[22], buf[23]); |
|---|
| 62 | printf("\n"); */ |
|---|
| 63 | switch (buf[1]) { |
|---|
| 64 | case RPT_STATUS: |
|---|
| 65 | err = process_status(wiimote, &buf[2], &ma); |
|---|
| 66 | break; |
|---|
| 67 | case RPT_BTN: |
|---|
| 68 | err = process_btn(wiimote, &buf[2], &ma); |
|---|
| 69 | break; |
|---|
| 70 | case RPT_BTN_ACC: |
|---|
| 71 | err = process_btn(wiimote, &buf[2], &ma) || |
|---|
| 72 | process_acc(wiimote, &buf[4], &ma); |
|---|
| 73 | break; |
|---|
| 74 | case RPT_BTN_EXT8: |
|---|
| 75 | err = process_btn(wiimote, &buf[2], &ma) || |
|---|
| 76 | process_ext(wiimote, &buf[4], 8, &ma); |
|---|
| 77 | break; |
|---|
| 78 | case RPT_BTN_ACC_IR12: |
|---|
| 79 | err = process_btn(wiimote, &buf[2], &ma) || |
|---|
| 80 | process_acc(wiimote, &buf[4], &ma) || |
|---|
| 81 | process_ir12(wiimote, &buf[7], &ma); |
|---|
| 82 | break; |
|---|
| 83 | case RPT_BTN_EXT19: |
|---|
| 84 | err = process_btn(wiimote, &buf[2], &ma) || |
|---|
| 85 | process_ext(wiimote, &buf[4], 19, &ma); |
|---|
| 86 | break; |
|---|
| 87 | case RPT_BTN_ACC_EXT16: |
|---|
| 88 | err = process_btn(wiimote, &buf[2], &ma) || |
|---|
| 89 | process_acc(wiimote, &buf[4], &ma) || |
|---|
| 90 | process_ext(wiimote, &buf[7], 16, &ma); |
|---|
| 91 | break; |
|---|
| 92 | case RPT_BTN_IR10_EXT9: |
|---|
| 93 | err = process_btn(wiimote, &buf[2], &ma) || |
|---|
| 94 | process_ir10(wiimote, &buf[4], &ma) || |
|---|
| 95 | process_ext(wiimote, &buf[14], 9, &ma); |
|---|
| 96 | break; |
|---|
| 97 | case RPT_BTN_ACC_IR10_EXT6: |
|---|
| 98 | err = process_btn(wiimote, &buf[2], &ma) || |
|---|
| 99 | process_acc(wiimote, &buf[4], &ma) || |
|---|
| 100 | process_ir10(wiimote, &buf[7], &ma) || |
|---|
| 101 | process_ext(wiimote, &buf[17], 6, &ma); |
|---|
| 102 | break; |
|---|
| 103 | case RPT_EXT21: |
|---|
| 104 | err = process_ext(wiimote, &buf[2], 21, &ma); |
|---|
| 105 | break; |
|---|
| 106 | case RPT_BTN_ACC_IR36_1: |
|---|
| 107 | case RPT_BTN_ACC_IR36_2: |
|---|
| 108 | cwiid_err(wiimote, "Unsupported report type received " |
|---|
| 109 | "(interleaved data)"); |
|---|
| 110 | err = 1; |
|---|
| 111 | break; |
|---|
| 112 | case RPT_READ_DATA: |
|---|
| 113 | err = process_read(wiimote, &buf[4]) || |
|---|
| 114 | process_btn(wiimote, &buf[2], &ma); |
|---|
| 115 | break; |
|---|
| 116 | case RPT_WRITE_ACK: |
|---|
| 117 | err = process_write(wiimote, &buf[2]); |
|---|
| 118 | break; |
|---|
| 119 | default: |
|---|
| 120 | cwiid_err(wiimote, "Unknown message type"); |
|---|
| 121 | err = 1; |
|---|
| 122 | break; |
|---|
| 123 | } |
|---|
| 124 | |
|---|
| 125 | if (!err && (ma.count > 0)) { |
|---|
| 126 | if (update_state(wiimote, &ma)) { |
|---|
| 127 | cwiid_err(wiimote, "State update error"); |
|---|
| 128 | } |
|---|
| 129 | if (wiimote->flags & CWIID_FLAG_MESG_IFC) { |
|---|
| 130 | /* prints its own errors */ |
|---|
| 131 | write_mesg_array(wiimote, &ma); |
|---|
| 132 | } |
|---|
| 133 | } |
|---|
| 134 | } |
|---|
| 135 | } |
|---|
| 136 | |
|---|
| 137 | return NULL; |
|---|
| 138 | } |
|---|
| 139 | |
|---|
| 140 | void *status_thread(struct wiimote *wiimote) |
|---|
| 141 | { |
|---|
| 142 | struct mesg_array ma; |
|---|
| 143 | struct cwiid_status_mesg *status_mesg; |
|---|
| 144 | unsigned char buf[2]; |
|---|
| 145 | |
|---|
| 146 | ma.count = 1; |
|---|
| 147 | status_mesg = &ma.array[0].status_mesg; |
|---|
| 148 | |
|---|
| 149 | while (1) { |
|---|
| 150 | if (full_read(wiimote->status_pipe[0], status_mesg, |
|---|
| 151 | sizeof *status_mesg)) { |
|---|
| 152 | cwiid_err(wiimote, "Pipe read error (status)"); |
|---|
| 153 | /* Quit! */ |
|---|
| 154 | break; |
|---|
| 155 | } |
|---|
| 156 | |
|---|
| 157 | if (status_mesg->type != CWIID_MESG_STATUS) { |
|---|
| 158 | cwiid_err(wiimote, "Bad message on status pipe"); |
|---|
| 159 | continue; |
|---|
| 160 | } |
|---|
| 161 | |
|---|
| 162 | if (status_mesg->ext_type == CWIID_EXT_UNKNOWN) { |
|---|
| 163 | /* Read extension ID */ |
|---|
| 164 | if (cwiid_read(wiimote, CWIID_RW_REG, 0xA400FE, 2, &buf)) { |
|---|
| 165 | cwiid_err(wiimote, "Read error (extension error)"); |
|---|
| 166 | status_mesg->ext_type = CWIID_EXT_UNKNOWN; |
|---|
| 167 | } |
|---|
| 168 | /* If the extension didn't change, or if the extension is a |
|---|
| 169 | * MotionPlus, no init necessary */ |
|---|
| 170 | switch ((buf[0] << 8) | buf[1]) { |
|---|
| 171 | case EXT_NONE: |
|---|
| 172 | status_mesg->ext_type = CWIID_EXT_NONE; |
|---|
| 173 | break; |
|---|
| 174 | case EXT_NUNCHUK: |
|---|
| 175 | status_mesg->ext_type = CWIID_EXT_NUNCHUK; |
|---|
| 176 | break; |
|---|
| 177 | case EXT_CLASSIC: |
|---|
| 178 | status_mesg->ext_type = CWIID_EXT_CLASSIC; |
|---|
| 179 | break; |
|---|
| 180 | case EXT_BALANCE: |
|---|
| 181 | status_mesg->ext_type = CWIID_EXT_BALANCE; |
|---|
| 182 | break; |
|---|
| 183 | case EXT_MOTIONPLUS: |
|---|
| 184 | status_mesg->ext_type = CWIID_EXT_MOTIONPLUS; |
|---|
| 185 | break; |
|---|
| 186 | case EXT_PARTIAL: |
|---|
| 187 | /* Everything (but MotionPlus) shows up as partial until initialized */ |
|---|
| 188 | buf[0] = 0x55; |
|---|
| 189 | buf[1] = 0x00; |
|---|
| 190 | /* Initialize extension register space */ |
|---|
| 191 | if (cwiid_write(wiimote, CWIID_RW_REG, 0xA400F0, 1, &buf[0])) { |
|---|
| 192 | cwiid_err(wiimote, "Extension initialization error"); |
|---|
| 193 | status_mesg->ext_type = CWIID_EXT_UNKNOWN; |
|---|
| 194 | } |
|---|
| 195 | else if (cwiid_write(wiimote, CWIID_RW_REG, 0xA400FB, 1, &buf[1])) { |
|---|
| 196 | cwiid_err(wiimote, "Extension initialization error"); |
|---|
| 197 | status_mesg->ext_type = CWIID_EXT_UNKNOWN; |
|---|
| 198 | } |
|---|
| 199 | /* Read extension ID */ |
|---|
| 200 | else if (cwiid_read(wiimote, CWIID_RW_REG, 0xA400FE, 2, &buf)) { |
|---|
| 201 | cwiid_err(wiimote, "Read error (extension error)"); |
|---|
| 202 | status_mesg->ext_type = CWIID_EXT_UNKNOWN; |
|---|
| 203 | } |
|---|
| 204 | else { |
|---|
| 205 | switch ((buf[0] << 8) | buf[1]) { |
|---|
| 206 | case EXT_NONE: |
|---|
| 207 | case EXT_PARTIAL: |
|---|
| 208 | status_mesg->ext_type = CWIID_EXT_NONE; |
|---|
| 209 | break; |
|---|
| 210 | case EXT_NUNCHUK: |
|---|
| 211 | status_mesg->ext_type = CWIID_EXT_NUNCHUK; |
|---|
| 212 | break; |
|---|
| 213 | case EXT_CLASSIC: |
|---|
| 214 | status_mesg->ext_type = CWIID_EXT_CLASSIC; |
|---|
| 215 | break; |
|---|
| 216 | case EXT_BALANCE: |
|---|
| 217 | status_mesg->ext_type = CWIID_EXT_BALANCE; |
|---|
| 218 | break; |
|---|
| 219 | default: |
|---|
| 220 | status_mesg->ext_type = CWIID_EXT_UNKNOWN; |
|---|
| 221 | break; |
|---|
| 222 | } |
|---|
| 223 | } |
|---|
| 224 | break; |
|---|
| 225 | } |
|---|
| 226 | } |
|---|
| 227 | |
|---|
| 228 | if (update_state(wiimote, &ma)) { |
|---|
| 229 | cwiid_err(wiimote, "State update error"); |
|---|
| 230 | } |
|---|
| 231 | if (update_rpt_mode(wiimote, -1)) { |
|---|
| 232 | cwiid_err(wiimote, "Error reseting report mode"); |
|---|
| 233 | } |
|---|
| 234 | if ((wiimote->state.rpt_mode & CWIID_RPT_STATUS) && |
|---|
| 235 | (wiimote->flags & CWIID_FLAG_MESG_IFC)) { |
|---|
| 236 | if (write_mesg_array(wiimote, &ma)) { |
|---|
| 237 | /* prints its own errors */ |
|---|
| 238 | } |
|---|
| 239 | } |
|---|
| 240 | } |
|---|
| 241 | |
|---|
| 242 | return NULL; |
|---|
| 243 | } |
|---|
| 244 | |
|---|
| 245 | void *mesg_callback_thread(struct wiimote *wiimote) |
|---|
| 246 | { |
|---|
| 247 | int mesg_pipe = wiimote->mesg_pipe[0]; |
|---|
| 248 | cwiid_mesg_callback_t *callback = wiimote->mesg_callback; |
|---|
| 249 | struct mesg_array ma; |
|---|
| 250 | int cancelstate; |
|---|
| 251 | |
|---|
| 252 | while (1) { |
|---|
| 253 | if (read_mesg_array(mesg_pipe, &ma)) { |
|---|
| 254 | cwiid_err(wiimote, "Mesg pipe read error"); |
|---|
| 255 | continue; |
|---|
| 256 | } |
|---|
| 257 | |
|---|
| 258 | /* TODO: The callback can still be called once after disconnect, |
|---|
| 259 | * although it's very unlikely. User must keep track and avoid |
|---|
| 260 | * accessing the wiimote struct after disconnect. */ |
|---|
| 261 | if (pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancelstate)) { |
|---|
| 262 | cwiid_err(wiimote, "Cancel state disable error (callback thread)"); |
|---|
| 263 | } |
|---|
| 264 | callback(wiimote, ma.count, ma.array, &ma.timestamp); |
|---|
| 265 | if (pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &cancelstate)) { |
|---|
| 266 | cwiid_err(wiimote, "Cancel state restore error (callback thread)"); |
|---|
| 267 | } |
|---|
| 268 | } |
|---|
| 269 | |
|---|
| 270 | return NULL; |
|---|
| 271 | } |
|---|