1 /* 2 * Copyright 2006-2010, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Jérôme Duval 7 * 8 * References: 9 * Google search "technic doc genius" , http://www.bebits.com/app/2152 10 */ 11 12 #include <stdlib.h> 13 #include <string.h> 14 #include <unistd.h> 15 16 #include <Debug.h> 17 #include <InterfaceDefs.h> 18 #include <SerialPort.h> 19 20 #include "EasyPenInputDevice.h" 21 #include "keyboard_mouse_driver.h" 22 23 /* 24 IN ABSOLUTE MODE (MM Series) 25 26 Least Most 27 28 Bytes Bit to Significant Significant Bit to 29 30 Transmitted Start Bit Bit Stop 31 32 0 1 2 3 4 5 6 7 8 33 34 ----------- ----- --------------------------------------------- ------ 35 36 1st Byte 0 Fa Fb Fc Sy Sx T PR PH P 1 37 38 2nd Byte 0 X0 X1 X2 X3 X4 X5 X6 0 P 1 39 40 3rd Byte 0 X7 X8 X9 X10 X11 X12 X13 0 P 1 41 42 4th Byte 0 Y0 Y1 Y2 Y3 Y4 Y5 Y6 0 P 1 43 44 5th Byte 0 Y7 Y8 Y9 Y10 Y11 Y12 Y13 0 P 1 45 46 */ 47 48 #if DEBUG 49 inline void LOG(const char *fmt, ...) { char buf[1024]; va_list ap; va_start(ap, fmt); vsprintf(buf, fmt, ap); va_end(ap); \ 50 fputs(buf, EasyPenInputDevice::sLogFile); fflush(EasyPenInputDevice::sLogFile); } 51 #define LOG_ERR(text...) LOG(text) 52 FILE *EasyPenInputDevice::sLogFile = NULL; 53 #else 54 #define LOG(text...) 55 #define LOG_ERR(text...) fprintf(stderr, text) 56 #endif 57 58 #define CALLED() LOG("%s\n", __PRETTY_FUNCTION__) 59 60 const static uint32 kTabletThreadPriority = B_FIRST_REAL_TIME_PRIORITY + 4; 61 62 struct tablet_device { 63 tablet_device(BSerialPort *port); 64 ~tablet_device(); 65 66 input_device_ref device_ref; 67 thread_id device_watcher; 68 bool active; 69 BSerialPort *serial; 70 EasyPenInputDevice *owner; 71 }; 72 73 74 extern "C" 75 BInputServerDevice * 76 instantiate_input_device() 77 { 78 return new EasyPenInputDevice(); 79 } 80 81 82 EasyPenInputDevice::EasyPenInputDevice() 83 { 84 #if DEBUG 85 sLogFile = fopen("/var/log/easypen_device_log.log", "a"); 86 #endif 87 } 88 89 90 EasyPenInputDevice::~EasyPenInputDevice() 91 { 92 CALLED(); 93 for (int32 i = 0; i < fDevices.CountItems(); i++) 94 delete (tablet_device *)fDevices.ItemAt(i); 95 96 #if DEBUG 97 fclose(sLogFile); 98 #endif 99 } 100 101 102 status_t 103 EasyPenInputDevice::InitCheck() 104 { 105 CALLED(); 106 107 BSerialPort *serial = new BSerialPort(); 108 int count = serial->CountDevices(); 109 char devName[B_OS_NAME_LENGTH]; 110 char byte; 111 112 for (int i=0; i<count; i++) { 113 serial->GetDeviceName(i, devName); 114 serial->SetDataRate(B_9600_BPS); 115 serial->SetDataBits(B_DATA_BITS_8); 116 serial->SetStopBits(B_STOP_BITS_1); 117 serial->SetParityMode(B_ODD_PARITY); 118 serial->SetFlowControl(B_HARDWARE_CONTROL+B_SOFTWARE_CONTROL); 119 serial->SetBlocking(true); 120 serial->SetTimeout(800000); 121 122 if (serial->Open(devName) < B_OK) { 123 LOG("Fail to open %s\n", devName); 124 continue; 125 } 126 snooze(250000); 127 serial->Write("z9", 2); // 8 data bits,odd parity <command> z 9 128 serial->Write("", 1); // Reset <command> NUL 129 serial->Write("DP", 2); // mode command D trigger command P 130 snooze(250000); 131 serial->ClearInput(); 132 serial->Write("twP", 3); // Self-Test, Send Test Results, trigger command P 133 serial->Read(&byte, 1); 134 if ((byte & 0xf7) != 0x87) { 135 LOG("Self test failed for %s %x\n", devName, byte & 0xf7); 136 continue; 137 } 138 tablet_device *device = new tablet_device(serial); 139 device->owner = this; 140 141 input_device_ref *devices[2]; 142 devices[0] = &device->device_ref; 143 devices[1] = NULL; 144 145 fDevices.AddItem(device); 146 RegisterDevices(devices); 147 148 serial = new BSerialPort(); 149 } 150 151 delete serial; 152 153 LOG("Found %ld devices\n", fDevices.CountItems()); 154 155 get_click_speed(&fClickSpeed); 156 157 return fDevices.CountItems() > 0 ? B_OK : B_ERROR; 158 } 159 160 161 status_t 162 EasyPenInputDevice::Start(const char *name, void *cookie) 163 { 164 tablet_device *device = (tablet_device *)cookie; 165 166 LOG("%s(%s)\n", __PRETTY_FUNCTION__, name); 167 168 char threadName[B_OS_NAME_LENGTH]; 169 snprintf(threadName, B_OS_NAME_LENGTH, "%s watcher", name); 170 171 device->active = true; 172 device->device_watcher = spawn_thread(DeviceWatcher, threadName, 173 kTabletThreadPriority, device); 174 175 if (device->device_watcher < B_OK) 176 return device->device_watcher; 177 resume_thread(device->device_watcher); 178 179 return B_OK; 180 } 181 182 183 status_t 184 EasyPenInputDevice::Stop(const char *name, void *cookie) 185 { 186 tablet_device *device = (tablet_device *)cookie; 187 188 LOG("%s(%s)\n", __PRETTY_FUNCTION__, name); 189 190 device->active = false; 191 if (device->device_watcher >= 0) { 192 suspend_thread(device->device_watcher); 193 resume_thread(device->device_watcher); 194 status_t dummy; 195 wait_for_thread(device->device_watcher, &dummy); 196 } 197 198 return B_OK; 199 } 200 201 202 status_t 203 EasyPenInputDevice::Control(const char *name, void *cookie, 204 uint32 command, BMessage *message) 205 { 206 LOG("%s(%s, code: %lu)\n", __PRETTY_FUNCTION__, name, command); 207 208 if (command == B_CLICK_SPEED_CHANGED) 209 get_click_speed(&fClickSpeed); 210 211 return B_OK; 212 } 213 214 215 int32 216 EasyPenInputDevice::DeviceWatcher(void *arg) 217 { 218 tablet_device *dev = (tablet_device *)arg; 219 EasyPenInputDevice *owner = dev->owner; 220 221 tablet_movement movements; 222 tablet_movement old_movements; 223 BMessage *message; 224 uint8 byte; 225 uint8 bytes[4]; 226 bigtime_t lastClickTimeStamp = 0LL; 227 228 memset(&movements, 0, sizeof(movements)); 229 memset(&old_movements, 0, sizeof(old_movements)); 230 231 dev->serial->Write(":@FRb", 5); 232 233 while (dev->active) { 234 byte = 0; 235 while(!byte) 236 dev->serial->Read(&byte, 1); 237 if (byte & 0x40 || !(byte & 0x80)) { // 7th bit on or 8th bit off 238 snooze(10000); // this is a realtime thread, and something is wrong... 239 continue; 240 } 241 242 if (dev->serial->Read(bytes, 4) != 4) 243 continue; 244 245 if (*(uint32*)bytes & 0x80808080) 246 continue; 247 248 movements.buttons = byte & 0x7; 249 movements.timestamp = system_time(); 250 movements.xpos = (bytes[1] << 7) | bytes[0]; 251 movements.ypos = (bytes[3] << 7) | bytes[2]; 252 253 uint32 buttons = old_movements.buttons ^ movements.buttons; 254 255 LOG("%s: buttons: 0x%lx, x: %f, y: %f, clicks:%ld, wheel_x:%ld, wheel_y:%ld\n", dev->device_ref.name, movements.buttons, 256 movements.xpos, movements.ypos, movements.clicks, movements.wheel_xdelta, movements.wheel_ydelta); 257 258 movements.xpos /= 1950.0; 259 movements.ypos /= 1500.0; 260 261 LOG("%s: x: %f, y: %f, \n", dev->device_ref.name, movements.xpos, movements.ypos); 262 263 if (buttons != 0) { 264 message = new BMessage(B_MOUSE_UP); 265 if ((buttons & movements.buttons) > 0) { 266 message->what = B_MOUSE_DOWN; 267 if(lastClickTimeStamp + owner->fClickSpeed <= movements.timestamp) 268 movements.clicks = 1; 269 else 270 movements.clicks++; 271 lastClickTimeStamp = movements.timestamp; 272 message->AddInt32("clicks", movements.clicks); 273 LOG("B_MOUSE_DOWN\n"); 274 } else { 275 LOG("B_MOUSE_UP\n"); 276 } 277 278 message->AddInt64("when", movements.timestamp); 279 message->AddInt32("buttons", movements.buttons); 280 message->AddFloat("x", movements.xpos); 281 message->AddFloat("y", movements.ypos); 282 owner->EnqueueMessage(message); 283 } 284 285 if (movements.xpos != 0.0 || movements.ypos != 0.0) { 286 message = new BMessage(B_MOUSE_MOVED); 287 if (message) { 288 message->AddInt64("when", movements.timestamp); 289 message->AddInt32("buttons", movements.buttons); 290 message->AddFloat("x", movements.xpos); 291 message->AddFloat("y", movements.ypos); 292 message->AddFloat("be:tablet_x", movements.xpos); 293 message->AddFloat("be:tablet_y", movements.ypos); 294 message->AddFloat("be:tablet_pressure", movements.pressure); 295 message->AddInt32("be:tablet_eraser", movements.eraser); 296 if (movements.tilt_x != 0.0 || movements.tilt_y != 0.0) { 297 message->AddFloat("be:tablet_tilt_x", movements.tilt_x); 298 message->AddFloat("be:tablet_tilt_y", movements.tilt_y); 299 } 300 301 owner->EnqueueMessage(message); 302 } 303 } 304 305 if ((movements.wheel_ydelta != 0) || (movements.wheel_xdelta != 0)) { 306 message = new BMessage(B_MOUSE_WHEEL_CHANGED); 307 if (message) { 308 message->AddInt64("when", movements.timestamp); 309 message->AddFloat("be:wheel_delta_x", movements.wheel_xdelta); 310 message->AddFloat("be:wheel_delta_y", movements.wheel_ydelta); 311 312 owner->EnqueueMessage(message); 313 } 314 } 315 316 old_movements = movements; 317 318 } 319 320 return 0; 321 } 322 323 324 // tablet_device 325 tablet_device::tablet_device(BSerialPort *port) 326 { 327 serial = port; 328 device_watcher = -1; 329 active = false; 330 device_ref.name = strdup("Genius EasyPen"); 331 device_ref.type = B_POINTING_DEVICE; 332 device_ref.cookie = this; 333 }; 334 335 336 tablet_device::~tablet_device() 337 { 338 free(device_ref.name); 339 } 340 341