/* LICENSE=GPL */ /* iFeel force feedback uinput driver ** written by emard@softhome.net */ /* compile with libusb, use gcc -lusb -lpthread */ #define THREAD 0 #include #include #include #include #include #include #include #include #if THREAD #include #endif #include #include #include #include #include #include #if THREAD pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; #endif #include "usbid.h" #define TIMEOUT 3000 #define WAITINBETWEEN 500000 /* From HID specification */ #define SET_REQUEST 0x21 #define SET_REPORT 0x09 #define OUTPUT_REPORT_TYPE 0x02 /* how many controlling threads */ #define THREADS 3 #define HAVEFF 0 int ledcounter = 0; /* debugging purpose */ #define MUTEX 0 char led0[] = { 0x01,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00, }; char led1[] = { 0x01,0x11,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00, }; char ring0[] = { 0x01,0x00,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00 }; char ring1[] = { 0x01,0xff,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00 }; struct lcdpacket { unsigned char type; unsigned char unknown[16]; unsigned char bitmap[16]; }; #define SCANMAX 32 /* keys */ int keys[][2] = { { KEY_BACKSPACE, 0x01}, { KEY_ENTER, 0x07 }, { KEY_UP, 0x0d }, { KEY_DOWN, 0x13 }, { KEY_1, 0x04 }, { KEY_2, 0x03 }, { KEY_3, 0x02 }, { KEY_4, 0x0a }, { KEY_5, 0x09 }, { KEY_6, 0x08 }, { KEY_7, 0x10 }, { KEY_8, 0x0f }, { KEY_9, 0x0e }, { KEY_KPASTERISK, 0x16 }, { KEY_0, 0x15 }, { KEY_KPPLUS, 0x14 }, { KEY_MAX, 0 } }; int scan2code[SCANMAX]; unsigned short usb_phone_vendor = USB_MOUSE_VENDOR; unsigned short usb_phone_product = USB_MOUSE_PRODUCT; #if THREAD int rc[THREADS]; pthread_t thread[THREADS]; #endif /* Global list of FDs each object is interested in, ** and FDs that there have been activity on. Each object ** adds itself to the fd_request_* lists on creation and removes ** itself on deletion, and checks the fd_* lists on poll. */ static fd_set fd_request_read, fd_request_write, fd_request_except; static fd_set fd_read, fd_write, fd_except; int fd_count = 0; struct handset { struct usb_device *dev; /* libusb dev */ struct usb_dev_handle *handle; int uifd; /* os-side uinput fd (read/write) */ struct uinput_user_dev device; unsigned int strength, delay, count; double t0; /* absolute unix time when effect was started t_effect=0 */ double t; /* current time when effect envelope is to be calculated */ /* immediately convert timeval to double to simplyfy handling */ int countdown; /* effect countdown counter */ struct timeval timer_cont; /* continuation timer */ unsigned char led; /* green LED */ unsigned char ringer; /* ringer status */ char data[20]; /* previous data received from the phone */ int custom_len; /* custom effect upload length */ unsigned char custom_data[16]; /* custom data */ time_t zaptime; int zaphold; }; struct handset handset; /* get absoulte current time */ double current_time() { struct timeval current_time; gettimeofday(¤t_time, NULL); return (double)current_time.tv_sec + 1.0e-6 * (double)current_time.tv_usec; } /* This initializes libusb and tracks down the device */ static struct usb_device *init_device() { struct usb_bus *bus; struct usb_device *dev; usb_init(); usb_find_busses(); usb_find_devices(); for (bus = usb_busses; bus; bus = bus->next) { for (dev = bus->devices; dev; dev = dev->next) { if ((dev->descriptor.idVendor == usb_phone_vendor) && (dev->descriptor.idProduct == usb_phone_product)) return dev; } } return NULL; } /* register the phone in uinput system as ** pointing device with ** a number of keys, relative pointers and leds ** then it will automatically appear as /dev/input/phone* */ int handset_register(struct handset *phone) { int aux, fd; /* libusb initialization */ phone->dev = init_device(); if(!phone->dev) { printf("Unable to find device.\n"); return 1; } /* Open device, note: we want interface zero so we only have to open once */ phone->handle = usb_open(phone->dev); if(!phone->handle) { printf("Unable to open device.\n"); return 1; } #if 0 /* device doesn't accept configuration change ! */ if(usb_set_configuration(phone->handle, 1)) { printf("Failed to set config 1: EBUS: %d ENONM: %d\n", EBUSY, ENOMEM); return 1; } #endif /* The keypad, led and ringer are at interface 3 */ if(usb_claim_interface(phone->handle, 3) < 0) { printf("Unable to claim interface 3.\n"); return 1; } /* uinput initialization */ FD_ZERO(&fd_request_read); FD_ZERO(&fd_request_write); FD_ZERO(&fd_request_except); /* open uinput device file */ fd = open("/dev/input/uinput", O_RDWR | O_NONBLOCK); if (fd < 0) { perror("open"); return -1; } phone->uifd = fd; /* sets the name of our device */ strcpy(phone->device.name, "BeyondTel USB Phone"); /* sets maximum number of simultaneous force feedback effects */ /* report kernel bug - not setting this leads to kernel oops */ #if HAVEFF phone->device.ff_effects_max = 1; #endif /* its bus */ phone->device.id.bustype = BUS_USB; /* and id/vendor id/product id/version ** we will create with uinput ** (not need to be the same as the hardware phone) */ phone->device.id.vendor = usb_phone_vendor; phone->device.id.product = usb_phone_product; phone->device.id.version = 257; /* inform that we'll generate key events */ ioctl(fd, UI_SET_EVBIT, EV_KEY); /* set keyboard events we can generate */ for (aux = 0; keys[aux][0] < KEY_MAX; aux++) { ioctl(fd, UI_SET_KEYBIT, keys[aux][0]); /* keyboard translation table */ scan2code[keys[aux][1]] = keys[aux][0]; } /* inform that we have ringer */ ioctl(fd, UI_SET_EVBIT, EV_SND); /* set sound we can ring bell */ ioctl(fd, UI_SET_SNDBIT, SND_BELL); /* inform that we have force feedback - hack to use LCD */ #if HAVEFF ioctl(fd, UI_SET_EVBIT, EV_FF); /* set force feedback events - custom container for LCD control */ ioctl(fd, UI_SET_FFBIT, FF_PERIODIC); ioctl(fd, UI_SET_FFBIT, FF_CUSTOM); #endif /* inform that we have LED */ ioctl(fd, UI_SET_EVBIT, EV_LED); /* set LED we can display */ ioctl(fd, UI_SET_LEDBIT, LED_NUML); /* write down information for creating a new device */ if (write(fd, &(phone->device), sizeof(struct uinput_user_dev)) < 0) { perror("write"); close(fd); return 1; } /* actually creates the device */ ioctl(fd, UI_DEV_CREATE); /* now we can register our uinput device for select() */ FD_SET(fd, &fd_request_read); if(fd >= fd_count) fd_count = fd + 1; /* start with LED off */ phone->led = 0; /* start with ringer off */ phone->ringer = 0; memset(&(phone->timer_cont), 0, sizeof(phone->timer_cont)); return 0; } /* wait for file descriptors. ** waits for vibration commands ** from uinput system, or for timeouts ** in order to continue lengthy vibrations */ int handset_wait(struct handset *phone) { int n; struct timeval *timeout; timeout = &(phone->timer_cont); if(timeout->tv_sec == 0 && timeout->tv_usec == 0) timeout = NULL; memcpy(&fd_read, &fd_request_read, sizeof(fd_set)); memcpy(&fd_write, &fd_request_write, sizeof(fd_set)); memcpy(&fd_except, &fd_request_except, sizeof(fd_set)); n = select(fd_count, &fd_read, &fd_request_write, &fd_request_except, timeout ); return 0; } /* unregister the phone from uinput system */ int handset_unregister(struct handset *phone) { /* uinput de-initialization */ close(phone->uifd); /* libusb de-initialization */ usb_close(phone->handle); return 0; } int set_led(struct handset *phone) { char buffer[20]; int ret; ret = usb_interrupt_read(phone->handle, 1, buffer, 17, 5000); if(phone->led) ret = usb_interrupt_write(phone->handle, 1, led1, sizeof(led1), 5000); else ret = usb_interrupt_write(phone->handle, 1, led0, sizeof(led0), 5000); ret = usb_interrupt_read(phone->handle, 1, buffer, 17, 5000); return 0; } int set_ringer(struct handset *phone) { char buffer[20]; int ret; ret = usb_interrupt_read(phone->handle, 1, buffer, 17, 5000); if(phone->ringer) ret = usb_interrupt_write(phone->handle, 1, ring1, sizeof(ring1), 5000); else ret = usb_interrupt_write(phone->handle, 1, ring0, sizeof(ring0), 5000); ret = usb_interrupt_read(phone->handle, 1, buffer, 17, 5000); return 0; } int set_lcd(struct handset *phone) { char buffer[20]; struct lcdpacket display[1]; unsigned char *packetdisp; int ret; int i; display->type = 3; memset(display->unknown, 0, sizeof(display->unknown)); memcpy(display->bitmap, phone->custom_data, sizeof(display->bitmap)); #if 0 packetdisp = (unsigned char *)display; for(i = 0; i < sizeof(display); i++) printf("%02x ", packetdisp[i]); printf("\n"); #endif ret = usb_interrupt_read(phone->handle, 1, buffer, 17, 5000); ret = usb_interrupt_write(phone->handle, 1, (unsigned char *)display, sizeof(display), 5000); ret = usb_interrupt_read(phone->handle, 1, buffer, 17, 5000); return 0; } /* process uinput event (ringer and LED) */ int uinput_event(struct handset *phone) { struct input_event event, received_event; struct uinput_ff_upload ff_up; struct uinput_ff_erase ff_erase; int fd; int long_count; int i; fd = phone->uifd; if(read(fd, &received_event, sizeof(received_event)) == sizeof(received_event)) { switch(received_event.type) { case EV_LED: switch(received_event.code) { case LED_NUML: phone->led = received_event.value > 0 ? 1 : 0; #if 0 printf("LED=%d cnt=%d\n", phone->led, ledcounter++); #endif set_led(phone); break; } break; case EV_SND: switch(received_event.code) { case SND_BELL: phone->ringer = received_event.value > 0 ? 1 : 0; set_ringer(phone); break; } break; #if HAVEFF case EV_UINPUT: switch(received_event.code) { case UI_FF_UPLOAD: printf("Effect upload requested.\n"); ff_up.request_id = received_event.value; if(ioctl(fd, UI_BEGIN_FF_UPLOAD, &ff_up) < 0) { perror("ioctl UI_BEGIN_FF_UPLOAD"); return; } /* upload effect (ff_up.request_id, ff_up.effect) */ switch(ff_up.effect.type) { case FF_PERIODIC: switch(ff_up.effect.u.periodic.waveform) { case FF_CUSTOM: memcpy(phone->custom_data, &(ff_up.effect.u.periodic.period), sizeof(phone->custom_data)); #if 0 printf("Our struct "); for(i = 0; i < sizeof(phone->custom_data); i++) printf("%02x ", phone->custom_data[i]); printf("\n"); #endif set_lcd(phone); ff_up.retval = 0; ff_up.effect.id = 0; break; } break; } if(ioctl(fd, UI_END_FF_UPLOAD, &ff_up) < 0) { perror("ioctl UI_END_FF_UPLOAD"); return; } break; case UI_FF_ERASE: ff_erase.request_id = received_event.value; if(ioctl(fd, UI_BEGIN_FF_ERASE, &ff_erase) < 0) { perror("ioctl UI_BEGIN_FF_ERASE"); } phone->custom_len = 0; // memset(phone->custom_data, 0, sizeof(phone->custom_data)); printf("request_id=%d effect_id=%d erased.\n", ff_erase.request_id, ff_erase.effect_id); ff_erase.retval = 0; if(ioctl(fd, UI_END_FF_ERASE, &ff_erase) < 0) { perror("ioctl UI_END_FF_UPLOAD"); } break; } break; #endif } } #if 0 else puts("uinput event wrong size..."); #endif return 0; } /* create single thread containing all here */ void *uinput_handler(void *data) { struct handset *phone = (struct handset *) data; int i, n; /* loop processing events */ for(;;) { /* wait for events from uinput */ handset_wait(phone); /* is this uinput event? */ if(FD_ISSET(phone->uifd, &fd_read)) { #if MUTEX pthread_mutex_lock(&mutex1); #endif uinput_event(phone); #if MUTEX pthread_mutex_unlock(&mutex1); #endif } else { #if 0 /* no, this could be timer-based continuation */ if(phone->countdown) handset_vibrate(phone, 1); #endif } } return NULL; } /* warning track keys if many of them are pressed ** need to generate key up - key down events inbetween */ int key_event(struct handset *phone, char *buffer) { struct input_event event; if( buffer[1] != phone->data[1] ) { #if MUTEX pthread_mutex_lock(&mutex1); #endif if( phone->data[1] != 0 && phone->data[1] < SCANMAX ) { event.type = EV_KEY; event.code = scan2code[phone->data[1]]; event.value = 0; write(phone->uifd, &event, sizeof(struct input_event)); event.type = EV_SYN; event.code = SYN_REPORT; event.value = 0; write(phone->uifd, &event, sizeof(struct input_event)); } if( buffer[1] != 0 && buffer[1] < SCANMAX ) { event.type = EV_KEY; event.code = scan2code[buffer[1]]; event.value = 1; write(phone->uifd, &event, sizeof(struct input_event)); event.type = EV_SYN; event.code = SYN_REPORT; event.value = 0; write(phone->uifd, &event, sizeof(struct input_event)); } fsync(phone->uifd); #if MUTEX pthread_mutex_unlock(&mutex1); #endif } return 0; } void *keypad_handler(void *data) { struct handset *phone = (struct handset *) data; struct input_event event; int i, j, ret, change, fd; char buffer[20]; fd = phone->uifd; for(;;) { #if MUTEX pthread_mutex_lock(&mutex1); #endif ret = usb_interrupt_read(phone->handle, 1, buffer, 17, 5000); #if MUTEX pthread_mutex_unlock(&mutex1); #endif if(ret < 0) { printf("Failure usb_interrupt_read %d '%s'\n", ret, strerror(-ret)); } #if 0 printf("Read %d bytes: ", ret); for(j = 0; j < ret; j++) printf("%02x ", ((int)buffer[j] & 0x0ff)); printf("\n"); #endif /* decode event */ /* In vague hallucination I have not heard ** master Yoda say: ** ** mutexing this and uinput for ** kernel 2.6.13.4 crash prevent could not not ** but rather delayed would */ if(ret == 17) { key_event(phone, buffer); memcpy(phone->data, buffer, 17); } /* here we can process uinput event in-thread ** beware of blocking */ #if THREAD #else uinput_event(phone); #endif } return NULL; } void terminate(int signal) { #if THREAD pthread_cancel(thread[0]); pthread_cancel(thread[1]); #endif } int main(int argc, char *argv[]) { int i, n = THREADS; struct handset *phone = &handset; if(argc >= 2 && argc <= 3) { int detach = 0; char* const HEXPREFIX = "0x"; unsigned char const HEXPREFIXLEN = 2; unsigned char const HEXSTRINGLEN = 6; unsigned char const HEXNUMLEN = 4; char vendor_id_s[HEXSTRINGLEN + 1], product_id_s[HEXSTRINGLEN + 1]; if(*argv[1] == '-') { if(argv[1][1] == 'd') { detach = 1; argc--; argv++; } else { fprintf(stderr, "Usage: %s <-d> \n", argv[0]); fprintf(stderr, " Eg: %s -d %04x:%04x\n", argv[0], usb_phone_vendor, usb_phone_product); return 0; } } if(argc == 2) { memcpy(vendor_id_s, HEXPREFIX, HEXPREFIXLEN); strncpy(vendor_id_s + HEXPREFIXLEN, argv[1], HEXNUMLEN); usb_phone_vendor = 0xffff & strtol(vendor_id_s, NULL, 16); memcpy(product_id_s, HEXPREFIX, HEXPREFIXLEN); strncpy(product_id_s + HEXPREFIXLEN, argv[1] + HEXNUMLEN + 1, HEXNUMLEN); usb_phone_product = 0xffff & strtol(product_id_s, NULL, 16); } if(detach) { HIDInterface* hid; hid_return ret; fprintf(stderr, "Trying to detach HID with IDs %04x:%04x... ", usb_phone_vendor, usb_phone_product); HIDInterfaceMatcher matcher = { usb_phone_vendor, usb_phone_product, NULL, NULL, 0 }; ret = hid_init(); if (ret != HID_RET_SUCCESS) { fprintf(stderr, "hid_init failed with return code %d.\n", ret); return 1; } hid = hid_new_HIDInterface(); if (hid == 0) { fprintf(stderr, "hid_new_HIDInterface() failed, out of memory?\n"); return 1; } ret = hid_force_open(hid, 0, &matcher, 3); if (ret != HID_RET_SUCCESS) { fprintf(stderr, "hid_force_open failed with return code %d.\n", ret); return 1; } ret = hid_close(hid); if (ret != HID_RET_SUCCESS) { fprintf(stderr, "hid_close failed with return code %d.\n", ret); return 1; } hid_delete_HIDInterface(&hid); ret = hid_cleanup(); if (ret != HID_RET_SUCCESS) { fprintf(stderr, "hid_cleanup failed with return code %d.\n", ret); return 1; } fprintf(stderr, "done.\n"); } } if(handset_register(phone)) { printf("Cannot open device\n"); return -1; } printf("User space driver (uinput) for BeyondTel USB Phone.\n"); /* Create 2 independant threads ** one for force feedback handler ** second for normal phone events (kays and movement) */ #if THREAD /* this is pthread version */ i = 0; if( (rc[i] = pthread_create( &thread[i], NULL, &uinput_handler, (void *) (phone) )) ) printf("Thread creation failed: %d\n", rc[i]); i = 1; if( (rc[i] = pthread_create( &thread[i], NULL, &keypad_handler, (void *) (phone) )) ) printf("Thread creation failed: %d\n", rc[i]); /* graceful exit handler */ signal(SIGINT, &terminate); signal(SIGTERM, &terminate); /* Wait till threads are complete before main continues. Unless we */ /* wait we run the risk of executing an exit which will terminate */ /* the process and all threads before the threads have completed. */ pthread_join( thread[0], NULL); pthread_join( thread[1], NULL); #else keypad_handler(phone); #endif /* try single execution no-thread version */ handset_unregister(phone); printf("exit.\n"); return 0; }