Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

import from SVN

  • Loading branch information...
commit 82f77a48ce98d8514b964c971dc713be4d34225c 1 parent 40463c5
@floe authored
View
16 Makefile
@@ -0,0 +1,16 @@
+CFLAGS=-ggdb -Wall
+
+all: test view
+
+clean:
+ rm *.o test view
+
+%.o: %.c surface.h
+ g++ $(CFLAGS) -c -o $@ $<
+
+test: test.o surface.o
+ g++ $(CFLAGS) -o $@ $^ -lusb
+
+view: view.o surface.o
+ g++ $(CFLAGS) -o $@ $^ -lusb -lGL -lGLU -lglut
+
View
6 README
@@ -0,0 +1,6 @@
+surface-2.0
+===========
+
+open-source driver for Microsoft Surface 2.0 (uses libusb)
+
+https://www.youtube.com/watch?v=e-JNqTY_3b0
View
4 README.md
@@ -1,4 +0,0 @@
-surface-2.0
-===========
-
-open-source driver for Microsoft Surface 2.0 (uses libusb)
View
168 surface.c
@@ -0,0 +1,168 @@
+/*
+ * microsoft surface 2.0 open source driver 0.0.1
+ *
+ * this is so experimental that the warranty shot itself.
+ * so don't expect any.
+ *
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "surface.h"
+
+int timeout = 1000;
+
+
+#define ENDPOINT_VIDEO 0x82
+#define ENDPOINT_BLOBS 0x86
+
+#define VIDEO_HEADER_MAGIC 0x46425553
+#define VIDEO_PACKET_SIZE 16384
+
+
+/************************* HELPER FUNCTIONS *************************/
+
+usb_dev_handle* usb_get_device_handle( int vendor, int product ) {
+
+ usb_init();
+ usb_find_busses();
+ usb_find_devices();
+
+ struct usb_bus* busses = usb_get_busses();
+
+ for (struct usb_bus* bus = busses; bus; bus = bus->next) {
+ for (struct usb_device* dev = bus->devices; dev; dev = dev->next) {
+ if ((dev->descriptor.idVendor == vendor) && (dev->descriptor.idProduct == product)) {
+ usb_dev_handle* handle = usb_open(dev);
+ if (!handle) return 0;
+ if (usb_claim_interface( handle, 0 ) < 0) return 0;
+ return handle;
+ }
+ }
+ }
+ return 0;
+}
+
+
+/************************** CONTROL STUFF ***************************/
+
+#define SURFACE_GET_VERSION 0xb0 // 12 bytes string
+#define SURFACE_UNKNOWN1 0xb3 // 5 bytes
+#define SURFACE_UNKNOWN2 0xc1 // 24 bytes
+
+#define SURFACE_GET_STATUS 0xc5 // 4 bytes state (?)
+#define SURFACE_GET_SENSORS 0xb1 // 8 bytes sensors
+
+// get version info
+void surface_get_version( usb_dev_handle* handle, uint16_t index ) {
+ uint8_t buf[13]; buf[12] = 0;
+ usb_control_msg( handle, 0xC0, SURFACE_GET_VERSION, 0x00, index, (char*)buf, 12, timeout );
+ printf("version string 0x%02x: %s\n", index, buf);
+}
+
+// get device status word
+int surface_get_status( usb_dev_handle* handle ) {
+ uint8_t buf[4];
+ usb_control_msg( handle, 0xC0, SURFACE_GET_STATUS, 0x00, 0x00, (char*)buf, 4, timeout );
+ return (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+}
+
+// get sensor status
+void surface_get_sensors( usb_dev_handle* handle ) {
+ surface_sensors sensors;
+ usb_control_msg( handle, 0xC0, SURFACE_GET_SENSORS, 0x00, 0x00, (char*)(&sensors), 8, timeout );
+ printf("temp: %d x: %d y: %d z: %d\n",sensors.temp,sensors.acc_x,sensors.acc_y,sensors.acc_z);
+}
+
+// other commands
+void surface_command( usb_dev_handle* handle, uint16_t cmd, uint16_t index, uint16_t len ) {
+ uint8_t buf[24];
+ usb_control_msg( handle, 0xC0, cmd, 0x00, index, (char*)buf, len, timeout );
+ printf("command 0x%02x,0x%02x: ", cmd, index );
+ for (int i = 0; i < len; i++) printf("0x%02x ", buf[i]);
+ printf("\n");
+}
+
+// mindless repetition of the microsoft driver's init sequence.
+// quite probably unnecessary, but leave it like this for now.
+void surface_init( usb_dev_handle* handle ) {
+
+ printf("microsoft surface 2.0 open source driver 0.0.1\n");
+
+ surface_get_version(handle, 0x00);
+ surface_get_version(handle, 0x01);
+ surface_get_version(handle, 0x02);
+
+ surface_command(handle, SURFACE_UNKNOWN2, 0x00, 24 );
+ surface_command(handle, SURFACE_UNKNOWN1, 0x00, 5 );
+
+ surface_get_version(handle, 0x03);
+}
+
+
+/************************** IMAGE FUNCTIONS *************************/
+
+int surface_get_image( usb_dev_handle* handle, uint8_t* image ) {
+
+ uint8_t buffer[512];
+ int result, bufpos = 0;
+
+ result = usb_bulk_read( handle, ENDPOINT_VIDEO, (char*)buffer, sizeof(buffer), timeout );
+ if (result != sizeof(surface_image)) { printf("transfer size mismatch\n"); return -1; }
+
+ surface_image* header = (surface_image*)buffer;
+ if (header->magic != VIDEO_HEADER_MAGIC) { printf("image magic mismatch\n"); return -1; }
+ if (header->size != VIDEO_BUFFER_SIZE ) { printf("image size mismatch\n"); return -1; }
+
+ while (bufpos < VIDEO_BUFFER_SIZE) {
+ result = usb_bulk_read( handle, ENDPOINT_VIDEO, (char*)(image+bufpos), VIDEO_PACKET_SIZE, timeout );
+ if (result < 0) { printf("error in usb_bulk_read\n"); return result; }
+ bufpos += result;
+ }
+
+ return header->timestamp;
+}
+
+
+/************************** BLOB FUNCTIONS **************************/
+
+int surface_get_blobs( usb_dev_handle* handle, surface_blob* outblob ) {
+
+ uint8_t buffer[512];
+ uint32_t packet_id;
+ int result;
+
+ int need_blobs = -1;
+ int current = 0;
+
+ surface_header* header = (surface_header*)buffer;
+ surface_blob* inblob = (surface_blob*)(buffer+sizeof(surface_header));
+
+ do {
+
+ result = usb_bulk_read( handle, ENDPOINT_BLOBS, (char*)(buffer), sizeof(buffer), timeout ) - sizeof(surface_header);
+ if (result < 0) { printf("error in usb_bulk_read\n"); return result; }
+ if (result % sizeof(surface_blob) != 0) { printf("transfer size mismatch\n"); return -1; }
+ //printf("id: %x count: %d\n",header->packet_id,header->count);
+
+ // first packet
+ if (need_blobs == -1) {
+ need_blobs = header->count;
+ packet_id = header->packet_id;
+ }
+
+ // sanity check. when video data is also being retrieved, the packet ID
+ // will usually increase in the middle of a series instead of at the end.
+ if (packet_id != header->packet_id) { printf("packet ID mismatch\n"); }
+
+ int packet_blobs = result / sizeof(surface_blob);
+
+ for (int i = 0; i < packet_blobs; i++) outblob[current++] = inblob[i];
+
+ } while (current < need_blobs);
+
+ return need_blobs;
+}
+
View
97 surface.h
@@ -0,0 +1,97 @@
+#ifndef _SURFACE_H_
+#define _SURFACE_H_
+
+#include <stdint.h>
+#include <usb.h>
+
+
+#define ID_MICROSOFT 0x045e
+#define ID_SURFACE 0x0775
+
+#define VIDEO_RES_X 960
+#define VIDEO_RES_Y 540
+
+#define VIDEO_BUFFER_SIZE VIDEO_RES_X * VIDEO_RES_Y
+
+
+// read 512 bytes from endpoint 0x86 -> get header + blobs
+struct surface_header {
+
+ uint16_t type; // always 0x0001
+ uint16_t count; // count of blobs (if == 0: continue prev. packet ID)
+
+ uint32_t packet_id;
+
+ uint32_t timestamp; // milliseconds (increases by 16 or 17 each frame)
+ uint32_t unknown; // "epoch?" always 02/03 00 00 00
+
+};
+
+
+struct surface_blob {
+
+ uint16_t blob_id;
+
+ uint8_t action; // 0x02 = enter/exit, 0x03 = update (?)
+ uint8_t unknown; // always 0x01 or 0x02 (no idea what this is?)
+
+ uint16_t bb_pos_x; // upper left corner of bounding box
+ uint16_t bb_pos_y;
+
+ uint16_t bb_size_x; // size of bounding box
+ uint16_t bb_size_y;
+
+ uint16_t pos_x; // finger tip position
+ uint16_t pos_y;
+
+ uint16_t ctr_x; // centroid position
+ uint16_t ctr_y;
+
+ uint16_t axis_x; // somehow related to major/minor axis, mostly:
+ uint16_t axis_y; // axis_x == bb_size_y && axis_y == bb_size_x
+
+ float angle; // orientation in radians relative to x axis
+ uint32_t area; // size in pixels/pressure (?)
+
+ uint8_t padding[32];
+};
+
+
+// read 512 bytes from endpoint 0x82 -> get header below
+// continue reading 16k blocks until header.size bytes read
+struct surface_image {
+ uint32_t magic; // "SUBF"
+ uint32_t packet_id;
+ uint32_t size; // always 0x0007e900 = 960x540
+ uint32_t timestamp; // milliseconds (increases by 16 or 17 each frame)
+ uint32_t unknown; // "epoch?" always 02/03 00 00 00
+};
+
+
+// read 8 bytes using control message 0xc0,0xb1,0x00,0x00
+struct surface_sensors {
+ uint16_t temp;
+ uint16_t acc_x;
+ uint16_t acc_y;
+ uint16_t acc_z;
+};
+
+
+// helper to find a device by vendor and product
+usb_dev_handle* usb_get_device_handle( int vendor, int product );
+
+// get device status word
+int surface_get_status( usb_dev_handle* handle );
+
+// get sensor status
+void surface_get_sensors( usb_dev_handle* handle );
+
+// initialization sequence
+void surface_init( usb_dev_handle* handle );
+
+// retrieve raw data from surface
+int surface_get_image( usb_dev_handle* handle, uint8_t* image );
+int surface_get_blobs( usb_dev_handle* handle, surface_blob* blob );
+
+#endif // _SURFACE_H_
+
View
31 test.c
@@ -0,0 +1,31 @@
+#include "surface.h"
+
+#include <stdio.h>
+
+/******************************* MAIN *******************************/
+
+int main( int argc, char* argv[] ) {
+
+ uint8_t buffer[VIDEO_BUFFER_SIZE];
+ surface_blob blob[256];
+ int result,frame = 0;
+
+ usb_dev_handle* s40 = usb_get_device_handle( ID_MICROSOFT, ID_SURFACE );
+
+ surface_init( s40 );
+
+ while (1) {
+ result = surface_get_blobs( s40, blob );
+ if (result <= 0) continue;
+ printf("%d blobs\n",result);
+ for (int i = 0; i < result; i++)
+ printf(" x: %d y: %d size: %d\n",blob[i].pos_x,blob[i].pos_y,blob[i].area);
+ if ((frame++ % 60) == 0) printf("status 0x%08x\n",surface_get_status(s40));
+ }
+
+ surface_get_image( s40, buffer );
+ FILE* foo = fopen("surface.raw","w+");
+ fwrite(buffer,VIDEO_BUFFER_SIZE,1,foo);
+ fclose(foo);
+}
+
View
198 view.c
@@ -0,0 +1,198 @@
+#include "surface.h"
+
+#include <stdlib.h> // random() etc.
+#include <string.h> // strlen() etc.
+#include <stdio.h> // printf() etc.
+#include <time.h> // time()
+#include <math.h> // fabsf()
+
+#include <unistd.h> // fcntl()
+#include <fcntl.h>
+
+#include <sys/socket.h> // inet_addr()
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <GL/glut.h>
+
+
+usb_dev_handle* s40;
+GLuint texture;
+
+
+void output( int x, int y, char *string ) {
+ glRasterPos2f(x, y);
+ int len, i;
+ len = (int)strlen(string);
+ for (i = 0; i < len; i++) {
+ glutBitmapCharacter(GLUT_BITMAP_9_BY_15, string[i]);
+ }
+}
+
+
+void cross( int x, int y ) {
+
+ glBegin(GL_LINES);
+ glVertex3f(x+10,y,0);
+ glVertex3f(x-10,y,0);
+ glVertex3f(x,y+10,0);
+ glVertex3f(x,y-10,0);
+ glEnd();
+}
+
+void box( int x1, int y1, int x2, int y2 ) {
+
+ glBegin(GL_LINES);
+ glVertex3f(x1,y1,0); glVertex3f(x1,y2,0);
+ glVertex3f(x2,y1,0); glVertex3f(x2,y2,0);
+ glVertex3f(x1,y1,0); glVertex3f(x2,y1,0);
+ glVertex3f(x1,y2,0); glVertex3f(x2,y2,0);
+ glEnd();
+}
+
+
+
+void idle() { glutPostRedisplay(); }
+
+void display() {
+
+ uint8_t image[VIDEO_BUFFER_SIZE];
+ surface_blob blobs[256];
+
+ // clear buffers
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ // move to origin
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glTranslatef(0,540,0);
+ glScalef(1.0f, -1.0f, 1.0f);
+
+ surface_get_image( s40, image );
+ int bc = surface_get_blobs( s40, blobs );
+
+ glEnable(GL_TEXTURE_2D);
+
+ glBindTexture(GL_TEXTURE_2D, texture);
+ glTexImage2D(GL_TEXTURE_2D, 0, 1, 960, 540, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, image);
+
+ glBegin(GL_TRIANGLE_FAN);
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+ glTexCoord2f(0, 0); glVertex3f(0,0,0);
+ glTexCoord2f(1, 0); glVertex3f(960,0,0);
+ glTexCoord2f(1, 1); glVertex3f(960,540,0);
+ glTexCoord2f(0, 1); glVertex3f(0,540,0);
+ glEnd();
+
+ glDisable(GL_TEXTURE_2D);
+
+ // green: tip
+ glColor4f(0.0f, 1.0f, 0.0f, 1.0f);
+ for (int i = 0; i < bc; i++) cross( blobs[i].pos_x/2, blobs[i].pos_y/2 );
+
+ // red: centroid(?)
+ glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
+ for (int i = 0; i < bc; i++) cross( blobs[i].ctr_x/2, blobs[i].ctr_y/2 );
+
+ // yellow: axis(?)
+ /*glColor4f(1.0f, 1.0f, 0.0f, 1.0f);
+ for (int i = 0; i < bc; i++) cross( (blobs[i].pos_x+blobs[i].axis_x)/2, (blobs[i].pos_y+blobs[i].axis_y)/2 );*/
+
+ // blue: bbox
+ glColor4f(0.0f, 0.0f, 1.0f, 1.0f);
+ for (int i = 0; i < bc; i++) box( blobs[i].bb_pos_x/2, blobs[i].bb_pos_y/2, (blobs[i].bb_pos_x+blobs[i].bb_size_x)/2, (blobs[i].bb_pos_y+blobs[i].bb_size_y)/2 );
+
+ // white: id
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+ for (int i = 0; i < bc; i++) {
+ char buf[64]; snprintf(buf,64,"%d",blobs[i].blob_id);
+ output(blobs[i].ctr_x/2, blobs[i].ctr_y/2,buf);
+ }
+
+
+ // redraw
+ glutSwapBuffers();
+}
+
+
+void resize(int w, int h) {
+
+ // set a whole-window viewport
+ glViewport(0,0,w,h);
+
+ glMatrixMode( GL_PROJECTION );
+ glLoadIdentity();
+ glOrtho( 0.0, w, 0.0, h, -1000, 1000 );
+
+ // invalidate display
+ glutPostRedisplay();
+}
+
+
+void keyboard(unsigned char key, int x, int y) {
+ switch (key) {
+ case 'q':
+ //usb_reset( s40 ); sleep(1);
+ usb_close( s40 );
+ exit(0);
+ break;
+ }
+}
+
+
+// initialize the GL library
+void initGL() {
+
+ // enable and set colors
+ glEnable(GL_COLOR_MATERIAL);
+ glClearColor(0.0,0.0,0.0,1.0);
+
+ // misc stuff
+ glDisable(GL_LIGHTING);
+ glDisable(GL_CULL_FACE);
+ glShadeModel( GL_FLAT );
+
+ glDepthFunc(GL_LESS);
+ glDepthMask(GL_FALSE);
+ glDisable(GL_DEPTH_TEST);
+
+ glDisable(GL_BLEND);
+ glDisable(GL_ALPHA_TEST);
+
+ glEnable(GL_TEXTURE_2D);
+ glGenTextures(1, &texture );
+ glBindTexture(GL_TEXTURE_2D, texture );
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ resize(VIDEO_RES_X,VIDEO_RES_Y);
+}
+
+
+/******************************* MAIN *******************************/
+
+int main(int argc, char* argv[]) {
+
+ s40 = usb_get_device_handle( ID_MICROSOFT, ID_SURFACE );
+ surface_init( s40 );
+
+ glutInitWindowSize(VIDEO_RES_X,VIDEO_RES_Y);
+ glutInit(&argc,argv);
+ glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH );
+ glutCreateWindow("surfaceview");
+
+ initGL();
+
+ // make functions known to GLUT
+ glutKeyboardFunc(keyboard);
+ glutDisplayFunc(display);
+ glutReshapeFunc(resize);
+ glutIdleFunc(idle);
+
+ // start the action
+ glutMainLoop();
+
+ return 0;
+}
+
+
Please sign in to comment.
Something went wrong with that request. Please try again.