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 const static char* kDeviceName = "Genius EasyPen"; 62 63 struct tablet_device { 64 tablet_device(BSerialPort *port); 65 ~tablet_device(); 66 67 input_device_ref device_ref; 68 thread_id device_watcher; 69 bool active; 70 BSerialPort *serial; 71 EasyPenInputDevice *owner; 72 }; 73 74 75 extern "C" 76 BInputServerDevice * 77 instantiate_input_device() 78 { 79 return new EasyPenInputDevice(); 80 } 81 82 83 EasyPenInputDevice::EasyPenInputDevice() 84 { 85 #if DEBUG 86 sLogFile = fopen("/var/log/easypen_device_log.log", "a"); 87 #endif 88 } 89 90 91 EasyPenInputDevice::~EasyPenInputDevice() 92 { 93 CALLED(); 94 for (int32 i = 0; i < fDevices.CountItems(); i++) 95 delete (tablet_device *)fDevices.ItemAt(i); 96 97 #if DEBUG 98 fclose(sLogFile); 99 #endif 100 } 101 102 103 status_t 104 EasyPenInputDevice::InitCheck() 105 { 106 CALLED(); 107 108 BSerialPort *serial = new BSerialPort(); 109 int count = serial->CountDevices(); 110 char devName[B_OS_NAME_LENGTH]; 111 char byte; 112 113 for (int i=0; i<count; i++) { 114 serial->GetDeviceName(i, devName); 115 serial->SetDataRate(B_9600_BPS); 116 serial->SetDataBits(B_DATA_BITS_8); 117 serial->SetStopBits(B_STOP_BITS_1); 118 serial->SetParityMode(B_ODD_PARITY); 119 serial->SetFlowControl(B_HARDWARE_CONTROL+B_SOFTWARE_CONTROL); 120 serial->SetBlocking(true); 121 serial->SetTimeout(800000); 122 123 if (serial->Open(devName) < B_OK) { 124 LOG("Fail to open %s\n", devName); 125 continue; 126 } 127 snooze(250000); 128 serial->Write("z9", 2); // 8 data bits,odd parity <command> z 9 129 serial->Write("", 1); // Reset <command> NUL 130 serial->Write("DP", 2); // mode command D trigger command P 131 snooze(250000); 132 serial->ClearInput(); 133 serial->Write("twP", 3); // Self-Test, Send Test Results, trigger command P 134 serial->Read(&byte, 1); 135 if ((byte & 0xf7) != 0x87) { 136 LOG("Self test failed for %s %x\n", devName, byte & 0xf7); 137 continue; 138 } 139 tablet_device *device = new tablet_device(serial); 140 device->owner = this; 141 142 input_device_ref *devices[2]; 143 devices[0] = &device->device_ref; 144 devices[1] = NULL; 145 146 fDevices.AddItem(device); 147 RegisterDevices(devices); 148 149 serial = new BSerialPort(); 150 } 151 152 delete serial; 153 154 LOG("Found %ld devices\n", fDevices.CountItems()); 155 156 get_click_speed(kDeviceName, &fClickSpeed); 157 158 return fDevices.CountItems() > 0 ? B_OK : B_ERROR; 159 } 160 161 162 status_t 163 EasyPenInputDevice::Start(const char *name, void *cookie) 164 { 165 tablet_device *device = (tablet_device *)cookie; 166 167 LOG("%s(%s)\n", __PRETTY_FUNCTION__, name); 168 169 char threadName[B_OS_NAME_LENGTH]; 170 snprintf(threadName, B_OS_NAME_LENGTH, "%s watcher", name); 171 172 device->active = true; 173 device->device_watcher = spawn_thread(DeviceWatcher, threadName, 174 kTabletThreadPriority, device); 175 176 if (device->device_watcher < B_OK) 177 return device->device_watcher; 178 resume_thread(device->device_watcher); 179 180 return B_OK; 181 } 182 183 184 status_t 185 EasyPenInputDevice::Stop(const char *name, void *cookie) 186 { 187 tablet_device *device = (tablet_device *)cookie; 188 189 LOG("%s(%s)\n", __PRETTY_FUNCTION__, name); 190 191 device->active = false; 192 if (device->device_watcher >= 0) { 193 suspend_thread(device->device_watcher); 194 resume_thread(device->device_watcher); 195 status_t dummy; 196 wait_for_thread(device->device_watcher, &dummy); 197 } 198 199 return B_OK; 200 } 201 202 203 status_t 204 EasyPenInputDevice::Control(const char *name, void *cookie, 205 uint32 command, BMessage *message) 206 { 207 LOG("%s(%s, code: %lu)\n", __PRETTY_FUNCTION__, name, command); 208 209 if (command == B_CLICK_SPEED_CHANGED) 210 get_click_speed(kDeviceName, &fClickSpeed); 211 212 return B_OK; 213 } 214 215 216 int32 217 EasyPenInputDevice::DeviceWatcher(void *arg) 218 { 219 tablet_device *dev = (tablet_device *)arg; 220 EasyPenInputDevice *owner = dev->owner; 221 222 tablet_movement movements; 223 tablet_movement old_movements; 224 BMessage *message; 225 uint8 byte; 226 uint8 bytes[4]; 227 bigtime_t lastClickTimeStamp = 0LL; 228 229 memset(&movements, 0, sizeof(movements)); 230 memset(&old_movements, 0, sizeof(old_movements)); 231 232 dev->serial->Write(":@FRb", 5); 233 234 while (dev->active) { 235 byte = 0; 236 while(!byte) 237 dev->serial->Read(&byte, 1); 238 if (byte & 0x40 || !(byte & 0x80)) { // 7th bit on or 8th bit off 239 snooze(10000); // this is a realtime thread, and something is wrong... 240 continue; 241 } 242 243 if (dev->serial->Read(bytes, 4) != 4) 244 continue; 245 246 if (*(uint32*)bytes & 0x80808080) 247 continue; 248 249 movements.buttons = byte & 0x7; 250 movements.timestamp = system_time(); 251 movements.xpos = (bytes[1] << 7) | bytes[0]; 252 movements.ypos = (bytes[3] << 7) | bytes[2]; 253 254 uint32 buttons = old_movements.buttons ^ movements.buttons; 255 256 LOG("%s: buttons: 0x%lx, x: %f, y: %f, clicks:%ld, wheel_x:%ld, wheel_y:%ld\n", dev->device_ref.name, movements.buttons, 257 movements.xpos, movements.ypos, movements.clicks, movements.wheel_xdelta, movements.wheel_ydelta); 258 259 movements.xpos /= 1950.0; 260 movements.ypos /= 1500.0; 261 262 LOG("%s: x: %f, y: %f, \n", dev->device_ref.name, movements.xpos, movements.ypos); 263 264 if (buttons != 0) { 265 message = new BMessage(B_MOUSE_UP); 266 if ((buttons & movements.buttons) > 0) { 267 message->what = B_MOUSE_DOWN; 268 if(lastClickTimeStamp + owner->fClickSpeed <= movements.timestamp) 269 movements.clicks = 1; 270 else 271 movements.clicks++; 272 lastClickTimeStamp = movements.timestamp; 273 message->AddInt32("clicks", movements.clicks); 274 LOG("B_MOUSE_DOWN\n"); 275 } else { 276 LOG("B_MOUSE_UP\n"); 277 } 278 279 message->AddInt64("when", movements.timestamp); 280 message->AddInt32("be:device_subtype", B_TABLET_DEVICE_SUBTYPE); 281 message->AddInt32("buttons", movements.buttons); 282 message->AddFloat("x", movements.xpos); 283 message->AddFloat("y", movements.ypos); 284 owner->EnqueueMessage(message); 285 } 286 287 if (movements.xpos != 0.0 || movements.ypos != 0.0) { 288 message = new BMessage(B_MOUSE_MOVED); 289 if (message) { 290 message->AddInt64("when", movements.timestamp); 291 message->AddInt32("be:device_subtype", B_TABLET_DEVICE_SUBTYPE); 292 message->AddInt32("buttons", movements.buttons); 293 message->AddFloat("x", movements.xpos); 294 message->AddFloat("y", movements.ypos); 295 message->AddFloat("be:tablet_x", movements.xpos); 296 message->AddFloat("be:tablet_y", movements.ypos); 297 message->AddFloat("be:tablet_pressure", movements.pressure); 298 message->AddInt32("be:tablet_eraser", (movements.switches & B_ERASER) != 0); 299 if (movements.tilt_x != 0.0 || movements.tilt_y != 0.0) { 300 message->AddFloat("be:tablet_tilt_x", movements.tilt_x); 301 message->AddFloat("be:tablet_tilt_y", movements.tilt_y); 302 } 303 304 owner->EnqueueMessage(message); 305 } 306 } 307 308 if ((movements.wheel_ydelta != 0) || (movements.wheel_xdelta != 0)) { 309 message = new BMessage(B_MOUSE_WHEEL_CHANGED); 310 if (message) { 311 message->AddInt64("when", movements.timestamp); 312 message->AddInt32("be:device_subtype", B_TABLET_DEVICE_SUBTYPE); 313 message->AddFloat("be:wheel_delta_x", movements.wheel_xdelta); 314 message->AddFloat("be:wheel_delta_y", movements.wheel_ydelta); 315 316 owner->EnqueueMessage(message); 317 } 318 } 319 320 old_movements = movements; 321 322 } 323 324 return 0; 325 } 326 327 328 // tablet_device 329 tablet_device::tablet_device(BSerialPort *port) 330 { 331 serial = port; 332 device_watcher = -1; 333 active = false; 334 device_ref.name = strdup(kDeviceName); 335 device_ref.type = B_POINTING_DEVICE; 336 device_ref.cookie = this; 337 }; 338 339 340 tablet_device::~tablet_device() 341 { 342 free(device_ref.name); 343 } 344 345